(function() { 'use strict'; /** * @name User * @constructor * @param {object} [userData] - some default values for the user */ function User(userData) { if (userData) { this.init(userData); } } /** * @memberof User * @desc The factory we'll use to register with Angular. * @return the User constructor */ User.factory = ['$q', '$log', 'sgSettings', 'Resource', function($q, $log, Settings, Resource) { angular.extend(User, { $q: $q, $log: $log, $$resource: new Resource(Settings.activeUser('folderURL'), Settings.activeUser()), $query: '', $users: [] }); return User; }]; /** * @module SOGo.Common * @desc Factory registration of User in Angular module. */ angular.module('SOGo.Common').factory('User', User.factory); /** * @memberof User * @desc Search for users that match a string. * @param {string} search - a string used to performed the search * @param {object[]} excludedUsers - a list of User objects that must be excluded from the results * @return a promise of an array of matching User objects */ User.$filter = function(search, excludedUsers, options) { var _this = this, resource = User.$$resource, param = {search: search}; if (!options || !options.dry) { if (!search) { // No query specified User.$users.splice(0, User.$users.length); return User.$q.when(User.$users); } if (User.$query == search) { // Query hasn't changed return User.$q.when(User.$users); } User.$query = search; } else if (options && options.uid) { resource = User.$$resource.userResource(options.uid); } return resource.fetch(null, 'usersSearch', param).then(function(response) { var results, index, user, users, compareUids = function(data) { return this.uid == data.uid; }; if (options) { if (options.dry) users = []; else if (options.results) users = options.results; } else users = User.$users; if (excludedUsers) { // Remove excluded users from response results = _.filter(response.users, function(user) { return !_.find(excludedUsers, _.bind(compareUids, user)); }); } else { results = response.users; } // Remove users that no longer match the search query for (index = users.length - 1; index >= 0; index--) { user = users[index]; if (!_.find(results, _.bind(compareUids, user))) { users.splice(index, 1); } } // Add new users matching the search query _.forEach(results, function(data, index) { if (_.isUndefined(_.find(users, _.bind(compareUids, data)))) { var user = new User(data); users.splice(index, 0, user); } }); User.$log.debug(users); return users; }); }; /** * @function init * @memberof User.prototype * @desc Extend instance with required attributes and new data. * @param {object} data - attributes of user */ User.prototype.init = function(data) { angular.extend(this, data); if (!this.$$shortFormat) this.$$shortFormat = this.$shortFormat(); if (!this.$$image) this.$$image = this.image; this.$avatarIcon = (this.$isGroup() || this.$isSpecial()) ? 'group' : 'person'; // NOTE: We can't assign a Gravatar at this stage since we would need the Preferences module // which already depend on the User module. // An empty attribute to trick md-autocomplete when adding users from the ACLs editor this.empty = ' '; }; /** * @function $fullname * @memberof User.prototype * @return a string representing the fullname */ User.prototype.$fullname = function() { var fullname = this.cn || this.uid; if (this.c_info) fullname += ' (' + this.c_info.split("\n").join("; ") + ')'; return fullname; }; /** * @function $shortFormat * @memberof User.prototype * @return the fullname along with the email address */ User.prototype.$shortFormat = function(options) { var fullname = this.$fullname(); var email = this.c_email; var no_email = options && options.email === false; if (!no_email && email && fullname != email) { fullname += ' <' + email + '>'; } return fullname; }; /** * @function $acl * @memberof User.prototype * @desc Fetch the user rights associated to a specific folder and populate the 'rights' attribute. * @param {string} the folder ID * @param {Object} owner - the owner to use when fetching the ACL as it might not be the Settings.activeUser * @return a promise */ User.prototype.$acl = function(folderId, owner) { var _this = this, deferred = User.$q.defer(), param = {uid: this.uid}; if (this.$shadowRights) { deferred.resolve(this.rights); } else { var rights; if (angular.isDefined(owner)) rights = User.$$resource.userResource(owner).fetch(folderId, 'userRights', param); else rights = User.$$resource.fetch(folderId, 'userRights', param); rights.then(function(data) { _this.rights = data; // Convert numbers (0|1) to boolean values //angular.forEach(_.keys(_this.rights), function(key) { // _this.rights[key] = _this.rights[key] ? true : false; //}); // console.debug('rights ' + _this.uid + ' => ' + JSON.stringify(data, undefined, 2)); // Keep a copy of the server's version _this.$shadowRights = angular.copy(data); deferred.resolve(data); return data; }); } return deferred.promise; }; /** * @function $isGroup * @memberof User.prototype * @return true if the user actually represents a group of users */ User.prototype.$isGroup = function() { return this.isGroup || this.userClass && this.userClass == 'normal-group'; }; /** * @function $isAnonymous * @memberof User.prototype * @return true if it's the special anonymous user */ User.prototype.$isAnonymous = function() { return this.uid == 'anonymous'; }; /** * @function $isSpecial * @memberof User.prototype * @desc Only accurate from the ACL editor. * @return true if the user is not a regular system user */ User.prototype.$isSpecial = function() { return this.userClass && this.userClass == 'public-user'; }; /** * @function $confirmRights * @memberof User.prototype * @desc Check if a confirmation is required before giving some rights. * @return the confirmation message or false if no confirmation is required */ User.prototype.$confirmRights = function(folder) { var confirmation = false; if (this.$confirmation) { // Don't bother the user more than once return false; } if (_.some(_.values(this.rights))) { if (this.uid == 'anonymous') { if (folder.constructor.name == 'AddressBook') confirmation = l('Potentially anyone on the Internet will be able to access your address book "%{0}", even if they do not have an account on this system. Is this information suitable for the public Internet?', folder.name); else if (folder.constructor.name == 'Calendar') confirmation = l('Potentially anyone on the Internet will be able to access your calendar "%{0}", even if they do not have an account on this system. Is this information suitable for the public Internet?', folder.name); } else if (this.uid == 'anyone' || this.uid == '') { if (folder.constructor.name == 'AddressBook') confirmation = l('Any user with an account on this system will be able to access your address book "%{0}". Are you certain you trust them all?', folder.name); else if (folder.constructor.name == 'Calendar') confirmation = l('Any user with an account on this system will be able to access your calendar "%{0}". Are you certain you trust them all?', folder.name); else if (folder.constructor.name == 'Mailbox') confirmation = l('Any user with an account on this system will be able to access your mailbox "%{0}". Are you certain you trust them all?', folder.name); } } this.$confirmation = confirmation; return confirmation; }; /** * @function $rightsAreDirty * @memberof User.prototype * @return whether or not the rights have changed from their initial values */ User.prototype.$rightsAreDirty = function() { return this.rights && !_.isEqual(this.rights, this.$shadowRights); }; /** * @function $resetRights * @memberof User.prototype * @desc Restore initial rights or disable all rights * @param {boolean} [zero] - reset all rights to zero when true */ User.prototype.$resetRights = function(zero) { var _this = this; if (zero) { // Disable all rights _.map(_.keys(this.rights), function(key) { if (angular.isString(_this.rights[key])) _this.rights[key] = 'None'; else _this.rights[key] = 0; }); } else if (this.$shadowRights) { // Restore initial rights this.rights = angular.copy(this.$shadowRights); } }; /** * @function $folders * @memberof User.prototype * @desc Retrieve the list of folders of a specific type * @param {string} type - either 'contact' or 'calendar' * @return a promise of the HTTP query result or the cached result */ User.prototype.$folders = function(type) { var _this = this, deferred = User.$q.defer(), param = {type: type}; if (this.$$folders) { deferred.resolve(this.$$folders); } else { User.$$resource.userResource(this.uid).fetch(null, 'foldersSearch', param).then(function(response) { _this.$$folders = response.folders; deferred.resolve(response.folders); }); } return deferred.promise; }; /** * @function $omit * @memberof User.prototype * @desc Return a sanitized object used to send to the server. * @return an object literal copy of the User instance */ User.prototype.$omit = function() { var user = {}; angular.forEach(this, function(value, key) { if (key != 'constructor' && key[0] != '$') { user[key] = value; } }); return user; }; User.prototype.toString = function() { return '[User ' + this.c_email + ']'; }; })();