owncloud/core

View on GitHub
settings/js/users/users.js

Summary

Maintainability
F
1 wk
Test Coverage
/**
 * 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 UserDeleteHandler;
var UserList = {
    availableGroups: {},
    offset: 0,
    usersToLoad: 200,
    initialUsersToLoad: 200, // initial number of users to load
    perPageUsersToLoad: 100, // users to load when user scrolls down
    currentUser: '',
    currentGid: '',
    filter: '',

    /**
     * Initializes the user list
     * @param $el user list table element
     */
    initialize: function($el) {
        this.$el = $el;

        UserList.currentUser = OC.getCurrentUser().uid;

        // initially the list might already contain user entries (not fully ajaxified yet)
        // initialize these entries
        this.$el.find('.isEnabled').on('change', this.onEnabledChange);
        this.$el.find('.quota-user').singleSelect().on('change', this.onQuotaSelect);
    },

    /**
     * Add a user row from user object
     *
     * @param user object containing following keys:
     *             {
     *                 'name':             'username',
     *                 'displayname':         'Users display name',
     *                 'groups':             ['group1', 'group2'],
     *                 'subadmin':         ['group4', 'group5'],
     *                'enabled'            'true'
     *                'quota':             '10 GB',
     *                'storageLocation':    '/srv/www/owncloud/data/username',
         *                              'creationTime':         '1418632333',
     *                'lastLogin':        '1418632333'
     *                'backend':            'LDAP',
     *                'email':            'username@example.org'
     *                'isRestoreDisabled':false
     *             }
     * @returns table row created for this user
     */
    add: function (user) {
        if (this.currentGid && this.currentGid !== '_everyone' && user.groups[this.currentGid] === undefined) {
            return;
        }

        var $tr = $userListBody.find('tr:first-child').clone();
        // this removes just the `display:none` of the template row
        $tr.removeAttr('style');

        /**
         * Avatar or placeholder
         */
        if ($tr.find('div.avatardiv').length) {
            if (user.isAvatarAvailable === true) {
                $('div.avatardiv', $tr).avatar(user.name, 32, undefined, undefined, undefined, user.displayname);
            } else {
                $('div.avatardiv', $tr).imageplaceholder(user.displayname, undefined, 32);
            }
        }

        /**
         * add username and displayname to row (in data and visible markup)
         */
        $tr.data('uid', user.name);
        $tr.data('displayname', user.displayname);
        $tr.data('mailAddress', user.email);
        $tr.data('restoreDisabled', user.isRestoreDisabled);
        $tr.find('.name').text(user.name);
        $tr.find('td.displayName > span').text(user.displayname);
        $tr.find('td.mailAddress > span').text(user.email);
        $tr.find('td.displayName > .action').tooltip({placement: 'top'});
        $tr.find('td.mailAddress > .action').tooltip({placement: 'top'});
        $tr.find('td.password > .action').tooltip({placement: 'top'});


        /**
         * groups and subadmins
         */
        var $tdGroups = $tr.find('td.groups');
        this._updateGroupListLabel($tdGroups, user.groups);
        $tdGroups.find('.action').tooltip({placement: 'top'});

        var $tdSubadmins = $tr.find('td.subadmins');
        this._updateGroupListLabel($tdSubadmins, user.subadmin);
        $tdSubadmins.find('.action').tooltip({placement: 'top'});

        /**
         * enabled
         */
        var $tdEnabled = $tr.find('.isEnabled');
        if(user.name !== UserList.currentUser) {
            $tdEnabled.attr("checked", user.isEnabled);
            $tdEnabled.on('change', UserList.onEnabledChange);
        } else {
            $tdEnabled.remove();
        }

        /**
         * resend invitation email action
         */
        if (user.lastLogin === 0 && user.isGuest !== true) {
            var resendImage = $('<img class="action">').attr({
                src: OC.imagePath('core', 'actions/mail')
            });
            var resendLink = $('<a class="action resendInvitationEmail">')
                .attr({ href: '#', 'title': t('settings', 'Resend invitation email')})
                .append(resendImage);
            $tr.find('td.resendInvitationEmail').append(resendLink);
        }

        /**
         * remove action
         */
        if ($tr.find('td.remove img').length === 0 && OC.currentUser !== user.name) {
            var deleteImage = $('<img class="action">').attr({
                src: OC.imagePath('core', 'actions/delete')
            });
            var deleteLink = $('<a class="action delete">')
                .attr({ href: '#', 'title': t('settings', 'Delete')})
                .append(deleteImage);
            $tr.find('td.remove').append(deleteLink);
        } else if (OC.currentUser === user.name) {
            $tr.find('td.remove a').remove();
        }

        /**
         * quota
         */
        var $quotaSelect = $tr.find('.quota-user');
        if (user.quota === 'default') {
            $quotaSelect
                .data('previous', 'default')
                .find('option').attr('selected', null)
                .first().attr('selected', 'selected');
        } else {
            var $options = $quotaSelect.find('option');
            var $foundOption = $options.filterAttr('value', user.quota);
            if ($foundOption.length > 0) {
                $foundOption.attr('selected', 'selected');
            } else {
                // append before "Other" entry
                $options.last().before('<option value="' + escapeHTML(user.quota) + '" selected="selected">' + escapeHTML(user.quota) + '</option>');
            }
        }

        /**
         * storage location
         */
        $tr.find('td.storageLocation').text(user.storageLocation);

        /**
         * user backend
         */
        $tr.find('td.userBackend').text(user.backend);

        /**
         * last login
         */
        var lastLoginRel = t('settings', 'never');
        var lastLoginAbs = lastLoginRel;
        if(user.lastLogin !== 0) {
            lastLoginRel = OC.Util.relativeModifiedDate(user.lastLogin);
            lastLoginAbs = OC.Util.formatDate(user.lastLogin);
        }
        var $tdLastLogin = $tr.find('td.lastLogin');
        $tdLastLogin.text(lastLoginRel);
        $tdLastLogin.attr('title', lastLoginAbs);
        // setup tooltip with #app-content as container to prevent the td to resize on hover
        $tdLastLogin.tooltip({placement: 'top', container: '#app-content'});

                /**
                 * creation time
                 */
                var creationTimeRel = t('settings', 'unknown');
                var creationTimeAbs = creationTimeRel;
                if(user.creationTime !== 0) {
                        creationTimeRel = OC.Util.relativeModifiedDate(user.creationTime);
                        creationTimeAbs = OC.Util.formatDate(user.creationTime);
                }
                var $tdCreationTime = $tr.find('td.creationTime');
                $tdCreationTime.text(creationTimeRel);
                $tdCreationTime.attr('title', creationTimeAbs);
                // setup tooltip with #app-content as container to prevent the td to resize on hover
                $tdCreationTime.tooltip({placement: 'top', container: '#app-content'});

        /**
         * append generated row to user list
         */
        $tr.appendTo($userList);
        if(UserList.isEmpty === true) {
            $tr.show();
            UserList.isEmpty = false;
            UserList.checkUsersToLoad();
        }

        $quotaSelect.on('change', UserList.onQuotaSelect);

        // defer init so the user first sees the list appear more quickly
        window.setTimeout(function(){
            $quotaSelect.singleSelect();
        }, 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 = this.filter;
        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;
        }
    },
    checkUsersToLoad: function() {
        if(UserList.isEmpty === false) {
            UserList.usersToLoad = UserList.perPageUsersToLoad;
        } else {
            UserList.usersToLoad = UserList.initialUsersToLoad;
        }
    },
    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();
    },
    markRemove: function(uid) {
        var $tr = UserList.getRow(uid);
        var groups = $tr.find('.groups').data('groups');
        for(var i in groups) {
            var gid = groups[i]['gid'];
            var $li = GroupList.getGroupLI(gid);
            var userCount = GroupList.getUserCount($li);
            GroupList.setUserCount($li, userCount - 1);
        }
        GroupList.decEveryoneCount();
        UserList.hide(uid);
    },
    remove: function(uid) {
        UserList.getRow(uid).remove();
    },
    undoRemove: function(uid) {
        var $tr = UserList.getRow(uid);
        var groups = $tr.find('.groups').data('groups');
        for(var i in groups) {
            var gid = groups[i]['gid'];
            var $li = GroupList.getGroupLI(gid);
            var userCount = GroupList.getUserCount($li);
            GroupList.setUserCount($li, userCount + 1);
        }
        GroupList.incEveryoneCount();
        UserList.getRow(uid).show();
    },
    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();
    },
    getMailAddress: function(element) {
        return ($(element).closest('tr').data('mailAddress') || '').toString();
    },
    getRestoreDisabled: function(element) {
        return ($(element).closest('tr').data('restoreDisabled') || '');
    },
    initDeleteHandling: function() {
        //set up handler
        UserDeleteHandler = new DeleteHandler('/settings/users/users', 'username',
                                            UserList.markRemove, UserList.remove, UserList.undoRemove);

        //when to mark user for delete
        $userListBody.on('click', '.delete', function () {
            // Call function for handling delete/undo
            var uid = UserList.getUID(this);
            OC.dialogs.confirm(
                t('settings', 'You are about to delete a user. This action can\'t be undone and is permanent. All user data, files and shares will be deleted. Are you sure that you want to permanently delete {userName}?', {userName: uid}),
                t('settings', 'Delete user'),
                function (confirmation) {
                    if (confirmation) {
                        UserDeleteHandler.mark(uid);
                        UserDeleteHandler.deleteEntry();
                    }
                }
            );
        });
    },
    initResendInvitationEmailHandling: function () {
        $userListBody.on('click', '.action.resendInvitationEmail', function () {
            var uid = UserList.getUID(this);
            $.post(
                OC.generateUrl('/resend/invitation/{id}', {id: uid}),
                {}
            ).done(function () {
                OC.Notification.showTemporary(t('settings', 'The invitation email for this user has been resent'));
            }).error(function () {
                OC.Notification.showTemporary(t('settings', 'The invitation email for this user could not be resent'));
            });
        });
    },
    update: function (gid, limit) {
        if (UserList.updating) {
            return;
        }
        if(!limit) {
            limit = UserList.usersToLoad;
        }
        $userList.siblings('.loading').css('visibility', 'visible');
        UserList.updating = true;
        if(gid === undefined) {
            gid = '';
        }
        UserList.currentGid = gid;
        var pattern = this.filter;
        $.get(
            OC.generateUrl('/settings/users/users'),
            { offset: UserList.offset, limit: limit, gid: gid, pattern: pattern },
            function (result) {
                var trs = [];
                //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, function (index, user) {
                    if(UserList.has(user.name)) {
                        return true;
                    }
                    var $tr = UserList.add(user);
                    trs.push($tr);
                });
                if (result.length > 0) {
                    $userList.siblings('.loading').css('visibility', 'hidden');
                    // reset state on load
                    UserList.noMoreEntries = false;
                }
                else {
                    UserList.noMoreEntries = true;
                    $userList.siblings('.loading').remove();
                }
                UserList.offset += limit;
            }).always(function() {
                UserList.updating = false;
            });
    },

    applyGroupSelect: function (element, user, checked) {
        var $element = $(element);

        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();
                            var groupGID = response.data.group.gid;
                            var groupName = response.data.group.name;
                            if (UserList.availableGroups[groupGID] === undefined &&
                                response.data.action === 'add'
                            ) {
                                UserList.availableGroups[groupGID] = {
                                    'gid': groupGID,
                                    'name': groupName
                                };
                            }

                            if (response.data.action === 'add') {
                                GroupList.incGroupCount(groupGID);
                            } else {
                                GroupList.decGroupCount(groupGID);
                            }
                        }
                        if (response.data.message) {
                            OC.Notification.show(response.data.message);
                        }
                    }
                );
            };
        }

        $element.multiSelect({
            selectedFirst: true,
            checked: checked,
            oncheck: checkHandler,
            onuncheck: checkHandler,
            minWidth: 100
        });
    },

    applySubadminSelect: function (element, user, checked) {
        var $element = $(element);
        var checkHandler = function (group) {
            if (group === 'admin') {
                return false;
            }
            $.post(
                OC.filePath('settings', 'ajax', 'togglesubadmins.php'),
                {
                    username: user,
                    group: group
                },
                function () {
                }
            );
        };

        $element.multiSelect({
            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);
        }
    },

    /**
     * Event handler for when a quota has been changed through a single select.
     * This will save the value.
     */
    onQuotaSelect: function(ev) {
        var $select = $(ev.target);
        var uid = UserList.getUID($select);
        var quota = $select.val();
        if (quota === 'other') {
            return;
        }
        if (
            ['default', 'none'].indexOf(quota) === -1
            && (OC.Util.computerFileSize(quota) === null)
        ) {
            // the select component has added the bogus value, delete it again
            $select.find('option[selected]').remove();
            OC.Notification.showTemporary(t('settings', 'Invalid quota value "{val}"', {val: quota}));
            return;
        }
        UserList._updateQuota(uid, quota, function(returnedQuota){
            if (quota !== returnedQuota) {
                $select.find(':selected').text(returnedQuota);
            }
        });
    },

    /**
     * Saves the quota for the given user
     * @param {String} [uid] optional user id, sets default quota if empty
     * @param {String} quota quota value
     * @param {Function} ready callback after save
     */
    _updateQuota: function(uid, quota, ready) {
        $.post(
            OC.filePath('settings', 'ajax', 'setquota.php'),
            {username: uid, quota: quota},
            function (result) {
                if (ready) {
                    ready(result.data.quota);
                }
            }
        );
    },

    /**
         * Event handler for when a enabled value has been changed.
         * This will save the value.
         */
        onEnabledChange: function() {
                var $select = $(this);
                var uid = UserList.getUID($select);
                var enabled = $select.prop('checked') ? 'true' : 'false';

                UserList._updateEnabled(uid, enabled,
                        function(returnedEnabled){
                                if (enabled !== returnedEnabled) {
                                          $select.prop('checked', user.isEnabled);
                                }
                        });
        },


        /**
         * Saves the enabled value for the given user
         * @param {String} [uid] optional user id, sets default quota if empty
         * @param {String} enabled value
         * @param {Function} ready callback after save
         */
        _updateEnabled: function(uid, enabled, ready) {
               $.post(
                        OC.generateUrl('/settings/users/{id}/enabled', {id: uid}),
                        {username: uid, enabled: enabled},
                        function (result) {
                                   if(result.status == 'success') {
                                        OC.Notification.showTemporary(t('settings', 'User {uid} has been {state}!',
                                                                        {uid: uid,
                                                                        state: result.data.enabled === 'true' ?
                                                                        t('settings', 'enabled') :
                                                                        t('settings', 'disabled')}
                                                                     ));
                } else {
                                        OC.Notification.showTemporary(t('settings', result.data.message));
                }
                        }
               );
        },


    /**
     * Creates a temporary jquery.multiselect selector on the given group field
     */
    _triggerGroupEdit: function($td, isSubadminSelect) {
        var $groupsListContainer = $td.find('.groupsListContainer');
        var placeholder = $groupsListContainer.attr('data-placeholder') || t('settings', 'no group');
        var user = UserList.getUID($td);
        var checked = _.keys($td.data('groups')) || [];
        var extraGroups = [].concat(checked);
        var assignableGroups = new Set();
        var removableGroups = new Set();
        var checkedSet = new Set();
        $.each(checked, function(pos, groupGID) {
            checkedSet.add(groupGID);
        });

        $td.find('.multiselectoptions').remove();

        // jquery.multiselect can only work with select+options in DOM ? We'll give jquery.multiselect what it wants...
        var $groupsSelect;
        if (isSubadminSelect) {
            $groupsSelect = $('<select multiple="multiple" class="groupsselect multiselect button" title="' + placeholder + '"></select>');
        } else {
            $groupsSelect = $('<select multiple="multiple" class="subadminsselect multiselect button" title="' + placeholder + '"></select>')
        }

        function createGroupItem(gid, group) {
            if (isSubadminSelect) {
                // this is solely for the dropdown menu "Group Admin for"
                if (gid === 'admin') {
                    // can't become subadmin of "admin" group
                    return;
                }

                $groupsSelect.append($('<option value="' + escapeHTML(gid) + '">' + escapeHTML(group['name']) + '</option>'));
                // return as we need to bypass the following group restrictions here
                return;
            }

            var groupIsChecked = checkedSet.has(gid);
            var groupIsAssignable = assignableGroups.has(gid);
            var groupIsRemovable = removableGroups.has(gid);
            if (!groupIsChecked && !groupIsAssignable) {
                $groupsSelect.append($('<option value="' + escapeHTML(gid) + '" disabled="disabled">' + escapeHTML(group['name']) + '</option>'));
                return;
            }
            if (groupIsChecked && !groupIsRemovable) {
                $groupsSelect.append($('<option value="' + escapeHTML(gid) + '" disabled="disabled">' + escapeHTML(group['name']) + '</option>'));
                return;
            }
            $groupsSelect.append($('<option value="' + escapeHTML(gid) + '">' + escapeHTML(group['name']) + '</option>'));
        }

        $.ajax({
            type: 'GET',
            url: OC.generateUrl('/settings/groups/available'),
        }).then(function (result) {
            var that = this;

            if (result.data) {
                this.availableGroups = {};
                $.each(result.data.assignableGroups, function(gid, group) {
                    assignableGroups.add(gid);
                    that.availableGroups[gid] = {
                        'gid': gid,
                        'name': group['name']
                    };
                });
                $.each(result.data.removableGroups, function(gid, group) {
                    removableGroups.add(gid);
                });
            }

            $.each(this.availableGroups, function (gid, group) {
                // some new groups might be selected but not in the available groups list yet
                var extraIndex = extraGroups.indexOf(gid);
                if (extraIndex >= 0) {
                    // remove extra group as it was found
                    extraGroups.splice(extraIndex, 1);
                }
                createGroupItem(gid, group);
            });
            $.each(extraGroups, function (pos, groupGID) {
                createGroupItem(groupGID, $td.data('groups')[groupGID]);
            });

            $td.append($groupsSelect);

            if (isSubadminSelect) {
                UserList.applySubadminSelect($groupsSelect, user, checked);
            } else {
                UserList.applyGroupSelect($groupsSelect, user, checked);
            }

            $groupsListContainer.addClass('hidden');
            $td.find('.multiselect:not(.groupsListContainer):first').click();
            $groupsSelect.on('dropdownclosed', function(e) {
                $groupsSelect.remove();
                $td.find('.multiselect:not(.groupsListContainer)').parent().remove();
                $td.find('.multiselectoptions').remove();
                $groupsListContainer.removeClass('hidden');
                var groups = {};
                for (var i in e.checked) {
                    var gid = e.checked[i];
                    var groupInfo = $td.data('groups')[gid] ? $td.data('groups')[gid] : that.availableGroups[gid];
                    groups[gid] = {
                        'gid': gid,
                        'name': groupInfo['name']
                    };
                }
                UserList._updateGroupListLabel($td, groups);
            });
        }.bind(this));
    },

    /**
     * Updates the groups list td with the given groups selection
     */
    _updateGroupListLabel: function($td, groups) {
        var placeholder = $td.find('.groupsListContainer').attr('data-placeholder');
        var $groupsEl = $td.find('.groupsList');
        $groupsEl.text(_.pluck(groups, 'name').join(', ') || placeholder || t('settings', 'no group'));
        $td.data('groups', groups);
    }
};

$(document).ready(function () {
    OC.Plugins.attach('OC.Settings.UserList', UserList);
    $userList = $('#userlist');
    $userListBody = $userList.find('tbody');

    UserList.initDeleteHandling();
    UserList.initResendInvitationEmailHandling();

    // Implements User Search
    OCA.Search.users= new UserManagementFilter(UserList, GroupList);

    UserList.scrollArea = $('#app-content');

    UserList.availableGroups = $userList.data('groups');

    UserList.scrollArea.scroll(function(e) {UserList._onScroll(e);});

    $userList.after($('<div class="loading" style="height: 200px; visibility: hidden;"></div>'));

    // TODO: move other init calls inside of initialize
    UserList.initialize($('#userlist'));

    OC.AppConfig.getValue('core', 'umgmt_set_password', 'false', function (data) {
        var showPassword = $.parseJSON(data);
        if (showPassword === true) {
            $("#newuserpassword").show();
            $("#newemail").hide();
            $('#CheckBoxPasswordOnUserCreate').attr('checked', true);
        } else {
            $("#newemail").show();
            $("#newuserpassword").hide();
            $('#CheckBoxPasswordOnUserCreate').attr('checked', false);
        }
    });

    $userListBody.on('click', '.password', function (event) {
        event.stopPropagation();

        var $td = $(this).closest('td');
        var $tr = $(this).closest('tr');
        var uid = UserList.getUID($td);
        var $input = $('<input type="password" autocomplete="new-password" autocorrect="off">');
        var isRestoreDisabled = UserList.getRestoreDisabled($td) === true;
        if(isRestoreDisabled) {
            $tr.addClass('row-warning');
            // add tooltip if the password change could cause data loss - no recovery enabled
            var title = t('settings', 'Changing the password will result in data loss, because data recovery is not available for this user');
            $input.tooltip({placement:'bottom', title: title});
        }
        $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.showTemporary(t('settings', 'Password successfully changed'));
                                } else {
                                    OC.Notification.showTemporary(t('settings', result.data.message));
                                }
                            }
                        );
                        $input.blur();
                    } else {
                        $input.blur();
                    }
                }
            })
            .blur(function () {
                $(this).replaceWith($('<span>●●●●●●●</span>'));
                $td.find('img').show();
                // remove highlight class from users without recovery ability
                $tr.removeClass('row-warning');
            });
    });
    $('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) {
                        var $div = $tr.find('div.avatardiv');
                        if ($div.length) {
                            $div.imageplaceholder(uid, displayName);
                        }
                        $.post(
                            OC.generateUrl('/settings/users/{id}/displayName', {id: uid}),
                            {username: uid, displayName: $(this).val()},
                            function (result) {
                                if (result && result.status==='success' && $div.length){
                                    $div.avatar(result.data.username, 32);
                                }
                            }
                        );
                        var displayName = $input.val();
                        $tr.data('displayname', displayName);
                        $input.blur();
                    } else {
                        $input.blur();
                    }
                }
            })
            .blur(function () {
                var displayName = $tr.data('displayname');
                $input.replaceWith('<span>' + escapeHTML(displayName) + '</span>');
                $td.find('img').show();
            });
    });

    $userListBody.on('click', '.mailAddress', function (event) {
        event.stopPropagation();
        var $td = $(this).closest('td');
        var $tr = $td.closest('tr');
        var uid = UserList.getUID($td);
        var mailAddress = escapeHTML(UserList.getMailAddress($td));
        var $input = $('<input type="text">').val(mailAddress);
        $td.children('span').replaceWith($input);
        $td.find('img').hide();
        $input
            .focus()
            .keypress(function (event) {
                if (event.keyCode === 13) {
                    // enter key

                    var mailAddress = $input.val();
                    $td.find('.loading-small').css('display', 'inline-block');
                    $input.css('padding-right', '26px');
                    $input.attr('disabled', 'disabled');
                    $.ajax({
                        type: 'PUT',
                        url: OC.generateUrl('/settings/admin/{id}/mailAddress', {id: uid}),
                        data: {
                            mailAddress: $(this).val()
                        }
                    }).success(function () {
                        // set data attribute to new value
                        // will in blur() be used to show the text instead of the input field
                        $tr.data('mailAddress', mailAddress);
                        $td.find('.loading-small').css('display', '');
                        $input.removeAttr('disabled')
                            .triggerHandler('blur'); // needed instead of $input.blur() for Firefox
                    }).fail(function (result) {
                        OC.Notification.showTemporary(result.responseJSON.data.message);
                        $td.find('.loading-small').css('display', '');
                        $input.removeAttr('disabled')
                            .css('padding-right', '6px');
                    });
                }
            })
            .blur(function () {
                if($td.find('.loading-small').css('display') === 'inline-block') {
                    // in Chrome the blur event is fired too early by the browser - even if the request is still running
                    return;
                }
                var $span = $('<span>').text($tr.data('mailAddress'));
                $input.replaceWith($span);
                $td.find('img').show();
            });
    });

    $('#newuser .groupsListContainer').on('click', function (event) {
        event.stopPropagation();
        var $div = $(this).closest('.groups');
        UserList._triggerGroupEdit($div);
    });
    $userListBody.on('click', '.groups .groupsListContainer, .subadmins .groupsListContainer', function (event) {
        event.stopPropagation();
        var $td = $(this).closest('td');
        var isSubadminSelect = $td.hasClass('subadmins');
        UserList._triggerGroupEdit($td, isSubadminSelect);
    });

    // init the quota field select box after it is shown the first time
    $('#app-settings').one('show', function() {
        $(this).find('#default_quota').singleSelect().on('change', UserList.onQuotaSelect);
    });

    UserList._updateGroupListLabel($('#newuser .groups'), []);
    $('#newuser').submit(function (event) {
        event.preventDefault();
        var username = $('#newusername').val();
        var password = $('#newuserpassword').val();
        var email = $('#newemail').val();
        var setPassword = $('#CheckBoxPasswordOnUserCreate').is(':checked');

        if ($.trim(username) === '') {
            OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', {
                message: t('settings', 'A valid username must be provided')
            }));
            return false;
        }
        if (setPassword === true) {
            if ($.trim(password) === '') {
                OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', {
                    message: t('settings', 'A valid password must be provided')
                }));
                return false;
            }
        } else {
            if ($.trim(email) === '') {
                OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', {
                    message: t('settings', 'A valid email must be provided')
                }));
                return false;
            }
        }

        var groups = _.keys($('#newuser .groups').data('groups')) || [];

        var data = {
            username: username,
            groups: groups,
        };

        if (setPassword === true) {
            data.password = password;
            data.email = '';
        } else {
            data.password = '';
            data.email = email;
        }

        $.post(
            OC.generateUrl('/settings/users/users'),
            data,
            function (result) {
                if (result.groups) {
                    for (var i in result.groups) {
                        var gid = result.groups[i]['gid'];
                        var displayname = result.groups[i]['name'];
                        if (UserList.availableGroups[gid] === undefined) {
                            UserList.availableGroups[gid] = {
                                'gid': gid,
                                'name': displayname
                            };
                        }
                        $li = GroupList.getGroupLI(gid);
                        userCount = GroupList.getUserCount($li);
                        GroupList.setUserCount($li, userCount + 1);
                    }
                }
                if (!UserList.has(username)) {
                    UserList.add(result);
                }
                $('#newusername').focus();
                GroupList.incEveryoneCount();
            }).fail(function (result) {
            OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', {
                message: result.responseJSON.message
            }, undefined, {escape: false}));
        }).success(function () {
            $('#newuser').get(0).reset();
        });
    });

    if ($('#CheckboxIsEnabled').is(':checked')) {
        $("#userlist .enabled").show();
    }
    // Option to display/hide the "Enabled" column
    $('#CheckboxIsEnabled').click(function () {
        if ($('#CheckboxIsEnabled').is(':checked')) {
            $("#userlist .enabled").show();
            OC.AppConfig.setValue('core', 'umgmt_show_is_enabled', 'true');
        } else {
            $("#userlist .enabled").hide();
            OC.AppConfig.setValue('core', 'umgmt_show_is_enabled', 'false');
        }
    });

    if ($('#CheckboxStorageLocation').is(':checked')) {
        $("#userlist .storageLocation").show();
    }
    // Option to display/hide the "Storage location" column
    $('#CheckboxStorageLocation').click(function() {
        if ($('#CheckboxStorageLocation').is(':checked')) {
            $("#userlist .storageLocation").show();
            OC.AppConfig.setValue('core', 'umgmt_show_storage_location', 'true');
        } else {
            $("#userlist .storageLocation").hide();
            OC.AppConfig.setValue('core', 'umgmt_show_storage_location', 'false');
        }
    });

        if ($('#CheckboxCreationTime').is(':checked')) {
                $("#userlist .creationTime").show();
        }
        // Option to display/hide the "Creation Time" column
        $('#CheckboxCreationTime').click(function() {
                if ($('#CheckboxCreationTime').is(':checked')) {
                        $("#userlist .creationTime").show();
                        OC.AppConfig.setValue('core', 'umgmt_show_creation_time', 'true');
                } else {
                        $("#userlist .creationTime").hide();
                        OC.AppConfig.setValue('core', 'umgmt_show_creation_time', 'false');
                }
        });

    if ($('#CheckboxLastLogin').is(':checked')) {
        $("#userlist .lastLogin").show();
    } else {
        $("#userlist .lastLogin").hide();
    }
    // Option to display/hide the "Last Login" column
    $('#CheckboxLastLogin').click(function() {
        if ($('#CheckboxLastLogin').is(':checked')) {
            $("#userlist .lastLogin").show();
            OC.AppConfig.setValue('core', 'umgmt_show_last_login', 'true');
        } else {
            $("#userlist .lastLogin").hide();
            OC.AppConfig.setValue('core', 'umgmt_show_last_login', 'false');
        }
    });

    if ($('#CheckboxEmailAddress').is(':checked')) {
        $("#userlist .mailAddress").show();
    }
    // Option to display/hide the "Mail Address" column
    $('#CheckboxEmailAddress').click(function() {
        if ($('#CheckboxEmailAddress').is(':checked')) {
            $("#userlist .mailAddress").show();
            OC.AppConfig.setValue('core', 'umgmt_show_email', 'true');
        } else {
            $("#userlist .mailAddress").hide();
            OC.AppConfig.setValue('core', 'umgmt_show_email', 'false');
        }
    });

    if ($('#CheckboxUserBackend').is(':checked')) {
        $("#userlist .userBackend").show();
    }
    // Option to display/hide the "User Backend" column
    $('#CheckboxUserBackend').click(function() {
        if ($('#CheckboxUserBackend').is(':checked')) {
            $("#userlist .userBackend").show();
            OC.AppConfig.setValue('core', 'umgmt_show_backend', 'true');
        } else {
            $("#userlist .userBackend").hide();
            OC.AppConfig.setValue('core', 'umgmt_show_backend', 'false');
        }
    });

    if ($('#CheckBoxPasswordOnUserCreate').is(':checked')) {
        $("#newuserpassword").show();
    }
    // Option to display/hide the "E-Mail" input field
    $('#CheckBoxPasswordOnUserCreate').click(function () {
        if ($('#CheckBoxPasswordOnUserCreate').is(':checked')) {
            OC.AppConfig.setValue('core', 'umgmt_set_password', 'true');
            $('#newemail').hide();
            $('#newuserpassword').show();
        } else {
            OC.AppConfig.setValue('core', 'umgmt_set_password', 'false');
            $('#newemail').show();
            $("#newuserpassword").hide();
        }
    });

    if ($('#CheckboxQuota').is(':checked')) {
        $("#userlist .quota").show();
    }
    // Option to display/hide the "User Backend" column
    $('#CheckboxQuota').click(function() {
        if ($('#CheckboxQuota').is(':checked')) {
            $("#userlist .quota").show();
            OC.AppConfig.setValue('core', 'umgmt_show_quota', 'true');
        } else {
            $("#userlist .quota").hide();
            OC.AppConfig.setValue('core', 'umgmt_show_quota', 'false');
        }
    });

    if ($('#CheckboxPassword').is(':checked')) {
        $("#userlist .password").show();
    }
    // Option to display/hide the "User Backend" column
    $('#CheckboxPassword').click(function() {
        if ($('#CheckboxPassword').is(':checked')) {
            $("#userlist .password").show();
            OC.AppConfig.setValue('core', 'umgmt_show_password', 'true');
        } else {
            $("#userlist .password").hide();
            OC.AppConfig.setValue('core', 'umgmt_show_password', 'false');
        }
    });

    // calculate initial limit of users to load
    var initialUserCountLimit = UserList.initialUsersToLoad,
        containerHeight = $('#app-content').height();
    if(containerHeight > 40) {
        initialUserCountLimit = Math.floor(containerHeight/40);
        if (initialUserCountLimit < UserList.initialUsersToLoad) {
            initialUserCountLimit = UserList.initialUsersToLoad;
        }
    }
    //realign initialUserCountLimit with usersToLoad as a safeguard
    while((initialUserCountLimit % UserList.usersToLoad) !== 0) {
        // must be a multiple of this, otherwise LDAP freaks out.
        // FIXME: solve this in LDAP backend in  8.1
        initialUserCountLimit = initialUserCountLimit + 1;
    }

    // trigger loading of users on startup
    UserList.update(UserList.currentGid, initialUserCountLimit);

    _.defer(function() {
        $('#app-content').trigger($.Event('apprendered'));
    });

});