Multiple improvements regarding ACLs

This commit is contained in:
Francis Lachapelle
2014-11-03 12:19:35 -05:00
parent bd39312b19
commit 97ce3e6b72
11 changed files with 773 additions and 594 deletions

View File

@@ -67,63 +67,95 @@
<!-- modal for addressbook sharing options -->
<script type="text/ng-template" id="addressbookSharing.html">
<div id="modalACL">
<h2><var:string label:value="Access rights - "/>{{addressbook.name}}</h2>
<h2><var:string label:value="Access Rights"/> - <em>{{addressbook.name}}</em></h2>
<ul>
<!-- left side -->
<li id="usersList">
<ul>
<li data-ng-repeat="user in users | orderBy:['userClass', 'displayName']" data-ng-click="selectUser(user)" data-ng-class="{_selected: user==selected}">
<span class="card-picture" data-ng-switch="user.userClass">
<i data-ng-switch-when="normal-user" class="icon-ion-ios7-person"><!-- normal-user --></i>
<i data-ng-switch-when="public-user" class="icon-ion-ios7-people"><!-- public-user --></i>
</span>
<span class="name">{{user.displayName}}</span>
<span class="subscriptionArea" data-ng-hide="user.userClass == 'public-user'">
<input type="checkbox" data-ng-model="user.isSubscribed" data-ng-checked="user.isSubscribed"
data-ng-disabled="!user.canSubscribeUser" data-ng-change="markUserAsDirty(user)" />
<!-- left side -->
<li>
<ul class="aclUsers">
<li data-ng-repeat="user in users | orderBy:['userClass', 'displayName']"
data-ng-click="selectUser(user)"
data-ng-class="{_selected: user==selectedUser}">
<span class="card-picture" data-ng-switch="user.userClass">
<i data-ng-switch-when="normal-user" class="icon-ion-ios7-person"><!-- normal-user --></i>
<i data-ng-switch-when="public-user" class="icon-ion-ios7-people"><!-- public-user --></i>
</span>
<span class="name">{{user.$shortFormat()}}</span>
<span class="subscriptionArea"
data-ng-hide="user.$isSpecial()">
<label>
<input type="checkbox"
data-ng-model="user.isSubscribed"
data-ng-checked="user.isSubscribed"
data-ng-disabled="user.wasSubscribed" />
<span><var:string label:value="Subscribe User"/></span>
</span>
</label>
</span>
</li>
</ul>
<form data-ng-submit="addUser(userToAdd)" class="addContactsToolbar">
<form class="addContactsToolbar"
data-ng-submit="addUser(userToAdd)">
<table id="bottomTable">
<tr>
<td id="td_1"><i class="icon-ion-search"><!-- search --></i>
<input type="search" data-ng-model="userToAdd" label:placeholder="email address" typeahead-wait-ms="500"
typeahead="user as user.cn for user in User.$filter($viewValue) | filter:$viewValue"
class="form-control" /></td>
<td id="td_2"><button type="submit" ><var:string label:value="Add User"/></button>
<button type="button" data-ng-disabled="userIsReadOnly()" data-ng-click="removeUser()"><var:string label:value="Remove User"/></button></td>
<input type="search" class="form-control" label:placeholder="email address"
data-ng-model="userToAdd"
data-typeahead-editable="false"
data-typeahead-wait-ms="500"
data-typeahead="user as user.$shortFormat() for user in userFilter($viewValue)"/>
</td>
<td id="td_2">
<button type="submit" ><var:string label:value="Add User" /></button>
<button type="button"
data-ng-disabled="!selectedUser || selectedUser.$isSpecial()"
data-ng-click="removeUser(selectedUser)"><var:string label:value="Remove User"/></button>
</td>
</tr>
</table>
</form>
</li>
<!-- right side -->
<li id="AccessRightList">
<!-- right side -->
<li id="AccessRightList" data-ng-show="selectedUser">
<div class="title">
{{userSelected.displayName}}
{{selectedUser.$shortFormat()}}
</div>
<div class="calendarUserRights">
<ul>
<li data-ng-show="displayUserRights()">
<input type="checkbox" data-ng-checked="userSelected.aclOptions.canCreateObjects"
data-ng-model="userSelected.aclOptions.canCreateObjects" data-ng-change="markUserAsDirty()" />
<var:string label:value="This person can add cards to this addressbook."/></li>
<li data-ng-show="displayUserRights()" data-ng-model="displayUserRights">
<input type="checkbox" data-ng-checked="userSelected.aclOptions.canEditObjects"
ng-model="userSelected.aclOptions.canEditObjects" data-ng-change="markUserAsDirty()" />
<var:string label:value="This person can edit the cards of this addressbook."/></li>
<li data-ng-show="displayUserRights()" data-ng-model="displayUserRights">
<input type="checkbox" data-ng-checked="userSelected.aclOptions.canEraseObjects"
data-ng-model="userSelected.aclOptions.canEraseObjects" data-ng-change="markUserAsDirty()" />
<var:string label:value="This person can erase cards from this addressbook."/></li>
<li data-ng-hide="!userSelected.displayName">
<input type="checkbox" data-ng-checked="userSelected.aclOptions.canViewObjects"
data-ng-model="userSelected.aclOptions.canViewObjects" data-ng-change="markUserAsDirty()" />
<var:string label:value="This person can read the cards of this addressbook."/></li>
<li>
<label>
<input type="checkbox"
data-ng-checked="selectedUser.rights.canViewObjects"
data-ng-model="selectedUser.rights.canViewObjects"
data-ng-change="confirmChange(selectedUser)" />
<var:string label:value="This person can read the cards of this addressbook."/>
</label>
</li>
<li data-ng-hide="selectedUser.$isAnonymous()">
<label>
<input type="checkbox"
data-ng-checked="selectedUser.rights.canCreateObjects"
data-ng-model="selectedUser.rights.canCreateObjects"
data-ng-change="confirmChange(selectedUser)" />
<var:string label:value="This person can add cards to this addressbook."/>
</label>
</li>
<li data-ng-hide="selectedUser.$isAnonymous()">
<label>
<input type="checkbox"
data-ng-checked="selectedUser.rights.canEditObjects"
data-ng-model="selectedUser.rights.canEditObjects"
data-ng-change="confirmChange(selectedUser)" />
<var:string label:value="This person can edit the cards of this addressbook."/>
</label>
</li>
<li data-ng-hide="selectedUser.$isAnonymous()">
<label>
<input type="checkbox"
data-ng-checked="selectedUser.rights.canEraseObjects"
data-ng-model="selectedUser.rights.canEraseObjects"
data-ng-change="confirmChange(selectedUser)" />
<var:string label:value="This person can erase cards from this addressbook."/>
</label>
</li>
</ul>
</div>
</li>

View File

@@ -8,7 +8,7 @@
xmlns:label="OGo:label"
xmlns:rsrc="OGo:url"
const:userDefaultsKeys="SOGoContactsCategories"
const:jsFiles="Common/user-model.js, Common/resource.js, Common/acl-model.js, Contacts/card-model.js, Contacts/addressbook-model.js"
const:jsFiles="Common/resource.js, Common/user-model.js, Common/acl-model.js, Contacts/card-model.js, Contacts/addressbook-model.js"
className="UIxPageFrame"
title="name"
var:popup="isPopup">
@@ -61,6 +61,7 @@
{{addressbook.name}}
<i class="icon ion-ios7-arrow-right"><!-- right arrow icon --></i>
<ion-option-button class="button-info"
ng-hide="addressbook.isRemote"
ng-click="edit(addressbook)"><var:string label:value="Edit"/></ion-option-button>
</ion-item>
</ion-list>
@@ -367,50 +368,66 @@
<script type="text/ng-template" id="acl-modal.html">
<ion-modal-view>
<ion-header-bar class="bar-positive">
<div class="buttons" data-ng-hide="onGoingSearch">
<div class="buttons" data-ng-hide="search.active">
<button class="button button-icon" data-ng-click="closeModal()"><var:string label:value="Close"/></button>
</div>
<div class="buttons" data-ng-show="onGoingSearch">
<div class="buttons" data-ng-show="search.active">
<button class="button button-icon" data-ng-click="cancelSearch()"><var:string label:value="Back"/></button>
</div>
<h1 class="title">{{addressbook.name}}</h1>
<h1 class="title">{{title}}</h1>
<div class="buttons">
<button class="button button-icon" data-ng-click="saveModal()"><var:string label:value="Save"/></button>
</div>
</ion-header-bar>
<ion-content>
<ion-list show-delete="showDelete" on-tap="toggleDelete(false)">
<ion-list show-delete="showDelete"
on-tap="toggleDelete(false)">
<div ng-repeat="user in users | orderBy:['userClass', 'displayName']">
<ion-item class="item-stable item-icon-left item-icon-right" data-ng-click="toggleUser(user)" on-swipe-right="toggleDelete(true)">
<i class="icon" data-ng-class="(user.userClass == 'public-user') ? 'ion-ios7-people' : 'ion-ios7-person'"><!-- spacer --></i>
{{user.displayName}}
<ion-item class="item-stable item-icon-left item-icon-right"
ng-click="toggleUser(user)"
on-swipe-right="toggleDelete(true)">
<i class="icon"
ng-class="(user.userClass == 'public-user') ? 'ion-ios7-people' : 'ion-ios7-person'"><!-- spacer --></i>
{{user.$shortFormat()}}
<i class="icon" data-ng-class="displayIcon(user)"><!-- spacer --></i>
<ion-delete-button class="ion-minus-circled" data-ng-disabled="user.userClass == 'public-user' || !user.inAclList"
data-ng-click="removeUser(user)"></ion-delete-button>
<ion-delete-button class="ion-minus-circled"
ng-disabled="user.$isSpecial() || !user.inAclList"
ng-click="removeUser(user)"></ion-delete-button>
</ion-item>
<div class="item-accordion" data-ng-show="isUserShown(user)">
<ion-toggle data-ng-show="displaySubscribeUser()" data-ng-model="userSelected.isSubscribed" data-ng-checked="userSelected.isSubscribed"
data-ng-disabled="!userSelected.canSubscribeUser" data-ng-change="markUserAsDirty()">
<div class="item-accordion" ng-show="userIsSelected(user)">
<ion-toggle ng-hide="user.$isSpecial()"
ng-model="selectedUser.isSubscribed"
ng-checked="selectedUser.isSubscribed"
ng-disabled="selectedUser.wasSubscribed">
<var:string label:value="Subscribe user"/></ion-toggle>
<ion-checkbox data-ng-show="displayUserRights()" data-ng-checked="userSelected.aclOptions.canCreateObjects"
data-ng-model="userSelected.aclOptions.canCreateObjects" data-ng-change="markUserAsDirty()">
<ion-checkbox data-ng-hide="selectedUser.$isAnonymous()"
data-ng-checked="selectedUser.rights.canCreateObjects"
data-ng-model="selectedUser.rights.canCreateObjects"
data-ng-change="confirmChange(selectedUser)">
<var:string label:value="This person can add cards to this addressbook."/></ion-checkbox>
<ion-checkbox data-ng-show="displayUserRights()" data-ng-checked="userSelected.aclOptions.canEditObjects"
data-ng-model="userSelected.aclOptions.canEditObjects" data-ng-change="markUserAsDirty()">
<ion-checkbox data-ng-hide="selectedUser.$isAnonymous()"
data-ng-checked="selectedUser.rights.canEditObjects"
data-ng-model="selectedUser.rights.canEditObjects"
data-ng-change="confirmChange(selectedUser)">
<var:string label:value="This person can edit the cards of this addressbook."/></ion-checkbox>
<ion-checkbox data-ng-show="displayUserRights()" data-ng-checked="userSelected.aclOptions.canEraseObjects"
data-ng-model="userSelected.aclOptions.canEraseObjects" data-ng-change="markUserAsDirty()">
<ion-checkbox data-ng-hide="selectedUser.$isAnonymous()"
data-ng-checked="selectedUser.rights.canEraseObjects"
data-ng-model="selectedUser.rights.canEraseObjects"
data-ng-change="confirmChange(selectedUser)">
<var:string label:value="This person can erase cards from this addressbook."/></ion-checkbox>
<ion-checkbox data-ng-checked="userSelected.aclOptions.canViewObjects"
data-ng-model="userSelected.aclOptions.canViewObjects" data-ng-change="markUserAsDirty()">
<ion-checkbox data-ng-checked="selectedUser.rights.canViewObjects"
data-ng-model="selectedUser.rights.canViewObjects"
data-ng-change="confirmChange(selectedUser)">
<var:string label:value="This person can read the cards of this addressbook."/></ion-checkbox>
</div>
</div>
</ion-list>
</ion-content>
<ion-footer-bar class="bar-footer">
<ion-search class="item item-light" label:placeholder="Add..." min-length="2" model="usersFound"
source="searchUsers(search)" clear="cancelSearch()">
<ion-search class="item item-light" label:placeholder="Add..." min-length="2"
model="usersFound"
source="searchUsers"
clear="cancelSearch()">
</ion-search>
</ion-footer-bar>
</ion-modal-view>

View File

@@ -1,51 +1,183 @@
(function() {
'use strict';
function Acl(folder_id) {
this.folder_id = folder_id;
/**
* @name Acl
* @constructor
* @param {String} folderId - the folder ID associated to the ACLs
*/
function Acl(folderId) {
this.folderId = folderId;
}
/* The factory we'll use to register with Angular */
Acl.factory = ['sgSettings', 'sgResource', function(Settings, Resource) {
/**
* @memberof Acl
* @desc The factory we'll use to register with Angular.
* @return the Acl constructor
*/
Acl.factory = ['$q', '$timeout', 'sgSettings', 'sgResource', 'sgUser', function($q, $timeout, Settings, Resource, User) {
angular.extend(Acl, {
$$resource: new Resource(Settings.baseURL)
$q: $q,
$timeout: $timeout,
$$resource: new Resource(Settings.baseURL),
$User: User
});
return Acl; // return constructor
return Acl;
}];
/* Factory registration in Angular module */
/**
* @module SOGo.Common
* @desc Factory registration of User in Angular module.
*/
angular.module('SOGo.Common').factory('sgAcl', Acl.factory);
/* Instance methods
* Public method, assigned to prototype
/**
* @function $users
* @memberof Acl.prototype
* @desc Fetch the list of users that have specific rights for the current folder.
* @return a promise of an array of User objects
*/
Acl.prototype.$userRights = function(uid) {
var param = {"uid": uid};
return Acl.$$resource.fetch(this.folder_id, "userRights", param);
};
Acl.prototype.$addUser = function(uid) {
var param = {"uid": uid};
return Acl.$$resource.fetch(this.folder_id, "addUserInAcls", param);
};
Acl.prototype.$removeUser = function(uid) {
var param = {"uid": uid};
return Acl.$$resource.fetch(this.folder_id, "removeUserFromAcls", param);
};
Acl.prototype.$saveUsersRights = function(dirtyObjects) {
var param = {"action": "saveUserRights"};
return Acl.$$resource.save(this.folder_id, dirtyObjects, param);
};
Acl.prototype.$subscribeUsers = function(uids) {
var param = {"uids": uids};
return Acl.$$resource.fetch(this.folder_id, "subscribeUsers", param);
};
Acl.prototype.$users = function() {
return Acl.$$resource.fetch(this.folder_id, "acls");
var _this = this,
deferred = Acl.$q.defer(),
user;
if (this.users) {
deferred.resolve(this.users);
}
else {
return Acl.$$resource.fetch(this.folderId, 'acls').then(function(users) {
_this.users = [];
// console.debug(JSON.stringify(users, undefined, 2));
angular.forEach(users, function(data) {
user = new Acl.$User(data);
user.canSubscribeUser = user.isSubscribed;
user.wasSubscribed = user.isSubscribed;
user.$rights = angular.bind(user, user.$acl, _this.folderId);
_this.users.push(user);
});
deferred.resolve(_this.users);
return _this.users;
});
}
return deferred.promise;
};
})();
/**
* @function $addUser
* @memberof Acl.prototype
* @param {Object} user - a User object with minimal set of attributes (uid, isGroup, cn, c_email)
* @see {@link User.$filter}
*/
Acl.prototype.$addUser = function(user) {
var _this = this,
deferred = Acl.$q.defer(),
param = {uid: user.uid};
if (!user.uid || _.indexOf(_.pluck(this.users, 'uid'), user.uid) > -1) {
// No UID specified or user already in ACLs
deferred.resolve();
}
else {
Acl.$$resource.fetch(this.folderId, 'addUserInAcls', param).then(function() {
user.wasSubscribed = false;
user.userClass = user.isGroup ? 'group-user' : 'normal-user';
user.$rights = angular.bind(user, user.$acl, _this.folderId);
_this.users.push(user);
deferred.resolve(_this.users);
}, function(data, status) {
deferred.reject(l('An error occured please try again.'));
});
}
return deferred.promise;
};
/**
* @function $removeUser
* @memberof Acl.prototype
* @desc Remove a user from the folder's ACL
* @return a promise of the server call to remove the user from the folder's ACL
*/
Acl.prototype.$removeUser = function(uid) {
var _this = this,
param = {uid: uid};
return Acl.$$resource.fetch(this.folderId, 'removeUserFromAcls', param).then(function() {
var i = _.indexOf(_.pluck(_this.users, 'uid'), uid);
if (i >= 0) {
_this.users.splice(i, 1);
}
});
};
/**
* @function $resetUsersRights
* @memberof Acl.prototype
* @desc Restore initial rights of all users.
*/
Acl.prototype.$resetUsersRights = function() {
angular.forEach(this.users, function(user) {
user.$resetRights();
});
};
/**
* @function $saveUsersRights
* @memberof Acl.prototype
* @desc Save user rights that have changed and subscribe users that have been selected.
* @return a promise that resolved only if the modifications and subscriptions were successful
*/
Acl.prototype.$saveUsersRights = function() {
var _this = this,
deferredSave = Acl.$q.defer(),
deferredSubscribe = Acl.$q.defer(),
param = {action: 'saveUserRights'},
users = [];
// Save user rights
angular.forEach(this.users, function(user) {
if (user.$rightsAreDirty()) {
users.push(user.$omit());
// console.debug('save ' + JSON.stringify(user.$omit(), undefined, 2));
}
});
if (users.length) {
Acl.$$resource.save(this.folderId, users, param)
.then(function() {
// Save was successful; copy rights to shadow rights
angular.forEach(_this.users, function(user) {
if (user.$rightsAreDirty()) {
user.$shadowRights = angular.copy(user.rights);
}
});
deferredSave.resolve();
}, deferredSave.reject);
}
else {
deferredSave.resolve();
}
// Subscribe users
users = [];
angular.forEach(this.users, function(user) {
if (!user.wasSubscribed && user.isSubscribed) {
users.push(user.uid);
// console.debug('subscribe ' + user.uid);
};
});
if (users.length) {
param = {uids: users.join(',')};
Acl.$$resource.fetch(this.folderId, 'subscribeUsers', param)
.then(function() {
// Subscribe was successful; reset "wasSubscribed" attribute
angular.forEach(_this.users, function(user) {
user.wasSubscribed = user.isSubscribed;
});
deferredSubscribe.resolve();
}, deferredSubscribe.reject);
}
else {
deferredSubscribe.resolve();
}
return Acl.$q.all([deferredSave.promise, deferredSubscribe.promise]);
};
})();

View File

@@ -3,7 +3,14 @@
(function() {
'use strict';
/* Constructor */
/**
* @name Resource
* @constructor
* @param {Object} $http - the Angular HTTP service
* @param {Object} $q - the Angular promise/deferred service
* @param {String} path - the base path of the external resource
* @param {Object} options - extra attributes to be associated to the object
*/
function Resource($http, $q, path, options) {
angular.extend(this, {
_http: $http,
@@ -13,39 +20,40 @@
angular.extend(this, options);
}
/* The factory we'll use to register with Angular */
/**
* @memberof Resource
* @desc The factory we'll use to register with Angular.
* @return a new Resource object
*/
Resource.$factory = ['$http', '$q', function($http, $q) {
return function(path, options) {
return new Resource($http, $q, path, options);
};
}];
/* Factory registration in Angular module */
/**
* @module SOGo.Common
* @desc Factory registration of Resource in Angular module.
*/
angular.module('SOGo.Common').factory('sgResource', Resource.$factory);
/* Instance methods */
Resource.prototype.path = function(uid) {
return (uid ? this._path + '/' + uid : this._path) + '/view';
};
Resource.prototype.find = function(uid) {
var deferred = this._q.defer();
this._http
.get(this.path(uid))
.success(deferred.resolve)
.error(deferred.reject);
return deferred.promise;
};
Resource.prototype.filter = function(uid, params) {
var deferred = this._q.defer();
/**
* @function fetch
* @memberof Resource.prototype
* @desc Fetch resource using a specific folder, action and/or parameters.
* @param {string} folderId - the folder on which the action will be applied (ex: addressbook, calendar)
* @param {string} action - the action to be used in the URL
* @param {Object} params - Object parameters injected through the $http service
* @return a promise
*/
Resource.prototype.fetch = function(folderId, action, params) {
var deferred = this._q.defer(),
path = [this._path, (folderId ? folderId : ''), (action ? action : '')];
path = _.compact(path).join('/');
this._http({
method: 'GET',
url: this.path(uid),
url: path,
params: params
})
.success(deferred.resolve)
@@ -54,9 +62,15 @@
return deferred.promise;
};
Resource.prototype.newguid = function(uid) {
/**
* @function newguid
* @memberof Resource.prototype
* @desc Fetch a new GUID on the specified folder ID.
* @return a promise of the new data structure
*/
Resource.prototype.newguid = function(folderId) {
var deferred = this._q.defer(),
path = this._path + '/' + uid + '/newguid';
path = this._path + '/' + folderId + '/newguid';
this._http
.get(path)
@@ -68,9 +82,11 @@
/**
* @function create
* @desc Create a new resource using a specific action
* @memberof Resource.prototype
* @desc Create a new resource using a specific action (post).
* @param {string} action - the action to be used in the URL
* @param {string} name - the new resource's name
* @return a promise
*/
Resource.prototype.create = function(action, name) {
var deferred = this._q.defer(),
@@ -84,6 +100,12 @@
return deferred.promise;
};
/**
* @function save
* @memberof Resource.prototype
* @desc Save a resource attributes on the server (post /save).
* @return a promise
*/
Resource.prototype.save = function(id, newValue, options) {
var deferred = this._q.defer(),
action = (options && options.action)? options.action : 'save',
@@ -97,6 +119,12 @@
return deferred.promise;
};
/**
* @function remove
* @memberof Resource.prototype
* @desc Delete a resource (get /delete).
* @return a promise
*/
Resource.prototype.remove = function(uid) {
var deferred = this._q.defer(),
path = this._path + '/' + uid + '/delete';
@@ -109,26 +137,4 @@
return deferred.promise;
};
/**
* @function fetch
* @desc Fetch resources using a specific folder, action and/or parameters
* @param {string} folder_id - the folder on which the action will be applied (ex: addressbook, calendar)
* @param {string} action - the action to be used in the URL
* @param {Object} params - Object parameters injected through the $http protocol
*/
Resource.prototype.fetch = function(folder_id, action, params) {
var deferred = this._q.defer(),
path = [this._path, (folder_id ? folder_id : ""), (action ? action : "")];
path = _.compact(path).join("/");
this._http({
method: 'GET',
url: path,
params: params
})
.success(deferred.resolve)
.error(deferred.reject);
return deferred.promise;
};
})();

View File

@@ -1,26 +1,188 @@
(function() {
'use strict';
function User() {}
/**
* @name User
* @constructor
* @param {object} [userData] - some default values for the user
*/
function User(userData) {
if (userData)
angular.extend(this, userData);
}
/* The factory we'll use to register with Angular */
User.factory = ['sgSettings', 'sgResource', function(Settings, Resource) {
/**
* @memberof User
* @desc The factory we'll use to register with Angular.
* @return the User constructor
*/
User.factory = ['$q', 'sgSettings', 'sgResource', function($q, Settings, Resource) {
angular.extend(User, {
$q: $q,
$$resource: new Resource(Settings.baseURL)
});
return User; // return constructor
return User;
}];
/* Factory registration in Angular module */
angular.module('SOGo.Common').factory('User', User.factory);
/* Instance methods
* Public method, assigned to prototype
/**
* @module SOGo.Common
* @desc Factory registration of User in Angular module.
*/
User.prototype.$filter = function(search) {
// return a collections of users for a filter
angular.module('SOGo.Common').factory('sgUser', User.factory);
/**
* @memberof User
* @desc Search for users that match a string.
* @param {String} search - a string used to performed the search
* @return a promise of an array of matching User objects
*/
User.$filter = function(search) {
var param = {search: search};
return User.$$resource.fetch(null, "usersSearch", param);
return User.$$resource.fetch(null, 'usersSearch', param).then(function(users) {
var results = [];
angular.forEach(users, function(data) {
console.debug(JSON.stringify(data, undefined, 2));
var user = new User(data);
results.push(user);
});
return results;
});
};
/**
* @function $shortFormat
* @memberof User.prototype
* @return the fullname along with the email address
*/
User.prototype.$shortFormat = function() {
var fullname = this.cn || this.c_email;
if (this.c_email && fullname != this.c_email) {
fullname += ' (' + this.c_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
* @return a promise
*/
User.prototype.$acl = function(folderId) {
var _this = this,
deferred = User.$q.defer(),
param = {uid: this.uid};
if (this.$shadowRights) {
deferred.resolve(this.rights);
}
else {
User.$$resource.fetch(folderId, 'userRights', param).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;
};
/**
* @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
* @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() {
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') {
confirmation = l('Potentially anyone on the Internet will be able to access your folder, even if they do not have an account on this system. Is this information suitable for the public Internet?');
}
else if (this.uid == '<default>') {
confirmation = l('Any user with an account on this system will be able to access your folder. Are you certain you trust them all?');
}
}
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) {
_this.rights[key] = 0;
});
}
else {
// Restore initial rights
this.rights = angular.copy(this.$shadowRights);
}
};
/**
* @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;
};
})();

View File

@@ -17,7 +17,7 @@
var newAddressBookData = AddressBook.$$resource.create('createFolder', this.name);
this.$unwrap(newAddressBookData);
}
else if (this.id){
else if (this.id) {
this.$acl = new AddressBook.$$Acl(this.id);
}
}
@@ -55,10 +55,12 @@
*/
AddressBook.$add = function(addressbook) {
// Insert new addressbook at proper index
var sibling = _.find(this.$addressbooks, function(o) {
var sibling, i;
sibling = _.find(this.$addressbooks, function(o) {
return (o.isRemote || (o.id != 'personal' && o.name.localeCompare(addressbook.name) === 1));
});
var i = sibling? _.indexOf(_.pluck(this.$addressbooks, 'id'), sibling.id) : 1;
i = sibling ? _.indexOf(_.pluck(this.$addressbooks, 'id'), sibling.id) : 1;
this.$addressbooks.splice(i, 0, addressbook);
};
@@ -83,11 +85,11 @@
/**
* @memberOf AddressBook
* @desc Fetch list of cards and return an AddressBook instance
* @param {string} addressbook_id - the addressbook identifier
* @param {string} addressbookId - the addressbook identifier
* @returns an AddressBook object instance
*/
AddressBook.$find = function(addressbook_id) {
var futureAddressBookData = AddressBook.$$resource.find(addressbook_id);
AddressBook.$find = function(addressbookId) {
var futureAddressBookData = AddressBook.$$resource.fetch(addressbookId, 'view');
return new AddressBook(futureAddressBookData);
};
@@ -109,17 +111,19 @@
* @returns a collection of Cards instances
*/
AddressBook.prototype.$filter = function(search, options) {
var _this = this;
var params = { 'search': 'name_or_address',
'value': search,
'sort': 'c_cn',
'asc': 'true' };
var _this = this,
params = {
search: 'name_or_address',
value: search,
sort: 'c_cn',
asc: 'true'
};
if (options && options.excludeLists) {
params.excludeLists = true;
}
return this.$id().then(function(addressbook_id) {
var futureAddressBookData = AddressBook.$$resource.filter(addressbook_id, params);
return this.$id().then(function(addressbookId) {
var futureAddressBookData = AddressBook.$$resource.fetch(addressbookId, 'view', params);
return futureAddressBookData.then(function(data) {
var cards;
if (options && options.dry) {
@@ -157,8 +161,8 @@
};
AddressBook.prototype.$delete = function() {
var _this = this;
var d = AddressBook.$q.defer();
var _this = this,
d = AddressBook.$q.defer();
AddressBook.$$resource.remove(this.id)
.then(function() {
var i = _.indexOf(_.pluck(AddressBook.$addressbooks, 'id'), _this.id);
@@ -171,14 +175,14 @@
};
AddressBook.prototype.$save = function() {
return AddressBook.$$resource.save(this.id, this.$omit()).then(function (data) {
return AddressBook.$$resource.save(this.id, this.$omit()).then(function(data) {
return data;
});
};
AddressBook.prototype.$getCard = function(card_id) {
return this.$id().then(function(addressbook_id) {
return AddressBook.$Card.$find(addressbook_id, card_id);
AddressBook.prototype.$getCard = function(cardId) {
return this.$id().then(function(addressbookId) {
return AddressBook.$Card.$find(addressbookId, cardId);
});
};

View File

@@ -89,7 +89,7 @@
* @see {@link AddressBook.$getCard}
*/
Card.$find = function(addressbook_id, card_id) {
var futureCardData = this.$$resource.find([addressbook_id, card_id].join('/'));
var futureCardData = this.$$resource.fetch([addressbook_id, card_id].join('/'), 'view');
if (card_id) return new Card(futureCardData); // a single card
@@ -340,7 +340,8 @@
};
Card.prototype.$addMember = function(email) {
var card = new Card({email: email, emails: [{value: email}]});
var card = new Card({email: email, emails: [{value: email}]}),
i;
if (angular.isUndefined(this.refs)) {
this.refs = [card];
}
@@ -348,7 +349,7 @@
this.refs.push(card);
}
else {
for (var i = 0; i < this.refs.length; i++) {
for (i = 0; i < this.refs.length; i++) {
if (this.refs[i].email == email) {
break;
}
@@ -393,10 +394,12 @@
* @param {Card} card
*/
Card.prototype.$updateMember = function(index, email, card) {
var ref = {email: email,
emails: [{value: email}],
reference: card.c_name,
fn: card.$fullname()};
var ref = {
email: email,
emails: [{value: email}],
reference: card.c_name,
fn: card.$fullname()
};
this.refs[index] = new Card(ref);
};

View File

@@ -52,7 +52,7 @@
abstract: true,
views: {
card: {
template: '<ui-view/>',
template: '<ui-view/>'
}
},
resolve: {
@@ -98,7 +98,7 @@
.controller('AddressBookCtrl', ['$state', '$scope', '$rootScope', '$stateParams', '$timeout', '$modal', 'sgFocus', 'sgCard', 'sgAddressBook', 'sgDialog', 'stateAddressbooks', 'stateAddressbook', function($state, $scope, $rootScope, $stateParams, $timeout, $modal, focus, Card, AddressBook, Dialog, stateAddressbooks, stateAddressbook) {
var currentAddressbook;
// Resolve objects
// Resolved objects
$scope.addressbooks = stateAddressbooks;
$rootScope.addressbook = stateAddressbook;
@@ -177,158 +177,62 @@
};
$scope.exportCards = function() {
window.location.href = ApplicationBaseURL + "/" + $rootScope.addressbook.id + "/exportFolder";
window.location.href = ApplicationBaseURL + '/' + $rootScope.addressbook.id + '/exportFolder';
};
$scope.share = function() {
var modal = $modal.open({
templateUrl: 'addressbookSharing.html',
controller: function($scope, $modalInstance, User) {
/* Variables for the scope */
var dirtyObjects = {};
stateAddressbook.$acl.$users().then(function(users) {
$scope.users = [];
angular.forEach(users, function(user){
user.canSubscribeUser = user.isSubscribed;
$scope.users.push(user);
})
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occurs while trying to fetch users from the server.'));
});
$scope.User = new User();
/* Functions */
resolve: {
modalUsers: function() {
return stateAddressbook.$acl.$users();
}
},
controller: ['$scope', '$modalInstance', 'sgUser', 'modalUsers', function($scope, $modalInstance, User, modalUsers) {
$scope.users = modalUsers; // ACL users
$scope.userFilter = User.$filter; // Filter for typeahead
$scope.closeModal = function() {
stateAddressbook.$acl.$resetUsersRights(); // cancel changes
$modalInstance.close();
};
$scope.saveModal = function() {
if(!_.isEmpty(dirtyObjects)) {
if(dirtyObjects["anonymous"]) {
if($scope.validateChanges(dirtyObjects["anonymous"])) {
Dialog.confirm(l("Warning"), l("Potentially anyone on the Internet will be able to access your folder, even if they do not have an account on this system. Is this information suitable for the public Internet?")).then(function(res){
if(res){
stateAddressbook.$acl.$saveUsersRights(dirtyObjects).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$modalInstance.close();
}
})
}
else{
stateAddressbook.$acl.$saveUsersRights(dirtyObjects).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$modalInstance.close();
}
}
else if (dirtyObjects["<default>"]) {
if($scope.validateChanges(dirtyObjects["<default>"])) {
Dialog.confirm(l("Warning"), l("Any user with an account on this system will be able to access your folder. Are you certain you trust them all?")).then(function(res){
if(res){
stateAddressbook.$acl.$saveUsersRights(dirtyObjects).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$modalInstance.close();
};
})
}
else{
stateAddressbook.$acl.$saveUsersRights(dirtyObjects).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$modalInstance.close();
}
}
else {
stateAddressbook.$acl.$saveUsersRights(dirtyObjects).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
var usersToSubscribe = [];
angular.forEach(dirtyObjects, function(dirtyObject){
if(dirtyObject.canSubscribeUser && dirtyObject.isSubscribed){
usersToSubscribe.push(dirtyObject.uid);
}
})
if(!_.isEmpty(usersToSubscribe))
stateAddressbook.$acl.$subscribeUsers(usersToSubscribe).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$modalInstance.close();
}
}
else
stateAddressbook.$acl.$saveUsersRights().then(function() {
$modalInstance.close();
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
});
};
$scope.validateChanges = function(object) {
if (object.aclOptions.canViewObjects || object.aclOptions.canCreateObjects || object.aclOptions.canEditObjects || object.aclOptions.canEraseObjects)
return true;
else
return false;
};
$scope.removeUser = function() {
if (!_.isEmpty($scope.userSelected)) {
if(dirtyObjects[$scope.userSelected.uid])
delete dirtyObjects[$scope.userSelected.uid];
stateAddressbook.$acl.$removeUser($scope.userSelected.uid).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
$scope.confirmChange = function(user) {
var confirmation = user.$confirmRights();
if (confirmation) {
Dialog.confirm(l('Warning'), confirmation).then(function(res) {
if (!res)
user.$resetRights(true);
});
// Remove from the users list
$scope.users = _.reject($scope.users, function(o) {
return o.uid == $scope.userSelected.uid;
});
$scope.userSelected = {};
}
};
$scope.addUser = function(user) {
if (user.uid) {
// Looks through the list and returns the first value that matches all of the key-value pairs listed
if(!_.findWhere($scope.users, {uid: user.uid})) {
stateAddressbook.$acl.$addUser(user.uid).then(function() {
var displayName = user.cn + " <" + user.c_email + ">";
var userClass = user.isGroup ? "group-user" : "normal-user";
var newUser = {canSubscribeUser: 0, displayName: displayName, isSubscribed: 0, uid: user.uid, userClass: userClass};
$scope.users.push(newUser);
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$scope.removeUser = function(user) {
stateAddressbook.$acl.$removeUser(user.uid).then(function() {
if (user.uid == $scope.selectedUser.uid) {
delete $scope.selectedUser;
}
else
Dialog.alert(l('Warning'), l('This user is already in your permissions list.'));
}
else
Dialog.alert(l('Warning'), l('Please select a user inside your domain'));
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
};
$scope.addUser = function(data) {
$scope.userToAdd = '';
stateAddressbook.$acl.$addUser(data).catch(function(error) {
Dialog.alert(l('Warning'), error);
});
};
$scope.selectUser = function(user) {
// Check if it is a different user
if ($scope.userSelected != user){
$scope.userSelected = {};
$scope.selected = user;
$scope.userSelected = user;
if (dirtyObjects[$scope.userSelected.uid]) {
$scope.userSelected.aclOptions = dirtyObjects[$scope.userSelected.uid].aclOptions;
}
else {
stateAddressbook.$acl.$userRights($scope.userSelected.uid).then(function(userRights) {
$scope.userSelected.aclOptions = userRights;
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
}
if ($scope.selectedUser != user) {
$scope.selectedUser = user;
$scope.selectedUser.$rights();
}
};
$scope.markUserAsDirty = function(user) {
if(!$scope.userSelected)
$scope.selectUser(user);
dirtyObjects[$scope.userSelected.uid] = $scope.userSelected;
};
$scope.displayUserRights = function() {
return ($scope.userSelected && ($scope.userSelected.uid != "anonymous")) ? true : false;
};
$scope.userIsReadOnly = function() {
return (!$scope.userSelected || $scope.userSelected.userClass == "public-user");
};
}
}]
});
};
$scope.doSearch = function(keyEvent) {

View File

@@ -107,12 +107,13 @@
$urlRouterProvider.otherwise('/app/addressbooks');
})
// Inspired from http://codepen.io/gastonbesada/pen/eqvJK
.directive('ionSearch', function() {
return {
restrict: 'E',
replace: true,
scope: {
getData: '&source',
getData: '=source',
clearData: '&clear',
model: '=?',
search: '=?filter'
@@ -126,9 +127,9 @@
element.addClass(attrs.class);
if (attrs.source) {
scope.$watch('search.value', function (newValue, oldValue) {
scope.$watch('search.value', function(newValue, oldValue) {
if (newValue.length > attrs.minLength) {
scope.getData({search: newValue}).then(function (results) {
scope.getData(newValue).then(function(results) {
scope.model = results;
});
}
@@ -140,10 +141,10 @@
};
},
template: '<div class="item-input-wrapper">' +
'<i class="icon ion-android-search"></i>' +
'<input type="search" placeholder="{{placeholder}}" ng-model="search.value" id="searchInput">' +
'<i ng-if="search.value.length > 0" ng-click="clearSearch()" class="icon ion-close"></i>' +
'</div>'
'<i class="icon ion-android-search"></i>' +
'<input type="search" placeholder="{{placeholder}}" ng-model="search.value" id="searchInput">' +
'<i ng-if="search.value.length > 0" ng-click="clearSearch()" class="icon ion-close"></i>' +
'</div>'
};
})
@@ -159,7 +160,7 @@
// };
}])
.controller('AddressBooksCtrl', ['$scope', '$state', '$rootScope', '$ionicModal', '$ionicListDelegate', '$ionicActionSheet', 'sgDialog', 'sgAddressBook', 'User', function($scope, $state, $rootScope, $ionicModal, $ionicListDelegate, $ionicActionSheet, Dialog, AddressBook, User) {
.controller('AddressBooksCtrl', ['$scope', '$state', '$rootScope', '$ionicModal', '$ionicListDelegate', '$ionicActionSheet', 'sgDialog', 'sgAddressBook', 'sgUser', function($scope, $state, $rootScope, $ionicModal, $ionicListDelegate, $ionicActionSheet, Dialog, AddressBook, User) {
// Initialize with data from template
$scope.addressbooks = AddressBook.$findAll(contactFolders);
$scope.newAddressbook = function() {
@@ -182,126 +183,91 @@
$ionicActionSheet.show({
buttons: [
{ text: l('Rename') },
{ text: l('Access rights') }
{ text: l('Set Access Rights') }
],
destructiveText: l('Delete'),
cancelText: l('Cancel'),
buttonClicked: function(index) {
if(index == 0) {
// Rename addressbook
Dialog.prompt(l('Rename addressbook'),
addressbook.name)
.then(function(name) {
if (name && name.length > 0) {
addressbook.$rename(name);
}
});
if (index == 0) {
// Rename
Dialog.prompt(l('Rename addressbook'), addressbook.name)
.then(function(name) {
if (name && name.length > 0) {
addressbook.$rename(name); // TODO check error
}
});
}
else if(index == 1) {
// Build modal editor
else if (index == 1) {
// Set Access Rights
$ionicModal.fromTemplateUrl('acl-modal.html', { scope: $scope }).then(function(modal) {
if ($scope.$aclEditorModal) {
$scope.$aclEditorModal.remove();
}
// Variables in scope
$scope.$aclEditorModal = modal;
$scope.User = new User();
var aclUsers = {};
function refreshUsers(users) {
$scope.users = [];
$scope.search.active = false;
angular.forEach(users, function(user) {
user.inAclList = true;
$scope.users.push(user);
aclUsers[user.uid] = user;
})
};
if ($scope.aclEditorModal) {
$scope.aclEditorModal.remove();
}
$scope.title = addressbook.name;
$scope.aclEditorModal = modal;
$scope.showDelete = false;
$scope.search = {active: false};
// Load ACL users
addressbook.$acl.$users().then(function(users) {
refreshUsers(users);
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occurs while trying to fetch users from the server.'));
});
$scope.showDelete = false;
$scope.onGoingSearch = false;
// Variables in javascript
var dirtyObjects = {};
// Local functions
function refreshUsers(users) {
$scope.users = [];
$scope.onGoingSearch = false;
angular.forEach(users, function(user){
user.inAclList = true;
user.canSubscribeUser = (user.isSubscribed) ? false : true;
$scope.users.push(user);
aclUsers[user.uid] = user;
})
};
// Function in scope
$scope.closeModal = function() {
$scope.$aclEditorModal.remove();
$scope.aclEditorModal.remove();
};
$scope.saveModal = function() {
if(!_.isEmpty(dirtyObjects)) {
if(dirtyObjects["anonymous"])
{
if($scope.validateChanges(dirtyObjects["anonymous"])) {
Dialog.confirm(l("Warning"), l("Potentially anyone on the Internet will be able to access your folder, even if they do not have an account on this system. Is this information suitable for the public Internet?")).then(function(res){
if(res){
addressbook.$acl.$saveUsersRights(dirtyObjects).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$scope.$aclEditorModal.remove();
};
})
}
else {
addressbook.$acl.$saveUsersRights(dirtyObjects).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$scope.$aclEditorModal.remove();
}
}
else if (dirtyObjects["<default>"]) {
if($scope.validateChanges(dirtyObjects["<default>"])) {
Dialog.confirm(l("Warning"), l("Any user with an account on this system will be able to access your folder. Are you certain you trust them all?")).then(function(res){
if(res){
addressbook.$acl.$saveUsersRights(dirtyObjects).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$scope.$aclEditorModal.remove();
};
})
}
else {
addressbook.$acl.$saveUsersRights(dirtyObjects).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$scope.$aclEditorModal.remove();
}
$scope.displayIcon = function(user) {
if (user.inAclList)
return ($scope.userIsSelected(user) ? 'ion-ios7-arrow-down' : 'ion-ios7-arrow-right');
else
return 'ion-plus';
}
$scope.toggleUser = function(user) {
if (user.inAclList) {
if ($scope.userIsSelected(user)) {
// Close rights editor
delete $scope.selectedUser;
}
else {
addressbook.$acl.$saveUsersRights(dirtyObjects).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
var usersToSubscribe = [];
angular.forEach(dirtyObjects, function(dirtyObject){
if(dirtyObject.canSubscribeUser && dirtyObject.isSubscribed){
usersToSubscribe.push(dirtyObject.uid);
}
})
if(!_.isEmpty(usersToSubscribe))
addressbook.$acl.$subscribeUsers(usersToSubscribe).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
$scope.$aclEditorModal.remove();
// Edit user rights
$scope.editUser(user);
}
}
else
$scope.$aclEditorModal.remove();
else {
// Add user
$scope.addUser(user);
}
};
$scope.validateChanges = function(object) {
if (object.aclOptions.canViewObjects || object.aclOptions.canCreateObjects || object.aclOptions.canEditObjects || object.aclOptions.canEraseObjects)
return true;
else
return false;
$scope.userIsSelected = function(user) {
return $scope.selectedUser && $scope.selectedUser.uid == user.uid;
};
$scope.searchUsers = function(search) {
$scope.users = [];
$scope.search.active = true;
return User.$filter(search).then(function(results) {
angular.forEach(results, function(userFound) {
userFound.inAclList = (aclUsers[userFound.uid]) ? true : false;
$scope.users.push(userFound);
})
});
};
$scope.cancelSearch = function() {
addressbook.$acl.$users().then(function(users) {
addressbook.$acl.$users().then(function(users) {
refreshUsers(users);
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
@@ -310,104 +276,57 @@
$scope.toggleDelete = function(boolean) {
$scope.showDelete = boolean;
};
$scope.removeUser = function(user) {
if (user) {
if(dirtyObjects[user.uid])
delete dirtyObjects[user.uid];
delete aclUsers[user.uid];
addressbook.$acl.$removeUser(user.uid).then(null, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
// Remove from the users list
$scope.users = _.reject($scope.users, function(o) {
return o.uid == user.uid;
});
$scope.userSelected = {};
}
};
$scope.addUser = function (user) {
if (user.uid) {
if(!aclUsers[user.uid]) {
addressbook.$acl.$addUser(user.uid).then(function() {
user.inAclList = true;
user.canSubscribeUser = (user.isSubscribed) ? false : true;
aclUsers[user.uid] = user;
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
}
else
Dialog.alert(l('Warning'), l('This user is already in your permissions list.'));
}
else
Dialog.alert(l('Warning'), l('Please select a user inside your domain'));
};
$scope.editUser = function(user) {
if ($scope.userSelected != user){
$scope.userSelected = user;
if (dirtyObjects[$scope.userSelected.uid]) {
// If the user already made changes on the user rights, it is saved inside an object called dirty.
// We preverse these changes untill the user decide to save or discard them.
$scope.userSelected.aclOptions = dirtyObjects[$scope.userSelected.uid].aclOptions;
}
else {
// Otherwise, if it's the first time the user consult the user rights; fetch from server
addressbook.$acl.$userRights($scope.userSelected.uid).then(function(userRights) {
$scope.userSelected.aclOptions = userRights;
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
}
if ($scope.selectedUser != user) {
$scope.selectedUser = user;
$scope.selectedUser.$rights();
}
};
$scope.searchUsers = function(search){
$scope.users = [];
$scope.onGoingSearch = true;
return $scope.User.$filter(search).then(function(results) {
angular.forEach(results, function(userFound){
userFound.inAclList = (aclUsers[userFound.uid]) ? true : false;
userFound["displayName"] = userFound.cn + " <" + userFound.c_email + ">";
$scope.users.push(userFound);
})
$scope.confirmChange = function(user) {
var confirmation = user.$confirmRights();
if (confirmation) {
Dialog.confirm(l('Warning'), confirmation).then(function(res) {
if (!res)
user.$resetRights(true);
});
}
};
$scope.saveModal = function() {
addressbook.$acl.$saveUsersRights().then(function() {
$scope.aclEditorModal.remove();
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'));
});
};
$scope.toggleUser = function(user) {
if (user.inAclList) {
if ($scope.isUserShown(user)) {
$scope.shownUser = null;
}
else {
$scope.shownUser = user;
$scope.editUser(user);
}
$scope.removeUser = function(user) {
if (user) {
addressbook.$acl.$removeUser(user.uid).then(function() {
delete aclUsers[user.uid];
delete $scope.selectedUser;
$scope.users = _.reject($scope.users, function(o) {
return o.uid == user.uid;
});
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
}
};
$scope.addUser = function(user) {
if (user.uid) {
addressbook.$acl.$addUser(user).then(function() {
user.inAclList = true;
aclUsers[user.uid] = user;
}, function(data, status) {
Dialog.alert(l('Warning'), l('An error occured please try again.'))
});
}
else {
$scope.addUser(user);
}
Dialog.alert(l('Warning'), l('Please select a user inside your domain'));
}
};
$scope.isUserShown = function(user) {
return $scope.shownUser === user;
};
$scope.markUserAsDirty = function() {
dirtyObjects[$scope.userSelected.uid] = $scope.userSelected;
};
$scope.displayUserRights = function() {
// Does the rights applies on the user/group
return ($scope.userSelected && ($scope.userSelected.uid != "anonymous")) ? true : false;
};
$scope.displaySubscribeUser = function() {
// Is the user/group available for subscription
return ($scope.userSelected && !($scope.userSelected.uid == "anonymous" || $scope.userSelected.uid == "<default>")) ? true : false;
};
$scope.displayIcon = function(user) {
if (user.inAclList)
return ($scope.isUserShown(user) ? 'ion-ios7-arrow-down' : 'ion-ios7-arrow-right');
else
return 'ion-plus';
}
// Show modal
$scope.$aclEditorModal.show();
$scope.aclEditorModal.show();
});
}
return true;
@@ -440,15 +359,15 @@
$ionicActionSheet.show({
//titleText: l('Create a new card or a new list'),
buttons: [
{ text: l('New Card')},
{ text: l('New List')}
{ text: l('New Card') },
{ text: l('New List') }
],
canceltext: l('Cancel'),
buttonClicked: function(index) {
if (index == 0){
if (index == 0) {
$state.go('app.newCard', { addressbookId: stateAddressbook.id, contactType: 'card' });
}
else if (index == 1){
else if (index == 1) {
$state.go('app.newCard', { addressbookId: stateAddressbook.id, contactType: 'list' });
}
return true;
@@ -488,28 +407,32 @@
$scope.allUrlTypes = Card.$URL_TYPES;
$scope.allAddressTypes = Card.$ADDRESS_TYPES;
$scope.search = {query: ""};
$scope.search = {query: ''};
$scope.cardsFilter = function(item) {
var query, id = false;
if (item.tag == "vcard" && $scope.search.query) {
if (item.tag == 'vcard' && $scope.search.query) {
query = $scope.search.query.toLowerCase();
if (item.emails && item.emails.length > 0) {
// Is one of the email addresses match the query string?
if (_.find(item.emails, function(email) {
return (email.value.toLowerCase().indexOf(query) >= 0);
}))
})) {
id = item.id;
}
}
if (!id && item.fn)
if (!id && item.fn) {
// Is the fn attribute matches the query string?
if (item.fn.toLowerCase().indexOf(query) >= 0)
if (item.fn.toLowerCase().indexOf(query) >= 0) {
id = item.id;
}
}
if (id) {
// Is the card already part of the members? If so, ignore it.
if (_.find($scope.card.refs, function(ref) {
return ref.reference == id;
}))
})) {
id = false;
}
}
}
return id;
@@ -524,7 +447,7 @@
$scope.popover.hide();
};
$ionicPopover.fromTemplateUrl('searchFolderContacts.html', {
scope: $scope,
scope: $scope
}).then(function(popover) {
$scope.popover = popover;
});
@@ -547,9 +470,9 @@
$scope.cancel = function() {
if ($scope.card.isNew) {
$scope.$cardEditorModal.hide().then(function() {
// Go back to addressbook
$state.go('app.addressbook', { addressbookId: $rootScope.addressbook.id });
});
// Go back to addressbook
$state.go('app.addressbook', { addressbookId: $rootScope.addressbook.id });
});
}
else {
$scope.card.$reset();

View File

@@ -365,74 +365,67 @@ $column-gutter: 0;
}
#modalACL {
height: 25vw;
ul {
height: 60vh;
>ul {
@include block-grid(2);
height: 85%;
}
#usersList {
height: 100%;
padding: 0;
ul.aclUsers {
@include block-grid(
$per-row: 1,
$spacing: $block-grid-default-spacing,
$base-style: false
);
height: 85%;
border-top: 1px solid black;
border-bottom: 1px solid black;
border-left: 1px solid black;
padding: 0;
ul {
@include block-grid(
$per-row: 1,
$spacing: $block-grid-default-spacing,
$base-style: false
);
height: 85%;
border-top: 1px solid black;
border-bottom: 1px solid black;
border-left: 1px solid black;
margin: 0;
li {
padding: 0;
padding-top: 5px;
line-height: 45px;
background-color: $f-dropdown-list-hover-bg;
transition: background 300ms ease;
&:hover, &:active {
background-color: $f-dropdown-list-hover-bg;
background-color: #fff;
}
&._selected, &._selected span {
background-color: $module-color;
background-color: #fff;
}
.subscriptionArea {
float: right;
padding-right: 5px;
}
}
}
#bottomTable {
width: 100%;
margin: 0;
border: none;
#td_1 {
padding: 0;
width:100%;
i {
position: absolute;
padding-top: 10px;
padding-left: 8px;
}
input {
padding-left: 20px;
margin: 0;
}
}
#td_2 {
padding: 0;
margin: 0;
li {
padding: 0;
padding-top: 5px;
line-height: 45px;
background-color: $f-dropdown-list-hover-bg;
transition: background 300ms ease;
&:hover, &:active {
background-color: $f-dropdown-list-hover-bg;
background-color: #fff;
}
&._selected, &._selected span {
background-color: $module-color;
background-color: #fff;
}
.subscriptionArea {
float: right;
padding-right: 5px;
}
}
}
.f-dropdown {
height: auto;
}
#bottomTable {
width: 100%;
margin: 0;
border: none;
#td_1 {
padding: 0;
width:100%;
i {
position: absolute;
padding-top: 10px;
padding-left: 8px;
}
input {
padding-left: 20px;
margin: 0;
}
}
#td_2 {
padding: 0;
float: right;
white-space: nowrap;
button {
margin: 0;
float: right;
white-space: nowrap;
button {
margin: 0;
}
}
}
}
@@ -573,8 +566,10 @@ $column-gutter: 0;
}
.f-dropdown {
width: auto;
max-width: 300px;
white-space: nowrap;
&.icons-dropdown {
width: auto;
height: inherit;
//background-color: $primary-color;
.button {

View File

@@ -85,3 +85,4 @@ ion-item {
.popover {
}