Изменения документа AdminUsersSheet

Редактировал(а) Alexey Semenihin 23.10.2026, 15:12

От версии 4.1
отредактировано Alexey Semenihin
на 23.10.2026, 15:12
Изменить комментарий: Install extension [org.xwiki.platform:xwiki-platform-administration-ui/17.10.12]
К версии 1.1
отредактировано Admin xWiKi
на 15.10.2019, 13:19
Изменить комментарий: Install extension [org.xwiki.platform:xwiki-platform-administration-ui/11.8.1]

Сводка

Подробности

Свойства страницы
Автор документа
... ... @@ -1,1 +1,1 @@
1 -XWiki.semenihinaa
1 +XWiki.admin
Синтаксис
... ... @@ -1,1 +1,1 @@
1 -XWiki 2.1
1 +XWiki 2.0
Содержимое
... ... @@ -1,93 +1,52 @@
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 - <button type="button" class="btn btn-primary" data-toggle="modal" data-target="${escapetool.h}createUserModal"
89 - ## Disable the button until the JavaScript code that handles the user creation is ready.
90 - disabled="disabled">
49 + <button type="button" class="btn btn-primary" data-toggle="modal" data-target="${escapetool.h}createUserModal">
91 91   $escapetool.xml($services.localization.render('rightsmanager.addnewuser'))
92 92   </button>
93 93   </p>
... ... @@ -111,9 +111,7 @@
111 111   <button type="button" class="btn btn-default" data-dismiss="modal">
112 112   $escapetool.xml($services.localization.render('cancel'))
113 113   </button>
114 - ## The registration form is not present on page load so we can't bind the submit button without triggerig an
115 - ## HTML validation error. That's why we set the form attribute from JavaScript instead.
116 - <button type="submit" class="btn btn-primary" disabled="disabled">
73 + <button type="button" class="btn btn-primary" disabled="disabled">
117 117   $escapetool.xml($services.localization.render('create'))
118 118   </button>
119 119   </div>
... ... @@ -124,7 +124,7 @@
124 124  
125 125  #macro (editUserModal)
126 126   <div class="modal" id="editUserModal" tabindex="-1" role="dialog" aria-labelledby="editUserModal-label"
127 - data-backdrop="static" data-keyboard="false" data-live-data="#userstable" data-live-data-action="edit">
84 + data-backdrop="static" data-keyboard="false" data-liveTable="#userstable" data-liveTableAction="edit">
128 128   <div class="modal-dialog modal-lg" role="document">
129 129   <div class="modal-content">
130 130   <div class="modal-header">
... ... @@ -154,7 +154,7 @@
154 154   ## have script or programming rights.
155 155   #userPicker_import
156 156   <div class="modal" id="deleteUserModal" tabindex="-1" role="dialog" aria-labelledby="deleteUserModal-label"
157 - data-live-data="#userstable" data-live-data-action="delete">
114 + data-liveTable="#userstable" data-liveTableAction="delete">
158 158   <div class="modal-dialog" role="document">
159 159   <div class="modal-content">
160 160   <div class="modal-header">
... ... @@ -208,13 +208,11 @@
208 208  #macro (maybeShowDeleteUserWarning $userReference $right)
209 209   #countPagesLastModifiedBy($userReference)
210 210   #if ($pageCount > 0)
211 - {{/html}}
212 -
213 - {{error cssClass="xform"}}
214 - {{html}}
168 + <div class="box errormessage xform">
215 215   #set ($pageIndexReference = $services.model.createDocumentReference(
216 216   $userReference.wikiReference.name, 'Main', 'AllDocs'))
217 - #set ($pageIndexURL = $xwiki.getURL($pageIndexReference, 'view', "doc.author=${escapetool.url($services.model.serialize($userReference, 'local'))}"))
171 + #set ($pageIndexURL = $xwiki.getURL($pageIndexReference) + '#|t=alldocs&doc.author=' +
172 + $escapetool.url($services.model.serialize($userReference, 'local')))
218 218   #set ($translationKey = "administration.section.users.deleteUser.${right}RightsWarning")
219 219   $services.localization.render($translationKey, ["<a href='$pageIndexURL'>", $pageCount, '</a>'])
220 220   <dl>
... ... @@ -239,10 +239,7 @@
239 239   [$rightTranslation]))</span>
240 240   </dd>
241 241   </dl>
242 - {{/html}}
243 - {{/error}}
244 -
245 - {{html clean="false"}}
197 + </div>
246 246   #end
247 247  #end
248 248  
... ... @@ -295,10 +295,10 @@
295 295   #set ($discard = $xwiki.jsx.use("XWiki.AdminUsersSheet"))
296 296  
297 297   {{html clean="false"}}
298 - #displayUsersLiveData()
299 - #createUserModal()
300 - #editUserModal()
301 - #deleteUserModal()
250 + #displayUsersLiveTable
251 + #createUserModal
252 + #editUserModal
253 + #deleteUserModal
302 302   {{/html}}
303 303   #end
304 304  #end
XWiki.JavaScriptExtension[0]
Код
... ... @@ -1,25 +1,31 @@
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 - const createUserModal = $('#createUserModal');
12 - // We set the form attribute from JavaScript in order to avoid HTML validation errors because the form is not present
13 - // on page load (we load it the first time the modal is shown, see below).
14 - const createUserButton = createUserModal.find('.btn-primary').attr('form', 'register');
20 + var createUserModal = $('#createUserModal');
15 15  
16 16   var validateCreateUserForm = function(form) {
17 - return form.length && (!window.LiveValidation ||
23 + return form.size() > 0 && (!window.LiveValidation ||
18 18   LiveValidation.massValidate(LiveValidationForm.getInstance(form[0]).fields));
19 19   };
20 20  
21 21   createUserModal.on('show.bs.modal', function() {
22 - createUserButton.prop('disabled', true);
28 + var createUserButton = createUserModal.find('.btn-primary').prop('disabled', true);
23 23   var createUserURL = XWiki.currentDocument.getURL('view', 'xpage=registerinline');
24 24   var createUserModalBody = createUserModal.find('.modal-body').empty().addClass('loading');
25 25   $.get(createUserURL, function(html) {
... ... @@ -29,27 +29,26 @@
29 29   createUserModalBody.removeClass('loading').append(createUserForm);
30 30   $(document).trigger('xwiki:dom:updated', {'elements': createUserModalBody.toArray()});
31 31   createUserForm.find(':input').filter(':visible').first().focus();
32 - createUserButton.prop('disabled', !createUserForm.length);
38 + createUserButton.prop('disabled', createUserForm.size() === 0);
33 33   });
34 - }).on('submit', 'form#register', function(event) {
35 - event.preventDefault();
40 + }).on('click', '.btn-primary', function(event) {
36 36   var createUserForm = createUserModal.find('form#register');
37 37   if (!validateCreateUserForm(createUserForm)) {
38 38   return;
39 39   }
40 - createUserButton.prop('disabled', true);
45 + var createUserButton = $(this).prop('disabled', true);
41 41   var notification = new XWiki.widgets.Notification(
42 42   $jsontool.serialize($services.localization.render('xe.admin.users.create.inProgress')),
43 43   'inprogress'
44 44   );
45 - $.post(createUserForm.attr('action'), createUserForm.serialize()).then(html => {
50 + $.post(createUserForm.attr('action'), createUserForm.serialize()).done(function(html) {
46 46   var errorMessage = $('<div/>').html(html).find('.errormessage, .LV_validation_message.LV_invalid');
47 - if (errorMessage.length) {
52 + if (errorMessage.size() > 0) {
48 48   createUserButton.prop('disabled', false);
49 49   notification.replace(new XWiki.widgets.Notification(errorMessage.text(), 'error'));
50 50   } else {
51 51   createUserModal.modal('hide');
52 - $('#userstable').data('liveData').updateEntries();
57 + window.livetable_userstable.refresh();
53 53   $('#userstable').trigger('xwiki:user:created');
54 54   notification.replace(new XWiki.widgets.Notification(
55 55   $jsontool.serialize($services.localization.render('xe.admin.users.create.done')),
... ... @@ -56,7 +56,7 @@
56 56   'done'
57 57   ));
58 58   }
59 - }).catch(() => {
64 + }).fail(function (response) {
60 60   createUserButton.prop('disabled', false);
61 61   notification.replace(new XWiki.widgets.Notification(
62 62   $jsontool.serialize($services.localization.render('xe.admin.users.create.failed')),
... ... @@ -73,22 +73,20 @@
73 73   var loadEditForm = function(forceLock) {
74 74   var saveButton = editUserModal.find('.btn-primary').prop('disabled', true);
75 75   var rowData = editUserModal.data('rowData');
76 - 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]);
77 77   var userDocument = new XWiki.Document(userReference);
78 78   var editUserURL = userDocument.getURL('edit');
79 79   var parameters = {xpage: 'edituser'};
80 80   if (forceLock === true) {
81 - parameters.force_token = xm.form_token;
82 82   parameters.force = 1;
83 83   }
84 84   editUserModal.find('.modal-body').first().empty().addClass('loading').load(editUserURL, parameters, function() {
85 - var self = $(this);
86 - self.removeClass('loading');
87 - self.find('.column.half').removeClass('half').addClass('full');
88 - $(document).trigger('xwiki:dom:updated', {'elements': self.toArray()});
89 + this.removeClass('loading');
90 + this.find('.column.half').removeClass('half').addClass('full');
91 + $(document).trigger('xwiki:dom:updated', {'elements': this.toArray()});
89 89   var editUserForm = editUserModal.find('form#edituser');
90 90   editUserForm.find(':input').filter(':visible').first().focus();
91 - saveButton.prop('disabled', !editUserForm.length);
94 + saveButton.prop('disabled', editUserForm.size() === 0);
92 92   });
93 93   };
94 94  
... ... @@ -106,14 +106,14 @@
106 106   'inprogress'
107 107   );
108 108   $(document).trigger('xwiki:actions:beforeSave');
109 - $.post(editForm.attr('action'), editForm.serialize()).then(() => {
112 + $.post(editForm.attr('action'), editForm.serialize()).done(function() {
110 110   $(document).trigger('xwiki:document:saved');
111 - editUserModal.modal('hide').data('liveDataElement').data('liveData').updateEntries();
114 + editUserModal.modal('hide').data('liveTable').refresh();
112 112   notification.replace(new XWiki.widgets.Notification(
113 113   $jsontool.serialize($services.localization.render('core.editors.saveandcontinue.notification.done')),
114 114   'done'
115 115   ));
116 - }).catch(response => {
119 + }).fail(function (response) {
117 117   saveButton.prop('disabled', false);
118 118   var message = $jsontool.serialize($services.localization.render('core.editors.saveandcontinue.notification.error',
119 119   ['__reason__']));
... ... @@ -142,32 +142,29 @@
142 142   failed: $jsontool.serialize($services.localization.render('administration.section.users.disableUser.failed'))
143 143   }
144 144   }
145 -
146 - function onToggleUser(action) {
147 - return function (event) {
148 - event.preventDefault();
149 - var actionTrigger = $(this);
150 - if (actionTrigger.hasClass('pending')) {
151 - // Another request is already in progress.
152 - return;
153 - }
154 - actionTrigger.addClass('pending');
155 - var notification = new XWiki.widgets.Notification(notificationMessage[action].inProgress, 'inprogress');
156 - // The enable and disable actions redirect to the user profile by default which may take a lot of time to
157 - // render so we redirect to a URL that doesn't render anything.
158 - var emptyResponseURL = new XWiki.Document('AdminUsersSheet', 'XWiki').getURL('get', 'outputSyntax=plain');
159 - Promise.resolve($.post(actionTrigger.attr('href'), {'xredirect': emptyResponseURL})).then(function () {
160 - $('#userstable').data('liveData').updateEntries();
161 - notification.replace(new XWiki.widgets.Notification(notificationMessage[action].done, 'done'));
162 - }).catch(function () {
163 - notification.replace(new XWiki.widgets.Notification(notificationMessage[action].failed, 'error'));
164 - }).finally(function () {
165 - actionTrigger.removeClass('pending');
166 - });
167 - };
168 - }
169 - $(document).on('click', '#userstable a.action_enable', onToggleUser('enable'));
170 - $(document).on('click', '#userstable a.action_disable', onToggleUser('disable'));
148 + var onToggleUser = function(action, event) {
149 + event.preventDefault();
150 + var actionTrigger = $(this);
151 + if (actionTrigger.hasClass('pending')) {
152 + // Another request is already in progress.
153 + return;
154 + }
155 + actionTrigger.addClass('pending');
156 + var notification = new XWiki.widgets.Notification(notificationMessage[action].inProgress, 'inprogress');
157 + // The enable and disable actions redirect to the user profile by default which may take a lot of time to render so
158 + // we redirect to a URL that doesn't render anything.
159 + var emptyResponseURL = new XWiki.Document('AdminUsersSheet', 'XWiki').getURL('get', 'outputSyntax=plain');
160 + $.post(actionTrigger.attr('href'), {'xredirect': emptyResponseURL}).done(function() {
161 + window['livetable_userstable'].refresh();
162 + notification.replace(new XWiki.widgets.Notification(notificationMessage[action].done, 'done'));
163 + }).fail(function() {
164 + notification.replace(new XWiki.widgets.Notification(notificationMessage[action].failed, 'error'));
165 + }).always(function() {
166 + actionTrigger.removeClass('pending');
167 + });
168 + };
169 + $('#userstable').on('click', 'a.actionenable', $.proxy(onToggleUser, null, 'enable'));
170 + $('#userstable').on('click', 'a.actiondisable', $.proxy(onToggleUser, null, 'disable'));
171 171   })();
172 172  
173 173   //
... ... @@ -201,9 +201,9 @@
201 201  
202 202   deleteUserModal.on('show.bs.modal', function() {
203 203   var rowData = deleteUserModal.data('rowData');
204 - var userReference = rowData['doc.fullName'];
205 - if (XWiki.currentWiki !== rowData['doc.wiki']) {
206 - userReference = XWiki.Model.resolve(userReference, XWiki.EntityType.DOCUMENT, [rowData['doc.wiki']]);
204 + var userReference = rowData.doc_fullName;
205 + if (XWiki.currentWiki !== rowData.doc_wiki) {
206 + userReference = XWiki.Model.resolve(userReference, XWiki.EntityType.DOCUMENT, [rowData.doc_wiki]);
207 207   userReference = XWiki.Model.serialize(userReference);
208 208   }
209 209   deleteUserModal.data('userReference', userReference);
... ... @@ -224,9 +224,9 @@
224 224   newAuthor: newAuthor,
225 225   requiredRight: requiredRight
226 226   });
227 - return Promise.resolve(newAuthorValidationRequest);
227 + return newAuthorValidationRequest;
228 228   } else {
229 - return Promise.resolve({valid: true});
229 + return $.Deferred().resolve({valid: true}).promise();
230 230   }
231 231   };
232 232   deleteUserModal.on('change', '#newAuthor', function(event) {
... ... @@ -235,17 +235,17 @@
235 235   var newAuthorField = $(event.target);
236 236   // Hide the previous error message.
237 237   newAuthorField.nextAll('.xErrorMsg').addClass('hidden');
238 - validateNewAuthor(newAuthorField.val(), newAuthorField.data('requiredRight')).then(function(result) {
238 + validateNewAuthor(newAuthorField.val(), newAuthorField.data('requiredRight')).done(function(result) {
239 239   if (result.valid === false) {
240 240   newAuthorField.nextAll('.xErrorMsg').removeClass('hidden');
241 241   }
242 - }).finally(function() {
242 + }).always(function() {
243 243   // Re-enable the modal submit button.
244 244   deleteUserButton.prop('disabled', false);
245 245   });
246 246   });
247 247  
248 - deleteUserButton.on('click', function() {
248 + deleteUserButton.click(function() {
249 249   var notification = new XWiki.widgets.Notification(
250 250   $jsontool.serialize($services.localization.render('xe.admin.users.delete.inProgress')),
251 251   'inprogress'
... ... @@ -256,14 +256,14 @@
256 256   docname: userReference,
257 257   newAuthor: deleteUserModal.find('#newAuthor').val(),
258 258   form_token: xm.form_token
259 - }).then(() => {
260 - deleteUserModal.data('liveDataElement').data('liveData').updateEntries();
261 - deleteUserModal.data('liveDataElement').trigger('xwiki:user:deleted', {reference: userReference});
259 + }).done(function() {
260 + deleteUserModal.data('liveTable').deleteRow(deleteUserModal.data('rowIndex'));
261 + deleteUserModal.data('liveTableElement').trigger('xwiki:user:deleted', {reference: userReference});
262 262   notification.replace(new XWiki.widgets.Notification(
263 263   $jsontool.serialize($services.localization.render('xe.admin.users.delete.done')),
264 264   'done'
265 265   ));
266 - }).catch(() => {
266 + }).fail(function() {
267 267   notification.replace(new XWiki.widgets.Notification(
268 268   $jsontool.serialize($services.localization.render('xe.admin.users.delete.failed')),
269 269   'error'
... ... @@ -270,7 +270,4 @@
270 270   ));
271 271   });
272 272   });
273 -
274 - // Finally, enable the user creation.
275 - $('button[data-target="#createUserModal"]').prop('disabled', false);
276 276  });
XWiki.StyleSheetExtension[0]
Код
... ... @@ -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
Разбор содержимого
... ... @@ -1,1 +1,1 @@
1 -Нет
1 +Да