# HG changeset patch # User Goffi # Date 1619887077 -7200 # Node ID 02eec2a5b5f9b514793fa4e318e176f6f032edfe # Parent b258dce27d6d30ec4d9c3d3c3a3954ccb9199dcb plugin XEP-0060, XEP-0277: new rename methods (`psItemRename`, `mbRename`): Those methods recreate an item to given new ID, and if successful, delete the former one. diff -r b258dce27d6d -r 02eec2a5b5f9 sat/plugins/plugin_xep_0060.py --- a/sat/plugins/plugin_xep_0060.py Sat May 01 18:36:13 2021 +0200 +++ b/sat/plugins/plugin_xep_0060.py Sat May 01 18:37:57 2021 +0200 @@ -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 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 @@ -233,6 +235,14 @@ async_=True, ) host.bridge.addMethod( + "psItemRename", + ".plugin", + in_sign="sssss", + out_sign="", + method=self._renameItem, + async_=True, + ) + host.bridge.addMethod( "psSubscribe", ".plugin", in_sign="ssa{ss}s", @@ -1031,26 +1041,61 @@ 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, + ): + 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) - return client.pubsub_client.retractItems( - service, nodeIdentifier, itemIdentifiers, notify=True - ) + 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) diff -r b258dce27d6d -r 02eec2a5b5f9 sat/plugins/plugin_xep_0277.py --- a/sat/plugins/plugin_xep_0277.py Sat May 01 18:36:13 2021 +0200 +++ b/sat/plugins/plugin_xep_0277.py Sat May 01 18:37:57 2021 +0200 @@ -124,6 +124,14 @@ async_=True, ) host.bridge.addMethod( + "mbRename", + ".plugin", + in_sign="sssss", + out_sign="", + method=self._mbRename, + async_=True, + ) + host.bridge.addMethod( "mbSetAccess", ".plugin", in_sign="ss", @@ -813,7 +821,14 @@ 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 @@ 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