users.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884
  1. /**
  2. * Copyright (c) 2014, Arthur Schiwon <blizzz@owncloud.com>
  3. * Copyright (c) 2014, Raghu Nayyar <beingminimal@gmail.com>
  4. * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
  5. * This file is licensed under the Affero General Public License version 3 or later.
  6. * See the COPYING-README file.
  7. */
  8. var $userList;
  9. var $userListBody;
  10. var filter;
  11. var UserList = {
  12. availableGroups: [],
  13. offset: 0,
  14. usersToLoad: 10, //So many users will be loaded when user scrolls down
  15. currentGid: '',
  16. /**
  17. * Initializes the user list
  18. * @param $el user list table element
  19. */
  20. initialize: function($el) {
  21. this.$el = $el;
  22. // initially the list might already contain user entries (not fully ajaxified yet)
  23. // initialize these entries
  24. this.$el.find('.quota-user').singleSelect().on('change', this.onQuotaSelect);
  25. },
  26. /**
  27. * Add a user row from user object
  28. *
  29. * @param user object containing following keys:
  30. * {
  31. * 'name': 'username',
  32. * 'displayname': 'Users display name',
  33. * 'groups': ['group1', 'group2'],
  34. * 'subadmin': ['group4', 'group5'],
  35. * 'quota': '10 GB',
  36. * 'storageLocation': '/srv/www/owncloud/data/username',
  37. * 'lastLogin': '1418632333'
  38. * 'backend': 'LDAP',
  39. * 'email': 'username@example.org'
  40. * 'isRestoreDisabled':false
  41. * }
  42. * @param sort
  43. * @returns table row created for this user
  44. */
  45. add: function (user, sort) {
  46. if (this.currentGid && this.currentGid !== '_everyone' && _.indexOf(user.groups, this.currentGid) < 0) {
  47. return;
  48. }
  49. var $tr = $userListBody.find('tr:first-child').clone();
  50. // this removes just the `display:none` of the template row
  51. $tr.removeAttr('style');
  52. var subAdminsEl;
  53. var subAdminSelect;
  54. var groupsSelect;
  55. /**
  56. * Avatar or placeholder
  57. */
  58. if ($tr.find('div.avatardiv').length){
  59. $tr.find('.avatardiv').imageplaceholder(user.name, user.displayname);
  60. $('div.avatardiv', $tr).avatar(user.name, 32);
  61. }
  62. /**
  63. * add username and displayname to row (in data and visible markup)
  64. */
  65. $tr.data('uid', user.name);
  66. $tr.data('displayname', user.displayname);
  67. $tr.data('mailAddress', user.email);
  68. $tr.data('restoreDisabled', user.isRestoreDisabled);
  69. $tr.find('.name').text(user.name);
  70. $tr.find('td.displayName > span').text(user.displayname);
  71. $tr.find('td.mailAddress > span').text(user.email);
  72. /**
  73. * groups and subadmins
  74. */
  75. // make them look like the multiselect buttons
  76. // until they get time to really get initialized
  77. groupsSelect = $('<select multiple="multiple" class="groupsselect multiselect button" data-placehoder="Groups" title="' + t('settings', 'no group') + '"></select>')
  78. .data('username', user.name)
  79. .data('user-groups', user.groups);
  80. if ($tr.find('td.subadmins').length > 0) {
  81. subAdminSelect = $('<select multiple="multiple" class="subadminsselect multiselect button" data-placehoder="subadmins" title="' + t('settings', 'no group') + '">')
  82. .data('username', user.name)
  83. .data('user-groups', user.groups)
  84. .data('subadmin', user.subadmin);
  85. $tr.find('td.subadmins').empty();
  86. }
  87. $.each(this.availableGroups, function (i, group) {
  88. groupsSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'));
  89. if (typeof subAdminSelect !== 'undefined' && group !== 'admin') {
  90. subAdminSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'));
  91. }
  92. });
  93. $tr.find('td.groups').empty().append(groupsSelect);
  94. subAdminsEl = $tr.find('td.subadmins');
  95. if (subAdminsEl.length > 0) {
  96. subAdminsEl.append(subAdminSelect);
  97. }
  98. /**
  99. * remove action
  100. */
  101. if ($tr.find('td.remove img').length === 0 && OC.currentUser !== user.name) {
  102. var deleteImage = $('<img class="svg action">').attr({
  103. src: OC.imagePath('core', 'actions/delete')
  104. });
  105. var deleteLink = $('<a class="action delete">')
  106. .attr({ href: '#', 'original-title': t('settings', 'Delete')})
  107. .append(deleteImage);
  108. $tr.find('td.remove').append(deleteLink);
  109. } else if (OC.currentUser === user.name) {
  110. $tr.find('td.remove a').remove();
  111. }
  112. /**
  113. * quota
  114. */
  115. var $quotaSelect = $tr.find('.quota-user');
  116. if (user.quota === 'default') {
  117. $quotaSelect
  118. .data('previous', 'default')
  119. .find('option').attr('selected', null)
  120. .first().attr('selected', 'selected');
  121. } else {
  122. if ($quotaSelect.find('option').filterAttr('value', user.quota).length > 0) {
  123. $quotaSelect.find('option').filterAttr('value', user.quota).attr('selected', 'selected');
  124. } else {
  125. $quotaSelect.append('<option value="' + escapeHTML(user.quota) + '" selected="selected">' + escapeHTML(user.quota) + '</option>');
  126. }
  127. }
  128. /**
  129. * storage location
  130. */
  131. $tr.find('td.storageLocation').text(user.storageLocation);
  132. /**
  133. * user backend
  134. */
  135. $tr.find('td.userBackend').text(user.backend);
  136. /**
  137. * last login
  138. */
  139. var lastLoginRel = t('settings', 'never');
  140. var lastLoginAbs = lastLoginRel;
  141. if(user.lastLogin !== 0) {
  142. lastLoginRel = OC.Util.relativeModifiedDate(user.lastLogin);
  143. lastLoginAbs = OC.Util.formatDate(user.lastLogin);
  144. }
  145. var $tdLastLogin = $tr.find('td.lastLogin');
  146. $tdLastLogin.text(lastLoginRel);
  147. //tooltip makes it complicated … to not insert new HTML, we adjust the
  148. //original title. We use a temporary div to get back the html that we
  149. //can pass later. It is also required to initialise tipsy.
  150. var tooltip = $('<div>').html($($tdLastLogin.attr('original-title')).text(lastLoginAbs)).html();
  151. $tdLastLogin.tipsy({gravity:'s', html:true});
  152. $tdLastLogin.attr('title', tooltip);
  153. /**
  154. * append generated row to user list
  155. */
  156. $tr.appendTo($userList);
  157. if(UserList.isEmpty === true) {
  158. //when the list was emptied, one row was left, necessary to keep
  159. //add working and the layout unbroken. We need to remove this item
  160. $tr.show();
  161. $userListBody.find('tr:first').remove();
  162. UserList.isEmpty = false;
  163. UserList.checkUsersToLoad();
  164. }
  165. /**
  166. * sort list
  167. */
  168. if (sort) {
  169. UserList.doSort();
  170. }
  171. $quotaSelect.on('change', UserList.onQuotaSelect);
  172. // defer init so the user first sees the list appear more quickly
  173. window.setTimeout(function(){
  174. $quotaSelect.singleSelect();
  175. UserList.applyGroupSelect(groupsSelect);
  176. if (subAdminSelect) {
  177. UserList.applySubadminSelect(subAdminSelect);
  178. }
  179. }, 0);
  180. return $tr;
  181. },
  182. // From http://my.opera.com/GreyWyvern/blog/show.dml/1671288
  183. alphanum: function(a, b) {
  184. function chunkify(t) {
  185. var tz = [], x = 0, y = -1, n = 0, i, j;
  186. while (i = (j = t.charAt(x++)).charCodeAt(0)) {
  187. var m = (i === 46 || (i >=48 && i <= 57));
  188. if (m !== n) {
  189. tz[++y] = "";
  190. n = m;
  191. }
  192. tz[y] += j;
  193. }
  194. return tz;
  195. }
  196. var aa = chunkify(a.toLowerCase());
  197. var bb = chunkify(b.toLowerCase());
  198. for (var x = 0; aa[x] && bb[x]; x++) {
  199. if (aa[x] !== bb[x]) {
  200. var c = Number(aa[x]), d = Number(bb[x]);
  201. if (c === aa[x] && d === bb[x]) {
  202. return c - d;
  203. } else {
  204. return (aa[x] > bb[x]) ? 1 : -1;
  205. }
  206. }
  207. }
  208. return aa.length - bb.length;
  209. },
  210. preSortSearchString: function(a, b) {
  211. var pattern = filter.getPattern();
  212. if(typeof pattern === 'undefined') {
  213. return undefined;
  214. }
  215. pattern = pattern.toLowerCase();
  216. var aMatches = false;
  217. var bMatches = false;
  218. if(typeof a === 'string' && a.toLowerCase().indexOf(pattern) === 0) {
  219. aMatches = true;
  220. }
  221. if(typeof b === 'string' && b.toLowerCase().indexOf(pattern) === 0) {
  222. bMatches = true;
  223. }
  224. if((aMatches && bMatches) || (!aMatches && !bMatches)) {
  225. return undefined;
  226. }
  227. if(aMatches) {
  228. return -1;
  229. } else {
  230. return 1;
  231. }
  232. },
  233. doSort: function() {
  234. var rows = $userListBody.find('tr').get();
  235. rows.sort(function(a, b) {
  236. // FIXME: inefficient way of getting the names,
  237. // better use a data attribute
  238. a = $(a).find('.name').text();
  239. b = $(b).find('.name').text();
  240. var firstSort = UserList.preSortSearchString(a, b);
  241. if(typeof firstSort !== 'undefined') {
  242. return firstSort;
  243. }
  244. return OC.Util.naturalSortCompare(a, b);
  245. });
  246. var items = [];
  247. $.each(rows, function(index, row) {
  248. items.push(row);
  249. if(items.length === 100) {
  250. $userListBody.append(items);
  251. items = [];
  252. }
  253. });
  254. if(items.length > 0) {
  255. $userListBody.append(items);
  256. }
  257. },
  258. checkUsersToLoad: function() {
  259. //30 shall be loaded initially, from then on always 10 upon scrolling
  260. if(UserList.isEmpty === false) {
  261. UserList.usersToLoad = 10;
  262. } else {
  263. UserList.usersToLoad = 30;
  264. }
  265. },
  266. empty: function() {
  267. //one row needs to be kept, because it is cloned to add new rows
  268. $userListBody.find('tr:not(:first)').remove();
  269. var $tr = $userListBody.find('tr:first');
  270. $tr.hide();
  271. //on an update a user may be missing when the username matches with that
  272. //of the hidden row. So change this to a random string.
  273. $tr.data('uid', Math.random().toString(36).substring(2));
  274. UserList.isEmpty = true;
  275. UserList.offset = 0;
  276. UserList.checkUsersToLoad();
  277. },
  278. hide: function(uid) {
  279. UserList.getRow(uid).hide();
  280. },
  281. show: function(uid) {
  282. UserList.getRow(uid).show();
  283. },
  284. markRemove: function(uid) {
  285. var $tr = UserList.getRow(uid);
  286. var groups = $tr.find('.groups .groupsselect').val();
  287. for(var i in groups) {
  288. var gid = groups[i];
  289. var $li = GroupList.getGroupLI(gid);
  290. var userCount = GroupList.getUserCount($li);
  291. if(userCount === 1) {
  292. GroupList.setUserCount($li, '');
  293. } else {
  294. GroupList.setUserCount($li, userCount - 1);
  295. }
  296. }
  297. GroupList.decEveryoneCount();
  298. UserList.hide(uid);
  299. },
  300. remove: function(uid) {
  301. UserList.getRow(uid).remove();
  302. },
  303. undoRemove: function(uid) {
  304. var $tr = UserList.getRow(uid);
  305. var groups = $tr.find('.groups .groupsselect').val();
  306. for(var i in groups) {
  307. var gid = groups[i];
  308. var $li = GroupList.getGroupLI(gid);
  309. var userCount = GroupList.getUserCount($li);
  310. if(userCount === 1) {
  311. GroupList.setUserCount($li, '');
  312. } else {
  313. GroupList.setUserCount($li, userCount + 1);
  314. }
  315. }
  316. GroupList.incEveryoneCount();
  317. UserList.getRow(uid).show();
  318. },
  319. has: function(uid) {
  320. return UserList.getRow(uid).length > 0;
  321. },
  322. getRow: function(uid) {
  323. return $userListBody.find('tr').filter(function(){
  324. return UserList.getUID(this) === uid;
  325. });
  326. },
  327. getUID: function(element) {
  328. return ($(element).closest('tr').data('uid') || '').toString();
  329. },
  330. getDisplayName: function(element) {
  331. return ($(element).closest('tr').data('displayname') || '').toString();
  332. },
  333. getMailAddress: function(element) {
  334. return ($(element).closest('tr').data('mailAddress') || '').toString();
  335. },
  336. getRestoreDisabled: function(element) {
  337. return ($(element).closest('tr').data('restoreDisabled') || '');
  338. },
  339. initDeleteHandling: function() {
  340. //set up handler
  341. UserDeleteHandler = new DeleteHandler('/settings/users/users', 'username',
  342. UserList.markRemove, UserList.remove);
  343. //configure undo
  344. OC.Notification.hide();
  345. var msg = escapeHTML(t('settings', 'deleted {userName}', {userName: '%oid'})) + '<span class="undo">' +
  346. escapeHTML(t('settings', 'undo')) + '</span>';
  347. UserDeleteHandler.setNotification(OC.Notification, 'deleteuser', msg,
  348. UserList.undoRemove);
  349. //when to mark user for delete
  350. $userListBody.on('click', '.delete', function () {
  351. // Call function for handling delete/undo
  352. var uid = UserList.getUID(this);
  353. UserDeleteHandler.mark(uid);
  354. });
  355. //delete a marked user when leaving the page
  356. $(window).on('beforeunload', function () {
  357. UserDeleteHandler.deleteEntry();
  358. });
  359. },
  360. update: function (gid, limit) {
  361. if (UserList.updating) {
  362. return;
  363. }
  364. if(!limit) {
  365. limit = UserList.usersToLoad;
  366. }
  367. $userList.siblings('.loading').css('visibility', 'visible');
  368. UserList.updating = true;
  369. if(gid === undefined) {
  370. gid = '';
  371. }
  372. UserList.currentGid = gid;
  373. var pattern = filter.getPattern();
  374. $.get(
  375. OC.generateUrl('/settings/users/users'),
  376. { offset: UserList.offset, limit: limit, gid: gid, pattern: pattern },
  377. function (result) {
  378. var loadedUsers = 0;
  379. var trs = [];
  380. //The offset does not mirror the amount of users available,
  381. //because it is backend-dependent. For correct retrieval,
  382. //always the limit(requested amount of users) needs to be added.
  383. $.each(result, function (index, user) {
  384. if(UserList.has(user.name)) {
  385. return true;
  386. }
  387. var $tr = UserList.add(user, user.lastLogin, false, user.backend);
  388. trs.push($tr);
  389. loadedUsers++;
  390. });
  391. if (result.length > 0) {
  392. UserList.doSort();
  393. $userList.siblings('.loading').css('visibility', 'hidden');
  394. // reset state on load
  395. UserList.noMoreEntries = false;
  396. }
  397. else {
  398. UserList.noMoreEntries = true;
  399. $userList.siblings('.loading').remove();
  400. }
  401. UserList.offset += limit;
  402. }).always(function() {
  403. UserList.updating = false;
  404. });
  405. },
  406. applyGroupSelect: function (element) {
  407. var checked = [];
  408. var $element = $(element);
  409. var user = UserList.getUID($element);
  410. if ($element.data('user-groups')) {
  411. if (typeof $element.data('user-groups') === 'string') {
  412. checked = $element.data('user-groups').split(", ");
  413. }
  414. else {
  415. checked = $element.data('user-groups');
  416. }
  417. }
  418. var checkHandler = null;
  419. if(user) { // Only if in a user row, and not the #newusergroups select
  420. checkHandler = function (group) {
  421. if (user === OC.currentUser && group === 'admin') {
  422. return false;
  423. }
  424. if (!oc_isadmin && checked.length === 1 && checked[0] === group) {
  425. return false;
  426. }
  427. $.post(
  428. OC.filePath('settings', 'ajax', 'togglegroups.php'),
  429. {
  430. username: user,
  431. group: group
  432. },
  433. function (response) {
  434. if (response.status === 'success') {
  435. GroupList.update();
  436. var groupName = response.data.groupname;
  437. if (UserList.availableGroups.indexOf(groupName) === -1 &&
  438. response.data.action === 'add'
  439. ) {
  440. UserList.availableGroups.push(groupName);
  441. }
  442. // in case this was the last user in that group the group has to be removed
  443. var groupElement = GroupList.getGroupLI(groupName);
  444. var userCount = GroupList.getUserCount(groupElement);
  445. if (response.data.action === 'remove' && userCount === 1) {
  446. _.without(UserList.availableGroups, groupName);
  447. GroupList.remove(groupName);
  448. $('.groupsselect option').filterAttr('value', groupName).remove();
  449. $('.subadminsselect option').filterAttr('value', groupName).remove();
  450. }
  451. }
  452. if (response.data.message) {
  453. OC.Notification.show(response.data.message);
  454. }
  455. }
  456. );
  457. };
  458. }
  459. var addGroup = function (select, group) {
  460. $('select[multiple]').each(function (index, element) {
  461. $element = $(element);
  462. if ($element.find('option').filterAttr('value', group).length === 0 &&
  463. select.data('msid') !== $element.data('msid')) {
  464. $element.append('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>');
  465. }
  466. });
  467. GroupList.addGroup(escapeHTML(group));
  468. };
  469. var label;
  470. if (oc_isadmin) {
  471. label = t('settings', 'add group');
  472. }
  473. else {
  474. label = null;
  475. }
  476. $element.multiSelect({
  477. createCallback: addGroup,
  478. createText: label,
  479. selectedFirst: true,
  480. checked: checked,
  481. oncheck: checkHandler,
  482. onuncheck: checkHandler,
  483. minWidth: 100
  484. });
  485. },
  486. applySubadminSelect: function (element) {
  487. var checked = [];
  488. var $element = $(element);
  489. var user = UserList.getUID($element);
  490. if ($element.data('subadmin')) {
  491. if (typeof $element.data('subadmin') === 'string') {
  492. checked = $element.data('subadmin').split(", ");
  493. }
  494. else {
  495. checked = $element.data('subadmin');
  496. }
  497. }
  498. var checkHandler = function (group) {
  499. if (group === 'admin') {
  500. return false;
  501. }
  502. $.post(
  503. OC.filePath('settings', 'ajax', 'togglesubadmins.php'),
  504. {
  505. username: user,
  506. group: group
  507. },
  508. function () {
  509. }
  510. );
  511. };
  512. var addSubAdmin = function (group) {
  513. $('select[multiple]').each(function (index, element) {
  514. if ($(element).find('option').filterAttr('value', group).length === 0) {
  515. $(element).append('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>');
  516. }
  517. });
  518. };
  519. $element.multiSelect({
  520. createCallback: addSubAdmin,
  521. createText: null,
  522. checked: checked,
  523. oncheck: checkHandler,
  524. onuncheck: checkHandler,
  525. minWidth: 100
  526. });
  527. },
  528. _onScroll: function() {
  529. if (!!UserList.noMoreEntries) {
  530. return;
  531. }
  532. if (UserList.scrollArea.scrollTop() + UserList.scrollArea.height() > UserList.scrollArea.get(0).scrollHeight - 500) {
  533. UserList.update(UserList.currentGid);
  534. }
  535. },
  536. /**
  537. * Event handler for when a quota has been changed through a single select.
  538. * This will save the value.
  539. */
  540. onQuotaSelect: function(ev) {
  541. var $select = $(ev.target);
  542. var uid = UserList.getUID($select);
  543. var quota = $select.val();
  544. UserList._updateQuota(uid, quota, function(returnedQuota){
  545. if (quota !== returnedQuota) {
  546. $select.find(':selected').text(returnedQuota);
  547. }
  548. });
  549. },
  550. /**
  551. * Saves the quota for the given user
  552. * @param {String} [uid] optional user id, sets default quota if empty
  553. * @param {String} quota quota value
  554. * @param {Function} ready callback after save
  555. */
  556. _updateQuota: function(uid, quota, ready) {
  557. $.post(
  558. OC.filePath('settings', 'ajax', 'setquota.php'),
  559. {username: uid, quota: quota},
  560. function (result) {
  561. if (ready) {
  562. ready(result.data.quota);
  563. }
  564. }
  565. );
  566. }
  567. };
  568. $(document).ready(function () {
  569. $userList = $('#userlist');
  570. $userListBody = $userList.find('tbody');
  571. UserList.initDeleteHandling();
  572. // Implements User Search
  573. filter = new UserManagementFilter($('#usersearchform input'), UserList, GroupList);
  574. UserList.doSort();
  575. UserList.availableGroups = $userList.data('groups');
  576. UserList.scrollArea = $('#app-content');
  577. UserList.scrollArea.scroll(function(e) {UserList._onScroll(e);});
  578. $userList.after($('<div class="loading" style="height: 200px; visibility: hidden;"></div>'));
  579. // TODO: move other init calls inside of initialize
  580. UserList.initialize($('#userlist'));
  581. $('.groupsselect').each(function (index, element) {
  582. UserList.applyGroupSelect(element);
  583. });
  584. $('.subadminsselect').each(function (index, element) {
  585. UserList.applySubadminSelect(element);
  586. });
  587. $userListBody.on('click', '.password', function (event) {
  588. event.stopPropagation();
  589. var $td = $(this).closest('td');
  590. var $tr = $(this).closest('tr');
  591. var uid = UserList.getUID($td);
  592. var $input = $('<input type="password">');
  593. var isRestoreDisabled = UserList.getRestoreDisabled($td) === true;
  594. if(isRestoreDisabled) {
  595. $tr.addClass('row-warning');
  596. // add tipsy if the password change could cause data loss - no recovery enabled
  597. $input.tipsy({gravity:'s'});
  598. $input.attr('title', t('settings', 'Changing the password will result in data loss, because data recovery is not available for this user'));
  599. }
  600. $td.find('img').hide();
  601. $td.children('span').replaceWith($input);
  602. $input
  603. .focus()
  604. .keypress(function (event) {
  605. if (event.keyCode === 13) {
  606. if ($(this).val().length > 0) {
  607. var recoveryPasswordVal = $('input:password[id="recoveryPassword"]').val();
  608. $.post(
  609. OC.generateUrl('/settings/users/changepassword'),
  610. {username: uid, password: $(this).val(), recoveryPassword: recoveryPasswordVal},
  611. function (result) {
  612. if (result.status != 'success') {
  613. OC.Notification.show(t('admin', result.data.message));
  614. }
  615. }
  616. );
  617. $input.blur();
  618. } else {
  619. $input.blur();
  620. }
  621. }
  622. })
  623. .blur(function () {
  624. $(this).replaceWith($('<span>●●●●●●●</span>'));
  625. $td.find('img').show();
  626. // remove highlight class from users without recovery ability
  627. $tr.removeClass('row-warning');
  628. });
  629. });
  630. $('input:password[id="recoveryPassword"]').keyup(function() {
  631. OC.Notification.hide();
  632. });
  633. $userListBody.on('click', '.displayName', function (event) {
  634. event.stopPropagation();
  635. var $td = $(this).closest('td');
  636. var $tr = $td.closest('tr');
  637. var uid = UserList.getUID($td);
  638. var displayName = escapeHTML(UserList.getDisplayName($td));
  639. var $input = $('<input type="text" value="' + displayName + '">');
  640. $td.find('img').hide();
  641. $td.children('span').replaceWith($input);
  642. $input
  643. .focus()
  644. .keypress(function (event) {
  645. if (event.keyCode === 13) {
  646. if ($(this).val().length > 0) {
  647. var $div = $tr.find('div.avatardiv');
  648. if ($div.length) {
  649. $div.imageplaceholder(uid, displayName);
  650. }
  651. $.post(
  652. OC.filePath('settings', 'ajax', 'changedisplayname.php'),
  653. {username: uid, displayName: $(this).val()},
  654. function (result) {
  655. if (result && result.status==='success' && $div.length){
  656. $div.avatar(result.data.username, 32);
  657. }
  658. }
  659. );
  660. $input.blur();
  661. } else {
  662. $input.blur();
  663. }
  664. }
  665. })
  666. .blur(function () {
  667. var displayName = $input.val();
  668. $tr.data('displayname', displayName);
  669. $input.replaceWith('<span>' + escapeHTML(displayName) + '</span>');
  670. $td.find('img').show();
  671. });
  672. });
  673. $userListBody.on('click', '.mailAddress', function (event) {
  674. event.stopPropagation();
  675. var $td = $(this).closest('td');
  676. var $tr = $td.closest('tr');
  677. var uid = UserList.getUID($td);
  678. var mailAddress = escapeHTML(UserList.getMailAddress($td));
  679. var $input = $('<input type="text">').val(mailAddress);
  680. $td.children('span').replaceWith($input);
  681. $input
  682. .focus()
  683. .keypress(function (event) {
  684. if (event.keyCode === 13) {
  685. if ($(this).val().length > 0) {
  686. $input.blur();
  687. $.ajax({
  688. type: 'PUT',
  689. url: OC.generateUrl('/settings/users/{id}/mailAddress', {id: uid}),
  690. data: {
  691. mailAddress: $(this).val()
  692. }
  693. }).fail(function (result) {
  694. OC.Notification.show(result.responseJSON.data.message);
  695. // reset the values
  696. $tr.data('mailAddress', mailAddress);
  697. $tr.children('.mailAddress').children('span').text(mailAddress);
  698. });
  699. } else {
  700. $input.blur();
  701. }
  702. }
  703. })
  704. .blur(function () {
  705. var mailAddress = $input.val();
  706. var $span = $('<span>').text(mailAddress);
  707. $tr.data('mailAddress', mailAddress);
  708. $input.replaceWith($span);
  709. });
  710. });
  711. // init the quota field select box after it is shown the first time
  712. $('#app-settings').one('show', function() {
  713. $(this).find('#default_quota').singleSelect().on('change', UserList.onQuotaSelect);
  714. });
  715. $('#newuser').submit(function (event) {
  716. event.preventDefault();
  717. var username = $('#newusername').val();
  718. var password = $('#newuserpassword').val();
  719. var email = $('#newemail').val();
  720. if ($.trim(username) === '') {
  721. OC.dialogs.alert(
  722. t('settings', 'A valid username must be provided'),
  723. t('settings', 'Error creating user'));
  724. return false;
  725. }
  726. if ($.trim(password) === '') {
  727. OC.dialogs.alert(
  728. t('settings', 'A valid password must be provided'),
  729. t('settings', 'Error creating user'));
  730. return false;
  731. }
  732. if(!$('#CheckboxMailOnUserCreate').is(':checked')) {
  733. email = '';
  734. }
  735. if ($('#CheckboxMailOnUserCreate').is(':checked') && $.trim(email) === '') {
  736. OC.dialogs.alert(
  737. t('settings', 'A valid email must be provided'),
  738. t('settings', 'Error creating user'));
  739. return false;
  740. }
  741. var groups = $('#newusergroups').val() || [];
  742. $.post(
  743. OC.generateUrl('/settings/users/users'),
  744. {
  745. username: username,
  746. password: password,
  747. groups: groups,
  748. email: email
  749. },
  750. function (result) {
  751. if (result.groups) {
  752. for (var i in result.groups) {
  753. var gid = result.groups[i];
  754. if(UserList.availableGroups.indexOf(gid) === -1) {
  755. UserList.availableGroups.push(gid);
  756. }
  757. $li = GroupList.getGroupLI(gid);
  758. userCount = GroupList.getUserCount($li);
  759. GroupList.setUserCount($li, userCount + 1);
  760. }
  761. }
  762. if(!UserList.has(username)) {
  763. UserList.add(result, true);
  764. }
  765. $('#newusername').focus();
  766. GroupList.incEveryoneCount();
  767. }).fail(function(result, textStatus, errorThrown) {
  768. OC.dialogs.alert(result.responseJSON.message, t('settings', 'Error creating user'));
  769. }).success(function(){
  770. $('#newuser').get(0).reset();
  771. });
  772. });
  773. // Option to display/hide the "Storage location" column
  774. $('#CheckboxStorageLocation').click(function() {
  775. if ($('#CheckboxStorageLocation').is(':checked')) {
  776. $("#userlist .storageLocation").show();
  777. } else {
  778. $("#userlist .storageLocation").hide();
  779. }
  780. });
  781. // Option to display/hide the "Last Login" column
  782. $('#CheckboxLastLogin').click(function() {
  783. if ($('#CheckboxLastLogin').is(':checked')) {
  784. $("#userlist .lastLogin").show();
  785. } else {
  786. $("#userlist .lastLogin").hide();
  787. }
  788. });
  789. // Option to display/hide the "Mail Address" column
  790. $('#CheckboxEmailAddress').click(function() {
  791. if ($('#CheckboxEmailAddress').is(':checked')) {
  792. $("#userlist .mailAddress").show();
  793. } else {
  794. $("#userlist .mailAddress").hide();
  795. }
  796. });
  797. // Option to display/hide the "User Backend" column
  798. $('#CheckboxUserBackend').click(function() {
  799. if ($('#CheckboxUserBackend').is(':checked')) {
  800. $("#userlist .userBackend").show();
  801. } else {
  802. $("#userlist .userBackend").hide();
  803. }
  804. });
  805. // Option to display/hide the "E-Mail" input field
  806. $('#CheckboxMailOnUserCreate').click(function() {
  807. if ($('#CheckboxMailOnUserCreate').is(':checked')) {
  808. $("#newemail").show();
  809. } else {
  810. $("#newemail").hide();
  811. }
  812. });
  813. // calculate initial limit of users to load
  814. var initialUserCountLimit = 20,
  815. containerHeight = $('#app-content').height();
  816. if(containerHeight > 40) {
  817. initialUserCountLimit = Math.floor(containerHeight/40);
  818. while((initialUserCountLimit % UserList.usersToLoad) !== 0) {
  819. // must be a multiple of this, otherwise LDAP freaks out.
  820. // FIXME: solve this in LDAP backend in 8.1
  821. initialUserCountLimit = initialUserCountLimit + 1;
  822. }
  823. }
  824. // trigger loading of users on startup
  825. UserList.update(UserList.currentGid, initialUserCountLimit);
  826. });