# HG changeset patch # User Goffi # Date 1613826442 -3600 # Node ID a84383c659b40faa50e257ec160bdb8e046e2bf8 # Parent 83be300d17e30b213a81bb5de970fb953f39cf91 lists: creation, invitation, item deletion: this big patch includes: - reorganisation of pages for consistency, discovery is now the main list page, and list overview is now in `view` while item view is moved to `view_item` - lists from lists of interest are now shown in discovery page - list deletion from discory page - list can now be created, using templates now available from backend - invitation manager can now be used from list overview - list item can now be deleted from `view_item` diff -r 83be300d17e3 -r a84383c659b4 libervia/pages/lists/_browser/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libervia/pages/lists/_browser/__init__.py Sat Feb 20 14:07:22 2021 +0100 @@ -0,0 +1,66 @@ +from browser import bind, DOMNode +from javascript import JSON +from bridge import Bridge +import dialog + +bridge = Bridge() + + +def list_delete_cb(item_elt, item): + dialog.notification.show(f"{item['name']!r} has been deleted") + + +def list_delete_eb(failure_, item_elt, item): + dialog.notification.show( + f"Error while deleting {item['name']!r}: {failure_['message']}", + "error" + ) + + +def interest_retract_cb(item_elt, item): + print(f"{item['name']!r} removed successfuly from list of interests") + item_elt.classList.add("state_deleted") + item_elt.bind("transitionend", lambda evt: item_elt.remove()) + bridge.psNodeDelete( + item['service'], + item['node'], + callback=lambda: list_delete_cb(item_elt, item), + errback=lambda failure: list_delete_eb(failure, item_elt, item), + ) + + +def interest_retract_eb(failure_, item_elt, item): + dialog.notification.show( + f"Can't remove list {item['name']!r} from personal interests: " + f"{failure_['message']}", + "error" + ) + + +def delete_ok(evt, notif_elt, item_elt, item): + bridge.interestRetract( + "", item['id'], + callback=lambda: interest_retract_cb(item_elt, item), + errback=lambda failure:interest_retract_eb(failure, item_elt, item)) + + +def delete_cancel(evt, notif_elt, item_elt, item): + notif_elt.remove() + item_elt.classList.remove("selected_for_deletion") + + +@bind(".action_delete", "click") +def on_delete(evt): + evt.stopPropagation() + evt.preventDefault() + target = evt.currentTarget + item_elt = DOMNode(target.closest('.item')) + item_elt.classList.add("selected_for_deletion") + item = JSON.parse(item_elt.dataset.item) + dialog.Confirm( + f"List {item['name']!r} will be deleted, are you sure?", + ok_label="delete", + ).show( + ok_cb=lambda evt, notif_elt: delete_ok(evt, notif_elt, item_elt, item), + cancel_cb=lambda evt, notif_elt: delete_cancel(evt, notif_elt, item_elt, item), + ) diff -r 83be300d17e3 -r a84383c659b4 libervia/pages/lists/create/page_meta.py --- a/libervia/pages/lists/create/page_meta.py Sat Feb 20 13:58:42 2021 +0100 +++ b/libervia/pages/lists/create/page_meta.py Sat Feb 20 14:07:22 2021 +0100 @@ -1,58 +1,41 @@ #!/usr/bin/env python3 - from libervia.server.constants import Const as C -from sat.tools.common import template_xmlui -from sat.tools.common import data_objects from sat.tools.common import data_format from sat.core.log import getLogger log = getLogger(__name__) name = "list_create" -access = C.PAGES_ACCESS_PUBLIC -template = "list/overview.html" +access = C.PAGES_ACCESS_PROFILE +template = "list/create.html" def parse_url(self, request): - self.getPathArgs(request, ["service", "node"], service="jid") + self.getPathArgs(request, ["template_id"]) data = self.getRData(request) - service, node = data["service"], data["node"] - if node is None: - self.pageRedirect("lists_disco", request) - if node == "@": - node = data["node"] = "" - template_data = request.template_data - template_data["url_list_items"] = self.getURL(service.full(), node or "@") - template_data["url_list_new"] = self.getSubPageURL(request, "list_new") + if data["template_id"]: + self.HTTPRedirect( + request, + self.getPageByName("list_create_from_tpl").getURL(data["template_id"]) + ) async def prepare_render(self, request): - data = self.getRData(request) template_data = request.template_data - service, node = data["service"], data["node"] - profile = self.getProfile(request) or C.SERVICE_PROFILE - - self.checkCache(request, C.CACHE_PUBSUB, service=service, node=node, short="tickets") - - extra = self.getPubsubExtra(request) - extra["labels_as_list"] = C.BOOL_TRUE - self.handleSearch(request, extra) - - list_data_raw = await self.host.bridgeCall( - "listGet", - service.full() if service else "", - node, - C.NO_LIMIT, - [], + profile = self.getProfile(request) + tpl_raw = await self.host.bridgeCall( + "listTemplatesNamesGet", "", - extra, profile, ) - list_items, metadata = data_format.deserialise(list_data_raw, type_check=list) - template_data["list_items"] = [template_xmlui.create(self.host, x) for x in - list_items] - template_data["on_list_item_click"] = data_objects.OnClick( - url=self.getSubPageURL(request, "list_view") + "/{item.id}" - ) - self.setPagination(request, metadata) + lists_templates = data_format.deserialise(tpl_raw, type_check=list) + template_data["icons_names"] = {tpl['icon'] for tpl in lists_templates} + template_data["lists_templates"] = [ + { + "icon": tpl["icon"], + "name": tpl["name"], + "url": self.getURL(tpl["id"]), + } + for tpl in lists_templates + ] diff -r 83be300d17e3 -r a84383c659b4 libervia/pages/lists/create_from_tpl/page_meta.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libervia/pages/lists/create_from_tpl/page_meta.py Sat Feb 20 14:07:22 2021 +0100 @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +from sat.tools.common import data_format +from sat.core.log import getLogger +from sat.core.i18n import D_ +from sat.core import exceptions +from libervia.server.constants import Const as C +from sat_frontends.bridge.bridge_frontend import BridgeException + +log = getLogger(__name__) + +name = "list_create_from_tpl" +access = C.PAGES_ACCESS_PROFILE +template = "list/create_from_template.html" + + +def parse_url(self, request): + self.getPathArgs(request, ["template_id"]) + +async def prepare_render(self, request): + data = self.getRData(request) + template_id = data["template_id"] + if not template_id: + self.pageError(request, C.HTTP_BAD_REQUEST) + + template_data = request.template_data + profile = self.getProfile(request) + tpl_raw = await self.host.bridgeCall( + "listTemplateGet", + template_id, + "", + profile, + ) + template = data_format.deserialise(tpl_raw) + template['id'] = template_id + template_data["list_template"] = template + +async def on_data_post(self, request): + data = self.getRData(request) + template_id = data['template_id'] + name, access = self.getPostedData(request, ('name', 'access')) + if access == 'private': + access_model = 'whitelist' + elif access == 'public': + access_model = 'open' + else: + log.warning(f"Unknown access for template creation: {access}") + self.pageError(request, C.HTTP_BAD_REQUEST) + profile = self.getProfile(request) + try: + service, node = await self.host.bridgeCall( + "listTemplateCreate", template_id, name, access_model, profile + ) + except BridgeException as e: + if e.condition == "conflict": + raise exceptions.DataError(D_("A list with this name already exists")) + else: + log.error(f"Can't create list from template: {e}") + raise e + data["post_redirect_page"] = ( + self.getPageByName("lists"), + service, + node or "@", + ) diff -r 83be300d17e3 -r a84383c659b4 libervia/pages/lists/disco/page_meta.py --- a/libervia/pages/lists/disco/page_meta.py Sat Feb 20 13:58:42 2021 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 - - -from libervia.server.constants import Const as C -from twisted.words.protocols.jabber import jid -from sat.core.log import getLogger - -log = getLogger(__name__) - -name = "lists_disco" -access = C.PAGES_ACCESS_PUBLIC -template = "list/discover.html" - - -def prepare_render(self, request): - lists_directory_config = self.host.options["lists_directory_json"] - if lists_directory_config: - trackers = request.template_data["lists_directory"] = [] - try: - for tracker_data in lists_directory_config: - service = tracker_data["service"] - node = tracker_data["node"] - name = tracker_data["name"] - url = self.getPageByName("lists").getURL(service, node) - trackers.append({"name": name, "url": url}) - except KeyError as e: - log.warning("Missing field in lists_directory_json: {msg}".format(msg=e)) - except Exception as e: - log.warning("Can't decode lists directory: {msg}".format(msg=e)) - - -def on_data_post(self, request): - jid_str = self.getPostedData(request, "jid") - try: - jid_ = jid.JID(jid_str) - except RuntimeError: - self.pageError(request, C.HTTP_BAD_REQUEST) - # for now we just use default node - url = self.getPageByName("lists").getURL(jid_.full(), "@") - self.HTTPRedirect(request, url) diff -r 83be300d17e3 -r a84383c659b4 libervia/pages/lists/edit/page_meta.py --- a/libervia/pages/lists/edit/page_meta.py Sat Feb 20 13:58:42 2021 +0100 +++ b/libervia/pages/lists/edit/page_meta.py Sat Feb 20 14:07:22 2021 +0100 @@ -16,24 +16,20 @@ def parse_url(self, request): - try: - item_id = self.nextPath(request) - except IndexError: + self.getPathArgs(request, ["service", "node", "item_id"], service="jid", node="@") + data = self.getRData(request) + if data["item_id"] is None: log.warning(_("no list item id specified")) self.pageError(request, C.HTTP_BAD_REQUEST) - data = self.getRData(request) - data["list_item_id"] = item_id - - @defer.inlineCallbacks def prepare_render(self, request): data = self.getRData(request) template_data = request.template_data - service, node, list_item_id = ( + service, node, item_id = ( data.get("service", ""), data.get("node", ""), - data["list_item_id"], + data["item_id"], ) profile = self.getProfile(request) @@ -53,7 +49,7 @@ service.full() if service else "", node, C.NO_LIMIT, - [list_item_id], + [item_id], "", {}, profile, @@ -87,7 +83,7 @@ data = self.getRData(request) service = data["service"] node = data["node"] - list_item_id = data["list_item_id"] + item_id = data["item_id"] posted_data = self.getAllPostedData(request) if not posted_data["title"] or not posted_data["body"]: self.pageError(request, C.HTTP_BAD_REQUEST) @@ -106,7 +102,7 @@ extra = {'update': True} yield self.host.bridgeCall( - "listSet", service.full(), node, posted_data, "", list_item_id, + "listSet", service.full(), node, posted_data, "", item_id, data_format.serialise(extra), profile ) # we don't want to redirect to edit page on success, but to list overview diff -r 83be300d17e3 -r a84383c659b4 libervia/pages/lists/new/page_meta.py --- a/libervia/pages/lists/new/page_meta.py Sat Feb 20 13:58:42 2021 +0100 +++ b/libervia/pages/lists/new/page_meta.py Sat Feb 20 14:07:22 2021 +0100 @@ -12,6 +12,9 @@ template = "list/create_item.html" +def parse_url(self, request): + self.getPathArgs(request, ["service", "node"], service="jid", node="@") + async def prepare_render(self, request): data = self.getRData(request) template_data = request.template_data @@ -38,13 +41,17 @@ except KeyError: pass - # same as for list_edit, we have to convert for now - wid = xmlui_obj.widgets['body'] - if wid.type == "xhtmlbox": - wid.type = "textbox" - wid.value = await self.host.bridgeCall( - "syntaxConvert", wid.value, C.SYNTAX_XHTML, "markdown", - False, profile) + try: + wid = xmlui_obj.widgets['body'] + except KeyError: + pass + else: + if wid.type == "xhtmlbox": + # same as for list_edit, we have to convert for now + wid.type = "textbox" + wid.value = await self.host.bridgeCall( + "syntaxConvert", wid.value, C.SYNTAX_XHTML, "markdown", + False, profile) template_data["new_list_item_xmlui"] = xmlui_obj @@ -53,7 +60,8 @@ service = data["service"] node = data["node"] posted_data = self.getAllPostedData(request) - if not posted_data["title"] or not posted_data["body"]: + if (("title" in posted_data and not posted_data["title"]) + or ("body" in posted_data and not posted_data["body"])): self.pageError(request, C.HTTP_BAD_REQUEST) try: posted_data["labels"] = [l.strip() for l in posted_data["labels"][0].split(",")] @@ -62,10 +70,11 @@ profile = self.getProfile(request) # we convert back body to XHTML - body = await self.host.bridgeCall( - "syntaxConvert", posted_data['body'][0], "markdown", C.SYNTAX_XHTML, - False, profile) - posted_data['body'] = ['
{body}
'.format(ns=C.NS_XHTML, + if "body" in posted_data: + body = await self.host.bridgeCall( + "syntaxConvert", posted_data['body'][0], "markdown", C.SYNTAX_XHTML, + False, profile) + posted_data['body'] = ['
{body}
'.format(ns=C.NS_XHTML, body=body)] diff -r 83be300d17e3 -r a84383c659b4 libervia/pages/lists/page_meta.py --- a/libervia/pages/lists/page_meta.py Sat Feb 20 13:58:42 2021 +0100 +++ b/libervia/pages/lists/page_meta.py Sat Feb 20 14:07:22 2021 +0100 @@ -1,58 +1,69 @@ #!/usr/bin/env python3 from libervia.server.constants import Const as C -from sat.tools.common import template_xmlui -from sat.tools.common import data_objects +from twisted.words.protocols.jabber import jid +from sat.core.i18n import _ +from sat.core.log import getLogger from sat.tools.common import data_format -from sat.core.log import getLogger log = getLogger(__name__) -name = "lists" +name = "lists_disco" access = C.PAGES_ACCESS_PUBLIC -template = "list/overview.html" - - -def parse_url(self, request): - self.getPathArgs(request, ["service", "node"], service="jid") - data = self.getRData(request) - service, node = data["service"], data["node"] - if node is None: - self.pageRedirect("lists_disco", request) - if node == "@": - node = data["node"] = "" - template_data = request.template_data - template_data["url_list_items"] = self.getURL(service.full(), node or "@") - template_data["url_list_new"] = self.getSubPageURL(request, "list_new") - +template = "list/discover.html" async def prepare_render(self, request): - data = self.getRData(request) + profile = self.getProfile(request) template_data = request.template_data - service, node = data["service"], data["node"] - profile = self.getProfile(request) or C.SERVICE_PROFILE + template_data["url_list_create"] = self.getPageByName("list_create").url + lists_directory_config = self.host.options["lists_directory_json"] + lists_directory = request.template_data["lists_directory"] = [] - self.checkCache(request, C.CACHE_PUBSUB, service=service, node=node, short="tickets") - - extra = self.getPubsubExtra(request) - extra["labels_as_list"] = C.BOOL_TRUE - self.handleSearch(request, extra) + if lists_directory_config: + try: + for list_data in lists_directory_config: + service = list_data["service"] + node = list_data["node"] + name = list_data["name"] + url = self.getPageByName("lists").getURL(service, node) + lists_directory.append({"name": name, "url": url}) + except KeyError as e: + log.warning("Missing field in lists_directory_json: {msg}".format(msg=e)) + except Exception as e: + log.warning("Can't decode lists directory: {msg}".format(msg=e)) - list_raw = await self.host.bridgeCall( - "listGet", - service.full() if service else "", - node, - C.NO_LIMIT, - [], - "", - extra, - profile, - ) - list_items, metadata = data_format.deserialise(list_raw, type_check=list) - template_data["list_items"] = [ - template_xmlui.create(self.host, x) for x in list_items - ] - template_data["on_list_item_click"] = data_objects.OnClick( - url=self.getSubPageURL(request, "list_view") + "/{item.id}" - ) - self.setPagination(request, metadata) + if profile is not None: + try: + lists_list_raw = await self.host.bridgeCall("listsList", "", "", profile) + except Exception as e: + log.warning( + _("Can't get list of registered lists for {profile}: {reason}") + .format(profile=profile, reason=e) + ) + else: + lists_list = data_format.deserialise(lists_list_raw, type_check=list) + for list_data in lists_list: + service = list_data["service"] + node = list_data["node"] + list_data["url"] = self.getPageByName("lists").getURL(service, node) + lists_directory.append(list_data) + + icons_names = set() + for list_data in lists_directory: + try: + icons_names.add(list_data['icon_name']) + except KeyError: + pass + if icons_names: + template_data["icons_names"] = icons_names + + +def on_data_post(self, request): + jid_str = self.getPostedData(request, "jid") + try: + jid_ = jid.JID(jid_str) + except RuntimeError: + self.pageError(request, C.HTTP_BAD_REQUEST) + # for now we just use default node + url = self.getPageByName("lists").getURL(jid_.full(), "@") + self.HTTPRedirect(request, url) diff -r 83be300d17e3 -r a84383c659b4 libervia/pages/lists/view/_browser/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libervia/pages/lists/view/_browser/__init__.py Sat Feb 20 14:07:22 2021 +0100 @@ -0,0 +1,31 @@ +from browser import window, bind +from invitation import InvitationManager + + +lists_ns = window.lists_ns +pubsub_service = window.pubsub_service +pubsub_node = window.pubsub_node +try: + affiliations = window.affiliations.to_dict() +except AttributeError: + pass + +@bind("#button_manage", "click") +def manage_click(evt): + evt.stopPropagation() + evt.preventDefault() + pubsub_data = { + "namespace": lists_ns, + "service": pubsub_service, + "node": pubsub_node + } + try: + name = pubsub_node.split('_', 1)[1] + except IndexError: + pass + else: + name = name.strip() + if name: + pubsub_data['name'] = name + manager = InvitationManager("pubsub", pubsub_data) + manager.attach(affiliations=affiliations) diff -r 83be300d17e3 -r a84383c659b4 libervia/pages/lists/view/page_meta.py --- a/libervia/pages/lists/view/page_meta.py Sat Feb 20 13:58:42 2021 +0100 +++ b/libervia/pages/lists/view/page_meta.py Sat Feb 20 14:07:22 2021 +0100 @@ -1,103 +1,89 @@ #!/usr/bin/env python3 - -from libervia.server.constants import Const as C -from sat.core.i18n import _ -from libervia.server.utils import SubPage -from libervia.server import session_iface -from twisted.words.protocols.jabber import jid from sat.tools.common import template_xmlui -from sat.tools.common import uri +from sat.tools.common import data_objects from sat.tools.common import data_format from sat.core.log import getLogger +from sat_frontends.bridge.bridge_frontend import BridgeException +from libervia.server.constants import Const as C log = getLogger(__name__) - -name = "list_view" +name = "lists" access = C.PAGES_ACCESS_PUBLIC -template = "list/item.html" +template = "list/overview.html" def parse_url(self, request): - try: - item_id = self.nextPath(request) - except IndexError: - log.warning(_("no list item id specified")) - self.pageError(request, C.HTTP_BAD_REQUEST) - + self.getPathArgs(request, ["service", "node"], service="jid") data = self.getRData(request) - data["list_item_id"] = item_id + service, node = data["service"], data["node"] + if node is None: + self.HTTPRedirect(request, self.getPageByName("lists_disco").url) + if node == "@": + node = data["node"] = "" + template_data = request.template_data + template_data["url_list_items"] = self.getURL(service.full(), node or "@") + template_data["url_list_new"] = self.getPageByName("list_new").getURL( + service.full(), node or "@") async def prepare_render(self, request): data = self.getRData(request) template_data = request.template_data - session = self.host.getSessionData(request, session_iface.ISATSession) - service, node, list_item_id = ( - data.get("service", ""), - data.get("node", ""), - data["list_item_id"], - ) - profile = self.getProfile(request) + service, node = data["service"], data["node"] + profile = self.getProfile(request) or C.SERVICE_PROFILE - if profile is None: - profile = C.SERVICE_PROFILE + self.checkCache(request, C.CACHE_PUBSUB, service=service, node=node, short="tickets") + + extra = self.getPubsubExtra(request) + extra["labels_as_list"] = C.BOOL_TRUE + self.handleSearch(request, extra) list_raw = await self.host.bridgeCall( "listGet", service.full() if service else "", node, C.NO_LIMIT, - [list_item_id], + [], "", - {"labels_as_list": C.BOOL_TRUE}, + extra, profile, ) - list_items, metadata = data_format.deserialise(list_raw, type_check=list) - list_item = [template_xmlui.create(self.host, x) for x in list_items][0] - template_data["item"] = list_item - comments_uri = list_item.widgets["comments_uri"].value - if comments_uri: - uri_data = uri.parseXMPPUri(comments_uri) - template_data["comments_node"] = comments_node = uri_data["node"] - template_data["comments_service"] = comments_service = uri_data["path"] - comments = data_format.deserialise(await self.host.bridgeCall( - "mbGet", comments_service, comments_node, C.NO_LIMIT, [], {}, profile - )) - - template_data["comments"] = comments - template_data["login_url"] = self.getPageRedirectURL(request) + if profile != C.SERVICE_PROFILE: + try: + affiliations = await self.host.bridgeCall( + "psNodeAffiliationsGet", + service.full() if service else "", + node, + profile + ) + except BridgeException as e: + log.warning(f"Can't get affiliations for node {node!r} at {service}: {e}") + template_data["owner"] = False + else: + is_owner = affiliations.get(self.getJid(request).userhost()) == 'owner' + template_data["owner"] = is_owner + if is_owner: + self.exposeToScripts( + request, + affiliations={str(e): str(a) for e, a in affiliations.items()} + ) + else: + template_data["owner"] = False - if session.connected: - # we set edition URL only if user is the publisher or the node owner - publisher = jid.JID(list_item.widgets["publisher"].value) - is_publisher = publisher.userhostJID() == session.jid.userhostJID() - affiliation = None - if not is_publisher: - node = node or self.host.ns_map["tickets"] - affiliation = await self.host.getAffiliation(request, service, node) - if is_publisher or affiliation == "owner": - template_data["url_list_item_edit"] = self.getURLByPath( - SubPage("lists"), - service.full(), - node or "@", - SubPage("list_edit"), - list_item_id, - ) - - # we add xmpp: URI - uri_args = {'path': service.full()} - uri_args['node'] = node or self.host.ns_map["tickets"] - if list_item_id: - uri_args['item'] = list_item_id - template_data['xmpp_uri'] = uri.buildXMPPUri('pubsub', **uri_args) - - -async def on_data_post(self, request): - type_ = self.getPostedData(request, "type") - if type_ == "comment": - blog_page = self.getPageByName("blog_view") - await blog_page.on_data_post(self, request) - else: - log.warning(_("Unhandled data type: {}").format(type_)) + list_items, metadata = data_format.deserialise(list_raw, type_check=list) + template_data["list_items"] = [ + template_xmlui.create(self.host, x) for x in list_items + ] + view_url = self.getPageByName('list_view').getURL(service.full(), node or '@') + template_data["on_list_item_click"] = data_objects.OnClick( + url=f"{view_url}/{{item.id}}" + ) + self.setPagination(request, metadata) + self.exposeToScripts( + request, + lists_ns=self.host.ns_map["tickets"], + pubsub_service=service.full(), + pubsub_node=node, + ) diff -r 83be300d17e3 -r a84383c659b4 libervia/pages/lists/view_item/page_meta.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libervia/pages/lists/view_item/page_meta.py Sat Feb 20 14:07:22 2021 +0100 @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +from twisted.words.protocols.jabber import jid +from sat.core.i18n import _ +from sat.tools.common import template_xmlui +from sat.tools.common import uri +from sat.tools.common import data_format +from sat.core.log import getLogger +from sat_frontends.bridge.bridge_frontend import BridgeException +from libervia.server.constants import Const as C +from libervia.server.utils import SubPage +from libervia.server import session_iface + +log = getLogger(__name__) + + +name = "list_view" +access = C.PAGES_ACCESS_PUBLIC +template = "list/item.html" + + +def parse_url(self, request): + self.getPathArgs(request, ["service", "node", "item_id"], service="jid", node="@") + data = self.getRData(request) + if data["item_id"] is None: + log.warning(_("no list item id specified")) + self.pageError(request, C.HTTP_BAD_REQUEST) + +async def prepare_render(self, request): + data = self.getRData(request) + template_data = request.template_data + session = self.host.getSessionData(request, session_iface.ISATSession) + service, node, item_id = ( + data.get("service", ""), + data.get("node", ""), + data["item_id"], + ) + profile = self.getProfile(request) + + if profile is None: + profile = C.SERVICE_PROFILE + + list_raw = await self.host.bridgeCall( + "listGet", + service.full() if service else "", + node, + C.NO_LIMIT, + [item_id], + "", + {"labels_as_list": C.BOOL_TRUE}, + profile, + ) + list_items, metadata = data_format.deserialise(list_raw, type_check=list) + list_item = [template_xmlui.create(self.host, x) for x in list_items][0] + template_data["item"] = list_item + try: + comments_uri = list_item.widgets["comments_uri"].value + except KeyError: + pass + else: + if comments_uri: + uri_data = uri.parseXMPPUri(comments_uri) + template_data["comments_node"] = comments_node = uri_data["node"] + template_data["comments_service"] = comments_service = uri_data["path"] + try: + comments = data_format.deserialise(await self.host.bridgeCall( + "mbGet", comments_service, comments_node, C.NO_LIMIT, [], {}, profile + )) + except BridgeException as e: + if e.classname == 'NotFound' or e.condition == 'item-not-found': + log.warning( + _("Can't find comment node at [{service}] {node!r}") + .format(service=comments_service, node=comments_node) + ) + else: + raise e + else: + template_data["comments"] = comments + template_data["login_url"] = self.getPageRedirectURL(request) + self.exposeToScripts( + request, + comments_node=comments_node, + comments_service=comments_service, + ) + + if session.connected: + # we activate modification action (edit, delete) only if user is the publisher or + # the node owner + publisher = jid.JID(list_item.widgets["publisher"].value) + is_publisher = publisher.userhostJID() == session.jid.userhostJID() + affiliation = None + if not is_publisher: + node = node or self.host.ns_map["tickets"] + affiliation = await self.host.getAffiliation(request, service, node) + if is_publisher or affiliation == "owner": + self.exposeToScripts( + request, + pubsub_service = service.full(), + pubsub_node = node, + pubsub_item = item_id, + ) + template_data["can_modify"] = True + template_data["url_list_item_edit"] = self.getURLByPath( + SubPage("list_edit"), + service.full(), + node or "@", + item_id, + ) + + # we add xmpp: URI + uri_args = {'path': service.full()} + uri_args['node'] = node or self.host.ns_map["tickets"] + if item_id: + uri_args['item'] = item_id + template_data['xmpp_uri'] = uri.buildXMPPUri('pubsub', **uri_args) + + +async def on_data_post(self, request): + type_ = self.getPostedData(request, "type") + if type_ == "comment": + blog_page = self.getPageByName("blog_view") + await blog_page.on_data_post(self, request) + else: + log.warning(_("Unhandled data type: {}").format(type_))