Changes for page MentionsMacro

Last modified by Drunk Monkey on 2023-11-02 09:50

From version 1.1
edited by Drunk Monkey
on 2020-06-29 20:57
Change comment: Install extension [org.xwiki.platform:xwiki-platform-mentions-ui/12.5]
To version 19.1
edited by Drunk Monkey
on 2023-11-02 09:50
Change comment: Install extension [org.xwiki.platform:xwiki-platform-mentions-ui/15.9]

Summary

Details

XWiki.JavaScriptExtension[0]
Code
... ... @@ -1,57 +1,37 @@
1 1  require.config({
2 2   paths: {
3 - 'xwiki-suggestUsers': "$xwiki.getSkinFile('uicomponents/suggest/suggestUsersAndGroups.js', true))" +
4 - "?v=$escapetool.url($xwiki.version)"
3 + 'xwiki-suggestUsers': $jsontool.serialize($xwiki.getSkinFile('uicomponents/suggest/suggestUsersAndGroups.js'))
5 5   }
6 6  });
7 -require(['deferred!ckeditor', 'xwiki-suggestUsers', 'jquery'], function (ckeditorPromise, suggestUsers, $) {
8 - /*
9 - * Keep records of the added anchors during the current edit session.
10 - * Note that the anchors saved here are not only the ones added on the current session,
11 - * but also those already present in the document.
12 - * For more information, see #getAnchor.
13 - * The information are stored on the form:
14 - * { reference: Array<String> }
15 - */
16 - const anchorIds = {};
17 -
6 +require(['deferred!ckeditor', 'xwiki-suggestUsers', 'jquery', 'xwiki-meta'], function (ckeditorPromise, suggestUsers, $, xm) {
7 +
18 18   /**
19 19   * Get the current wiki scope for displaying global, local or global and local users
20 20   */
21 21   const userScope = "$!services.wiki.user.userScope";
22 -
12 +
13 + // see https://stackoverflow.com/a/6248722/657524
14 + function random6chars() {
15 + // I generate the UID from two parts here
16 + // to ensure the random number provide enough bits.
17 + var firstPart = (Math.random() * 46656) | 0;
18 + var secondPart = (Math.random() * 46656) | 0;
19 + firstPart = ("000" + firstPart.toString(36)).slice(-3);
20 + secondPart = ("000" + secondPart.toString(36)).slice(-3);
21 + return firstPart + secondPart;
22 + }
23 +
23 23   /**
24 24   * Compute a new unique anchor for the given reference.
25 - * The uniqueness of the anchor is given by two mechanisms:
26 - * - retrieve on first call all mentions available on current document and store them
27 - * - then use that information to compute a next anchor not overlapping an existing one on the current document.
28 - * The mechanism is obviously not perfect and might be improved later but should be enough for most usage.
26 + * The unique anchor is based on the mentionned user id, concatenaed with a random string of 6 alphanumeric
27 + * characters.
28 + * The chances of collision are quite low, about 46k mentions for a given mentioned user on a given page (assuming
29 + * that no mentions are ever deleted).
29 29   */
30 30   const getAnchor = function (reference) {
31 - var existingIds;
32 -
33 - if (anchorIds.hasOwnProperty(reference)) {
34 - existingIds = anchorIds[reference];
35 - } else {
36 - existingIds = [];
37 - $('.xwiki-mention').each(function() {
38 - var mention = $(this);
39 - if (mention.attr('data-reference') === reference) {
40 - existingIds.push(mention.attr('id'));
41 - }
42 - });
43 - anchorIds[reference] = existingIds;
44 - }
45 -
46 - var counter = existingIds.length + 1;
47 - var refId = reference.replace(/[.:]/g, '-');
48 - var proposedAnchor = refId + "-" + counter;
49 - while (existingIds.indexOf(proposedAnchor) != -1) {
50 - counter++;
51 - proposedAnchor = refId + "-" + counter;
52 - }
53 - anchorIds[reference].push(proposedAnchor);
54 - return proposedAnchor;
32 + const refId = reference.replace(/[.:]/g, '-');
33 + const randomId = random6chars();
34 + return refId + '-' + randomId;
55 55   };
56 56  
57 57   const search = function (text, callback) {
... ... @@ -59,8 +59,8 @@
59 59   'input': text,
60 60   'limit': 6,
61 61   };
62 - $.when(suggestUsers.loadUsers(userScope, params)).then(function (user) {
63 - const cct = user.map(function (x) {
42 + suggestUsers.loadUsers(userScope, params).then(users => {
43 + const cct = users.map(function (x) {
64 64   // insert an id because that's required by the mentions plugins.
65 65   x.id = x.value;
66 66   // Make sure to display the icon avatar or the image one.
... ... @@ -76,11 +76,11 @@
76 76   return x;
77 77   });
78 78   callback(cct);
79 - })
59 + });
80 80   }
81 81  
82 - ckeditorPromise.done(function (ckeditor) {
83 - function confMentions(name) {
62 + ckeditorPromise.then(ckeditor => {
63 + function getUserMentionsConfig(editor) {
84 84   return {
85 85   feed: function (opts, callback) {
86 86   search(opts.query, callback);
... ... @@ -88,21 +88,21 @@
88 88   marker: '@',
89 89   minChars: 0,
90 90   itemsLimit: 6,
91 - itemTemplate: '<li data-id="{id}" class="ckeditor-autocomplete-item">'+
92 - '<div>'+
93 - '<span class="ckeditor-autocomplete-item-icon-wrapper">'+
94 - '<span class="{cssClass}"></span>'+
95 - '<img src="{imgUrl}" class="{imgClass}"/>'+
96 - '</span>'+
97 - '<span class="ckeditor-autocomplete-item-label">{label}</span>'+
98 - '</div>'+
99 - '</li>',
71 + itemTemplate:
72 + `<li data-id="{id}" class="ckeditor-autocomplete-item">
73 + <div>
74 + <span class="ckeditor-autocomplete-item-icon-wrapper">
75 + <span class="{cssClass}"></span>
76 + <img src="{imgUrl}" class="{imgClass}"/>
77 + </span>
78 + <span class="ckeditor-autocomplete-item-label">{label}</span>
79 + </div>
80 + </li>`,
100 100   outputTemplate: function (param) {
101 - var editor = ckeditor.instances[name];
102 - editor.once('afterInsertHtml', function () {
82 + editor.once('afterInsertHtml', function() {
103 103   editor.execCommand('xwiki-macro-insert', {
104 104   name: 'mention',
105 - inline: true,
85 + inline: 'enforce',
106 106   parameters: {
107 107   reference: param.id,
108 108   style: 'FULL_NAME',
... ... @@ -117,22 +117,35 @@
117 117   };
118 118   }
119 119  
120 - function updateConf(config, name) {
121 - const newConf = config;
122 - newConf.mentions = newConf.mentions || [];
123 - newConf.mentions.push(confMentions(name));
124 - return newConf;
100 + // Enable the user mentions for the CKEditor instances that have been already created.
101 + Object.values(ckeditor.instances).forEach(maybeEnableUserMentions);
102 + // Enable the user mentions for the CKEditor instances that are going to be created from now on.
103 + ckeditor.on('instanceCreated', (event) => {
104 + maybeEnableUserMentions(event.editor);
105 + });
106 +
107 + function maybeEnableUserMentions(editor) {
108 + return waitForEditorReady(editor).then((editor) => {
109 + // Check if the Mentions plugin is enabled for the given editor instance.
110 + // TODO: Add support for disabling the user mentions for a particular editor instance (without disabling all
111 + // types of mentions).
112 + if (editor.plugins.mentions) {
113 + editor.plugins.mentions.instances.push(new ckeditor.plugins.mentions(editor, getUserMentionsConfig(editor)));
114 + }
115 + return editor;
116 + });
125 125   }
126 126  
127 - var oldReplace = ckeditor.replace;
128 - ckeditor.replace = function (element, config) {
129 - return oldReplace.call(this, element, updateConf(config, element.id));
130 - };
131 -
132 - var oldInline = ckeditor.inline;
133 - ckeditor.inline = function (element, config) {
134 - return oldInline.call(this, element, updateConf(config, element.id));
135 - };
119 + function waitForEditorReady(editor) {
120 + return new Promise((resolve, reject) => {
121 + if (editor.status === 'ready') {
122 + resolve(editor);
123 + } else {
124 + editor.once('instanceReady', (event) => {
125 + resolve(event.editor);
126 + });
127 + }
128 + });
129 + }
136 136   });
137 137  });
138 -
XWiki.StyleSheetExtension[0]
Code
... ... @@ -1,6 +1,6 @@
1 1  .xwiki-mention {
2 2   background-color: $services.mentions.mentionsColor;
3 - border-radius: 8px;
3 + border-radius: 10px;
4 4   padding: 2px 5px 2px 5px;
5 5  }
6 6  
... ... @@ -11,3 +11,7 @@
11 11  .xwiki-mention.removed {
12 12   text-decoration: line-through;
13 13  }
14 +
15 +blockquote.mention-quote {
16 + font-size: inherit;
17 +}
XWiki.WikiMacroClass[0]
Macro code
... ... @@ -1,26 +1,18 @@
1 1  {{velocity}}
2 2  #set ($reference = $wikimacro.parameters.reference)
3 3  #set ($style = $wikimacro.parameters.style)
4 +#set ($type = "$!wikimacro.parameters.type")
5 +#set ($content = $services.mentions.format($reference.reference, $style, $type))
4 4  #set ($anchor = $wikimacro.parameters.anchor)
5 -#set ($userProperties = $services.user.getProperties($reference))
6 -#set ($firstName = $userProperties.getFirstName())
7 -#set ($lastName = $userProperties.getLastName())
8 -#set ($isCurrentUser = $xcontext.userReference == $reference.reference)
7 +#set ($isCurrentUser = $xcontext.userReference == $reference.reference && ($type == '' || $type == 'user'))
9 9  #set ($cssClasses = ['xwiki-mention', 'user'])
10 10  #if ($isCurrentUser)
11 11   #set ($discard = $cssClasses.add('self'))
12 12  #end
13 -#if ("$!firstName" == "")
14 - #set($content = "@$reference.reference.name")
15 -#elseif ($style == 'FIRST_NAME')
16 - #set($content = "@$firstName")
17 -#elseif ($style == 'LOGIN')
18 - #set($content = "@$reference.reference.name")
19 -#else
20 - #set($content = "@$firstName $!lastName")
21 -#end
22 22  #set ($link = $xwiki.getURL($reference.reference, 'view'))
23 23  {{html}}
24 -<a id="$anchor" class="$stringtool.join($cssClasses, ' ')" data-reference="$services.model.serialize($reference.reference, 'compactwiki')" href="$link">$content</a>
14 +<a id="$escapetool.xml($anchor)" class="$stringtool.join($cssClasses, ' ')" data-reference="$escapetool.xml($services.model.serialize($reference.reference, 'default'))" href="$escapetool.xml($link)">##
15 + $escapetool.xml($content)## Do not remove this comment as it ensures that the spacing after mention is not broken.
16 +</a>
25 25  {{/html}}
26 26  {{/velocity}}
Macro description
... ... @@ -1,1 +1,1 @@
1 -Insert a user mention.
1 +Inserts a user mention.
Default categories
... ... @@ -1,0 +1,1 @@
1 +Notifications
XWiki.WikiMacroParameterClass[3]
Parameter name
... ... @@ -1,0 +1,1 @@
1 +type
Parameter description
... ... @@ -1,0 +1,1 @@
1 +The type of mentioned actor.
Parameter type
... ... @@ -1,0 +1,1 @@
1 +java.lang.String