GUI for mobile version of AclUsers
Conflicts: UI/Common/UIxAclEditor.m UI/Common/UIxUserRightsEditor.m UI/WebServerResources/js/Common/acl-model.jspull/91/head
parent
e4b1c0d5b9
commit
f3c26671af
|
@ -31,7 +31,7 @@
|
|||
BOOL publishInFreeBusy;
|
||||
NSArray *aclUsers;
|
||||
NSArray *savedUIDs;
|
||||
NSMutableArray *users;
|
||||
NSMutableDictionary *users;
|
||||
NSString *currentUser;
|
||||
NSString *defaultUserID;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
aclUsers = nil;
|
||||
prepared = NO;
|
||||
publishInFreeBusy = NO;
|
||||
users = [NSMutableArray new];
|
||||
users = [NSMutableDictionary new];
|
||||
currentUser = nil;
|
||||
defaultUserID = nil;
|
||||
savedUIDs = nil;
|
||||
|
@ -96,9 +96,23 @@
|
|||
{
|
||||
if (!([currentUID isEqualToString: ownerLogin]
|
||||
|| [currentUID isEqualToString: defaultUserID]
|
||||
|| [currentUID isEqualToString: @"anonymous"]))
|
||||
[users addObjectUniquely: currentUID];
|
||||
|| [currentUID isEqualToString: @"anonymous"]))
|
||||
{
|
||||
// Set the current user in order to get information associated with it
|
||||
[self setCurrentUser: currentUID];
|
||||
|
||||
// Build the object associated with the key; currentUID
|
||||
object = [NSDictionary dictionaryWithObjectsAndKeys: currentUser, @"uid",
|
||||
[self currentUserClass], @"userClass",
|
||||
[self currentUserDisplayName], @"displayName",
|
||||
[NSNumber numberWithBool:[self currentUserIsSubscribed]], @"isSubscribed", nil];
|
||||
[users setObject:object forKey: currentUID];
|
||||
}
|
||||
}
|
||||
// Adding the Any authenticated user and the public access
|
||||
[users setObject:[NSDictionary dictionaryWithObjectsAndKeys: @"<default>", @"uid", [self labelForKey: @"Any Authenticated User"], @"displayName", @"public-user", @"userClass", nil] forKey: @"<default>"];
|
||||
if ([self isPublicAccessEnabled])
|
||||
[users setObject:[NSDictionary dictionaryWithObjectsAndKeys: @"anonymous", @"uid", [self labelForKey: @"Public Access"], @"displayName", @"public-user", @"userClass", nil] forKey: @"anonymous"];
|
||||
prepared = YES;
|
||||
}
|
||||
|
||||
|
|
|
@ -187,11 +187,25 @@
|
|||
{
|
||||
id <WOActionResults> response;
|
||||
SOGoDomainDefaults *dd;
|
||||
NSDictionary *jsonObject, *currentObject;
|
||||
NSEnumerator *enumerator;
|
||||
NSArray *o;
|
||||
id key;
|
||||
|
||||
if (![self _initRights])
|
||||
response = [NSException exceptionWithHTTPStatus: 403
|
||||
reason: @"No such user."];
|
||||
else
|
||||
value = [[self context] request];
|
||||
jsonObject = [[value contentAsString] objectFromJSONString];
|
||||
enumerator = [jsonObject keyEnumerator];
|
||||
|
||||
while((key = [enumerator nextObject]))
|
||||
{
|
||||
currentObject = [jsonObject objectForKey: key];
|
||||
if(![self _initRightsWithParameter: [currentObject objectForKey: @"uid"]])
|
||||
{
|
||||
response = [self responseWithStatus: 403
|
||||
andString: @"No such user."];
|
||||
return response;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSArray *o;
|
||||
|
||||
|
|
|
@ -440,7 +440,8 @@
|
|||
NSString *uid;
|
||||
NSDictionary *contact;
|
||||
NSString *contactInfo, *login;
|
||||
NSMutableArray *jsonResponse, *jsonLine;
|
||||
NSMutableArray *jsonResponse;
|
||||
NSMutableDictionary *jsonLine;
|
||||
NSArray *allUsers;
|
||||
int count, max;
|
||||
BOOL activeUserIsInDomain;
|
||||
|
@ -462,16 +463,15 @@
|
|||
// We do NOT return the current authenticated user
|
||||
if (!activeUserIsInDomain || ![uid isEqualToString: login])
|
||||
{
|
||||
jsonLine = [NSMutableArray arrayWithCapacity: 4];
|
||||
if ([domain length] && [uid rangeOfString: @"@"].location == NSNotFound)
|
||||
jsonLine = [NSMutableDictionary dictionary];
|
||||
if ([domain length])
|
||||
uid = [NSString stringWithFormat: @"%@@%@", uid, domain];
|
||||
[jsonLine addObject: uid];
|
||||
[jsonLine addObject: [contact objectForKey: @"cn"]];
|
||||
[jsonLine addObject: [contact objectForKey: @"c_email"]];
|
||||
[jsonLine addObject: [NSNumber numberWithBool: [[contact objectForKey: @"isGroup"] boolValue]]];
|
||||
[jsonLine setObject: uid forKey: @"uid"];
|
||||
[jsonLine setObject: [NSString stringWithFormat:@"%@ <%@>",[contact objectForKey: @"cn"], [contact objectForKey: @"c_email"]] forKey: @"displayName"];
|
||||
[jsonLine setObject: [NSNumber numberWithBool: [[contact objectForKey: @"isGroup"] boolValue]] forKey: @"isGroup"];
|
||||
contactInfo = [contact objectForKey: @"c_info"];
|
||||
if (contactInfo)
|
||||
[jsonLine addObject: contactInfo];
|
||||
[jsonLine setObject: contactInfo forKey: @"c_info"];
|
||||
[jsonResponse addObject: jsonLine];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
xmlns:label="OGo:label"
|
||||
xmlns:rsrc="OGo:url"
|
||||
const:userDefaultsKeys="SOGoContactsCategories"
|
||||
const:jsFiles="Common/resource.js, Contacts/card-model.js, Contacts/addressbook-model.js"
|
||||
const:jsFiles="Common/resource.js, Common/acl-model.js, Contacts/card-model.js, Contacts/addressbook-model.js"
|
||||
className="UIxPageFrame"
|
||||
title="name"
|
||||
var:popup="isPopup">
|
||||
|
@ -364,4 +364,55 @@
|
|||
</ion-popover-view>
|
||||
</script>
|
||||
|
||||
<script type="text/ng-template" id="acl-modal.html">
|
||||
<ion-modal-view>
|
||||
<ion-header-bar class="bar-positive">
|
||||
<div class="buttons" ng-show="onGoingSearch">
|
||||
<button class="button button-icon" ng-click="closeModal()">close</button>
|
||||
</div>
|
||||
<div class="buttons" ng-hide="onGoingSearch">
|
||||
<button class="button button-icon" ng-click="cancelSearch()">back</button>
|
||||
</div>
|
||||
<h1 class="title">{{addressbook.name}}</h1>
|
||||
<div class="buttons">
|
||||
<button class="button button-icon" ng-click="saveModal()">save</button>
|
||||
</div>
|
||||
</ion-header-bar>
|
||||
<ion-content>
|
||||
<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" 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.displayName}}
|
||||
<i class="icon" ng-class="displayIcon(user)"><!-- spacer --></i>
|
||||
<ion-delete-button class="ion-minus-circled" ng-disabled="user.userClass == 'public-user' || !user.inAclList"
|
||||
ng-click="removeUser(user)"></ion-delete-button>
|
||||
</ion-item>
|
||||
<div class="item-accordion" ng-show="isUserShown(user)">
|
||||
<ion-toggle ng-show="displaySubscribeUser()" ng-model="userSelected.isSubscribed" ng-checked="userSelected.isSubscribed"
|
||||
ng-disabled="!userSelected.canSubscribeUser" ng-change="markUserAsDirty()">
|
||||
<var:string label:value="Subscribe user"/></ion-toggle>
|
||||
<ion-checkbox ng-show="displayUserRights()" ng-checked="userSelected.aclOptions.canCreateObjects"
|
||||
ng-model="userSelected.aclOptions.canCreateObjects" ng-change="markUserAsDirty()">
|
||||
<var:string label:value="This person can add cards to this addressbook."/></ion-checkbox>
|
||||
<ion-checkbox ng-show="displayUserRights()" ng-checked="userSelected.aclOptions.canEditObjects"
|
||||
ng-model="userSelected.aclOptions.canEditObjects" ng-change="markUserAsDirty()">
|
||||
<var:string label:value="This person can edit the cards of this addressbook."/></ion-checkbox>
|
||||
<ion-checkbox ng-show="displayUserRights()" ng-checked="userSelected.aclOptions.canEraseObjects"
|
||||
ng-model="userSelected.aclOptions.canEraseObjects" ng-change="markUserAsDirty()">
|
||||
<var:string label:value="This person can erase cards from this addressbook."/></ion-checkbox>
|
||||
<ion-checkbox ng-checked="userSelected.aclOptions.canViewObjects"
|
||||
ng-model="userSelected.aclOptions.canViewObjects" ng-change="markUserAsDirty()">
|
||||
<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" placeholder="Search a user/group" min-length="2" model="usersFound"
|
||||
source="getContacts(str)" clear="cancelSearch()">
|
||||
</ion-search>
|
||||
</ion-footer-bar>
|
||||
</ion-modal-view>
|
||||
</script>
|
||||
</var:component>
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
function AclUsers(addressbook) {
|
||||
this.addressbook_id = addressbook.id;
|
||||
this.addressbook_name = addressbook.name;
|
||||
this.addressbook_owner = addressbook.owner;
|
||||
}
|
||||
|
||||
/* The factory we'll use to register with Angular */
|
||||
AclUsers.factory = ['$q', '$timeout', 'sgSettings', 'sgResource', function($q, $timeout, Settings, Resource) {
|
||||
angular.extend(AclUsers, {
|
||||
$q: $q,
|
||||
$timeout: $timeout,
|
||||
$$resource: new Resource(Settings.baseURL)
|
||||
});
|
||||
|
||||
return AclUsers; // return constructor
|
||||
}];
|
||||
|
||||
/* Factory registration in Angular module */
|
||||
angular.module('SOGo.Common').factory('sgAclUsers', AclUsers.factory);
|
||||
|
||||
/* Instance methods
|
||||
* Public method, assigned to prototype
|
||||
*/
|
||||
AclUsers.prototype.getUsers = function() {
|
||||
return AclUsers.$$resource.fetch(this.addressbook_id, "getUsersForObject");
|
||||
};
|
||||
|
||||
AclUsers.prototype.searchUsers = function(inputText) {
|
||||
var param = "search=" + inputText;
|
||||
return AclUsers.$$resource.fetch(null, "usersSearch", param);
|
||||
};
|
||||
|
||||
AclUsers.prototype.openRightsForUserId = function(user) {
|
||||
var param = "uid=" + user;
|
||||
return AclUsers.$$resource.fetch(this.addressbook_id, "userRights", param);
|
||||
};
|
||||
|
||||
AclUsers.prototype.addUser = function(user) {
|
||||
var param = "uid=" + user;
|
||||
AclUsers.$$resource.fetch(this.addressbook_id, "addUserInAcls", param);
|
||||
};
|
||||
|
||||
AclUsers.prototype.subscribeUsers = function(users) {
|
||||
var param = "uids=" + users;
|
||||
AclUsers.$$resource.fetch(this.addressbook_id, "subscribeUsers", param);
|
||||
};
|
||||
|
||||
AclUsers.prototype.removeUser = function(user) {
|
||||
var userId = "uid=" + user.uid;
|
||||
AclUsers.$$resource.fetch(this.addressbook_id, "removeUserFromAcls", userId);
|
||||
};
|
||||
|
||||
AclUsers.prototype.saveUsersRights = function(dirtyObjects) {
|
||||
AclUsers.$$resource.saveAclUsers(this.addressbook_id, "saveUserRights", dirtyObjects);
|
||||
};
|
||||
})();
|
|
@ -107,6 +107,49 @@
|
|||
$urlRouterProvider.otherwise('/app/addressbooks');
|
||||
})
|
||||
|
||||
.directive('ionSearch', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
scope: {
|
||||
getData: '&source',
|
||||
clearData: '&clear',
|
||||
model: '=?',
|
||||
search: '=?filter'
|
||||
},
|
||||
link: function(scope, element, attrs) {
|
||||
attrs.minLength = attrs.minLength || 0;
|
||||
scope.placeholder = attrs.placeholder || '';
|
||||
scope.search = {value: ''};
|
||||
|
||||
if (attrs.class)
|
||||
element.addClass(attrs.class);
|
||||
|
||||
if (attrs.source) {
|
||||
scope.$watch('search.value', function (newValue, oldValue) {
|
||||
if (newValue.length > attrs.minLength) {
|
||||
scope.getData({str: newValue}).then(function (results) {
|
||||
scope.model = results;
|
||||
});
|
||||
}
|
||||
else {
|
||||
scope.model = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
scope.clearSearch = function() {
|
||||
scope.search.value = '';
|
||||
scope.clearData();
|
||||
};
|
||||
},
|
||||
template: '<div class="item-input-wrapper">' +
|
||||
'<i class="icon ion-android-search"></i>' +
|
||||
'<input type="search" placeholder="{{placeholder}}" ng-model="search.value">' +
|
||||
'<i ng-if="search.value.length > 0" ng-click="clearSearch()" class="icon ion-close"></i>' +
|
||||
'</div>'
|
||||
};
|
||||
})
|
||||
|
||||
.controller('AppCtrl', ['$scope', '$http', function($scope, $http) {
|
||||
$scope.UserLogin = UserLogin;
|
||||
$scope.UserFolderURL = UserFolderURL;
|
||||
|
@ -119,7 +162,7 @@
|
|||
// };
|
||||
}])
|
||||
|
||||
.controller('AddressBooksCtrl', ['$scope', '$rootScope', '$ionicModal', '$ionicListDelegate', '$ionicActionSheet', 'sgDialog', 'sgAddressBook', function($scope, $rootScope, $ionicModal, $ionicListDelegate, $ionicActionSheet, Dialog, AddressBook) {
|
||||
.controller('AddressBooksCtrl', ['$scope', '$rootScope', '$ionicModal', '$ionicListDelegate', '$ionicActionSheet', 'sgDialog', 'sgAddressBook', 'sgAclUsers', function($scope, $rootScope, $ionicModal, $ionicListDelegate, $ionicActionSheet, Dialog, AddressBook, sgAclUsers) {
|
||||
// Initialize with data from template
|
||||
$scope.addressbooks = AddressBook.$findAll(contactFolders);
|
||||
$scope.newAddressbook = function() {
|
||||
|
@ -141,19 +184,181 @@
|
|||
$scope.edit = function(addressbook) {
|
||||
$ionicActionSheet.show({
|
||||
buttons: [
|
||||
{ text: l('Rename') }
|
||||
{ text: l('Rename') },
|
||||
{ text: l('Sharing') }
|
||||
],
|
||||
destructiveText: l('Delete'),
|
||||
cancelText: l('Cancel'),
|
||||
buttonClicked: function(index) {
|
||||
// Rename addressbook
|
||||
Dialog.prompt(l('Rename addressbook'),
|
||||
addressbook.name)
|
||||
if(index == 0) {
|
||||
// Rename addressbook
|
||||
Dialog.prompt(l('Rename addressbook'),
|
||||
addressbook.name)
|
||||
.then(function(name) {
|
||||
if (name && name.length > 0) {
|
||||
addressbook.$rename(name);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if(index == 1) {
|
||||
// Build modal editor
|
||||
$ionicModal.fromTemplateUrl('acl-modal.html', { scope: $scope }).then(function(modal) {
|
||||
if ($scope.$aclEditorModal) {
|
||||
$scope.$aclEditorModal.remove();
|
||||
}
|
||||
// Variables in scope
|
||||
$scope.$aclEditorModal = modal;
|
||||
$scope.addressbook = addressbook;
|
||||
$scope.AclUsers = new sgAclUsers(addressbook);
|
||||
var aclUsers = {};
|
||||
$scope.AclUsers.getUsers().then(function(users) {
|
||||
$scope.refreshUsers(users);
|
||||
});
|
||||
$scope.showDelete = false;
|
||||
$scope.onGoingSearch = true;
|
||||
|
||||
// Variables in javascript
|
||||
var dirtyObjects = {};
|
||||
|
||||
// Local functions
|
||||
$scope.refreshUsers = function(users) {
|
||||
$scope.users = [];
|
||||
$scope.onGoingSearch = true;
|
||||
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.saveModal = function() {
|
||||
if(Object.keys(dirtyObjects).length > 0) {
|
||||
if(dirtyObjects["anonymous"])
|
||||
{
|
||||
Dialog.confirm(l("Warning"), l("Any user with an account on this system will be able to access your mailbox \"%{0}\". Are you certain you trust them all?")).then(function(res){
|
||||
if(res){Dialog.alert("okay!")};
|
||||
})
|
||||
}
|
||||
else if (dirtyObjects["<default>"]) {
|
||||
Dialog.confirm(l("Warning"), 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?")).then(function(res){
|
||||
if(res){Dialog.alert("okay!")};
|
||||
})
|
||||
}
|
||||
else {
|
||||
$scope.AclUsers.saveUsersRights(dirtyObjects);
|
||||
var usersToSubscribe = [];
|
||||
angular.forEach(dirtyObjects, function(dirtyObject){
|
||||
if(dirtyObject.canSubscribeUser && dirtyObject.isSubscribed){
|
||||
usersToSubscribe.push(dirtyObject.uid);
|
||||
}
|
||||
})
|
||||
$scope.AclUsers.subscribeUsers(usersToSubscribe);
|
||||
}
|
||||
}
|
||||
$scope.$aclEditorModal.remove();
|
||||
};
|
||||
$scope.cancelSearch = function() {
|
||||
$scope.AclUsers.getUsers().then(function(users) {
|
||||
$scope.refreshUsers(users);
|
||||
});
|
||||
};
|
||||
$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];
|
||||
$scope.AclUsers.removeUser(user);
|
||||
$scope.AclUsers.getUsers().then(function(users) {
|
||||
$scope.refreshUsers(users);
|
||||
});
|
||||
$scope.userSelected = {};
|
||||
}
|
||||
};
|
||||
$scope.addUser = function (user) {
|
||||
if (user) {
|
||||
$scope.AclUsers.addUser(user.uid);
|
||||
$scope.AclUsers.getUsers().then(function(users) {
|
||||
$scope.refreshUsers(users);
|
||||
});
|
||||
}
|
||||
else {
|
||||
// TODO : Write a better msg and add the string inside the .string
|
||||
Dialog.alert(l('Warning'), l('This user is already added to your AclUsers list'));
|
||||
}
|
||||
};
|
||||
$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
|
||||
$scope.AclUsers.openRightsForUserId($scope.userSelected.uid).then(function(userRights) {
|
||||
$scope.userSelected.aclOptions = userRights;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
$scope.getContacts = function(value){
|
||||
$scope.users = [];
|
||||
$scope.onGoingSearch = false;
|
||||
return $scope.AclUsers.searchUsers(value).then(function(usersFound) {
|
||||
angular.forEach(usersFound, function(userFound){
|
||||
userFound.inAclList = (aclUsers[userFound.uid]) ? true : false;
|
||||
$scope.users.push(userFound);
|
||||
})
|
||||
});
|
||||
};
|
||||
$scope.toggleUser = function(user) {
|
||||
if (user.inAclList) {
|
||||
if ($scope.isUserShown(user)) {
|
||||
$scope.shownUser = null;
|
||||
}
|
||||
else {
|
||||
$scope.shownUser = user;
|
||||
$scope.editUser(user);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$scope.addUser(user);
|
||||
}
|
||||
};
|
||||
$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();
|
||||
});
|
||||
}
|
||||
return true;
|
||||
},
|
||||
destructiveButtonClicked: function() {
|
||||
|
|
Loading…
Reference in New Issue