changeset 3502:6132d363f0e9

plugin XEP-0277: user friendly ID: when publishing a blog post without `id` specified, instead of using uuid, a user friendly ID based on title or content is used. This allow to have better URLs in web frontend. There is a (very low) risk of name collision if several articles with the same title or content are published on the same pubsub node, user friendly ID can be deactivated by using `user_friendly_id=False` in blog data to avoid that.
author Goffi <>
date Fri, 16 Apr 2021 18:33:00 +0200
parents 85b8a899f407
children 482098e521ff
files sat/plugins/
diffstat 1 files changed, 15 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/sat/plugins/	Fri Apr 16 18:32:44 2021 +0200
+++ b/sat/plugins/	Fri Apr 16 18:33:00 2021 +0200
@@ -16,14 +16,16 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <>.
-import shortuuid
 import time
 import dateutil
 import calendar
 import urllib.parse
+from secrets import token_urlsafe
 from typing import Optional
 from functools import partial
+import shortuuid
 from twisted.words.protocols.jabber import jid, error
 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
 from twisted.words.xish import domish
@@ -45,6 +47,7 @@
 from import utils
 from import data_format
 from import uri as xmpp_uri
+from import regex
 log = getLogger(__name__)
@@ -807,6 +810,11 @@
         data = data_format.deserialise(data)
         return self.send(client, data, service, node)
+    def friendlyId(self, data):
+        """Generate a user friendly id from title or content"""
+        id_base = regex.urlFriendlyText(data.get('title') or data.get('content', ''))
+        return f"{id_base}-{token_urlsafe(3)}"
     def send(self, client, data, service=None, node=NS_MICROBLOG):
         """Send XEP-0277's microblog data
@@ -823,7 +831,12 @@
         if node is None:
             node = NS_MICROBLOG
-        item_id = data.get("id") or str(shortuuid.uuid())
+        item_id = data.get("id")
+        if item_id is None:
+            if data.get("user_friendly_id", True):
+                item_id = self.friendlyId(data)
+            else:
+                item_id = str(shortuuid.uuid())
             yield self._manageComments(client, data, service, node, item_id, access=None)