'mentioning' users inside text

I got this question from my customer:

can we use the "@" symbol to mention users inside text, so these users get notified.

About 'how to notify them', I got that covered ( medialog.notifications add-on ), but how difficult would it be to do the rest.

I assume I would need to make an indexer that 'looks for every '@name' in the bodytext (or in comments / discussions) . But: how do I get 'suggestions', in other words, when someone starts typing '@usern', '@username' is suggested.

This could be achieved with a custom autocompleter as documented here: Autocompleter | TinyMCE Documentation .. the options list should be a BrowserView returning the valid usernames.

Also add a check that the '@' have at least a space before it, so you don't get suggestions on emails or quoting '@' (like Discourse does here!)

THANK YOU, that works.
I made a restapi endpoint instead. Dont know it it matters, but maybe it will be better 'in the future' (with Volto, maybe?).
I post the code here in case it should be of interest to someone:


tinymce.PluginManager.add('mentions_autocomplete', function(editor) {
const portalUrl = document.body.getAttribute('data-portal-url') || '';
const mentionsUrl = portalUrl + '/@mentions_view';

editor.ui.registry.addAutocompleter('mentions', {
  trigger: '@',
  minChars: 1,
  columns: 1,
  fetch: function (pattern) {
    return fetch(mentionsUrl, {
      headers: {
        'Accept': 'application/json'
      }
    })
      .then(res => res.json())
      .then(users => {
        return users
          .filter(u => u.key.toLowerCase().includes(pattern.toLowerCase()))
          .map(u => ({
            value: '@' + u.value,
            text: u.key
          }));
      });
  },
  onAction: function (autocompleteApi, rng, value) {
    editor.selection.setRng(rng);
    editor.insertContent(value + ' ');
    autocompleteApi.hide();
  }
});

});


# -*- coding: utf-8 -*-
from plone.restapi.interfaces import IExpandableElement
from zope.interface import implementer
from zope.component import adapter
from zope.interface import Interface
from Products.CMFPlone.utils import safe_unicode
from plone.restapi.services import Service
from plone import api

@implementer(IExpandableElement)
@adapter(Interface, Interface)
class MentionsView(object):

    def __init__(self, context, request):
        self.context = context.aq_explicit
        self.request = request

    def __call__(self, expand=False):
        if not expand:
            return {
                'mentions_view': {
                    '@id': f"{self.context.absolute_url()}/@mentions_view"
                }
            }

        users = api.user.get_users()
        return [
            {
                "key": safe_unicode(user.getProperty('fullname') or user.getUserName()),
                "value": user.getUserName()
            }
            for user in users
        ]



class MentionsViewGet(Service):

    # def reply(self):
    #     service_factory = MentionsView(self.context, self.request)
    #     return service_factory(expand=True)['mentions_view']
    
    def reply(self):
        service_factory = MentionsView(self.context, self.request)
        return service_factory(expand=True)  # ✅ already a list, no ['mentions_view']
1 Like

Looks like I dont need to add any, it worked 'like that already'.

1 Like