]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1173 d1b88da0-ebda-0310...
authorwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Fri, 14 Sep 2007 22:04:17 +0000 (22:04 +0000)
committerwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Fri, 14 Sep 2007 22:04:17 +0000 (22:04 +0000)
103 files changed:
ChangeLog
NEWS
SoObjects/Appointments/English.lproj/Localizable.strings [new file with mode: 0644]
SoObjects/Appointments/French.lproj/Localizable.strings [new file with mode: 0644]
SoObjects/Appointments/GNUmakefile
SoObjects/Appointments/German.lproj/Localizable.strings [new file with mode: 0644]
SoObjects/Appointments/SOGoAppointmentFolder.h
SoObjects/Appointments/SOGoAppointmentFolder.m
SoObjects/Appointments/SOGoAppointmentObject.h
SoObjects/Appointments/SOGoAppointmentObject.m
SoObjects/Appointments/SOGoTaskObject.h
SoObjects/Appointments/SOGoTaskObject.m
SoObjects/Appointments/product.plist
SoObjects/Contacts/English.lproj/Localizable.strings [new file with mode: 0644]
SoObjects/Contacts/French.lproj/Localizable.strings [new file with mode: 0644]
SoObjects/Contacts/German.lproj/Localizable.strings [new file with mode: 0644]
SoObjects/Contacts/SOGoContactFolder.h
SoObjects/Contacts/SOGoContactFolders.h
SoObjects/Contacts/SOGoContactFolders.m
SoObjects/Contacts/SOGoContactGCSFolder.h
SoObjects/Contacts/SOGoContactGCSFolder.m
SoObjects/Contacts/SOGoContactLDAPFolder.h
SoObjects/Contacts/SOGoContactLDAPFolder.m
SoObjects/Contacts/product.plist
SoObjects/SOGo/GNUmakefile
SoObjects/SOGo/NSDictionary+Utilities.h
SoObjects/SOGo/NSDictionary+Utilities.m
SoObjects/SOGo/SOGoContentObject.m
SoObjects/SOGo/SOGoFolder.h
SoObjects/SOGo/SOGoFolder.m
SoObjects/SOGo/SOGoObject.h
SoObjects/SOGo/SOGoObject.m
SoObjects/SOGo/SOGoParentFolder.h [new file with mode: 0644]
SoObjects/SOGo/SOGoParentFolder.m [new file with mode: 0644]
SoObjects/SOGo/SOGoUserFolder.m
UI/Common/English.lproj/Localizable.strings
UI/Common/French.lproj/Localizable.strings
UI/Common/GNUmakefile
UI/Common/German.lproj/Localizable.strings
UI/Common/UIxFolderActions.h
UI/Common/UIxFolderActions.m
UI/Common/WODirectAction+SOGo.h
UI/Common/WODirectAction+SOGo.m
UI/Common/product.plist
UI/Contacts/English.lproj/Localizable.strings
UI/Contacts/French.lproj/Localizable.strings
UI/Contacts/German.lproj/Localizable.strings
UI/Contacts/Toolbars/SOGoContactFolder.toolbar
UI/Contacts/UIxContactEditor.m
UI/Contacts/UIxContactFoldersView.m
UI/Contacts/UIxContactsListView.m
UI/Contacts/UIxContactsListViewContainer.h
UI/Contacts/UIxContactsListViewContainer.m
UI/Contacts/product.plist
UI/MailerUI/English.lproj/Localizable.strings
UI/MailerUI/Toolbars/SOGoDraftObject.toolbar
UI/MailerUI/Toolbars/SOGoMailObject.toolbar
UI/MailerUI/UIxMailAccountActions.m
UI/MailerUI/UIxMailActions.m
UI/MailerUI/UIxMailEditor.m
UI/MailerUI/UIxMailFolderActions.m
UI/MailerUI/UIxMailListView.m
UI/MailerUI/UIxMailSourceView.m
UI/MailerUI/UIxMailView.m
UI/MailerUI/product.plist
UI/MainUI/SOGoRootPage.m
UI/MainUI/product.plist
UI/PreferencesUI/UIxPreferences.m
UI/SOGoUI/UIxComponent.h
UI/SOGoUI/UIxComponent.m
UI/Scheduler/English.lproj/Localizable.strings
UI/Scheduler/French.lproj/Localizable.strings
UI/Scheduler/GNUmakefile
UI/Scheduler/German.lproj/Localizable.strings
UI/Scheduler/Toolbars/SOGoAppointmentFolders.toolbar [moved from UI/Scheduler/Toolbars/SOGoAppointmentFolder.toolbar with 60% similarity]
UI/Scheduler/UIxAppointmentEditor.m
UI/Scheduler/UIxCalListingActions.m
UI/Scheduler/UIxCalMainView.m
UI/Scheduler/UIxCalendarSelector.h
UI/Scheduler/UIxCalendarSelector.m
UI/Scheduler/UIxComponentEditor.m
UI/Scheduler/UIxTaskEditor.m
UI/Scheduler/product.plist
UI/Templates/ContactsUI/UIxContactsListViewContainer.wox
UI/Templates/MailerUI/UIxMailListView.wox
UI/Templates/SchedulerUI/UIxCalMainView.wox
UI/Templates/SchedulerUI/UIxCalendarSelector.wox
UI/Templates/UIxPageFrame.wox
UI/Templates/UIxToolbar.wox
UI/WebServerResources/ContactsUI.js
UI/WebServerResources/MailerUI.js
UI/WebServerResources/SOGoRootPage.css
UI/WebServerResources/SchedulerUI.js
UI/WebServerResources/UIxAclEditor.js
UI/WebServerResources/UIxAppointmentEditor.js
UI/WebServerResources/UIxComponentEditor.js
UI/WebServerResources/UIxContactEditor.js
UI/WebServerResources/UIxContactsUserFolders.js
UI/WebServerResources/UIxMailEditor.js
UI/WebServerResources/UIxTaskEditor.js
UI/WebServerResources/add-user-calendar.png [new file with mode: 0644]
UI/WebServerResources/generic.js
UI/WebServerResources/lori-login.jpg

index 6cf4ed546149a40034faddd3a383689a19285a98..8ce905a88f479a5ceebcb1aafd9e71c3bcf54d27 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,79 @@
+2007-09-14  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * 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  <wsourdeau@inverse.ca>
+
+       * 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  <wsourdeau@inverse.ca>
+
+       * SoObjects/SOGo/SOGoFolder.m ([SOGoFolder
+       +folderWithName:aNameandDisplayName:aDisplayNameinContainer:aContainer]):
+       new method.
+       ([SOGoFolder -displayName]): new method.
+       ([SOGoFolder -delete]): accept to proceed only if nameInContainer
+       != "personal".
+
+       * SoObjects/Contacts/SOGoContactLDAPFolder.m
+       ([SOGoContactLDAPFolder
+       +folderWithName:aNameandDisplayName:aDisplayNameinContainer:aContainer]):
+       renamed from "contactFolderWithName..." for compatibility with SOGoFolder.
+
+       * SoObjects/Contacts/SOGoContactGCSFolder.m ([SOGoContactGCSFolder
+       +contactFolderWithName:aNameandDisplayName:aDisplayNameinContainer:aContainer]):
+       removed method, reimplemented in SOGoFolder.
+       ([SOGoContactGCSFolder -displayName]): removed method,
+       reimplemented in SOGoFolder.
+       ([-delete]): removed method, modified in SOGoFolder.
+
+       * SoObjects/Contacts/SOGoContactFolders.[hm]: modified class to be
+       a subclass of SOGoParentFolder.
+
+       * SoObjects/SOGo/SOGoParentFolder.[hm]: new class module derived
+       from SOGoContactFolders and modified to be more content-independent.
+
+       * UI/MailerUI/UIxMailActions.m ([UIxMailActions -markMessageUnreadAction])
+       ([UIxMailActions -markMessageReadAction]): new methods moved from
+       UIxMailListView and adapted to invoke the client object directly,
+       since the previous versions had to to a lookup from the parent
+       SOGoMailFolder.
+
+       * UI/MailerUI/UIxMailListView.m ([-markMessageUnreadAction]): move
+       method into UIxMailActions.
+       ([-markMessageReadAction]): same as above.
+       ([-viewAction]): removed useless method.
+       ([-javaScriptOK]): removed useless method.
+       ([-isJavaScriptRequest]): removed useless method.
+       ([-lookupActiveMessage]): removed useless method.
+
+       * UI/Common/WODirectAction+SOGo.m ([WODirectAction
+       -responseWithStatus:status]): new method that returns a WOResponse
+       initialized with the specified status code.
+       ([WODirectAction -responseWith204]): new method that invokes the
+       above one with "204" as parameter.
+       ([WODirectAction -redirectToLocation:newLocation]): rewrote method
+       to make use of -responseWithStatus:.
+
+       * UI/SOGoUI/UIxComponent.m ([UIxComponent -responseWith204]): new
+       method that returns a WOResponse initialized with the 204 status
+       code.
+       
+       * UI/MailerUI/UIxMailListView.m ([UIxMailListView -sortedUIDs]):
+       always use a "not deleted" search qualifier along with the user
+       qualifier (if present).
+
 2007-09-10  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
 
        * UI/Contacts/UIxContactFoldersView.m ([UIxContactFoldersView
diff --git a/NEWS b/NEWS
index f0c0a1b08b2d9c060afe5440a42a08e57c693e59..b51426397a7cced75661179fc919fbb070857d11 100644 (file)
--- 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;
 - countless bugfixes;
 
diff --git a/SoObjects/Appointments/English.lproj/Localizable.strings b/SoObjects/Appointments/English.lproj/Localizable.strings
new file mode 100644 (file)
index 0000000..95feda9
--- /dev/null
@@ -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 (file)
index 0000000..11b333c
--- /dev/null
@@ -0,0 +1 @@
+"Personal Calendar" = "Agenda personnel";
index 789501a3d8fdcc8246449072ca5dc921d0d7e10f..0ae0a98f1b8298ca5e96252505815f4aaca7f649 100644 (file)
@@ -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 (file)
index 0000000..95feda9
--- /dev/null
@@ -0,0 +1 @@
+"Personal Calendar" = "Personal Calendar";
index f3e71cd8d1f1a7f01f94a5a2c741d8564a6a31ce..06edea0e6e4aac7aea9528b5fe8bc7a13439982c 100644 (file)
@@ -53,6 +53,8 @@
   NSMutableDictionary *uidToFilename;
 }
 
+- (BOOL) isActive;
+
 /* selection */
 
 - (NSArray *) calendarUIDs;
 
 - (NSArray *) fetchAllSOGoAppointments;
 
-- (NSArray *) calendarFolders;
-
 - (NSString *) roleForComponentsWithAccessClass: (iCalAccessClass) accessClass
                                        forUser: (NSString *) uid;
 
index 9fa1695fc93fc74706d8dbcb6fdd4ef308c40bf8..38c34a943cc9347f09765530538ed2a99cfd81d0 100644 (file)
@@ -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;
-    
-    folder = [self lookupCalendarFolderForUID: uid];
-    if (![folder isNotNull])
-      [self logWithFormat:@"Note: did not find folder for uid: '%@'", uid];
+  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];
+       }
     
-    /* 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;
-
-  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];
-    }
-
-  return firstShouldBeActive;
-}
+// #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;
+
+//   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;
+// }
 
-- (NSArray *) calendarFolders
-{
-  NSMutableDictionary *userCalendar, *calendarDict;
-  NSMutableArray *calendarFolders;
-  SOGoUser *calendarUser;
-  BOOL firstActive;
-
-  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];
-
-  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;
-}
+// - (NSArray *) calendarFolders
+// {
+//   NSMutableDictionary *userCalendar, *calendarDict;
+//   NSMutableArray *calendarFolders;
+//   SOGoUser *calendarUser;
+//   BOOL firstActive;
+
+//   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];
+
+//   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;
+// }
 
 // - (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"];
-
-  return newPath;
-}
+  settings = [[context activeUser] userSettings];
+  activeFolders
+    = [[settings objectForKey: @"Calendar"] objectForKey: @"ActiveFolders"];
 
-- (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 */
index d6fe91e865f9ee70a8017a6ab4670ce788730821..8262970f22f9eafa769799a54b89c27eee80ec30 100644 (file)
 
 @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
index 33e14028227b76b2908e7d2c0dd14098843d6217..404428ea716f82fab37d1241669b6af2c163e1b0 100644 (file)
@@ -34,6 +34,8 @@
 #import <SoObjects/SOGo/SOGoPermissions.h>
 
 #import "NSArray+Appointments.h"
+#import "SOGoAppointmentFolder.h"
+
 #import "SOGoAppointmentObject.h"
 
 @implementation SOGoAppointmentObject
   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
   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;
   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;
index 9f57bf588f9b33591c640c5129cb1194742ef135..da442022ba0dd9931078e877a7dcb0ac717daa59 100644 (file)
 
 @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
index 547d19e04a399fc1f1a7e4bee512669346f675ba..2357ac8b2ef6bde8ebce1ca3efd8a4f699fd440c 100644 (file)
@@ -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;
index d70b2faad499663e47f691c5c86e57bd26a88e44..1fdd67e882f73df490a68253be07ea0f9b90fab8 100644 (file)
@@ -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 (file)
index 0000000..56404b0
--- /dev/null
@@ -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 (file)
index 0000000..af4d87c
--- /dev/null
@@ -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 (file)
index 0000000..95feda9
--- /dev/null
@@ -0,0 +1 @@
+"Personal Calendar" = "Personal Calendar";
index 6ced261a51e212bd4755c0a191abf183da99d5fb..c1ff89685a18aa4e47168d71c359cfd678e785dd 100644 (file)
 
 @protocol SOGoContactFolder <NSObject>
 
-+ (id <SOGoContactFolder>) contactFolderWithName: (NSString *) aName
-                                  andDisplayName: (NSString *) aDisplayName
-                                     inContainer: (SOGoObject *) aContainer;
-
-- (id <SOGoContactFolder>) initWithName: (NSString *) aName
-                         andDisplayName: (NSString *) aDisplayName
-                            inContainer: (SOGoObject *) aContainer;
-
-- (NSString *) displayName;
-
 - (NSArray *) lookupContactsWithFilter: (NSString *) filter
                                 sortBy: (NSString *) sortKey
                               ordering: (NSComparisonResult) sortOrdering;
index 0272f3bbb0940bd62cdc019b0761178c5d34ec9c..4e271e5cff09072bbb601c71e9b628c235b1fb24 100644 (file)
@@ -1,6 +1,6 @@
 /* SOGoContactFolders.h - this file is part of SOGo
  *
- * Copyright (C) 2006 Inverse groupe conseil
+ * Copyright (C) 2006, 2007 Inverse groupe conseil
  *
  * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
  *
 #ifndef SOGOCONTACTFOLDERS_H
 #define SOGOCONTACTFOLDERS_H
 
-#import <SOGo/SOGoObject.h>
+#import <SoObjects/SOGo/SOGoParentFolder.h>
 
-@class NSMutableDictionary;
-@class NSString;
-@class WOResponse;
-
-@interface SOGoContactFolders : SOGoObject
-{
-  NSMutableDictionary *contactFolders;
-  NSString *OCSPath;
-}
-
-- (NSString *) defaultSourceName;
-
-- (void) setBaseOCSPath: (NSString *) newOCSPath;
-
-- (NSArray *) contactFolders;
-
-- (WOResponse *) newFolderWithName: (NSString *) name;
+@interface SOGoContactFolders : SOGoParentFolder
 
 @end
 
index 215870978fff5d63b09c6503919420c9ccdc6497..2d75c8f0c489dd87c3ba93f3d49d7521769d5f64 100644 (file)
@@ -1,6 +1,6 @@
 /* SOGoContactFolders.m - this file is part of SOGo
  *
- * Copyright (C) 2006 Inverse groupe conseil
+ * Copyright (C) 2006, 2007 Inverse groupe conseil
  *
  * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
  *
@@ -20,7 +20,6 @@
  * Boston, MA 02111-1307, USA.
  */
 
-/* exchange folder types:                   */
 /* MailItems                IPF.Note
    ContactItems             IPF.Contact
    AppointmentItems         IPF.Appointment
    TaskItems                IPF.Task
    JournalItems             IPF.Journal     */
 
-#import <Foundation/NSDictionary.h>
+#import <Foundation/NSArray.h>
 #import <Foundation/NSString.h>
-
-#import <NGObjWeb/NSException+HTTP.h>
-#import <NGObjWeb/WOApplication.h>
-#import <NGObjWeb/WOContext.h>
-#import <NGObjWeb/WOContext+SoObjects.h>
-#import <NGObjWeb/WOResponse.h>
-#import <NGObjWeb/SoUser.h>
-
-#import <GDLContentStore/GCSFolderManager.h>
-#import <GDLContentStore/GCSChannelManager.h>
-#import <GDLAccess/EOAdaptorChannel.h>
-#import <GDLContentStore/NSURL+GCS.h>
+#import <Foundation/NSEnumerator.h>
 
 #import <SoObjects/SOGo/LDAPUserManager.h>
-#import <SoObjects/SOGo/SOGoPermissions.h>
 
 #import "SOGoContactGCSFolder.h"
 #import "SOGoContactLDAPFolder.h"
 
 @implementation SOGoContactFolders
 
-- (id) init
-{
-  if ((self = [super init]))
-    {
-      contactFolders = nil;
-      OCSPath = nil;
-    }
-
-  return self;
-}
-
-- (void) dealloc
++ (NSString *) gcsFolderType
 {
-  if (contactFolders)
-    [contactFolders release];
-  if (OCSPath)
-    [OCSPath release];
-  [super dealloc];
-}
-
-- (void) _fetchPersonalFolders: (NSString *) sql
-                  withChannel: (EOAdaptorChannel *) fc
-{
-  NSArray *attrs;
-  NSDictionary *row;
-  SOGoContactGCSFolder *ab;
-  BOOL hasPersonal;
-  NSString *key, *path;
-
-  hasPersonal = NO;
-  [fc evaluateExpressionX: sql];
-  attrs = [fc describeResults: NO];
-  row = [fc fetchAttributes: attrs withZone: NULL];
-  while (row)
-    {
-      ab = [SOGoContactGCSFolder
-            contactFolderWithName: [row objectForKey: @"c_path4"]
-            andDisplayName: [row objectForKey: @"c_foldername"]
-            inContainer: self];
-      key = [row objectForKey: @"c_path4"];
-      hasPersonal = (hasPersonal || [key isEqualToString: @"personal"]);
-      [ab setOCSPath: [NSString stringWithFormat: @"%@/%@",
-                               OCSPath, key]];
-      [contactFolders setObject: ab forKey: key];
-      row = [fc fetchAttributes: attrs withZone: NULL];
-    }
-
-  if (!hasPersonal)
-    {
-      ab = [SOGoContactGCSFolder contactFolderWithName: @"personal"
-                                andDisplayName: @"Contacts"
-                                inContainer: self];
-      path = [NSString stringWithFormat:
-                        @"/Users/%@/Contacts/personal",
-                      [self ownerInContext: context]];
-      [ab setOCSPath: path];
-      [contactFolders setObject: ab forKey: @"personal"];
-    }
+  return @"Contact";
 }
 
-- (void) appendPersonalSources
++ (Class) subFolderClass
 {
-  GCSChannelManager *cm;
-  EOAdaptorChannel *fc;
-  NSURL *folderLocation;
-  NSString *sql;
-
-  cm = [GCSChannelManager defaultChannelManager];
-  folderLocation
-    = [[GCSFolderManager defaultFolderManager] folderInfoLocation];
-  fc = [cm acquireOpenChannelForURL: folderLocation];
-  if (fc)
-    {
-      sql = [NSString
-             stringWithFormat: (@"SELECT c_path4, c_foldername FROM %@"
-                                @" WHERE c_path2 = '%@'"
-                                @" AND c_folder_type = 'Contact'"),
-             [folderLocation gcsTableName], [self ownerInContext: context]];
-      [self _fetchPersonalFolders: sql withChannel: fc];
-      [cm releaseChannel: fc];
-//       sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'", 
-//                  uidColumnName, [self uid]];
-    }
+  return [SOGoContactGCSFolder class];
 }
 
 - (void) appendSystemSources
   while (currentSourceID)
     {
       displayName = [um displayNameForSourceWithID: currentSourceID];
-      currentFolder = [SOGoContactLDAPFolder contactFolderWithName: currentSourceID
+      currentFolder = [SOGoContactLDAPFolder folderWithName: currentSourceID
                                             andDisplayName: displayName
                                             inContainer: self];
       [currentFolder setLDAPSource: [um sourceWithID: currentSourceID]];
-      [contactFolders setObject: currentFolder forKey: currentSourceID];
+      [subFolders setObject: currentFolder forKey: currentSourceID];
       currentSourceID = [sourceIDs nextObject];
     }
 }
 
-- (WOResponse *) newFolderWithName: (NSString *) name
-{
-  SOGoContactGCSFolder *newFolder;
-  WOResponse *response;
-
-  newFolder = [SOGoContactGCSFolder contactFolderWithName: name
-                                    andDisplayName: name
-                                    inContainer: self];
-  if ([newFolder isKindOfClass: [NSException class]])
-    response = (WOResponse *) newFolder;
-  else
-    {
-      [newFolder setOCSPath: [NSString stringWithFormat: @"%@/%@",
-                                       OCSPath, name]];
-      if ([newFolder create])
-        {
-          response = [WOResponse new];
-          [response setStatus: 201];
-          [response autorelease];
-        }
-      else
-        response = [NSException exceptionWithHTTPStatus: 400
-                                reason: @"The new folder could not be created"];
-    }
-
-  return response;
-}
-
-- (void) initContactSources
-{
-  if (!contactFolders)
-    {
-      contactFolders = [NSMutableDictionary new];
-      [self appendPersonalSources];
-      [self appendSystemSources];
-    }
-}
-
-- (id) lookupName: (NSString *) name
-        inContext: (WOContext *) lookupContext
-          acquire: (BOOL) acquire
-{
-  id obj;
-
-  /* first check attributes directly bound to the application */
-  obj = [super lookupName: name inContext: lookupContext acquire: NO];
-  if (!obj)
-    {
-      if (!contactFolders)
-        [self initContactSources];
-
-      obj = [contactFolders objectForKey: name];
-      if (!obj)
-       obj = [NSException exceptionWithHTTPStatus: 404];
-    }
-
-  return obj;
-}
-
-- (NSArray *) toManyRelationshipKeys
-{
-  if (!contactFolders)
-    [self initContactSources];
-
-  return [contactFolders allKeys];
-}
-
-- (NSArray *) contactFolders
-{
-  if (!contactFolders)
-    [self initContactSources];
-
-  return [contactFolders allValues];
-}
-
-/* acls */
-- (NSArray *) aclsForUser: (NSString *) uid
-{
-  return nil;
-}
-
-- (BOOL) davIsCollection
-{
-  return YES;
-}
-
-- (NSString *) davContentType
-{
-  return @"httpd/unix-directory";
-}
-
-- (void) setBaseOCSPath: (NSString *) newOCSPath
-{
-  if (OCSPath)
-    [OCSPath release];
-  OCSPath = newOCSPath;
-  if (OCSPath)
-    [OCSPath retain];
-}
-
-/* web interface */
-- (NSString *) defaultSourceName
+- (NSString *) defaultFolderName
 {
-  return @"personal";
+  return @"Personal Address Book";
 }
 
 @end
index 1dd91a53b608f2f74159468dd97db5b5e6aec09e..d10913d0b3010685805893febd7c0e547354614d 100644 (file)
@@ -30,9 +30,6 @@
 @class NSString;
 
 @interface SOGoContactGCSFolder : SOGoFolder <SOGoContactFolder>
-{
-  NSString *displayName;
-}
 
 @end
 
index 47eb5f13c20737037513d9eb81830082cc5f4505..e00e9674992cf3fba3f42ca299eff5c280bb1bb8 100644 (file)
@@ -21,6 +21,7 @@
 
 #import <Foundation/NSArray.h>
 #import <Foundation/NSString.h>
+
 #import <NGObjWeb/NSException+HTTP.h>
 #import <NGObjWeb/SoObject+SoDAV.h>
 #import <NGObjWeb/WOContext.h>
 
 @implementation SOGoContactGCSFolder
 
-+ (id <SOGoContactFolder>) contactFolderWithName: (NSString *) aName
-                                  andDisplayName: (NSString *) aDisplayName
-                                     inContainer: (SOGoObject *) aContainer
-{
-  SOGoContactGCSFolder *folder;
-
-  folder = [[self alloc] initWithName: aName
-                         andDisplayName: aDisplayName
-                         inContainer: aContainer];
-  [folder autorelease];
-
-  return folder;
-}
-
-- (void) dealloc
-{
-  [displayName release];
-  [super dealloc];
-}
-
-- (id <SOGoContactFolder>) initWithName: (NSString *) newName
-                         andDisplayName: (NSString *) newDisplayName
-                            inContainer: (SOGoObject *) newContainer
-{
-  if ((self = [self initWithName: newName
-                    inContainer: newContainer]))
-    ASSIGN (displayName, newDisplayName);
-
-  return self;
-}
-
-- (BOOL) folderIsMandatory
-{
-  return [nameInContainer isEqualToString: @"personal"];
-}
-
-- (NSString *) displayName
-{
-  return displayName;
-}
-
 /* name lookup */
 
 - (id <SOGoContactObject>) lookupContactWithId: (NSString *) recordId
   BOOL isPut;
 
   isPut = NO;
-  /* first check attributes directly bound to the application */
   obj = [super lookupName:_key inContext:_ctx acquire:NO];
   if (!obj)
     {
   return @"vcard-collection";
 }
 
-- (NSException *) delete
-{
-  return (([nameInContainer isEqualToString: @"personal"])
-         ? [NSException exceptionWithHTTPStatus: 403
-                        reason: @"the 'personal' folder cannot be deleted"]
-         : [super delete]);
-}
-
 // /* GET */
 
 // - (id) GETAction: (id)_ctx
index ad1ceb89cbe0c83eb9983751d4271e67da959b1e..e405a3faf5c00f85db40d6617472523a30d1d750 100644 (file)
   BOOL ignoreSoObjectHunger;
 }
 
-- (id <SOGoContactFolder>) initWithName: (NSString *) newName
-                         andDisplayName: (NSString *) newDisplayName
-                            inContainer: (SOGoObject *) newContainer;
++ (id) folderWithName: (NSString *) aName
+       andDisplayName: (NSString *) aDisplayName
+         inContainer: (id) aContainer;
+- (id) initWithName: (NSString *) newName
+     andDisplayName: (NSString *) newDisplayName
+       inContainer: (id) newContainer;
 - (void) setLDAPSource: (LDAPSource *) newLdapSource;
 
 @end
index 2704be3321abfd684d775400a5d9cf23ce2fe24f..459f2998eb8512021ae5b1e7f4f5ed73f9170d08 100644 (file)
 
 @implementation SOGoContactLDAPFolder
 
-+ (id <SOGoContactFolder>) contactFolderWithName: (NSString *) aName
-                                  andDisplayName: (NSString *) aDisplayName
-                                     inContainer: (SOGoObject *) aContainer
++ (id) folderWithName: (NSString *) aName
+       andDisplayName: (NSString *) aDisplayName
+         inContainer: (id) aContainer
 {
-  SOGoContactLDAPFolder *folder;
+  id folder;
 
   folder = [[self alloc] initWithName: aName
                          andDisplayName: aDisplayName
@@ -68,9 +68,9 @@
   return self;
 }
 
-- (id <SOGoContactFolder>) initWithName: (NSString *) newName
-                         andDisplayName: (NSString *) newDisplayName
-                            inContainer: (SOGoObject *) newContainer
+- (id) initWithName: (NSString *) newName
+     andDisplayName: (NSString *) newDisplayName
+       inContainer: (id) newContainer
 {
   if ((self = [self initWithName: newName
                    inContainer: newContainer]))
 }
 
 /* acls */
+- (NSString *) ownerInContext: (WOContext *) noContext
+{
+  return @"nobody";
+}
+
 /* TODO: this might change one day when we support LDAP acls */
 - (NSArray *) aclsForUser: (NSString *) uid
 {
index cb6b5124dc868ad1ba6629adbc2b5d950c4b8d9e..e1557d613524f3cd4da96689b69bac1485c25bb9 100644 (file)
@@ -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";
index f8a602b736aec1dde1b40141472bb3fc7948c0fd..d2b9d35688049d5d0240aebb9c20e0bf7c6ed54d 100644 (file)
@@ -21,8 +21,9 @@ FHS_HEADER_DIRS = SOGo
 
 libSOGo_HEADER_FILES = \
        SOGoObject.h                    \
-       SOGoFolder.h                    \
        SOGoContentObject.h             \
+       SOGoFolder.h                    \
+       SOGoParentFolder.h              \
        SOGoUserFolder.h                \
        SOGoGroupsFolder.h              \
        SOGoGroupFolder.h               \
@@ -50,8 +51,9 @@ libSOGo_HEADER_FILES = \
 
 libSOGo_OBJC_FILES = \
        SOGoObject.m                    \
-       SOGoFolder.m                    \
        SOGoContentObject.m             \
+       SOGoFolder.m                    \
+       SOGoParentFolder.m              \
        SOGoUserFolder.m                \
        SOGoGroupsFolder.m              \
        SOGoGroupFolder.m               \
index bc054c813e87179f0a2ac92734661dcbff0a4c1c..1c5622f137739b82d771584822dcc9a848c3b3b4 100644 (file)
@@ -29,6 +29,8 @@
 
 @interface NSDictionary (SOGoDictionaryUtilities)
 
++ (NSDictionary *) dictionaryFromStringsFile: (NSString *) file;
+
 - (NSString *) jsonRepresentation;
 - (NSString *) keysWithFormat: (NSString *) keyFormat;
 
index 6caaeaa67033ff7bb47016a4f6ef2e7d7cc0d40b..666aef023a77ecdd9dfa713daa51f0280956b3a1 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 #import <Foundation/NSArray.h>
+#import <Foundation/NSData.h>
 #import <Foundation/NSString.h>
 
 #import "NSArray+Utilities.h"
 
 @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;
index 945382da5b1d2d27cb399318e43d7e4e307fbdc4..255db3b6506c29db6b481f02131c7853a1ca30a8 100644 (file)
   needsLocation = NO;
   tmp = [[self nameInContainer] stringByDeletingPathExtension];
   if ([tmp isEqualToString:@"new"]) {
-    tmp = [[[self container] class] globallyUniqueObjectId];
+    tmp = [self globallyUniqueObjectId];
     needsLocation = YES;
     
     [self debugWithFormat:
 
 - (NSArray *) aclUsers
 {
-  return [container aclUsersForObjectAtPath: [self pathArrayToSoObject]];
+  return [container aclUsersForObjectAtPath: [self pathArrayToSOGoObject]];
 }
 
 - (NSArray *) aclsForUser: (NSString *) uid
 
   acls = [NSMutableArray array];
   ownAcls = [container aclsForUser: uid
-                      forObjectAtPath: [self pathArrayToSoObject]];
+                      forObjectAtPath: [self pathArrayToSOGoObject]];
   [acls addObjectsFromArray: ownAcls];
   containerAcls = [container aclsForUser: uid];
   if ([containerAcls count] > 0)
index 5358299d37c72669875585ca8016daac126e360c..ed57b8059530bceac1873d63e8680a5e2d2a7edc 100644 (file)
 
 @interface SOGoFolder : SOGoObject
 {
-  NSString  *ocsPath;
+  NSMutableString *displayName;
+  NSString *ocsPath;
   GCSFolder *ocsFolder;
   NSMutableDictionary *aclCache;
 }
 
-+ (NSString *) globallyUniqueObjectId;
++ (id) folderWithSubscriptionReference: (NSString *) reference
+                          inContainer: (id) aContainer;
 
 /* accessors */
 
+- (void) setDisplayName: (NSString *) newDisplayName;
+- (NSString *) displayName;
+
 - (void) setOCSPath: (NSString *)_Path;
 - (NSString *) ocsPath;
 
 - (NSString *) outlookFolderClass;
 
 - (BOOL) folderIsMandatory;
+- (NSString *) folderType;
 
 - (BOOL) create;
 - (NSException *) delete;
-
+- (void) renameTo: (NSString *) newName;
 /* dav */
 - (NSArray *) davNamespaces;
 
index 20a3b8944cba79139f75160b10da6e34c00ba18e..a78d50740a1215711cac3a14350ab43c8ed83784 100644 (file)
@@ -19,9 +19,6 @@
   02111-1307, USA.
 */
 
-#import <unistd.h>
-#import <stdlib.h>
-
 #import <Foundation/NSArray.h>
 #import <Foundation/NSDate.h>
 #import <Foundation/NSDictionary.h>
@@ -29,6 +26,7 @@
 #import <Foundation/NSKeyValueCoding.h>
 #import <Foundation/NSURL.h>
 
+#import <NGObjWeb/NSException+HTTP.h>
 #import <NGObjWeb/SoObject.h>
 #import <NGObjWeb/SoObject+SoDAV.h>
 #import <NGObjWeb/SoSelectorInvocation.h>
 #import <NGExtensions/NSObject+Logs.h>
 #import <EOControl/EOQualifier.h>
 #import <GDLAccess/EOAdaptorChannel.h>
+#import <GDLContentStore/GCSChannelManager.h>
 #import <GDLContentStore/GCSFolderManager.h>
 #import <GDLContentStore/GCSFolder.h>
 #import <GDLContentStore/GCSFolderType.h>
+#import <GDLContentStore/NSURL+GCS.h>
 #import <SaxObjC/XMLNamespaces.h>
 
 #import "NSArray+Utilities.h"
@@ -66,33 +66,38 @@ static NSString *defaultUserID = @"<default>";
             NSStringFromClass([self superclass]), [super version]);
 }
 
-+ (NSString *) globallyUniqueObjectId
++ (id) folderWithSubscriptionReference: (NSString *) reference
+                          inContainer: (id) aContainer
 {
-  /*
-    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 newFolder;
+  NSArray *elements, *pathElements;
+  NSString *ocsPath, *objectPath, *owner, *ocsName, *folderName;
+
+  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 setOCSPath: ocsPath];
+  [newFolder setOwner: owner];
+
+  return newFolder;
 }
 
 - (id) init
 {
   if ((self = [super init]))
     {
+      displayName = nil;
       ocsPath = nil;
       ocsFolder = nil;
       aclCache = [NSMutableDictionary new];
@@ -106,6 +111,7 @@ static NSString *defaultUserID = @"<default>";
   [ocsFolder release];
   [ocsPath release];
   [aclCache release];
+  [displayName release];
   [super dealloc];
 }
 
@@ -118,13 +124,12 @@ static NSString *defaultUserID = @"<default>";
 
 - (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
@@ -149,9 +154,73 @@ static NSString *defaultUserID = @"<default>";
 
 - (BOOL) folderIsMandatory
 {
-  [self subclassResponsibility: _cmd];
+  return [nameInContainer isEqualToString: @"personal"];
+}
+
+- (void) _setDisplayNameFromRow: (NSDictionary *) row
+{
+  NSString *currentLogin, *ownerLogin;
+  NSDictionary *ownerIdentity;
 
-  return NO;
+  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
@@ -188,7 +257,9 @@ static NSString *defaultUserID = @"<default>";
 {
   NSException *result;
 
+//   [self dieHard];
   result = [[self folderManager] createFolderOfType: [self folderType]
+                                withName: displayName
                                  atPath: ocsPath];
 
   return (result == nil);
@@ -196,7 +267,42 @@ static NSString *defaultUserID = @"<default>";
 
 - (NSException *) delete
 {
-  return [[self folderManager] deleteFolderAtPath: ocsPath];
+  NSException *error;
+
+  if ([nameInContainer isEqualToString: @"personal"])
+    error = [NSException exceptionWithHTTPStatus: 403
+                        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
@@ -470,7 +576,7 @@ static NSString *defaultUserID = @"<default>";
 /* acls */
 - (NSArray *) aclUsers
 {
-  return [self aclUsersForObjectAtPath: [self pathArrayToSoObject]];
+  return [self aclUsersForObjectAtPath: [self pathArrayToSOGoObject]];
 }
 
 - (NSArray *) aclsForUser: (NSString *) uid
@@ -480,7 +586,7 @@ static NSString *defaultUserID = @"<default>";
 
   acls = [NSMutableArray array];
   ownAcls = [self aclsForUser: uid
-                 forObjectAtPath: [self pathArrayToSoObject]];
+                 forObjectAtPath: [self pathArrayToSOGoObject]];
   [acls addObjectsFromArray: ownAcls];
   if ([container respondsToSelector: @selector (aclsForUser:)])
     {
@@ -503,13 +609,13 @@ static NSString *defaultUserID = @"<default>";
 {
   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
index 67142bfef0c42918aed124628dda24ba1b547ba4..5404508a03d001cec8d7c7fa906ad37841bf35d9 100644 (file)
@@ -61,6 +61,9 @@
   id container;
 }
 
++ (NSString *) globallyUniqueObjectId;
+- (NSString *) globallyUniqueObjectId;
+
 + (id) objectWithName: (NSString *)_name inContainer:(id)_container;
 
 - (id) initWithName: (NSString *) _name inContainer:(id)_container;
 - (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;
 
 /* etag support */
 
-- (NSException *)matchesRequestConditionInContext:(id)_ctx;
+- (NSException *) matchesRequestConditionInContext:(id)_ctx;
 
 /* acls */
 
index 280211e53699e2aaaafdc7727336af1fbdc5275a..31a6b6ce6e9ab4f304df2d436e5322ff65964643 100644 (file)
@@ -24,6 +24,9 @@
        Please use gnustep-base instead.
 #endif
 
+#import <unistd.h>
+
+#import <Foundation/NSBundle.h>
 #import <Foundation/NSClassDescription.h>
 #import <Foundation/NSString.h>
 #import <Foundation/NSUserDefaults.h>
@@ -35,6 +38,7 @@
 #import <NGObjWeb/WEClientCapabilities.h>
 #import <NGObjWeb/WOApplication.h>
 #import <NGObjWeb/WOContext.h>
+#import <NGObjWeb/WOResourceManager.h>
 #import <NGObjWeb/WOResponse.h>
 #import <NGObjWeb/WORequest.h>
 #import <NGObjWeb/WORequest+So.h>
@@ -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
new file mode 100644 (file)
index 0000000..c54d08f
--- /dev/null
@@ -0,0 +1,51 @@
+/* SOGoParentFolder.h - this file is part of SOGo
+ *
+ * Copyright (C) 2006, 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SOGOPARENTFOLDERS_H
+#define SOGOPARENTFOLDERS_H
+
+#import "SOGoObject.h"
+
+@class NSMutableDictionary;
+@class NSString;
+@class WOResponse;
+
+@interface SOGoParentFolder : SOGoObject
+{
+  NSMutableDictionary *subFolders;
+  NSString *OCSPath;
+  Class subFolderClass;
+}
+
++ (NSString *) gcsFolderType;
++ (Class) subFolderClass;
+
+- (void) setBaseOCSPath: (NSString *) newOCSPath;
+
+- (NSArray *) subFolders;
+
+- (NSException *) newFolderWithName: (NSString *) name
+                   nameInContainer: (NSString **) newNameInContainer;
+
+@end
+
+#endif /* SOGOPARENTFOLDERS_H */
diff --git a/SoObjects/SOGo/SOGoParentFolder.m b/SoObjects/SOGo/SOGoParentFolder.m
new file mode 100644 (file)
index 0000000..6bdf0f7
--- /dev/null
@@ -0,0 +1,285 @@
+/* SOGoParentFolder.m - this file is part of SOGo
+ *
+ * Copyright (C) 2006, 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSString.h>
+#import <Foundation/NSUserDefaults.h>
+
+#import <NGObjWeb/NSException+HTTP.h>
+#import <NGObjWeb/WOContext+SoObjects.h>
+#import <GDLContentStore/GCSChannelManager.h>
+#import <GDLContentStore/GCSFolderManager.h>
+#import <GDLContentStore/NSURL+GCS.h>
+#import <GDLAccess/EOAdaptorChannel.h>
+
+#import "SOGoFolder.h"
+#import "SOGoUser.h"
+
+#import "SOGoParentFolder.h"
+
+@implementation SOGoParentFolder
+
+- (id) init
+{
+  if ((self = [super init]))
+    {
+      subFolders = nil;
+      OCSPath = nil;
+      subFolderClass = Nil;
+    }
+
+  return self;
+}
+
+- (void) dealloc
+{
+  [subFolders release];
+  [OCSPath release];
+  [super dealloc];
+}
+
++ (Class) subFolderClass
+{
+  [self subclassResponsibility: _cmd];
+
+  return Nil;
+}
+
++ (NSString *) gcsFolderType
+{
+  [self subclassResponsibility: _cmd];
+
+  return nil;
+}
+
+- (void) setBaseOCSPath: (NSString *) newOCSPath
+{
+  ASSIGN (OCSPath, newOCSPath);
+}
+
+- (NSString *) defaultFolderName
+{
+  return @"Personal";
+}
+
+- (void) _fetchPersonalFolders: (NSString *) sql
+                  withChannel: (EOAdaptorChannel *) fc
+{
+  NSArray *attrs;
+  NSDictionary *row;
+  SOGoFolder *folder;
+  BOOL hasPersonal;
+  NSString *key, *path, *personalName;
+
+  if (!subFolderClass)
+    subFolderClass = [[self class] subFolderClass];
+
+  hasPersonal = NO;
+  [fc evaluateExpressionX: sql];
+  attrs = [fc describeResults: NO];
+  row = [fc fetchAttributes: attrs withZone: NULL];
+  while (row)
+    {
+      folder
+       = [subFolderClass objectWithName: [row objectForKey: @"c_path4"]
+                         inContainer: self];
+      key = [row objectForKey: @"c_path4"];
+      hasPersonal = (hasPersonal || [key isEqualToString: @"personal"]);
+      [folder setOCSPath: [NSString stringWithFormat: @"%@/%@",
+                                   OCSPath, key]];
+      [subFolders setObject: folder forKey: key];
+      row = [fc fetchAttributes: attrs withZone: NULL];
+    }
+
+  if (!hasPersonal)
+    {
+      folder = [subFolderClass objectWithName: @"personal" inContainer: self];
+      personalName = [self labelForKey: [self defaultFolderName]];
+      [folder setDisplayName: personalName];
+      path = [NSString stringWithFormat: @"/Users/%@/%@/personal",
+                      [self ownerInContext: context],
+                      nameInContainer];
+      [folder setOCSPath: path];
+      [subFolders setObject: folder forKey: @"personal"];
+    }
+}
+
+- (void) appendPersonalSources
+{
+  GCSChannelManager *cm;
+  EOAdaptorChannel *fc;
+  NSURL *folderLocation;
+  NSString *sql, *gcsFolderType;
+
+  cm = [GCSChannelManager defaultChannelManager];
+  folderLocation
+    = [[GCSFolderManager defaultFolderManager] folderInfoLocation];
+  fc = [cm acquireOpenChannelForURL: folderLocation];
+  if (fc)
+    {
+      gcsFolderType = [[self class] gcsFolderType];
+      
+      sql
+       = [NSString stringWithFormat: (@"SELECT c_path4 FROM %@"
+                                      @" WHERE c_path2 = '%@'"
+                                      @" AND c_folder_type = '%@'"),
+                   [folderLocation gcsTableName],
+                   [self ownerInContext: context],
+                   gcsFolderType];
+      [self _fetchPersonalFolders: sql withChannel: fc];
+      [cm releaseChannel: fc];
+//       sql = [sql stringByAppendingFormat:@" WHERE %@ = '%@'", 
+//                  uidColumnName, [self uid]];
+    }
+}
+
+- (void) appendSystemSources
+{
+}
+
+- (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];
+
+  *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, newFolderID]];
+      if ([newFolder create])
+       {
+         error = nil;
+         *newNameInContainer = newFolderID;
+       }
+      else
+        error = [NSException exceptionWithHTTPStatus: 400
+                            reason: @"The new folder could not be created"];
+    }
+
+  return error;
+}
+
+- (void) initSubFolders
+{
+  NSString *login;
+
+  if (!subFolders)
+    {
+      subFolders = [NSMutableDictionary new];
+      [self appendPersonalSources];
+      [self appendSystemSources];
+      login = [[context activeUser] login];
+      if ([login isEqualToString: owner])
+       [self appendSubscribedSources];
+    }
+}
+
+- (id) lookupName: (NSString *) name
+        inContext: (WOContext *) lookupContext
+          acquire: (BOOL) acquire
+{
+  id obj;
+
+  /* first check attributes directly bound to the application */
+  obj = [super lookupName: name inContext: lookupContext acquire: NO];
+  if (!obj)
+    {
+      if (!subFolders)
+        [self initSubFolders];
+
+      obj = [subFolders objectForKey: name];
+    }
+
+  return obj;
+}
+
+- (NSArray *) toManyRelationshipKeys
+{
+  if (!subFolders)
+    [self initSubFolders];
+
+  return [subFolders allKeys];
+}
+
+- (NSArray *) subFolders
+{
+  if (!subFolders)
+    [self initSubFolders];
+
+  return [subFolders allValues];
+}
+
+/* acls */
+- (NSArray *) aclsForUser: (NSString *) uid
+{
+  return nil;
+}
+
+- (BOOL) davIsCollection
+{
+  return YES;
+}
+
+- (NSString *) davContentType
+{
+  return @"httpd/unix-directory";
+}
+
+@end
index 10194d27a151d8b7b53d6899808edead87fb0866..84297af23ed8f717571871c7e77f176c4c4e992a 100644 (file)
@@ -26,7 +26,7 @@
 #import <NGObjWeb/SoClassSecurityInfo.h>
 #import <NGExtensions/NSObject+Logs.h>
 
-#import <Appointments/SOGoAppointmentFolder.h>
+#import <Appointments/SOGoAppointmentFolders.h>
 #import <Appointments/SOGoFreeBusyObject.h>
 #import <Contacts/SOGoContactFolders.h>
 #import <Mailer/SOGoMailAccounts.h>
 
 - (NSString *) ocsPrivateCalendarPath
 {
-  return [[self ocsUserPath] stringByAppendingString:@"/Calendar/personal"];
+  return [[self ocsUserPath] stringByAppendingString:@"/Calendar"];
 }
 
 - (NSString *) ocsPrivateContactsPath
 //           : [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
   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];
index 9af10007e41ae91a16bad5a4f7c4fb9e2fde59cd..9f20244c806197fb201768380c329e3edc2aa29b 100644 (file)
@@ -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!";
index 18a984d3b65630cb4e63d91dadb3c292299c3f1c..e77de6d771dcbf6599ae4b299ab5ed286e397776 100644 (file)
@@ -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.";
index ebdc4fea97e6d55147db098dcbaf32eac4c54b79..80a1a5943a9e40729a78ce99442e1600eef9a4a3 100644 (file)
@@ -17,6 +17,7 @@ CommonUI_OBJC_FILES +=                \
        UIxAclEditor.m  \
        UIxObjectActions.m      \
        UIxFolderActions.m      \
+       UIxParentFolderActions.m \
        UIxElemBuilder.m        \
        UIxTabView.m            \
        UIxTabItem.m            \
index d1b779ba6730ffdcbe6232f9366d86dfa9b978f1..9a0bb6969c2fd664b7a1fa7bbb98e779d787cfae 100644 (file)
@@ -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!";
index b74a20a30286bba30e6101f85340f0ebbcc1fd9d..8794bbeabfe8cb5983b3f10b7c77b056b1461fc0 100644 (file)
@@ -41,7 +41,7 @@
   NSString *owner;
   NSString *login;
   NSString *baseFolder;
-  NSMutableString *subscriptionPointer;
+  NSString *subscriptionPointer;
   NSMutableDictionary *moduleSettings;
   BOOL isMailInvitation;
 }
index ca14b9d5e55972e2b3b3e1c48159a393e9754005..e152ed3357d6d13a09d7d0f8d2a7813e7a079280 100644 (file)
 #import <NGObjWeb/SoSecurityManager.h>
 
 #import <SoObjects/SOGo/LDAPUserManager.h>
+#import <SoObjects/SOGo/NSArray+Utilities.h>
 #import <SoObjects/SOGo/SOGoUser.h>
-#import <SoObjects/SOGo/SOGoObject.h>
+#import <SoObjects/SOGo/SOGoFolder.h>
 #import <SoObjects/SOGo/SOGoPermissions.h>
 
+#import "WODirectAction+SOGo.h"
+
 #import "UIxFolderActions.h"
 
 @implementation UIxFolderActions
     }
   [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;
 
-  response = [context response];
   if ([owner isEqualToString: login])
     {
-      [response setStatus: 403];
+      response = [self responseWithStatus: 403];
       [response appendContentString:
                 @"You cannot (un)subscribe to a folder that you own!"];
     }
     {
       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];
 
          mailInvitationURL
            = [[clientObject soURLToBaseContainerForCurrentUser]
                absoluteString];
-         [response setStatus: 302];
+         response = [self responseWithStatus: 302];
          [response setHeader: mailInvitationURL
                    forKey: @"location"];
        }
       else
-       [response setStatus: 204];
+       response = [self responseWith204];
     }
 
   return response;
 
 - (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
 {
-  WOResponse *response;
-
-  response = [context response];
-  [response setStatus: 204];
-
-  return response;
+  return [self responseWith204];
 }
 
 - (WOResponse *) _realFolderActivation: (BOOL) makeActive
 {
-  WOResponse *response;
-  NSMutableDictionary *folderSubscription, *folderDict;
-  NSNumber *active;
-  
-  response = [context response];
+  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];
-  [response setStatus: 204];
 
-  return response;
+  return [self responseWith204];
 }
 
 - (WOResponse *) activateFolderAction
   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
index 92ec032068d4b3db72555e9b2ea4eca22f4a6cfa..067046b69e5e1951314a72316d98e42a760a6ffb 100644 (file)
@@ -30,6 +30,8 @@
 
 @interface WODirectAction (SOGoExtension)
 
+- (WOResponse *) responseWithStatus: (unsigned int) status;
+- (WOResponse *) responseWith204;
 - (WOResponse *) redirectToLocation: (NSString *) newLocation;
 
 @end
index ddeea2aaa5434ff286edea348c5024e9b0bf5a2a..13c8b86d86cb9c34213ec86b325d5076d7ef20d0 100644 (file)
 
 @implementation WODirectAction (SOGoExtension)
 
-- (WOResponse *) redirectToLocation: (NSString *) newLocation
+- (WOResponse *) responseWithStatus: (unsigned int) status
 {
   WOResponse *response;
 
   response = [context response];
-  [response setStatus: 302 /* moved */];
+  [response setStatus: status];
+
+  return response;
+}
+
+- (WOResponse *) responseWith204
+{
+  return [self responseWithStatus: 204];
+}
+
+- (WOResponse *) redirectToLocation: (NSString *) newLocation
+{
+  WOResponse *response;
+
+  response = [self responseWithStatus: 302];
   [response setHeader: newLocation forKey: @"location"];
 
   return response;
 }
 
+
 @end
index 9e014e8e978d001f575bf745a7369f97ca882866..704880c100703579b3fc339abec1d5750aa7f19e 100644 (file)
-{ /* -*-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";
+       };
+      };
+    };
+    SOGoParentFolder = {
+      methods = {
+       createFolder = {
+         protectedBy = "View";
+         actionClass = "UIxParentFolderActions";
+         actionName = "createFolder";
+       };
       };
-      SOGoFolder = {
-        methods = {
-           subscribe = {
-              protectedBy = "<public>";
-              actionClass = "UIxFolderActions";
-              actionName = "subscribe";
-           };
-           unsubscribe = {
-              protectedBy = "<public>";
-              actionClass = "UIxFolderActions";
-              actionName = "unsubscribe";
-           };
-           canAccessContent = {
-              protectedBy = "<public>";
-              actionClass = "UIxFolderActions";
-              actionName = "canAccessContent";
-           };
-            activateFolder = {
-              protectedBy = "<public>";
-              actionClass = "UIxFolderActions";
-              actionName = "activateFolder";
-            };
-            deactivateFolder = {
-              protectedBy = "<public>";
-              actionClass = "UIxFolderActions";
-              actionName  = "deactivateFolder";
-            };
-        };
+    };
+    SOGoFolder = {
+      methods = {
+       subscribe = {
+         protectedBy = "<public>";
+         actionClass = "UIxFolderActions";
+         actionName = "subscribe";
+       };
+       unsubscribe = {
+         protectedBy = "<public>";
+         actionClass = "UIxFolderActions";
+         actionName = "unsubscribe";
+       };
+       canAccessContent = {
+         protectedBy = "<public>";
+         actionClass = "UIxFolderActions";
+         actionName = "canAccessContent";
+       };
+       activateFolder = {
+         protectedBy = "<public>";
+         actionClass = "UIxFolderActions";
+         actionName = "activateFolder";
+       };
+       deactivateFolder = {
+         protectedBy = "<public>";
+         actionClass = "UIxFolderActions";
+         actionName  = "deactivateFolder";
+       };
+       deleteFolder = {
+         protectedBy = "SaveAcls"; /* a hack to force "owner" */
+         actionClass = "UIxFolderActions";
+         actionName  = "deleteFolder";
+       };
+       renameFolder = {
+         protectedBy = "SaveAcls";
+         actionClass = "UIxFolderActions";
+         actionName  = "renameFolder";
+       };
       };
-   };
+    };
+  };
 }
index 9f8a0ac234cf85ae8980d40dc679dfd4aac2c9bb..f7d9db02879caa2bac5e53a24dc9d1fa37006ff3 100644 (file)
@@ -97,6 +97,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!"
index f0ee23b30ce7460ebe977c71aa6701a0f7535cec..7574940685ca881290a2c47fe328b6d34747b0e4 100644 (file)
 "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!"
index 9f4500abbbe332e1146fd81a27553de9275feac8..19bd4387e7a89bed5c6723fd28ef845159ed133d 100644 (file)
 "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!"
index 2bcf4b68c5f42941c84964e4ab6eb86c645fda00..6c6971f5ed53a4310476664f010884e878f2339d 100644 (file)
@@ -4,26 +4,31 @@
      jsLink="js_card";
      label="New Card";
      image="new-card.png";
-     onclick = "newContact(this); return false;"; },
+     onclick = "newContact(this); return false;";
+     tooltip = "Create a new address book card"; },
    { link = "new_list";
      enabled = "NO";
      label="New List";
-     image="new-list.png"; }
+     image="new-list.png";
+     tooltip = "Create a new list"; }
   ),
   (
    { link = "edit";
      label = "Modify";
      onclick = "return onToolbarEditSelectedContacts(this);";
-     image = "properties.png"; },
+     image = "properties.png";
+     tooltip = "Edit the selected card"; },
    { link = "write";
      label="Write";
      onclick = "return onToolbarWriteToSelectedContacts(this);";
-     image="write.png"; }
+     image="write.png";
+     tooltip = "Send a mail message"; }
   ),
   (
    { link = "delete";
      label="Delete";
      onclick = "return uixDeleteSelectedContacts(this);";
-     image="delete.png"; }
+     image="delete.png";
+     tooltip = "Delete selected card or address book"; }
   )
 )
index 578c65ce39feffabc2249ee2624bb648d080bc30..6f395411204dca8ff4115475fdc02863bd45104c 100644 (file)
   id <SOGoContactFolder> 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;
 
index 1509f4a524f3c4cf39e2b25ee11edd900b7edcb6..fa43d81097a2976d302d30970ab4c367248d85de 100644 (file)
@@ -52,9 +52,7 @@
   WORequest *request;
 
   folders = [self clientObject];
-  action = [NSString stringWithFormat: @"../%@/%@",
-                     [folders defaultSourceName],
-                     actionName];
+  action = [NSString stringWithFormat: @"../personal/%@", actionName];
 
   request = [[self context] request];
 
   return [self _selectActionForApplication: @"new"];
 }
 
-- (id <WOActionResults>) newAbAction
-{
-  id <WOActionResults> response;
-  NSString *name;
-
-  name = [self queryParameterForKey: @"name"];
-  if ([name length] > 0)
-    response = [[self clientObject] newFolderWithName: name];
-  else
-    response = [NSException exceptionWithHTTPStatus: 400
-                            reason: @"The name is missing"];
-  
-  return response;
-}
-
 - (id) selectForMailerAction
 {
   return [self _selectActionForApplication: @"mailer-contacts"];
     {
       uid = [currentContact objectForKey: @"c_uid"];
       if (uid && ![results objectForKey: uid])
-       [results setObject: currentContact
-                forKey: uid];
+       [results setObject: currentContact forKey: uid];
       currentContact = [folderResults nextObject];
     }
 }
   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 contactFolders] 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];
   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;
   return result;
 }
 
-- (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];
-
-  securityManager = [SoSecurityManager sharedSecurityManager];
-
-  return (([securityManager validatePermission: SoPerm_AccessContentsInformation
-                            onObject: contactFolder
-                            inContext: context] == nil)
-          ? contactFolder : nil);
-}
+// - (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 = [SOGoUserFolder lookupName: @"Contacts"
+//                                inContext: context
+//                                acquire: NO];
+//   contactFolder = [contactFolders lookupName: @"personal"
+//                               inContext: context
+//                               acquire: NO];
+
+//   securityManager = [SoSecurityManager sharedSecurityManager];
+
+//   return (([securityManager validatePermission: SoPerm_AccessContentsInformation
+//                             onObject: contactFolder
+//                             inContext: context] == nil)
+//           ? contactFolder : nil);
+// }
 
 @end
index 45dadfa6204ca7e634ba450be8bc813b901565bc..e6b2e2ad183fc242417e4fcea2de6ab3f309068d 100644 (file)
   return selectorComponentClass;
 }
 
-- (id <WOActionResults>) deleteAction
-{
-  id <WOActionResults> result;
-  NSException <WOActionResults> *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";
index 5be9899fd0a95f28754d50f61264dbb6b4f9c184..eb0d72d87c9aabe6fbc71ee23c3d43f3570782a8 100644 (file)
 
 @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 */
index 2bf4f0c0999ee4fa909a49846e0fe3874344f245..f472b156056c068673fa2433903154285fe3378c 100644 (file)
@@ -29,6 +29,7 @@
 
 #import <SoObjects/SOGo/SOGoUser.h>
 #import <SoObjects/Contacts/SOGoContactFolder.h>
+#import <SoObjects/Contacts/SOGoContactFolders.h>
 
 #import "UIxContactsListViewContainer.h"
 
 {
   if ((self = [super init]))
     {
-      foldersPrefix = nil;
       selectorComponentClass = nil;
-      additionalFolders = nil;
     }
 
   return self;
 }
 
-- (void) dealloc
-{
-  [additionalFolders release];
-  [super dealloc];
-}
-
 - (void) setSelectorComponentClass: (NSString *) aComponentClass
 {
   selectorComponentClass = aComponentClass;
   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;
 
   folderContainer = [[self clientObject] container];
 
-  return [folderContainer contactFolders];
+  return [folderContainer subFolders];
 }
 
 - (NSString *) currentContactFolderId
 {
-  return [NSString stringWithFormat: @"%@/%@",
-                   [self foldersPrefix],
+  return [NSString stringWithFormat: @"/%@",
                    [currentFolder nameInContainer]];
 }
 
   return [currentFolder displayName];
 }
 
-- (NSArray *) additionalFolders
-{
-  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
+- (NSString *) currentContactFolderOwner
 {
-  return [[additionalFolders objectForKey: currentAdditionalFolder]
-          objectForKey: @"displayName"];
+  return [currentFolder ownerInContext: context];
 }
 
 - (BOOL) hasContactSelectionButtons
index cff8e800d881a19ef3c988549015a51b87a02e2f..3602be7ae0561a544a7a4797da30870c3a315a86 100644 (file)
               pageName    = "UIxContactFoldersView";
               actionName  = "new";
            };
-           newAb = {
-              protectedBy = "View";
-              pageName    = "UIxContactFoldersView";
-              actionName  = "newAb";
-           };
            mailer-contacts = {
               protectedBy = "View";
               pageName    = "UIxContactFoldersView";
               pageName    = "UIxContactsListView";
               actionName  = "mailerContacts";
            };
-          delete = {
-              protectedBy = "SaveAcls"; /* a hack to force "owner" */
-              pageName    = "UIxContactsListView";
-              actionName  = "delete";
-          };
           userRights = {
              protectedBy = "ReadAcls";
              pageName    = "UIxContactsUserRightsEditor";
index 01d4440b43dedcbfb228b07c6b2ce2bbc34a1c6a..9e18ecd3037b8ced6aaee34c91bdf269fbc9d086 100644 (file)
@@ -23,7 +23,7 @@
 
 "Home"                 = "Home"; 
 "Calendar"             = "Calendar";
-"Addressbook"          = "Addressbook";
+"Addressbook"          = "Address Book";
 "Mail"                 = "Mail";
 "Right Administration" = "Right Administration";
 
@@ -71,7 +71,7 @@
 "cc"            = "Cc";
 "bcc"           = "Bcc";
 
-"Addressbook"   = "Addressbook";
+"Addressbook"   = "Address Book";
 "Anais"         = "Anais";
 
 "Edit Draft..." = "Edit Draft...";
index 6355b0a0ef1fedbc669fa6cf47016a08eb051b35..c78528b91c18668f8d8feea629fd9fa7e6ce6082 100644 (file)
@@ -5,23 +5,27 @@
       onclick  = "return clickedEditorSend(this);";
       image = "tb-compose-send-flat-24x24.png";
       cssClass = "tbicon_send";
-      label = "Send"; },
+      label = "Send";
+      tooltip = "Send this message now"; },
     { link  = "#";
       onclick  = "return onContactAdd(null);";
       image = "tb-compose-contacts-flat-24x24.png";
       cssClass = "tbicon_addressbook";
-      label = "Contacts"; },
+      label = "Contacts";
+      tooltip = "Select a recipient from an Address Book"; },
     { link  = "#";
       isSafe = NO;
       onclick  = "return clickedEditorAttach(this)";
       image = "tb-compose-attach-flat-24x24.png";
       cssClass = "tbicon_attach";
-      label = "Attach"; },
+      label = "Attach";
+      tooltip = "Include an attachment"; },
     { link  = "#";
       isSafe = NO;
       onclick  = "return clickedEditorSave(this);";
       image = "tb-mail-file-flat-24x24.png";
       cssClass = "tbicon_save";
-      label = "Save"; },
+      label = "Save";
+      tooltip = "Save this message"; },
   )
 )
index ac4ad5a824b720fbd9744354227cef491bd44b4d..a2ba2dbef9c6e83971cae7191b7e9d5125cca51e 100644 (file)
@@ -6,16 +6,21 @@
       cssClass = "tbicon_getmail";
       label = "Get Mail";
       onclick = "return refreshMailbox(this);";
-    },
+      tooltip = "Get new messages"; },
     { link     = "#";
       isSafe   = NO;
       image = "tb-mail-write-flat-24x24.png";
       onclick  = "return onComposeMessage();";
-      cssClass = "tbicon_compose"; label = "Write"; },
-    { link  = "#"; target = "addressbook";
+      cssClass = "tbicon_compose";
+      label = "Write";
+      tooltip = "Create a new message"; },
+    { link  = "#";
+      target = "addressbook";
       onclick  = "openAddressbook(this);return false;";
       image = "tb-mail-addressbook-flat-24x24.png";
-      cssClass = "tbicon_addressbook"; label = "Addressbook"; },
+      cssClass = "tbicon_addressbook";
+      label = "Addressbook";
+      tooltip = "Go to address book"; },
   ),
 
   ( // second group
       onclick = "return openMessageWindowsForSelection('reply');";
       isSafe   = NO;
       image = "tb-mail-reply-flat-24x24.png";
-      cssClass = "tbicon_reply";    label = "Reply";     },
-
+      cssClass = "tbicon_reply";
+      label = "Reply";
+      tooltip = "Reply to the message"; },
     { link = "replyall";
       onclick = "return openMessageWindowsForSelection('replyall');";
       isSafe   = NO;
       image = "tb-mail-replyall-flat-24x24.png";
-      cssClass = "tbicon_replyall"; label = "Reply All"; },
-
+      cssClass = "tbicon_replyall";
+      label = "Reply All";
+      tooltip = "Reply to sender and all recipients"; },
     { link = "forward";
       onclick = "return openMessageWindowsForSelection('forward');";
       isSafe   = NO;
       image = "tb-mail-forward-flat-24x24.png";
-      cssClass = "tbicon_forward";  label = "Forward";   },
+      cssClass = "tbicon_forward";
+      label = "Forward";
+      tooltip = "Forward selected message"; },
   ),
 
   ( // third group
       onclick = "onMenuDeleteMessage(event);";
 //       enabled  = showMarkDeletedButton;
       image = "tb-mail-delete-flat-24x24.png";
-      cssClass = "tbicon_delete"; label = "Delete"; },
+      cssClass = "tbicon_delete";
+      label = "Delete";
+      tooltip = "Delete selected message or folder"; },
     { link = "#";
       isSafe = NO;
       image = "tb-mail-junk-flat-24x24.png";
-      cssClass = "tbicon_junk";   label = "Junk";
-    },
+      cssClass = "tbicon_junk";
+      label = "Junk";
+      tooltip = "Mark the selected messages as junk"; },
   ),
   (
    { link = "#";
      onclick = "return onPrintCurrentMessage(event);";
      cssClass = "tbicon_print";
      image = "tb-mail-print-flat-24x24.png";
-     label = "Print"; },
+     label = "Print"; 
+     tooltip = "Print this message"; },
    { link = "#";
      image = "tb-mail-stop-flat-24x24.png";
      cssClass = "tbicon_stop";
-     label = "Stop";  },
+     label = "Stop";
+     tooltip = "Stop the current transfer"; },
   ),
 )
index 004c3a634b13b2ce93f6fe9bca6b7dee4816eef2..7f3a66cb3f19639d5d46541c6afeed8811c4b11e 100644 (file)
   rawFolders = [co allFolderPaths];
   folders = [self _jsonFolders: [rawFolders objectEnumerator]];
 
-  response = [context response];
-  [response setStatus: 200];
+  response = [self responseWithStatus: 200];
   [response setHeader: @"text/plain; charset=utf-8"
            forKey: @"content-type"];
   [response appendContentString: [folders jsonRepresentation]];
index 42b5fa0e3614ae93e5b5bf3af7fb85b65e876841..3ebf019c01e9406325c92169cf278a50330b8f52 100644 (file)
 
   response = [[self clientObject] trashInContext: context];
   if (!response)
-    {
-      response = [context response];
-      [response setStatus: 204];
-    }
+    response = [self responseWith204];
 
   return response;
 }
       response = [[self clientObject] moveToFolderNamed: destinationFolder
                                      inContext: context];
       if (!response)
-        {
-         response = [context response];
-         [response setStatus: 204];
-        }
+       response = [self responseWith204];
     }
   else
     response = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
   return response;
 }
 
+/* active message */
+
+- (id) markMessageUnreadAction 
+{
+  id response;
+
+  response = [[self clientObject] removeFlags: @"seen"];
+  if (!response)
+    response = [self responseWith204];
+
+  return response;
+}
+
+- (id) markMessageReadAction 
+{
+  id response;
+
+  response = [[self clientObject] addFlags: @"seen"];
+  if (!response)
+    response = [self responseWith204];
+
+  return response;
+}
+
 /* SOGoDraftObject */
 - (WOResponse *) editAction
 {
   if (error)
     response = error;
   else
-    {
-      response = [context response];
-      [response setStatus: 204];
-    }
+    response = [self responseWith204];
 
   return response;
 }
   WOResponse *response;
   NSString *filename;
 
-  response = [context response];
-
   filename = [[context request] formValueForKey: @"filename"];
   if ([filename length] > 0)
     {
+      response = [self responseWith204];
       [[self clientObject] deleteAttachmentWithName: filename];
-      [response setStatus: 204];
     }
   else
     {
-      [response setStatus: 500];
+      response = [self responseWithStatus: 500];
       [response appendContentString: @"How did you end up here?"];
     }
 
index 726b7970989472ed5898a46cd53b6ce42404f16f..d09189a6a3ad9983832c1baa217042bb47725e99 100644 (file)
@@ -404,10 +404,7 @@ static NSArray *infoKeys = nil;
     {
       result = [[self clientObject] save];
       if (!result)
-       {
-         result = [context response];
-         [result setStatus: 204];
-       }
+       result = [self responseWith204];
     }
   else
     result = [self failedToSaveFormResponse];
index b4a184b847e69520e7a0af7c98fde8b73c5a286d..84d7fa95e5981399d532b5cb34e3b85bd63c5710 100644 (file)
@@ -36,6 +36,8 @@
 #import <SoObjects/Mailer/SOGoMailAccount.h>
 #import <SoObjects/SOGo/NSObject+Utilities.h>
 
+#import <UI/Common/WODirectAction+SOGo.h>
+
 #import "UIxMailFolderActions.h"
 
 @implementation UIxMailFolderActions
@@ -49,7 +51,6 @@
   NSString *folderName;
 
   co = [self clientObject];
-  response = [context response];
 
   folderName = [[context request] formValueForKey: @"name"];
   if ([folderName length] > 0)
       error = [connection createMailbox: folderName atURL: [co imap4URL]];
       if (error)
        {
-         [response setStatus: 500];
+         response = [self responseWithStatus: 500];
          [response appendContentString: @"Unable to create folder."];
        }
       else
-       [response setStatus: 204];
+       response = [self responseWith204];
     }
   else
     {
-      [response setStatus: 500];
+      response = [self responseWithStatus: 500];
       [response appendContentString: @"Missing 'name' parameter."];
     }
 
   NSURL *srcURL, *destURL;
 
   co = [self clientObject];
-  response = [context response];
 
   folderName = [[context request] formValueForKey: @"name"];
   if ([folderName length] > 0)
                          toURL: destURL];
       if (error)
        {
-         [response setStatus: 500];
+         response = [self responseWithStatus: 500];
          [response appendContentString: @"Unable to rename folder."];
        }
       else
-       [response setStatus: 204];
+       response = [self responseWith204];
     }
   else
     {
-      [response setStatus: 500];
+      response = [self responseWithStatus: 500];
       [response appendContentString: @"Missing 'name' parameter."];
     }
 
   NSURL *srcURL, *destURL;
 
   co = [self clientObject];
-  response = [context response];
   connection = [co imap4Connection];
   srcURL = [co imap4URL];
   destURL = [self _trashedURLOfFolder: srcURL
                      toURL: destURL];
   if (error)
     {
-      [response setStatus: 500];
+      response = [self responseWithStatus: 500];
       [response appendContentString: @"Unable to move folder."];
     }
   else
-    [response setStatus: 204];
+    response = [self responseWith204];
 
   return response;
 }
   WOResponse *response;
 
   co = [self clientObject];
-  response = [context response];
 
   error = [co expunge];
   if (error)
     {
-      [response setStatus: 500];
+      response = [self responseWithStatus: 500];
       [response appendContentString: @"Unable to expunge folder."];
     }
   else
     {
       [co flushMailCaches];
-      [response setStatus: 204];
+      response = [self responseWith204];
     }
 
   return response;
   NSURL *currentURL;
 
   co = [self clientObject];
-  response = [context response];
 
   error = [co addFlagsToAllMessages: @"deleted"];
   if (!error)
     }
   if (error)
     {
-      [response setStatus: 500];
+      response = [self responseWithStatus: 500];
       [response appendContentString: @"Unable to empty the trash folder."];
     }
   else
-    [response setStatus: 204];
+    response = [self responseWith204];
 
   return response;
 }
   WOResponse *response;
   SOGoMailFolder *clientObject;
 
-  response = [context response];
   mailInvitationParam
     = [[context request] formValueForKey: @"mail-invitation"];
   if ([mailInvitationParam boolValue])
     }
   else
     {
-      [response setStatus: 500];
+      response = [self responseWithStatus: 500];
       [response appendContentString: @"How did you end up here?"];
     }
 
   NGImap4Client *client;
   NSString *responseString;
 
-  response = [context response];
-  [response setStatus: 200];
+  response = [self responseWithStatus: 200];
   [response setHeader: @"text/plain; charset=UTF-8"
            forKey: @"content-type"];
 
index 46f7505e766c439df86f97e7b2a6cd652826c718..ac5b718440e50cea781f3efbe8255b546d1c488b 100644 (file)
@@ -221,10 +221,24 @@ static int attachmentFlagSize = 8096;
 
 - (NSArray *) sortedUIDs 
 {
+  EOQualifier *fetchQualifier, *notDeleted;
   if (!sortedUIDs)
     {
+      notDeleted = [EOQualifier qualifierWithQualifierFormat:
+                                 @"(not (flags = %@))",
+                               @"deleted"];
+      if (qualifier)
+       {
+         fetchQualifier = [[EOAndQualifier alloc] initWithQualifiers:
+                                                    notDeleted, qualifier,
+                                                  nil];
+         [fetchQualifier autorelease];
+       }
+      else
+       fetchQualifier = notDeleted;
+
       sortedUIDs
-        = [[self clientObject] fetchUIDsMatchingQualifier: qualifier
+        = [[self clientObject] fetchUIDsMatchingQualifier: fetchQualifier
                               sortOrdering: [self imap4SortOrdering]];
       [sortedUIDs retain];
     }
@@ -407,36 +421,8 @@ static int attachmentFlagSize = 8096;
   return [self redirectToLocation:url];
 }
 
-/* active message */
-
-- (SOGoMailObject *) lookupActiveMessage 
-{
-  NSString *uid;
-  
-  if ((uid = [[context request] formValueForKey: @"uid"]) == nil)
-    return nil;
-
-  return [[self clientObject] lookupName: uid
-                             inContext: context
-                             acquire: NO];
-}
-
 /* actions */
 
-- (BOOL) isJavaScriptRequest 
-{
-  return [[[context request] formValueForKey:@"jsonly"] boolValue];
-}
-
-- (id) javaScriptOK 
-{
-  WOResponse *r;
-
-  r = [context response];
-  [r setStatus:200 /* OK */];
-  return r;
-}
-
 - (int) firstMessageOfPageFor: (int) messageNbr
 {
   NSArray *messageNbrs;
@@ -462,14 +448,13 @@ static int attachmentFlagSize = 8096;
 
   if ([criteria isEqualToString: @"subject"])
     qualifier = [EOQualifier qualifierWithQualifierFormat:
-                              @"(subject doesContain: %@)",
-                            value];
+                              @"(subject doesContain: %@)", value];
   else if ([criteria isEqualToString: @"sender"])
     qualifier = [EOQualifier qualifierWithQualifierFormat:
-                              @"(sender doesContain: %@)", value];
+                              @"(from doesContain: %@)", value];
   else if ([criteria isEqualToString: @"subject_or_sender"])
     qualifier = [EOQualifier qualifierWithQualifierFormat:
-                              @"((sender doesContain: %@)"
+                              @"((subject doesContain: %@)"
                             @" OR (from doesContain: %@))",
                             value, value];
   else if ([criteria isEqualToString: @"to_or_cc"])
@@ -506,43 +491,9 @@ static int attachmentFlagSize = 8096;
     = ((specificMessage)
        ? [self firstMessageOfPageFor: [specificMessage intValue]]
        : [[request formValueForKey:@"idx"] intValue]);
-
   return self;
 }
 
-- (id) viewAction 
-{
-  return [self defaultAction];
-}
-
-- (id) markMessageUnreadAction 
-{
-  NSException *error;
-  
-  if ((error = [[self lookupActiveMessage] removeFlags:@"seen"]) != nil)
-    // TODO: improve error handling
-    return error;
-
-  if ([self isJavaScriptRequest])
-    return [self javaScriptOK];
-  
-  return [self redirectToLocation:@"view"];
-}
-
-- (id) markMessageReadAction 
-{
-  NSException *error;
-  
-  if ((error = [[self lookupActiveMessage] addFlags:@"seen"]) != nil)
-    // TODO: improve error handling
-    return error;
-  
-  if ([self isJavaScriptRequest])
-    return [self javaScriptOK];
-  
-  return [self redirectToLocation:@"view"];
-}
-
 - (id) getMailAction 
 {
   // TODO: we might want to flush the caches?
index c0586221d6356ea5dd788f9d2c32f6d3253cd08a..60976e53d530fdfb79c93eb146ea02bb41960491 100644 (file)
@@ -25,6 +25,8 @@
 #import <Foundation/NSString.h>
 #import <SoObjects/Mailer/SOGoMailObject.h>
 
+#import <UI/Common/WODirectAction+SOGo.h>
+
 #import "UIxMailSourceView.h"
 
 @implementation UIxMailSourceView
@@ -36,8 +38,7 @@
 
   source = [[self clientObject] contentAsString];
 
-  response = [context response];
-  [response setStatus: 200];
+  response = [self responseWithStatus: 200];
   [response setHeader: @"text/plain; charset=utf-8"
            forKey: @"content-type"];
   [response appendContentString: source];
index 107e9c2ad8d3315e4d74a9711065412d78b16d7e..e3da019f6edb000216e79d29301fcabb6411efc7 100644 (file)
@@ -142,37 +142,42 @@ static NSString *mailETag = nil;
 
 - (id) defaultAction
 {
+  WOResponse *response;
+  NSString *s;
+
   /* check etag to see whether we really must rerender */
-  if (mailETag != nil ) {
-    /*
-      Note: There is one thing which *can* change for an existing message,
-            those are the IMAP4 flags (and annotations, which we do not use).
-           Since we don't render the flags, it should be OK, if this changes
-           we must embed the flagging into the etag.
-    */
-    NSString *s;
-    
-    if ((s = [[context request] headerForKey:@"if-none-match"])) {
-      if ([s rangeOfString:mailETag].length > 0) { /* not perfectly correct */
-       /* client already has the proper entity */
-       // [self logWithFormat:@"MATCH: %@ (tag %@)", s, mailETag];
-       
-       if (![[self clientObject] doesMailExist]) {
-         return [NSException exceptionWithHTTPStatus:404 /* Not Found */
-                             reason:@"message got deleted"];
+  if (mailETag)
+    {
+      /*
+       Note: There is one thing which *can* change for an existing message,
+       those are the IMAP4 flags (and annotations, which we do not use).
+       Since we don't render the flags, it should be OK, if this changes
+       we must embed the flagging into the etag.
+      */
+      s = [[context request] headerForKey: @"if-none-match"];
+      if (s)
+       {
+         if ([s rangeOfString:mailETag].length > 0) /* not perfectly correct */
+           { 
+             /* client already has the proper entity */
+             // [self logWithFormat:@"MATCH: %@ (tag %@)", s, mailETag];
+             
+             if (![[self clientObject] doesMailExist]) {
+               return [NSException exceptionWithHTTPStatus:404 /* Not Found */
+                                   reason:@"message got deleted"];
+             }
+
+             response = [context response];
+             [response setStatus: 304 /* Not Modified */];
+
+             return response;
+           }
        }
-       
-       [[context response] setStatus:304 /* Not Modified */];
-       return [context response];
-      }
     }
-  }
   
-  if ([self message] == nil) {
-    // TODO: redirect to proper error
+  if (![self message]) // TODO: redirect to proper error
     return [NSException exceptionWithHTTPStatus:404 /* Not Found */
                        reason:@"did not find specified message!"];
-  }
   
   return self;
 }
index d6806567c2897239644565c56b1495d7f294ab10..73ed08099722bff43437e7af3aad936a5f69981c 100644 (file)
          protectedBy = "View";
          pageName    = "UIxMailListView";
        };
-       markMessageUnread = {
-         protectedBy = "View";
-         pageName    = "UIxMailListView";
-         actionName  = "markMessageUnread";
-       };
-       markMessageRead = {
-         protectedBy = "View";
-         pageName    = "UIxMailListView";
-         actionName  = "markMessageRead";
-       };
        getMail = {
          protectedBy = "View";
          pageName    = "UIxMailListView";
          actionClass = "UIxMailActions";
          actionName  = "forward";
        };
+       markMessageUnread = {
+         protectedBy = "View";
+         actionClass = "UIxMailActions";
+         actionName  = "markMessageUnread";
+       };
+       markMessageRead = {
+         protectedBy = "View";
+         actionClass = "UIxMailActions";
+         actionName  = "markMessageRead";
+       };
       };
     };
 
index c88e9af4f629b3ea597608cb9c2ceec9151a3545..9749d13b5a3e4f9095ce10bc7e5141c2eae09e16 100644 (file)
 
   auth = [[WOApplication application]
           authenticatorInContext: context];
-  response = [context response];
+  response = [self responseWith204];
   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];
   [authCookie setPath: @"/"];
-  [response setStatus: 204];
   [response addCookie: authCookie];
 
   return response;
 }
 
-// - (id <WOActionResults>) defaultAction
-// {
-//   WOResponse *r;
-//   NSString *login, *rhk;
-//   SOGoWebAuthenticator *auth;
-//   SOGoUser *user;
-//   SOGoUserFolder *home;
-//   WOApplication *base;
-
-//   /* 
-//      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];
-
-//   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];
-// }
+- (id <WOActionResults>) defaultAction
+{
+  id <WOActionResults> response;
+  NSString *login, *oldLocation;
+
+  login = [[context activeUser] login];
+  if ([login isEqualToString: @"anonymous"])
+    response = self;
+  else
+    {
+      oldLocation = [[self clientObject] baseURLInContext: context];
+      response
+       = [self redirectToLocation: [NSString stringWithFormat: @"%@/%@",
+                                             oldLocation, login]];
+    }
+
+  return response;
+}
 
 - (BOOL) isPublicInContext: (WOContext *) localContext
 {
index c3ebb09938582cd3a975da27e53ead740bd554aa..11e282820b77922947abc5274e8c39ab2951139c 100644 (file)
        "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";
index 4131db0a575aed26d27c582930548d16f3bc1551..7259f60d21c00e70137e2fb4ff59732eb814b15e 100644 (file)
@@ -125,7 +125,8 @@ static BOOL shouldDisplayPasswordChange = NO;
 
 - (NSArray *) timeZonesList
 {
-  return [NSTimeZone knownTimeZoneNames];
+  return [[NSTimeZone knownTimeZoneNames]
+          sortedArrayUsingSelector: @selector (localizedCaseInsensitiveCompare:)];
 }
 
 - (NSString *) userTimeZone
index 4435c7303686880e30fcb7d1e0b962ab2be74136..f0c0190835762d527a35bdfd8797fb19fab6de0b 100644 (file)
 /* date selection */
 - (NSCalendarDate *) selectedDate;
 
-- (NSString *)dateStringForDate:(NSCalendarDate *)_date;
+- (NSString *) dateStringForDate: (NSCalendarDate *)_date;
 
 - (BOOL) hideFrame;
 
 - (UIxComponent *) jsCloseWithRefreshMethod: (NSString *) methodName;
 
 /* SoUser */
-- (NSString *)shortUserNameForDisplay;
+- (NSString *) shortUserNameForDisplay;
 
 /* labels */
-- (NSString *)labelForKey:(NSString *)_key;
+- (NSString *) labelForKey:(NSString *)_key;
 
-- (NSString *)localizedNameForDayOfWeek:(unsigned)_dayOfWeek;
-- (NSString *)localizedAbbreviatedNameForDayOfWeek:(unsigned)_dayOfWeek;
-- (NSString *)localizedNameForMonthOfYear:(unsigned)_monthOfYear;
-- (NSString *)localizedAbbreviatedNameForMonthOfYear:(unsigned)_monthOfYear;
+- (NSString *) localizedNameForDayOfWeek:(unsigned)_dayOfWeek;
+- (NSString *) localizedAbbreviatedNameForDayOfWeek:(unsigned)_dayOfWeek;
+- (NSString *) localizedNameForMonthOfYear:(unsigned)_monthOfYear;
+- (NSString *) localizedAbbreviatedNameForMonthOfYear:(unsigned)_monthOfYear;
 
 /* HTTP method safety */
-- (BOOL)isInvokedBySafeMethod;
+- (BOOL) isInvokedBySafeMethod;
     
 /* locale */
 - (NSDictionary *)locale;
@@ -95,6 +95,8 @@
 - (WOResourceManager *) pageResourceManager;
 - (NSString *) urlForResourceFilename: (NSString *) filename;
 
+- (WOResponse *) responseWith204;
+
 /* Debugging */
 - (BOOL)isUIxDebugEnabled;
 
index 95e48cf9413660c311d5a6ac6310d2a8d7f3c5a9..643dca5631ed92f0ca6532938f45b397b874198f 100644 (file)
@@ -400,7 +400,7 @@ static BOOL uixDebugEnabled = NO;
   userTimeZone = [[context activeUser] timeZone];
   [_date setTimeZone: userTimeZone];
 
-  return [_date descriptionWithCalendarFormat:@"%Y%m%d"];
+  return [_date descriptionWithCalendarFormat: @"%Y%m%d"];
 }
 
 - (BOOL) hideFrame
@@ -569,6 +569,16 @@ static BOOL uixDebugEnabled = NO;
   return url;
 }
 
+- (WOResponse *) responseWith204
+{
+  WOResponse *response;
+
+  response = [context response];
+  [response setStatus: 204];
+
+  return response;
+}
+
 /* debugging */
 
 - (BOOL)isUIxDebugEnabled {
index f49a55a92ccf014c1f445c51dba17bf6b4e014d7..5e7ee587b641171819ab46016c52241e05fa8f20 100644 (file)
 
 /* 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";
index 9fb1d9fcc67eac62efaf199e8bb369c13f6f7392..1d8f083eb9c28748701d4a93191df5cc4386c478 100644 (file)
 
 /* 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";
index 0089f139a2354c5a6f851fefde8f93f20142bedf..8c65e344b338e6b0389d9383d0e472a7407e37c1 100644 (file)
@@ -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 \
index 9a52d23d9172084e565d35885ca4c48768b4e29a..b05adbc75f714cdedbcbce38f09197a903c10588 100644 (file)
 
 /* 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";
similarity index 60%
rename from UI/Scheduler/Toolbars/SOGoAppointmentFolder.toolbar
rename to UI/Scheduler/Toolbars/SOGoAppointmentFolders.toolbar
index 296c30696a7de886d5d13ea7dce179811740c63f..b50fa350ed07a39ed36c9f93f9faabb63f6887cd 100644 (file)
@@ -3,39 +3,48 @@
       isSafe = NO;
       label = "New Event";
       onclick = "return newEvent(this, 'event');";
-      image = "new-event.png"; },
+      image = "new-event.png";
+      tooltip = "Create a new event"; },
     { link  = "new_task";
       label="New Task";
       image = "new-task.png";
       onclick = "return newEvent(this, 'task');";
-      image = "new-task.png"; },
+      image = "new-task.png";
+      tooltip = "Create a new task"; },
     { link  = "edit";
       label="Edit";
       onclick = "return editEvent(this);";
-      image = "edit.png"; },
+      image = "edit.png";
+      tooltip = "Edit this event or task"; },
     { link  = "delete";
       label="Delete";
       onclick = "return deleteEvent(this);";
-      image = "delete.png"; } ),
+      image = "delete.png"; 
+      tooltip = "Delete this event or task"; } ),
   ( { link  = "today";
       label="Go to Today";
       onclick = "return gotoToday();";
-      image = "goto-today.png" } ),
+      image = "goto-today.png";
+      tooltip = "Go to today"; } ),
   ( { link  = "dayoverview";
       label="Day View";
       onclick = "return onDayOverview();";
-      image = "day-view.png"; },
+      image = "day-view.png";
+      tooltip = "Switch to day view"; },
 /* disabled until we fix the view */
 /*    { link  = "dayoverview";
       label="Multicolumn Day View";
       onclick = "return onMulticolumnDayOverview();";
-      image = "day-view-multicolumn.png"; }, */
+      image = "day-view-multicolumn.png";
+      tooltip = ""; }, */
     { link  = "weekoverview";
       label="Week View";
       onclick = "return onWeekOverview();";
-      image = "week-view.png"; },
+      image = "week-view.png";
+      tooltip = "Switch to week view"; },
     { link  = "monthoverview";
       label="Month View";
       onclick = "return onMonthOverview();";
-      image = "month-view.png"; } )
+      image = "month-view.png";
+      tooltip = "Switch to month view"; } )
 )
index f4f041979508939748be44434a7fd7543f8e2bab..a88b3f3829bfdbba85f901ded60eb519456ad2ec 100644 (file)
 {
   NSString *objectId, *method, *uri;
   id <WOActionResults> 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];
     }
index 03f647f9dac358538a0ac0d17248e10a9bc2613d..b036060f904ffdee469c434c108ad15ced569931 100644 (file)
@@ -25,6 +25,7 @@
 #import <Foundation/NSEnumerator.h>
 #import <Foundation/NSNull.h>
 #import <Foundation/NSString.h>
+#import <Foundation/NSValue.h>
 
 #import <NGObjWeb/WOContext.h>
 #import <NGObjWeb/WOContext+SoObjects.h>
@@ -38,6 +39,9 @@
 #import <SoObjects/SOGo/NSArray+Utilities.h>
 #import <SoObjects/SOGo/NSObject+Utilities.h>
 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
+#import <SoObjects/Appointments/SOGoAppointmentFolders.h>
+
+#import <UI/Common/WODirectAction+SOGo.h>
 
 #import "NSArray+Scheduler.h"
 
   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
 {
   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])
+         owner = [currentFolder ownerInContext: context];
+         currentInfos = [[currentFolder fetchCoreInfosFrom: startDate
+                                        to: endDate
+                                        component: component] objectEnumerator];
+         newInfo = [currentInfos nextObject];
+         while (newInfo)
            {
-             [self _updatePrivacyInComponent: newInfo
-                   fromFolder: currentFolder];
-             [newInfo setObject: owner forKey: @"c_owner"];
-             [infos setObject: [newInfo objectsForKeys: fields
-                                        notFoundMarker: marker]
-                    forKey: uid];
+             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];
            }
-         newInfo = [currentInfos nextObject];
        }
       currentFolder = [folders nextObject];
     }
 {
   WOResponse *response;
 
-  response = [context response];
+  response = [self responseWithStatus: 200];
   [response setHeader: @"text/plain; charset=utf-8"
            forKey: @"content-type"];
-  [response setStatus: 200];
   [response appendContentString: [data jsonRepresentation]];
 
   return response;
   [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
 
   [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
index 80b66f66d643de14a889d0490b4521183bc4fce4..ca3933a0cdd7506fdce69b94a56447908931c47c 100644 (file)
@@ -23,6 +23,7 @@
 #import <Foundation/NSArray.h>
 #import <Foundation/NSCalendarDate.h>
 #import <Foundation/NSString.h>
+#import <Foundation/NSTimeZone.h>
 #import <Foundation/NSUserDefaults.h>
 #import <Foundation/NSValue.h>
 
@@ -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;
index 410ce6dbb12a0a88512af8bcfbd68230f4ca4304..21feda2f89b7fa5affbd7f7361fed5a5f3e08c31 100644 (file)
 #ifndef UIXCALENDARSELECTOR_H
 #define UIXCALENDARSELECTOR_H
 
+#import <UI/SOGoUI/UIxComponent.h>
+
 @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;
-
-- (void) setCurrentCalendarFolder: (NSDictionary *) newCurrentCalendarFolder;
-- (NSDictionary *) currentCalendarFolder;
+- (NSArray *) calendars;
 
-- (NSString *) currentCalendarSpanBG;
-- (NSString *) currentCalendarLogin;
-- (NSString *) currentCalendarStyle;
+- (void) setCurrentCalendar: (NSDictionary *) newCalendar;
+- (NSDictionary *) currentCalendar;
 
 @end
 
index 1beef4b43ed55104a1e563818047c676479cf08f..b411f6b4407ce7764009e53423a5289fa15fb4f7 100644 (file)
 
 #import <Foundation/NSArray.h>
 #import <Foundation/NSDictionary.h>
-#import <Foundation/NSString.h>
-#import <Foundation/NSUserDefaults.h>
+#import <Foundation/NSValue.h>
 
-#import <NGExtensions/NGExtensions.h>
-#import <NGCards/iCalPerson.h>
-
-#import <SOGo/SOGoUser.h>
-#import <SOGoUI/UIxComponent.h>
+#import <SOGo/NSDictionary+Utilities.h>
 #import <Appointments/SOGoAppointmentFolder.h>
+#import <Appointments/SOGoAppointmentFolders.h>
 
 #import "UIxCalendarSelector.h"
 
-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;
+// static inline char
+// darkenedColor (const char value)
+// {
+//   char newValue;
 
-  return 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;
+
+//   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;
-
-  calendarFolders = [[self clientObject] calendarFolders];
-  if (!colors)
+  NSArray *folders;
+  SOGoAppointmentFolder *folder;
+  NSMutableDictionary *calendar;
+  unsigned int count, max;
+  NSString *folderId, *folderName;
+  NSNumber *isActive;
+
+  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 */
index 7937e8fa7aeadaa552d261e2ebf6493dca8562d8..5cfeecaf41613ea0bc0d86348f79b84f9318b3f5 100644 (file)
@@ -42,6 +42,7 @@
 #import <NGExtensions/NSString+misc.h>
 
 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
+#import <SoObjects/Appointments/SOGoAppointmentFolders.h>
 #import <SoObjects/Appointments/SOGoAppointmentObject.h>
 #import <SoObjects/Appointments/SOGoTaskObject.h>
 #import <SoObjects/SOGo/NSString+Utilities.h>
 
 - (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];
        }
index 0258ae2d24ae6adc9256ce416215b66c6589fcd4..bc7e1bed1d3c022257e2ffb6f8c03ec4573dcbb3 100644 (file)
 {
   NSString *objectId, *method, *uri;
   id <WOActionResults> 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];
     }
index 2041d757081c767c0aaaae9abebbc5b627161b68..c5afd5cc6e0c35e31d1b7cfadd02280865b285a2 100644 (file)
   };
 
   categories = {
-     SOGoAppointmentFolder = {
+     SOGoAppointmentFolders = {
        slots = {
           toolbar = {
              protectedBy = "View";
-             value = "SOGoAppointmentFolder.toolbar";
+             value = "SOGoAppointmentFolders.toolbar";
           };
        };
        methods = {
              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"; 
              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";
           };
        };
      };
+
      SOGoCalendarComponent = {
      };
 
              pageName    = "UIxAppointmentEditor"; 
              actionName  = "decline";
           };
+          editAttendees = {
+             protectedBy = "View";
+             pageName    = "UIxAttendeesEditor";
+          };
        };
      };
 
index 8a2d86556577b73f0731ad40b035290e666cb8ba..1733b8969f5d5edca0200e1fca9de1d2c40e46bc 100644 (file)
        <ul id="contactFolders">
          <var:foreach list="contactFolders" item="currentFolder"
            ><li var:id="currentContactFolderId"
-              ><var:string value="currentContactFolderName" /></li
+             var:owner="currentContactFolderOwner"
+             ><var:string value="currentContactFolderName" /></li
              ></var:foreach
-           ><var:foreach list="additionalFolders"
-           item="currentAdditionalFolder"
-           ><li var:id="currentAdditionalFolder" class="denied"
-             ><var:string value="currentAdditionalFolderName" /></li
-             ></var:foreach>
+           >
        </ul>
 
        <var:if condition="hasContactSelectionButtons">
index b6514b3a71a7545103055e29b289f4b63da08b78..c5a3f0de0ac2e767953002bfcb111c938ac64127 100644 (file)
@@ -63,7 +63,6 @@
          var:class="messageSubjectCellStyleClass"
          var:id="msgDivID"
          ><var:string value="message.envelope.subject"
-           formatter="context.mailSubjectFormatter"
            /></td
 
          ><td class="messageAddressColumn"
index 863be2ff4477a748616f0f0214facf1f5d8cc24f..9ab3d56d5da26776d4e12bef7ad0c3ff71527813 100644 (file)
@@ -9,6 +9,9 @@
   className="UIxPageFrame"
   title="title"
   >
+  <script type="text/javascript">
+    var UTCOffset = <var:string value="userUTCOffset"/>;
+  </script>
   <div class="preload" style="visibility: hidden;">
     <img rsrc:src="event-gradient.png"/>
   </div>
index bee2ff03ce539f9e6f08ddbdefc492d322ad2484..a54fcfb6bae02b9a81ed12e4272b58a743800ff1 100644 (file)
@@ -7,30 +7,34 @@
   xmlns:rsrc="OGo:url"
   xmlns:label="OGo:label">
   <style type="text/css">
-    <var:foreach list="calendarFolders" item="currentCalendarFolder">
-.ownerIs<var:string value="currentCalendarLogin" />
-{ background-color: <var:string value="currentCalendarSpanBG" /> !important; }
+    <var:foreach list="calendars" item="currentCalendar">
+.calendarFolder<var:string value="currentCalendar.folder" />
+{ background-color: <var:string value="currentCalendar.color" /> !important; }
     </var:foreach>
   </style>
   <div id="calendarSelector">
     <span id="calendarSelectorButtons">
       <a href="#" class="toolbarButton"
        ><span class="toolbarButton"><img rsrc:src="add-calendar.png"
-           label:title="Add..."
+           label:title="New Calendar..."
+           /></span></a>
+      <a href="#" class="toolbarButton"
+       ><span class="toolbarButton"><img rsrc:src="add-user-calendar.png"
+           label:title="Subscribe to a Calendar..."
            /></span></a>
       <a href="#" class="toolbarButton"
        ><span class="toolbarButton"><img rsrc:src="remove-calendar.png"
-           label:title="Remove"
+           label:title="Remove the selected Calendar"
            /></span></a>
     </span>
     <ul id="calendarList" multiselect="yes">
-      <var:foreach list="calendarFolders" item="currentCalendarFolder"
-       ><li class="denied" var:id="currentCalendarFolder.folder">
+      <var:foreach list="calendars" item="currentCalendar"
+       ><li class="denied" var:id="currentCalendar.id">
          <input type="checkbox" class="checkBox"
            const:disabled="disabled"
-           var:checked="currentCalendarFolder.active" />
+           var:checked="currentCalendar.active" />
          <div class="colorBox" var:style="currentCalendarStyle">OO</div>
-         <var:string value="currentCalendarFolder.displayName"
+         <var:string value="currentCalendar.displayName"
            /></li>
       </var:foreach>
     </ul>
index 6bad635bb41786f48337192a23c257c5f6582f64..59244aed272bfc0668bd1e39ee87cbe466a16a20 100644 (file)
@@ -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"
   ><var:string var:value="doctype" const:escapeHTML="NO" />
   <var:if condition="hideFrame" const:negate="YES"
     ><html>
index 5ba95b34bf7cd48775629f59fb0a6464ce0f739e..38ac3b24cfbaf884a359661440593ae5911805e0 100644 (file)
               ><span class="toolbarButton"
                 ><img class="buttonImage"
                   var:src="buttonImage"
-                  var:alt="buttonInfo.image"
+                  var:alt="buttonInfo.tooltip"
                   /><var:if condition="hasMenu"
                   ><img class="buttonMenuArrow"
                     rsrc:src="arrow-dwn-sharp.gif"
-                    var:alt="buttonInfo.image"
+                    var:alt="buttonInfo.tooltip"
                     /></var:if
                   ><br
                   /><span class="buttonLabel"
@@ -37,7 +37,7 @@
             ><span class="disabledToolbarButton"
               ><img class="buttonImage"
                 var:src="buttonImage"
-                var:alt="buttonInfo.image"
+                var:alt="buttonInfo.tooltip"
                 /><br
                 /><span class="buttonLabel"
                 ><var:string
index f38c9d05b689025b29efb6ea81121f54eba57f37..e5b841468be2e61dd942a988fc4f7b35fe6f06a4 100644 (file)
@@ -18,8 +18,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;
@@ -468,28 +468,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;
@@ -523,60 +519,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() {
@@ -648,6 +644,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];
@@ -659,7 +683,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, "-",
index 09814bc5536eff9ac4d27ef2d4340c52a8ee5a72..6b626482bf2300c529ec5efc548179dca6840b80 100644 (file)
@@ -149,7 +149,7 @@ function openMessageWindowsForSelection(action, firstOnly) {
 
 function mailListMarkMessage(event) {
   var http = createHTTPClient();
-  var url = ApplicationBaseURL + currentMailbox + "/" + action + "?uid=" + msguid;
+  var url = ApplicationBaseURL + currentMailbox + "/" + msguid + "/" + action;
 
   if (http) {
     // TODO: add parameter to signal that we are only interested in OK
@@ -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();
@@ -489,7 +489,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;
     }
@@ -1263,7 +1263,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;
@@ -1273,7 +1273,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");
@@ -1283,7 +1283,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";
@@ -1318,7 +1318,7 @@ function folderOperationCallback(http) {
       && http.status == 204)
     initMailboxTree();
   else
-    window.alert(labels["Operation failed"].decodeEntities());
+    window.alert(labels["Operation failed"]);
 }
 
 function folderRefreshCallback(http) {
@@ -1329,7 +1329,7 @@ function folderRefreshCallback(http) {
       refreshCurrentFolder();
   }
   else
-    window.alert(labels["Operation failed"].decodeEntities());
+    window.alert(labels["Operation failed"]);
 }
 
 function getMenus() {
index fa534ef43669de1cba0c4ab17dc9dfdda1569e7f..15d0c1f47b9169600843bcea1620e54e58ad5c3a 100644 (file)
@@ -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; }
index fc864847b3e9d38b656473f6c3834b224b2f7c45..7c992039a38d2b073db895b2e7684f7956e69db6 100644 (file)
@@ -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 onCalendarAdd(event) {
-   openUserFolderSelector(onFolderSubscribeCB, "calendar");
+function onCalendarNew(event) {
+  createFolder(window.prompt(labels["Name of the Calendar"]),
+              appendCalendar);
+  preventDefault(event);
+}
 
-   preventDefault(event);
+function onCalendarAdd(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);
-
-   var li = document.createElement("li");
-   calendarList.appendChild(li);
-
-   var checkBox = document.createElement("input");
-   checkBox.setAttribute("type", "checkbox");
-   li.appendChild(checkBox);
-   
-   li.appendChild(document.createTextNode(" "));
-
-   var colorBox = document.createElement("div");
-   li.appendChild(colorBox);
-   li.appendChild(document.createTextNode(" " + folderName));
-   colorBox.appendChild(document.createTextNode("OO"));
+  if (folder)
+    folder = accessToSubscribedFolder(folder);
+  else
+    folder = "/" + folderName;
 
-   li.setAttribute("id", folder);
-   Event.observe(li, "mousedown",  listRowMouseDownHandler);
-   Event.observe(li, "click",  onRowClick);
-   $(checkBox).addClassName("checkBox");
+//   log ("append: " + folderName + "; folder: " + folder);
 
-   Event.observe(checkBox, "click",  updateCalendarStatus.bindAsEventListener(checkBox));
+  var calendarList = $("calendarList");
+  var lis = calendarList.childNodesWithTag("li");
+  var color = indexColor(lis.length + 100);
+  //log ("color: " + color);
 
-   $(colorBox).addClassName("colorBox");
-   if (color) {
-      $(colorBox).setStyle({ color: color,
-                            backgroundColor: color });
-   }
+  var li = document.createElement("li");
+  calendarList.appendChild(li);
 
-   var contactId = folder.split(":")[0];
-   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; }');
-   }
+  var checkBox = document.createElement("input");
+  checkBox.setAttribute("type", "checkbox");
+  li.appendChild(checkBox);
+   
+  li.appendChild(document.createTextNode(" "));
+
+  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");
+
+  Event.observe(checkBox, "click",
+               updateCalendarStatus.bindAsEventListener(checkBox));
+
+  $(colorBox).addClassName("colorBox");
+  if (color)
+    $(colorBox).setStyle({color: color,
+                         backgroundColor: color});
+
+  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('.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;
index 91c249ab9fa746846832a89ef2420ed1839d71bc..2bef5e1d3b796f25490e1e22b6cc7aa3c3ca37ad 100644 (file)
@@ -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) {
index 91c58ecab281b99540fd399809a84c20c4c342ef..9f89c798f2758a8356226da4fcec68e00606dc95 100644 (file)
@@ -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;
       }
     }
index 82213035d8c32125b6c2247727f4fc55d37551fe..af61b807ea25ae044c421c87332a825b695ea61b 100644 (file)
@@ -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");
index 32670f0bf666ef52e1112d882f1af88e4fe6afbb..b17720dad2d6011977514acda9a381e7d976c3be 100644 (file)
@@ -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;
 }
index d060d319a22c46b4f1c00bd6f119d081a2d05fd5..bfcbd245cf8856cfcc6ef157b1495089e5fbe8ef 100644 (file)
@@ -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';
index d84345b71c90e2722530a8b7a725dcfcc7a573b0..bddc2b238b56104869b62e8698e565724d9609a5 100644 (file)
@@ -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;
index b68d3dac9d7b322d7a8a66f50abd7e08e691dca4..5207d57e723875ae26d6a71f97586aa2517cdac6 100644 (file)
@@ -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 (file)
index 0000000..30c3926
Binary files /dev/null and b/UI/WebServerResources/add-user-calendar.png differ
index c16071500fcd8222f834a97c1b2fd3101f7e6fc8..3a4462c4d79d87560d66ab30b216de6387ac8e34 100644 (file)
@@ -971,6 +971,7 @@ function subscribeToFolder(refreshCallback, refreshCallbackData) {
         document.subscriptionAjaxRequest.aborted = true;
         document.subscriptionAjaxRequest.abort();
       }
+
       var rfCbData = { method: refreshCallback, data: refreshCallbackData };
       document.subscriptionAjaxRequest = triggerAjaxRequest(url,
                                                            folderSubscriptionCallback,
@@ -994,29 +995,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) {
@@ -1277,12 +1291,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);
 }
@@ -1328,6 +1355,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) {
index a8947edb4f8a3cf9c76ed99f2effec29f4af838c..7ab42d121e19921b798c31fad3efe3ccee1cb29b 100644 (file)
Binary files a/UI/WebServerResources/lori-login.jpg and b/UI/WebServerResources/lori-login.jpg differ