users.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. /**
  2. * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
  3. * This file is licensed under the Affero General Public License version 3 or later.
  4. * See the COPYING-README file.
  5. */
  6. function setQuota (uid, quota, ready) {
  7. $.post(
  8. OC.filePath('settings', 'ajax', 'setquota.php'),
  9. {username: uid, quota: quota},
  10. function (result) {
  11. if (ready) {
  12. ready(result.data.quota);
  13. }
  14. }
  15. );
  16. }
  17. var UserList = {
  18. useUndo: true,
  19. availableGroups: [],
  20. offset: 30, //The first 30 users are there. No prob, if less in total.
  21. //hardcoded in settings/users.php
  22. usersToLoad: 10, //So many users will be loaded when user scrolls down
  23. /**
  24. * @brief Initiate user deletion process in UI
  25. * @param string uid the user ID to be deleted
  26. *
  27. * Does not actually delete the user; it sets them for
  28. * deletion when the current page is unloaded, at which point
  29. * finishDelete() completes the process. This allows for 'undo'.
  30. */
  31. do_delete: function (uid) {
  32. if (typeof UserList.deleteUid !== 'undefined') {
  33. //Already a user in the undo queue
  34. UserList.finishDelete(null);
  35. }
  36. UserList.deleteUid = uid;
  37. // Set undo flag
  38. UserList.deleteCanceled = false;
  39. // Provide user with option to undo
  40. $('#notification').data('deleteuser', true);
  41. OC.Notification.showHtml(t('users', 'deleted') + ' ' + escapeHTML(uid) + '<span class="undo">' + t('users', 'undo') + '</span>');
  42. },
  43. /**
  44. * @brief Delete a user via ajax
  45. * @param bool ready whether to use ready() upon completion
  46. *
  47. * Executes deletion via ajax of user identified by property deleteUid
  48. * if 'undo' has not been used. Completes the user deletion procedure
  49. * and reflects success in UI.
  50. */
  51. finishDelete: function (ready) {
  52. // Check deletion has not been undone
  53. if (!UserList.deleteCanceled && UserList.deleteUid) {
  54. // Delete user via ajax
  55. $.ajax({
  56. type: 'POST',
  57. url: OC.filePath('settings', 'ajax', 'removeuser.php'),
  58. async: false,
  59. data: { username: UserList.deleteUid },
  60. success: function (result) {
  61. if (result.status === 'success') {
  62. // Remove undo option, & remove user from table
  63. OC.Notification.hide();
  64. $('tr').filterAttr('data-uid', UserList.deleteUid).remove();
  65. UserList.deleteCanceled = true;
  66. if (ready) {
  67. ready();
  68. }
  69. } else {
  70. OC.dialogs.alert(result.data.message, t('settings', 'Unable to remove user'));
  71. }
  72. }
  73. });
  74. }
  75. },
  76. add: function (username, displayname, groups, subadmin, quota, sort) {
  77. var tr = $('tbody tr').first().clone();
  78. if (tr.find('div.avatardiv')){
  79. $('div.avatardiv', tr).avatar(username, 32);
  80. }
  81. tr.attr('data-uid', username);
  82. tr.attr('data-displayName', displayname);
  83. tr.find('td.name').text(username);
  84. tr.find('td.displayName > span').text(displayname);
  85. var groupsSelect = $('<select multiple="multiple" class="groupsselect" data-placehoder="Groups" title="' + t('settings', 'Groups') + '"></select>')
  86. .attr('data-username', username)
  87. .data('user-groups', groups);
  88. tr.find('td.groups').empty();
  89. if (tr.find('td.subadmins').length > 0) {
  90. var subadminSelect = $('<select multiple="multiple" class="subadminsselect" data-placehoder="subadmins" title="' + t('settings', 'Group Admin') + '">')
  91. .attr('data-username', username)
  92. .data('user-groups', groups)
  93. .data('subadmin', subadmin);
  94. tr.find('td.subadmins').empty();
  95. }
  96. $.each(this.availableGroups, function (i, group) {
  97. groupsSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'));
  98. if (typeof subadminSelect !== 'undefined' && group !== 'admin') {
  99. subadminSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'));
  100. }
  101. });
  102. tr.find('td.groups').append(groupsSelect);
  103. UserList.applyMultiplySelect(groupsSelect);
  104. if (tr.find('td.subadmins').length > 0) {
  105. tr.find('td.subadmins').append(subadminSelect);
  106. UserList.applyMultiplySelect(subadminSelect);
  107. }
  108. if (tr.find('td.remove img').length === 0 && OC.currentUser !== username) {
  109. var rm_img = $('<img class="svg action">').attr({
  110. src: OC.imagePath('core', 'actions/delete')
  111. });
  112. var rm_link = $('<a class="action delete">')
  113. .attr({ href: '#', 'original-title': t('settings', 'Delete')})
  114. .append(rm_img);
  115. tr.find('td.remove').append(rm_link);
  116. } else if (OC.currentUser === username) {
  117. tr.find('td.remove a').remove();
  118. }
  119. var quotaSelect = tr.find('select.quota-user');
  120. if (quota === 'default') {
  121. quotaSelect.find('option').attr('selected', null);
  122. quotaSelect.find('option').first().attr('selected', 'selected');
  123. quotaSelect.data('previous', 'default');
  124. } else {
  125. if (quotaSelect.find('option[value="' + quota + '"]').length > 0) {
  126. quotaSelect.find('option[value="' + quota + '"]').attr('selected', 'selected');
  127. } else {
  128. quotaSelect.append('<option value="' + escapeHTML(quota) + '" selected="selected">' + escapeHTML(quota) + '</option>');
  129. }
  130. }
  131. $(tr).appendTo('tbody');
  132. if (sort) {
  133. UserList.doSort();
  134. }
  135. quotaSelect.singleSelect();
  136. quotaSelect.on('change', function () {
  137. var uid = $(this).parent().parent().attr('data-uid');
  138. var quota = $(this).val();
  139. setQuota(uid, quota, function(returnedQuota){
  140. if (quota !== returnedQuota) {
  141. $(quotaSelect).find(':selected').text(returnedQuota);
  142. }
  143. });
  144. });
  145. },
  146. // From http://my.opera.com/GreyWyvern/blog/show.dml/1671288
  147. alphanum: function(a, b) {
  148. function chunkify(t) {
  149. var tz = [], x = 0, y = -1, n = 0, i, j;
  150. while (i = (j = t.charAt(x++)).charCodeAt(0)) {
  151. var m = (i === 46 || (i >=48 && i <= 57));
  152. if (m !== n) {
  153. tz[++y] = "";
  154. n = m;
  155. }
  156. tz[y] += j;
  157. }
  158. return tz;
  159. }
  160. var aa = chunkify(a.toLowerCase());
  161. var bb = chunkify(b.toLowerCase());
  162. for (x = 0; aa[x] && bb[x]; x++) {
  163. if (aa[x] !== bb[x]) {
  164. var c = Number(aa[x]), d = Number(bb[x]);
  165. if (c === aa[x] && d === bb[x]) {
  166. return c - d;
  167. } else {
  168. return (aa[x] > bb[x]) ? 1 : -1;
  169. }
  170. }
  171. }
  172. return aa.length - bb.length;
  173. },
  174. doSort: function() {
  175. var self = this;
  176. var rows = $('tbody tr').get();
  177. rows.sort(function(a, b) {
  178. return UserList.alphanum($(a).find('td.name').text(), $(b).find('td.name').text());
  179. });
  180. var items = [];
  181. $.each(rows, function(index, row) {
  182. items.push(row);
  183. if(items.length === 100) {
  184. $('tbody').append(items);
  185. items = [];
  186. }
  187. });
  188. if(items.length > 0) {
  189. $('tbody').append(items);
  190. }
  191. },
  192. update: function () {
  193. if (UserList.updating) {
  194. return;
  195. }
  196. UserList.updating = true;
  197. $.get(OC.Router.generate('settings_ajax_userlist', { offset: UserList.offset, limit: UserList.usersToLoad }), function (result) {
  198. if (result.status === 'success') {
  199. //The offset does not mirror the amount of users available,
  200. //because it is backend-dependent. For correct retrieval,
  201. //always the limit(requested amount of users) needs to be added.
  202. UserList.offset += UserList.usersToLoad;
  203. $.each(result.data, function (index, user) {
  204. if($('tr[data-uid="' + user.name + '"]').length > 0) {
  205. return true;
  206. }
  207. var tr = UserList.add(user.name, user.displayname, user.groups, user.subadmin, user.quota, false);
  208. if (index === 9) {
  209. $(tr).bind('inview', function (event, isInView, visiblePartX, visiblePartY) {
  210. $(this).unbind(event);
  211. UserList.update();
  212. });
  213. }
  214. });
  215. if (result.data.length > 0) {
  216. UserList.doSort();
  217. }
  218. }
  219. UserList.updating = false;
  220. });
  221. },
  222. applyMultiplySelect: function (element) {
  223. var checked = [];
  224. var user = element.attr('data-username');
  225. if ($(element).attr('class') === 'groupsselect') {
  226. if (element.data('userGroups')) {
  227. checked = element.data('userGroups');
  228. }
  229. if (user) {
  230. var checkHandeler = function (group) {
  231. if (user === OC.currentUser && group === 'admin') {
  232. return false;
  233. }
  234. if (!isadmin && checked.length === 1 && checked[0] === group) {
  235. return false;
  236. }
  237. $.post(
  238. OC.filePath('settings', 'ajax', 'togglegroups.php'),
  239. {
  240. username: user,
  241. group: group
  242. },
  243. function (response) {
  244. if(response.status === 'success'
  245. && UserList.availableGroups.indexOf(response.data.groupname) === -1
  246. && response.data.action === 'add') {
  247. UserList.availableGroups.push(response.data.groupname);
  248. }
  249. if(response.data.message) {
  250. OC.Notification.show(response.data.message);
  251. }
  252. }
  253. );
  254. };
  255. } else {
  256. checkHandeler = false;
  257. }
  258. var addGroup = function (select, group) {
  259. $('select[multiple]').each(function (index, element) {
  260. if ($(element).find('option[value="' + group + '"]').length === 0 && select.data('msid') !== $(element).data('msid')) {
  261. $(element).append('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>');
  262. }
  263. });
  264. };
  265. var label;
  266. if (isadmin) {
  267. label = t('settings', 'add group');
  268. } else {
  269. label = null;
  270. }
  271. element.multiSelect({
  272. createCallback: addGroup,
  273. createText: label,
  274. selectedFirst: true,
  275. checked: checked,
  276. oncheck: checkHandeler,
  277. onuncheck: checkHandeler,
  278. minWidth: 100
  279. });
  280. }
  281. if ($(element).attr('class') === 'subadminsselect') {
  282. if (element.data('subadmin')) {
  283. checked = element.data('subadmin');
  284. }
  285. var checkHandeler = function (group) {
  286. if (group === 'admin') {
  287. return false;
  288. }
  289. $.post(
  290. OC.filePath('settings', 'ajax', 'togglesubadmins.php'),
  291. {
  292. username: user,
  293. group: group
  294. },
  295. function () {
  296. }
  297. );
  298. };
  299. var addSubAdmin = function (group) {
  300. $('select[multiple]').each(function (index, element) {
  301. if ($(element).find('option[value="' + group + '"]').length === 0) {
  302. $(element).append('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>');
  303. }
  304. });
  305. };
  306. element.multiSelect({
  307. createCallback: addSubAdmin,
  308. createText: null,
  309. checked: checked,
  310. oncheck: checkHandeler,
  311. onuncheck: checkHandeler,
  312. minWidth: 100
  313. });
  314. }
  315. }
  316. };
  317. $(document).ready(function () {
  318. UserList.doSort();
  319. UserList.availableGroups = $('#content table').data('groups');
  320. $('tbody tr:last').bind('inview', function (event, isInView, visiblePartX, visiblePartY) {
  321. OC.Router.registerLoadedCallback(function () {
  322. UserList.update();
  323. });
  324. });
  325. $('select[multiple]').each(function (index, element) {
  326. UserList.applyMultiplySelect($(element));
  327. });
  328. $('table').on('click', 'td.remove>a', function (event) {
  329. var row = $(this).parent().parent();
  330. var uid = $(row).attr('data-uid');
  331. $(row).hide();
  332. // Call function for handling delete/undo
  333. UserList.do_delete(uid);
  334. });
  335. $('table').on('click', 'td.password>img', function (event) {
  336. event.stopPropagation();
  337. var img = $(this);
  338. var uid = img.parent().parent().attr('data-uid');
  339. var input = $('<input type="password">');
  340. img.css('display', 'none');
  341. img.parent().children('span').replaceWith(input);
  342. input.focus();
  343. input.keypress(function (event) {
  344. if (event.keyCode === 13) {
  345. if ($(this).val().length > 0) {
  346. var recoveryPasswordVal = $('input:password[id="recoveryPassword"]').val();
  347. $.post(
  348. OC.Router.generate('settings_users_changepassword'),
  349. {username: uid, password: $(this).val(), recoveryPassword: recoveryPasswordVal},
  350. function (result) {
  351. if (result.status != 'success') {
  352. OC.Notification.show(t('admin', result.data.message));
  353. }
  354. }
  355. );
  356. input.blur();
  357. } else {
  358. input.blur();
  359. }
  360. }
  361. });
  362. input.blur(function () {
  363. $(this).replaceWith($('<span>●●●●●●●</span>'));
  364. img.css('display', '');
  365. });
  366. });
  367. $('input:password[id="recoveryPassword"]').keyup(function(event) {
  368. OC.Notification.hide();
  369. });
  370. $('table').on('click', 'td.password', function (event) {
  371. $(this).children('img').click();
  372. });
  373. $('table').on('click', 'td.displayName>img', function (event) {
  374. event.stopPropagation();
  375. var img = $(this);
  376. var uid = img.parent().parent().attr('data-uid');
  377. var displayName = escapeHTML(img.parent().parent().attr('data-displayName'));
  378. var input = $('<input type="text" value="' + displayName + '">');
  379. img.css('display', 'none');
  380. img.parent().children('span').replaceWith(input);
  381. input.focus();
  382. input.keypress(function (event) {
  383. if (event.keyCode === 13) {
  384. if ($(this).val().length > 0) {
  385. $.post(
  386. OC.filePath('settings', 'ajax', 'changedisplayname.php'),
  387. {username: uid, displayName: $(this).val()},
  388. function (result) {
  389. if (result && result.status==='success'){
  390. img.parent().parent().find('div.avatardiv').avatar(result.data.username, 32);
  391. }
  392. }
  393. );
  394. input.blur();
  395. } else {
  396. input.blur();
  397. }
  398. }
  399. });
  400. input.blur(function () {
  401. $(this).replaceWith(escapeHTML($(this).val()));
  402. img.css('display', '');
  403. });
  404. });
  405. $('table').on('click', 'td.displayName', function (event) {
  406. $(this).children('img').click();
  407. });
  408. $('select.quota, select.quota-user').singleSelect().on('change', function () {
  409. var select = $(this);
  410. var uid = $(this).parent().parent().attr('data-uid');
  411. var quota = $(this).val();
  412. setQuota(uid, quota, function(returnedQuota){
  413. if (quota !== returnedQuota) {
  414. select.find(':selected').text(returnedQuota);
  415. }
  416. });
  417. });
  418. $('#newuser').submit(function (event) {
  419. event.preventDefault();
  420. var username = $('#newusername').val();
  421. var password = $('#newuserpassword').val();
  422. if ($.trim(username) === '') {
  423. OC.dialogs.alert(
  424. t('settings', 'A valid username must be provided'),
  425. t('settings', 'Error creating user'));
  426. return false;
  427. }
  428. if ($.trim(password) === '') {
  429. OC.dialogs.alert(
  430. t('settings', 'A valid password must be provided'),
  431. t('settings', 'Error creating user'));
  432. return false;
  433. }
  434. var groups = $('#newusergroups').prev().children('div').data('settings').checked;
  435. $('#newuser').get(0).reset();
  436. $.post(
  437. OC.filePath('settings', 'ajax', 'createuser.php'),
  438. {
  439. username: username,
  440. password: password,
  441. groups: groups
  442. },
  443. function (result) {
  444. if (result.status !== 'success') {
  445. OC.dialogs.alert(result.data.message,
  446. t('settings', 'Error creating user'));
  447. } else {
  448. if (result.data.groups) {
  449. var addedGroups = result.data.groups;
  450. UserList.availableGroups = $.unique($.merge(UserList.availableGroups, addedGroups));
  451. }
  452. if (result.data.homeExists){
  453. OC.Notification.hide();
  454. OC.Notification.show(t('settings', 'Warning: Home directory for user "{user}" already exists', {user: result.data.username}));
  455. if (UserList.notificationTimeout){
  456. window.clearTimeout(UserList.notificationTimeout);
  457. }
  458. UserList.notificationTimeout = window.setTimeout(
  459. function(){
  460. OC.Notification.hide();
  461. UserList.notificationTimeout = null;
  462. }, 10000);
  463. }
  464. if($('tr[data-uid="' + username + '"]').length === 0) {
  465. UserList.add(username, username, result.data.groups, null, 'default', true);
  466. }
  467. }
  468. }
  469. );
  470. });
  471. // Handle undo notifications
  472. OC.Notification.hide();
  473. $('#notification').on('click', '.undo', function () {
  474. if ($('#notification').data('deleteuser')) {
  475. $('tbody tr').filterAttr('data-uid', UserList.deleteUid).show();
  476. UserList.deleteCanceled = true;
  477. }
  478. OC.Notification.hide();
  479. });
  480. UserList.useUndo = ('onbeforeunload' in window);
  481. $(window).bind('beforeunload', function () {
  482. UserList.finishDelete(null);
  483. });
  484. });