diff --git a/ChangeLog b/ChangeLog index 53a2270d6..8ce905a88 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2007-09-14 Wolfgang Sourdeau + + * SoObjects/SOGo/SOGoObject.m ([SOGoObject -labelForKey:key]): new + method that returns translated strings for controller bundles + (same as what UIxComponent does for view bundles). + ([SOGoObject -pathArrayToSOGoObject]): new method that returns + the real path to a subscribed folder (if subscribed). + ([SOGoObject +globallyUniqueObjectId]): move method from SOGoFolder. + ([SOGoObject -globallyUniqueObjectId]): new instance method + calling its class equivalent. + +2007-09-12 Wolfgang Sourdeau + + * UI/MainUI/SOGoRootPage.m ([SOGoRootPage -defaultAction]): test + whether the user is logged in and if so, redirect to his/her + homepage. + ([SOGoRootPage -appendToResponse:inContext:]): removed useless + method. + 2007-09-11 Wolfgang Sourdeau * SoObjects/SOGo/SOGoFolder.m ([SOGoFolder diff --git a/NEWS b/NEWS index 56abf6626..52b2d9f3a 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,8 @@ - fixed a bug where a false positive happening whenever a wrong user login was given during an indirect bind; - deleting a message no longer expunges its parent folder; +- implemented support for multiple calendars; +- it is not possible to rename folders; - fixed search in message content; - added tooltips for toolbar buttons (English and French); - added checkmarks in live search options popup menus; diff --git a/SoObjects/Appointments/English.lproj/Localizable.strings b/SoObjects/Appointments/English.lproj/Localizable.strings new file mode 100644 index 000000000..95feda9fd --- /dev/null +++ b/SoObjects/Appointments/English.lproj/Localizable.strings @@ -0,0 +1 @@ +"Personal Calendar" = "Personal Calendar"; diff --git a/SoObjects/Appointments/French.lproj/Localizable.strings b/SoObjects/Appointments/French.lproj/Localizable.strings new file mode 100644 index 000000000..11b333c2a --- /dev/null +++ b/SoObjects/Appointments/French.lproj/Localizable.strings @@ -0,0 +1 @@ +"Personal Calendar" = "Agenda personnel"; diff --git a/SoObjects/Appointments/GNUmakefile b/SoObjects/Appointments/GNUmakefile index 789501a3d..0ae0a98f1 100644 --- a/SoObjects/Appointments/GNUmakefile +++ b/SoObjects/Appointments/GNUmakefile @@ -6,7 +6,9 @@ WOBUNDLE_NAME = Appointments Appointments_PRINCIPAL_CLASS = SOGoAppointmentsProduct -# Appointments_LANGUAGES = English French +Appointments_LANGUAGES = English French German + +Appointments_LOCALIZED_RESOURCE_FILES=Localizable.strings Appointments_OBJC_FILES = \ Product.m \ @@ -17,6 +19,7 @@ Appointments_OBJC_FILES = \ SOGoAppointmentObject.m \ SOGoTaskObject.m \ SOGoAppointmentFolder.m \ + SOGoAppointmentFolders.m \ SOGoGroupAppointmentFolder.m \ SOGoFreeBusyObject.m \ \ diff --git a/SoObjects/Appointments/German.lproj/Localizable.strings b/SoObjects/Appointments/German.lproj/Localizable.strings new file mode 100644 index 000000000..95feda9fd --- /dev/null +++ b/SoObjects/Appointments/German.lproj/Localizable.strings @@ -0,0 +1 @@ +"Personal Calendar" = "Personal Calendar"; diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.h b/SoObjects/Appointments/SOGoAppointmentFolder.h index f3e71cd8d..06edea0e6 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.h +++ b/SoObjects/Appointments/SOGoAppointmentFolder.h @@ -53,6 +53,8 @@ NSMutableDictionary *uidToFilename; } +- (BOOL) isActive; + /* selection */ - (NSArray *) calendarUIDs; @@ -113,8 +115,6 @@ - (NSArray *) fetchAllSOGoAppointments; -- (NSArray *) calendarFolders; - - (NSString *) roleForComponentsWithAccessClass: (iCalAccessClass) accessClass forUser: (NSString *) uid; diff --git a/SoObjects/Appointments/SOGoAppointmentFolder.m b/SoObjects/Appointments/SOGoAppointmentFolder.m index 9fa1695fc..38c34a943 100644 --- a/SoObjects/Appointments/SOGoAppointmentFolder.m +++ b/SoObjects/Appointments/SOGoAppointmentFolder.m @@ -125,11 +125,6 @@ static NSNumber *sharedYes = nil; return logger; } -- (BOOL) folderIsMandatory -{ - return YES; -} - /* selection */ - (NSArray *) calendarUIDs @@ -746,7 +741,7 @@ static NSNumber *sharedYes = nil; privacySqlString = @"and (c_isopaque = 1)"; else { -#warning we do not manage all the user's possible emails +#warning we do not manage all the possible user emails email = [[activeUser primaryIdentity] objectForKey: @"email"]; privacySqlString @@ -905,7 +900,6 @@ static NSNumber *sharedYes = nil; component: _component]; } - - (NSArray *) fetchFreeBusyInfosFrom: (NSCalendarDate *) _startDate to: (NSCalendarDate *) _endDate { @@ -1041,21 +1035,28 @@ static NSNumber *sharedYes = nil; /* Note: can return NSNull objects in the array! */ NSMutableArray *folders; NSEnumerator *e; - NSString *uid; - + NSString *uid, *ownerLogin; + id folder; + + ownerLogin = [self ownerInContext: context]; + if ([_uids count] == 0) return nil; folders = [NSMutableArray arrayWithCapacity:16]; e = [_uids objectEnumerator]; - while ((uid = [e nextObject])) { - id folder; + while ((uid = [e nextObject])) + { + if ([uid isEqualToString: ownerLogin]) + folder = self; + else + { + folder = [self lookupCalendarFolderForUID: uid]; + if (![folder isNotNull]) + [self logWithFormat:@"Note: did not find folder for uid: '%@'", uid]; + } - folder = [self lookupCalendarFolderForUID: uid]; - if (![folder isNotNull]) - [self logWithFormat:@"Note: did not find folder for uid: '%@'", uid]; - - /* Note: intentionally add 'null' folders to allow a mapping */ - [folders addObject:folder ? folder : [NSNull null]]; - } + [folders addObject: folder]; + } + return folders; } @@ -1196,67 +1197,67 @@ static NSNumber *sharedYes = nil; return events; } -#warning We only support ONE calendar per user at this time -- (BOOL) _appendSubscribedFolders: (NSDictionary *) subscribedFolders - toFolderList: (NSMutableArray *) calendarFolders -{ - NSEnumerator *keys; - NSString *currentKey; - NSMutableDictionary *currentCalendar; - BOOL firstShouldBeActive; - unsigned int count; +// #warning We only support ONE calendar per user at this time +// - (BOOL) _appendSubscribedFolders: (NSDictionary *) subscribedFolders +// toFolderList: (NSMutableArray *) calendarFolders +// { +// NSEnumerator *keys; +// NSString *currentKey; +// NSMutableDictionary *currentCalendar; +// BOOL firstShouldBeActive; +// unsigned int count; - firstShouldBeActive = YES; +// firstShouldBeActive = YES; - keys = [[subscribedFolders allKeys] objectEnumerator]; - currentKey = [keys nextObject]; - count = 1; - while (currentKey) - { - currentCalendar = [NSMutableDictionary new]; - [currentCalendar autorelease]; - [currentCalendar - setDictionary: [subscribedFolders objectForKey: currentKey]]; - [currentCalendar setObject: currentKey forKey: @"folder"]; - [calendarFolders addObject: currentCalendar]; - if ([[currentCalendar objectForKey: @"active"] boolValue]) - firstShouldBeActive = NO; - count++; - currentKey = [keys nextObject]; - } +// keys = [[subscribedFolders allKeys] objectEnumerator]; +// currentKey = [keys nextObject]; +// count = 1; +// while (currentKey) +// { +// currentCalendar = [NSMutableDictionary new]; +// [currentCalendar autorelease]; +// [currentCalendar +// setDictionary: [subscribedFolders objectForKey: currentKey]]; +// [currentCalendar setObject: currentKey forKey: @"folder"]; +// [calendarFolders addObject: currentCalendar]; +// if ([[currentCalendar objectForKey: @"active"] boolValue]) +// firstShouldBeActive = NO; +// count++; +// currentKey = [keys nextObject]; +// } - return firstShouldBeActive; -} +// return firstShouldBeActive; +// } -- (NSArray *) calendarFolders -{ - NSMutableDictionary *userCalendar, *calendarDict; - NSMutableArray *calendarFolders; - SOGoUser *calendarUser; - BOOL firstActive; +// - (NSArray *) calendarFolders +// { +// NSMutableDictionary *userCalendar, *calendarDict; +// NSMutableArray *calendarFolders; +// SOGoUser *calendarUser; +// BOOL firstActive; - calendarFolders = [NSMutableArray new]; - [calendarFolders autorelease]; +// calendarFolders = [NSMutableArray new]; +// [calendarFolders autorelease]; - calendarUser = [SOGoUser userWithLogin: [self ownerInContext: context] - roles: nil]; - userCalendar = [NSMutableDictionary new]; - [userCalendar autorelease]; - [userCalendar setObject: @"/" forKey: @"folder"]; - [userCalendar setObject: @"Calendar" forKey: @"displayName"]; - [calendarFolders addObject: userCalendar]; +// calendarUser = [SOGoUser userWithLogin: [self ownerInContext: context] +// roles: nil]; +// userCalendar = [NSMutableDictionary new]; +// [userCalendar autorelease]; +// [userCalendar setObject: @"/" forKey: @"folder"]; +// [userCalendar setObject: @"Calendar" forKey: @"displayName"]; +// [calendarFolders addObject: userCalendar]; - calendarDict = [[calendarUser userSettings] objectForKey: @"Calendar"]; - firstActive = [[calendarDict objectForKey: @"activateUserFolder"] boolValue]; - firstActive = ([self _appendSubscribedFolders: - [calendarDict objectForKey: @"SubscribedFolders"] - toFolderList: calendarFolders] - || firstActive); - [userCalendar setObject: [NSNumber numberWithBool: firstActive] - forKey: @"active"]; +// calendarDict = [[calendarUser userSettings] objectForKey: @"Calendar"]; +// firstActive = [[calendarDict objectForKey: @"activateUserFolder"] boolValue]; +// firstActive = ([self _appendSubscribedFolders: +// [calendarDict objectForKey: @"SubscribedFolders"] +// toFolderList: calendarFolders] +// || firstActive); +// [userCalendar setObject: [NSNumber numberWithBool: firstActive] +// forKey: @"active"]; - return calendarFolders; -} +// return calendarFolders; +// } // - (NSArray *) fetchContentObjectNames // { @@ -1297,49 +1298,16 @@ static NSNumber *sharedYes = nil; return @"IPF.Appointment"; } -/* hack until we permit more than 1 cal per user */ -- (NSArray *) _fixedPath: (NSArray *) objectPath +- (BOOL) isActive { - NSMutableArray *newPath; + NSUserDefaults *settings; + NSArray *activeFolders; - newPath = [NSMutableArray arrayWithArray: objectPath]; - if ([newPath count] > 2) - { - if (![[newPath objectAtIndex: 2] isEqualToString: @"personal"]) - [newPath insertObject: @"personal" atIndex: 2]; - } - else - [newPath addObject: @"personal"]; + settings = [[context activeUser] userSettings]; + activeFolders + = [[settings objectForKey: @"Calendar"] objectForKey: @"ActiveFolders"]; - return newPath; -} - -- (NSArray *) aclUsersForObjectAtPath: (NSArray *) objectPathArray -{ - return [super aclUsersForObjectAtPath: [self _fixedPath: objectPathArray]]; -} - -- (NSArray *) aclsForUser: (NSString *) uid - forObjectAtPath: (NSArray *) objectPathArray -{ - return [super aclsForUser: uid - forObjectAtPath: [self _fixedPath: objectPathArray]]; -} - -- (void) setRoles: (NSArray *) roles - forUser: (NSString *) uid - forObjectAtPath: (NSArray *) objectPathArray -{ - [super setRoles: roles - forUser: uid - forObjectAtPath: [self _fixedPath: objectPathArray]]; -} - -- (void) removeAclsForUsers: (NSArray *) users - forObjectAtPath: (NSArray *) objectPathArray -{ - [super removeAclsForUsers: users - forObjectAtPath: [self _fixedPath: objectPathArray]]; + return [activeFolders containsObject: nameInContainer]; } @end /* SOGoAppointmentFolder */ diff --git a/SoObjects/Appointments/SOGoAppointmentObject.h b/SoObjects/Appointments/SOGoAppointmentObject.h index d6fe91e86..8262970f2 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.h +++ b/SoObjects/Appointments/SOGoAppointmentObject.h @@ -46,13 +46,6 @@ @interface SOGoAppointmentObject : SOGoCalendarComponent -/* folder management */ - -- (id) lookupHomeFolderForUID: (NSString *) _uid - inContext: (id) _ctx; -- (NSArray *) lookupCalendarFoldersForUIDs: (NSArray *) _uids - inContext: (id) _ctx; - /* "iCal multifolder saves" */ - (NSException *) saveContentString: (NSString *) _iCal diff --git a/SoObjects/Appointments/SOGoAppointmentObject.m b/SoObjects/Appointments/SOGoAppointmentObject.m index 33e140282..404428ea7 100644 --- a/SoObjects/Appointments/SOGoAppointmentObject.m +++ b/SoObjects/Appointments/SOGoAppointmentObject.m @@ -34,6 +34,8 @@ #import #import "NSArray+Appointments.h" +#import "SOGoAppointmentFolder.h" + #import "SOGoAppointmentObject.h" @implementation SOGoAppointmentObject @@ -96,16 +98,6 @@ return uids; } -/* folder management */ - -- (id)lookupHomeFolderForUID:(NSString *)_uid inContext:(id)_ctx { - // TODO: what does this do? lookup the home of the organizer? - return [[self container] lookupHomeFolderForUID:_uid inContext:_ctx]; -} -- (NSArray *)lookupCalendarFoldersForUIDs:(NSArray *)_uids inContext:(id)_ctx { - return [[self container] lookupCalendarFoldersForUIDs:_uids inContext:_ctx]; -} - /* store in all the other folders */ - (NSException *) saveContentString: (NSString *) _iCal @@ -115,8 +107,8 @@ id folder; NSException *allErrors = nil; - e = [[self lookupCalendarFoldersForUIDs:_uids inContext: context] - objectEnumerator]; + e = [[container lookupCalendarFoldersForUIDs:_uids inContext: context] + objectEnumerator]; while ((folder = [e nextObject]) != nil) { NSException *error; SOGoAppointmentObject *apt; @@ -160,8 +152,8 @@ id folder; NSException *allErrors = nil; - e = [[self lookupCalendarFoldersForUIDs:_uids inContext: context] - objectEnumerator]; + e = [[container lookupCalendarFoldersForUIDs:_uids inContext: context] + objectEnumerator]; while ((folder = [e nextObject])) { NSException *error; SOGoAppointmentObject *apt; diff --git a/SoObjects/Appointments/SOGoTaskObject.h b/SoObjects/Appointments/SOGoTaskObject.h index 9f57bf588..da442022b 100644 --- a/SoObjects/Appointments/SOGoTaskObject.h +++ b/SoObjects/Appointments/SOGoTaskObject.h @@ -44,13 +44,6 @@ @interface SOGoTaskObject : SOGoCalendarComponent -/* folder management */ - -- (id) lookupHomeFolderForUID: (NSString *) _uid - inContext: (id) _ctx; -- (NSArray *) lookupCalendarFoldersForUIDs: (NSArray *) _uids - inContext: (id) _ctx; - /* "iCal multifolder saves" */ - (NSException *) saveContentString: (NSString *) _iCal diff --git a/SoObjects/Appointments/SOGoTaskObject.m b/SoObjects/Appointments/SOGoTaskObject.m index 547d19e04..2357ac8b2 100644 --- a/SoObjects/Appointments/SOGoTaskObject.m +++ b/SoObjects/Appointments/SOGoTaskObject.m @@ -34,6 +34,7 @@ #import "NSArray+Appointments.h" #import "SOGoAptMailNotification.h" +#import "SOGoAppointmentFolder.h" #import "SOGoTaskObject.h" @@ -118,17 +119,6 @@ static NSString *mailTemplateDefaultLanguage = nil; return uids; } -/* folder management */ - -- (id)lookupHomeFolderForUID:(NSString *)_uid inContext:(id)_ctx { - // TODO: what does this do? lookup the home of the organizer? - return [[self container] lookupHomeFolderForUID:_uid inContext:_ctx]; -} - -- (NSArray *)lookupCalendarFoldersForUIDs:(NSArray *)_uids inContext:(id)_ctx { - return [[self container] lookupCalendarFoldersForUIDs:_uids inContext:_ctx]; -} - /* store in all the other folders */ - (NSException *)saveContentString:(NSString *)_iCal inUIDs:(NSArray *)_uids { @@ -136,7 +126,7 @@ static NSString *mailTemplateDefaultLanguage = nil; id folder; NSException *allErrors = nil; - e = [[self lookupCalendarFoldersForUIDs: _uids inContext: context] + e = [[container lookupCalendarFoldersForUIDs: _uids inContext: context] objectEnumerator]; while ((folder = [e nextObject]) != nil) { NSException *error; @@ -175,7 +165,7 @@ static NSString *mailTemplateDefaultLanguage = nil; id folder; NSException *allErrors = nil; - e = [[self lookupCalendarFoldersForUIDs: _uids inContext: context] + e = [[container lookupCalendarFoldersForUIDs: _uids inContext: context] objectEnumerator]; while ((folder = [e nextObject])) { NSException *error; diff --git a/SoObjects/Appointments/product.plist b/SoObjects/Appointments/product.plist index d70b2faad..1fdd67e88 100644 --- a/SoObjects/Appointments/product.plist +++ b/SoObjects/Appointments/product.plist @@ -8,6 +8,9 @@ }; classes = { + SOGoAppointmentFolder = { + superclass = "SOGoParentFolder"; + }; SOGoAppointmentFolder = { superclass = "SOGoFolder"; defaultRoles = { diff --git a/SoObjects/Contacts/English.lproj/Localizable.strings b/SoObjects/Contacts/English.lproj/Localizable.strings new file mode 100644 index 000000000..56404b013 --- /dev/null +++ b/SoObjects/Contacts/English.lproj/Localizable.strings @@ -0,0 +1 @@ +"Personal Address Book" = "Personal Address Book"; diff --git a/SoObjects/Contacts/French.lproj/Localizable.strings b/SoObjects/Contacts/French.lproj/Localizable.strings new file mode 100644 index 000000000..af4d87c3a --- /dev/null +++ b/SoObjects/Contacts/French.lproj/Localizable.strings @@ -0,0 +1 @@ +"Personal Address Book" = "Carnet d'adresses personnel"; diff --git a/SoObjects/Contacts/German.lproj/Localizable.strings b/SoObjects/Contacts/German.lproj/Localizable.strings new file mode 100644 index 000000000..95feda9fd --- /dev/null +++ b/SoObjects/Contacts/German.lproj/Localizable.strings @@ -0,0 +1 @@ +"Personal Calendar" = "Personal Calendar"; diff --git a/SoObjects/Contacts/SOGoContactFolders.m b/SoObjects/Contacts/SOGoContactFolders.m index dc3ffe256..2d75c8f0c 100644 --- a/SoObjects/Contacts/SOGoContactFolders.m +++ b/SoObjects/Contacts/SOGoContactFolders.m @@ -27,25 +27,11 @@ TaskItems IPF.Task JournalItems IPF.Journal */ -// #import #import #import #import -// #import -// #import -// #import -// #import -// #import -// #import - -// #import -// #import -// #import -// #import - #import -// #import #import "SOGoContactGCSFolder.h" #import "SOGoContactLDAPFolder.h" @@ -85,4 +71,9 @@ } } +- (NSString *) defaultFolderName +{ + return @"Personal Address Book"; +} + @end diff --git a/SoObjects/Contacts/SOGoContactGCSFolder.m b/SoObjects/Contacts/SOGoContactGCSFolder.m index a3f603185..e00e96749 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.m +++ b/SoObjects/Contacts/SOGoContactGCSFolder.m @@ -21,6 +21,7 @@ #import #import + #import #import #import @@ -41,11 +42,6 @@ @implementation SOGoContactGCSFolder -- (BOOL) folderIsMandatory -{ - return [nameInContainer isEqualToString: @"personal"]; -} - /* name lookup */ - (id ) lookupContactWithId: (NSString *) recordId diff --git a/SoObjects/Contacts/SOGoContactLDAPFolder.m b/SoObjects/Contacts/SOGoContactLDAPFolder.m index 841a79a00..459f2998e 100644 --- a/SoObjects/Contacts/SOGoContactLDAPFolder.m +++ b/SoObjects/Contacts/SOGoContactLDAPFolder.m @@ -257,6 +257,11 @@ } /* acls */ +- (NSString *) ownerInContext: (WOContext *) noContext +{ + return @"nobody"; +} + /* TODO: this might change one day when we support LDAP acls */ - (NSArray *) aclsForUser: (NSString *) uid { diff --git a/SoObjects/Contacts/product.plist b/SoObjects/Contacts/product.plist index cb6b5124d..e1557d613 100644 --- a/SoObjects/Contacts/product.plist +++ b/SoObjects/Contacts/product.plist @@ -9,12 +9,7 @@ classes = { SOGoContactFolders = { - superclass = "SOGoFolder"; - protectedBy = "Access Contents Information"; - defaultRoles = { - "Access Contents Information" = ( "Authenticated" ); - "WebDAV Access" = ( "Authenticated" ); - }; + superclass = "SOGoParentFolder"; }; SOGoContactGCSFolder = { superclass = "SOGoFolder"; diff --git a/SoObjects/SOGo/NSDictionary+Utilities.h b/SoObjects/SOGo/NSDictionary+Utilities.h index bc054c813..1c5622f13 100644 --- a/SoObjects/SOGo/NSDictionary+Utilities.h +++ b/SoObjects/SOGo/NSDictionary+Utilities.h @@ -29,6 +29,8 @@ @interface NSDictionary (SOGoDictionaryUtilities) ++ (NSDictionary *) dictionaryFromStringsFile: (NSString *) file; + - (NSString *) jsonRepresentation; - (NSString *) keysWithFormat: (NSString *) keyFormat; diff --git a/SoObjects/SOGo/NSDictionary+Utilities.m b/SoObjects/SOGo/NSDictionary+Utilities.m index 6caaeaa67..666aef023 100644 --- a/SoObjects/SOGo/NSDictionary+Utilities.m +++ b/SoObjects/SOGo/NSDictionary+Utilities.m @@ -21,6 +21,7 @@ */ #import +#import #import #import "NSArray+Utilities.h" @@ -29,6 +30,25 @@ @implementation NSDictionary (SOGoDictionaryUtilities) ++ (NSDictionary *) dictionaryFromStringsFile: (NSString *) file +{ + NSString *serialized; + NSMutableData *content; + NSDictionary *newDictionary; + + content = [NSMutableData new]; + [content appendBytes: "{" length: 1]; + [content appendData: [NSData dataWithContentsOfFile: file]]; + [content appendBytes: "}" length: 1]; + serialized = [[NSString alloc] initWithData: content + encoding: NSUTF8StringEncoding]; + [content release]; + newDictionary = [serialized propertyList]; + [serialized release]; + + return newDictionary; +} + - (NSString *) jsonRepresentation { NSMutableArray *values; diff --git a/SoObjects/SOGo/SOGoContentObject.m b/SoObjects/SOGo/SOGoContentObject.m index 945382da5..255db3b65 100644 --- a/SoObjects/SOGo/SOGoContentObject.m +++ b/SoObjects/SOGo/SOGoContentObject.m @@ -231,7 +231,7 @@ needsLocation = NO; tmp = [[self nameInContainer] stringByDeletingPathExtension]; if ([tmp isEqualToString:@"new"]) { - tmp = [[[self container] class] globallyUniqueObjectId]; + tmp = [self globallyUniqueObjectId]; needsLocation = YES; [self debugWithFormat: @@ -359,7 +359,7 @@ - (NSArray *) aclUsers { - return [container aclUsersForObjectAtPath: [self pathArrayToSoObject]]; + return [container aclUsersForObjectAtPath: [self pathArrayToSOGoObject]]; } - (NSArray *) aclsForUser: (NSString *) uid @@ -369,7 +369,7 @@ acls = [NSMutableArray array]; ownAcls = [container aclsForUser: uid - forObjectAtPath: [self pathArrayToSoObject]]; + forObjectAtPath: [self pathArrayToSOGoObject]]; [acls addObjectsFromArray: ownAcls]; containerAcls = [container aclsForUser: uid]; if ([containerAcls count] > 0) diff --git a/SoObjects/SOGo/SOGoFolder.h b/SoObjects/SOGo/SOGoFolder.h index dad4629ad..ed57b8059 100644 --- a/SoObjects/SOGo/SOGoFolder.h +++ b/SoObjects/SOGo/SOGoFolder.h @@ -43,23 +43,18 @@ @interface SOGoFolder : SOGoObject { - NSString *displayName; + NSMutableString *displayName; NSString *ocsPath; GCSFolder *ocsFolder; NSMutableDictionary *aclCache; } -+ (NSString *) globallyUniqueObjectId; ++ (id) folderWithSubscriptionReference: (NSString *) reference + inContainer: (id) aContainer; /* accessors */ -+ (id) folderWithName: (NSString *) aName - andDisplayName: (NSString *) aDisplayName - inContainer: (id) aContainer; -- (id) initWithName: (NSString *) aName - andDisplayName: (NSString *) aDisplayName - inContainer: (id) aContainer; - +- (void) setDisplayName: (NSString *) newDisplayName; - (NSString *) displayName; - (void) setOCSPath: (NSString *)_Path; @@ -79,10 +74,11 @@ - (NSString *) outlookFolderClass; - (BOOL) folderIsMandatory; +- (NSString *) folderType; - (BOOL) create; - (NSException *) delete; - +- (void) renameTo: (NSString *) newName; /* dav */ - (NSArray *) davNamespaces; diff --git a/SoObjects/SOGo/SOGoFolder.m b/SoObjects/SOGo/SOGoFolder.m index 18d2d4856..a78d50740 100644 --- a/SoObjects/SOGo/SOGoFolder.m +++ b/SoObjects/SOGo/SOGoFolder.m @@ -19,9 +19,6 @@ 02111-1307, USA. */ -#import -#import - #import #import #import @@ -38,9 +35,11 @@ #import #import #import +#import #import #import #import +#import #import #import "NSArray+Utilities.h" @@ -67,40 +66,29 @@ static NSString *defaultUserID = @""; NSStringFromClass([self superclass]), [super version]); } -+ (NSString *) globallyUniqueObjectId -{ - /* - 4C08AE1A-A808-11D8-AC5A-000393BBAFF6 - SOGo-Web-28273-18283-288182 - printf( "%x", *(int *) &f); - */ - static int pid = 0; - static int sequence = 0; - static float rndm = 0; - float f; - - if (pid == 0) - { /* break if we fork ;-) */ - pid = getpid(); - rndm = random(); - } - sequence++; - f = [[NSDate date] timeIntervalSince1970]; - - return [NSString stringWithFormat:@"%0X-%0X-%0X-%0X", - pid, (int) f, sequence++, random]; -} - -+ (id) folderWithName: (NSString *) aName - andDisplayName: (NSString *) aDisplayName - inContainer: (id) aContainer ++ (id) folderWithSubscriptionReference: (NSString *) reference + inContainer: (id) aContainer { id newFolder; + NSArray *elements, *pathElements; + NSString *ocsPath, *objectPath, *owner, *ocsName, *folderName; - newFolder = [[self alloc] initWithName: aName - andDisplayName: aDisplayName + elements = [reference componentsSeparatedByString: @":"]; + owner = [elements objectAtIndex: 0]; + objectPath = [elements objectAtIndex: 1]; + pathElements = [objectPath componentsSeparatedByString: @"/"]; + if ([pathElements count] > 1) + ocsName = [pathElements objectAtIndex: 1]; + else + ocsName = @"personal"; + + ocsPath = [NSString stringWithFormat: @"/Users/%@/%@/%@", + owner, [pathElements objectAtIndex: 0], ocsName]; + folderName = [NSString stringWithFormat: @"%@_%@", owner, ocsName]; + newFolder = [[self alloc] initWithName: folderName inContainer: aContainer]; - [newFolder autorelease]; + [newFolder setOCSPath: ocsPath]; + [newFolder setOwner: owner]; return newFolder; } @@ -118,17 +106,6 @@ static NSString *defaultUserID = @""; return self; } -- (id) initWithName: (NSString *) aName - andDisplayName: (NSString *) aDisplayName - inContainer: (id) aContainer -{ - if ((self = [self initWithName: aName - inContainer: aContainer])) - ASSIGN (displayName, aDisplayName); - - return self; -} - - (void) dealloc { [ocsFolder release]; @@ -147,13 +124,12 @@ static NSString *defaultUserID = @""; - (void) setOCSPath: (NSString *) _path { - if ([ocsPath isEqualToString:_path]) - return; - - if (ocsPath) - [self warnWithFormat:@"GCS path is already set! '%@'", _path]; - - ASSIGNCOPY(ocsPath, _path); + if (![ocsPath isEqualToString:_path]) + { + if (ocsPath) + [self warnWithFormat: @"GCS path is already set! '%@'", _path]; + ASSIGN (ocsPath, _path); + } } - (NSString *) ocsPath @@ -178,16 +154,75 @@ static NSString *defaultUserID = @""; - (BOOL) folderIsMandatory { - [self subclassResponsibility: _cmd]; + return [nameInContainer isEqualToString: @"personal"]; +} - return NO; +- (void) _setDisplayNameFromRow: (NSDictionary *) row +{ + NSString *currentLogin, *ownerLogin; + NSDictionary *ownerIdentity; + + displayName + = [NSMutableString stringWithString: [row objectForKey: @"c_foldername"]]; + currentLogin = [[context activeUser] login]; + ownerLogin = [self ownerInContext: context]; + if (![currentLogin isEqualToString: ownerLogin]) + { + ownerIdentity = [[SOGoUser userWithLogin: ownerLogin roles: nil] + primaryIdentity]; + [displayName appendFormat: @" (%@ <%@>)", + [ownerIdentity objectForKey: @"fullName"], + [ownerIdentity objectForKey: @"email"]]; + } +} + +- (void) _fetchDisplayName +{ + GCSChannelManager *cm; + EOAdaptorChannel *fc; + NSURL *folderLocation; + NSString *sql; + NSArray *attrs; + NSDictionary *row; + + cm = [GCSChannelManager defaultChannelManager]; + folderLocation + = [[GCSFolderManager defaultFolderManager] folderInfoLocation]; + fc = [cm acquireOpenChannelForURL: folderLocation]; + if (fc) + { + sql + = [NSString stringWithFormat: (@"SELECT c_foldername FROM %@" + @" WHERE c_path = '%@'"), + [folderLocation gcsTableName], ocsPath]; + [fc evaluateExpressionX: sql]; + attrs = [fc describeResults: NO]; + row = [fc fetchAttributes: attrs withZone: NULL]; + if (row) + [self _setDisplayNameFromRow: row]; + [fc cancelFetch]; + [cm releaseChannel: fc]; + } +} + +- (void) setDisplayName: (NSString *) newDisplayName +{ + ASSIGN (displayName, newDisplayName); } - (NSString *) displayName { + if (!displayName) + [self _fetchDisplayName]; + return displayName; } +- (NSString *) davDisplayName +{ + return [self displayName]; +} + - (GCSFolder *) ocsFolder { GCSFolder *folder; @@ -222,7 +257,9 @@ static NSString *defaultUserID = @""; { NSException *result; +// [self dieHard]; result = [[self folderManager] createFolderOfType: [self folderType] + withName: displayName atPath: ocsPath]; return (result == nil); @@ -234,13 +271,40 @@ static NSString *defaultUserID = @""; if ([nameInContainer isEqualToString: @"personal"]) error = [NSException exceptionWithHTTPStatus: 403 - reason: @"the 'personal' folder cannot be deleted"]; + reason: @"folder 'personal' cannot be deleted"]; else error = [[self folderManager] deleteFolderAtPath: ocsPath]; return error; } +- (void) renameTo: (NSString *) newName +{ + GCSChannelManager *cm; + EOAdaptorChannel *fc; + NSURL *folderLocation; + NSString *sql; + + [displayName release]; + displayName = nil; + + cm = [GCSChannelManager defaultChannelManager]; + folderLocation + = [[GCSFolderManager defaultFolderManager] folderInfoLocation]; + fc = [cm acquireOpenChannelForURL: folderLocation]; + if (fc) + { + sql + = [NSString stringWithFormat: (@"UPDATE %@ SET c_foldername = '%@'" + @" WHERE c_path = '%@'"), + [folderLocation gcsTableName], newName, ocsPath]; + [fc evaluateExpressionX: sql]; + [cm releaseChannel: fc]; +// sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'", +// uidColumnName, [self uid]]; + } +} + - (NSArray *) fetchContentObjectNames { NSArray *fields, *records; @@ -512,7 +576,7 @@ static NSString *defaultUserID = @""; /* acls */ - (NSArray *) aclUsers { - return [self aclUsersForObjectAtPath: [self pathArrayToSoObject]]; + return [self aclUsersForObjectAtPath: [self pathArrayToSOGoObject]]; } - (NSArray *) aclsForUser: (NSString *) uid @@ -522,7 +586,7 @@ static NSString *defaultUserID = @""; acls = [NSMutableArray array]; ownAcls = [self aclsForUser: uid - forObjectAtPath: [self pathArrayToSoObject]]; + forObjectAtPath: [self pathArrayToSOGoObject]]; [acls addObjectsFromArray: ownAcls]; if ([container respondsToSelector: @selector (aclsForUser:)]) { @@ -545,13 +609,13 @@ static NSString *defaultUserID = @""; { return [self setRoles: roles forUser: uid - forObjectAtPath: [self pathArrayToSoObject]]; + forObjectAtPath: [self pathArrayToSOGoObject]]; } - (void) removeAclsForUsers: (NSArray *) users { return [self removeAclsForUsers: users - forObjectAtPath: [self pathArrayToSoObject]]; + forObjectAtPath: [self pathArrayToSOGoObject]]; } - (NSString *) defaultUserID diff --git a/SoObjects/SOGo/SOGoObject.h b/SoObjects/SOGo/SOGoObject.h index 67142bfef..5404508a0 100644 --- a/SoObjects/SOGo/SOGoObject.h +++ b/SoObjects/SOGo/SOGoObject.h @@ -61,6 +61,9 @@ id container; } ++ (NSString *) globallyUniqueObjectId; +- (NSString *) globallyUniqueObjectId; + + (id) objectWithName: (NSString *)_name inContainer:(id)_container; - (id) initWithName: (NSString *) _name inContainer:(id)_container; @@ -70,11 +73,15 @@ - (NSString *) nameInContainer; - (id) container; +- (NSArray *) pathArrayToSOGoObject; + - (NSURL *) davURL; - (NSURL *) soURL; - (NSURL *) soURLToBaseContainerForUser: (NSString *) uid; - (NSURL *) soURLToBaseContainerForCurrentUser; +- (NSString *) labelForKey: (NSString *) key; + /* ownership */ - (void) setOwner: (NSString *) newOwner; @@ -100,7 +107,7 @@ /* etag support */ -- (NSException *)matchesRequestConditionInContext:(id)_ctx; +- (NSException *) matchesRequestConditionInContext:(id)_ctx; /* acls */ diff --git a/SoObjects/SOGo/SOGoObject.m b/SoObjects/SOGo/SOGoObject.m index 280211e53..31a6b6ce6 100644 --- a/SoObjects/SOGo/SOGoObject.m +++ b/SoObjects/SOGo/SOGoObject.m @@ -24,6 +24,9 @@ Please use gnustep-base instead. #endif +#import + +#import #import #import #import @@ -35,6 +38,7 @@ #import #import #import +#import #import #import #import @@ -52,6 +56,7 @@ #import "SOGoDAVRendererTypes.h" #import "NSArray+Utilities.h" +#import "NSDictionary+Utilities.h" #import "NSString+Utilities.h" #import "SOGoObject.h" @@ -179,6 +184,35 @@ static BOOL kontactGroupDAV = YES; // asDefaultForPermission: SoPerm_WebDAVAccess]; } ++ (NSString *) globallyUniqueObjectId +{ + /* + 4C08AE1A-A808-11D8-AC5A-000393BBAFF6 + SOGo-Web-28273-18283-288182 + printf( "%x", *(int *) &f); + */ + static int pid = 0; + static int sequence = 0; + static float rndm = 0; + float f; + + if (pid == 0) + { /* break if we fork ;-) */ + pid = getpid(); + rndm = random(); + } + sequence++; + f = [[NSDate date] timeIntervalSince1970]; + + return [NSString stringWithFormat:@"%0X-%0X-%0X-%0X", + pid, (int) f, sequence++, random]; +} + +- (NSString *) globallyUniqueObjectId +{ + return [[self class] globallyUniqueObjectId]; +} + + (void) _fillDictionary: (NSMutableDictionary *) dictionary withDAVMethods: (NSString *) firstMethod, ... { @@ -447,6 +481,30 @@ static BOOL kontactGroupDAV = YES; return container; } +- (NSArray *) pathArrayToSOGoObject +{ + NSMutableArray *realPathArray; + NSString *objectName; + NSArray *objectDescription; + + realPathArray + = [NSMutableArray arrayWithArray: [self pathArrayToSoObject]]; + if ([realPathArray count] > 2) + { + objectName = [realPathArray objectAtIndex: 2]; + objectDescription = [objectName componentsSeparatedByString: @"_"]; + if ([objectDescription count] > 1) + { + [realPathArray replaceObjectAtIndex: 0 + withObject: [objectDescription objectAtIndex: 0]]; + [realPathArray replaceObjectAtIndex: 2 + withObject: [objectDescription objectAtIndex: 1]]; + } + } + + return realPathArray; +} + /* ownership */ - (void) setOwner: (NSString *) newOwner @@ -493,14 +551,16 @@ static BOOL kontactGroupDAV = YES; /* looking up shared objects */ -- (SOGoUserFolder *)lookupUserFolder { +- (SOGoUserFolder *) lookupUserFolder +{ if (![container respondsToSelector:_cmd]) return nil; return [container lookupUserFolder]; } -- (SOGoGroupsFolder *)lookupGroupsFolder { +- (SOGoGroupsFolder *) lookupGroupsFolder +{ return [[self lookupUserFolder] lookupGroupsFolder]; } @@ -905,6 +965,34 @@ static BOOL kontactGroupDAV = YES; return nil; } +- (NSString *) labelForKey: (NSString *) key +{ + NSString *userLanguage, *label; + NSArray *paths; + NSBundle *bundle; + NSDictionary *strings; + + bundle = [NSBundle bundleForClass: [self class]]; + if (!bundle) + bundle = [NSBundle mainBundle]; + + userLanguage = [[context activeUser] language]; + paths = [bundle pathsForResourcesOfType: @"strings" + inDirectory: [NSString stringWithFormat: @"%@.lproj", userLanguage] + forLocalization: userLanguage]; + if ([paths count] > 0) + { + strings = [NSDictionary dictionaryFromStringsFile: [paths objectAtIndex: 0]]; + label = [strings objectForKey: key]; + if (!label) + label = key; + } + else + label = key; + + return label; +} + /* description */ - (void)appendAttributesToDescription:(NSMutableString *)_ms { diff --git a/SoObjects/SOGo/SOGoParentFolder.h b/SoObjects/SOGo/SOGoParentFolder.h index 5bbde0bc6..c54d08f87 100644 --- a/SoObjects/SOGo/SOGoParentFolder.h +++ b/SoObjects/SOGo/SOGoParentFolder.h @@ -43,7 +43,8 @@ - (NSArray *) subFolders; -- (NSException *) newFolderWithName: (NSString *) name; +- (NSException *) newFolderWithName: (NSString *) name + nameInContainer: (NSString **) newNameInContainer; @end diff --git a/SoObjects/SOGo/SOGoParentFolder.m b/SoObjects/SOGo/SOGoParentFolder.m index 2bd1f6963..6bdf0f783 100644 --- a/SoObjects/SOGo/SOGoParentFolder.m +++ b/SoObjects/SOGo/SOGoParentFolder.m @@ -22,14 +22,17 @@ #import #import +#import #import +#import #import #import #import #import #import "SOGoFolder.h" +#import "SOGoUser.h" #import "SOGoParentFolder.h" @@ -73,6 +76,11 @@ ASSIGN (OCSPath, newOCSPath); } +- (NSString *) defaultFolderName +{ + return @"Personal"; +} + - (void) _fetchPersonalFolders: (NSString *) sql withChannel: (EOAdaptorChannel *) fc { @@ -80,7 +88,7 @@ NSDictionary *row; SOGoFolder *folder; BOOL hasPersonal; - NSString *key, *path; + NSString *key, *path, *personalName; if (!subFolderClass) subFolderClass = [[self class] subFolderClass]; @@ -92,8 +100,7 @@ while (row) { folder - = [subFolderClass folderWithName: [row objectForKey: @"c_path4"] - andDisplayName: [row objectForKey: @"c_foldername"] + = [subFolderClass objectWithName: [row objectForKey: @"c_path4"] inContainer: self]; key = [row objectForKey: @"c_path4"]; hasPersonal = (hasPersonal || [key isEqualToString: @"personal"]); @@ -105,9 +112,9 @@ if (!hasPersonal) { - folder = [subFolderClass folderWithName: @"personal" - andDisplayName: @"personal" - inContainer: self]; + folder = [subFolderClass objectWithName: @"personal" inContainer: self]; + personalName = [self labelForKey: [self defaultFolderName]]; + [folder setDisplayName: personalName]; path = [NSString stringWithFormat: @"/Users/%@/%@/personal", [self ownerInContext: context], nameInContainer]; @@ -132,7 +139,7 @@ gcsFolderType = [[self class] gcsFolderType]; sql - = [NSString stringWithFormat: (@"SELECT c_path4, c_foldername FROM %@" + = [NSString stringWithFormat: (@"SELECT c_path4 FROM %@" @" WHERE c_path2 = '%@'" @" AND c_folder_type = '%@'"), [folderLocation gcsTableName], @@ -149,25 +156,58 @@ { } -- (NSException *) newFolderWithName: (NSString *) name +- (void) appendSubscribedSources { + NSArray *subscribedReferences; + NSUserDefaults *settings; + NSEnumerator *allKeys; + NSString *currentKey; + SOGoFolder *subscribedFolder; + + settings = [[context activeUser] userSettings]; + subscribedReferences = [[settings objectForKey: nameInContainer] + objectForKey: @"SubscribedFolders"]; + if ([subscribedReferences isKindOfClass: [NSArray class]]) + { + allKeys = [subscribedReferences objectEnumerator]; + currentKey = [allKeys nextObject]; + while (currentKey) + { + subscribedFolder + = [subFolderClass folderWithSubscriptionReference: currentKey + inContainer: self]; + [subFolders setObject: subscribedFolder + forKey: [subscribedFolder nameInContainer]]; + currentKey = [allKeys nextObject]; + } + } +} + +- (NSException *) newFolderWithName: (NSString *) name + nameInContainer: (NSString **) newNameInContainer +{ + NSString *newFolderID; SOGoFolder *newFolder; NSException *error; if (!subFolderClass) subFolderClass = [[self class] subFolderClass]; - newFolder = [subFolderClass folderWithName: name - andDisplayName: name - inContainer: self]; + *newNameInContainer = nil; + newFolderID = [self globallyUniqueObjectId]; + newFolder = [subFolderClass objectWithName: newFolderID inContainer: self]; if ([newFolder isKindOfClass: [NSException class]]) error = (NSException *) newFolder; else { + [newFolder setDisplayName: name]; [newFolder setOCSPath: [NSString stringWithFormat: @"%@/%@", - OCSPath, name]]; + OCSPath, newFolderID]]; if ([newFolder create]) - error = nil; + { + error = nil; + *newNameInContainer = newFolderID; + } else error = [NSException exceptionWithHTTPStatus: 400 reason: @"The new folder could not be created"]; @@ -178,11 +218,16 @@ - (void) initSubFolders { + NSString *login; + if (!subFolders) { subFolders = [NSMutableDictionary new]; [self appendPersonalSources]; [self appendSystemSources]; + login = [[context activeUser] login]; + if ([login isEqualToString: owner]) + [self appendSubscribedSources]; } } diff --git a/SoObjects/SOGo/SOGoUserFolder.m b/SoObjects/SOGo/SOGoUserFolder.m index 10194d27a..84297af23 100644 --- a/SoObjects/SOGo/SOGoUserFolder.m +++ b/SoObjects/SOGo/SOGoUserFolder.m @@ -26,7 +26,7 @@ #import #import -#import +#import #import #import #import @@ -117,7 +117,7 @@ - (NSString *) ocsPrivateCalendarPath { - return [[self ocsUserPath] stringByAppendingString:@"/Calendar/personal"]; + return [[self ocsUserPath] stringByAppendingString:@"/Calendar"]; } - (NSString *) ocsPrivateContactsPath @@ -134,15 +134,15 @@ // : [super permissionForKey: key]); // } -- (SOGoAppointmentFolder *) privateCalendar: (NSString *) _key - inContext: (WOContext *) _ctx +- (SOGoAppointmentFolders *) privateCalendars: (NSString *) _key + inContext: (WOContext *) _ctx { - SOGoAppointmentFolder *calendar; + SOGoAppointmentFolders *calendars; - calendar = [$(@"SOGoAppointmentFolder") objectWithName: _key inContainer: self]; - [calendar setOCSPath: [self ocsPrivateCalendarPath]]; + calendars = [$(@"SOGoAppointmentFolders") objectWithName: _key inContainer: self]; + [calendars setBaseOCSPath: [self ocsPrivateCalendarPath]]; - return calendar; + return calendars; } - (SOGoContactFolders *) privateContacts: (NSString *) _key @@ -185,7 +185,7 @@ if (!obj) { if ([_key isEqualToString: @"Calendar"]) - obj = [self privateCalendar: @"Calendar" inContext: _ctx]; + obj = [self privateCalendars: @"Calendar" inContext: _ctx]; // if (![_key isEqualToString: @"Calendar"]) // obj = [obj lookupName: [_key pathExtension] // inContext: _ctx acquire: NO]; diff --git a/UI/Common/English.lproj/Localizable.strings b/UI/Common/English.lproj/Localizable.strings index 9af10007e..9f20244c8 100644 --- a/UI/Common/English.lproj/Localizable.strings +++ b/UI/Common/English.lproj/Localizable.strings @@ -32,3 +32,4 @@ "You cannot subscribe to a folder that you own!" = "You cannot subscribe to a folder that you own!"; "Unable to unsubscribe from that folder!" = "Unable to unsubscribe from that folder!"; "You cannot unsubscribe from a folder that you own!" = "You cannot unsubscribe from a folder that you own!"; +"Unable to rename that folder!" = "Unable to rename that folder!"; diff --git a/UI/Common/French.lproj/Localizable.strings b/UI/Common/French.lproj/Localizable.strings index 18a984d3b..e77de6d77 100644 --- a/UI/Common/French.lproj/Localizable.strings +++ b/UI/Common/French.lproj/Localizable.strings @@ -33,3 +33,4 @@ "You cannot subscribe to a folder that you own!" = "Impossible de vous abonner à un dossier qui vous appartient."; "Unable to unsubscribe from that folder!" = "Impossible de se désabonner de ce dossier."; "You cannot unsubscribe from a folder that you own!" = "Impossible de vous désabonner d'un dossier qui vous appartient."; +"Unable to rename that folder!" = "Impossible de renommer ce dossier."; diff --git a/UI/Common/GNUmakefile b/UI/Common/GNUmakefile index ebdc4fea9..80a1a5943 100644 --- a/UI/Common/GNUmakefile +++ b/UI/Common/GNUmakefile @@ -17,6 +17,7 @@ CommonUI_OBJC_FILES += \ UIxAclEditor.m \ UIxObjectActions.m \ UIxFolderActions.m \ + UIxParentFolderActions.m \ UIxElemBuilder.m \ UIxTabView.m \ UIxTabItem.m \ diff --git a/UI/Common/German.lproj/Localizable.strings b/UI/Common/German.lproj/Localizable.strings index d1b779ba6..9a0bb6969 100644 --- a/UI/Common/German.lproj/Localizable.strings +++ b/UI/Common/German.lproj/Localizable.strings @@ -35,3 +35,4 @@ "You cannot subscribe to a folder that you own!" = "Unmöglich sich an einem Ordner zu abonnieren, der Ihnen selbst gehört."; "Unable to unsubscribe from that folder!" = "Unmöglich sich von diesem Ordner zu des-abonnieren."; "You cannot unsubscribe from a folder that you own!" = "Unmöglich sich von einem Ordner zu des-abonnieren, der Ihnen selbst gehört."; +"Unable to rename that folder!" = "Unable to rename that folder!"; diff --git a/UI/Common/UIxFolderActions.h b/UI/Common/UIxFolderActions.h index b74a20a30..8794bbeab 100644 --- a/UI/Common/UIxFolderActions.h +++ b/UI/Common/UIxFolderActions.h @@ -41,7 +41,7 @@ NSString *owner; NSString *login; NSString *baseFolder; - NSMutableString *subscriptionPointer; + NSString *subscriptionPointer; NSMutableDictionary *moduleSettings; BOOL isMailInvitation; } diff --git a/UI/Common/UIxFolderActions.m b/UI/Common/UIxFolderActions.m index b7a469a00..e152ed335 100644 --- a/UI/Common/UIxFolderActions.m +++ b/UI/Common/UIxFolderActions.m @@ -33,8 +33,9 @@ #import #import +#import #import -#import +#import #import #import "WODirectAction+SOGo.h" @@ -74,21 +75,19 @@ } [ud setObject: moduleSettings forKey: baseFolder]; - subscriptionPointer = [NSMutableString stringWithFormat: @"%@:%@", - owner, baseFolder]; - if ([baseFolder isEqualToString: @"Contacts"]) - [subscriptionPointer appendFormat: @"/%@", - [clientObject nameInContainer]]; + subscriptionPointer = [NSString stringWithFormat: @"%@:%@/%@", + owner, baseFolder, + [clientObject nameInContainer]]; mailInvitationParam = [[context request] formValueForKey: @"mail-invitation"]; isMailInvitation = [mailInvitationParam boolValue]; } -- (WOResponse *) _realActionWithFolderName: (NSDictionary *) folderDict +- (WOResponse *) _realSubscribe: (BOOL) reallyDo { WOResponse *response; - NSMutableDictionary *folderSubscription; + NSMutableArray *folderSubscription; NSString *mailInvitationURL; if ([owner isEqualToString: login]) @@ -101,17 +100,17 @@ { folderSubscription = [moduleSettings objectForKey: @"SubscribedFolders"]; - if (!folderSubscription) + if (!(folderSubscription + && [folderSubscription isKindOfClass: [NSMutableArray class]])) { - folderSubscription = [NSMutableDictionary dictionary]; + folderSubscription = [NSMutableArray array]; [moduleSettings setObject: folderSubscription forKey: @"SubscribedFolders"]; } - if (folderDict) - [folderSubscription setObject: folderDict - forKey: subscriptionPointer]; + if (reallyDo) + [folderSubscription addObjectUniquely: subscriptionPointer]; else - [folderSubscription removeObjectForKey: subscriptionPointer]; + [folderSubscription removeObject: subscriptionPointer]; [ud synchronize]; @@ -133,32 +132,16 @@ - (WOResponse *) subscribeAction { - NSString *email; - NSMutableDictionary *folderDict; - NSString *folderName; - [self _setupContext]; - email = [NSString stringWithFormat: @"%@ <%@>", - [um getCNForUID: owner], - [um getEmailForUID: owner]]; - if ([baseFolder isEqualToString: @"Contacts"]) - folderName = [NSString stringWithFormat: @"%@ (%@)", - [clientObject nameInContainer], email]; - else - folderName = email; - folderDict = [NSMutableDictionary dictionary]; - [folderDict setObject: folderName forKey: @"displayName"]; - [folderDict setObject: [NSNumber numberWithBool: NO] forKey: @"active"]; - - return [self _realActionWithFolderName: folderDict]; + return [self _realSubscribe: YES]; } - (WOResponse *) unsubscribeAction { [self _setupContext]; - return [self _realActionWithFolderName: nil]; + return [self _realSubscribe: NO]; } - (WOResponse *) canAccessContentAction @@ -168,26 +151,24 @@ - (WOResponse *) _realFolderActivation: (BOOL) makeActive { - NSMutableDictionary *folderSubscription, *folderDict; - NSNumber *active; - + NSMutableArray *folderSubscription; + NSString *folderName; + [self _setupContext]; - active = [NSNumber numberWithBool: makeActive]; - if ([owner isEqualToString: login]) - [moduleSettings setObject: active forKey: @"activateUserFolder"]; - else + folderSubscription + = [moduleSettings objectForKey: @"ActiveFolders"]; + if (!folderSubscription) { - folderSubscription - = [moduleSettings objectForKey: @"SubscribedFolders"]; - if (folderSubscription) - { - folderDict = [folderSubscription objectForKey: subscriptionPointer]; - if (folderDict) - [folderDict setObject: active - forKey: @"active"]; - } + folderSubscription = [NSMutableArray array]; + [moduleSettings setObject: folderSubscription forKey: @"ActiveFolders"]; } + folderName = [clientObject nameInContainer]; + if (makeActive) + [folderSubscription addObjectUniquely: folderName]; + else + [folderSubscription removeObject: folderName]; + [ud synchronize]; return [self responseWith204]; @@ -203,4 +184,36 @@ return [self _realFolderActivation: NO]; } +- (WOResponse *) deleteFolderAction +{ + WOResponse *response; + + response = (WOResponse *) [[self clientObject] delete]; + if (!response) + response = [self responseWith204]; + + return response; +} + +- (WOResponse *) renameFolderAction +{ + WOResponse *response; + NSString *folderName; + + folderName = [[context request] formValueForKey: @"name"]; + if ([folderName length] > 0) + { + clientObject = [self clientObject]; + [clientObject renameTo: folderName]; + response = [self responseWith204]; + } + else + { + response = [self responseWithStatus: 500]; + [response appendContentString: @"Missing 'name' parameter."]; + } + + return response; +} + @end diff --git a/UI/Common/product.plist b/UI/Common/product.plist index 9e014e8e9..704880c10 100644 --- a/UI/Common/product.plist +++ b/UI/Common/product.plist @@ -1,95 +1,114 @@ -{ /* -*-javascript-*- */ - requires = ( MAIN, Mailer ); +{ /* -*-java-*- */ + requires = ( MAIN, Mailer ); + + publicResources = ( + calendar.css, + uix.css, + menu_logo_top.gif, + line_left.gif, + line_stretch.gif, + line_right.gif, + box_topleft.gif, + box_top.gif, + box_topright.gif, + box_left.gif, + box_right.gif, + box_botleft.gif, + box_bottom.gif, + box_botright.gif, + tab_selected.gif, + tab_.gif, + corner_right.gif, + closewindow.gif, + OGoLogo.gif, + upward_sorted.gif, + downward_sorted.gif, + non_sorted.gif + ); - publicResources = ( - calendar.css, - uix.css, - menu_logo_top.gif, - line_left.gif, - line_stretch.gif, - line_right.gif, - box_topleft.gif, - box_top.gif, - box_topright.gif, - box_left.gif, - box_right.gif, - box_botleft.gif, - box_bottom.gif, - box_botright.gif, - tab_selected.gif, - tab_.gif, - corner_right.gif, - closewindow.gif, - OGoLogo.gif, - upward_sorted.gif, - downward_sorted.gif, - non_sorted.gif - ); + factories = { + }; - factories = { - }; - - categories = { - SOGoObject = { - methods = { - addUserInAcls = { - protectedBy = "SaveAcls"; - actionClass = "UIxObjectActions"; - actionName = "addUserInAcls"; - }; - removeUserFromAcls = { - protectedBy = "SaveAcls"; - actionClass = "UIxObjectActions"; - actionName = "removeUserFromAcls"; - }; - acls = { - protectedBy = "ReadAcls"; - pageName = "UIxAclEditor"; - }; - saveAcls = { - protectedBy = "SaveAcls"; - pageName = "UIxAclEditor"; - actionName = "saveAcls"; - }; - userRights = { - protectedBy = "ReadAcls"; - pageName = "UIxUserRightsEditor"; - }; - saveUserRights = { - protectedBy = "ReadAcls"; - pageName = "UIxUserRightsEditor"; - actionName = "saveUserRights"; - }; - }; + categories = { + SOGoObject = { + methods = { + addUserInAcls = { + protectedBy = "SaveAcls"; + actionClass = "UIxObjectActions"; + actionName = "addUserInAcls"; + }; + removeUserFromAcls = { + protectedBy = "SaveAcls"; + actionClass = "UIxObjectActions"; + actionName = "removeUserFromAcls"; + }; + acls = { + protectedBy = "ReadAcls"; + pageName = "UIxAclEditor"; + }; + saveAcls = { + protectedBy = "SaveAcls"; + pageName = "UIxAclEditor"; + actionName = "saveAcls"; + }; + userRights = { + protectedBy = "ReadAcls"; + pageName = "UIxUserRightsEditor"; + }; + saveUserRights = { + protectedBy = "ReadAcls"; + pageName = "UIxUserRightsEditor"; + actionName = "saveUserRights"; + }; }; - SOGoFolder = { - methods = { - subscribe = { - protectedBy = ""; - actionClass = "UIxFolderActions"; - actionName = "subscribe"; - }; - unsubscribe = { - protectedBy = ""; - actionClass = "UIxFolderActions"; - actionName = "unsubscribe"; - }; - canAccessContent = { - protectedBy = ""; - actionClass = "UIxFolderActions"; - actionName = "canAccessContent"; - }; - activateFolder = { - protectedBy = ""; - actionClass = "UIxFolderActions"; - actionName = "activateFolder"; - }; - deactivateFolder = { - protectedBy = ""; - actionClass = "UIxFolderActions"; - actionName = "deactivateFolder"; - }; - }; + }; + SOGoParentFolder = { + methods = { + createFolder = { + protectedBy = "View"; + actionClass = "UIxParentFolderActions"; + actionName = "createFolder"; + }; }; - }; + }; + SOGoFolder = { + methods = { + subscribe = { + protectedBy = ""; + actionClass = "UIxFolderActions"; + actionName = "subscribe"; + }; + unsubscribe = { + protectedBy = ""; + actionClass = "UIxFolderActions"; + actionName = "unsubscribe"; + }; + canAccessContent = { + protectedBy = ""; + actionClass = "UIxFolderActions"; + actionName = "canAccessContent"; + }; + activateFolder = { + protectedBy = ""; + actionClass = "UIxFolderActions"; + actionName = "activateFolder"; + }; + deactivateFolder = { + protectedBy = ""; + actionClass = "UIxFolderActions"; + actionName = "deactivateFolder"; + }; + deleteFolder = { + protectedBy = "SaveAcls"; /* a hack to force "owner" */ + actionClass = "UIxFolderActions"; + actionName = "deleteFolder"; + }; + renameFolder = { + protectedBy = "SaveAcls"; + actionClass = "UIxFolderActions"; + actionName = "renameFolder"; + }; + }; + }; + }; } diff --git a/UI/Contacts/English.lproj/Localizable.strings b/UI/Contacts/English.lproj/Localizable.strings index bb57947f9..08044be83 100644 --- a/UI/Contacts/English.lproj/Localizable.strings +++ b/UI/Contacts/English.lproj/Localizable.strings @@ -105,6 +105,8 @@ "Are you sure you want to delete the selected address book?" = "Are you sure you want to delete the selected address book?"; +"Address Book Name" = "Address Book Name"; + "You cannot subscribe to a folder that you own!" = "You cannot subscribe to a folder that you own!"; "Unable to subscribe to that folder!" @@ -125,4 +127,4 @@ = "This person can erase cards from this addressbook."; "The selected contact has no email address." -= "The selected contact has no email address."; \ No newline at end of file += "The selected contact has no email address."; diff --git a/UI/Contacts/French.lproj/Localizable.strings b/UI/Contacts/French.lproj/Localizable.strings index a90f3cd38..ca2f8aa2e 100644 --- a/UI/Contacts/French.lproj/Localizable.strings +++ b/UI/Contacts/French.lproj/Localizable.strings @@ -118,6 +118,8 @@ "Are you sure you want to delete the selected address book?" = "Voulez-vous vraiment supprimer le carnet d'adresses sélectionné ?"; +"Address Book Name" = "Nom du carnet d'adresses"; + "You cannot subscribe to a folder that you own!" = "Vous ne pouvez pas vous inscrire à un dossier qui vous appartient!"; "Unable to subscribe to that folder!" @@ -138,4 +140,4 @@ = "Cette personne peut effacer des fiches de ce carnet d'adresses."; "The selected contact has no email address." -= "Cette personne n'a pas d'adresse courriel." \ No newline at end of file += "Cette personne n'a pas d'adresse courriel." diff --git a/UI/Contacts/German.lproj/Localizable.strings b/UI/Contacts/German.lproj/Localizable.strings index 9f4500abb..19bd4387e 100644 --- a/UI/Contacts/German.lproj/Localizable.strings +++ b/UI/Contacts/German.lproj/Localizable.strings @@ -109,6 +109,8 @@ "Are you sure you want to delete the selected address book?" = "Voulez-vous vraiment supprimer le carnet d'adresses sélectionné ?"; +"Address Book Name" = "Address Book Name"; + "You cannot subscribe to a folder that you own!" = "Vous ne pouvez pas vous inscrire à un dossier qui vous appartient!"; "Unable to subscribe to that folder!" diff --git a/UI/Contacts/UIxContactEditor.m b/UI/Contacts/UIxContactEditor.m index 578c65ce3..6f3954112 100644 --- a/UI/Contacts/UIxContactEditor.m +++ b/UI/Contacts/UIxContactEditor.m @@ -584,8 +584,8 @@ id co; co = [self clientObject]; - if ([[co class] respondsToSelector: @selector (globallyUniqueObjectId)]) - objectId = [[[self clientObject] class] globallyUniqueObjectId]; + if ([co respondsToSelector: @selector (globallyUniqueObjectId)]) + objectId = [co globallyUniqueObjectId]; else objectId = nil; diff --git a/UI/Contacts/UIxContactFoldersView.m b/UI/Contacts/UIxContactFoldersView.m index fa47cb91e..fa43d8109 100644 --- a/UI/Contacts/UIxContactFoldersView.m +++ b/UI/Contacts/UIxContactFoldersView.m @@ -52,8 +52,7 @@ WORequest *request; folders = [self clientObject]; - action = [NSString stringWithFormat: @"../personal/%@", - actionName]; + action = [NSString stringWithFormat: @"../personal/%@", actionName]; request = [[self context] request]; @@ -74,25 +73,6 @@ return [self _selectActionForApplication: @"new"]; } -- (id ) newAbAction -{ - id response; - NSString *name; - - name = [self queryParameterForKey: @"name"]; - if ([name length] > 0) - { - response = [[self clientObject] newFolderWithName: name]; - if (!response) - response = [self responseWith204]; - } - else - response = [NSException exceptionWithHTTPStatus: 400 - reason: @"The name is missing"]; - - return response; -} - - (id) selectForMailerAction { return [self _selectActionForApplication: @"mailer-contacts"]; @@ -193,50 +173,55 @@ return result; } -- (NSArray *) _gcsFoldersFromFolder: (SOGoContactFolders *) contactFolders +- (NSArray *) _subFoldersFromFolder: (SOGoParentFolder *) parentFolder { - NSMutableArray *gcsFolders; - NSEnumerator *contactSubfolders; - SOGoContactGCSFolder *currentContactFolder; - NSString *folderName, *displayName; + NSMutableArray *folders; + NSEnumerator *subfolders; + SOGoFolder *currentFolder; + NSString *folderName; NSMutableDictionary *currentDictionary; + SoSecurityManager *securityManager; - gcsFolders = [NSMutableArray new]; - [gcsFolders autorelease]; + securityManager = [SoSecurityManager sharedSecurityManager]; + +// return (([securityManager validatePermission: SoPerm_AccessContentsInformation +// onObject: contactFolder +// inContext: context] == nil) - contactSubfolders = [[contactFolders subFolders] objectEnumerator]; - currentContactFolder = [contactSubfolders nextObject]; - while (currentContactFolder) + folders = [NSMutableArray new]; + [folders autorelease]; + + subfolders = [[parentFolder subFolders] objectEnumerator]; + currentFolder = [subfolders nextObject]; + while (currentFolder) { - if ([currentContactFolder - isKindOfClass: [SOGoContactGCSFolder class]]) + if (![securityManager validatePermission: SOGoPerm_AccessObject + onObject: currentFolder inContext: context]) { - displayName = [[currentContactFolder ocsFolder] folderName]; - if (displayName) - { - folderName = [NSString stringWithFormat: @"/Contacts/%@", - [currentContactFolder nameInContainer]]; - currentDictionary - = [NSMutableDictionary dictionaryWithCapacity: 3]; - [currentDictionary setObject: displayName forKey: @"displayName"]; - [currentDictionary setObject: folderName forKey: @"name"]; - [currentDictionary setObject: @"contact" forKey: @"type"]; - [gcsFolders addObject: currentDictionary]; - } + folderName = [NSString stringWithFormat: @"/%@/%@", + [parentFolder nameInContainer], + [currentFolder nameInContainer]]; + currentDictionary + = [NSMutableDictionary dictionaryWithCapacity: 3]; + [currentDictionary setObject: [currentFolder displayName] + forKey: @"displayName"]; + [currentDictionary setObject: folderName forKey: @"name"]; + [currentDictionary setObject: [currentFolder folderType] + forKey: @"type"]; + [folders addObject: currentDictionary]; } - currentContactFolder = [contactSubfolders nextObject]; + currentFolder = [subfolders nextObject]; } - return gcsFolders; + return folders; } - (NSArray *) _foldersForUID: (NSString *) uid ofType: (NSString *) folderType { NSObject *topFolder, *userFolder; - SOGoContactFolders *contactFolders; + SOGoParentFolder *parentFolder; NSMutableArray *folders; - NSMutableDictionary *currentDictionary; folders = [NSMutableArray new]; [folders autorelease]; @@ -245,23 +230,19 @@ userFolder = [topFolder lookupName: uid inContext: context acquire: NO]; /* FIXME: should be moved in the SOGo* classes. Maybe by having a SOGoFolderManager. */ -#warning this might need adjustments whenever we permit multiple calendar folders per-user if ([folderType length] == 0 || [folderType isEqualToString: @"calendar"]) { - currentDictionary = [NSMutableDictionary new]; - [currentDictionary autorelease]; - [currentDictionary setObject: [self labelForKey: @"Calendar"] - forKey: @"displayName"]; - [currentDictionary setObject: @"/Calendar" forKey: @"name"]; - [currentDictionary setObject: @"calendar" forKey: @"type"]; - [folders addObject: currentDictionary]; + parentFolder = [userFolder lookupName: @"Calendar" + inContext: context acquire: NO]; + [folders + addObjectsFromArray: [self _subFoldersFromFolder: parentFolder]]; } if ([folderType length] == 0 || [folderType isEqualToString: @"contact"]) { - contactFolders = [userFolder lookupName: @"Contacts" - inContext: context acquire: NO]; + parentFolder = [userFolder lookupName: @"Contacts" + inContext: context acquire: NO]; [folders - addObjectsFromArray: [self _gcsFoldersFromFolder: contactFolders]]; + addObjectsFromArray: [self _subFoldersFromFolder: parentFolder]]; } return folders; @@ -352,31 +333,30 @@ return result; } -- (SOGoContactGCSFolder *) contactFolderForUID: (NSString *) uid -{ - SOGoFolder *upperContainer; - SOGoUserFolder *userFolder; - SOGoContactFolders *contactFolders; - SOGoContactGCSFolder *contactFolder; - SoSecurityManager *securityManager; +// - (SOGoContactGCSFolder *) contactFolderForUID: (NSString *) uid +// { +// SOGoFolder *upperContainer; +// SOGoUserFolder *userFolder; +// SOGoContactFolders *contactFolders; +// SOGoContactGCSFolder *contactFolder; +// SoSecurityManager *securityManager; - upperContainer = [[[self clientObject] container] container]; - userFolder = [SOGoUserFolder objectWithName: uid - inContainer: upperContainer]; - contactFolders = [SOGoContactFolders objectWithName: @"Contacts" - inContainer: userFolder]; - contactFolder = [SOGoContactGCSFolder objectWithName: @"personal" - inContainer: contactFolders]; - [contactFolder - setOCSPath: [NSString stringWithFormat: @"/Users/%@/Contacts/personal", uid]]; - [contactFolder setOwner: uid]; +// upperContainer = [[[self clientObject] container] container]; +// userFolder = [SOGoUserFolder objectWithName: uid +// inContainer: upperContainer]; +// contactFolders = [SOGoUserFolder lookupName: @"Contacts" +// inContext: context +// acquire: NO]; +// contactFolder = [contactFolders lookupName: @"personal" +// inContext: context +// acquire: NO]; - securityManager = [SoSecurityManager sharedSecurityManager]; +// securityManager = [SoSecurityManager sharedSecurityManager]; - return (([securityManager validatePermission: SoPerm_AccessContentsInformation - onObject: contactFolder - inContext: context] == nil) - ? contactFolder : nil); -} +// return (([securityManager validatePermission: SoPerm_AccessContentsInformation +// onObject: contactFolder +// inContext: context] == nil) +// ? contactFolder : nil); +// } @end diff --git a/UI/Contacts/UIxContactsListView.m b/UI/Contacts/UIxContactsListView.m index 45dadfa62..e6b2e2ad1 100644 --- a/UI/Contacts/UIxContactsListView.m +++ b/UI/Contacts/UIxContactsListView.m @@ -81,25 +81,6 @@ return selectorComponentClass; } -- (id ) deleteAction -{ - id result; - NSException *ex; - WOResponse *response; - - ex = [[self clientObject] delete]; - if (ex) - result = ex; - else - { - response = [context response]; - [response setStatus: 200]; - result = response; - } - - return result; -} - - (NSString *) defaultSortKey { return @"displayName"; diff --git a/UI/Contacts/UIxContactsListViewContainer.h b/UI/Contacts/UIxContactsListViewContainer.h index 5be9899fd..eb0d72d87 100644 --- a/UI/Contacts/UIxContactsListViewContainer.h +++ b/UI/Contacts/UIxContactsListViewContainer.h @@ -31,28 +31,18 @@ @interface UIxContactsListViewContainer : UIxComponent { - NSString *foldersPrefix; NSString *selectorComponentClass; - NSString *currentAdditionalFolder; - NSDictionary *additionalFolders; id currentFolder; } - (void) setCurrentFolder: (id) folder; -- (NSString *) foldersPrefix; - - (NSArray *) contactFolders; - (NSString *) currentContactFolderId; +- (NSString *) currentContactFolderOwner; - (NSString *) currentContactFolderName; -- (NSArray *) additionalFolders; - -- (void) setCurrentAdditionalFolder: (NSString *) newCurrentAdditionalFolder; -- (NSString *) currentAdditionalFolder; -- (NSString *) currentAdditionalFolderName; - @end #endif /* UIXCONTACTSLISTVIEWCONTAINERBASE_H */ diff --git a/UI/Contacts/UIxContactsListViewContainer.m b/UI/Contacts/UIxContactsListViewContainer.m index 5aab0c6d7..f472b1560 100644 --- a/UI/Contacts/UIxContactsListViewContainer.m +++ b/UI/Contacts/UIxContactsListViewContainer.m @@ -41,20 +41,12 @@ { if ((self = [super init])) { - foldersPrefix = nil; selectorComponentClass = nil; - additionalFolders = nil; } return self; } -- (void) dealloc -{ - [additionalFolders release]; - [super dealloc]; -} - - (void) setSelectorComponentClass: (NSString *) aComponentClass { selectorComponentClass = aComponentClass; @@ -86,30 +78,6 @@ currentFolder = folder; } -- (NSString *) foldersPrefix -{ - NSMutableArray *folders; - SOGoObject *currentObject; - - if (!foldersPrefix) - { - folders = [NSMutableArray new]; - [folders autorelease]; - - currentObject = [[self clientObject] container]; - while (![currentObject isKindOfClass: [SOGoContactFolders class]]) - { - [folders insertObject: [currentObject nameInContainer] atIndex: 0]; - currentObject = [currentObject container]; - } - - foldersPrefix = [folders componentsJoinedByString: @"/"]; - [foldersPrefix retain]; - } - - return foldersPrefix; -} - - (NSArray *) contactFolders { SOGoContactFolders *folderContainer; @@ -121,8 +89,7 @@ - (NSString *) currentContactFolderId { - return [NSString stringWithFormat: @"%@/%@", - [self foldersPrefix], + return [NSString stringWithFormat: @"/%@", [currentFolder nameInContainer]]; } @@ -131,35 +98,9 @@ return [currentFolder displayName]; } -- (NSArray *) additionalFolders +- (NSString *) currentContactFolderOwner { - NSUserDefaults *ud; - - if (!additionalFolders) - { - ud = [[context activeUser] userSettings]; - additionalFolders - = [[ud objectForKey: @"Contacts"] objectForKey: @"SubscribedFolders"]; - [additionalFolders retain]; - } - - return [additionalFolders allKeys]; -} - -- (void) setCurrentAdditionalFolder: (NSString *) newCurrentAdditionalFolder -{ - currentAdditionalFolder = newCurrentAdditionalFolder; -} - -- (NSString *) currentAdditionalFolder -{ - return currentAdditionalFolder; -} - -- (NSString *) currentAdditionalFolderName -{ - return [[additionalFolders objectForKey: currentAdditionalFolder] - objectForKey: @"displayName"]; + return [currentFolder ownerInContext: context]; } - (BOOL) hasContactSelectionButtons diff --git a/UI/Contacts/product.plist b/UI/Contacts/product.plist index cff8e800d..3602be7ae 100644 --- a/UI/Contacts/product.plist +++ b/UI/Contacts/product.plist @@ -17,11 +17,6 @@ pageName = "UIxContactFoldersView"; actionName = "new"; }; - newAb = { - protectedBy = "View"; - pageName = "UIxContactFoldersView"; - actionName = "newAb"; - }; mailer-contacts = { protectedBy = "View"; pageName = "UIxContactFoldersView"; @@ -87,11 +82,6 @@ pageName = "UIxContactsListView"; actionName = "mailerContacts"; }; - delete = { - protectedBy = "SaveAcls"; /* a hack to force "owner" */ - pageName = "UIxContactsListView"; - actionName = "delete"; - }; userRights = { protectedBy = "ReadAcls"; pageName = "UIxContactsUserRightsEditor"; diff --git a/UI/MainUI/SOGoRootPage.m b/UI/MainUI/SOGoRootPage.m index d4bdc2070..9749d13b5 100644 --- a/UI/MainUI/SOGoRootPage.m +++ b/UI/MainUI/SOGoRootPage.m @@ -73,7 +73,7 @@ cookieString = [NSString stringWithFormat: @"%@:%@", [self queryParameterForKey: @"userName"], [self queryParameterForKey: @"password"]]; - cookieValue = [NSString stringWithFormat: @"basic%@", + cookieValue = [NSString stringWithFormat: @"basic %@", [cookieString stringByEncodingBase64]]; authCookie = [WOCookie cookieWithName: [auth cookieNameInContext: context] value: cookieValue]; @@ -83,88 +83,24 @@ return response; } -// - (id ) defaultAction -// { -// WOResponse *r; -// NSString *login, *rhk; -// SOGoWebAuthenticator *auth; -// SOGoUser *user; -// SOGoUserFolder *home; -// WOApplication *base; +- (id ) defaultAction +{ + id response; + NSString *login, *oldLocation; -// /* -// Note: ctx.activeUser is NOT set here. Don't know why, so we retrieve -// the user from the authenticator. -// */ - -// auth = [[self clientObject] authenticatorInContext: context]; -// user = [auth userInContext: context]; -// login = [user login]; + login = [[context activeUser] login]; + if ([login isEqualToString: @"anonymous"]) + response = self; + else + { + oldLocation = [[self clientObject] baseURLInContext: context]; + response + = [self redirectToLocation: [NSString stringWithFormat: @"%@/%@", + oldLocation, login]]; + } -// if ([login isEqualToString:@"anonymous"]) { -// /* use root page for unauthenticated users */ -// return self; -// } - -// /* check base */ - -// base = [self application]; -// rhk = [[context request] requestHandlerKey]; -// if (([rhk length] == 0) || ([base requestHandlerForKey:rhk] == nil)) { -// base = [base lookupName: @"so" inContext: context acquire: NO]; - -// if (![base isNotNull] || [base isKindOfClass:[NSException class]]) { -// /* use root page if home could not be found */ -// [self errorWithFormat:@"Did not find 'so' request handler!"]; -// return self; -// } -// } - -// /* lookup home-page */ - -// home = [base lookupName: login inContext: context acquire: NO]; -// if (![home isNotNull] || [home isKindOfClass:[NSException class]]) { -// /* use root page if home could not be found */ -// return self; -// } - -// /* redirect to home-page */ - -// r = [context response]; -// [r setStatus: 302 /* moved */]; -// [r setHeader: [home baseURLInContext: context] -// forKey: @"location"]; - -// return r; -// } - -/* response generation */ - -// - (void) appendToResponse: (WOResponse *) response -// inContext: (WOContext *) ctx -// { -// NSString *rhk; - -// // TODO: we might also want to look into the HTTP basic-auth to redirect to -// // the login URL! - -// rhk = [[ctx request] requestHandlerKey]; -// if ([rhk length] == 0 -// || [[self application] requestHandlerForKey: rhk] == nil) -// { -// /* a small hack to redirect to a valid URL */ -// NSString *url; - -// url = [ctx urlWithRequestHandlerKey: @"so" path: @"/" queryString: nil]; -// [response setStatus: 302 /* moved */]; -// [response setHeader: url forKey: @"location"]; -// [self logWithFormat: @"URL: %@", url]; -// return; -// } - -// [response setHeader: @"text/html" forKey: @"content-type"]; -// [super appendToResponse: response inContext: ctx]; -// } + return response; +} - (BOOL) isPublicInContext: (WOContext *) localContext { diff --git a/UI/MainUI/product.plist b/UI/MainUI/product.plist index c3ebb0993..11e282820 100644 --- a/UI/MainUI/product.plist +++ b/UI/MainUI/product.plist @@ -46,6 +46,14 @@ "Access Contents Information" = ( "Owner", "ObjectViewer" ); }; }; + SOGoParentFolder = { + superclass = "SOGoObject"; + protectedBy = "Access Contents Information"; + defaultRoles = { + "Access Contents Information" = ( "Authenticated" ); + "WebDAV Access" = ( "Authenticated" ); + }; + }; SOGoUserFolder = { superclass = "SOGoFolder"; protectedBy = "Access Contents Information"; diff --git a/UI/PreferencesUI/UIxPreferences.m b/UI/PreferencesUI/UIxPreferences.m index 4131db0a5..7259f60d2 100644 --- a/UI/PreferencesUI/UIxPreferences.m +++ b/UI/PreferencesUI/UIxPreferences.m @@ -125,7 +125,8 @@ static BOOL shouldDisplayPasswordChange = NO; - (NSArray *) timeZonesList { - return [NSTimeZone knownTimeZoneNames]; + return [[NSTimeZone knownTimeZoneNames] + sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)]; } - (NSString *) userTimeZone diff --git a/UI/Scheduler/English.lproj/Localizable.strings b/UI/Scheduler/English.lproj/Localizable.strings index cae726ee4..1fb6a12f6 100644 --- a/UI/Scheduler/English.lproj/Localizable.strings +++ b/UI/Scheduler/English.lproj/Localizable.strings @@ -110,8 +110,11 @@ /* Button Titles */ -"Add..." = "Add..."; -"Remove" = "Remove"; +"New Calendar..." = "New Calendar..."; +"Subscribe to a Calendar..." = "Subscribe to a Calendar..."; +"Remove the selected Calendar" = "Remove the selected Calendar"; + +"Name of the Calendar" = "Name of the Calendar"; "new" = "New"; "printview" = "Print View"; diff --git a/UI/Scheduler/French.lproj/Localizable.strings b/UI/Scheduler/French.lproj/Localizable.strings index bf6d23187..940257db4 100644 --- a/UI/Scheduler/French.lproj/Localizable.strings +++ b/UI/Scheduler/French.lproj/Localizable.strings @@ -111,8 +111,11 @@ /* Button Titles */ -"Add..." = "Ajouter..."; -"Remove" = "Enlever"; +"New Calendar..." = "Nouvel agenda..."; +"Subscribe to a Calendar..." = "S'inscrire à un agenda..."; +"Remove the selected Calendar" = "Enlever l'agenda sélectionné"; + +"Name of the Calendar" = "Nom de l'agenda"; "new" = "Nouveau"; "printview" = "Version imprimable"; diff --git a/UI/Scheduler/GNUmakefile b/UI/Scheduler/GNUmakefile index 0089f139a..8c65e344b 100644 --- a/UI/Scheduler/GNUmakefile +++ b/UI/Scheduler/GNUmakefile @@ -48,7 +48,7 @@ SchedulerUI_RESOURCE_FILES += \ product.plist SchedulerUI_RESOURCE_FILES += \ - Toolbars/SOGoAppointmentFolder.toolbar \ + Toolbars/SOGoAppointmentFolders.toolbar \ Toolbars/SOGoAppointmentObject.toolbar \ Toolbars/SOGoAppointmentObjectAccept.toolbar \ Toolbars/SOGoAppointmentObjectDecline.toolbar \ diff --git a/UI/Scheduler/German.lproj/Localizable.strings b/UI/Scheduler/German.lproj/Localizable.strings index 9a52d23d9..b05adbc75 100644 --- a/UI/Scheduler/German.lproj/Localizable.strings +++ b/UI/Scheduler/German.lproj/Localizable.strings @@ -100,8 +100,11 @@ /* Button Titles */ -"Add..." = "Hinzufügen..."; -"Remove" = "Löschen"; +"New Calendar..." = "New Calendar..."; +"Subscribe to a Calendar..." = "Subscribe to a Calendar..."; +"Remove the selected Calendar" = "Remove the selected Calendar"; + +"Name of the Calendar" = "Name of the Calendar"; "new" = "Neu"; "printview" = "Version imprimable"; diff --git a/UI/Scheduler/Toolbars/SOGoAppointmentFolder.toolbar b/UI/Scheduler/Toolbars/SOGoAppointmentFolders.toolbar similarity index 100% rename from UI/Scheduler/Toolbars/SOGoAppointmentFolder.toolbar rename to UI/Scheduler/Toolbars/SOGoAppointmentFolders.toolbar diff --git a/UI/Scheduler/UIxAppointmentEditor.m b/UI/Scheduler/UIxAppointmentEditor.m index f4f041979..a88b3f382 100644 --- a/UI/Scheduler/UIxAppointmentEditor.m +++ b/UI/Scheduler/UIxAppointmentEditor.m @@ -286,14 +286,14 @@ { NSString *objectId, *method, *uri; id result; - Class clientKlazz; + SOGoAppointmentFolder *co; - clientKlazz = [[self clientObject] class]; - objectId = [clientKlazz globallyUniqueObjectId]; + co = [self clientObject]; + objectId = [co globallyUniqueObjectId]; if ([objectId length] > 0) { - method = [NSString stringWithFormat:@"%@/Calendar/%@/editAsAppointment", - [self userFolderPath], objectId]; + method = [NSString stringWithFormat:@"%@/%@/editAsAppointment", + [co soURL], objectId]; uri = [self completeHrefForMethod: method]; result = [self redirectToLocation: uri]; } diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index 34a1ec92d..b036060f9 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -25,6 +25,7 @@ #import #import #import +#import #import #import @@ -38,6 +39,7 @@ #import #import #import +#import #import @@ -214,36 +216,6 @@ return aptFolder; } -- (NSArray *) _activeCalendarFolders -{ - NSMutableArray *activeFolders; - NSEnumerator *folders; - NSDictionary *currentFolderDict; - SOGoAppointmentFolder *currentFolder, *clientObject; - - activeFolders = [NSMutableArray new]; - [activeFolders autorelease]; - - clientObject = [self clientObject]; - - folders = [[clientObject calendarFolders] objectEnumerator]; - currentFolderDict = [folders nextObject]; - while (currentFolderDict) - { - if ([[currentFolderDict objectForKey: @"active"] boolValue]) - { - currentFolder - = [self _aptFolder: [currentFolderDict objectForKey: @"folder"] - withClientObject: clientObject]; - [activeFolders addObject: currentFolder]; - } - - currentFolderDict = [folders nextObject]; - } - - return activeFolders; -} - - (NSArray *) _fetchFields: (NSArray *) fields forComponentOfType: (NSString *) component { @@ -252,34 +224,42 @@ NSMutableDictionary *infos, *currentInfo, *newInfo; NSString *owner, *uid; NSNull *marker; + SOGoAppointmentFolders *clientObject; marker = [NSNull null]; infos = [NSMutableDictionary dictionary]; - folders = [[self _activeCalendarFolders] objectEnumerator]; + clientObject = [self clientObject]; + + folders = [[clientObject subFolders] objectEnumerator]; currentFolder = [folders nextObject]; while (currentFolder) { - owner = [currentFolder ownerInContext: context]; - currentInfos = [[currentFolder fetchCoreInfosFrom: startDate - to: endDate - component: component] objectEnumerator]; - newInfo = [currentInfos nextObject]; - while (newInfo) + if ([currentFolder isActive]) { - uid = [newInfo objectForKey: @"c_uid"]; - currentInfo = [infos objectForKey: uid]; - if (!currentInfo - || [owner isEqualToString: userLogin]) - { - [self _updatePrivacyInComponent: newInfo - fromFolder: currentFolder]; - [newInfo setObject: owner forKey: @"c_owner"]; - [infos setObject: [newInfo objectsForKeys: fields - notFoundMarker: marker] - forKey: uid]; - } + owner = [currentFolder ownerInContext: context]; + currentInfos = [[currentFolder fetchCoreInfosFrom: startDate + to: endDate + component: component] objectEnumerator]; newInfo = [currentInfos nextObject]; + while (newInfo) + { + uid = [newInfo objectForKey: @"c_uid"]; + currentInfo = [infos objectForKey: uid]; + if (!currentInfo + || [owner isEqualToString: userLogin]) + { + [self _updatePrivacyInComponent: newInfo + fromFolder: currentFolder]; + [newInfo setObject: [currentFolder nameInContainer] + forKey: @"c_folder"]; + // [newInfo setObject: owner forKey: @"c_owner"]; + [infos setObject: [newInfo objectsForKeys: fields + notFoundMarker: marker] + forKey: uid]; + } + newInfo = [currentInfos nextObject]; + } } currentFolder = [folders nextObject]; } @@ -326,7 +306,7 @@ [self _setupContext]; newEvents = [NSMutableArray array]; - fields = [NSArray arrayWithObjects: @"c_name", @"c_owner", @"c_status", + fields = [NSArray arrayWithObjects: @"c_name", @"c_folder", @"c_status", @"c_title", @"c_startdate", @"c_enddate", @"c_location", @"c_isallday", nil]; events = [[self _fetchFields: fields @@ -397,7 +377,7 @@ [self _setupContext]; - fields = [NSArray arrayWithObjects: @"c_name", @"c_owner", @"c_status", + fields = [NSArray arrayWithObjects: @"c_name", @"c_folder", @"c_status", @"c_title", @"c_enddate", nil]; tasks = [[self _fetchFields: fields diff --git a/UI/Scheduler/UIxCalMainView.m b/UI/Scheduler/UIxCalMainView.m index 80b66f66d..ca3933a0c 100644 --- a/UI/Scheduler/UIxCalMainView.m +++ b/UI/Scheduler/UIxCalMainView.m @@ -23,6 +23,7 @@ #import #import #import +#import #import #import @@ -41,6 +42,15 @@ static NSMutableArray *yearMenuItems = nil; @implementation UIxCalMainView +- (NSString *) userUTCOffset +{ + NSTimeZone *userTZ; + + userTZ = [[context activeUser] timeZone]; + + return [NSString stringWithFormat: @"%d", [userTZ secondsFromGMT]]; +} + - (NSArray *) monthMenuItems { unsigned int count; diff --git a/UI/Scheduler/UIxCalendarSelector.h b/UI/Scheduler/UIxCalendarSelector.h index 410ce6dbb..21feda2f8 100644 --- a/UI/Scheduler/UIxCalendarSelector.h +++ b/UI/Scheduler/UIxCalendarSelector.h @@ -23,29 +23,23 @@ #ifndef UIXCALENDARSELECTOR_H #define UIXCALENDARSELECTOR_H +#import + @class NSArray; @class NSMutableArray; @class NSDictionary; -@class NSMutableDictionary; @class NSString; -@class iCalPerson; @interface UIxCalendarSelector : UIxComponent { - NSMutableDictionary *colors; - - NSDictionary *currentCalendarFolder; - NSString *currentCalendarLogin; + NSMutableArray *calendars; + NSDictionary *currentCalendar; } -- (NSArray *) calendarFolders; +- (NSArray *) calendars; -- (void) setCurrentCalendarFolder: (NSDictionary *) newCurrentCalendarFolder; -- (NSDictionary *) currentCalendarFolder; - -- (NSString *) currentCalendarSpanBG; -- (NSString *) currentCalendarLogin; -- (NSString *) currentCalendarStyle; +- (void) setCurrentCalendar: (NSDictionary *) newCalendar; +- (NSDictionary *) currentCalendar; @end diff --git a/UI/Scheduler/UIxCalendarSelector.m b/UI/Scheduler/UIxCalendarSelector.m index 1beef4b43..b411f6b44 100644 --- a/UI/Scheduler/UIxCalendarSelector.m +++ b/UI/Scheduler/UIxCalendarSelector.m @@ -22,34 +22,30 @@ #import #import -#import -#import +#import -#import -#import - -#import -#import +#import #import +#import #import "UIxCalendarSelector.h" -static inline char -darkenedColor (const char value) -{ - char newValue; +// static inline char +// darkenedColor (const char value) +// { +// char newValue; - if (value >= '0' && value <= '9') - newValue = ((value - '0') / 2) + '0'; - else if (value >= 'a' && value <= 'f') - newValue = ((value + 10 - 'a') / 2) + '0'; - else if (value >= 'A' && value <= 'F') - newValue = ((value + 10 - 'A') / 2) + '0'; - else - newValue = value; +// if (value >= '0' && value <= '9') +// newValue = ((value - '0') / 2) + '0'; +// else if (value >= 'a' && value <= 'f') +// newValue = ((value + 10 - 'a') / 2) + '0'; +// else if (value >= 'A' && value <= 'F') +// newValue = ((value + 10 - 'A') / 2) + '0'; +// else +// newValue = value; - return newValue; -} +// return newValue; +// } static inline NSString * colorForNumber (unsigned int number) @@ -90,8 +86,8 @@ colorForNumber (unsigned int number) { if ((self = [super init])) { - colors = nil; - currentCalendarFolder = nil; + calendars = nil; + currentCalendar = nil; } return self; @@ -99,82 +95,61 @@ colorForNumber (unsigned int number) - (void) dealloc { - [currentCalendarFolder release]; - [colors release]; + [calendars release]; + [currentCalendar release]; [super dealloc]; } -- (NSArray *) calendarFolders +- (NSArray *) calendars { - NSArray *calendarFolders; - NSEnumerator *newFolders; - NSDictionary *currentFolder; - unsigned int count; + NSArray *folders; + SOGoAppointmentFolder *folder; + NSMutableDictionary *calendar; + unsigned int count, max; + NSString *folderId, *folderName; + NSNumber *isActive; - calendarFolders = [[self clientObject] calendarFolders]; - if (!colors) + if (!calendars) { - colors = [NSMutableDictionary new]; - count = 0; - newFolders = [calendarFolders objectEnumerator]; - currentFolder = [newFolders nextObject]; - while (currentFolder) + folders = [[self clientObject] subFolders]; + max = [folders count]; + calendars = [[NSMutableArray alloc] initWithCapacity: max]; + for (count = 0; count < max; count++) { - [colors setObject: colorForNumber (count) - forKey: [currentFolder objectForKey: @"folder"]]; - count++; - currentFolder = [newFolders nextObject]; + folder = [folders objectAtIndex: count]; + calendar = [NSMutableDictionary dictionary]; + folderName = [folder nameInContainer]; + [calendar setObject: + [NSString stringWithFormat: @"/%@", folderName] + forKey: @"id"]; + [calendar setObject: [folder displayName] + forKey: @"displayName"]; + [calendar setObject: folderName forKey: @"folder"]; + [calendar setObject: colorForNumber (count) + forKey: @"color"]; + isActive = [NSNumber numberWithBool: [folder isActive]]; + [calendar setObject: isActive forKey: @"active"]; + [calendars addObject: calendar]; } } - return calendarFolders; + return calendars; } -- (void) setCurrentCalendarFolder: (NSDictionary *) newCurrentCalendarFolder +- (void) setCurrentCalendar: (NSDictionary *) newCalendar { - ASSIGN (currentCalendarFolder, newCurrentCalendarFolder); + ASSIGN (currentCalendar, newCalendar); } -- (NSDictionary *) currentCalendarFolder +- (NSDictionary *) currentCalendar { - return currentCalendarFolder; -} - -- (NSString *) currentCalendarSpanBG -{ - NSString *colorKey; - - colorKey = [currentCalendarFolder objectForKey: @"folder"]; - - return [colors objectForKey: colorKey]; -} - -- (NSString *) currentCalendarLogin -{ - NSArray *parts; - NSMutableString *login; - - login = [NSMutableString string]; - parts = [[currentCalendarFolder objectForKey: @"folder"] - componentsSeparatedByString: @":"]; - [login appendString: (([parts count] > 1) - ? [parts objectAtIndex: 0] - : [[context activeUser] login])]; - [login replaceString: @"." withString: @"_"]; - [login replaceString: @"#" withString: @"_"]; - [login replaceString: @"@" withString: @"_"]; - - return login; + return currentCalendar; } - (NSString *) currentCalendarStyle { - NSString *color; - - color = [self currentCalendarSpanBG]; - - return [NSString stringWithFormat: @"color: %@; background-color: %@;", - color, color]; + return [currentCalendar + keysWithFormat: @"color: %{color}; background-color: %{color};"]; } @end /* UIxCalendarSelector */ diff --git a/UI/Scheduler/UIxComponentEditor.m b/UI/Scheduler/UIxComponentEditor.m index 7937e8fa7..5cfeecaf4 100644 --- a/UI/Scheduler/UIxComponentEditor.m +++ b/UI/Scheduler/UIxComponentEditor.m @@ -42,6 +42,7 @@ #import #import +#import #import #import #import @@ -331,19 +332,20 @@ - (NSArray *) calendarList { - SOGoAppointmentFolder *folder; + SOGoAppointmentFolder *calendar, *currentCalendar; + SOGoAppointmentFolders *calendarParent; NSEnumerator *allCalendars; - NSDictionary *currentCalendar; if (!calendarList) { calendarList = [NSMutableArray new]; - folder = [[self clientObject] container]; - allCalendars = [[folder calendarFolders] objectEnumerator]; + calendar = [[self clientObject] container]; + calendarParent = [calendar container]; + allCalendars = [[calendarParent subFolders] objectEnumerator]; currentCalendar = [allCalendars nextObject]; while (currentCalendar) { - if ([[currentCalendar objectForKey: @"active"] boolValue]) + if ([currentCalendar isActive]) [calendarList addObject: currentCalendar]; currentCalendar = [allCalendars nextObject]; } diff --git a/UI/Scheduler/UIxTaskEditor.m b/UI/Scheduler/UIxTaskEditor.m index 0258ae2d2..bc7e1bed1 100644 --- a/UI/Scheduler/UIxTaskEditor.m +++ b/UI/Scheduler/UIxTaskEditor.m @@ -324,14 +324,14 @@ { NSString *objectId, *method, *uri; id result; - Class clientKlazz; + SOGoAppointmentFolder *co; - clientKlazz = [[self clientObject] class]; - objectId = [clientKlazz globallyUniqueObjectId]; + co = [self clientObject]; + objectId = [co globallyUniqueObjectId]; if ([objectId length] > 0) { - method = [NSString stringWithFormat:@"%@/Calendar/%@/editAsTask", - [self userFolderPath], objectId]; + method = [NSString stringWithFormat:@"%@/%@/editAsTask", + [co soURL], objectId]; uri = [self completeHrefForMethod: method]; result = [self redirectToLocation: uri]; } diff --git a/UI/Scheduler/product.plist b/UI/Scheduler/product.plist index 2041d7570..c5afd5cc6 100644 --- a/UI/Scheduler/product.plist +++ b/UI/Scheduler/product.plist @@ -28,11 +28,11 @@ }; categories = { - SOGoAppointmentFolder = { + SOGoAppointmentFolders = { slots = { toolbar = { protectedBy = "View"; - value = "SOGoAppointmentFolder.toolbar"; + value = "SOGoAppointmentFolders.toolbar"; }; }; methods = { @@ -70,16 +70,6 @@ protectedBy = "View"; pageName = "UIxCalMonthView"; }; - newevent = { - protectedBy = "Add Documents, Images, and Files"; - pageName = "UIxAppointmentEditor"; - actionName = "new"; - }; - newtask = { - protectedBy = "Add Documents, Images, and Files"; - pageName = "UIxTaskEditor"; - actionName = "new"; - }; show = { protectedBy = "View"; pageName = "UIxCalView"; @@ -94,15 +84,45 @@ pageName = "UIxAppointmentProposal"; actionName = "proposalSearch"; }; + userRights = { + protectedBy = "ReadAcls"; + pageName = "UIxCalUserRightsEditor"; + }; + saveUserRights = { + protectedBy = "SaveAcls"; + pageName = "UIxCalUserRightsEditor"; + actionName = "saveUserRights"; + }; + }; + }; + + SOGoAppointmentFolder = { + methods = { + newevent = { + protectedBy = "Add Documents, Images, and Files"; + pageName = "UIxAppointmentEditor"; + actionName = "new"; + }; + newtask = { + protectedBy = "Add Documents, Images, and Files"; + pageName = "UIxTaskEditor"; + actionName = "new"; + }; batchDelete = { protectedBy = "Delete Objects"; pageName = "UIxCalMainView"; actionName = "batchDelete"; }; - updateCalendars = { + + show = { protectedBy = "View"; + pageName = "UIxCalView"; + actionName = "redirectForUIDs"; + }; + batchDelete = { + protectedBy = "Delete Objects"; pageName = "UIxCalMainView"; - actionName = "updateCalendars"; + actionName = "batchDelete"; }; editAttendees = { protectedBy = "View"; @@ -119,6 +139,7 @@ }; }; }; + SOGoCalendarComponent = { }; @@ -158,6 +179,10 @@ pageName = "UIxAppointmentEditor"; actionName = "decline"; }; + editAttendees = { + protectedBy = "View"; + pageName = "UIxAttendeesEditor"; + }; }; }; diff --git a/UI/Templates/ContactsUI/UIxContactsListViewContainer.wox b/UI/Templates/ContactsUI/UIxContactsListViewContainer.wox index 8a2d86556..1733b8969 100644 --- a/UI/Templates/ContactsUI/UIxContactsListViewContainer.wox +++ b/UI/Templates/ContactsUI/UIxContactsListViewContainer.wox @@ -62,13 +62,10 @@
  • + >
diff --git a/UI/Templates/SchedulerUI/UIxCalMainView.wox b/UI/Templates/SchedulerUI/UIxCalMainView.wox index 863be2ff4..9ab3d56d5 100644 --- a/UI/Templates/SchedulerUI/UIxCalMainView.wox +++ b/UI/Templates/SchedulerUI/UIxCalMainView.wox @@ -9,6 +9,9 @@ className="UIxPageFrame" title="title" > + diff --git a/UI/Templates/SchedulerUI/UIxCalendarSelector.wox b/UI/Templates/SchedulerUI/UIxCalendarSelector.wox index bee2ff03c..a54fcfb6b 100644 --- a/UI/Templates/SchedulerUI/UIxCalendarSelector.wox +++ b/UI/Templates/SchedulerUI/UIxCalendarSelector.wox @@ -7,30 +7,34 @@ xmlns:rsrc="OGo:url" xmlns:label="OGo:label">
+
    -
  • +
  • + var:checked="currentCalendar.active" />
    OO
    -
diff --git a/UI/Templates/UIxPageFrame.wox b/UI/Templates/UIxPageFrame.wox index 447e65207..ff1588e05 100644 --- a/UI/Templates/UIxPageFrame.wox +++ b/UI/Templates/UIxPageFrame.wox @@ -5,7 +5,7 @@ xmlns:var="http://www.skyrix.com/od/binding" xmlns:const="http://www.skyrix.com/od/constant" xmlns:rsrc="OGo:url" - xmlns:label="OGo:label" + xmlns:label="OGo:label" > diff --git a/UI/WebServerResources/ContactsUI.js b/UI/WebServerResources/ContactsUI.js index 4ae7d282e..f1912558c 100644 --- a/UI/WebServerResources/ContactsUI.js +++ b/UI/WebServerResources/ContactsUI.js @@ -19,8 +19,8 @@ function validateEditorInput(sender) { errortext = errortext + labels.error_missingrecipients + "\n"; if (errortext.length > 0) { - alert(labels.error_validationfailed.decodeEntities() + ":\n" - + errortext.decodeEntities()); + alert(labels.error_validationfailed + ":\n" + + errortext); return false; } return true; @@ -497,28 +497,24 @@ function refreshContacts(contactId) { } function onAddressBookNew(event) { - var name = window.prompt(labels["Name of the Address Book"].decodeEntities()); - if (name) { - if (document.newAbAjaxRequest) { - document.newAbAjaxRequest.aborted = true; - document.newAbAjaxRequest.abort(); - } - var url = ApplicationBaseURL + "/newAb?name=" + name; - document.newAbAjaxRequest - = triggerAjaxRequest(url, newAbCallback, name); - } + createFolder(window.prompt(labels["Name of the Address Book"]), + appendAddressBook); preventDefault(event); } function appendAddressBook(name, folder) { - var li = document.createElement("li"); - $("contactFolders").appendChild(li); - li.setAttribute("id", folder); - li.appendChild(document.createTextNode(name)); - setEventsOnContactFolder(li); + if (folder) + folder = accessToSubscribedFolder(folder); + else + folder = "/" + name; + var li = document.createElement("li"); + $("contactFolders").appendChild(li); + li.setAttribute("id", folder); + li.appendChild(document.createTextNode(name)); + setEventsOnContactFolder(li); } -function newAbCallback(http) { +function newFolderCallback(http) { if (http.readyState == 4 && http.status == 201) { var name = http.callbackData; @@ -552,60 +548,60 @@ function onAddressBookRemove(event) { var selector = $("contactFolders"); var nodes = selector.getSelectedNodes(); if (nodes.length > 0) { - nodes[0].deselect(); - var folderId = nodes[0].getAttribute("id"); - var folderIdElements = folderId.split(":"); - if (folderIdElements.length > 1) - unsubscribeFromFolder(folderId, onFolderUnsubscribeCB, folderId); - else { - var abId = folderIdElements[0].substr(1); - deletePersonalAddressBook(abId); - var personal = $("/personal"); - personal.select(); - onFolderSelectionChange(); - } + nodes[0].deselect(); + var folderId = nodes[0].getAttribute("id"); + var folderIdElements = folderId.split("_"); + if (folderIdElements.length > 1) + unsubscribeFromFolder(folderId, onFolderUnsubscribeCB, folderId); + else { + var abId = folderIdElements[0].substr(1); + deletePersonalAddressBook(abId); + var personal = $("/personal"); + personal.select(); + onFolderSelectionChange(); + } } preventDefault(event); } function deletePersonalAddressBook(folderId) { - var label - = labels["Are you sure you want to delete the selected address book?"]; - if (window.confirm(label.decodeEntities())) { - if (document.deletePersonalABAjaxRequest) { - document.deletePersonalABAjaxRequest.aborted = true; - document.deletePersonalABAjaxRequest.abort(); - } - var url = ApplicationBaseURL + "/" + folderId + "/delete"; - document.deletePersonalABAjaxRequest - = triggerAjaxRequest(url, deletePersonalAddressBookCallback, - folderId); - } + var label + = labels["Are you sure you want to delete the selected address book?"]; + if (window.confirm(label)) { + if (document.deletePersonalABAjaxRequest) { + document.deletePersonalABAjaxRequest.aborted = true; + document.deletePersonalABAjaxRequest.abort(); + } + var url = ApplicationBaseURL + "/" + folderId + "/deleteFolder"; + document.deletePersonalABAjaxRequest + = triggerAjaxRequest(url, deletePersonalAddressBookCallback, + folderId); + } } function deletePersonalAddressBookCallback(http) { if (http.readyState == 4) { - if (http.status == 200) { - var ul = $("contactFolders"); + if (isHttpStatus204(http.status)) { + var ul = $("contactFolders"); - var children = ul.childNodesWithTag("li"); - var i = 0; - var done = false; - while (!done && i < children.length) { - var currentFolderId = children[i].getAttribute("id").substr(1); - if (currentFolderId == http.callbackData) { - ul.removeChild(children[i]); - done = true; - } - else - i++; + var children = ul.childNodesWithTag("li"); + var i = 0; + var done = false; + while (!done && i < children.length) { + var currentFolderId = children[i].getAttribute("id").substr(1); + if (currentFolderId == http.callbackData) { + ul.removeChild(children[i]); + done = true; } - } - document.deletePersonalABAjaxRequest = null; + else + i++; + } + } + document.deletePersonalABAjaxRequest = null; } else - log ("ajax problem 5: " + http.status); + log ("ajax problem 5: " + http.status); } function configureDragHandles() { @@ -677,6 +673,34 @@ function setEventsOnContactFolder(node) { onContactFoldersContextMenu.bindAsEventListener(node), false); } +function onMenuModify(event) { + var folders = $("contactFolders"); + var selected = folders.getSelectedNodes()[0]; + + if (UserLogin == selected.getAttribute("owner")) { + var currentName = selected.innerHTML; + var newName = window.prompt(labels["Address Book Name"], + currentName); + if (newName && newName.length > 0 + && newName != currentName) { + var url = (URLForFolderID(selected.getAttribute("id")) + + "/renameFolder?name=" + escape(newName.utf8encode())); + triggerAjaxRequest(url, folderRenameCallback, + {node: selected, name: newName}); + } + } else + window.alert(clabels["Unable to rename that folder!"]); +} + +function folderRenameCallback(http) { + if (http.readyState == 4) { + if (isHttpStatus204(http.status)) { + var dict = http.callbackData; + dict["node"].innerHTML = dict["name"]; + } + } +} + function onMenuSharing(event) { var folders = $("contactFolders"); var selected = folders.getSelectedNodes()[0]; @@ -688,7 +712,7 @@ function onMenuSharing(event) { function getMenus() { var menus = {}; - menus["contactFoldersMenu"] = new Array(null, "-", null, + menus["contactFoldersMenu"] = new Array(onMenuModify, "-", null, null, "-", null, "-", onMenuSharing); menus["contactMenu"] = new Array(onMenuEditContact, "-", diff --git a/UI/WebServerResources/MailerUI.js b/UI/WebServerResources/MailerUI.js index ebd3b2204..074a94f94 100644 --- a/UI/WebServerResources/MailerUI.js +++ b/UI/WebServerResources/MailerUI.js @@ -207,7 +207,7 @@ function ctxFolderAdd(sender) { } function ctxFolderDelete(sender) { - if (!confirm("Delete current folder?").decodeEntities()) + if (!confirm("Delete current folder?")) return false; // TODO: should use a form-POST or AJAX @@ -306,10 +306,10 @@ function onMenuDeleteMessage(event) { function onPrintCurrentMessage(event) { var rowIds = $("messageList").getSelectedRowsId(); if (rowIds.length == 0) { - window.alert(labels["Please select a message to print."].decodeEntities()); + window.alert(labels["Please select a message to print."]); } else if (rowIds.length > 1) { - window.alert(labels["Please select only one message to print."].decodeEntities()); + window.alert(labels["Please select only one message to print."]); } else window.print(); @@ -492,7 +492,7 @@ function quotasCallback(http) { var used = mbQuotas["usedSpace"]; var max = mbQuotas["maxQuota"]; var percents = (Math.round(used * 10000 / max) / 100); - var format = labels["quotasFormat"].decodeEntities(); + var format = labels["quotasFormat"]; var text = format.formatted(used, max, percents); window.status = text; } @@ -1265,7 +1265,7 @@ function buildMailboxes(accountName, encoded) { } function onMenuCreateFolder(event) { - var name = window.prompt(labels["Name :"].decodeEntities(), ""); + var name = window.prompt(labels["Name :"], ""); if (name && name.length > 0) { var folderID = document.menuTarget.getAttribute("dataname"); var urlstr = URLForFolderID(folderID) + "/createFolder?name=" + name; @@ -1275,7 +1275,7 @@ function onMenuCreateFolder(event) { function onMenuRenameFolder(event) { var name = window.prompt(labels["Enter the new name of your folder :"] - .decodeEntities(), + , ""); if (name && name.length > 0) { var folderID = document.menuTarget.getAttribute("dataname"); @@ -1285,7 +1285,7 @@ function onMenuRenameFolder(event) { } function onMenuDeleteFolder(event) { - var answer = window.confirm(labels["Do you really want to move this folder into the trash ?"].decodeEntities()); + var answer = window.confirm(labels["Do you really want to move this folder into the trash ?"]); if (answer) { var folderID = document.menuTarget.getAttribute("dataname"); var urlstr = URLForFolderID(folderID) + "/deleteFolder"; @@ -1320,7 +1320,7 @@ function folderOperationCallback(http) { && http.status == 204) initMailboxTree(); else - window.alert(labels["Operation failed"].decodeEntities()); + window.alert(labels["Operation failed"]); } function folderRefreshCallback(http) { @@ -1331,7 +1331,7 @@ function folderRefreshCallback(http) { refreshCurrentFolder(); } else - window.alert(labels["Operation failed"].decodeEntities()); + window.alert(labels["Operation failed"]); } function getMenus() { diff --git a/UI/WebServerResources/SOGoRootPage.css b/UI/WebServerResources/SOGoRootPage.css index fa534ef43..15d0c1f47 100644 --- a/UI/WebServerResources/SOGoRootPage.css +++ b/UI/WebServerResources/SOGoRootPage.css @@ -12,10 +12,10 @@ DIV#loginScreen background-color: #d4d0c8; margin: 0px auto; margin-top: 5em; - padding: 10px; + padding: 5px; border: 2px solid transparent; - width: 197px; - height: 300px; + width: 200px; + height: 315px; -moz-border-top-colors: #efebe7 #fff; -moz-border-left-colors: #efebe7 #fff; -moz-border-right-colors: #000 #9c9a94 transparent; @@ -26,8 +26,8 @@ DIV#loginScreen IMG { border: 0px; margin: 0px; padding: 0px; - height: 192px; - width: 192px; } + height: 200px; + width: 200px; } DIV#loginScreen INPUT.textField { width: 187px; } diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index fc864847b..7c992039a 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -17,7 +17,7 @@ var cachedDateSelectors = new Array(); var contactSelectorAction = 'calendars-contacts'; var eventsToDelete = new Array(); -var ownersOfEventsToDelete = new Array(); +var calendarsOfEventsToDelete = new Array(); var usersRightsWindowHeight = 250; var usersRightsWindowWidth = 502; @@ -27,15 +27,11 @@ function newEvent(sender, type) { if (!day) day = currentDay; - var user = UserLogin; - if (sender.parentNode.getAttribute("id") != "toolbar" - && currentView == "multicolumndayview" && type == "event") - user = sender.parentNode.parentNode.getAttribute("user"); - var hour = sender.hour; if (!hour) hour = sender.getAttribute("hour"); - var urlstr = UserFolderURL + "../" + user + "/Calendar/new" + type; + var folderID = getSelectedFolder(); + var urlstr = ApplicationBaseURL + folderID + "/new" + type; var params = new Array(); if (day) params.push("day=" + day); @@ -49,6 +45,18 @@ function newEvent(sender, type) { return false; /* stop following the link */ } +function getSelectedFolder() { + var folder; + + var nodes = $("calendarList").getSelectedRows(); + if (nodes.length > 0) + folder = nodes[0].getAttribute("id"); + else + folder = "/personal"; + + return folder; +} + function onMenuNewEventClick(event) { newEvent(this, "event"); } @@ -57,13 +65,8 @@ function onMenuNewTaskClick(event) { newEvent(this, "task"); } -function _editEventId(id, owner) { - var urlBase; - if (owner) - urlBase = UserFolderURL + "../" + owner + "/"; - urlBase += "Calendar/" - - var urlstr = urlBase + id + "/edit"; +function _editEventId(id, calendar) { + var urlstr = ApplicationBaseURL + "/" + calendar + "/" + id + "/edit"; var targetname = "SOGo_edit_" + id; var win = window.open(urlstr, "_blank", "width=490,height=470,resizable=0"); @@ -76,10 +79,10 @@ function editEvent() { for (var i = 0; i < nodes.length; i++) _editEventId(nodes[i].getAttribute("id"), - nodes[i].owner); + nodes[i].calendar); } else if (selectedCalendarCell) { _editEventId(selectedCalendarCell[0].cname, - selectedCalendarCell[0].owner); + selectedCalendarCell[0].calendar); } return false; /* stop following the link */ @@ -87,9 +90,9 @@ function editEvent() { function _batchDeleteEvents() { var events = eventsToDelete.shift(); - var owner = ownersOfEventsToDelete.shift(); - var urlstr = (UserFolderURL + "../" + owner + "/Calendar/batchDelete?ids=" - + events.join('/')); + var calendar = calendarsOfEventsToDelete.shift(); + var urlstr = (ApplicationBaseURL + "/" + calendar + + "/batchDelete?ids=" + events.join('/')); document.deleteEventAjaxRequest = triggerAjaxRequest(urlstr, deleteEventCallback, events); @@ -102,9 +105,9 @@ function deleteEvent() { if (nodes.length > 0) { var label = ""; if (listOfSelection == $("tasksList")) - label = labels["taskDeleteConfirmation"].decodeEntities(); + label = labels["taskDeleteConfirmation"]; else - label = labels["eventDeleteConfirmation"].decodeEntities(); + label = labels["eventDeleteConfirmation"]; if (confirm(label)) { if (document.deleteEventAjaxRequest) { @@ -112,33 +115,33 @@ function deleteEvent() { document.deleteEventAjaxRequest.abort(); } var sortedNodes = new Array(); - var owners = new Array(); + var calendars = new Array(); for (var i = 0; i < nodes.length; i++) { - var owner = nodes[i].owner; - if (!sortedNodes[owner]) { - sortedNodes[owner] = new Array(); - owners.push(owner); + var calendar = nodes[i].calendar; + if (!sortedNodes[calendar]) { + sortedNodes[calendar] = new Array(); + calendars.push(calendar); } - sortedNodes[owner].push(nodes[i].cname); + sortedNodes[calendar].push(nodes[i].cname); } - for (var i = 0; i < owners.length; i++) { - ownersOfEventsToDelete.push(owners[i]); - eventsToDelete.push(sortedNodes[owners[i]]); + for (var i = 0; i < calendars.length; i++) { + calendarsOfEventsToDelete.push(calendars[i]); + eventsToDelete.push(sortedNodes[calendars[i]]); } _batchDeleteEvents(); } } } else if (selectedCalendarCell) { - var label = labels["eventDeleteConfirmation"].decodeEntities(); + var label = labels["eventDeleteConfirmation"]; if (confirm(label)) { if (document.deleteEventAjaxRequest) { document.deleteEventAjaxRequest.aborted = true; document.deleteEventAjaxRequest.abort(); } eventsToDelete.push([selectedCalendarCell[0].cname]); - ownersOfEventsToDelete.push(selectedCalendarCell[0].owner); + calendarsOfEventsToDelete.push(selectedCalendarCell[0].calendar); _batchDeleteEvents(); } } @@ -168,7 +171,7 @@ function closeInvitationWindow() { closePseudoWin.style.top = "0px;"; closePseudoWin.style.left = "0px;"; closePseudoWin.style.right = "0px;"; - closePseudoWin.appendChild(document.createTextNode(labels["closeThisWindowMessage"].decodeEntities())); + closePseudoWin.appendChild(document.createTextNode(labels["closeThisWindowMessage"])); document.body.appendChild(closeDiv); document.body.appendChild(closePseudoWin); } @@ -215,7 +218,7 @@ function deleteEventCallback(http) { } function editDoubleClickedEvent(event) { - _editEventId(this.cname, this.owner); + _editEventId(this.cname, this.calendar); preventDefault(event); event.cancelBubble = true; @@ -316,7 +319,7 @@ function eventsListCallback(http) { $(row).addClassName("eventRow"); row.setAttribute("id", escape(data[i][0])); row.cname = escape(data[i][0]); - row.owner = data[i][1]; + row.calendar = data[i][1]; var startDate = new Date(); startDate.setTime(data[i][4] * 1000); @@ -372,9 +375,8 @@ function tasksListCallback(http) { Event.observe(listItem, "dblclick", editDoubleClickedEvent.bindAsEventListener(listItem)); listItem.setAttribute("id", data[i][0]); $(listItem).addClassName(data[i][5]); - var owner = data[i][1]; - listItem.owner = owner; - $(listItem).addClassName("ownerIs" + owner.cssSafeString()); + listItem.calendar = data[i][1]; + $(listItem).addClassName("calendarFolder" + data[i][1]); listItem.cname = escape(data[i][0]); var input = document.createElement("input"); input.setAttribute("type", "checkbox"); @@ -429,7 +431,7 @@ function restoreCurrentDaySelection(div) { } function changeDateSelectorDisplay(day, keepCurrentDay) { - var url = ApplicationBaseURL + "dateselector"; + var url = ApplicationBaseURL + "/dateselector"; if (day) url += "?day=" + day; @@ -457,7 +459,7 @@ function changeDateSelectorDisplay(day, keepCurrentDay) { } function changeCalendarDisplay(time, newView) { - var url = ApplicationBaseURL + ((newView) ? newView : currentView); + var url = ApplicationBaseURL + "/" + ((newView) ? newView : currentView); selectedCalendarCell = null; @@ -583,7 +585,7 @@ function refreshCalendarEvents() { document.refreshCalendarEventsAjaxRequest.aborted = true; document.refreshCalendarEventsAjaxRequest.abort(); } - var url = ApplicationBaseURL + "eventslist?sd=" + sd + "&ed=" + ed; + var url = ApplicationBaseURL + "/eventslist?sd=" + sd + "&ed=" + ed; document.refreshCalendarEventsAjaxRequest = triggerAjaxRequest(url, refreshCalendarEventsCallback, {"startDate": sd, "endDate": ed}); @@ -608,9 +610,9 @@ function drawCalendarEvent(eventData, sd, ed) { var viewEndDate = ed.asDate(); var startDate = new Date(); - startDate.setTime(eventData[4] * 1000); + startDate.setTime(eventData[4] * 1000 + (1000 * UTCOffset)); var endDate = new Date(); - endDate.setTime(eventData[5] * 1000); + endDate.setTime(eventData[5] * 1000 + (1000 * UTCOffset)); var days = startDate.daysUpTo(endDate); @@ -636,8 +638,8 @@ function drawCalendarEvent(eventData, sd, ed) { // log("day: " + days[i]); if (i == 0) { - var quarters = (startDate.getHours() * 4 - + Math.floor(startDate.getMinutes() / 15)); + var quarters = (startDate.getUTCHours() * 4 + + Math.floor(startDate.getUTCMinutes() / 15)); starts = quarters; startHour = startDate.getDisplayHoursString(); endHour = endDate.getDisplayHoursString(); @@ -648,8 +650,8 @@ function drawCalendarEvent(eventData, sd, ed) { var ends; var lasts; if (i == days.length - 1) { - var quarters = (endDate.getHours() * 4 - + Math.ceil(endDate.getMinutes() / 15)); + var quarters = (endDate.getUTCHours() * 4 + + Math.ceil(endDate.getUTCMinutes() / 15)); ends = quarters; } else @@ -706,11 +708,11 @@ function drawCalendarEvent(eventData, sd, ed) { } } -function newEventDIV(cname, owner, starts, lasts, +function newEventDIV(cname, calendar, starts, lasts, startHour, endHour, title) { var eventDiv = document.createElement("div"); eventDiv.cname = escape(cname); - eventDiv.owner = owner; + eventDiv.calendar = calendar; $(eventDiv).addClassName("event"); $(eventDiv).addClassName("starts" + starts); $(eventDiv).addClassName("lasts" + lasts); @@ -723,7 +725,7 @@ function newEventDIV(cname, owner, starts, lasts, var innerDiv = document.createElement("div"); eventDiv.appendChild(innerDiv); $(innerDiv).addClassName("eventInside"); - $(innerDiv).addClassName("ownerIs" + owner.cssSafeString()); + $(innerDiv).addClassName("calendarFolder" + calendar); var gradientDiv = document.createElement("div"); innerDiv.appendChild(gradientDiv); @@ -881,7 +883,7 @@ function _loadEventHref(href) { document.eventsListAjaxRequest.aborted = true; document.eventsListAjaxRequest.abort(); } - var url = ApplicationBaseURL + href; + var url = ApplicationBaseURL + "/" + href; document.eventsListAjaxRequest = triggerAjaxRequest(url, eventsListCallback, href); @@ -897,7 +899,7 @@ function _loadTasksHref(href) { document.tasksListAjaxRequest.aborted = true; document.tasksListAjaxRequest.abort(); } - url = ApplicationBaseURL + href; + url = ApplicationBaseURL + "/" + href; var tasksList = $("tasksList"); var selectedIds; @@ -1119,7 +1121,6 @@ function onShowCompletedTasks(event) { function updateTaskStatus(event) { var taskId = this.parentNode.getAttribute("id"); - var taskOwner = this.parentNode.owner; var newStatus = (this.checked ? 1 : 0); var http = createHTTPClient(); @@ -1128,9 +1129,8 @@ function updateTaskStatus(event) { //log("update task status: " + taskId + " to " + this.checked); event.cancelBubble = true; - url = (UserFolderURL + "../" + taskOwner - + "/Calendar/" + taskId - + "/changeStatus?status=" + newStatus); + url = (ApplicationBaseURL + "/" + this.parentNode.calendar + + "/" + taskId + "/changeStatus?status=" + newStatus); if (http) { // log ("url: " + url); @@ -1162,10 +1162,11 @@ function updateCalendarStatus(event) { } } - if (!list.length) { - list.push(UserLogin); - nodes[0].childNodesWithTag("input")[0].checked = true; - } +// if (!list.length) { +// list.push(UserLogin); +// nodes[0].childNodesWithTag("input")[0].checked = true; +// } + // ApplicationBaseURL = (UserFolderURL + "Groups/_custom_" // + list.join(",") + "/Calendar/"); @@ -1207,7 +1208,7 @@ function calendarStatusCallback(http) { } function calendarEntryCallback(http) { - if (http.readyState == 4) { + if (http.readyState == 4) { var denied = !isHttpStatus204(http.status); var entry = $(http.callbackData); if (denied) @@ -1339,79 +1340,94 @@ function initCalendarSelector() { } var links = $("calendarSelectorButtons").childNodesWithTag("a"); - Event.observe(links[0], "click", onCalendarAdd); - Event.observe(links[1], "click", onCalendarRemove); + Event.observe(links[0], "click", onCalendarNew); + Event.observe(links[1], "click", onCalendarAdd); + Event.observe(links[2], "click", onCalendarRemove); +} + +function onCalendarNew(event) { + createFolder(window.prompt(labels["Name of the Calendar"]), + appendCalendar); + preventDefault(event); } function onCalendarAdd(event) { - openUserFolderSelector(onFolderSubscribeCB, "calendar"); - - preventDefault(event); + openUserFolderSelector(onFolderSubscribeCB, "calendar"); + preventDefault(event); } function appendCalendar(folderName, folder) { - var calendarList = $("calendarList"); - var lis = calendarList.childNodesWithTag("li"); - var color = indexColor(lis.length); - //log ("color: " + color); + if (folder) + folder = accessToSubscribedFolder(folder); + else + folder = "/" + folderName; - var li = document.createElement("li"); - calendarList.appendChild(li); +// log ("append: " + folderName + "; folder: " + folder); - var checkBox = document.createElement("input"); - checkBox.setAttribute("type", "checkbox"); - li.appendChild(checkBox); + var calendarList = $("calendarList"); + var lis = calendarList.childNodesWithTag("li"); + var color = indexColor(lis.length + 100); + //log ("color: " + color); + + var li = document.createElement("li"); + calendarList.appendChild(li); + + var checkBox = document.createElement("input"); + checkBox.setAttribute("type", "checkbox"); + li.appendChild(checkBox); - li.appendChild(document.createTextNode(" ")); + li.appendChild(document.createTextNode(" ")); - var colorBox = document.createElement("div"); - li.appendChild(colorBox); - li.appendChild(document.createTextNode(" " + folderName)); - colorBox.appendChild(document.createTextNode("OO")); + var colorBox = document.createElement("div"); + li.appendChild(colorBox); + li.appendChild(document.createTextNode(" " + folderName)); + colorBox.appendChild(document.createTextNode("OO")); - li.setAttribute("id", folder); - Event.observe(li, "mousedown", listRowMouseDownHandler); - Event.observe(li, "click", onRowClick); - $(checkBox).addClassName("checkBox"); + li.setAttribute("id", folder); + Event.observe(li, "mousedown", listRowMouseDownHandler); + Event.observe(li, "click", onRowClick); + $(checkBox).addClassName("checkBox"); - Event.observe(checkBox, "click", updateCalendarStatus.bindAsEventListener(checkBox)); + Event.observe(checkBox, "click", + updateCalendarStatus.bindAsEventListener(checkBox)); - $(colorBox).addClassName("colorBox"); - if (color) { - $(colorBox).setStyle({ color: color, - backgroundColor: color }); - } + $(colorBox).addClassName("colorBox"); + if (color) + $(colorBox).setStyle({color: color, + backgroundColor: color}); - var contactId = folder.split(":")[0]; - var url = URLForFolderID(folder) + "/canAccessContent"; - triggerAjaxRequest(url, calendarEntryCallback, folder); + var url = URLForFolderID(folder) + "/canAccessContent"; + triggerAjaxRequest(url, calendarEntryCallback, folder); - if (!document.styleSheets) return; - var theRules = new Array(); - var lastSheet = document.styleSheets[document.styleSheets.length - 1]; - if (lastSheet.insertRule) { // Mozilla - lastSheet.insertRule('.ownerIs' + contactId.cssSafeString() + ' {' - + ' background-color: ' - + color - + ' !important; }', 0); - } - else { // IE - lastSheet.addRule('.ownerIs' + contactId.cssSafeString(), - ' background-color: ' - + color - + ' !important; }'); - } + if (!document.styleSheets) return; + var theRules = new Array(); + var lastSheet = document.styleSheets[document.styleSheets.length - 1]; + if (lastSheet.insertRule) { // Mozilla + lastSheet.insertRule('.calendarFolder' + folder.substr(1) + ' {' + + ' background-color: ' + + color + + ' !important; }', 0); + } + else { // IE + lastSheet.addRule('.calendarFolder' + folder.substr(1), + ' background-color: ' + + color + + ' !important; }'); + } } function onFolderSubscribeCB(folderData) { var folder = $(folderData["folder"]); if (!folder) - appendCalendar(folderData["folderName"], folderData["folder"]); + appendCalendar(folderData["folderName"], folderData["folder"]); } function onFolderUnsubscribeCB(folderId) { - var node = $(folderId); - node.parentNode.removeChild(node); + var node = $(folderId); + node.parentNode.removeChild(node); + refreshEvents(); + refreshTasks(); + changeCalendarDisplay(); } function onCalendarRemove(event) { @@ -1419,15 +1435,59 @@ function onCalendarRemove(event) { if (nodes.length > 0) { nodes[0].deselect(); var folderId = nodes[0].getAttribute("id"); - var folderIdElements = folderId.split(":"); + var folderIdElements = folderId.split("_"); if (folderIdElements.length > 1) { - unsubscribeFromFolder(folderId, onFolderUnsubscribeCB, folderId); + unsubscribeFromFolder(folderId, onFolderUnsubscribeCB, folderId); + } + else { + var calId = folderIdElements[0].substr(1); + deletePersonalCalendar(calId); } } preventDefault(event); } +function deletePersonalCalendar(folderId) { + var label + = labels["Are you sure you want to delete the selected calendar?"]; + if (window.confirm(label)) { + if (document.deletePersonalCalendarAjaxRequest) { + document.deletePersonalCalendarAjaxRequest.aborted = true; + document.deletePersonalCalendarAjaxRequest.abort(); + } + var url = ApplicationBaseURL + "/" + folderId + "/deleteFolder"; + document.deletePersonalCalendarAjaxRequest + = triggerAjaxRequest(url, deletePersonalCalendarCallback, folderId); + } +} + +function deletePersonalCalendarCallback(http) { + if (http.readyState == 4) { + if (isHttpStatus204(http.status)) { + var ul = $("calendarList"); + var children = ul.childNodesWithTag("li"); + var i = 0; + var done = false; + while (!done && i < children.length) { + var currentFolderId = children[i].getAttribute("id").substr(1); + if (currentFolderId == http.callbackData) { + ul.removeChild(children[i]); + done = true; + } + else + i++; + } + refreshEvents(); + refreshTasks(); + changeCalendarDisplay(); + } + document.deletePersonalCalendarAjaxRequest = null; + } + else + log ("ajax problem 5: " + http.status); +} + function configureLists() { var list = $("tasksList"); list.multiselect = true; diff --git a/UI/WebServerResources/UIxAclEditor.js b/UI/WebServerResources/UIxAclEditor.js index 91c249ab9..2bef5e1d3 100644 --- a/UI/WebServerResources/UIxAclEditor.js +++ b/UI/WebServerResources/UIxAclEditor.js @@ -88,8 +88,7 @@ function subscribeToFolder(refreshCallback, refreshCallbackData) { refreshCallbackData["folder"]); } else - refreshCallbackData["window"].alert(clabels["You cannot subscribe to a folder that you own!"] - .decodeEntities()); + refreshCallbackData["window"].alert(clabels["You cannot subscribe to a folder that you own!"]); } function openRightsForUserID(userID) { diff --git a/UI/WebServerResources/UIxAppointmentEditor.js b/UI/WebServerResources/UIxAppointmentEditor.js index 91c58ecab..9f89c798f 100644 --- a/UI/WebServerResources/UIxAppointmentEditor.js +++ b/UI/WebServerResources/UIxAppointmentEditor.js @@ -43,36 +43,36 @@ function validateAptEditor() { e = $('summary'); if (e.value.length == 0) { - if (!confirm(labels.validate_notitle.decodeEntities())) + if (!confirm(labels.validate_notitle)) return false; } e = $('startTime_date'); if (e.value.length != 10) { - alert(labels.validate_invalid_startdate.decodeEntities()); + alert(labels.validate_invalid_startdate); return false; } startdate = e.calendar.prs_date(e.value); if (startdate == null) { - alert(labels.validate_invalid_startdate.decodeEntities()); + alert(labels.validate_invalid_startdate); return false; } e = $('endTime_date'); if (e.value.length != 10) { - alert(labels.validate_invalid_enddate.decodeEntities()); + alert(labels.validate_invalid_enddate); return false; } enddate = e.calendar.prs_date(e.value); if (enddate == null) { - alert(labels.validate_invalid_enddate.decodeEntities()); + alert(labels.validate_invalid_enddate); return false; } // cuicui = ''; tmpdate = uixEarlierDate(startdate, enddate); if (tmpdate == enddate) { // window.alert(cuicui); - alert(labels.validate_endbeforestart.decodeEntities()); + alert(labels.validate_endbeforestart); return false; } else if (tmpdate == null /* means: same date */) { @@ -83,14 +83,14 @@ function validateAptEditor() { end = parseInt(document.forms[0]['endTime_time_hour'].value); if (start > end) { - alert(labels.validate_endbeforestart.decodeEntities()); + alert(labels.validate_endbeforestart); return false; } else if (start == end) { start = parseInt(document.forms[0]['startTime_time_minute'].value); end = parseInt(document.forms[0]['endTime_time_minute'].value); if (start > end) { - alert(labels.validate_endbeforestart.decodeEntities()); + alert(labels.validate_endbeforestart); return false; } } diff --git a/UI/WebServerResources/UIxComponentEditor.js b/UI/WebServerResources/UIxComponentEditor.js index f15648bfb..7d51eed66 100644 --- a/UI/WebServerResources/UIxComponentEditor.js +++ b/UI/WebServerResources/UIxComponentEditor.js @@ -24,7 +24,7 @@ function onPopupUrlWindow(event) { preventDefault(event); var urlInput = document.getElementById("url"); - var newUrl = window.prompt(labels["Target:"].decodeEntities(), urlInput.value); + var newUrl = window.prompt(labels["Target:"], urlInput.value); if (newUrl != null) { var documentHref = $("documentHref"); var documentLabel = $("documentLabel"); diff --git a/UI/WebServerResources/UIxContactEditor.js b/UI/WebServerResources/UIxContactEditor.js index 32670f0bf..b17720dad 100644 --- a/UI/WebServerResources/UIxContactEditor.js +++ b/UI/WebServerResources/UIxContactEditor.js @@ -84,13 +84,13 @@ function validateContactEditor() { if (e.value.length == 0) return true; if (uixEmailRegex.test(e.value) != true) - return confirm(labels.invalidemailwarn.decodeEntities()); + return confirm(labels.invalidemailwarn); e = $('homeMail'); if (e.value.length == 0) return true; if (uixEmailRegex.test(e.value) != true) - return confirm(labels.invalidemailwarn.decodeEntities()); + return confirm(labels.invalidemailwarn); return true; } diff --git a/UI/WebServerResources/UIxContactsUserFolders.js b/UI/WebServerResources/UIxContactsUserFolders.js index d060d319a..bfcbd245c 100644 --- a/UI/WebServerResources/UIxContactsUserFolders.js +++ b/UI/WebServerResources/UIxContactsUserFolders.js @@ -30,7 +30,7 @@ function addLineToTree(tree, parent, line) { for (var i = 1; i < nodes.length; i++) { var folderInfos = nodes[i].split(":"); var icon = ResourcesURL + '/'; - if (folderInfos[2] == 'contact') + if (folderInfos[2] == 'Contacts') icon += 'tb-mail-addressbook-flat-16x16.png'; else icon += 'calendar-folder-16x16.png'; diff --git a/UI/WebServerResources/UIxMailEditor.js b/UI/WebServerResources/UIxMailEditor.js index d84345b71..bddc2b238 100644 --- a/UI/WebServerResources/UIxMailEditor.js +++ b/UI/WebServerResources/UIxMailEditor.js @@ -133,8 +133,7 @@ function validateEditorInput(sender) { errortext = errortext + labels.error_missingrecipients + "\n"; if (errortext.length > 0) { - alert(labels.error_validationfailed.decodeEntities() + ":\n" - + errortext.decodeEntities()); + alert(labels.error_validationfailed + ":\n" + errortext); return false; } return true; diff --git a/UI/WebServerResources/UIxTaskEditor.js b/UI/WebServerResources/UIxTaskEditor.js index b68d3dac9..5207d57e7 100644 --- a/UI/WebServerResources/UIxTaskEditor.js +++ b/UI/WebServerResources/UIxTaskEditor.js @@ -24,7 +24,7 @@ function validateDate(date, label) { dateValue = date.calendar.prs_date(date.value); if (date.value.length != 10 || !dateValue) { - alert(label.decodeEntities()); + alert(label); result = false; } else result = dateValue; @@ -37,7 +37,7 @@ function validateTaskEditor() { e = document.getElementById('summary'); if (e.value.length == 0 - && !confirm(labels.validate_notitle.decodeEntities())) + && !confirm(labels.validate_notitle)) return false; e = document.getElementById('startTime_date'); @@ -58,7 +58,7 @@ function validateTaskEditor() { tmpdate = uixEarlierDate(startdate, enddate); if (tmpdate == enddate) { // window.alert(cuicui); - alert(labels.validate_endbeforestart.decodeEntities()); + alert(labels.validate_endbeforestart); return false; } else if (tmpdate == null /* means: same date */) { @@ -69,14 +69,14 @@ function validateTaskEditor() { end = parseInt(document.forms[0]['dueTime_time_hour'].value); if (start > end) { - alert(labels.validate_endbeforestart.decodeEntities()); + alert(labels.validate_endbeforestart); return false; } else if (start == end) { start = parseInt(document.forms[0]['startTime_time_minute'].value); end = parseInt(document.forms[0]['dueTime_time_minute'].value); if (start > end) { - alert(labels.validate_endbeforestart.decodeEntities()); + alert(labels.validate_endbeforestart); return false; } } diff --git a/UI/WebServerResources/add-user-calendar.png b/UI/WebServerResources/add-user-calendar.png new file mode 100644 index 000000000..30c392672 Binary files /dev/null and b/UI/WebServerResources/add-user-calendar.png differ diff --git a/UI/WebServerResources/generic.js b/UI/WebServerResources/generic.js index fcc12081d..9a7c60538 100644 --- a/UI/WebServerResources/generic.js +++ b/UI/WebServerResources/generic.js @@ -983,6 +983,7 @@ function subscribeToFolder(refreshCallback, refreshCallbackData) { document.subscriptionAjaxRequest.aborted = true; document.subscriptionAjaxRequest.abort(); } + var rfCbData = { method: refreshCallback, data: refreshCallbackData }; document.subscriptionAjaxRequest = triggerAjaxRequest(url, folderSubscriptionCallback, @@ -1006,29 +1007,42 @@ function folderUnsubscriptionCallback(http) { } function unsubscribeFromFolder(folder, refreshCallback, refreshCallbackData) { - if (document.body.hasClassName("popup")) { - window.opener.unsubscribeFromFolder(folder, refreshCallback, - refreshCallbackData); - } - else { - var folderData = folder.split(":"); - var username = folderData[0]; - var folderPath = folderData[1]; - if (username != UserLogin) { - var url = (UserFolderURL + "../" + username - + "/" + folderPath + "/unsubscribe"); - if (document.unsubscriptionAjaxRequest) { - document.unsubscriptionAjaxRequest.aborted = true; - document.unsubscriptionAjaxRequest.abort(); - } - var rfCbData = { method: refreshCallback, data: refreshCallbackData }; - document.unsubscriptionAjaxRequest - = triggerAjaxRequest(url, folderUnsubscriptionCallback, - rfCbData); + if (document.body.hasClassName("popup")) { + window.opener.unsubscribeFromFolder(folder, refreshCallback, + refreshCallbackData); + } + else { + var folderData = folder.split("+"); + var username = folderData[0]; + var folderPath = folderData[1]; + if (username != UserLogin) { + var url = (ApplicationBaseURL + folder + "/unsubscribe"); + if (document.unsubscriptionAjaxRequest) { + document.unsubscriptionAjaxRequest.aborted = true; + document.unsubscriptionAjaxRequest.abort(); } - else - window.alert(clabels["You cannot unsubscribe from a folder that you own!"].decodeEntities()); - } + var rfCbData = { method: refreshCallback, data: refreshCallbackData }; + document.unsubscriptionAjaxRequest + = triggerAjaxRequest(url, folderUnsubscriptionCallback, + rfCbData); + } + else + window.alert(clabels["You cannot unsubscribe from a folder that you own!"].decodeEntities()); + } +} + +function accessToSubscribedFolder(serverFolder) { + var folder; + + var parts = serverFolder.split(":"); + if (parts.length > 1) { + var paths = parts[1].split("/"); + folder = "/" + parts[0] + "_" + paths[2]; + } + else + folder = serverFolder; + + return folder; } function listRowMouseDownHandler(event) { @@ -1289,12 +1303,25 @@ function onLoadHandler(event) { configureDragHandles(); configureSortableTableHeaders(); configureLinkBanner(); + translateLabels(); var progressImage = $("progressIndicator"); if (progressImage) progressImage.parentNode.removeChild(progressImage); Event.observe(document.body, "contextmenu", onBodyClickContextMenu); } +function translateLabels() { + if (typeof labels != "undefined") { + for (var key in labels) + labels[key] = labels[key].decodeEntities(); + } + + if (typeof clabels != "undefined") { + for (var key in clabels) + clabels[key] = clabels[key].decodeEntities(); + } +} + function onBodyClickContextMenu(event) { preventDefault(event); } @@ -1340,6 +1367,38 @@ function configureLinkBanner() { } } +/* folder creation */ +function createFolder(name, okCB, notOkCB) { + if (name) { + if (document.newFolderAjaxRequest) { + document.newFolderAjaxRequest.aborted = true; + document.newFolderAjaxRequest.abort(); + } + var url = ApplicationBaseURL + "/createFolder?name=" + name; + document.newFolderAjaxRequest + = triggerAjaxRequest(url, createFolderCallback, + {name: name, + okCB: okCB, + notOkCB: notOkCB}); + } +} + +function createFolderCallback(http) { + if (http.readyState == 4) { + var data = http.callbackData; + if (http.status == 201) { + if (data.okCB) + data.okCB(data.name, "/" + http.responseText); + } + else { + if (data.notOkCB) + data.notOkCB(name); + else + log("ajax problem:" + http.status); + } + } +} + addEvent(window, 'load', onLoadHandler); function parent$(element) { diff --git a/UI/WebServerResources/lori-login.jpg b/UI/WebServerResources/lori-login.jpg index a8947edb4..7ab42d121 100644 Binary files a/UI/WebServerResources/lori-login.jpg and b/UI/WebServerResources/lori-login.jpg differ