diff options
author | Thomas Müller <thomas.mueller@tmit.eu> | 2014-06-03 15:04:30 +0200 |
---|---|---|
committer | Thomas Müller <thomas.mueller@tmit.eu> | 2014-06-03 15:04:30 +0200 |
commit | 79b3558c6d6f76095a04bb32bed05dbb5493ca91 (patch) | |
tree | 2a31a2f700ef6171ad0d8b2fc3ec1c3b4060a523 /settings | |
parent | 2b1a7a76c37239c4f515f701a686889a359f4567 (diff) | |
parent | 39982c2aea841658bd6dbc67db50b568a32c8608 (diff) | |
download | nextcloud-server-79b3558c6d6f76095a04bb32bed05dbb5493ca91.tar.gz nextcloud-server-79b3558c6d6f76095a04bb32bed05dbb5493ca91.zip |
Merge pull request #7151 from owncloud/user-jquery
Continuing the New User management
Diffstat (limited to 'settings')
-rw-r--r-- | settings/ajax/creategroup.php | 1 | ||||
-rw-r--r-- | settings/ajax/createuser.php | 9 | ||||
-rw-r--r-- | settings/ajax/grouplist.php | 48 | ||||
-rw-r--r-- | settings/ajax/userlist.php | 51 | ||||
-rw-r--r-- | settings/css/settings.css | 42 | ||||
-rw-r--r-- | settings/js/users.js | 546 | ||||
-rw-r--r-- | settings/js/users/deleteHandler.js | 171 | ||||
-rw-r--r-- | settings/js/users/filter.js | 82 | ||||
-rw-r--r-- | settings/js/users/groups.js | 292 | ||||
-rw-r--r-- | settings/js/users/users.js | 616 | ||||
-rw-r--r-- | settings/routes.php | 4 | ||||
-rw-r--r-- | settings/templates/users.php | 176 | ||||
-rw-r--r-- | settings/templates/users/main.php | 32 | ||||
-rw-r--r-- | settings/templates/users/part.createuser.php | 34 | ||||
-rw-r--r-- | settings/templates/users/part.grouplist.php | 50 | ||||
-rw-r--r-- | settings/templates/users/part.setquota.php | 39 | ||||
-rw-r--r-- | settings/templates/users/part.userlist.php | 116 | ||||
-rw-r--r-- | settings/users.php | 42 |
18 files changed, 1595 insertions, 756 deletions
diff --git a/settings/ajax/creategroup.php b/settings/ajax/creategroup.php index 0a79527c219..854f2c37189 100644 --- a/settings/ajax/creategroup.php +++ b/settings/ajax/creategroup.php @@ -4,6 +4,7 @@ OCP\JSON::callCheck(); OC_JSON::checkAdminUser(); $groupname = $_POST["groupname"]; +$l = OC_L10N::get('settings'); // Does the group exist? if( in_array( $groupname, OC_Group::getGroups())) { diff --git a/settings/ajax/createuser.php b/settings/ajax/createuser.php index 94b56fa0349..ae1d8856f43 100644 --- a/settings/ajax/createuser.php +++ b/settings/ajax/createuser.php @@ -43,12 +43,15 @@ try { OC_Group::addToGroup( $username, $i ); } - OC_JSON::success(array("data" => + $userManager = \OC_User::getManager(); + $user = $userManager->get($username); + OCP\JSON::success(array("data" => array( // returns whether the home already existed "homeExists" => $homeExists, "username" => $username, - "groups" => OC_Group::getUserGroups( $username )))); + "groups" => OC_Group::getUserGroups( $username ), + 'storageLocation' => $user->getHome()))); } catch (Exception $exception) { - OC_JSON::error(array("data" => array( "message" => $exception->getMessage()))); + OCP\JSON::error(array("data" => array( "message" => $exception->getMessage()))); } diff --git a/settings/ajax/grouplist.php b/settings/ajax/grouplist.php new file mode 100644 index 00000000000..91700adc359 --- /dev/null +++ b/settings/ajax/grouplist.php @@ -0,0 +1,48 @@ +<?php +/** + * ownCloud + * + * @author Arthur Schiwon + * @copyright 2014 Arthur Schiwon <blizzz@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +OC_JSON::callCheck(); +OC_JSON::checkSubAdminUser(); +if (isset($_GET['pattern']) && !empty($_GET['pattern'])) { + $pattern = $_GET['pattern']; +} else { + $pattern = ''; +} +$groups = array(); +$adminGroups = array(); +$groupManager = \OC_Group::getManager(); +$isAdmin = OC_User::isAdminUser(OC_User::getUser()); + +//we pass isAdmin as true, because OC_SubAdmin has no search feature, +//groups will be filtered out later +$groupsInfo = new \OC\Group\MetaData(OC_User::getUser(), true, $groupManager); +$groupsInfo->setSorting($groupsInfo::SORT_USERCOUNT); +list($adminGroups, $groups) = $groupsInfo->get($pattern); + +$accessibleGroups = $groupManager->search($pattern); +if(!$isAdmin) { + $subadminGroups = OC_SubAdmin::getSubAdminsGroups(OC_User::getUser()); + $accessibleGroups = array_intersect($groups, $subadminGroups); +} + +OC_JSON::success( + array('data' => array('adminGroups' => $adminGroups, 'groups' => $groups))); diff --git a/settings/ajax/userlist.php b/settings/ajax/userlist.php index 4abf54b8987..32237d60b6e 100644 --- a/settings/ajax/userlist.php +++ b/settings/ajax/userlist.php @@ -32,26 +32,55 @@ if (isset($_GET['limit'])) { } else { $limit = 10; } +if (isset($_GET['gid']) && !empty($_GET['gid'])) { + $gid = $_GET['gid']; +} else { + $gid = false; +} +if (isset($_GET['pattern']) && !empty($_GET['pattern'])) { + $pattern = $_GET['pattern']; +} else { + $pattern = ''; +} $users = array(); +$userManager = \OC_User::getManager(); if (OC_User::isAdminUser(OC_User::getUser())) { - $batch = OC_User::getDisplayNames('', $limit, $offset); - foreach ($batch as $user => $displayname) { + if($gid !== false) { + $batch = OC_Group::displayNamesInGroup($gid, $pattern, $limit, $offset); + } else { + $batch = OC_User::getDisplayNames($pattern, $limit, $offset); + } + foreach ($batch as $uid => $displayname) { + $user = $userManager->get($uid); $users[] = array( - 'name' => $user, + 'name' => $uid, 'displayname' => $displayname, - 'groups' => join(', ', OC_Group::getUserGroups($user)), - 'subadmin' => join(', ', OC_SubAdmin::getSubAdminsGroups($user)), - 'quota' => OC_Preferences::getValue($user, 'files', 'quota', 'default')); + 'groups' => join(', ', OC_Group::getUserGroups($uid)), + 'subadmin' => join(', ', OC_SubAdmin::getSubAdminsGroups($uid)), + 'quota' => OC_Preferences::getValue($uid, 'files', 'quota', 'default'), + 'storageLocation' => $user->getHome(), + 'lastLogin' => $user->getLastLogin(), + ); } } else { $groups = OC_SubAdmin::getSubAdminsGroups(OC_User::getUser()); - $batch = OC_Group::usersInGroups($groups, '', $limit, $offset); - foreach ($batch as $user) { + if($gid !== false && in_array($gid, $groups)) { + $groups = array($gid); + } elseif($gid !== false) { + //don't you try to investigate loops you must not know about + $groups = array(); + } + $batch = OC_Group::usersInGroups($groups, $pattern, $limit, $offset); + foreach ($batch as $uid) { + $user = $userManager->get($uid); $users[] = array( 'name' => $user, - 'displayname' => OC_User::getDisplayName($user), - 'groups' => join(', ', OC_Group::getUserGroups($user)), - 'quota' => OC_Preferences::getValue($user, 'files', 'quota', 'default')); + 'displayname' => $user->getDisplayName(), + 'groups' => join(', ', OC_Group::getUserGroups($uid)), + 'quota' => OC_Preferences::getValue($uid, 'files', 'quota', 'default'), + 'storageLocation' => $user->getHome(), + 'lastLogin' => $user->getLastLogin(), + ); } } OC_JSON::success(array('data' => $users)); diff --git a/settings/css/settings.css b/settings/css/settings.css index 10d740c81b7..b67c88a9c8f 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -5,6 +5,13 @@ select#languageinput, select#timezone { width:15em; } input#openid, input#webdav { width:20em; } +#user-controls { + -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; + position: fixed; right: 0; left:380px; height: 44px; + padding: 0; margin: 0; + background: #eee; border-bottom: 1px solid #e7e7e7; + z-index: 50; +} /* PERSONAL */ #rootcert_import { @@ -44,7 +51,37 @@ table.nostyle label { margin-right: 2em; } table.nostyle td { padding: 0.2em 0; } /* USERS */ +#newgroup-init a span { margin-left: 20px; } +#newgroup-init a span:before { + position: absolute; left: 12px; top:-2px; + content: '+'; font-weight: bold; font-size: 150%; +} +.usercount { float: left; margin: 5px; } +li.active span.utils .delete { + float: left; position: relative; opacity: 0.5; + top: -7px; left: 7px; width: 44px; height: 44px; +} +li.active .rename { + padding: 8px 14px 20px 14px; + top: 0px; position: absolute; width: 16px; height: 16px; + opacity: 0.5; + display: inline-block !important; +} +li.active span.utils .delete img { margin: 14px; } +li.active .rename { opacity: 0.5; } +li.active span.utils .delete:hover, li.active .rename:hover { opacity: 1; } +span.utils .delete, .rename { display: none; } +#app-navigation ul li.active > span.utils .delete, +#app-navigation ul li.active > span.utils .rename { display: block; } +#usersearchform { position: absolute; top: 4px; right: 10px; } +#usersearchform label { font-weight: 700; } form { display:inline; } + +/* display table at full width */ +table.grid { + width: 100%; +} + table.grid th { height:2em; color:#999; } table.grid th, table.grid td { border-bottom:1px solid #ddd; padding:0 .5em; padding-left:.8em; text-align:left; font-weight:normal; } td.name, td.password { padding-left:.8em; } @@ -57,9 +94,8 @@ tr:hover>td.password>span, tr:hover>td.displayName>span { margin:0; cursor:point tr:hover>td.remove>a, tr:hover>td.password>img,tr:hover>td.displayName>img, tr:hover>td.quota>img { visibility:visible; cursor:pointer; } tr:hover>td.remove>a { float:right; } -table.grid { width:100%; } div.quota { - float: right; + margin: 10px; display: block; } div.quota-select-wrapper { position: relative; } @@ -78,6 +114,8 @@ div.quota>span { } select.quota.active { background: #fff; } +input.userFilter {width: 200px;} + /* positioning fixes */ #newuser .multiselect { min-width: 150px !important; diff --git a/settings/js/users.js b/settings/js/users.js deleted file mode 100644 index eef3c237277..00000000000 --- a/settings/js/users.js +++ /dev/null @@ -1,546 +0,0 @@ -/** - * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com> - * This file is licensed under the Affero General Public License version 3 or later. - * See the COPYING-README file. - */ - -function setQuota (uid, quota, ready) { - $.post( - OC.filePath('settings', 'ajax', 'setquota.php'), - {username: uid, quota: quota}, - function (result) { - if (ready) { - ready(result.data.quota); - } - } - ); -} - -var UserList = { - useUndo: true, - availableGroups: [], - offset: 30, //The first 30 users are there. No prob, if less in total. - //hardcoded in settings/users.php - - usersToLoad: 10, //So many users will be loaded when user scrolls down - - /** - * @brief Initiate user deletion process in UI - * @param string uid the user ID to be deleted - * - * Does not actually delete the user; it sets them for - * deletion when the current page is unloaded, at which point - * finishDelete() completes the process. This allows for 'undo'. - */ - do_delete: function (uid) { - if (typeof UserList.deleteUid !== 'undefined') { - //Already a user in the undo queue - UserList.finishDelete(null); - } - UserList.deleteUid = uid; - - // Set undo flag - UserList.deleteCanceled = false; - - // Provide user with option to undo - $('#notification').data('deleteuser', true); - OC.Notification.showHtml(t('settings', 'deleted') + ' ' + escapeHTML(uid) + '<span class="undo">' + t('settings', 'undo') + '</span>'); - }, - - /** - * @brief Delete a user via ajax - * @param bool ready whether to use ready() upon completion - * - * Executes deletion via ajax of user identified by property deleteUid - * if 'undo' has not been used. Completes the user deletion procedure - * and reflects success in UI. - */ - finishDelete: function (ready) { - - // Check deletion has not been undone - if (!UserList.deleteCanceled && UserList.deleteUid) { - - // Delete user via ajax - $.ajax({ - type: 'POST', - url: OC.filePath('settings', 'ajax', 'removeuser.php'), - async: false, - data: { username: UserList.deleteUid }, - success: function (result) { - if (result.status === 'success') { - // Remove undo option, & remove user from table - OC.Notification.hide(); - $('tr').filterAttr('data-uid', UserList.deleteUid).remove(); - UserList.deleteCanceled = true; - if (ready) { - ready(); - } - } else { - OC.dialogs.alert(result.data.message, t('settings', 'Unable to remove user')); - } - } - }); - } - }, - - add: function (username, displayname, groups, subadmin, quota, sort) { - var tr = $('tbody tr').first().clone(); - var subadminsEl; - var subadminSelect; - var groupsSelect; - if (tr.find('div.avatardiv').length){ - $('div.avatardiv', tr).avatar(username, 32); - } - tr.attr('data-uid', username); - tr.attr('data-displayName', displayname); - tr.find('td.name').text(username); - tr.find('td.displayName > span').text(displayname); - - // make them look like the multiselect buttons - // until they get time to really get initialized - groupsSelect = $('<select multiple="multiple" class="groupsselect multiselect button" data-placehoder="Groups" title="' + t('settings', 'Groups') + '"></select>') - .attr('data-username', username) - .data('user-groups', groups); - if (tr.find('td.subadmins').length > 0) { - subadminSelect = $('<select multiple="multiple" class="subadminsselect multiselect button" data-placehoder="subadmins" title="' + t('settings', 'Group Admin') + '">') - .attr('data-username', username) - .data('user-groups', groups) - .data('subadmin', subadmin); - tr.find('td.subadmins').empty(); - } - $.each(this.availableGroups, function (i, group) { - groupsSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>')); - if (typeof subadminSelect !== 'undefined' && group !== 'admin') { - subadminSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>')); - } - }); - tr.find('td.groups').empty().append(groupsSelect); - subadminsEl = tr.find('td.subadmins'); - if (subadminsEl.length > 0) { - subadminsEl.append(subadminSelect); - } - if (tr.find('td.remove img').length === 0 && OC.currentUser !== username) { - var rm_img = $('<img class="svg action">').attr({ - src: OC.imagePath('core', 'actions/delete') - }); - var rm_link = $('<a class="action delete">') - .attr({ href: '#', 'original-title': t('settings', 'Delete')}) - .append(rm_img); - tr.find('td.remove').append(rm_link); - } else if (OC.currentUser === username) { - tr.find('td.remove a').remove(); - } - var quotaSelect = tr.find('select.quota-user'); - if (quota === 'default') { - quotaSelect.find('option').attr('selected', null); - quotaSelect.find('option').first().attr('selected', 'selected'); - quotaSelect.data('previous', 'default'); - } else { - if (quotaSelect.find('option[value="' + quota + '"]').length > 0) { - quotaSelect.find('option[value="' + quota + '"]').attr('selected', 'selected'); - } else { - quotaSelect.append('<option value="' + escapeHTML(quota) + '" selected="selected">' + escapeHTML(quota) + '</option>'); - } - } - $(tr).appendTo('tbody'); - - if (sort) { - UserList.doSort(); - } - - quotaSelect.on('change', function () { - var uid = $(this).parent().parent().attr('data-uid'); - var quota = $(this).val(); - setQuota(uid, quota, function(returnedQuota){ - if (quota !== returnedQuota) { - $(quotaSelect).find(':selected').text(returnedQuota); - } - }); - }); - - // defer init so the user first sees the list appear more quickly - window.setTimeout(function(){ - quotaSelect.singleSelect(); - UserList.applyMultiplySelect(groupsSelect); - if (subadminSelect) { - UserList.applyMultiplySelect(subadminSelect); - } - }, 0); - return tr; - }, - // From http://my.opera.com/GreyWyvern/blog/show.dml/1671288 - alphanum: function(a, b) { - function chunkify(t) { - var tz = [], x = 0, y = -1, n = 0, i, j; - - while (i = (j = t.charAt(x++)).charCodeAt(0)) { - var m = (i === 46 || (i >=48 && i <= 57)); - if (m !== n) { - tz[++y] = ""; - n = m; - } - tz[y] += j; - } - return tz; - } - - var aa = chunkify(a.toLowerCase()); - var bb = chunkify(b.toLowerCase()); - - for (x = 0; aa[x] && bb[x]; x++) { - if (aa[x] !== bb[x]) { - var c = Number(aa[x]), d = Number(bb[x]); - if (c === aa[x] && d === bb[x]) { - return c - d; - } else { - return (aa[x] > bb[x]) ? 1 : -1; - } - } - } - return aa.length - bb.length; - }, - doSort: function() { - var self = this; - var rows = $('tbody tr').get(); - - rows.sort(function(a, b) { - return UserList.alphanum($(a).find('td.name').text(), $(b).find('td.name').text()); - }); - - var items = []; - $.each(rows, function(index, row) { - items.push(row); - if(items.length === 100) { - $('tbody').append(items); - items = []; - } - }); - if(items.length > 0) { - $('tbody').append(items); - } - }, - update: function () { - if (UserList.updating) { - return; - } - $('table+.loading').css('visibility', 'visible'); - UserList.updating = true; - var query = $.param({ offset: UserList.offset, limit: UserList.usersToLoad }); - $.get(OC.generateUrl('/settings/ajax/userlist') + '?' + query, function (result) { - var loadedUsers = 0; - var trs = []; - if (result.status === 'success') { - //The offset does not mirror the amount of users available, - //because it is backend-dependent. For correct retrieval, - //always the limit(requested amount of users) needs to be added. - $.each(result.data, function (index, user) { - if($('tr[data-uid="' + user.name + '"]').length > 0) { - return true; - } - var tr = UserList.add(user.name, user.displayname, user.groups, user.subadmin, user.quota, false); - tr.addClass('appear transparent'); - trs.push(tr); - loadedUsers++; - }); - if (result.data.length > 0) { - UserList.doSort(); - $('table+.loading').css('visibility', 'hidden'); - } - else { - UserList.noMoreEntries = true; - $('table+.loading').remove(); - } - UserList.offset += loadedUsers; - // animate - setTimeout(function() { - for (var i = 0; i < trs.length; i++) { - trs[i].removeClass('transparent'); - } - }, 0); - } - UserList.updating = false; - }); - }, - - applyMultiplySelect: function (element) { - var checked = []; - var user = element.attr('data-username'); - if ($(element).hasClass('groupsselect')) { - if (element.data('userGroups')) { - checked = element.data('userGroups'); - } - if (user) { - var checkHandeler = function (group) { - if (user === OC.currentUser && group === 'admin') { - return false; - } - if (!oc_isadmin && checked.length === 1 && checked[0] === group) { - return false; - } - $.post( - OC.filePath('settings', 'ajax', 'togglegroups.php'), - { - username: user, - group: group - }, - function (response) { - if(response.status === 'success' - && UserList.availableGroups.indexOf(response.data.groupname) === -1 - && response.data.action === 'add') { - UserList.availableGroups.push(response.data.groupname); - } - if(response.data.message) { - OC.Notification.show(response.data.message); - } - } - ); - }; - } else { - checkHandeler = false; - } - var addGroup = function (select, group) { - $('select[multiple]').each(function (index, element) { - if ($(element).find('option[value="' + group + '"]').length === 0 && select.data('msid') !== $(element).data('msid')) { - $(element).append('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'); - } - }); - }; - var label; - if (oc_isadmin) { - label = t('settings', 'add group'); - } else { - label = null; - } - element.multiSelect({ - createCallback: addGroup, - createText: label, - selectedFirst: true, - checked: checked, - oncheck: checkHandeler, - onuncheck: checkHandeler, - minWidth: 100 - }); - } - if ($(element).hasClass('subadminsselect')) { - if (element.data('subadmin')) { - checked = element.data('subadmin'); - } - var checkHandeler = function (group) { - if (group === 'admin') { - return false; - } - $.post( - OC.filePath('settings', 'ajax', 'togglesubadmins.php'), - { - username: user, - group: group - }, - function () { - } - ); - }; - - var addSubAdmin = function (group) { - $('select[multiple]').each(function (index, element) { - if ($(element).find('option[value="' + group + '"]').length === 0) { - $(element).append('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'); - } - }); - }; - element.multiSelect({ - createCallback: addSubAdmin, - createText: null, - checked: checked, - oncheck: checkHandeler, - onuncheck: checkHandeler, - minWidth: 100 - }); - } - }, - - _onScroll: function(e) { - if (!!UserList.noMoreEntries) { - return; - } - if ($(window).scrollTop() + $(window).height() > $(document).height() - 500) { - UserList.update(true); - } - }, -}; - -$(document).ready(function () { - - UserList.doSort(); - UserList.availableGroups = $('#content table').data('groups'); - $(window).scroll(function(e) {UserList._onScroll(e);}); - $('table').after($('<div class="loading" style="height: 200px; visibility: hidden;"></div>')); - - $('select[multiple]').each(function (index, element) { - UserList.applyMultiplySelect($(element)); - }); - - $('table').on('click', 'td.remove>a', function (event) { - var row = $(this).parent().parent(); - var uid = $(row).attr('data-uid'); - $(row).hide(); - // Call function for handling delete/undo - UserList.do_delete(uid); - }); - - $('table').on('click', 'td.password>img', function (event) { - event.stopPropagation(); - var img = $(this); - var uid = img.parent().parent().attr('data-uid'); - var input = $('<input type="password">'); - img.css('display', 'none'); - img.parent().children('span').replaceWith(input); - input.focus(); - input.keypress(function (event) { - if (event.keyCode === 13) { - if ($(this).val().length > 0) { - var recoveryPasswordVal = $('input:password[id="recoveryPassword"]').val(); - $.post( - OC.generateUrl('/settings/users/changepassword'), - {username: uid, password: $(this).val(), recoveryPassword: recoveryPasswordVal}, - function (result) { - if (result.status != 'success') { - OC.Notification.show(t('admin', result.data.message)); - } - } - ); - input.blur(); - } else { - input.blur(); - } - } - }); - input.blur(function () { - $(this).replaceWith($('<span>●●●●●●●</span>')); - img.css('display', ''); - }); - }); - $('input:password[id="recoveryPassword"]').keyup(function(event) { - OC.Notification.hide(); - }); - - $('table').on('click', 'td.password', function (event) { - $(this).children('img').click(); - }); - - $('table').on('click', 'td.displayName>img', function (event) { - event.stopPropagation(); - var img = $(this); - var uid = img.parent().parent().attr('data-uid'); - var displayName = escapeHTML(img.parent().parent().attr('data-displayName')); - var input = $('<input type="text" value="' + displayName + '">'); - img.css('display', 'none'); - img.parent().children('span').replaceWith(input); - input.focus(); - input.keypress(function (event) { - if (event.keyCode === 13) { - if ($(this).val().length > 0) { - $.post( - OC.filePath('settings', 'ajax', 'changedisplayname.php'), - {username: uid, displayName: $(this).val()}, - function (result) { - if (result && result.status==='success'){ - img.parent().parent().find('div.avatardiv').avatar(result.data.username, 32); - } - } - ); - input.blur(); - } else { - input.blur(); - } - } - }); - input.blur(function () { - var input = $(this), - displayName = input.val(); - input.closest('tr').attr('data-displayName', displayName); - input.replaceWith('<span>' + escapeHTML(displayName) + '</span>'); - img.css('display', ''); - }); - }); - $('table').on('click', 'td.displayName', function (event) { - $(this).children('img').click(); - }); - - $('select.quota, select.quota-user').singleSelect().on('change', function () { - var select = $(this); - var uid = $(this).parent().parent().attr('data-uid'); - var quota = $(this).val(); - setQuota(uid, quota, function(returnedQuota){ - if (quota !== returnedQuota) { - select.find(':selected').text(returnedQuota); - } - }); - }); - - $('#newuser').submit(function (event) { - event.preventDefault(); - var username = $('#newusername').val(); - var password = $('#newuserpassword').val(); - if ($.trim(username) === '') { - OC.dialogs.alert( - t('settings', 'A valid username must be provided'), - t('settings', 'Error creating user')); - return false; - } - if ($.trim(password) === '') { - OC.dialogs.alert( - t('settings', 'A valid password must be provided'), - t('settings', 'Error creating user')); - return false; - } - var groups = $('#newusergroups').prev().children('div').data('settings').checked; - $('#newuser').get(0).reset(); - $.post( - OC.filePath('settings', 'ajax', 'createuser.php'), - { - username: username, - password: password, - groups: groups - }, - function (result) { - if (result.status !== 'success') { - OC.dialogs.alert(result.data.message, - t('settings', 'Error creating user')); - } else { - if (result.data.groups) { - var addedGroups = result.data.groups; - UserList.availableGroups = $.unique($.merge(UserList.availableGroups, addedGroups)); - } - if (result.data.homeExists){ - OC.Notification.hide(); - OC.Notification.show(t('settings', 'Warning: Home directory for user "{user}" already exists', {user: result.data.username})); - if (UserList.notificationTimeout){ - window.clearTimeout(UserList.notificationTimeout); - } - UserList.notificationTimeout = window.setTimeout( - function(){ - OC.Notification.hide(); - UserList.notificationTimeout = null; - }, 10000); - } - if($('tr[data-uid="' + username + '"]').length === 0) { - UserList.add(username, username, result.data.groups, null, 'default', true); - } - } - } - ); - }); - // Handle undo notifications - OC.Notification.hide(); - $('#notification').on('click', '.undo', function () { - if ($('#notification').data('deleteuser')) { - $('tbody tr').filterAttr('data-uid', UserList.deleteUid).show(); - UserList.deleteCanceled = true; - } - OC.Notification.hide(); - }); - UserList.useUndo = ('onbeforeunload' in window); - $(window).bind('beforeunload', function () { - UserList.finishDelete(null); - }); -}); diff --git a/settings/js/users/deleteHandler.js b/settings/js/users/deleteHandler.js new file mode 100644 index 00000000000..894744ba3e9 --- /dev/null +++ b/settings/js/users/deleteHandler.js @@ -0,0 +1,171 @@ +/** + * Copyright (c) 2014, Arthur Schiwon <blizzz@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +/** + * takes care of deleting things represented by an ID + * + * @class + * @param {string} endpoint the corresponding ajax PHP script. Currently limited + * to settings - ajax path. + * @param {string} paramID the by the script expected parameter name holding the + * ID of the object to delete + * @param {markCallback} markCallback function to be called after successfully + * marking the object for deletion. + * @param {removeCallback} removeCallback the function to be called after + * successful delete. + */ +function DeleteHandler(endpoint, paramID, markCallback, removeCallback) { + this.oidToDelete = false; + this.canceled = false; + + this.ajaxEndpoint = endpoint; + this.ajaxParamID = paramID; + + this.markCallback = markCallback; + this.removeCallback = removeCallback; + this.undoCallback = false; + + this.notifier = false; + this.notificationDataID = false; + this.notificationMessage = false; + this.notificationPlaceholder = '%oid'; +} + +/** + * The function to be called after successfully marking the object for deletion + * @callback markCallback + * @param {string} oid the ID of the specific user or group + */ + +/** + * The function to be called after successful delete. The id of the object will + * be passed as argument. Unsuccessful operations will display an error using + * OC.dialogs, no callback is fired. + * @callback removeCallback + * @param {string} oid the ID of the specific user or group + */ + +/** + * This callback is fired after "undo" was clicked so the consumer can update + * the web interface + * @callback undoCallback + * @param {string} oid the ID of the specific user or group + */ + +/** + * enabled the notification system. Required for undo UI. + * + * @param {object} notifier Usually OC.Notification + * @param {string} dataID an identifier for the notifier, e.g. 'deleteuser' + * @param {string} message the message that should be shown upon delete. %oid + * will be replaced with the affected id of the item to be deleted + * @param {undoCallback} undoCallback called after "undo" was clicked + */ +DeleteHandler.prototype.setNotification = function(notifier, dataID, message, undoCallback) { + this.notifier = notifier; + this.notificationDataID = dataID; + this.notificationMessage = message; + this.undoCallback = undoCallback; + + var dh = this; + + $('#notification').on('click', '.undo', function () { + if ($('#notification').data(dh.notificationDataID)) { + var oid = dh.oidToDelete; + dh.cancel(); + if(typeof dh.undoCallback !== 'undefined') { + dh.undoCallback(oid); + } + } + dh.notifier.hide(); + }); +}; + +/** + * shows the Undo Notification (if configured) + */ +DeleteHandler.prototype.showNotification = function() { + if(this.notifier !== false) { + if(!this.notifier.isHidden()) { + this.hideNotification(); + } + $('#notification').data(this.notificationDataID, true); + var msg = this.notificationMessage.replace(this.notificationPlaceholder, + this.oidToDelete); + this.notifier.showHtml(msg); + } +}; + +/** + * hides the Undo Notification + */ +DeleteHandler.prototype.hideNotification = function() { + if(this.notifier !== false) { + $('#notification').removeData(this.notificationDataID); + this.notifier.hide(); + } +}; + +/** + * initializes the delete operation for a given object id + * + * @param {string} oid the object id + */ +DeleteHandler.prototype.mark = function(oid) { + if(this.oidToDelete !== false) { + this.delete(); + } + this.oidToDelete = oid; + this.canceled = false; + this.markCallback(oid); + this.showNotification(); +}; + +/** + * cancels a delete operation + */ +DeleteHandler.prototype.cancel = function() { + this.canceled = true; + this.oidToDelete = false; +}; + +/** + * executes a delete operation. Requires that the operation has been + * initialized by mark(). On error, it will show a message via + * OC.dialogs.alert. On success, a callback is fired so that the client can + * update the web interface accordingly. + */ +DeleteHandler.prototype.delete = function() { + if(this.canceled || this.oidToDelete === false) { + return false; + } + + var dh = this; + if($('#notification').data(this.notificationDataID) === true) { + dh.hideNotification(); + } + + var payload = {}; + payload[dh.ajaxParamID] = dh.oidToDelete; + $.ajax({ + type: 'POST', + url: OC.filePath('settings', 'ajax', dh.ajaxEndpoint), + async: false, + data: payload, + success: function (result) { + if (result.status === 'success') { + // Remove undo option, & remove user from table + + //TODO: following line + dh.removeCallback(dh.oidToDelete); + dh.canceled = true; + } else { + OC.dialogs.alert(result.data.message, t('settings', 'Unable to delete ' + escapeHTML(dh.oidToDelete))); + dh.undoCallback(dh.oidToDelete); + } + } + }); +}; diff --git a/settings/js/users/filter.js b/settings/js/users/filter.js new file mode 100644 index 00000000000..1f7a29de0c9 --- /dev/null +++ b/settings/js/users/filter.js @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2014, Arthur Schiwon <blizzz@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +/** + * @brief this object takes care of the filter functionality on the user + * management page + * @param jQuery input element that works as the user text input field + * @param object the UserList object + */ +function UserManagementFilter(filterInput, userList, groupList) { + this.filterInput = filterInput; + this.userList = userList; + this.groupList = groupList; + this.thread = undefined; + this.oldval = this.filterInput.val(); + + this.init(); +} + +/** + * @brief sets up when the filter action shall be triggered + */ +UserManagementFilter.prototype.init = function() { + var umf = this; + this.filterInput.keyup(function(e) { + //we want to react on any printable letter, plus on modifying stuff like + //Backspace and Delete. extended https://stackoverflow.com/a/12467610 + var valid = + e.keyCode === 0 || e.keyCode === 8 || // like ö or ж; backspace + e.keyCode === 9 || e.keyCode === 46 || // tab; delete + e.keyCode === 32 || // space + (e.keyCode > 47 && e.keyCode < 58) || // number keys + (e.keyCode > 64 && e.keyCode < 91) || // letter keys + (e.keyCode > 95 && e.keyCode < 112) || // numpad keys + (e.keyCode > 185 && e.keyCode < 193) || // ;=,-./` (in order) + (e.keyCode > 218 && e.keyCode < 223); // [\]' (in order) + + //besides the keys, the value must have been changed compared to last + //time + if(valid && umf.oldVal !== umf.getPattern()) { + umf.run(); + } + + umf.oldVal = umf.getPattern(); + }); +}; + +/** + * @brief the filter action needs to be done, here the accurate steps are being + * taken care of + */ +UserManagementFilter.prototype.run = _.debounce(function() { + this.userList.empty(); + this.userList.update(GroupList.getCurrentGID()); + this.groupList.empty(); + this.groupList.update(); + }, + 300 +); + +/** + * @brief returns the filter String + * @returns string + */ +UserManagementFilter.prototype.getPattern = function() { + return this.filterInput.val(); +}; + +/** + * @brief adds reset functionality to an HTML element + * @param jQuery the jQuery representation of that element + */ +UserManagementFilter.prototype.addResetButton = function(button) { + var umf = this; + button.click(function(){ + umf.filterInput.val(''); + umf.run(); + }); +}; diff --git a/settings/js/users/groups.js b/settings/js/users/groups.js new file mode 100644 index 00000000000..0ff8bdd6384 --- /dev/null +++ b/settings/js/users/groups.js @@ -0,0 +1,292 @@ +/** + * Copyright (c) 2014, Raghu Nayyar <beingminimal@gmail.com> + * Copyright (c) 2014, Arthur Schiwon <blizzz@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +var $userGroupList; + +var GroupList; +GroupList = { + activeGID: '', + + addGroup: function (gid, usercount) { + var $li = $userGroupList.find('.isgroup:last-child').clone(); + $li + .data('gid', gid) + .find('.groupname').text(gid); + GroupList.setUserCount($li, usercount); + + $li.appendTo($userGroupList); + + GroupList.sortGroups(); + + return $li; + }, + + setUserCount: function (groupLiElement, usercount) { + var $groupLiElement = $(groupLiElement); + if (usercount === undefined || usercount === 0) { + usercount = ''; + } + $groupLiElement.data('usercount', usercount); + $groupLiElement.find('.usercount').text(usercount); + }, + + getCurrentGID: function () { + return GroupList.activeGID; + }, + + sortGroups: function () { + var lis = $('.isgroup').get(); + + lis.sort(function (a, b) { + return UserList.alphanum( + $(a).find('a span').text(), + $(b).find('a span').text() + ); + }); + + var items = []; + $.each(lis, function (index, li) { + items.push(li); + if (items.length === 100) { + $userGroupList.append(items); + items = []; + } + }); + if (items.length > 0) { + $userGroupList.append(items); + } + }, + + createGroup: function (groupname) { + $.post( + OC.filePath('settings', 'ajax', 'creategroup.php'), + { + groupname: groupname + }, + function (result) { + if (result.status !== 'success') { + OC.dialogs.alert(result.data.message, + t('settings', 'Error creating group')); + } + else { + if (result.data.groupname) { + var addedGroup = result.data.groupname; + UserList.availableGroups = $.unique($.merge(UserList.availableGroups, [addedGroup])); + GroupList.addGroup(result.data.groupname); + + $('.groupsselect, .subadminsselect') + .append($('<option>', { value: result.data.groupname }) + .text(result.data.groupname)); + } + GroupList.toggleAddGroup(); + } + } + ); + }, + + update: function () { + if (GroupList.updating) { + return; + } + GroupList.updating = true; + $.get( + OC.generateUrl('/settings/ajax/grouplist'), + {pattern: filter.getPattern()}, + function (result) { + + var lis = []; + if (result.status === 'success') { + $.each(result.data, function (i, subset) { + $.each(subset, function (index, group) { + if (GroupList.getGroupLI(group.name).length > 0) { + GroupList.setUserCount(GroupList.getGroupLI(group.name).first(), group.usercount); + } + else { + var $li = GroupList.addGroup(group.name, group.usercount); + + $li.addClass('appear transparent'); + lis.push($li); + } + }); + }); + if (result.data.length > 0) { + GroupList.doSort(); + } + else { + GroupList.noMoreEntries = true; + } + _.defer(function () { + $(lis).each(function () { + this.removeClass('transparent') + }); + }); + } + GroupList.updating = false; + + } + ); + }, + + elementBelongsToAddGroup: function (el) { + return !(el !== $('#newgroup-form').get(0) && + $('#newgroup-form').find($(el)).length === 0); + }, + + hasAddGroupNameText: function () { + var name = $('#newgroupname').val(); + return $.trim(name) !== ''; + + }, + + showGroup: function (gid) { + GroupList.activeGID = gid; + UserList.empty(); + UserList.update(gid); + $userGroupList.find('li').removeClass('active'); + if (gid !== undefined) { + //TODO: treat Everyone properly + GroupList.getGroupLI(gid).addClass('active'); + } + }, + + isAddGroupButtonVisible: function () { + return $('#newgroup-init').is(":visible"); + }, + + toggleAddGroup: function (event) { + if (GroupList.isAddGroupButtonVisible()) { + event.stopPropagation(); + $('#newgroup-form').show(); + $('#newgroup-init').hide(); + $('#newgroupname').focus(); + } + else { + $('#newgroup-form').hide(); + $('#newgroup-init').show(); + $('#newgroupname').val(''); + } + }, + + isGroupNameValid: function (groupname) { + if ($.trim(groupname) === '') { + OC.dialogs.alert( + t('settings', 'A valid group name must be provided'), + t('settings', 'Error creating group')); + return false; + } + return true; + }, + + hide: function (gid) { + GroupList.getGroupLI(gid).hide(); + }, + show: function (gid) { + GroupList.getGroupLI(gid).show(); + }, + remove: function (gid) { + GroupList.getGroupLI(gid).remove(); + }, + empty: function () { + $userGroupList.find('.isgroup').filter(function(index, item){ + return $(item).data('gid') !== ''; + }).remove(); + }, + initDeleteHandling: function () { + //set up handler + GroupDeleteHandler = new DeleteHandler('removegroup.php', 'groupname', + GroupList.hide, GroupList.remove); + + //configure undo + OC.Notification.hide(); + var msg = t('settings', 'deleted') + ' %oid <span class="undo">' + + t('settings', 'undo') + '</span>'; + GroupDeleteHandler.setNotification(OC.Notification, 'deletegroup', msg, + GroupList.show); + + //when to mark user for delete + $userGroupList.on('click', '.delete', function () { + // Call function for handling delete/undo + GroupDeleteHandler.mark(GroupList.getElementGID(this)); + }); + + //delete a marked user when leaving the page + $(window).on('beforeunload', function () { + GroupDeleteHandler.delete(); + }); + }, + + getGroupLI: function (gid) { + return $userGroupList.find('li.isgroup').filter(function () { + return GroupList.getElementGID(this) === gid; + }); + }, + + getElementGID: function (element) { + return ($(element).closest('li').data('gid') || '').toString(); + } +}; + +$(document).ready( function () { + $userGroupList = $('#usergrouplist'); + GroupList.initDeleteHandling(); + + // Display or hide of Create Group List Element + $('#newgroup-form').hide(); + $('#newgroup-init').on('click', function (e) { + GroupList.toggleAddGroup(e); + }); + + $(document).on('click keydown keyup', function(event) { + if(!GroupList.isAddGroupButtonVisible() && + !GroupList.elementBelongsToAddGroup(event.target) && + !GroupList.hasAddGroupNameText()) { + GroupList.toggleAddGroup(); + } + // Escape + if(!GroupList.isAddGroupButtonVisible() && event.keyCode && event.keyCode === 27) { + GroupList.toggleAddGroup(); + } + }); + + + // Responsible for Creating Groups. + $('#newgroup-form form').submit(function (event) { + event.preventDefault(); + if(GroupList.isGroupNameValid($('#newgroupname').val())) { + GroupList.createGroup($('#newgroupname').val()); + } + }); + + // click on group name + $userGroupList.on('click', '.isgroup', function () { + GroupList.showGroup(GroupList.getElementGID(this)); + }); + + // Implements Quota Settings Toggle. + var $appSettings = $('#app-settings'); + $('#app-settings-header').on('click keydown',function(event) { + if(wrongKey(event)) { + return; + } + if($appSettings.hasClass('open')) { + $appSettings.switchClass('open', ''); + } else { + $appSettings.switchClass('', 'open'); + } + }); + $('body').on('click', function(event){ + if($appSettings.find(event.target).length === 0) { + $appSettings.switchClass('open', ''); + } + }); + +}); + +var wrongKey = function(event) { + return ((event.type === 'keydown' || event.type === 'keypress') && + (event.keyCode !== 32 && event.keyCode !== 13)); +}; diff --git a/settings/js/users/users.js b/settings/js/users/users.js new file mode 100644 index 00000000000..68098e03a50 --- /dev/null +++ b/settings/js/users/users.js @@ -0,0 +1,616 @@ +/** + * Copyright (c) 2014, Arthur Schiwon <blizzz@owncloud.com> + * Copyright (c) 2014, Raghu Nayyar <beingminimal@gmail.com> + * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com> + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +var $userList; +var $userListBody; +var filter; + +var UserList = { + availableGroups: [], + offset: 30, //The first 30 users are there. No prob, if less in total. + //hardcoded in settings/users.php + + usersToLoad: 10, //So many users will be loaded when user scrolls down + currentGid: '', + + add: function (username, displayname, groups, subadmin, quota, storageLocation, lastLogin, sort) { + var $tr = $userListBody.find('tr:first-child').clone(); + var subadminsEl; + var subadminSelect; + var groupsSelect; + if ($tr.find('div.avatardiv').length){ + $tr.find('.avatardiv').imageplaceholder(username, displayname); + $('div.avatardiv', $tr).avatar(username, 32); + } + $tr.data('uid', username); + $tr.data('displayname', displayname); + $tr.find('td.name').text(username); + $tr.find('td.displayName > span').text(displayname); + + // make them look like the multiselect buttons + // until they get time to really get initialized + groupsSelect = $('<select multiple="multiple" class="groupsselect multiselect button" data-placehoder="Groups" title="' + t('settings', 'Groups') + '"></select>') + .data('username', username) + .data('user-groups', groups); + if ($tr.find('td.subadmins').length > 0) { + subadminSelect = $('<select multiple="multiple" class="subadminsselect multiselect button" data-placehoder="subadmins" title="' + t('settings', 'Group Admin') + '">') + .data('username', username) + .data('user-groups', groups) + .data('subadmin', subadmin); + $tr.find('td.subadmins').empty(); + } + $.each(this.availableGroups, function (i, group) { + groupsSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>')); + if (typeof subadminSelect !== 'undefined' && group !== 'admin') { + subadminSelect.append($('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>')); + } + }); + $tr.find('td.groups').empty().append(groupsSelect); + subadminsEl = $tr.find('td.subadmins'); + if (subadminsEl.length > 0) { + subadminsEl.append(subadminSelect); + } + if ($tr.find('td.remove img').length === 0 && OC.currentUser !== username) { + var deleteImage = $('<img class="svg action">').attr({ + src: OC.imagePath('core', 'actions/delete') + }); + var deleteLink = $('<a class="action delete">') + .attr({ href: '#', 'original-title': t('settings', 'Delete')}) + .append(deleteImage); + $tr.find('td.remove').append(deleteLink); + } else if (OC.currentUser === username) { + $tr.find('td.remove a').remove(); + } + var $quotaSelect = $tr.find('.quota-user'); + if (quota === 'default') { + $quotaSelect + .data('previous', 'default') + .find('option').attr('selected', null) + .first().attr('selected', 'selected'); + } else { + if ($quotaSelect.find('option[value="' + quota + '"]').length > 0) { + $quotaSelect.find('option[value="' + quota + '"]').attr('selected', 'selected'); + } else { + $quotaSelect.append('<option value="' + escapeHTML(quota) + '" selected="selected">' + escapeHTML(quota) + '</option>'); + } + } + $tr.find('td.storageLocation').text(storageLocation); + if(lastLogin === 0) { + lastLogin = t('settings', 'never'); + } else { + lastLogin = new Date(lastLogin * 1000); + lastLogin = relative_modified_date(lastLogin.getTime() / 1000); + } + $tr.find('td.lastLogin').text(lastLogin); + $tr.appendTo($userList); + if(UserList.isEmpty === true) { + //when the list was emptied, one row was left, necessary to keep + //add working and the layout unbroken. We need to remove this item + $tr.show(); + $userListBody.find('tr:first').remove(); + UserList.isEmpty = false; + UserList.checkUsersToLoad(); + } + if (sort) { + UserList.doSort(); + } + + $quotaSelect.on('change', function () { + var uid = UserList.getUID(this); + var quota = $(this).val(); + setQuota(uid, quota, function(returnedQuota){ + if (quota !== returnedQuota) { + $($quotaSelect).find(':selected').text(returnedQuota); + } + }); + }); + + // defer init so the user first sees the list appear more quickly + window.setTimeout(function(){ + $quotaSelect.singleSelect(); + UserList.applyGroupSelect(groupsSelect); + if (subadminSelect) { + UserList.applySubadminSelect(subadminSelect); + } + }, 0); + return $tr; + }, + // From http://my.opera.com/GreyWyvern/blog/show.dml/1671288 + alphanum: function(a, b) { + function chunkify(t) { + var tz = [], x = 0, y = -1, n = 0, i, j; + + while (i = (j = t.charAt(x++)).charCodeAt(0)) { + var m = (i === 46 || (i >=48 && i <= 57)); + if (m !== n) { + tz[++y] = ""; + n = m; + } + tz[y] += j; + } + return tz; + } + + var aa = chunkify(a.toLowerCase()); + var bb = chunkify(b.toLowerCase()); + + for (var x = 0; aa[x] && bb[x]; x++) { + if (aa[x] !== bb[x]) { + var c = Number(aa[x]), d = Number(bb[x]); + if (c === aa[x] && d === bb[x]) { + return c - d; + } else { + return (aa[x] > bb[x]) ? 1 : -1; + } + } + } + return aa.length - bb.length; + }, + preSortSearchString: function(a, b) { + var pattern = filter.getPattern(); + if(typeof pattern === 'undefined') { + return undefined; + } + pattern = pattern.toLowerCase(); + var aMatches = false; + var bMatches = false; + if(typeof a === 'string' && a.toLowerCase().indexOf(pattern) === 0) { + aMatches = true; + } + if(typeof b === 'string' && b.toLowerCase().indexOf(pattern) === 0) { + bMatches = true; + } + + if((aMatches && bMatches) || (!aMatches && !bMatches)) { + return undefined; + } + + if(aMatches) { + return -1; + } else { + return 1; + } + }, + doSort: function() { + var rows = $userListBody.find('tr').get(); + + rows.sort(function(a, b) { + a = $(a).find('td.name').text(); + b = $(b).find('td.name').text(); + var firstSort = UserList.preSortSearchString(a, b); + if(typeof firstSort !== 'undefined') { + return firstSort; + } + return UserList.alphanum(a, b); + }); + + var items = []; + $.each(rows, function(index, row) { + items.push(row); + if(items.length === 100) { + $userListBody.append(items); + items = []; + } + }); + if(items.length > 0) { + $userListBody.append(items); + } + }, + checkUsersToLoad: function() { + //30 shall be loaded initially, from then on always 10 upon scrolling + if(UserList.isEmpty === false) { + UserList.usersToLoad = 10; + } else { + UserList.usersToLoad = 30; + } + }, + empty: function() { + //one row needs to be kept, because it is cloned to add new rows + $userListBody.find('tr:not(:first)').remove(); + var $tr = $userListBody.find('tr:first'); + $tr.hide(); + //on an update a user may be missing when the username matches with that + //of the hidden row. So change this to a random string. + $tr.data('uid', Math.random().toString(36).substring(2)); + UserList.isEmpty = true; + UserList.offset = 0; + UserList.checkUsersToLoad(); + }, + hide: function(uid) { + UserList.getRow(uid).hide(); + }, + show: function(uid) { + UserList.getRow(uid).show(); + }, + remove: function(uid) { + UserList.getRow(uid).remove(); + }, + has: function(uid) { + return UserList.getRow(uid).length > 0; + }, + getRow: function(uid) { + return $userListBody.find('tr').filter(function(){ + return UserList.getUID(this) === uid; + }); + }, + getUID: function(element) { + return ($(element).closest('tr').data('uid') || '').toString(); + }, + getDisplayName: function(element) { + return ($(element).closest('tr').data('displayname') || '').toString(); + }, + initDeleteHandling: function() { + //set up handler + UserDeleteHandler = new DeleteHandler('removeuser.php', 'username', + UserList.hide, UserList.remove); + + //configure undo + OC.Notification.hide(); + var msg = t('settings', 'deleted') + ' %oid <span class="undo">' + + t('settings', 'undo') + '</span>'; + UserDeleteHandler.setNotification(OC.Notification, 'deleteuser', msg, + UserList.show); + + //when to mark user for delete + $userListBody.on('click', '.delete', function () { + // Call function for handling delete/undo + var uid = UserList.getUID(this); + UserDeleteHandler.mark(uid); + }); + + //delete a marked user when leaving the page + $(window).on('beforeunload', function () { + UserDeleteHandler.delete(); + }); + }, + update: function (gid) { + if (UserList.updating) { + return; + } + $userList.siblings('.loading').css('visibility', 'visible'); + UserList.updating = true; + if(gid === undefined) { + gid = ''; + } + UserList.currentGid = gid; + var pattern = filter.getPattern(); + $.get( + OC.generateUrl('/settings/ajax/userlist'), + { offset: UserList.offset, limit: UserList.usersToLoad, gid: gid, pattern: pattern }, + function (result) { + var loadedUsers = 0; + var trs = []; + if (result.status === 'success') { + //The offset does not mirror the amount of users available, + //because it is backend-dependent. For correct retrieval, + //always the limit(requested amount of users) needs to be added. + $.each(result.data, function (index, user) { + if(UserList.has(user.name)) { + return true; + } + var $tr = UserList.add(user.name, user.displayname, user.groups, user.subadmin, user.quota, user.storageLocation, user.lastLogin, false); + $tr.addClass('appear transparent'); + trs.push($tr); + loadedUsers++; + }); + if (result.data.length > 0) { + UserList.doSort(); + $userList.siblings('.loading').css('visibility', 'hidden'); + } + else { + UserList.noMoreEntries = true; + $userList.siblings('.loading').remove(); + } + UserList.offset += loadedUsers; + // animate + setTimeout(function() { + for (var i = 0; i < trs.length; i++) { + trs[i].removeClass('transparent'); + } + }, 0); + } + UserList.updating = false; + }); + }, + + applyGroupSelect: function (element) { + var checked = []; + var $element = $(element); + var user = UserList.getUID($element); + + if ($element.data('user-groups')) { + checked = $element.data('user-groups'); + } + var checkHandler = null; + if(user) { // Only if in a user row, and not the #newusergroups select + checkHandler = function (group) { + if (user === OC.currentUser && group === 'admin') { + return false; + } + if (!oc_isadmin && checked.length === 1 && checked[0] === group) { + return false; + } + $.post( + OC.filePath('settings', 'ajax', 'togglegroups.php'), + { + username: user, + group: group + }, + function (response) { + if (response.status === 'success') { + GroupList.update(); + if (UserList.availableGroups.indexOf(response.data.groupname) === -1 && + response.data.action === 'add' + ) { + UserList.availableGroups.push(response.data.groupname); + } + } + if (response.data.message) { + OC.Notification.show(response.data.message); + } + } + ); + } + }; + var addGroup = function (select, group) { + $('select[multiple]').each(function (index, element) { + $element = $(element); + if ($element.find('option[value="' + group + '"]').length === 0 && select.data('msid') !== $element.data('msid')) { + $element.append('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'); + } + }); + GroupList.addGroup(escapeHTML(group)); + }; + var label; + if (oc_isadmin) { + label = t('settings', 'add group'); + } + else { + label = null; + } + $element.multiSelect({ + createCallback: addGroup, + createText: label, + selectedFirst: true, + checked: checked, + oncheck: checkHandler, + onuncheck: checkHandler, + minWidth: 100 + }); + }, + + applySubadminSelect: function (element) { + var checked = []; + var $element = $(element); + var user = UserList.getUID($element); + + if ($element.data('subadmin')) { + checked = $element.data('subadmin'); + } + var checkHandler = function (group) { + if (group === 'admin') { + return false; + } + $.post( + OC.filePath('settings', 'ajax', 'togglesubadmins.php'), + { + username: user, + group: group + }, + function () { + } + ); + }; + + var addSubAdmin = function (group) { + $('select[multiple]').each(function (index, element) { + if ($(element).find('option[value="' + group + '"]').length === 0) { + $(element).append('<option value="' + escapeHTML(group) + '">' + escapeHTML(group) + '</option>'); + } + }); + }; + $element.multiSelect({ + createCallback: addSubAdmin, + createText: null, + checked: checked, + oncheck: checkHandler, + onuncheck: checkHandler, + minWidth: 100 + }); + }, + + _onScroll: function() { + if (!!UserList.noMoreEntries) { + return; + } + if (UserList.scrollArea.scrollTop() + UserList.scrollArea.height() > UserList.scrollArea.get(0).scrollHeight - 500) { + UserList.update(UserList.currentGid, true); + } + } +}; + +function setQuota (uid, quota, ready) { + $.post( + OC.filePath('settings', 'ajax', 'setquota.php'), + {username: uid, quota: quota}, + function (result) { + if (ready) { + ready(result.data.quota); + } + } + ); +} + +$(document).ready(function () { + $userList = $('#userlist'); + $userListBody = $userList.find('tbody'); + + UserList.initDeleteHandling(); + + // Implements User Search + filter = new UserManagementFilter($('#usersearchform input'), UserList, GroupList); + + UserList.doSort(); + UserList.availableGroups = $userList.data('groups'); + + + UserList.scrollArea = $('#app-content'); + UserList.scrollArea.scroll(function(e) {UserList._onScroll(e);}); + + + $userList.after($('<div class="loading" style="height: 200px; visibility: hidden;"></div>')); + + $('.groupsselect').each(function (index, element) { + UserList.applyGroupSelect(element); + }); + $('.subadminsselect').each(function (index, element) { + UserList.applySubadminSelect(element); + }); + + $userListBody.on('click', '.password', function (event) { + event.stopPropagation(); + + var $td = $(this).closest('td'); + var uid = UserList.getUID($td); + var $input = $('<input type="password">'); + $td.find('img').hide(); + $td.children('span').replaceWith($input); + $input + .focus() + .keypress(function (event) { + if (event.keyCode === 13) { + if ($(this).val().length > 0) { + var recoveryPasswordVal = $('input:password[id="recoveryPassword"]').val(); + $.post( + OC.generateUrl('/settings/users/changepassword'), + {username: uid, password: $(this).val(), recoveryPassword: recoveryPasswordVal}, + function (result) { + if (result.status != 'success') { + OC.Notification.show(t('admin', result.data.message)); + } + } + ); + $input.blur(); + } else { + $input.blur(); + } + } + }) + .blur(function () { + $(this).replaceWith($('<span>●●●●●●●</span>')); + $td.find('img').show(); + }); + }); + $('input:password[id="recoveryPassword"]').keyup(function() { + OC.Notification.hide(); + }); + + $userListBody.on('click', '.displayName', function (event) { + event.stopPropagation(); + var $td = $(this).closest('td'); + var $tr = $td.closest('tr'); + var uid = UserList.getUID($td); + var displayName = escapeHTML(UserList.getDisplayName($td)); + var $input = $('<input type="text" value="' + displayName + '">'); + $td.find('img').hide(); + $td.children('span').replaceWith($input); + $input + .focus() + .keypress(function (event) { + if (event.keyCode === 13) { + if ($(this).val().length > 0) { + $tr.find('.avatardiv').imageplaceholder(uid, displayName); + $.post( + OC.filePath('settings', 'ajax', 'changedisplayname.php'), + {username: uid, displayName: $(this).val()}, + function (result) { + if (result && result.status==='success'){ + $tr.find('.avatardiv').avatar(result.data.username, 32); + } + } + ); + $input.blur(); + } else { + $input.blur(); + } + } + }) + .blur(function () { + var displayName = $input.val(); + $tr.data('displayname', displayName); + $input.replaceWith('<span>' + escapeHTML(displayName) + '</span>'); + $td.find('img').show(); + }); + }); + + $('#default_quota, .quota-user').singleSelect().on('change', function () { + var $select = $(this); + var uid = UserList.getUID($select); + var quota = $select.val(); + setQuota(uid, quota, function(returnedQuota){ + if (quota !== returnedQuota) { + $select.find(':selected').text(returnedQuota); + } + }); + }); + + $('#newuser').submit(function (event) { + event.preventDefault(); + var username = $('#newusername').val(); + var password = $('#newuserpassword').val(); + if ($.trim(username) === '') { + OC.dialogs.alert( + t('settings', 'A valid username must be provided'), + t('settings', 'Error creating user')); + return false; + } + if ($.trim(password) === '') { + OC.dialogs.alert( + t('settings', 'A valid password must be provided'), + t('settings', 'Error creating user')); + return false; + } + var groups = $('#newusergroups').val(); + $('#newuser').get(0).reset(); + $.post( + OC.filePath('settings', 'ajax', 'createuser.php'), + { + username: username, + password: password, + groups: groups + }, + function (result) { + if (result.status !== 'success') { + OC.dialogs.alert(result.data.message, + t('settings', 'Error creating user')); + } else { + if (result.data.groups) { + var addedGroups = result.data.groups; + UserList.availableGroups = $.unique($.merge(UserList.availableGroups, addedGroups)); + } + if (result.data.homeExists){ + OC.Notification.hide(); + OC.Notification.show(t('settings', 'Warning: Home directory for user "{user}" already exists', {user: result.data.username})); + if (UserList.notificationTimeout){ + window.clearTimeout(UserList.notificationTimeout); + } + UserList.notificationTimeout = window.setTimeout( + function(){ + OC.Notification.hide(); + UserList.notificationTimeout = null; + }, 10000); + } + if(!UserList.has(username)) { + UserList.add(username, username, result.data.groups, null, 'default', result.data.storageLocation, 0, true); + } + } + } + ); + }); + +}); diff --git a/settings/routes.php b/settings/routes.php index 0e0f293b9be..9acfc2852bd 100644 --- a/settings/routes.php +++ b/settings/routes.php @@ -25,6 +25,8 @@ $this->create('settings_admin', '/settings/admin') // users $this->create('settings_ajax_userlist', '/settings/ajax/userlist') ->actionInclude('settings/ajax/userlist.php'); +$this->create('settings_ajax_grouplist', '/settings/ajax/grouplist') + ->actionInclude('settings/ajax/grouplist.php'); $this->create('settings_ajax_createuser', '/settings/ajax/createuser.php') ->actionInclude('settings/ajax/createuser.php'); $this->create('settings_ajax_removeuser', '/settings/ajax/removeuser.php') @@ -44,6 +46,8 @@ $this->create('settings_users_changepassword', '/settings/users/changepassword') ->action('OC\Settings\ChangePassword\Controller', 'changeUserPassword'); $this->create('settings_ajax_changedisplayname', '/settings/ajax/changedisplayname.php') ->actionInclude('settings/ajax/changedisplayname.php'); +$this->create('settings_ajax_changegorupname', '/settings/ajax/changegroupname.php') + ->actionInclude('settings/ajax/changegroupname.php'); // personal $this->create('settings_personal_changepassword', '/settings/personal/changepassword') ->post() diff --git a/settings/templates/users.php b/settings/templates/users.php deleted file mode 100644 index 937b40611b0..00000000000 --- a/settings/templates/users.php +++ /dev/null @@ -1,176 +0,0 @@ -<?php -/** - * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com> - * This file is licensed under the Affero General Public License version 3 or later. - * See the COPYING-README file. - */ -$allGroups=array(); -foreach($_["groups"] as $group) { - $allGroups[] = $group['name']; -} -$_['subadmingroups'] = $allGroups; -$items = array_flip($_['subadmingroups']); -unset($items['admin']); -$_['subadmingroups'] = array_flip($items); -?> - -<div id="controls"> - <form id="newuser" autocomplete="off"> - <input id="newusername" type="text" placeholder="<?php p($l->t('Login Name'))?>" /> <input - type="password" id="newuserpassword" - placeholder="<?php p($l->t('Password'))?>" /> <select - class="groupsselect" - id="newusergroups" data-placeholder="groups" - title="<?php p($l->t('Groups'))?>" multiple="multiple"> - <?php foreach($_["groups"] as $group): ?> - <option value="<?php p($group['name']);?>"><?php p($group['name']);?></option> - <?php endforeach;?> - </select> <input type="submit" value="<?php p($l->t('Create'))?>" /> - </form> - <?php if((bool)$_['recoveryAdminEnabled']): ?> - <div class="recoveryPassword"> - <input id="recoveryPassword" - type="password" - placeholder="<?php p($l->t('Admin Recovery Password'))?>" - title="<?php p($l->t('Enter the recovery password in order to recover the users files during password change'))?>" - alt="<?php p($l->t('Enter the recovery password in order to recover the users files during password change'))?>"/> - </div> - <?php endif; ?> - <div class="quota"> - <span><?php p($l->t('Default Storage'));?></span> - <?php if((bool) $_['isadmin']): ?> - <select class='quota' data-inputtitle="<?php p($l->t('Please enter storage quota (ex: "512 MB" or "12 GB")')) ?>"> - <option - <?php if($_['default_quota'] === 'none') print_unescaped('selected="selected"');?> - value='none'> - <?php p($l->t('Unlimited'));?> - </option> - <?php foreach($_['quota_preset'] as $preset):?> - <?php if($preset !== 'default'):?> - <option - <?php if($_['default_quota']==$preset) print_unescaped('selected="selected"');?> - value='<?php p($preset);?>'> - <?php p($preset);?> - </option> - <?php endif;?> - <?php endforeach;?> - <?php if($_['defaultQuotaIsUserDefined']):?> - <option selected="selected" - value='<?php p($_['default_quota']);?>'> - <?php p($_['default_quota']);?> - </option> - <?php endif;?> - <option data-new value='other'> - <?php p($l->t('Other'));?> - ... - </option> - </select> - <?php endif; ?> - <?php if((bool) !$_['isadmin']): ?> - <select class='quota' disabled="disabled"> - <option selected="selected"> - <?php p($_['default_quota']);?> - </option> - </select> - <?php endif; ?> - </div> -</div> - -<table class="hascontrols grid" data-groups="<?php p(json_encode($allGroups));?>"> - <thead> - <tr> - <?php if ($_['enableAvatars']): ?> - <th id='headerAvatar'></th> - <?php endif; ?> - <th id='headerName'><?php p($l->t('Username'))?></th> - <th id="headerDisplayName"><?php p($l->t( 'Full Name' )); ?></th> - <th id="headerPassword"><?php p($l->t( 'Password' )); ?></th> - <th id="headerGroups"><?php p($l->t( 'Groups' )); ?></th> - <?php if(is_array($_['subadmins']) || $_['subadmins']): ?> - <th id="headerSubAdmins"><?php p($l->t('Group Admin')); ?></th> - <?php endif;?> - <th id="headerQuota"><?php p($l->t('Storage')); ?></th> - <th id="headerRemove"> </th> - </tr> - </thead> - <tbody> - <?php foreach($_["users"] as $user): ?> - <tr data-uid="<?php p($user["name"]) ?>" - data-displayName="<?php p($user["displayName"]) ?>"> - <?php if ($_['enableAvatars']): ?> - <td class="avatar"><div class="avatardiv"></div></td> - <?php endif; ?> - <td class="name"><?php p($user["name"]); ?></td> - <td class="displayName"><span><?php p($user["displayName"]); ?></span> <img class="svg action" - src="<?php p(image_path('core', 'actions/rename.svg'))?>" - alt="<?php p($l->t("change full name"))?>" title="<?php p($l->t("change full name"))?>"/> - </td> - <td class="password"><span>●●●●●●●</span> <img class="svg action" - src="<?php print_unescaped(image_path('core', 'actions/rename.svg'))?>" - alt="<?php p($l->t("set new password"))?>" title="<?php p($l->t("set new password"))?>"/> - </td> - <td class="groups"><select - class="groupsselect" - data-username="<?php p($user['name']) ;?>" - data-user-groups="<?php p(json_encode($user['groups'])) ;?>" - data-placeholder="groups" title="<?php p($l->t('Groups'))?>" - multiple="multiple"> - <?php foreach($_["groups"] as $group): ?> - <option value="<?php p($group['name']);?>"><?php p($group['name']);?></option> - <?php endforeach;?> - </select> - </td> - <?php if(is_array($_['subadmins']) || $_['subadmins']): ?> - <td class="subadmins"><select - class="subadminsselect" - data-username="<?php p($user['name']) ;?>" - data-subadmin="<?php p(json_encode($user['subadmin']));?>" - data-placeholder="subadmins" title="<?php p($l->t('Group Admin'))?>" - multiple="multiple"> - <?php foreach($_["subadmingroups"] as $group): ?> - <option value="<?php p($group);?>"><?php p($group);?></option> - <?php endforeach;?> - </select> - </td> - <?php endif;?> - <td class="quota"> - <select class='quota-user' data-inputtitle="<?php p($l->t('Please enter storage quota (ex: "512 MB" or "12 GB")')) ?>"> - <option - <?php if($user['quota'] === 'default') print_unescaped('selected="selected"');?> - value='default'> - <?php p($l->t('Default'));?> - </option> - <option - <?php if($user['quota'] === 'none') print_unescaped('selected="selected"');?> - value='none'> - <?php p($l->t('Unlimited'));?> - </option> - <?php foreach($_['quota_preset'] as $preset):?> - <option - <?php if($user['quota']==$preset) print_unescaped('selected="selected"');?> - value='<?php p($preset);?>'> - <?php p($preset);?> - </option> - <?php endforeach;?> - <?php if($user['isQuotaUserDefined']):?> - <option selected="selected" value='<?php p($user['quota']);?>'> - <?php p($user['quota']);?> - </option> - <?php endif;?> - <option value='other' data-new> - <?php p($l->t('Other'));?> - ... - </option> - </select> - </td> - <td class="remove"> - <?php if($user['name']!=OC_User::getUser()):?> - <a href="#" class="action delete" original-title="<?php p($l->t('Delete'))?>"> - <img src="<?php print_unescaped(image_path('core', 'actions/delete.svg')) ?>" class="svg" /> - </a> - <?php endif;?> - </td> - </tr> - <?php endforeach; ?> - </tbody> -</table> diff --git a/settings/templates/users/main.php b/settings/templates/users/main.php new file mode 100644 index 00000000000..c5805d53476 --- /dev/null +++ b/settings/templates/users/main.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com> + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ +$userlistParams = array(); +$allGroups=array(); +foreach($_["groups"] as $group) { + $allGroups[] = $group['name']; +} +foreach($_["adminGroup"] as $group) { + $allGroups[] = $group['name']; +} +$userlistParams['subadmingroups'] = $allGroups; +$userlistParams['allGroups'] = json_encode($allGroups); +$items = array_flip($userlistParams['subadmingroups']); +unset($items['admin']); +$userlistParams['subadmingroups'] = array_flip($items); +?> + +<div id="app-navigation"> + <?php print_unescaped($this->inc('users/part.grouplist')); ?> + <div id="app-settings"> + <?php print_unescaped($this->inc('users/part.setquota')); ?> + </div> +</div> + +<div id="app-content"> + <?php print_unescaped($this->inc('users/part.createuser')); ?> + <?php print_unescaped($this->inc('users/part.userlist', $userlistParams)); ?> +</div>
\ No newline at end of file diff --git a/settings/templates/users/part.createuser.php b/settings/templates/users/part.createuser.php new file mode 100644 index 00000000000..4d573168fc1 --- /dev/null +++ b/settings/templates/users/part.createuser.php @@ -0,0 +1,34 @@ +<div id="user-controls"> + <form id="newuser" autocomplete="off"> + <input id="newusername" type="text" + placeholder="<?php p($l->t('Login Name'))?>" + autocomplete="off" autocapitalize="off" autocorrect="off" /> + <input + type="password" id="newuserpassword" + placeholder="<?php p($l->t('Password'))?>" + autocomplete="off" autocapitalize="off" autocorrect="off" /> + <select + class="groupsselect" id="newusergroups" data-placeholder="groups" + title="<?php p($l->t('Groups'))?>" multiple="multiple"> + <?php foreach($_["adminGroup"] as $adminGroup): ?> + <option value="<?php p($adminGroup['name']);?>"><?php p($adminGroup['name']); ?></option> + <?php endforeach; ?> + <?php foreach($_["groups"] as $group): ?> + <option value="<?php p($group['name']);?>"><?php p($group['name']);?></option> + <?php endforeach;?> + </select> + <input type="submit" class="button" value="<?php p($l->t('Create'))?>" /> + </form> + <?php if((bool)$_['recoveryAdminEnabled']): ?> + <div class="recoveryPassword"> + <input id="recoveryPassword" + type="password" + placeholder="<?php p($l->t('Admin Recovery Password'))?>" + title="<?php p($l->t('Enter the recovery password in order to recover the users files during password change'))?>" + alt="<?php p($l->t('Enter the recovery password in order to recover the users files during password change'))?>"/> + </div> + <?php endif; ?> + <form autocomplete="off" id="usersearchform"> + <input type="text" class="input userFilter" placeholder="<?php p($l->t('Search Users and Groups')); ?>" /> + </form> +</div>
\ No newline at end of file diff --git a/settings/templates/users/part.grouplist.php b/settings/templates/users/part.grouplist.php new file mode 100644 index 00000000000..a903f43f876 --- /dev/null +++ b/settings/templates/users/part.grouplist.php @@ -0,0 +1,50 @@ +<ul id="usergrouplist"> + <!-- Add new group --> + <li id="newgroup-init"> + <a href="#"> + <span><?php p($l->t('Add Group'))?></span> + </a> + </li> + <li id="newgroup-form"> + <form> + <input type="text" id="newgroupname" placeholder="<?php p($l->t('Group')); ?>..." /> + <input type="submit" class="button" value="<?php p($l->t('Add Group'))?>" /> + </form> + </li> + <!-- Everyone --> + <li data-gid="" class="isgroup"> + <a href="#"> + <span class="groupname"> + <?php p($l->t('Everyone')); ?> + </span> + </a> + <span class="utils"> + <span class="usercount"></span> + </span> + </li> + + <!-- The Admin Group --> + <?php foreach($_["adminGroup"] as $adminGroup): ?> + <li data-gid="admin" class="isgroup"> + <a href="#"><span class="groupname"><?php p($l->t('Admins')); ?></span></a> + <span class="utils"> + <span class="usercount"><?php if($adminGroup['usercount'] > 0) { p($adminGroup['usercount']); } ?></span> + </span> + </li> + <?php endforeach; ?> + + <!--List of Groups--> + <?php foreach($_["groups"] as $group): ?> + <li data-gid="<?php p($group['name']) ?>" data-usercount="<?php p($group['usercount']) ?>" class="isgroup"> + <a href="#" class="dorename"> + <span class="groupname"><?php p($group['name']); ?></span> + </a> + <span class="utils"> + <span class="usercount"><?php if($group['usercount'] > 0) { p($group['usercount']); } ?></span> + <a href="#" class="action delete" original-title="<?php p($l->t('Delete'))?>"> + <img src="<?php print_unescaped(image_path('core', 'actions/delete.svg')) ?>" class="svg" /> + </a> + </span> + </li> + <?php endforeach; ?> +</ul> diff --git a/settings/templates/users/part.setquota.php b/settings/templates/users/part.setquota.php new file mode 100644 index 00000000000..fc5624d069a --- /dev/null +++ b/settings/templates/users/part.setquota.php @@ -0,0 +1,39 @@ +<div id="app-settings-header"> + <button class="settings-button" tabindex="0"></button> +</div> +<div id="app-settings-content"> + <div class="quota"> + <!-- Default storage --> + <span><?php p($l->t('Default Quota'));?></span> + <?php if((bool) $_['isAdmin']): ?> + <select id='default_quota' data-inputtitle="<?php p($l->t('Please enter storage quota (ex: "512 MB" or "12 GB")')) ?>"> + <option <?php if($_['default_quota'] === 'none') print_unescaped('selected="selected"');?> value='none'> + <?php p($l->t('Unlimited'));?> + </option> + <?php foreach($_['quota_preset'] as $preset):?> + <?php if($preset !== 'default'):?> + <option <?php if($_['default_quota']==$preset) print_unescaped('selected="selected"');?> value='<?php p($preset);?>'> + <?php p($preset);?> + </option> + <?php endif;?> + <?php endforeach;?> + <?php if($_['defaultQuotaIsUserDefined']):?> + <option selected="selected" value='<?php p($_['default_quota']);?>'> + <?php p($_['default_quota']);?> + </option> + <?php endif;?> + <option data-new value='other'> + <?php p($l->t('Other'));?> + ... + </option> + </select> + <?php endif; ?> + <?php if((bool) !$_['isAdmin']): ?> + <select class='quota' disabled="disabled"> + <option selected="selected"> + <?php p($_['default_quota']);?> + </option> + </select> + <?php endif; ?> + </div> +</div>
\ No newline at end of file diff --git a/settings/templates/users/part.userlist.php b/settings/templates/users/part.userlist.php new file mode 100644 index 00000000000..c74fdcc9efa --- /dev/null +++ b/settings/templates/users/part.userlist.php @@ -0,0 +1,116 @@ +<table id="userlist" class="hascontrols grid" data-groups="<?php p($_['allGroups']);?>"> + <thead> + <tr> + <?php if ($_['enableAvatars']): ?> + <th id='headerAvatar'></th> + <?php endif; ?> + <th id='headerName'><?php p($l->t('Username'))?></th> + <th id="headerDisplayName"><?php p($l->t( 'Full Name' )); ?></th> + <th id="headerPassword"><?php p($l->t( 'Password' )); ?></th> + <th id="headerGroups"><?php p($l->t( 'Groups' )); ?></th> + <?php if(is_array($_['subadmins']) || $_['subadmins']): ?> + <th id="headerSubAdmins"><?php p($l->t('Group Admin')); ?></th> + <?php endif;?> + <th id="headerQuota"><?php p($l->t('Quota')); ?></th> + <th id="headerStorageLocation"><?php p($l->t('Storage Location')); ?></th> + <th id="headerLastLogin"><?php p($l->t('Last Login')); ?></th> + <th id="headerRemove"> </th> + </tr> + </thead> + <tbody> + <?php foreach($_["users"] as $user): ?> + <tr data-uid="<?php p($user["name"]) ?>" + data-displayname="<?php p($user["displayName"]) ?>"> + <?php if ($_['enableAvatars']): ?> + <td class="avatar"><div class="avatardiv"></div></td> + <?php endif; ?> + <td class="name"><?php p($user["name"]); ?></td> + <td class="displayName"><span><?php p($user["displayName"]); ?></span> <img class="svg action" + src="<?php p(image_path('core', 'actions/rename.svg'))?>" + alt="<?php p($l->t("change full name"))?>" title="<?php p($l->t("change full name"))?>"/> + </td> + <td class="password"><span>●●●●●●●</span> <img class="svg action" + src="<?php print_unescaped(image_path('core', 'actions/rename.svg'))?>" + alt="<?php p($l->t("set new password"))?>" title="<?php p($l->t("set new password"))?>"/> + </td> + <td class="groups"> + <select + class="groupsselect" + data-username="<?php p($user['name']) ;?>" + data-user-groups="<?php p(json_encode($user['groups'])) ;?>" + data-placeholder="groups" title="<?php p($l->t('Groups'))?>" + multiple="multiple"> + <?php foreach($_["adminGroup"] as $adminGroup): ?> + <option value="<?php p($adminGroup['name']);?>"><?php p($adminGroup['name']); ?></option> + <?php endforeach; ?> + <?php foreach($_["groups"] as $group): ?> + <option value="<?php p($group['name']);?>"><?php p($group['name']);?></option> + <?php endforeach;?> + </select> + </td> + <?php if(is_array($_['subadmins']) || $_['subadmins']): ?> + <td class="subadmins"> + <select + class="subadminsselect" + data-username="<?php p($user['name']) ;?>" + data-subadmin="<?php p(json_encode($user['subadmin']));?>" + data-placeholder="subadmins" title="<?php p($l->t('Group Admin'))?>" + multiple="multiple"> + <?php foreach($_["subadmingroups"] as $group): ?> + <option value="<?php p($group);?>"><?php p($group);?></option> + <?php endforeach;?> + </select> + </td> + <?php endif;?> + <td class="quota"> + <select class='quota-user' data-inputtitle="<?php p($l->t('Please enter storage quota (ex: "512 MB" or "12 GB")')) ?>"> + <option + <?php if($user['quota'] === 'default') print_unescaped('selected="selected"');?> + value='default'> + <?php p($l->t('Default'));?> + </option> + <option + <?php if($user['quota'] === 'none') print_unescaped('selected="selected"');?> + value='none'> + <?php p($l->t('Unlimited'));?> + </option> + <?php foreach($_['quota_preset'] as $preset):?> + <option + <?php if($user['quota']==$preset) print_unescaped('selected="selected"');?> + value='<?php p($preset);?>'> + <?php p($preset);?> + </option> + <?php endforeach;?> + <?php if($user['isQuotaUserDefined']):?> + <option selected="selected" value='<?php p($user['quota']);?>'> + <?php p($user['quota']);?> + </option> + <?php endif;?> + <option value='other' data-new> + <?php p($l->t('Other'));?> + ... + </option> + </select> + </td> + <td class="storageLocation"><?php p($user["storageLocation"]); ?></td> + <?php + if($user["lastLogin"] === 0) { + $lastLogin = $l->t('never'); + $lastLoginDate = ''; + } else { + $lastLogin = relative_modified_date($user["lastLogin"]); + $lastLoginDate = \OC_Util::formatDate($user["lastLogin"]); + } + ?> + <td class="lastLogin" title="<?php p('<span style="white-space: nowrap;">'.$lastLoginDate.'</span>'); ?>"><?php p($lastLogin); ?></td> + <td class="remove"> + <?php if($user['name']!=OC_User::getUser()):?> + <a href="#" class="action delete" original-title="<?php p($l->t('Delete'))?>"> + <img src="<?php print_unescaped(image_path('core', 'actions/delete.svg')) ?>" class="svg" /> + </a> + <?php endif;?> + </td> + </tr> + <?php endforeach; ?> + </tbody> +</table> diff --git a/settings/users.php b/settings/users.php index f09d0e90d3c..8f72fc9d5c8 100644 --- a/settings/users.php +++ b/settings/users.php @@ -8,7 +8,10 @@ OC_Util::checkSubAdminUser(); // We have some javascript foo! -OC_Util::addScript( 'settings', 'users' ); +OC_Util::addScript('settings', 'users/deleteHandler'); +OC_Util::addScript('settings', 'users/filter'); +OC_Util::addScript( 'settings', 'users/users' ); +OC_Util::addScript( 'settings', 'users/groups' ); OC_Util::addScript( 'core', 'multiselect' ); OC_Util::addScript( 'core', 'singleselect' ); OC_Util::addScript('core', 'jquery.inview'); @@ -16,19 +19,23 @@ OC_Util::addStyle( 'settings', 'settings' ); OC_App::setActiveNavigationEntry( 'core_users' ); $users = array(); -$groups = array(); +$userManager = \OC_User::getManager(); +$groupManager = \OC_Group::getManager(); + +$isAdmin = OC_User::isAdminUser(OC_User::getUser()); + +$groupsInfo = new \OC\Group\MetaData(OC_User::getUser(), $isAdmin, $groupManager); +$groupsInfo->setSorting($groupsInfo::SORT_USERCOUNT); +list($adminGroup, $groups) = $groupsInfo->get(); -$isadmin = OC_User::isAdminUser(OC_User::getUser()); $recoveryAdminEnabled = OC_App::isEnabled('files_encryption') && OC_Appconfig::getValue( 'files_encryption', 'recoveryAdminEnabled' ); -if($isadmin) { - $accessiblegroups = OC_Group::getGroups(); - $accessibleusers = OC_User::getDisplayNames('', 30); +if($isAdmin) { + $accessibleUsers = OC_User::getDisplayNames('', 30); $subadmins = OC_SubAdmin::getAllSubAdmins(); }else{ - $accessiblegroups = OC_SubAdmin::getSubAdminsGroups(OC_User::getUser()); - $accessibleusers = OC_Group::displayNamesInGroups($accessiblegroups, '', 30); + $accessibleUsers = OC_Group::displayNamesInGroups($groups, '', 30); $subadmins = false; } @@ -45,7 +52,7 @@ $defaultQuotaIsUserDefined=array_search($defaultQuota, $quotaPreset)===false && array_search($defaultQuota, array('none', 'default'))===false; // load users and quota -foreach($accessibleusers as $uid => $displayName) { +foreach($accessibleUsers as $uid => $displayName) { $quota=OC_Preferences::getValue($uid, 'files', 'quota', 'default'); $isQuotaUserDefined=array_search($quota, $quotaPreset)===false && array_search($quota, array('none', 'default'))===false; @@ -55,6 +62,7 @@ foreach($accessibleusers as $uid => $displayName) { $name = $name . ' ('.$uid.')'; } + $user = $userManager->get($uid); $users[] = array( "name" => $uid, "displayName" => $displayName, @@ -62,23 +70,21 @@ foreach($accessibleusers as $uid => $displayName) { 'quota' => $quota, 'isQuotaUserDefined' => $isQuotaUserDefined, 'subadmin' => OC_SubAdmin::getSubAdminsGroups($uid), + 'storageLocation' => $user->getHome(), + 'lastLogin' => $user->getLastLogin(), ); } -foreach( $accessiblegroups as $i ) { - // Do some more work here soon - $groups[] = array( "name" => $i ); -} - -$tmpl = new OC_Template( "settings", "users", "user" ); +$tmpl = new OC_Template( "settings", "users/main", "user" ); $tmpl->assign( 'users', $users ); $tmpl->assign( 'groups', $groups ); -$tmpl->assign( 'isadmin', (int) $isadmin); +$tmpl->assign( 'adminGroup', $adminGroup ); +$tmpl->assign( 'isAdmin', (int) $isAdmin); $tmpl->assign( 'subadmins', $subadmins); -$tmpl->assign( 'numofgroups', count($accessiblegroups)); +$tmpl->assign( 'numofgroups', count($groups) + count($adminGroup)); $tmpl->assign( 'quota_preset', $quotaPreset); $tmpl->assign( 'default_quota', $defaultQuota); $tmpl->assign( 'defaultQuotaIsUserDefined', $defaultQuotaIsUserDefined); $tmpl->assign( 'recoveryAdminEnabled', $recoveryAdminEnabled); -$tmpl->assign('enableAvatars', \OC_Config::getValue('enable_avatars', true)); +$tmpl->assign( 'enableAvatars', \OC_Config::getValue('enable_avatars', true)); $tmpl->printPage(); |