diff --git a/sat/plugins/plugin_xep_0060.py b/sat/plugins/plugin_xep_0060.py index 8c3cc691..baf77fac 100644 --- a/sat/plugins/plugin_xep_0060.py +++ b/sat/plugins/plugin_xep_0060.py @@ -17,6 +17,7 @@ # along with this program. If not, see . +from typing import Optional from collections import namedtuple import urllib.request, urllib.parse, urllib.error from functools import reduce @@ -34,6 +35,7 @@ from wokkel import mam from sat.core.i18n import _ from sat.core.constants import Const as C from sat.core.log import getLogger +from sat.core.xmpp import SatXMPPEntity from sat.core import exceptions from sat.tools import sat_defer from sat.tools import xml_tools @@ -232,6 +234,14 @@ class XEP_0060(object): method=self._retractItems, async_=True, ) + host.bridge.addMethod( + "psItemRename", + ".plugin", + in_sign="sssss", + out_sign="", + method=self._renameItem, + async_=True, + ) host.bridge.addMethod( "psSubscribe", ".plugin", @@ -1031,27 +1041,62 @@ class XEP_0060(object): def _retractItems( self, service_s, nodeIdentifier, itemIdentifiers, notify, profile_key ): + client = self.host.getClient(profile_key) return self.retractItems( + client, jid.JID(service_s) if service_s else None, nodeIdentifier, itemIdentifiers, notify, - profile_key, ) def retractItems( self, + client, service, nodeIdentifier, itemIdentifiers, notify=True, - profile_key=C.PROF_KEY_NONE, ): - client = self.host.getClient(profile_key) return client.pubsub_client.retractItems( service, nodeIdentifier, itemIdentifiers, notify=True ) + def _renameItem( + self, + service, + node, + item_id, + new_id, + profile_key=C.PROF_KEY_NONE, + ): + client = self.host.getClient(profile_key) + service = jid.JID(service) if service else None + return defer.ensureDeferred(self.renameItem( + client, service, node, item_id, new_id + )) + + async def renameItem( + self, + client: SatXMPPEntity, + service: Optional[jid.JID], + node: str, + item_id: str, + new_id: str + ) -> None: + """Rename an item by recreating it then deleting it + + we have to recreate then delete because there is currently no rename operation + with PubSub + """ + if not item_id or not new_id: + raise ValueError("item_id and new_id must not be empty") + # retract must be done last, so if something goes wrong, the exception will stop + # the workflow and no accidental delete should happen + item_elt = (await self.getItems(client, service, node, item_ids=[item_id]))[0][0] + await self.sendItem(client, service, node, item_elt.firstChildElement(), new_id) + await self.retractItems(client, service, node, [item_id]) + def _subscribe(self, service, nodeIdentifier, options, profile_key=C.PROF_KEY_NONE): client = self.host.getClient(profile_key) service = None if not service else jid.JID(service) diff --git a/sat/plugins/plugin_xep_0277.py b/sat/plugins/plugin_xep_0277.py index a4b9cfc6..cd922247 100644 --- a/sat/plugins/plugin_xep_0277.py +++ b/sat/plugins/plugin_xep_0277.py @@ -123,6 +123,14 @@ class XEP_0277(object): method=self._mbGet, async_=True, ) + host.bridge.addMethod( + "mbRename", + ".plugin", + in_sign="sssss", + out_sign="", + method=self._mbRename, + async_=True, + ) host.bridge.addMethod( "mbSetAccess", ".plugin", @@ -813,7 +821,14 @@ class XEP_0277(object): def friendlyId(self, data): """Generate a user friendly id from title or content""" - id_base = regex.urlFriendlyText(data.get('title') or data.get('content', '')) + # TODO: rich content should be converted to plain text + id_base = regex.urlFriendlyText( + data.get('title') + or data.get('title_rich') + or data.get('content') + or data.get('content_rich') + or '' + ) return f"{id_base}-{token_urlsafe(3)}" def _mbSend(self, service, node, data, profile_key): @@ -954,6 +969,27 @@ class XEP_0277(object): items_data, partial(self.item2mbdata, client, service=service, node=node)) defer.returnValue(mb_data) + def _mbRename(self, service, node, item_id, new_id, profile_key): + return defer.ensureDeferred(self.mbRename( + self.host.getClient(profile_key), + jid.JID(service) if service else None, + node or None, + item_id, + new_id + )) + + async def mbRename( + self, + client: SatXMPPEntity, + service: Optional[jid.JID], + node: Optional[str], + item_id: str, + new_id: str + ) -> None: + if not node: + node = NS_MICROBLOG + await self._p.renameItem(client, service, node, item_id, new_id) + def parseCommentUrl(self, node_url): """Parse a XMPP URI