From 4639bf6b999e7aaba11dd9fd61217b978e2876a0 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Tue, 19 Jan 2010 12:28:10 +0000 Subject: [PATCH] Monotone-Parent: 538aedd7d01174f61f329561b71c913f2c048a26 Monotone-Revision: d4724feea77a2bfe4a1315b1572ab4ae3ea3afc9 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2010-01-19T12:28:10 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 39 ++++ .../Appointments/SOGoAppointmentFolder.h | 16 +- .../Appointments/SOGoAppointmentFolder.m | 132 ++++++------- .../Appointments/SOGoAppointmentFolders.h | 14 +- .../Appointments/SOGoAppointmentFolders.m | 187 ++++++++++++++---- SoObjects/Appointments/SOGoCalendarProxy.m | 66 +++---- .../Appointments/SOGoUser+Appointments.h | 6 +- .../Appointments/SOGoUser+Appointments.m | 48 +---- .../SOGoUserFolder+Appointments.m | 44 +++-- SoObjects/SOGo/SOGoParentFolder.m | 10 +- SoObjects/SOGo/SOGoUserSettings.h | 15 +- SoObjects/SOGo/SOGoUserSettings.m | 58 +----- Tests/test-ical.py | 81 ++++++++ Tests/test-webdavsync.py | 2 +- Tests/utilities.py | 22 ++- .../Localizable.strings | 1 + UI/Common/Czech.lproj/Localizable.strings | 1 + UI/Common/Dutch.lproj/Localizable.strings | 1 + UI/Common/English.lproj/Localizable.strings | 1 + UI/Common/French.lproj/Localizable.strings | 1 + UI/Common/German.lproj/Localizable.strings | 1 + UI/Common/Hungarian.lproj/Localizable.strings | 1 + UI/Common/Italian.lproj/Localizable.strings | 1 + UI/Common/Russian.lproj/Localizable.strings | 1 + UI/Common/Spanish.lproj/Localizable.strings | 1 + UI/Common/Swedish.lproj/Localizable.strings | 1 + UI/Common/UIxAclEditor.m | 26 ++- UI/Common/UIxFolderActions.m | 78 +++++--- UI/Common/Welsh.lproj/Localizable.strings | 1 + UI/Common/product.plist | 5 + .../Localizable.strings | 3 - UI/Scheduler/Czech.lproj/Localizable.strings | 3 - UI/Scheduler/Dutch.lproj/Localizable.strings | 3 - .../English.lproj/Localizable.strings | 3 - UI/Scheduler/French.lproj/Localizable.strings | 3 - UI/Scheduler/German.lproj/Localizable.strings | 3 - .../Hungarian.lproj/Localizable.strings | 3 - .../Italian.lproj/Localizable.strings | 3 - .../Russian.lproj/Localizable.strings | 3 - .../Spanish.lproj/Localizable.strings | 3 - .../Swedish.lproj/Localizable.strings | 3 - UI/Scheduler/UIxCalendarProperties.h | 3 - UI/Scheduler/UIxCalendarProperties.m | 11 -- UI/Scheduler/Welsh.lproj/Localizable.strings | 3 - .../SchedulerUI/UIxCalendarProperties.wox | 10 - UI/Templates/UIxAclEditor.wox | 14 +- UI/WebServerResources/SchedulerUI.js | 11 +- UI/WebServerResources/UIxAclEditor.css | 10 +- UI/WebServerResources/UIxAclEditor.js | 60 +++++- 49 files changed, 616 insertions(+), 400 deletions(-) diff --git a/ChangeLog b/ChangeLog index d6fca10cd..13fdd1f44 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,44 @@ 2010-01-19 Wolfgang Sourdeau + * UI/Common/UIxFolderActions.m (-subscribeUsersAction): new web + method that enables resource owners to subscribe other users to + their resources from the acl editor. + + * UI/Common/UIxAclEditor.m (-currentUserIsSubscribed) + (-folderID): new accessors. + + * Tests/utilities.py (TestACLUtility.(un)subscribe): added utility + methods. + + * Tests/test-ical.py (iCalTest.testCalendarProxy2): test the + behaviour of the ical proxy mechanisms when subscriptions and + access rights are modified. + + * SoObjects/SOGo/SOGoUserSettings.m (-subscribedCalendars) + (-subscribedAddressBooks): new accessors. + (-setCalendarProxyUsers:withWriteAccess:) + (-setCalendarProxySubscriptionUsers:withWriteAccess:) removed accessors. + + * SoObjects/Appointments/SOGoUser+Appointments.m + (-adjustProxySubscriptionToUser:remove:forWriteAccess:) removed + methods. + (-hasSubscribedToCalendar:): new method that returns whether a + user has subscribed to a calendar resource. Might be a duplicate + of [SOGoGCSFolder userIsSubscriber:]. + + * SoObjects/Appointments/SOGoAppointmentFolders.m + (-adjustProxyRolesForUsers:remove:forWriteAccess:) + (-adjustProxySubscriptionsForUsers:remove:forWriteAccess:): + removed methods. + (-hasProxyCalendarsWithWriteAccess:forUserWithLogin:): new method + that returns whether the user passed as parameter should be + returned in the list of subscribers corresponding to the proper + write access. + (-proxySubscribersWithWriteAccess:) new method that returns the + list of users that have a proxy access to the current account. + (-addProxySubscribers:withWriteAccess:,-removeProxySubscribers:withWriteAccess:) + setters for the above method. + * UI/WebServerResources/ContactsUI.js (initContacts): fixed a bug causing a null exception error on IE7 when the window is not the main window, and is therefore missing the "uploadCancel" and diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.h b/SoObjects/Appointments/SOGoAppointmentFolder.h index d53adbaa0..5887ab975 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.h +++ b/SoObjects/Appointments/SOGoAppointmentFolder.h @@ -50,6 +50,12 @@ @class GCSFolder; @class iCalCalendar; +typedef enum { + SOGoAppointmentProxyPermissionNone = 0, + SOGoAppointmentProxyPermissionRead = 1, + SOGoAppointmentProxyPermissionWrite = 2, +} SOGoAppointmentProxyPermission; + @interface SOGoAppointmentFolder : SOGoGCSFolder { NSTimeZone *timeZone; @@ -138,9 +144,6 @@ - (BOOL) showCalendarTasks; - (void) setShowCalendarTasks: (BOOL) new; -- (BOOL) isProxied; -- (void) setIsProxied: (BOOL) isProxied; - - (NSString *) syncTag; - (void) setSyncTag: (NSString *) newSyncTag; @@ -152,9 +155,10 @@ /* caldav proxy */ -- (void) adjustProxyRolesForUsers: (NSArray *) proxyUsers - remove: (BOOL) remove - forWriteAccess: (BOOL) write; +- (SOGoAppointmentProxyPermission) + proxyPermissionForUserWithLogin: (NSString *) login; + +- (NSArray *) aclUsersWithProxyWriteAccess: (BOOL) write; @end diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index cf7afd1be..87b8c64f0 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -2906,47 +2906,6 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return (![inactiveFolders containsObject: nameInContainer]); } -- (BOOL) isProxied -{ - NSArray *proxiedCalendars; - SOGoUser *ownerUser; - - ownerUser = [SOGoUser userWithLogin: [self ownerInContext: nil]]; - proxiedCalendars = [[ownerUser userSettings] proxiedCalendars]; - - return [proxiedCalendars containsObject: [self realNameInContainer]]; -} - -- (void) setIsProxied: (BOOL) isProxied -{ - NSMutableArray *proxiedCalendars; - NSArray *subscriptionUsers; - SOGoUser *ownerUser; - SOGoUserSettings *us; - - ownerUser = [SOGoUser userWithLogin: [self ownerInContext: nil]]; - us = [ownerUser userSettings]; - proxiedCalendars = [[us proxiedCalendars] mutableCopy]; - if (isProxied) - [proxiedCalendars addObjectUniquely: [self realNameInContainer]]; - else - [proxiedCalendars removeObject: [self realNameInContainer]]; - - [us setProxiedCalendars: proxiedCalendars]; - - subscriptionUsers = [us calendarProxyUsersWithWriteAccess: YES]; - [self adjustProxyRolesForUsers: subscriptionUsers - remove: !isProxied - forWriteAccess: YES]; - subscriptionUsers = [us calendarProxyUsersWithWriteAccess: NO]; - [self adjustProxyRolesForUsers: subscriptionUsers - remove: !isProxied - forWriteAccess: NO]; - - [us synchronize]; - [proxiedCalendars autorelease]; -} - - (BOOL) importComponent: (iCalEntityObject *) event { SOGoAppointmentObject *object; @@ -3013,49 +2972,78 @@ firstInstanceCalendarDateRange: (NGCalendarDateRange *) fir return aclsForUser; } -- (NSArray *) requiredProxyRolesWithWriteAccess: (BOOL) hasWriteAccess +/* caldav-proxy */ +- (SOGoAppointmentProxyPermission) + proxyPermissionForUserWithLogin: (NSString *) login { - static NSArray *writeAccessRoles = nil; - static NSArray *readAccessRoles = nil; - - if (!writeAccessRoles) + SOGoAppointmentProxyPermission permission; + NSArray *roles; + static NSArray *readRoles = nil; + static NSArray *writeRoles = nil; + + if (!readRoles) { - writeAccessRoles = [NSArray arrayWithObjects: - SOGoCalendarRole_ConfidentialModifier, - SOGoRole_ObjectCreator, - SOGoRole_ObjectEraser, - SOGoCalendarRole_PrivateModifier, - SOGoCalendarRole_PublicModifier, - nil]; - [writeAccessRoles retain]; + readRoles = [NSArray arrayWithObjects: + SOGoCalendarRole_ConfidentialViewer, + SOGoCalendarRole_ConfidentialDAndTViewer, + SOGoCalendarRole_PrivateViewer, + SOGoCalendarRole_PrivateDAndTViewer, + SOGoCalendarRole_PublicViewer, + SOGoCalendarRole_PublicDAndTViewer, + nil]; + [readRoles retain]; } - - if (!readAccessRoles) + if (!writeRoles) { - readAccessRoles = [NSArray arrayWithObjects: - SOGoCalendarRole_ConfidentialViewer, - SOGoCalendarRole_PrivateViewer, - SOGoCalendarRole_PublicViewer, - nil]; - [readAccessRoles retain]; + writeRoles = [NSArray arrayWithObjects: + SOGoRole_ObjectCreator, + SOGoRole_ObjectEraser, + SOGoCalendarRole_ConfidentialModifier, + SOGoCalendarRole_ConfidentialResponder, + SOGoCalendarRole_PrivateModifier, + SOGoCalendarRole_PrivateResponder, + SOGoCalendarRole_PublicModifier, + SOGoCalendarRole_PublicResponder, + nil]; + [writeRoles retain]; } - return (hasWriteAccess) ? writeAccessRoles : readAccessRoles; + permission = SOGoAppointmentProxyPermissionNone; + roles = [self aclsForUser: login]; + if ([roles count]) + { + if ([roles firstObjectCommonWithArray: readRoles]) + permission = SOGoAppointmentProxyPermissionRead; + if ([roles firstObjectCommonWithArray: writeRoles]) + permission = SOGoAppointmentProxyPermissionWrite; + } + + return permission; } -- (void) adjustProxyRolesForUsers: (NSArray *) proxyUsers - remove: (BOOL) remove - forWriteAccess: (BOOL) write +- (NSArray *) aclUsersWithProxyWriteAccess: (BOOL) write { - NSArray *roles; + NSMutableArray *users; + NSArray *aclUsers; + NSString *aclUser; + SOGoAppointmentProxyPermission permission; + int count, max; - if (remove) - [self removeAclsForUsers: proxyUsers]; - else + permission = (write + ? SOGoAppointmentProxyPermissionWrite + : SOGoAppointmentProxyPermissionRead); + aclUsers = [self aclUsers]; + max = [aclUsers count]; + users = [NSMutableArray arrayWithCapacity: max]; + for (count = 0; count < max; count++) { - roles = [self requiredProxyRolesWithWriteAccess: write]; - [self setRoles: roles forUsers: proxyUsers]; + aclUser = [aclUsers objectAtIndex: count]; + if ([self proxyPermissionForUserWithLogin: aclUser] + == permission) + [users addObject: aclUser]; } + + return users; } @end /* SOGoAppointmentFolder */ diff --git a/SoObjects/Appointments/SOGoAppointmentFolders.h b/SoObjects/Appointments/SOGoAppointmentFolders.h index 38f8ccc84..bf7fba704 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolders.h +++ b/SoObjects/Appointments/SOGoAppointmentFolders.h @@ -31,13 +31,15 @@ - (NSArray *) webCalendarIds; -- (void) adjustProxyRolesForUsers: (NSArray *) proxyUsers - remove: (BOOL) remove - forWriteAccess: (BOOL) write; +- (BOOL) hasProxyCalendarsWithWriteAccess: (BOOL) write + forUserWithLogin: (NSString *) userLogin; -- (void) adjustProxySubscriptionsForUsers: (NSArray *) proxyUsers - remove: (BOOL) remove - forWriteAccess: (BOOL) write; +- (NSArray *) proxySubscribersWithWriteAccess: (BOOL) write; + +- (void) addProxySubscribers: (NSArray *) proxySubscribers + withWriteAccess: (BOOL) write; +- (void) removeProxySubscribers: (NSArray *) proxySubscribers + withWriteAccess: (BOOL) write; @end diff --git a/SoObjects/Appointments/SOGoAppointmentFolders.m b/SoObjects/Appointments/SOGoAppointmentFolders.m index b3eb98b30..527ad1958 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolders.m +++ b/SoObjects/Appointments/SOGoAppointmentFolders.m @@ -79,13 +79,14 @@ { NSMutableArray *keys; NSEnumerator *sortedSubFolders; - SOGoGCSFolder *currentFolder; + SOGoAppointmentFolder *currentFolder; + SOGoUser *currentUser; NSString *login; - SOGoUser *ownerUser; if ([[context request] isICal]) { - login = [[context activeUser] login]; + currentUser = [context activeUser]; + login = [currentUser login]; keys = [NSMutableArray array]; if ([owner isEqualToString: login]) { @@ -100,9 +101,12 @@ } else { - ownerUser = [SOGoUser userWithLogin: owner]; - keys = (NSMutableArray *) [[ownerUser userSettings] - proxiedCalendars]; + sortedSubFolders = [[self subFolders] objectEnumerator]; + while ((currentFolder = [sortedSubFolders nextObject])) + if ([currentUser hasSubscribedToCalendar: currentFolder] + && ([currentFolder proxyPermissionForUserWithLogin: login] + != SOGoAppointmentProxyPermissionNone)) + [keys addObject: [currentFolder nameInContainer]]; } } else @@ -369,50 +373,157 @@ withEquivalent: SoPerm_AddDocumentsImagesAndFiles asChildOf: davElement (@"write", XMLNS_WEBDAV)]; } + return aclManager; } -- (void) adjustProxyRolesForUsers: (NSArray *) proxyUsers - remove: (BOOL) remove - forWriteAccess: (BOOL) write +- (BOOL) hasProxyCalendarsWithWriteAccess: (BOOL) write + forUserWithLogin: (NSString *) userLogin { - NSArray *calendars; - SOGoUser *ownerUser; - SOGoAppointmentFolder *folder; - int count, max; + NSEnumerator *sortedSubFolders; + SOGoAppointmentFolder *currentFolder; + SOGoUser *currentUser; + SOGoAppointmentProxyPermission permission, curPermission, foundPermission; + BOOL rc; - ownerUser = [SOGoUser userWithLogin: owner]; - calendars = [[ownerUser userSettings] proxiedCalendars]; - max = [calendars count]; - for (count = 0; count < max; count++) + if ([owner isEqualToString: userLogin]) + rc = NO; + else { - folder = [self lookupName: [calendars objectAtIndex: count] - inContext: context - acquire: NO]; - [folder adjustProxyRolesForUsers: proxyUsers - remove: remove - forWriteAccess: write]; + foundPermission = SOGoAppointmentProxyPermissionNone; + permission = (write + ? SOGoAppointmentProxyPermissionWrite + : SOGoAppointmentProxyPermissionRead); + currentUser = [SOGoUser userWithLogin: userLogin]; + sortedSubFolders = [[self subFolders] objectEnumerator]; + while ((currentFolder = [sortedSubFolders nextObject])) + if ([currentUser hasSubscribedToCalendar: currentFolder]) + { + curPermission = [currentFolder + proxyPermissionForUserWithLogin: userLogin]; + if ((foundPermission == SOGoAppointmentProxyPermissionNone) + || (foundPermission == SOGoAppointmentProxyPermissionRead + && curPermission == SOGoAppointmentProxyPermissionWrite)) + foundPermission = curPermission; + } + rc = (foundPermission == permission); + } + + return rc; +} + +- (NSArray *) proxySubscribersWithWriteAccess: (BOOL) write +{ + SOGoAppointmentFolder *currentFolder; + SOGoUser *currentUser; + NSArray *subFolderNames, *aclUsers; + NSString *aclUser; + NSMutableArray *subscribers; + int folderCount, folderMax, userCount, userMax; + + subscribers = [NSMutableArray array]; + + subFolderNames = [self subFolders]; + folderMax = [subFolderNames count]; + for (folderCount = 0; folderCount < folderMax; folderCount++) + { + currentFolder = [subFolderNames objectAtIndex: folderCount]; + aclUsers = [currentFolder aclUsersWithProxyWriteAccess: write]; + userMax = [aclUsers count]; + for (userCount = 0; userCount < userMax; userCount++) + { + aclUser = [aclUsers objectAtIndex: userCount]; + if (![subscribers containsObject: aclUser]) + { + currentUser = [SOGoUser userWithLogin: aclUser]; + if ([currentUser hasSubscribedToCalendar: currentFolder]) + [subscribers addObject: aclUser]; + } + } + } + + return subscribers; +} + +- (NSArray *) _requiredProxyRolesWithWriteAccess: (BOOL) hasWriteAccess +{ + static NSArray *writeAccessRoles = nil; + static NSArray *readAccessRoles = nil; + + if (!writeAccessRoles) + { + writeAccessRoles = [NSArray arrayWithObjects: + SOGoCalendarRole_ConfidentialModifier, + SOGoRole_ObjectCreator, + SOGoRole_ObjectEraser, + SOGoCalendarRole_PrivateModifier, + SOGoCalendarRole_PublicModifier, + nil]; + [writeAccessRoles retain]; + } + + if (!readAccessRoles) + { + readAccessRoles = [NSArray arrayWithObjects: + SOGoCalendarRole_ConfidentialViewer, + SOGoCalendarRole_PrivateViewer, + SOGoCalendarRole_PublicViewer, + nil]; + [readAccessRoles retain]; + } + + return (hasWriteAccess) ? writeAccessRoles : readAccessRoles; +} + +- (void) addProxySubscribers: (NSArray *) proxySubscribers + withWriteAccess: (BOOL) write +{ + SOGoAppointmentFolder *currentFolder; + NSArray *subFolderNames, *proxyRoles; + NSMutableArray *subscribers; + int folderCount, folderMax, userCount, userMax; + + subscribers = [NSMutableArray array]; + + proxyRoles = [self _requiredProxyRolesWithWriteAccess: write]; + subFolderNames = [self subFolders]; + folderMax = [subFolderNames count]; + for (folderCount = 0; folderCount < folderMax; folderCount++) + { + currentFolder = [subFolderNames objectAtIndex: folderCount]; + [currentFolder setRoles: proxyRoles + forUsers: proxySubscribers]; + + userMax = [proxySubscribers count]; + for (userCount = 0; userCount < userMax; userCount++) + [currentFolder + subscribeUser: [proxySubscribers objectAtIndex: userCount] + reallyDo: YES]; } } -- (void) adjustProxySubscriptionsForUsers: (NSArray *) proxyUsers - remove: (BOOL) remove - forWriteAccess: (BOOL) write +- (void) removeProxySubscribers: (NSArray *) proxySubscribers + withWriteAccess: (BOOL) write { - int count, max; - SOGoUser *proxyUser; + SOGoAppointmentFolder *currentFolder; + NSArray *subFolderNames; + NSMutableArray *subscribers; + int folderCount, folderMax, userCount, userMax; - max = [proxyUsers count]; - for (count = 0; count < max; count++) + subscribers = [NSMutableArray array]; + + subFolderNames = [self subFolders]; + folderMax = [subFolderNames count]; + for (folderCount = 0; folderCount < folderMax; folderCount++) { - proxyUser = [SOGoUser userWithLogin: [proxyUsers objectAtIndex: count]]; - if (proxyUser) - [proxyUser adjustProxySubscriptionToUser: owner - remove: remove - forWriteAccess: write]; - else - [self warnWithFormat: @"(%@) user '%@' is invalid (ignored)", - NSStringFromSelector (_cmd), [proxyUser login]]; + currentFolder = [subFolderNames objectAtIndex: folderCount]; + [currentFolder removeAclsForUsers: proxySubscribers]; + + userMax = [proxySubscribers count]; + for (userCount = 0; userCount < userMax; userCount++) + [currentFolder + subscribeUser: [proxySubscribers objectAtIndex: userCount] + reallyDo: NO]; } } diff --git a/SoObjects/Appointments/SOGoCalendarProxy.m b/SoObjects/Appointments/SOGoCalendarProxy.m index bf2d21e4a..be6f2c9b8 100644 --- a/SoObjects/Appointments/SOGoCalendarProxy.m +++ b/SoObjects/Appointments/SOGoCalendarProxy.m @@ -71,21 +71,20 @@ - (NSArray *) davGroupMemberSet { NSMutableArray *members; - NSArray *proxyUsers, *member; - SOGoUser *ownerUser; + NSArray *proxySubscribers, *member; NSString *appName, *proxyUser; int count, max; appName = [[context request] applicationName]; - ownerUser = [SOGoUser userWithLogin: [self ownerInContext: context]]; - proxyUsers = [[ownerUser userSettings] - calendarProxyUsersWithWriteAccess: hasWriteAccess]; - max = [proxyUsers count]; + proxySubscribers + = [[container lookupName: @"Calendar" inContext: context acquire: NO] + proxySubscribersWithWriteAccess: hasWriteAccess]; + max = [proxySubscribers count]; members = [NSMutableArray arrayWithCapacity: max]; for (count = 0; count < max; count++) { - proxyUser = [proxyUsers objectAtIndex: count]; + proxyUser = [proxySubscribers objectAtIndex: count]; member = [NSArray arrayWithObjects: @"href", XMLNS_WEBDAV, @"D", [NSString stringWithFormat: @"/%@/dav/%@/", appName, proxyUser], @@ -141,46 +140,35 @@ { SOGoUser *ownerUser; SOGoUserSettings *us; - NSMutableArray *addedUsers, *removedUsers; - NSArray *oldProxyUsers, *newProxyUsers; + NSMutableArray *addedSubscribers, *removedSubscribers; + NSArray *oldProxySubscribers, *newProxySubscribers; NSString *login; SOGoAppointmentFolders *folders; login = [self ownerInContext: context]; ownerUser = [SOGoUser userWithLogin: login roles: nil]; us = [ownerUser userSettings]; - oldProxyUsers = [us calendarProxyUsersWithWriteAccess: hasWriteAccess]; - if (!oldProxyUsers) - oldProxyUsers = [NSMutableArray array]; - newProxyUsers = [self _parseSubscribers: memberSet]; - if (!newProxyUsers) - newProxyUsers = [NSMutableArray array]; - [us setCalendarProxyUsers: newProxyUsers - withWriteAccess: hasWriteAccess]; + folders = [container lookupName: @"Calendar" + inContext: context acquire: NO]; + oldProxySubscribers + = [folders proxySubscribersWithWriteAccess: hasWriteAccess]; + if (!oldProxySubscribers) + oldProxySubscribers = [NSMutableArray array]; + newProxySubscribers = [self _parseSubscribers: memberSet]; + if (!newProxySubscribers) + newProxySubscribers = [NSMutableArray array]; - folders = [container lookupName: @"Calendar" inContext: context - acquire: NO]; - addedUsers = [newProxyUsers mutableCopy]; - [addedUsers removeObjectsInArray: oldProxyUsers]; - [folders adjustProxyRolesForUsers: addedUsers - remove: NO - forWriteAccess: hasWriteAccess]; - [folders adjustProxySubscriptionsForUsers: addedUsers - remove: NO - forWriteAccess: hasWriteAccess]; - [addedUsers autorelease]; + addedSubscribers = [newProxySubscribers mutableCopy]; + [addedSubscribers removeObjectsInArray: oldProxySubscribers]; + [addedSubscribers autorelease]; + [folders addProxySubscribers: addedSubscribers + withWriteAccess: hasWriteAccess]; - removedUsers = [oldProxyUsers mutableCopy]; - [removedUsers removeObjectsInArray: newProxyUsers]; - [folders adjustProxyRolesForUsers: removedUsers - remove: YES - forWriteAccess: hasWriteAccess]; - [folders adjustProxySubscriptionsForUsers: removedUsers - remove: YES - forWriteAccess: hasWriteAccess]; - [removedUsers autorelease]; - - [us synchronize]; + removedSubscribers = [oldProxySubscribers mutableCopy]; + [removedSubscribers removeObjectsInArray: newProxySubscribers]; + [removedSubscribers autorelease]; + [folders removeProxySubscribers: removedSubscribers + withWriteAccess: hasWriteAccess]; return nil; } diff --git a/SoObjects/Appointments/SOGoUser+Appointments.h b/SoObjects/Appointments/SOGoUser+Appointments.h index 8057ad2af..b56879e5e 100644 --- a/SoObjects/Appointments/SOGoUser+Appointments.h +++ b/SoObjects/Appointments/SOGoUser+Appointments.h @@ -25,11 +25,11 @@ #import +@class SOGoAppointmentFolder; + @interface SOGoUser (SOGoCalDAVSupport) -- (void) adjustProxySubscriptionToUser: (NSString *) ownerUser - remove: (BOOL) remove - forWriteAccess: (BOOL) write; +- (BOOL) hasSubscribedToCalendar: (SOGoAppointmentFolder *) calendar; @end diff --git a/SoObjects/Appointments/SOGoUser+Appointments.m b/SoObjects/Appointments/SOGoUser+Appointments.m index 6f0dcf931..3f7e67964 100644 --- a/SoObjects/Appointments/SOGoUser+Appointments.m +++ b/SoObjects/Appointments/SOGoUser+Appointments.m @@ -23,52 +23,22 @@ #import #import +#import "SOGoAppointmentFolder.h" + #import "SOGoUser+Appointments.h" @implementation SOGoUser (SOGoCalDAVSupport) -- (void) adjustProxySubscriptionToUser: (NSString *) ownerUser - remove: (BOOL) remove - forWriteAccess: (BOOL) write +#warning duplicate of [SOGoGCSFolder userIsSubscriber:] +- (BOOL) hasSubscribedToCalendar: (SOGoAppointmentFolder *) calendar { - SOGoUserSettings *us; - NSMutableArray *subscriptions; + NSArray *subscriptions; + NSString *reference; - us = [self userSettings]; + subscriptions = [[self userSettings] subscribedCalendars]; + reference = [calendar folderReference]; - /* first, we want to ensure the subscription does not appear in the - opposite list... */ - if (!remove) - { - subscriptions - = [[us calendarProxySubscriptionUsersWithWriteAccess: !write] - mutableCopy]; - [subscriptions autorelease]; - if ([subscriptions containsObject: ownerUser]) - { - [subscriptions removeObject: ownerUser]; - [us setCalendarProxySubscriptionUsers: subscriptions - withWriteAccess: !write]; - - } - } - - subscriptions - = [[us calendarProxySubscriptionUsersWithWriteAccess: write] - mutableCopy]; - [subscriptions autorelease]; - if (remove) - [subscriptions removeObject: ownerUser]; - else - { - if (!subscriptions) - subscriptions = [NSMutableArray array]; - [subscriptions addObjectUniquely: ownerUser]; - } - - [us setCalendarProxySubscriptionUsers: subscriptions - withWriteAccess: write]; - [us synchronize]; + return [subscriptions containsObject: reference]; } @end diff --git a/SoObjects/Appointments/SOGoUserFolder+Appointments.m b/SoObjects/Appointments/SOGoUserFolder+Appointments.m index f07592700..0c58b8c63 100644 --- a/SoObjects/Appointments/SOGoUserFolder+Appointments.m +++ b/SoObjects/Appointments/SOGoUserFolder+Appointments.m @@ -23,6 +23,7 @@ #import #import #import +#import #import #import @@ -137,25 +138,42 @@ - (NSArray *) _calendarProxiedUsersWithWriteAccess: (BOOL) write { - NSMutableArray *proxiedUsers; - SOGoUser *ownerUser; - NSArray *subscriptions; - NSString *ownerLogin, *currentLogin; + NSMutableDictionary *proxiedUsers; + NSArray *references, *elements; + NSString *currentLogin; + NSNumber *yesNumber; + SOGoAppointmentFolders *parentFolder; + SOGoUserSettings *us; int count, max; - ownerLogin = [self ownerInContext: nil]; - ownerUser = [SOGoUser userWithLogin: ownerLogin]; - subscriptions = [[ownerUser userSettings] - calendarProxySubscriptionUsersWithWriteAccess: write]; - max = [subscriptions count]; - proxiedUsers = [NSMutableArray arrayWithCapacity: max]; + yesNumber = [NSNumber numberWithBool: YES]; + + us = [[SOGoUser userWithLogin: owner] userSettings]; + references = [us subscribedCalendars]; + max = [references count]; + proxiedUsers = [NSMutableDictionary dictionaryWithCapacity: max]; for (count = 0; count < max; count++) { - currentLogin = [subscriptions objectAtIndex: count]; - [proxiedUsers addObject: currentLogin]; + elements = [[references objectAtIndex: count] + componentsSeparatedByString: @":"]; + if ([elements count]) + { + currentLogin = [elements objectAtIndex: 0]; + if (![proxiedUsers objectForKey: currentLogin]) + { + parentFolder = [[container lookupName: currentLogin + inContext: context + acquire: NO] + lookupName: @"Calendar" + inContext: context acquire: NO]; + if ([parentFolder hasProxyCalendarsWithWriteAccess: write + forUserWithLogin: owner]) + [proxiedUsers setObject: yesNumber forKey: currentLogin]; + } + } } - return proxiedUsers; + return [proxiedUsers allKeys]; } - (void) _addGroupMembershipToArray: (NSMutableArray *) groups diff --git a/SoObjects/SOGo/SOGoParentFolder.m b/SoObjects/SOGo/SOGoParentFolder.m index 4b0d93d12..09d1f5ffe 100644 --- a/SoObjects/SOGo/SOGoParentFolder.m +++ b/SoObjects/SOGo/SOGoParentFolder.m @@ -336,7 +336,7 @@ static SoSecurityManager *sm = nil; return error; } -- (NSException *) initSubFolders; +- (NSException *) initSubFolders { NSException *error; @@ -435,10 +435,10 @@ static SoSecurityManager *sm = nil; error = [self initSubFolders]; if (error && isPropfind) { - /* We exceptionnally raise the exception here because doPROPFIND: - will not care for errors in its response from - toManyRelationShipKeys, which may in turn trigger the - disappearance of user folders in the SOGo extensions. */ + /* We exceptionnally raise the exception here because doPROPFIND: will + not care for errors in its response from toManyRelationShipKeys, + which may in turn trigger the disappearance of user folders in the + SOGo extensions. */ [error raise]; } diff --git a/SoObjects/SOGo/SOGoUserSettings.h b/SoObjects/SOGo/SOGoUserSettings.h index b250c8118..6a07fb962 100644 --- a/SoObjects/SOGo/SOGoUserSettings.h +++ b/SoObjects/SOGo/SOGoUserSettings.h @@ -32,19 +32,8 @@ + (SOGoUserSettings *) settingsForUser: (NSString *) userId; -/* the calendars that we publish to our proxy subscribers */ -- (void) setProxiedCalendars: (NSArray *) proxiedCalendars; -- (NSArray *) proxiedCalendars; - -/* the users that we have subscribed us as a proxy to our calendars */ -- (void) setCalendarProxyUsers: (NSArray *) proxyUsers - withWriteAccess: (BOOL) writeAccess; -- (NSArray *) calendarProxyUsersWithWriteAccess: (BOOL) writeAccess; - -/* the users that have subscribed us as a proxy to their calendars */ -- (void) setCalendarProxySubscriptionUsers: (NSArray *) subscriptionUsers - withWriteAccess: (BOOL) writeAccess; -- (NSArray *) calendarProxySubscriptionUsersWithWriteAccess: (BOOL) writeAccess; +- (NSArray *) subscribedCalendars; +- (NSArray *) subscribedAddressBooks; @end diff --git a/SoObjects/SOGo/SOGoUserSettings.m b/SoObjects/SOGo/SOGoUserSettings.m index 4fb7c9acd..13f3d38da 100644 --- a/SoObjects/SOGo/SOGoUserSettings.m +++ b/SoObjects/SOGo/SOGoUserSettings.m @@ -55,65 +55,19 @@ static Class SOGoUserProfileKlass = Nil; return ud; } -/* the calendars that we publish to our proxy subscribers */ -- (void) setProxiedCalendars: (NSArray *) proxiedCalendars +- (NSArray *) _subscribedFoldersForModule: (NSString *) module { - [self setObject: proxiedCalendars forKey: @"ProxiedCalendars"]; + return [[self dictionaryForKey: module] objectForKey: @"SubscribedFolders"]; } -- (NSArray *) proxiedCalendars +- (NSArray *) subscribedCalendars { - NSArray *proxiedCalendars; - - proxiedCalendars = [self arrayForKey: @"ProxiedCalendars"]; - if (!proxiedCalendars) - proxiedCalendars = [NSArray arrayWithObject: @"personal"]; - - return proxiedCalendars; + return [self _subscribedFoldersForModule: @"Calendar"]; } -/* the users that we have subscribed us as a proxy to our calendars */ -- (void) setCalendarProxyUsers: (NSArray *) proxyUsers - withWriteAccess: (BOOL) writeAccess +- (NSArray *) subscribedAddressBooks { - NSString *key; - - key = [NSString stringWithFormat: @"CalendarProxy%@Users", - (writeAccess ? @"Write" : @"Read")]; - - [self setObject: proxyUsers forKey: key]; -} - -- (NSArray *) calendarProxyUsersWithWriteAccess: (BOOL) writeAccess -{ - NSString *key; - - key = [NSString stringWithFormat: @"CalendarProxy%@Users", - (writeAccess ? @"Write" : @"Read")]; - - return [self arrayForKey: key]; -} - -/* the users that have subscribed us as a proxy to their calendars */ -- (void) setCalendarProxySubscriptionUsers: (NSArray *) subscriptionUsers - withWriteAccess: (BOOL) writeAccess -{ - NSString *key; - - key = [NSString stringWithFormat: @"CalendarProxy%@SubscriptionUsers", - (writeAccess ? @"Write" : @"Read")]; - - [self setObject: subscriptionUsers forKey: key]; -} - -- (NSArray *) calendarProxySubscriptionUsersWithWriteAccess: (BOOL) writeAccess -{ - NSString *key; - - key = [NSString stringWithFormat: @"CalendarProxy%@SubscriptionUsers", - (writeAccess ? @"Write" : @"Read")]; - - return [self arrayForKey: key]; + return [self _subscribedFoldersForModule: @"Contacts"]; } @end diff --git a/Tests/test-ical.py b/Tests/test-ical.py index 446a43c77..fce16ae2b 100755 --- a/Tests/test-ical.py +++ b/Tests/test-ical.py @@ -3,6 +3,7 @@ from config import hostname, port, username, password, subscriber_username import unittest +import utilities import webdavlib class iCalTest(unittest.TestCase): @@ -121,5 +122,85 @@ class iCalTest(unittest.TestCase): "'%s' expected to be %s proxy for %s: %s" % (users[1], perm, users[0], proxyFor)) + def _testMapping(self, client, perm, resource, rights): + dav_utility = utilities.TestCalendarACLUtility(client, resource) + dav_utility.setupRights(subscriber_username, rights) + + membership = self._getMembership(subscriber_username) + self.assertEquals(['/SOGo/dav/%s/calendar-proxy-%s/' + % (username, perm)], + membership, + "'%s' must have %s access to %s's calendars:\n%s" + % (subscriber_username, perm, username, membership)) + proxyFor = self._getProxyFor(subscriber_username, perm) + self.assertEquals([username], proxyFor, + "'%s' expected to be %s proxy for %s: %s" + % (subscriber_username, perm, username, proxyFor)) + + def testCalendarProxy2(self): + """calendar-proxy as used from SOGo""" + client = webdavlib.WebDAVClient(hostname, port, username, password) + client.user_agent = "DAVKit/4.0.1 (730); CalendarStore/4.0.1 (973); iCal/4.0.1 (1374); Mac OS X/10.6.2 (10C540)" + personal_resource = "/SOGo/dav/%s/Calendar/personal/" % username + dav_utility = utilities.TestCalendarACLUtility(client, + personal_resource) + dav_utility.setupRights(subscriber_username, {}) + dav_utility.subscribe([subscriber_username]) + + other_resource = ("/SOGo/dav/%s/Calendar/test-calendar-proxy2/" + % username) + delete = webdavlib.WebDAVDELETE(other_resource) + client.execute(delete) + mkcol = webdavlib.WebDAVMKCOL(other_resource) + client.execute(mkcol) + dav_utility = utilities.TestCalendarACLUtility(client, + other_resource) + dav_utility.setupRights(subscriber_username, {}) + dav_utility.subscribe([subscriber_username]) + + ## we test the rights mapping + # write: write on 'personal', none on 'test-calendar-proxy2' + self._testMapping(client, "write", personal_resource, + { "c": True, "d": False, "pu": "v" }) + self._testMapping(client, "write", personal_resource, + { "c": False, "d": True, "pu": "v" }) + self._testMapping(client, "write", personal_resource, + { "c": False, "d": False, "pu": "m" }) + self._testMapping(client, "write", personal_resource, + { "c": False, "d": False, "pu": "r" }) + + # read: read on 'personal', none on 'test-calendar-proxy2' + self._testMapping(client, "read", personal_resource, + { "c": False, "d": False, "pu": "d" }) + self._testMapping(client, "read", personal_resource, + { "c": False, "d": False, "pu": "v" }) + + # write: read on 'personal', write on 'test-calendar-proxy2' + self._testMapping(client, "write", other_resource, + { "c": False, "d": False, "pu": "r" }) + + ## we test the unsubscription + # unsubscribed from personal, subscribed to 'test-calendar-proxy2' + dav_utility = utilities.TestCalendarACLUtility(client, + personal_resource) + dav_utility.unsubscribe([subscriber_username]) + membership = self._getMembership(subscriber_username) + self.assertEquals(['/SOGo/dav/%s/calendar-proxy-write/' % username], + membership, + "'%s' must have write access to %s's calendars" + % (subscriber_username, username)) + # unsubscribed from personal, unsubscribed from 'test-calendar-proxy2' + dav_utility = utilities.TestCalendarACLUtility(client, + other_resource) + dav_utility.unsubscribe([subscriber_username]) + membership = self._getMembership(subscriber_username) + self.assertEquals([], + membership, + "'%s' must have no access to %s's calendars" + % (subscriber_username, username)) + + delete = webdavlib.WebDAVDELETE(other_resource) + client.execute(delete) + if __name__ == "__main__": unittest.main() diff --git a/Tests/test-webdavsync.py b/Tests/test-webdavsync.py index 46b96559d..7159d28d9 100755 --- a/Tests/test-webdavsync.py +++ b/Tests/test-webdavsync.py @@ -49,7 +49,7 @@ class WebdavSyncTest(unittest.TestCase): token = int(token_node.childNodes[0].nodeValue) self.assertTrue(token > 0) - self.assertTrue(token < int(query1.start)) + self.assertTrue(token <= int(query1.start)) # we make sure that any token is invalid when the collection is empty query2 = webdavlib.WebDAVSyncQuery(resource, "1234", [ "getetag" ]) diff --git a/Tests/utilities.py b/Tests/utilities.py index 7d9297c23..c90c3cd15 100644 --- a/Tests/utilities.py +++ b/Tests/utilities.py @@ -33,28 +33,34 @@ class TestACLUtility(TestUtility): TestUtility.__init__(self, client) self.resource = resource - def subscribe(self, subscribers=None): - rights_str = "".join(["<%s/>" % x - for x in self.rightsToSOGoRights(rights) ]) + def _subscriptionOperation(self, subscribers, operation): subscribeQuery = ("\n" - + "" post = webdavlib.HTTPPOST(self.resource, subscribeQuery) post.content_type = "application/xml; charset=\"utf-8\"" self.client.execute(post) self.assertEquals(post.response["status"], 204, - "subscribtion failure to set '%s' (status: %d)" - % (rights_str, post.response["status"])) + "subscribtion failure to '%s' for '%s' (status: %d)" + % (self.resource, "', '".join(subscribers), + post.response["status"])) + + def subscribe(self, subscribers=None): + self._subscriptionOperation(subscribers, "subscribe") + + def unsubscribe(self, subscribers=None): + self._subscriptionOperation(subscribers, "unsubscribe") def rightsToSOGoRights(self, rights): self.fail("subclass must implement this method") def setupRights(self, username, rights): - rights_str = "".join(["<%s/>" % x for x in self.rightsToSOGoRights(rights) ]) + rights_str = "".join(["<%s/>" + % x for x in self.rightsToSOGoRights(rights) ]) aclQuery = ("\n" + "" diff --git a/UI/Common/BrazilianPortuguese.lproj/Localizable.strings b/UI/Common/BrazilianPortuguese.lproj/Localizable.strings index 8375e6758..8dad1b704 100644 --- a/UI/Common/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/Common/BrazilianPortuguese.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Divulgar a informação Livre/Ocupado"; "Default Roles" = "Papéis Padrão"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "Desculpe, os direitos de usuário não podem ser modificados para este objeto."; diff --git a/UI/Common/Czech.lproj/Localizable.strings b/UI/Common/Czech.lproj/Localizable.strings index 4ac57ca30..d5ef100e1 100644 --- a/UI/Common/Czech.lproj/Localizable.strings +++ b/UI/Common/Czech.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Zveřejni informace o Volný/Zaneprázdněný"; "Default Roles" = "Výchozí role"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "Omlouváme se, ale uživatelská práva pro tento objekt nemohou být nastaveny."; diff --git a/UI/Common/Dutch.lproj/Localizable.strings b/UI/Common/Dutch.lproj/Localizable.strings index ee15cab2e..eb2db2465 100644 --- a/UI/Common/Dutch.lproj/Localizable.strings +++ b/UI/Common/Dutch.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Beschikbaarheidsinformatie publiceren"; "Default Roles" = "Standaardmachtigingen"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "De machtigingen kunnen niet worden ingesteld voor dit object."; diff --git a/UI/Common/English.lproj/Localizable.strings b/UI/Common/English.lproj/Localizable.strings index 184baf4fc..d4bcb9b80 100644 --- a/UI/Common/English.lproj/Localizable.strings +++ b/UI/Common/English.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Publish the Free/Busy information"; "Default Roles" = "Default Roles"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "Sorry, the user rights can not be configured for that object."; diff --git a/UI/Common/French.lproj/Localizable.strings b/UI/Common/French.lproj/Localizable.strings index 937293ab3..7c87823f4 100644 --- a/UI/Common/French.lproj/Localizable.strings +++ b/UI/Common/French.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Publier l'occupation du temps"; "Default Roles" = "Rôles par défaut"; +"Subscribe User" = "Abonner l'utilisateur"; "Sorry, the user rights can not be configured for that object." = "Sorry, the user rights can not be configured for that object."; diff --git a/UI/Common/German.lproj/Localizable.strings b/UI/Common/German.lproj/Localizable.strings index 5a9c087f5..e2d64d196 100644 --- a/UI/Common/German.lproj/Localizable.strings +++ b/UI/Common/German.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Verfügbarkeitsinformationen veröffentlichen"; "Default Roles" = "Standardrechte"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "Leider können die Benutzerrechte für dieses Objekt nicht konfiguriert werden."; diff --git a/UI/Common/Hungarian.lproj/Localizable.strings b/UI/Common/Hungarian.lproj/Localizable.strings index d8b9c418d..256daea22 100644 --- a/UI/Common/Hungarian.lproj/Localizable.strings +++ b/UI/Common/Hungarian.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Foglaltsági információ nyilvánossá tétele"; "Default Roles" = "Alapértelmezett jogok"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "Sajnálom, erre az objektumra nem állíthatók be felhasználói jogosultságok."; diff --git a/UI/Common/Italian.lproj/Localizable.strings b/UI/Common/Italian.lproj/Localizable.strings index ede6794df..5b4f3ea30 100644 --- a/UI/Common/Italian.lproj/Localizable.strings +++ b/UI/Common/Italian.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Pubblica le informazioni sullo stato (libero/impegnato)"; "Default Roles" = "Permessi predefiniti"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "Non è possibile configurare i permessi per questo oggetto."; diff --git a/UI/Common/Russian.lproj/Localizable.strings b/UI/Common/Russian.lproj/Localizable.strings index 2799366a7..3f75fb27b 100644 --- a/UI/Common/Russian.lproj/Localizable.strings +++ b/UI/Common/Russian.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Публиковать информацию о занятом/свободном времени"; "Default Roles" = "Контроль доступа для всех"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "Извините, для данного объекта невозможно настроить права доступа."; diff --git a/UI/Common/Spanish.lproj/Localizable.strings b/UI/Common/Spanish.lproj/Localizable.strings index ee6049168..18f033a1b 100644 --- a/UI/Common/Spanish.lproj/Localizable.strings +++ b/UI/Common/Spanish.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Publicar información de disponibilidad"; "Default Roles" = "Roles por defecto"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "Sorry, the user rights can not be configured for that object."; diff --git a/UI/Common/Swedish.lproj/Localizable.strings b/UI/Common/Swedish.lproj/Localizable.strings index d945c153d..d60658647 100644 --- a/UI/Common/Swedish.lproj/Localizable.strings +++ b/UI/Common/Swedish.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Publisera ledig/upptagen information"; "Default Roles" = "Standardroller"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "Tyvärr, användarrättigheterna kan inte konfigureras för objektet."; diff --git a/UI/Common/UIxAclEditor.m b/UI/Common/UIxAclEditor.m index 128ffa06e..786ca17d8 100644 --- a/UI/Common/UIxAclEditor.m +++ b/UI/Common/UIxAclEditor.m @@ -29,11 +29,12 @@ #import #import #import -#import -#import -#import -#import -#import +#import +#import +#import +#import +#import +#import #import "UIxAclEditor.h" @@ -158,6 +159,16 @@ return [self _displayNameForUID: currentUser]; } +- (BOOL) currentUserIsSubscribed +{ + SOGoGCSFolder *folder; + + folder = [self clientObject]; + + return ([folder respondsToSelector: @selector (userIsSubscriber:)] + && [folder userIsSubscriber: currentUser]); +} + - (void) setUserUIDS: (NSString *) retainedUsers { if ([retainedUsers length] > 0) @@ -169,6 +180,11 @@ savedUIDs = [NSArray new]; } +- (NSString *) folderID +{ + return [[self clientObject] nameInContainer]; +} + - (BOOL) shouldTakeValuesFromRequest: (WORequest *) request inContext: (WOContext *) context { diff --git a/UI/Common/UIxFolderActions.m b/UI/Common/UIxFolderActions.m index 077b47475..7878f19cf 100644 --- a/UI/Common/UIxFolderActions.m +++ b/UI/Common/UIxFolderActions.m @@ -313,23 +313,21 @@ NSException *ex; request = [context request]; - ex = nil; if ((destinationFolderId = [request formValueForKey: @"folder"]) && (contactsId = [request formValuesForKey: @"uid"])) - { - ex = [self _moveContacts: contactsId - toFolder: destinationFolderId + ex = [self _moveContacts: contactsId + toFolder: destinationFolderId andKeepCopy: YES]; - if (ex != nil) - response = (id)ex; - } else - response = [NSException exceptionWithHTTPStatus: 400 - reason: @"missing 'folder' and/or 'uid' parameter"]; + ex = [NSException exceptionWithHTTPStatus: 400 + reason: (@"missing 'folder' and/or" + @" 'uid' parameter")]; - if (ex == nil) - response = [self responseWith204]; + if (ex) + response = (id) ex; + else + response = [self responseWith204]; return response; } @@ -343,25 +341,55 @@ NSException *ex; request = [context request]; - ex = nil; - if ((destinationFolderId = [request formValueForKey: @"folder"]) && - (contactsId = [request formValuesForKey: @"uid"])) - { - ex = [self _moveContacts: contactsId - toFolder: destinationFolderId + if ((destinationFolderId = [request formValueForKey: @"folder"]) + && (contactsId = [request formValuesForKey: @"uid"])) + ex = [self _moveContacts: contactsId + toFolder: destinationFolderId andKeepCopy: NO]; - if (ex != nil) - response = (id)ex; + else + ex = [NSException exceptionWithHTTPStatus: 400 + reason: (@"missing 'folder' and/or" + @"'uid' parameter")]; + + if (ex) + response = (id ) ex; + else + response = [self responseWith204]; + + return response; +} + +- (id ) subscribeUsersAction +{ + id response; + NSString *uids; + NSArray *userIDs; + SOGoGCSFolder *folder; + NSException *ex; + int count, max; + + uids = [[context request] formValueForKey: @"uids"]; + if ([uids length]) + { + userIDs = [uids componentsSeparatedByString: @","]; + folder = [self clientObject]; + max = [userIDs count]; + for (count = 0; count < max; count++) + [folder subscribeUser: [userIDs objectAtIndex: count] + reallyDo: YES]; + ex = nil; } else - response = [NSException exceptionWithHTTPStatus: 400 - reason: @"missing 'folder' and/or 'uid' parameter"]; + ex = [NSException exceptionWithHTTPStatus: 400 + reason: @"missing 'uids' parameter"]; - if (ex == nil) - response = [self responseWith204]; - - return response; + if (ex) + response = (id ) ex; + else + response = [self responseWith204]; + + return response; } @end diff --git a/UI/Common/Welsh.lproj/Localizable.strings b/UI/Common/Welsh.lproj/Localizable.strings index 8eb8c9c19..884344e85 100644 --- a/UI/Common/Welsh.lproj/Localizable.strings +++ b/UI/Common/Welsh.lproj/Localizable.strings @@ -26,6 +26,7 @@ "Publish the Free/Busy information" = "Cyhoddwch y wybodaeth Rhydd/Brysur"; "Default Roles" = "Rolau Gwreiddiol"; +"Subscribe User" = "Subscribe User"; "Sorry, the user rights can not be configured for that object." = "Sori, ni all hawliau'r defnyddiwr cael ei newid ar gyfer y gwrthrych hwn."; diff --git a/UI/Common/product.plist b/UI/Common/product.plist index 73a787a6e..160e4155f 100644 --- a/UI/Common/product.plist +++ b/UI/Common/product.plist @@ -101,6 +101,11 @@ actionClass = "UIxFolderActions"; actionName = "deactivateFolder"; }; + subscribeUsers = { + protectedBy = "Change Permissions"; + actionClass = "UIxFolderActions"; + actionName = "subscribeUsers"; + }; renameFolder = { protectedBy = "Change Permissions"; actionClass = "UIxFolderActions"; diff --git a/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings b/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings index c99554057..6c44772b9 100644 --- a/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/Scheduler/BrazilianPortuguese.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Tarefa Confidencial)"; "Synchronize" = "Synchronize"; "Tag:" = "Marca:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Display"; "Show alarms" = "Show alarms"; "Show tasks" = "Show tasks"; diff --git a/UI/Scheduler/Czech.lproj/Localizable.strings b/UI/Scheduler/Czech.lproj/Localizable.strings index 7e1e0eec0..2289512ab 100644 --- a/UI/Scheduler/Czech.lproj/Localizable.strings +++ b/UI/Scheduler/Czech.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Důvěrný úkol)"; "Synchronize" = "Synchronize"; "Tag:" = "Štítek:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Display"; "Show alarms" = "Show alarms"; "Show tasks" = "Show tasks"; diff --git a/UI/Scheduler/Dutch.lproj/Localizable.strings b/UI/Scheduler/Dutch.lproj/Localizable.strings index f5afd5945..4f649a120 100644 --- a/UI/Scheduler/Dutch.lproj/Localizable.strings +++ b/UI/Scheduler/Dutch.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Vertrouwelijke taak)"; "Synchronize" = "Synchronize"; "Tag:" = "Markering:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Display"; "Show alarms" = "Show alarms"; "Show tasks" = "Show tasks"; diff --git a/UI/Scheduler/English.lproj/Localizable.strings b/UI/Scheduler/English.lproj/Localizable.strings index 2db0d97fa..888eaca9d 100644 --- a/UI/Scheduler/English.lproj/Localizable.strings +++ b/UI/Scheduler/English.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Confidential task)"; "Synchronize" = "Synchronize"; "Tag:" = "Tag:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Display"; "Show alarms" = "Show alarms"; "Show tasks" = "Show tasks"; diff --git a/UI/Scheduler/French.lproj/Localizable.strings b/UI/Scheduler/French.lproj/Localizable.strings index f67a87040..0a0972554 100644 --- a/UI/Scheduler/French.lproj/Localizable.strings +++ b/UI/Scheduler/French.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Tâche confidentielle)"; "Synchronize" = "Synchroniser"; "Tag:" = "Label :"; -"iCal Delegation" = "Délégation iCal"; -"Shared when account is delegated" = "Inclure cet agenda lors de la délégation"; - "Display" = "Affichage"; "Show alarms" = "Afficher les alarmes"; "Show tasks" = "Afficher les tâches"; diff --git a/UI/Scheduler/German.lproj/Localizable.strings b/UI/Scheduler/German.lproj/Localizable.strings index 7a4f2e20b..44fcb7ba0 100644 --- a/UI/Scheduler/German.lproj/Localizable.strings +++ b/UI/Scheduler/German.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Vertrauliche Aufgabe)"; "Synchronize" = "Synchronize"; "Tag:" = "Tag:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Display"; "Show alarms" = "Show alarms"; "Show tasks" = "Show tasks"; diff --git a/UI/Scheduler/Hungarian.lproj/Localizable.strings b/UI/Scheduler/Hungarian.lproj/Localizable.strings index 7b8986a5f..18ea56ba7 100644 --- a/UI/Scheduler/Hungarian.lproj/Localizable.strings +++ b/UI/Scheduler/Hungarian.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Bizalmas feladat)"; "Synchronize" = "Synchronize"; "Tag:" = "Cimke:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Display"; "Show alarms" = "Show alarms"; "Show tasks" = "Show tasks"; diff --git a/UI/Scheduler/Italian.lproj/Localizable.strings b/UI/Scheduler/Italian.lproj/Localizable.strings index 456aac743..39b7de04f 100644 --- a/UI/Scheduler/Italian.lproj/Localizable.strings +++ b/UI/Scheduler/Italian.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Attività confidenziale)"; "Synchronize" = "Synchronize"; "Tag:" = "Etichetta:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Display"; "Show alarms" = "Show alarms"; "Show tasks" = "Show tasks"; diff --git a/UI/Scheduler/Russian.lproj/Localizable.strings b/UI/Scheduler/Russian.lproj/Localizable.strings index bc2f9940f..099b74190 100644 --- a/UI/Scheduler/Russian.lproj/Localizable.strings +++ b/UI/Scheduler/Russian.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Confidential task)"; "Synchronize" = "Synchronize"; "Tag:" = "Tag:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Display"; "Show alarms" = "Show alarms"; "Show tasks" = "Show tasks"; diff --git a/UI/Scheduler/Spanish.lproj/Localizable.strings b/UI/Scheduler/Spanish.lproj/Localizable.strings index d6e41c99a..8cf76c1d1 100644 --- a/UI/Scheduler/Spanish.lproj/Localizable.strings +++ b/UI/Scheduler/Spanish.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Tarea confidencial)"; "Synchronize" = "Synchronize"; "Tag:" = "Redacción:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Display"; "Show alarms" = "Show alarms"; "Show tasks" = "Show tasks"; diff --git a/UI/Scheduler/Swedish.lproj/Localizable.strings b/UI/Scheduler/Swedish.lproj/Localizable.strings index 1802fa24a..693f363e5 100644 --- a/UI/Scheduler/Swedish.lproj/Localizable.strings +++ b/UI/Scheduler/Swedish.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Konfidentiell uppgift)"; "Synchronize" = "Synkronisera"; "Tag:" = "Etikett:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Visa"; "Show alarms" = "Visa alarm"; "Show tasks" = "Visa uppgifter"; diff --git a/UI/Scheduler/UIxCalendarProperties.h b/UI/Scheduler/UIxCalendarProperties.h index 456c9eb4c..0cc80609a 100644 --- a/UI/Scheduler/UIxCalendarProperties.h +++ b/UI/Scheduler/UIxCalendarProperties.h @@ -51,7 +51,4 @@ - (NSString *) calendarSyncTag; - (void) setCalendarSyncTag: (NSString *) newTag; -- (BOOL) isProxied; -- (void) setIsProxied: (BOOL) isProxied; - @end diff --git a/UI/Scheduler/UIxCalendarProperties.m b/UI/Scheduler/UIxCalendarProperties.m index 3f4166927..841faf414 100644 --- a/UI/Scheduler/UIxCalendarProperties.m +++ b/UI/Scheduler/UIxCalendarProperties.m @@ -149,17 +149,6 @@ [calendar setSyncTag: newTag]; } -/* DAV: calendar-proxy protocol */ -- (BOOL) isProxied -{ - return [calendar isProxied]; -} - -- (void) setIsProxied: (BOOL) isProxied -{ - [calendar setIsProxied: isProxied]; -} - - (BOOL) showCalendarAlarms { return [calendar showCalendarAlarms]; diff --git a/UI/Scheduler/Welsh.lproj/Localizable.strings b/UI/Scheduler/Welsh.lproj/Localizable.strings index 30be0f128..55f0d03fc 100644 --- a/UI/Scheduler/Welsh.lproj/Localizable.strings +++ b/UI/Scheduler/Welsh.lproj/Localizable.strings @@ -512,9 +512,6 @@ vtodo_class2 = "(Tasg gyhoeddus)"; "Synchronize" = "Synchronize"; "Tag:" = "Tag:"; -"iCal Delegation" = "iCal Delegation"; -"Shared when account is delegated" = "Shared when account is delegated"; - "Display" = "Display"; "Show alarms" = "Show alarms"; "Show tasks" = "Show tasks"; diff --git a/UI/Templates/SchedulerUI/UIxCalendarProperties.wox b/UI/Templates/SchedulerUI/UIxCalendarProperties.wox index 6fdf00220..05b11c9e8 100644 --- a/UI/Templates/SchedulerUI/UIxCalendarProperties.wox +++ b/UI/Templates/SchedulerUI/UIxCalendarProperties.wox @@ -70,16 +70,6 @@ /> -
- -
-
-
diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index d35d88d88..11f676004 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -1891,17 +1891,20 @@ function onCalendarModify(event) { var url = ApplicationBaseURL + calendarID + "/properties"; var windowID = sanitizeWindowName(calendarID + " properties"); var width = 310; - var height = 310; + var height = 260; var isWebCalendar = false; if (UserSettings['Calendar'] && UserSettings['Calendar']['WebCalendars']) { var webCalendars = UserSettings['Calendar']['WebCalendars']; var realID = calendarID.substr (1, calendarID.length - 1); - if (webCalendars[realID]) + if (webCalendars[realID]) { isWebCalendar = true; + } } - if (isWebCalendar || calendarID == "/personal") - height -= 25; + if (isWebCalendar) + height += 25; + else if (calendarID == "/personal") + height -= 25; var properties = window.open(url, windowID, "width="+width+",height="+height+",resizable=0"); diff --git a/UI/WebServerResources/UIxAclEditor.css b/UI/WebServerResources/UIxAclEditor.css index cdd15ede4..1f941ea66 100644 --- a/UI/WebServerResources/UIxAclEditor.css +++ b/UI/WebServerResources/UIxAclEditor.css @@ -57,8 +57,16 @@ UL#userList list-style-image: none; } UL#userList LI -{ cursor: pointer; +{ clear: both; + cursor: pointer; + text-align: right; padding-left: 3px; } +SPAN.userFullName +{ float: left; } + DIV#userSelectorButtons A.smallToolbarButton { float: left; } + +LABEL.subscriptionArea +{ padding-right: 5px; } diff --git a/UI/WebServerResources/UIxAclEditor.js b/UI/WebServerResources/UIxAclEditor.js index 475e28f21..58a4ffc7b 100644 --- a/UI/WebServerResources/UIxAclEditor.js +++ b/UI/WebServerResources/UIxAclEditor.js @@ -7,6 +7,8 @@ var AclEditor = { userRightsWidth: null }; +var usersToSubscribe = []; + function addUser(userName, userID) { var result = false; if (!$(userID)) { @@ -32,19 +34,49 @@ function setEventsOnUserNode(node) { n.observe("selectstart", listRowMouseDownHandler); n.observe("dblclick", onOpenUserRights); n.observe("click", onRowClick); + + var cbParents = n.childNodesWithTag("label"); + if (cbParents && cbParents.length) { + var cbParent = $(cbParents[0]); + var checkbox = cbParent.childNodesWithTag("input")[0]; + $(checkbox).observe("change", onSubscriptionChange); + } +} + +function onSubscriptionChange(event) { + var li = this.parentNode.parentNode; + var username = li.getAttribute("id"); + var idx = usersToSubscribe.indexOf(username); + if (this.checked) { + if (idx < 0) + usersToSubscribe.push(username); + } else { + if (idx > -1) + usersToSubscribe.splice(idx, 1); + } } function nodeForUser(userName, userId) { - var node = document.createElement("li"); + var node = $(document.createElement("li")); node.setAttribute("id", userId); - node.setAttribute("class", ""); - setEventsOnUserNode(node); + var span = $(document.createElement("span")); + span.addClassName("userFullName"); var image = document.createElement("img"); image.setAttribute("src", ResourcesURL + "/abcard.gif"); + span.appendChild(image); + span.appendChild(document.createTextNode(" " + userName)); + node.appendChild(span); - node.appendChild(image); - node.appendChild(document.createTextNode(" " + userName)); + var label = $(document.createElement("label")); + label.addClassName("class", "subscriptionArea"); + var cb = document.createElement("input"); + cb.type = "checkbox"; + label.appendChild(cb); + label.appendChild(document.createTextNode(getLabel("Subscribe User"))); + node.appendChild(label); + + setEventsOnUserNode(node); return node; } @@ -156,6 +188,24 @@ function onAclLoadHandler() { AclEditor['userRightsHeight'] = window.opener.getUsersRightsWindowHeight(); AclEditor['userRightsWidth'] = window.opener.getUsersRightsWindowWidth(); + + Event.observe(window, "beforeunload", onAclCloseHandler); +} + +function onAclCloseHandler(event) { + if (usersToSubscribe.length) { + var url = (URLForFolderID($("folderID").value) + + "/subscribeUsers?uids=" + usersToSubscribe.join(",")); + new Ajax.Request(url, { + asynchronous: false, + method: 'get', + onFailure: function(transport) { + log("Can't expunge current folder: " + transport.status); + } + }); + } + + return true; } document.observe("dom:loaded", onAclLoadHandler);