Changes for page MentionsMacro

on 2020-06-29 20:57

on 2023-11-02 09:50
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 uniquen ess oftheanchorisgivenbytwomechanisms:26 - * - retrieve on firstcallall mentions available oncurrentdocument and store them27 - * - thenuse that information tocomputeanext anchornotoverlappinganexistingone onthecurrentdocument.28 - * Themechanism isobviouslynot perfectand might beimproved laterbut shouldbenough for mostusage.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