Modifications pour le document AdminUsersSheet

Modifié par Florent Charton le 2025/08/19 15:15

Depuis la version 4.1
modifié par Florent Charton
sur 2025/08/19 15:15
Commentaire de modification : Install extension [org.xwiki.platform:xwiki-platform-administration-ui/17.4.3]
À la version 1.1
modifié par superadmin
sur 2022/06/20 08:48
Commentaire de modification : Install extension [org.xwiki.platform:xwiki-platform-administration-ui/13.10.6]

Résumé

Détails

Propriétés de la Page
Auteur du document
... ... @@ -1,1 +1,1 @@
1 -xwiki:XWiki.fcharton
1 +XWiki.superadmin
Syntaxe
... ... @@ -1,1 +1,1 @@
1 -XWiki 2.1
1 +XWiki 2.0
Contenu
... ... @@ -1,88 +1,49 @@
1 1  {{velocity output="false"}}
2 -#macro (displayUsersLiveData)
3 - #set ($properties = ['name', 'first_name', 'last_name', '_actions'])
4 - #set ($sourceParameters = {
5 - 'template': 'getusers.vm',
6 - 'translationPrefix': 'xe.admin.users.'
2 +#macro (userScopeFilter)
3 + <select name="wiki">
4 + <option selected="selected" value="local">
5 + $escapetool.xml($services.localization.render('rightsmanager.local'))
6 + </option>
7 + <option value="global">
8 + $escapetool.xml($services.localization.render('rightsmanager.global'))
9 + </option>
10 + <option value="both">
11 + $escapetool.xml($services.localization.render('rightsmanager.both'))
12 + </option>
13 + </select>
14 +#end
15 +
16 +#macro (displayUsersLiveTable)
17 + #set ($columnOptions = {
18 + 'name': {'type': 'text', 'sortable': false, 'html': true},
19 + 'first_name': {'type': 'text', 'sortable': false},
20 + 'last_name': {'type': 'text', 'sortable': false},
21 + 'scope': {'type': 'list', 'sortable': false},
22 + '_actions': {
23 + 'actions': [
24 + 'edit',
25 + {'id': 'disable', 'icon': 'lock'},
26 + {'id': 'enable', 'icon': 'unlock'},
27 + 'delete'
28 + ],
29 + 'labels': false
30 + }
7 7   })
32 + #set ($columns = ['name', 'first_name', 'last_name', '_actions'])
33 + #set ($liveTableOptions = {
34 + 'url': $doc.getURL('view', 'xpage=getusers'),
35 + 'translationPrefix': 'xe.admin.users.',
36 + 'outputOnlyHtml': true
37 + })
8 8   #if (!$xcontext.isMainWiki())
9 - #set ($discard = $properties.add(3, 'scope'))
39 + #set ($discard = $columns.add(3, 'scope'))
40 + ## We use the top filters option to show only the local users by default because the JavaScript code from the sheet
41 + ## is executed after the livetable is loaded. The JavaScript code removes the top filters and updates the scope filter
42 + ## afterwards.
43 + #set ($liveTableOptions.topFilters = "#userScopeFilter")
10 10   #end
11 11   <div class="medium-avatars">
12 - $services.liveData.render({
13 - 'id': 'userstable',
14 - 'source': 'liveTable',
15 - 'properties': $stringtool.join($properties, ','),
16 - 'sourceParameters': $escapetool.url($sourceParameters)
17 - }, {
18 - 'query': {
19 - 'filters': [
20 - {
21 - 'property': 'scope',
22 - 'constraints': [{
23 - 'operator': 'contains',
24 - 'value': 'local'
25 - }]
26 - }
27 - ]
28 - },
29 - 'meta': {
30 - 'propertyDescriptors': [
31 - {
32 - 'id': 'name',
33 - 'displayer': 'html',
34 - 'sortable': false,
35 - 'editable': false
36 - },
37 - {
38 - 'id': 'first_name',
39 - 'sortable': false,
40 - 'editable': false
41 - },
42 - {
43 - 'id': 'last_name',
44 - 'sortable': false,
45 - 'editable': false
46 - },
47 - {
48 - 'id': 'scope',
49 - 'sortable': false,
50 - 'editable': false,
51 - 'filter': {
52 - 'id': 'list',
53 - 'options': [
54 - {'value': 'local', 'label': $services.localization.render('rightsmanager.local')},
55 - {'value': 'global', 'label': $services.localization.render('rightsmanager.global')},
56 - {'value': 'both', 'label': $services.localization.render('rightsmanager.both')}
57 - ]
58 - }
59 - },
60 - {
61 - 'id': '_actions',
62 - 'displayer': {
63 - 'id': 'actions',
64 - 'actions': ['edit', 'disable', 'enable', 'delete']
65 - }
66 - }
67 - ],
68 - 'actions': [
69 - {
70 - 'id': 'disable',
71 - 'icon': 'lock',
72 - 'allowProperty': 'doc.hasdisable',
73 - 'urlProperty': 'doc.disable_url',
74 - 'extraIconClasses': 'text-warning'
75 - },
76 - {
77 - 'id': 'enable',
78 - 'icon': 'unlock',
79 - 'allowProperty': 'doc.hasenable',
80 - 'urlProperty': 'doc.enable_url',
81 - 'extraIconClasses': 'text-success'
82 - }
83 - ]
84 - }
85 - })
46 + #livetable('userstable' $columns $columnOptions $liveTableOptions)
86 86   </div>
87 87   <p>
88 88   <button type="button" class="btn btn-primary" data-toggle="modal" data-target="${escapetool.h}createUserModal"
... ... @@ -122,7 +122,7 @@
122 122  
123 123  #macro (editUserModal)
124 124   <div class="modal" id="editUserModal" tabindex="-1" role="dialog" aria-labelledby="editUserModal-label"
125 - data-backdrop="static" data-keyboard="false" data-live-data="#userstable" data-live-data-action="edit">
86 + data-backdrop="static" data-keyboard="false" data-liveTable="#userstable" data-liveTableAction="edit">
126 126   <div class="modal-dialog modal-lg" role="document">
127 127   <div class="modal-content">
128 128   <div class="modal-header">
... ... @@ -152,7 +152,7 @@
152 152   ## have script or programming rights.
153 153   #userPicker_import
154 154   <div class="modal" id="deleteUserModal" tabindex="-1" role="dialog" aria-labelledby="deleteUserModal-label"
155 - data-live-data="#userstable" data-live-data-action="delete">
116 + data-liveTable="#userstable" data-liveTableAction="delete">
156 156   <div class="modal-dialog" role="document">
157 157   <div class="modal-content">
158 158   <div class="modal-header">
... ... @@ -206,13 +206,11 @@
206 206  #macro (maybeShowDeleteUserWarning $userReference $right)
207 207   #countPagesLastModifiedBy($userReference)
208 208   #if ($pageCount > 0)
209 - {{/html}}
210 -
211 - {{error cssClass="xform"}}
212 - {{html}}
170 + <div class="box errormessage xform">
213 213   #set ($pageIndexReference = $services.model.createDocumentReference(
214 214   $userReference.wikiReference.name, 'Main', 'AllDocs'))
215 - #set ($pageIndexURL = $xwiki.getURL($pageIndexReference, 'view', "doc.author=${escapetool.url($services.model.serialize($userReference, 'local'))}"))
173 + #set ($pageIndexURL = $xwiki.getURL($pageIndexReference) + '#|t=alldocs&doc.author=' +
174 + $escapetool.url($services.model.serialize($userReference, 'local')))
216 216   #set ($translationKey = "administration.section.users.deleteUser.${right}RightsWarning")
217 217   $services.localization.render($translationKey, ["<a href='$pageIndexURL'>", $pageCount, '</a>'])
218 218   <dl>
... ... @@ -237,10 +237,7 @@
237 237   [$rightTranslation]))</span>
238 238   </dd>
239 239   </dl>
240 - {{/html}}
241 - {{/error}}
242 -
243 - {{html clean="false"}}
199 + </div>
244 244   #end
245 245  #end
246 246  
... ... @@ -293,10 +293,10 @@
293 293   #set ($discard = $xwiki.jsx.use("XWiki.AdminUsersSheet"))
294 294  
295 295   {{html clean="false"}}
296 - #displayUsersLiveData()
297 - #createUserModal()
298 - #editUserModal()
299 - #deleteUserModal()
252 + #displayUsersLiveTable
253 + #createUserModal
254 + #editUserModal
255 + #deleteUserModal
300 300   {{/html}}
301 301   #end
302 302  #end
XWiki.JavaScriptExtension[0]
Code
... ... @@ -1,10 +1,19 @@
1 1  require.config({
2 2   paths: {
3 - 'xwiki-livedata-modal': new XWiki.Document('AdminGroupsSheet', 'XWiki').getURL('jsx', 'r=1')
3 + 'xwiki-livetable-modal': new XWiki.Document('AdminGroupsSheet', 'XWiki').getURL('jsx', 'r=1')
4 4   }
5 5  });
6 6  
7 -require(['jquery', 'xwiki-meta', 'xwiki-livedata-modal', 'xwiki-events-bridge'], function($, xm) {
7 +//
8 +// Scope Filtering
9 +//
10 +require(['jquery'], function($) {
11 + var options = $('#userstable').prev('.tipfilters').remove().find('select[name="wiki"]').html();
12 + var scopeFilter = $('#userstable .xwiki-livetable-display-filters select[name="scope"]')
13 + scopeFilter.attr('name', 'wiki').html(options);
14 +});
15 +
16 +require(['jquery', 'xwiki-meta', 'xwiki-livetable-modal', 'xwiki-events-bridge'], function($, xm) {
8 8   //
9 9   // User Creation
10 10   //
... ... @@ -11,7 +11,7 @@
11 11   var createUserModal = $('#createUserModal');
12 12  
13 13   var validateCreateUserForm = function(form) {
14 - return form.length && (!window.LiveValidation ||
23 + return form.size() > 0 && (!window.LiveValidation ||
15 15   LiveValidation.massValidate(LiveValidationForm.getInstance(form[0]).fields));
16 16   };
17 17  
... ... @@ -26,7 +26,7 @@
26 26   createUserModalBody.removeClass('loading').append(createUserForm);
27 27   $(document).trigger('xwiki:dom:updated', {'elements': createUserModalBody.toArray()});
28 28   createUserForm.find(':input').filter(':visible').first().focus();
29 - createUserButton.prop('disabled', !createUserForm.length);
38 + createUserButton.prop('disabled', createUserForm.size() === 0);
30 30   });
31 31   }).on('click', '.btn-primary', function(event) {
32 32   var createUserForm = createUserModal.find('form#register');
... ... @@ -38,14 +38,14 @@
38 38   $jsontool.serialize($services.localization.render('xe.admin.users.create.inProgress')),
39 39   'inprogress'
40 40   );
41 - $.post(createUserForm.attr('action'), createUserForm.serialize()).then(html => {
50 + $.post(createUserForm.attr('action'), createUserForm.serialize()).done(function(html) {
42 42   var errorMessage = $('<div/>').html(html).find('.errormessage, .LV_validation_message.LV_invalid');
43 - if (errorMessage.length) {
52 + if (errorMessage.size() > 0) {
44 44   createUserButton.prop('disabled', false);
45 45   notification.replace(new XWiki.widgets.Notification(errorMessage.text(), 'error'));
46 46   } else {
47 47   createUserModal.modal('hide');
48 - $('#userstable').data('liveData').updateEntries();
57 + window.livetable_userstable.refresh();
49 49   $('#userstable').trigger('xwiki:user:created');
50 50   notification.replace(new XWiki.widgets.Notification(
51 51   $jsontool.serialize($services.localization.render('xe.admin.users.create.done')),
... ... @@ -52,7 +52,7 @@
52 52   'done'
53 53   ));
54 54   }
55 - }).catch(() => {
64 + }).fail(function (response) {
56 56   createUserButton.prop('disabled', false);
57 57   notification.replace(new XWiki.widgets.Notification(
58 58   $jsontool.serialize($services.localization.render('xe.admin.users.create.failed')),
... ... @@ -69,7 +69,7 @@
69 69   var loadEditForm = function(forceLock) {
70 70   var saveButton = editUserModal.find('.btn-primary').prop('disabled', true);
71 71   var rowData = editUserModal.data('rowData');
72 - var userReference = XWiki.Model.resolve(rowData['doc.fullName'], XWiki.EntityType.DOCUMENT, [rowData['doc.wiki']]);
81 + var userReference = XWiki.Model.resolve(rowData.doc_fullName, XWiki.EntityType.DOCUMENT, [rowData.doc_wiki]);
73 73   var userDocument = new XWiki.Document(userReference);
74 74   var editUserURL = userDocument.getURL('edit');
75 75   var parameters = {xpage: 'edituser'};
... ... @@ -83,7 +83,7 @@
83 83   $(document).trigger('xwiki:dom:updated', {'elements': self.toArray()});
84 84   var editUserForm = editUserModal.find('form#edituser');
85 85   editUserForm.find(':input').filter(':visible').first().focus();
86 - saveButton.prop('disabled', !editUserForm.length);
95 + saveButton.prop('disabled', editUserForm.size() === 0);
87 87   });
88 88   };
89 89  
... ... @@ -101,14 +101,14 @@
101 101   'inprogress'
102 102   );
103 103   $(document).trigger('xwiki:actions:beforeSave');
104 - $.post(editForm.attr('action'), editForm.serialize()).then(() => {
113 + $.post(editForm.attr('action'), editForm.serialize()).done(function() {
105 105   $(document).trigger('xwiki:document:saved');
106 - editUserModal.modal('hide').data('liveDataElement').data('liveData').updateEntries();
115 + editUserModal.modal('hide').data('liveTable').refresh();
107 107   notification.replace(new XWiki.widgets.Notification(
108 108   $jsontool.serialize($services.localization.render('core.editors.saveandcontinue.notification.done')),
109 109   'done'
110 110   ));
111 - }).catch(response => {
120 + }).fail(function (response) {
112 112   saveButton.prop('disabled', false);
113 113   var message = $jsontool.serialize($services.localization.render('core.editors.saveandcontinue.notification.error',
114 114   ['__reason__']));
... ... @@ -137,32 +137,29 @@
137 137   failed: $jsontool.serialize($services.localization.render('administration.section.users.disableUser.failed'))
138 138   }
139 139   }
140 -
141 - function onToggleUser(action) {
142 - return function (event) {
143 - event.preventDefault();
144 - var actionTrigger = $(this);
145 - if (actionTrigger.hasClass('pending')) {
146 - // Another request is already in progress.
147 - return;
148 - }
149 - actionTrigger.addClass('pending');
150 - var notification = new XWiki.widgets.Notification(notificationMessage[action].inProgress, 'inprogress');
151 - // The enable and disable actions redirect to the user profile by default which may take a lot of time to
152 - // render so we redirect to a URL that doesn't render anything.
153 - var emptyResponseURL = new XWiki.Document('AdminUsersSheet', 'XWiki').getURL('get', 'outputSyntax=plain');
154 - Promise.resolve($.post(actionTrigger.attr('href'), {'xredirect': emptyResponseURL})).then(function () {
155 - $('#userstable').data('liveData').updateEntries();
156 - notification.replace(new XWiki.widgets.Notification(notificationMessage[action].done, 'done'));
157 - }).catch(function () {
158 - notification.replace(new XWiki.widgets.Notification(notificationMessage[action].failed, 'error'));
159 - }).finally(function () {
160 - actionTrigger.removeClass('pending');
161 - });
162 - };
163 - }
164 - $(document).on('click', '#userstable a.action_enable', onToggleUser('enable'));
165 - $(document).on('click', '#userstable a.action_disable', onToggleUser('disable'));
149 + var onToggleUser = function(action, event) {
150 + event.preventDefault();
151 + var actionTrigger = $(this);
152 + if (actionTrigger.hasClass('pending')) {
153 + // Another request is already in progress.
154 + return;
155 + }
156 + actionTrigger.addClass('pending');
157 + var notification = new XWiki.widgets.Notification(notificationMessage[action].inProgress, 'inprogress');
158 + // The enable and disable actions redirect to the user profile by default which may take a lot of time to render so
159 + // we redirect to a URL that doesn't render anything.
160 + var emptyResponseURL = new XWiki.Document('AdminUsersSheet', 'XWiki').getURL('get', 'outputSyntax=plain');
161 + $.post(actionTrigger.attr('href'), {'xredirect': emptyResponseURL}).done(function() {
162 + window['livetable_userstable'].refresh();
163 + notification.replace(new XWiki.widgets.Notification(notificationMessage[action].done, 'done'));
164 + }).fail(function() {
165 + notification.replace(new XWiki.widgets.Notification(notificationMessage[action].failed, 'error'));
166 + }).always(function() {
167 + actionTrigger.removeClass('pending');
168 + });
169 + };
170 + $('#userstable').on('click', 'a.actionenable', $.proxy(onToggleUser, null, 'enable'));
171 + $('#userstable').on('click', 'a.actiondisable', $.proxy(onToggleUser, null, 'disable'));
166 166   })();
167 167  
168 168   //
... ... @@ -196,9 +196,9 @@
196 196  
197 197   deleteUserModal.on('show.bs.modal', function() {
198 198   var rowData = deleteUserModal.data('rowData');
199 - var userReference = rowData['doc.fullName'];
200 - if (XWiki.currentWiki !== rowData['doc.wiki']) {
201 - userReference = XWiki.Model.resolve(userReference, XWiki.EntityType.DOCUMENT, [rowData['doc.wiki']]);
205 + var userReference = rowData.doc_fullName;
206 + if (XWiki.currentWiki !== rowData.doc_wiki) {
207 + userReference = XWiki.Model.resolve(userReference, XWiki.EntityType.DOCUMENT, [rowData.doc_wiki]);
202 202   userReference = XWiki.Model.serialize(userReference);
203 203   }
204 204   deleteUserModal.data('userReference', userReference);
... ... @@ -219,9 +219,9 @@
219 219   newAuthor: newAuthor,
220 220   requiredRight: requiredRight
221 221   });
222 - return Promise.resolve(newAuthorValidationRequest);
228 + return newAuthorValidationRequest;
223 223   } else {
224 - return Promise.resolve({valid: true});
230 + return $.Deferred().resolve({valid: true}).promise();
225 225   }
226 226   };
227 227   deleteUserModal.on('change', '#newAuthor', function(event) {
... ... @@ -230,17 +230,17 @@
230 230   var newAuthorField = $(event.target);
231 231   // Hide the previous error message.
232 232   newAuthorField.nextAll('.xErrorMsg').addClass('hidden');
233 - validateNewAuthor(newAuthorField.val(), newAuthorField.data('requiredRight')).then(function(result) {
239 + validateNewAuthor(newAuthorField.val(), newAuthorField.data('requiredRight')).done(function(result) {
234 234   if (result.valid === false) {
235 235   newAuthorField.nextAll('.xErrorMsg').removeClass('hidden');
236 236   }
237 - }).finally(function() {
243 + }).always(function() {
238 238   // Re-enable the modal submit button.
239 239   deleteUserButton.prop('disabled', false);
240 240   });
241 241   });
242 242  
243 - deleteUserButton.on('click', function() {
249 + deleteUserButton.click(function() {
244 244   var notification = new XWiki.widgets.Notification(
245 245   $jsontool.serialize($services.localization.render('xe.admin.users.delete.inProgress')),
246 246   'inprogress'
... ... @@ -251,14 +251,14 @@
251 251   docname: userReference,
252 252   newAuthor: deleteUserModal.find('#newAuthor').val(),
253 253   form_token: xm.form_token
254 - }).then(() => {
255 - deleteUserModal.data('liveDataElement').data('liveData').updateEntries();
256 - deleteUserModal.data('liveDataElement').trigger('xwiki:user:deleted', {reference: userReference});
260 + }).done(function() {
261 + deleteUserModal.data('liveTable').deleteRow(deleteUserModal.data('rowIndex'));
262 + deleteUserModal.data('liveTableElement').trigger('xwiki:user:deleted', {reference: userReference});
257 257   notification.replace(new XWiki.widgets.Notification(
258 258   $jsontool.serialize($services.localization.render('xe.admin.users.delete.done')),
259 259   'done'
260 260   ));
261 - }).catch(() => {
267 + }).fail(function() {
262 262   notification.replace(new XWiki.widgets.Notification(
263 263   $jsontool.serialize($services.localization.render('xe.admin.users.delete.failed')),
264 264   'error'
XWiki.StyleSheetExtension[0]
Code
... ... @@ -1,4 +1,19 @@
1 +#template('colorThemeInit.vm')
2 +
1 1  /**
4 + * Users Live Table
5 + */
6 +#userstable td[data-title] {
7 + vertical-align: middle;
8 +}
9 +#userstable .actiondisable .action-icon {
10 + color: $theme.notificationWarningColor;
11 +}
12 +#userstable .actionenable .action-icon {
13 + color: $theme.notificationSuccessColor;
14 +}
15 +
16 +/**
2 2   * Delete User Modal
3 3   */
4 4  #deleteUserModal .userName {
Content Type
... ... @@ -1,1 +1,1 @@
1 -LESS
1 +CSS
Parser le contenu
... ... @@ -1,1 +1,1 @@
1 -Non
1 +Oui