Skip to content

Commit

Permalink
WIP: implement setting circle avatar
Browse files Browse the repository at this point in the history
missing: actually doing the thing, we get forbidden back because the
role doesn't propagate to groups.$SNIKKET_DOMAIN

Fixes #49.
  • Loading branch information
horazont committed Mar 28, 2023
1 parent 49bbc3a commit 788ca73
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 2 deletions.
24 changes: 24 additions & 0 deletions snikket_web/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

from . import prosodyclient, _version
from .infra import client, circle_name, BaseForm
from .user import EAVATARTOOBIG

bp = Blueprint("admin", __name__, url_prefix="/admin")

Expand Down Expand Up @@ -443,6 +444,10 @@ class EditCircleForm(BaseForm):
validators=[wtforms.validators.InputRequired()],
)

avatar = wtforms.FileField(
_l("Avatar")
)

user_to_add = wtforms.SelectField(
_l("Select user"),
validate_choice=False,
Expand All @@ -466,6 +471,8 @@ class EditCircleForm(BaseForm):
@bp.route("/circle/<id_>", methods=["GET", "POST"])
@client.require_admin_session()
async def edit_circle(id_: str) -> typing.Union[str, werkzeug.Response]:
max_avatar_size = current_app.config["MAX_AVATAR_SIZE"]

async with client.authenticated_session() as session:
try:
circle = await client.get_group_by_id(
Expand Down Expand Up @@ -511,6 +518,20 @@ async def edit_circle(id_: str) -> typing.Union[str, werkzeug.Response]:
id_,
new_name=form.name.data,
)
file_info = (await request.files).get(form.avatar.name)
if file_info is not None:
mimetype = file_info.mimetype
data = file_info.stream.read()
if len(data) > max_avatar_size:
form.avatar.errors.append(EAVATARTOOBIG)
ok = False
elif len(data) > 0:
print("setting muc avatar")
await client.set_muc_avatar(
circle.muc_jid,
data,
mimetype,
)
await flash(
_("Circle data updated"),
"success",
Expand Down Expand Up @@ -550,6 +571,9 @@ async def edit_circle(id_: str) -> typing.Union[str, werkzeug.Response]:
form=form,
circle_members=circle_members,
invite_form=invite_form,
max_avatar_size=max_avatar_size,
avatar_too_big_warning_header=_l("Error"),
avatar_too_big_warning=EAVATARTOOBIG,
)


Expand Down
20 changes: 20 additions & 0 deletions snikket_web/prosodyclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -1246,3 +1246,23 @@ async def post_announcement(
json=payload) as resp:
self._raise_error_from_response(resp)
resp.raise_for_status()

@autosession
async def set_muc_avatar(
self,
muc_jid: str,
data: bytes,
mimetype: str,
*,
session: aiohttp.ClientSession,
):
xmpputil.extract_iq_reply(
await self._xml_iq_call(
session,
xmpputil.make_muc_avatar_set_request(
muc_jid,
data,
mimetype,
),
)
)
13 changes: 11 additions & 2 deletions snikket_web/templates/admin_edit_circle.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{% extends "admin_app.html" %}
{% from "library.j2" import form_button, standard_button, value_or_hint, custom_form_button, clipboard_button, icon %}
{% from "library.j2" import form_button, standard_button, value_or_hint, custom_form_button, clipboard_button, icon, avatar with context %}
{% block head_lead %}
{{ super() }}
{% include "copy-snippet.html" %}
{% endblock %}
{% block content %}
<h1>{% trans circle_name=(target_circle | circle_name) %}Edit circle {{ circle_name }}{% endtrans %}</h1>
<form method="POST">
<form method="POST" enctype="multipart/form-data">
{{- form.csrf_token -}}
{%- if target_circle.id_ == "default" -%}
<input type="hidden" name="{{ form.name.name }}" value="{{ form.name.data }}">{#- -#}
Expand Down Expand Up @@ -39,6 +39,15 @@ <h2 class="form-title">{% trans %}Circle information{% endtrans %}</h2>
<p>{% trans %}This circle has no group chat associated.{% endtrans %}<p>
{%- endif -%}
</div>
<div class="f-ebox">
{{ form.avatar.label }}
<div class="avatar-wrap">
{{ form.avatar(accept="image/png",
data_maxsize=max_avatar_size,
data_warning_header=avatar_too_big_warning_header,
data_maxsize_warning=avatar_too_big_warning) }}
</div>
</div>
<div class="f-bbox">
{%- call standard_button("back", url_for(".circles"), class="tertiary") -%}
{% trans %}Return to circle list{% endtrans %}
Expand Down
31 changes: 31 additions & 0 deletions snikket_web/xmpputil.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
FORM_NODE_CONFIG = "http://jabber.org/protocol/pubsub#node_config"
FORM_FIELD_PUBSUB_ACCESS_MODEL = "pubsub#access_model"

NS_VCARD_TEMP = "vcard-temp"


SimpleJID = typing.Tuple[typing.Optional[str], str, typing.Optional[str]]
T = typing.TypeVar("T")
Expand Down Expand Up @@ -226,6 +228,35 @@ def make_avatar_metadata_set_request(
return req


def make_muc_avatar_set_request(
to: str,
data: bytes,
mimetype: str,
) -> ET.Element:
req = ET.Element("iq", type="set", to=to)
vcard = ET.SubElement(
req,
"vCard",
xmlns=NS_VCARD_TEMP,
)
photo_el = ET.SubElement(
vcard,
"PHOTO",
xmlns=NS_VCARD_TEMP,
)
ET.SubElement(
photo_el,
"BINVAL",
xmlns=NS_VCARD_TEMP,
).text = base64.b64encode(data).decode("ascii")
ET.SubElement(
photo_el,
"TYPE",
xmlns=NS_VCARD_TEMP,
).text = mimetype
return req


def _require_child(t: ET.Element, tag: str) -> ET.Element:
el = t.find(tag)
if el is None:
Expand Down

0 comments on commit 788ca73

Please sign in to comment.