]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1292 d1b88da0-ebda-0310...
authorwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Thu, 6 Dec 2007 22:56:53 +0000 (22:56 +0000)
committerwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Thu, 6 Dec 2007 22:56:53 +0000 (22:56 +0000)
24 files changed:
ChangeLog
SoObjects/Appointments/SOGoAppointmentFolder.m
SoObjects/Appointments/SOGoAppointmentObject.m
SoObjects/Appointments/SOGoCalendarComponent.m
SoObjects/Appointments/product.plist
SoObjects/Contacts/SOGoContactGCSFolder.m
SoObjects/Contacts/SOGoContactLDAPFolder.m
SoObjects/Mailer/SOGoMailBodyPart.m
SoObjects/SOGo/SOGoFolder.h
SoObjects/SOGo/SOGoObject.m
UI/Common/UIxPageFrame.m
UI/MailerUI/UIxMailEditor.m
UI/Scheduler/NSArray+Scheduler.h
UI/Scheduler/NSArray+Scheduler.m
UI/Scheduler/UIxCalListingActions.m
UI/Scheduler/UIxComponentEditor.m
UI/Templates/ContactsUI/UIxContactsListViewContainer.wox
UI/Templates/SchedulerUI/UIxCalMainView.wox
UI/Templates/UIxPageFrame.wox
UI/WebServerResources/ContactsUI.js
UI/WebServerResources/SchedulerUI.js
UI/WebServerResources/UIxMailToSelection.js
UI/WebServerResources/generic.css
UI/WebServerResources/generic.js

index af43fe95093356de9bd71b69a6c0ab7c68b371ef..419b641c02af64d4f2065e6d3400ea43149467e9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,62 @@
+2007-12-06  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * SoObjects/Appointments/SOGoCalendarComponent.m
+       ([SOGoCalendarComponent -contentAsString]): securize the returned content.
+       ([SOGoCalendarComponent -aclsForUser:uid]): check whether the user
+       is an organizer or a participant as well.
+
+       * SoObjects/Appointments/SOGoAppointmentObject.m
+       ([SOGoAppointmentObject -changeParticipationStatus:_status]): the
+       user we should change the status of is not the active user but the
+       owner of the object. This is for delegation.
+
+       * SoObjects/Appointments/SOGoAppointmentFolder.m
+       ([SOGoAppointmentFolder -fetchContentObjectNames]): fetch the
+       elements through the fetchFields:from:to:title:component: method
+       method, so that the result is already filtered depending on the
+       acl.
+
+       * UI/Scheduler/UIxComponentEditor.m ([UIxComponentEditor
+       -toolbar]): if the current user is a delegate, take the permission
+       "ModifyComponent" into account. Also, do not let him/her modify an
+       event for which he/she is the organizer.
+
+       * SoObjects/Contacts/SOGoContactLDAPFolder.m
+       ([SOGoContactLDAPFolder -davResourceType]): declared as a carddav
+       addressbook collection.
+
+       * SoObjects/Contacts/SOGoContactGCSFolder.m ([SOGoContactGCSFolder
+       -davResourceType]):  declared as a carddav addressbook collection.
+
+       * SoObjects/SOGo/SOGoObject.m ([SOGoObject -davLastModified]):
+       override SoObject's implementation. Otherwise a localized date is returned.
+
+       * UI/Common/UIxPageFrame.m ([UIxPageFrame -isCalendar])
+       ([UIxPageFrame -isContacts], [UIxPageFrame -isMail]): getters to
+       determine which module is the current one.
+
+2007-12-06  Francis Lachapelle  <flachapelle@inverse.ca>
+
+       * UI/Scheduler/NSArray+Scheduler.m ([NSArray -compareEventsTitleAscending:otherEvent])
+       ([NSArray -compareEventsLocationAscending:otherEvent])
+       ([NSArray -compareEventsEndDateAscending:otherEvent])
+       ([NSArray -reversedArray]): new methods that sort an array of
+       events depending of various parameters.
+
+       * UI/Scheduler/UIxCalListingActions.m ([UIxCalListingActions
+       -eventsListAction]): added support for sorting events.
+
+2007-12-06  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * UI/MailerUI/UIxMailEditor.m ([UIxMailEditor -saveAction])
+       ([UIxMailEditor -sendAction]): make sure that the attachments
+       having the same filename are correctly sequenced (ex: base.ext,
+       base-1.ext, base-2.ext).
+
+       * SoObjects/Mailer/SOGoMailBodyPart.m ([SOGoMailBodyPart
+       -fetchBLOB]): added support for quoted-printable decoding. Also,
+       we return nil if the type is not supported.
+
 2007-12-05  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
 
        * UI/Scheduler/UIxTaskEditor.m ([UIxTaskEditor -newAction]):
@@ -25,7 +84,7 @@
 
 2007-12-05  Francis Lachapelle  <flachapelle@inverse.ca>
 
-       * SoObjects/Mailer/SOGoDraftObject.m: The condition for replyToAll
+       * SoObjects/Mailer/SOGoDraftObject.m: the condition for replyToAll
        has to be done later to avoid duplicated to and cc addresses.
 
 2007-12-04  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
index 6deb3e3c5aed0593ff36c861fb7c8aae1b263eee..5be6e558889fc3ea424363505f4befc62c1ba08d 100644 (file)
@@ -26,6 +26,7 @@
 
 #import <NGObjWeb/NSException+HTTP.h>
 #import <NGObjWeb/SoObject+SoDAV.h>
+#import <NGObjWeb/SoSecurityManager.h>
 #import <NGObjWeb/WOContext+SoObjects.h>
 #import <NGObjWeb/WOMessage.h>
 #import <NGObjWeb/WORequest.h>
@@ -230,7 +231,6 @@ static NSNumber   *sharedYes = nil;
   return filterData;
 }
 
-#warning filters is leaked here
 - (NSArray *) _parseCalendarFilters: (id <DOMElement>) parentNode
 {
   NSEnumerator *children;
@@ -1422,6 +1422,17 @@ static NSNumber   *sharedYes = nil;
 //   return objectNames;
 // }
 
+- (NSArray *) fetchContentObjectNames
+{
+  static NSArray *cNameField = nil;
+
+  if (!cNameField)
+    cNameField = [[NSArray alloc] initWithObjects: @"c_name", nil];
+
+  return [[self fetchFields: cNameField from: nil to: nil
+               title: nil component: nil] objectsForKey: @"c_name"];
+}
+
 /* folder type */
 
 - (NSString *) folderType
index b8814b8e77b12a1dde7189a061066b0d543aeb2f..c3f28371323da2128ed9268e9dbf85725dc18aa1 100644 (file)
   iCalEvent *event;
   iCalPerson *attendee;
   NSException *ex;
+  SOGoUser *ownerUser;
   
   ex = nil;
 
   event = [self component: NO secure: NO];
   if (event)
     {
-      attendee = [event findParticipant: [context activeUser]];
+      ownerUser = [SOGoUser userWithLogin: owner roles: nil];
+      attendee = [event findParticipant: ownerUser];
       if (attendee)
        ex = [self _handleAttendee: attendee statusChange: _status
                   inEvent: event];
index 4236a6204800c88246fa6525ea54bdda4fbce95e..8411b79415932bcb8ed8ac70cd108ef2e7da1478 100644 (file)
@@ -27,6 +27,7 @@
 #import <NGObjWeb/SoSecurityManager.h>
 #import <NGObjWeb/WOApplication.h>
 #import <NGObjWeb/WOContext+SoObjects.h>
+#import <NGObjWeb/WORequest+So.h>
 #import <NGExtensions/NSObject+Logs.h>
 #import <NGExtensions/NGHashMap.h>
 #import <NGCards/iCalCalendar.h>
@@ -142,6 +143,18 @@ static BOOL sendEMailNotifications = NO;
   return iCalString;
 }
 
+- (NSString *) contentAsString
+{
+  NSString *secureContent;
+
+  if ([[context request] isSoWebDAVRequest])
+    secureContent = [self secureContentAsString];
+  else
+    secureContent = [super contentAsString];
+
+  return secureContent;
+}
+
 - (iCalCalendar *) calendar: (BOOL) create secure: (BOOL) secure
 {
   NSString *componentTag;
@@ -559,6 +572,7 @@ static BOOL sendEMailNotifications = NO;
   NSArray *superAcls;
   iCalRepeatableEntityObject *component;
   NSString *accessRole, *ownerRole;
+  SOGoUser *aclUser;
 
   roles = [NSMutableArray array];
   superAcls = [super aclsForUser: uid];
@@ -573,6 +587,11 @@ static BOOL sendEMailNotifications = NO;
     {
       if (component)
        {
+         aclUser = [SOGoUser userWithLogin: uid roles: nil];
+         if ([component userIsOrganizer: aclUser])
+           [roles addObject: SOGoCalendarRole_Organizer];
+         else if ([component userIsParticipant: aclUser])
+           [roles addObject: SOGoCalendarRole_Participant];
          accessRole = [container roleForComponentsWithAccessClass:
                                    [component symbolicAccessClass]
                                  forUser: uid];
index 4f0cb12bbc8c66ab1bd901c7ad2a5467b48b9456..297b9b2315b8cfa7c33af837e0e11371580d63aa 100644 (file)
     };
     SOGoCalendarComponent = {
       superclass = "SOGoContentObject";
-/*      defaultAccess = "SeeComponent"; */
       defaultRoles = {
        "ViewAllComponent" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer" );
        "ViewDAndT" = ( "Organizer", "Participant", "ComponentDAndTViewer" );
        "ModifyComponent" = ( "Owner", "Organizer" );
-       "RespondToComponent" = ( "Participant" );
-/*     "SeeComponent" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" ); */
+       "RespondToComponent" = ( "Participant", "ComponentModifier", "ComponentResponder" );
+       "Access Object" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" );
+       "Access Contents Information" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" );
+        "WebDAV Access" = ( "Owner", "Organizer", "Participant", "ComponentModifier", "ComponentResponder", "ComponentViewer", "ComponentDAndTViewer" );
       };
     };
     SOGoAppointmentObject = {
index 074048b8a2f5f38a2e96d319ec6a770fca5d0939..dfc77caefc5b2b5d8e454e9a886c3f01dcef034f 100644 (file)
   return @"vcard-collection";
 }
 
+- (NSArray *) davResourceType
+{
+  NSMutableArray *resourceType;
+  NSArray *cardDavCollection;
+
+  cardDavCollection
+    = [NSArray arrayWithObjects: @"addressbook",
+              @"urn:ietf:params:xml:ns:carddav", nil];
+
+  resourceType = [NSMutableArray arrayWithArray: [super davResourceType]];
+  [resourceType addObject: cardDavCollection];
+
+  return resourceType;
+}
+
 /* sorting */
 - (NSComparisonResult) compare: (id) otherFolder
 {
index a23f09a536c6ef3b2cac896c6d03680dd477d2c8..f3d1732831b6298aa0bb808a80cd72b054f1f1b2 100644 (file)
   return @"vcard-collection";
 }
 
+- (NSArray *) davResourceType
+{
+  NSMutableArray *resourceType;
+  NSArray *cardDavCollection;
+
+  cardDavCollection
+    = [NSArray arrayWithObjects: @"addressbook",
+              @"urn:ietf:params:xml:ns:carddav", nil];
+
+  resourceType = [NSMutableArray arrayWithArray: [super davResourceType]];
+  [resourceType addObject: cardDavCollection];
+
+  return resourceType;
+}
+
 - (id) lookupName: (NSString *) objectName
         inContext: (WOContext *) lookupContext
           acquire: (BOOL) acquire
index c60f34b9b179c6d325a2d8f64748796039081a87..0ce0fcd7f59f3f2f9b4229ada4e20c07f5eded30 100644 (file)
@@ -32,6 +32,7 @@
 #import <NGExtensions/NSNull+misc.h>
 #import <NGExtensions/NSObject+Logs.h>
 #import <NGExtensions/NGBase64Coding.h>
+#import <NGExtensions/NGQuotedPrintableCoding.h>
 #import <NGImap4/NGImap4Connection.h>
 
 #import <SoObjects/SOGo/NSDictionary+Utilities.h>
@@ -209,16 +210,22 @@ static BOOL debugOn = NO;
 
   if (enc)
     {
-      enc = [enc uppercaseString];
+      enc = [enc lowercaseString];
       
-      if ([enc isEqualToString:@"BASE64"])
+      if ([enc isEqualToString: @"base64"])
        data = [data dataByDecodingBase64];
-      else if ([enc isEqualToString:@"7BIT"])
+      else if ([enc isEqualToString: @"quoted-printable"])
+       data = [data dataByDecodingQuotedPrintableTransferEncoding];
+      else if ([enc isEqualToString: @"7bit"]
+              || [enc isEqualToString: @"8bit"])
        ; /* keep data as is */ // TODO: do we need to change encodings?
       else
-       [self errorWithFormat:@"unsupported encoding: %@", enc];
+       {
+         data = nil;
+         [self errorWithFormat: @"unsupported encoding: %@", enc];
+       }
     }
-  
+
   return data;
 }
 
index afb55cfc5e32043a966d49760e83d98d70c73ff9..2e02a9f3fd7bcb9c5112e961142ec5bbcf670835 100644 (file)
@@ -40,6 +40,7 @@
 
 /* dav */
 - (NSArray *) davNamespaces;
+- (NSArray *) davResourceType;
 
 /* outlook */
 - (NSString *) outlookFolderClass;
index a9a4900392d705fcd85c56590d117a38d2d520f2..8bb510661e65ee54f916f9fd4300633634df1c5b 100644 (file)
@@ -58,6 +58,7 @@
 #import "SOGoDAVRendererTypes.h"
 
 #import "NSArray+Utilities.h"
+#import "NSCalendarDate+SOGo.h"
 #import "NSDictionary+Utilities.h"
 #import "NSString+Utilities.h"
 
@@ -629,6 +630,11 @@ static BOOL kontactGroupDAV = YES;
   return @"text/plain";
 }
 
+- (NSString *) davLastModified
+{
+  return [[NSCalendarDate date] rfc822DateString];
+}
+
 - (WOResponse *) _webDAVResponse: (WOContext *) localContext
 {
   WOResponse *response;
index b3565daed4a77c8e5d9c292ffbe5f9ac58b32ed1..54b70d5d7801ef4be9a34bf602dbf0f4d757aed4 100644 (file)
   return ([[self productCSSURL] length] > 0);
 }
 
+- (BOOL) _moduleIs: (NSString *) moduleName
+{
+  NSString *frameworkName;
+
+  frameworkName = [[context page] frameworkName];
+
+  return [frameworkName isEqualToString: moduleName];
+}
+
+- (BOOL) isCalendar
+{
+  return [self _moduleIs: @"SchedulerUI"];
+}
+
+- (BOOL) isContacts
+{
+  return [self _moduleIs: @"ContactsUI"];
+}
+
+- (BOOL) isMail
+{
+  return [self _moduleIs: @"MailerUI"];
+}
+
 - (void) setToolbar: (NSString *) newToolbar
 {
   ASSIGN (toolbar, newToolbar);
index 31e33f02af7b7dac69a4ed2b2e8f0a4f5aebcdc4..c9c41dc2c6fea39ecd6b1a1206f662bcc3147b8c 100644 (file)
@@ -67,6 +67,7 @@
   /* these are for the inline attachment list */
   NSString *attachmentName;
   NSArray  *attachmentNames;
+  NSMutableArray *attachedFiles;
 }
 
 @end
@@ -115,6 +116,7 @@ static NSArray *infoKeys = nil;
   [bcc release];
   [attachmentName release];
   [attachmentNames release];
+  [attachedFiles release];
   [super dealloc];
 }
 
@@ -262,6 +264,29 @@ static NSArray *infoKeys = nil;
 }
 
 /* actions */
+- (NSString *) _fixedFilename: (NSString *) filename
+{
+  NSString *newFilename, *baseFilename, *extension;
+  unsigned int variation;
+
+  if (!attachedFiles)
+    attachedFiles = [NSMutableArray new];
+
+  newFilename = filename;
+
+  baseFilename = [filename stringByDeletingPathExtension];
+  extension = [filename pathExtension];
+  variation = 0;
+  while ([attachedFiles containsObject: newFilename])
+    {
+      variation++;
+      newFilename = [NSString stringWithFormat: @"%@-%d.%@", baseFilename,
+                             variation, extension];
+    }
+  [attachedFiles addObject: newFilename];
+
+  return newFilename;
+}
 
 - (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody
 {
@@ -271,7 +296,7 @@ static NSArray *infoKeys = nil;
   unsigned int count, max;
   NGMimeBodyPart *part;
   NGMimeContentDispositionHeaderField *header;
-  NSString *mimeType;
+  NSString *mimeType, *filename;
 
   parts = [httpBody parts];
   max = [parts count];
@@ -284,8 +309,9 @@ static NSArray *infoKeys = nil;
        [part headerForKey: @"content-disposition"];
       mimeType = [(NGMimeType *)
                   [part headerForKey: @"content-type"] stringValue];
+      filename = [self _fixedFilename: [header filename]];
       attachment = [NSDictionary dictionaryWithObjectsAndKeys:
-                                  [header filename], @"filename",
+                                  filename, @"filename",
                                 mimeType, @"mimetype", nil];
       [filenames setObject: attachment forKey: [header name]];
     }
@@ -370,12 +396,12 @@ static NSArray *infoKeys = nil;
 {
   NSArray *a;
 
-  if (attachmentNames != nil)
-    return attachmentNames;
-
-  a = [[self clientObject] fetchAttachmentNames];
-  a = [a sortedArrayUsingSelector: @selector (compare:)];
-  attachmentNames = [a copy];
+  if (attachmentNames)
+    {
+      a = [[self clientObject] fetchAttachmentNames];
+      a = [a sortedArrayUsingSelector: @selector (compare:)];
+      attachmentNames = [a copy];
+    }
 
   return attachmentNames;
 }
index ac8e8b9874a9c21853727ea43cafefa9a00402eb..b9da8dc3f0c1ce2d9cbaf1a434596f32141d0f8e 100644 (file)
 
 @interface NSArray (SOGoEventComparison)
 
-- (NSComparisonResult) compareEventsAscending: (NSArray *) otherEvent;
+- (NSComparisonResult) compareEventsStartDateAscending: (NSArray *) otherEvent;
+- (NSComparisonResult) compareEventsEndDateAscending: (NSArray *) otherEvent;
+- (NSComparisonResult) compareEventsTitleAscending: (NSArray *) otherEvent;
+- (NSComparisonResult) compareEventsLocationAscending: (NSArray *) otherEvent;
 - (NSComparisonResult) compareTasksAscending: (NSArray *) otherTask;
+- (NSArray *) reversedArray;
 
 @end
 
index 6425f6c53721036b5b442ddca7a1c269a82511e9..eaf7f0d44b377d6409653c07d63a38d09d3f17b7 100644 (file)
@@ -47,7 +47,7 @@
   return result;
 }
 
-- (NSComparisonResult) compareEventsAscending: (NSArray *) otherEvent
+- (NSComparisonResult) compareEventsStartDateAscending: (NSArray *) otherEvent
 {
   NSComparisonResult result;
   unsigned int selfTime, otherTime;
   return result;
 }
 
+- (NSComparisonResult) compareEventsEndDateAscending: (NSArray *) otherEvent
+{
+  NSComparisonResult result;
+  unsigned int selfTime, otherTime;
+
+  selfTime = [[self objectAtIndex: 5] intValue];
+  otherTime = [[otherEvent objectAtIndex: 5] intValue];
+  if (selfTime > otherTime)
+    result = NSOrderedDescending;
+  else if (selfTime < otherTime)
+    result = NSOrderedAscending;
+  else
+    result = NSOrderedSame;
+
+  return result;
+}
+
+- (NSComparisonResult) compareEventsTitleAscending: (NSArray *) otherEvent
+{
+  NSString *selfTitle, *otherTitle;
+
+  selfTitle = [self objectAtIndex: 3];
+  otherTitle = [otherEvent objectAtIndex: 3];
+
+  return [selfTitle caseInsensitiveCompare: otherTitle];
+}
+
+- (NSComparisonResult) compareEventsLocationAscending: (NSArray *) otherEvent
+{
+  NSString *selfTitle, *otherTitle;
+
+  selfTitle = [self objectAtIndex: 6];
+  otherTitle = [otherEvent objectAtIndex: 6];
+
+  return [selfTitle caseInsensitiveCompare: otherTitle];
+}
+
 - (NSComparisonResult) compareTasksAscending: (NSArray *) otherTask
 {
   NSComparisonResult result;
   return result;
 }
 
+- (NSArray *) reversedArray 
+{
+  return [[self reverseObjectEnumerator] allObjects];
+}
+
 @end
index f5a25d5a9443ef8c19616c62d4815ba6bb79bfd1..5e8905a355ba5a8f826e7f6ce58cd00144764c56 100644 (file)
   NSMutableArray *newEvents, *newEvent;
   unsigned int interval;
   BOOL isAllDay;
+  NSString *sort, *ascending;
 
   [self _setupContext];
 
       [newEvent addObject: [self _formattedDateForSeconds: interval
                                 forAllDay: isAllDay]];
       [newEvents addObject: newEvent];
-
+      
       oldEvent = [events nextObject];
     }
-  [newEvents sortUsingSelector: @selector (compareEventsAscending:)];
+  
+  sort = [[context request] formValueForKey: @"sort"];
+  if ([sort isEqualToString: @"title"])
+    [newEvents sortUsingSelector: @selector (compareEventsTitleAscending:)];
+  else if ([sort isEqualToString: @"end"])
+    [newEvents sortUsingSelector: @selector (compareEventsEndDateAscending:)];
+  else if ([sort isEqualToString: @"location"])
+    [newEvents sortUsingSelector: @selector (compareEventsLocationAscending:)];
+  else
+    [newEvents sortUsingSelector: @selector (compareEventsStartDateAscending:)];
+  
+  ascending = [[context request] formValueForKey: @"asc"];
+  if (![ascending boolValue])
+    newEvents = [newEvents reversedArray];
 
   return [self _responseWithData: newEvents];
 }
index 9e5f812f8acc00b0fc8b857e368a68ea60466405..6af7d172421f598babac9caef833133b1f6bc3cd 100644 (file)
   SoSecurityManager *sm;
   NSString *toolbarFilename, *adminToolbar;
   iCalPersonPartStat participationStatus;
+  SOGoUser *currentUser;
 
   if ([clientObject isKindOfClass: [SOGoAppointmentObject class]])
     adminToolbar = @"SOGoAppointmentObject.toolbar";
   sm = [SoSecurityManager sharedSecurityManager];
   if ([[component attendees] count])
     {
-      if ([component userIsOrganizer: ownerUser]
-         && ![sm validatePermission: SOGoCalendarPerm_ModifyComponent
-                 onObject: clientObject
-                 inContext: context])
-       toolbarFilename = adminToolbar;
-      else if ([component userIsParticipant: ownerUser]
-              && ![sm validatePermission: SOGoCalendarPerm_RespondToComponent
-                      onObject: clientObject
-                      inContext: context])
-       {
-         participationStatus
-           = [[component findParticipant: ownerUser] participationStatus];
-         /* Lightning does not manage participation status within tasks */
-         if (participationStatus == iCalPersonPartStatAccepted)
-           toolbarFilename = @"SOGoAppointmentObjectDecline.toolbar";
-         else if (participationStatus == iCalPersonPartStatDeclined)
-           toolbarFilename = @"SOGoAppointmentObjectAccept.toolbar";
+      currentUser = [context activeUser];
+      if ([component userIsOrganizer: currentUser])
+       toolbarFilename = @"SOGoComponentClose.toolbar";
+      else
+       {       
+         if ([component userIsOrganizer: ownerUser]
+             && ![sm validatePermission: SOGoCalendarPerm_ModifyComponent
+                     onObject: clientObject
+                     inContext: context])
+           toolbarFilename = adminToolbar;
+         else if ([component userIsParticipant: ownerUser]
+                  && !([sm validatePermission: SOGoCalendarPerm_RespondToComponent
+                           onObject: clientObject
+                           inContext: context]
+                       && [sm validatePermission: SOGoCalendarPerm_ModifyComponent
+                              onObject: clientObject
+                              inContext: context]))
+           {
+             participationStatus
+               = [[component findParticipant: ownerUser] participationStatus];
+             /* Lightning does not manage participation status within tasks */
+             if (participationStatus == iCalPersonPartStatAccepted)
+               toolbarFilename = @"SOGoAppointmentObjectDecline.toolbar";
+             else if (participationStatus == iCalPersonPartStatDeclined)
+               toolbarFilename = @"SOGoAppointmentObjectAccept.toolbar";
+             else
+               toolbarFilename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar";
+           }
          else
-           toolbarFilename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar";
+           toolbarFilename = @"SOGoComponentClose.toolbar";
        }
-      else
-       toolbarFilename = @"SOGoComponentClose.toolbar";
     }
   else
     {
index 1733b8969f5d5edca0200e1fca9de1d2c40e46bc..31ccfc8c2a080151c2d60f392f939182995ab1bd 100644 (file)
@@ -35,8 +35,6 @@
       </ul>
     </div>
 
-    <form name="searchform" var:href="view" var:_sort="sortKey"
-      onsubmit="return onSearchFormSubmit();" method="GET">
       <!--     var:if condition="isPopup" const:negate="YES" -->
       <!--          <var:if condition="hideFolderTree" const:negate="YES" -->
       <div class="contactFoldersList" id="contactFoldersList">
@@ -95,7 +93,6 @@
       <!--     var:if condition="isPopup">
       var:component-content />
       /var:if> -->
-    </form>
 
     <var:string value="errorAlertJavaScript" const:escapeHTML="NO" />
   </var:if>
index 0431db72f4f8062084a9b4dcc4bde8f0ee474ebd..54fac20cb87b6efc39ae6653a4796271e896199d 100644 (file)
       <table id="eventsList" cellspacing="0">
         <thead>
          <tr>
-           <td class="headerCell headerTitle sortableTableHeader"><var:string label:value="Title"/></td>
-           <td class="headerCell headerDateTime sortableTableHeader"><var:string label:value="Start"/></td>
-           <td class="headerCell headerDateTime sortableTableHeader"><var:string label:value="End"/></td>
-           <td class="headerCell headerLocation sortableTableHeader"><var:string label:value="Location"/></td>
+           <td id="titleHeader" class="headerCell headerTitle sortableTableHeader"><var:string label:value="Title"/></td>
+           <td id="startHeader" class="headerCell headerDateTime sortableTableHeader"><var:string label:value="Start"/></td>
+           <td id="endHeader" class="headerCell headerDateTime sortableTableHeader"><var:string label:value="End"/></td>
+           <td id="locationHeader" class="headerCell headerLocation sortableTableHeader"><var:string label:value="Location"/></td>
          </tr>
         </thead>
         <tbody></tbody>
index d942676ed6070fe596e0e734669e99af8624c413..3d936ae62423854c0797072c95c7a83fe50b49af 100644 (file)
                  <a id="logoff" var:href="logoffPath"
                    ><var:string label:value="Disconnect" /></a>
                  <var:if condition="userHasCalendarAccess">
-                   <a id="calendarBannerLink"
-                     var:href="relativeCalendarPath"
-                     ><var:string label:value="Calendar" /></a> |
+                   <var:if condition="isCalendar">
+                     <span class="active"><var:string label:value="Calendar"
+                         /></span>
+                   </var:if>
+                   <var:if condition="isCalendar" const:negate="YES">
+                     <a id="calendarBannerLink"
+                       var:href="relativeCalendarPath"
+                       ><var:string label:value="Calendar" /></a>
+                   </var:if>
+                   |
                  </var:if>
-                 <a id="contactsBannerLink"
-                   var:href="relativeContactsPath"
-                   ><var:string label:value="Address Book" /></a> |
+                 <var:if condition="isContacts">
+                   <span class="active"><var:string label:value="Address Book"
+                       /></span>
+                 </var:if>
+                 <var:if condition="isContacts" const:negate="YES">
+                   <a id="contactsBannerLink"
+                     var:href="relativeContactsPath"
+                     ><var:string label:value="Address Book" /></a>
+                 </var:if>
+                 |
                  <var:if condition="userHasMailAccess">
-                   <a id="mailBannerLink" var:href="relativeMailPath"
-                     ><var:string label:value="Mail" /></a> |
+                   <var:if condition="isMail">
+                     <span class="active"><var:string label:value="Mail"
+                         /></span>
+                   </var:if>
+                   <var:if condition="isMail" const:negate="YES">
+                     <a id="mailBannerLink" var:href="relativeMailPath"
+                       ><var:string label:value="Mail" /></a>
+                   </var:if>
+                     |
                  </var:if>
                  <a id="preferencesBannerLink"
                    var:href="relativePreferencesPath"
index 087d4f84847c1bb79c6c2b292fb28bc3f5a6fc4d..fa2ba949f80ec60a2635442acf40d6df53f9749c 100644 (file)
@@ -532,16 +532,34 @@ function onAddressBookNew(event) {
 }
 
 function appendAddressBook(name, folder) {
-  if (folder)
+  var owner;
+
+  if (folder) {
+    owner = getSubscribedFolderOwner(folder);
     folder = accessToSubscribedFolder(folder);
+  }
   else
     folder = "/" + name;
+  
+  if (!owner)
+    owner = UserLogin;
+
   if ($(folder))
     window.alert(clabels["You have already subscribed to that folder!"]);
   else {
+    var contactFolders = $("contactFolders");
+    var items = contactFolders.childNodesWithTag("li");
     var li = document.createElement("li");
-    $("contactFolders").appendChild(li);
+
+    // Add the calendar to the proper place
+    var i = getListIndexForFolder(items, owner, name);
+    if (i != items.length) // User is subscribed to other calendars of the same owner
+      contactFolders.insertBefore(li, items[i]);
+    else 
+      contactFolders.appendChild(li);
+
     li.setAttribute("id", folder);
+    li.setAttribute("owner", owner);
     li.appendChild(document.createTextNode(name));
     setEventsOnContactFolder(li);
   }
@@ -758,12 +776,18 @@ function onContactFoldersMenuPrepareVisibility() {
 
   if (selected.length > 0) {
     var folderOwner = selected[0].getAttribute("owner");
+    var modifyOption = $(this).down("ul").childElements().first();
     var sharingOption = $(this).down("ul").childElements().last();
-    // Disable the "Sharing" option when address book is not owned by user
-    if (folderOwner == UserLogin || IsSuperUser)
+    // Disable the "Sharing" and "Modify" options when address book
+    // is not owned by user
+    if (folderOwner == UserLogin || IsSuperUser) {
+      modifyOption.removeClassName("disabled");
       sharingOption.removeClassName("disabled");
-    else
+    }
+    else {
+      modifyOption.addClassName("disabled");
       sharingOption.addClassName("disabled");
+    }
   }
 }
 
index e4ebdd0d192f04152911a69205d9511d3a18387e..8dd803d1062cce318802928183e1ca329c034d6e 100644 (file)
@@ -1,7 +1,5 @@
 /* JavaScript for SOGoCalendar */
 
-var sortOrder = '';
-var sortKey = '';
 var listFilter = 'view_today';
 
 var listOfSelection = null;
@@ -328,9 +326,6 @@ function eventsListCallback(http) {
 
     document.eventsListAjaxRequest = null;
     var table = $("eventsList");
-    var params = parseQueryParameters(http.callbackData);
-    sortKey = params["sort"];
-    sortOrder = params["desc"];
     lastClickedRow = -1; // from generic.js
 
     if (http.responseText.length > 0) {
@@ -374,6 +369,24 @@ function eventsListCallback(http) {
        Event.observe(td, "mousedown", listRowMouseDownHandler, true);
        td.appendChild(document.createTextNode(data[i][6]));
       }
+
+      if (sorting["attribute"] && sorting["attribute"].length > 0) {
+       var sortHeader = $(sorting["attribute"] + "Header");
+      
+       if (sortHeader) {
+         var sortImages = $(table.tHead).getElementsByClassName("sortImage");
+         $(sortImages).each(function(item) {
+             item.remove();
+           });
+
+         var sortImage = createElement("img", "messageSortImage", "sortImage");
+         sortHeader.insertBefore(sortImage, sortHeader.firstChild);
+         if (sorting["ascending"])
+           sortImage.src = ResourcesURL + "/title_sortdown_12x12.png";
+         else
+           sortImage.src = ResourcesURL + "/title_sortup_12x12.png";
+       }
+      }
     }
   }
   else
@@ -995,10 +1008,28 @@ function _loadTasksHref(href) {
 }
 
 function onHeaderClick(event) {
-  //log("onHeaderClick: " + this.link);
-  //_loadEventHref(this.link);
+   var headerId = this.getAttribute("id");
+  var newSortAttribute;
+  if (headerId == "titleHeader")
+    newSortAttribute = "title";
+  else if (headerId == "startHeader")
+    newSortAttribute = "start";
+  else if (headerId == "endHeader")
+    newSortAttribute = "end";
+  else if (headerId == "locationHeader")
+    newSortAttribute = "location";
+  else
+    newSortAttribute = "start";
+  
+   if (sorting["attribute"] == newSortAttribute)
+    sorting["ascending"] = !sorting["ascending"];
+  else {
+    sorting["attribute"] = newSortAttribute;
+    sorting["ascending"] = true;
+  }
+   refreshEvents();
 
-  preventDefault(event);
+  Event.stop(event);
 }
 
 function refreshCurrentFolder() {
@@ -1012,9 +1043,9 @@ function refreshEvents() {
     titleSearch = "&search=" + value;
   else
     titleSearch = "";
-
-  return _loadEventHref("eventslist?desc=" + sortOrder
-                       + "&sort=" + sortKey
+  return _loadEventHref("eventslist?asc=" + sorting["ascending"]
+                       + "&sort=" + sorting["attribute"]
                        + "&day=" + currentDay
                        + titleSearch
                        + "&filterpopup=" + listFilter);
@@ -1517,25 +1548,13 @@ function appendCalendar(folderName, folderPath) {
     window.alert(clabels["You have already subscribed to that folder!"]);
   else {
     var calendarList = $("calendarList");
-    var lis = calendarList.childNodesWithTag("li");
+    var items = calendarList.childNodesWithTag("li");
     var li = document.createElement("li");
     
     // Add the calendar to the proper place
-    var previousOwner = null;
-    for (var i = 0; i < lis.length; i++) {
-      var currentFolderName = lis[i].lastChild.nodeValue.strip();
-      var currentOwner = lis[i].readAttribute('owner');
-      if (currentOwner == owner) {
-       previousOwner = currentOwner;
-       if (currentFolderName > folderName)
-         break;
-      }
-      else if (previousOwner || 
-              (currentOwner != UserLogin && currentOwner > owner))
-       break;
-    }
-    if (i != lis.length) // User is subscribed to other calendars of the same owner
-      calendarList.insertBefore(li, lis[i]);
+    var i = getListIndexForFolder(items, owner, folderName);
+    if (i != items.length) // User is subscribed to other calendars of the same owner
+      calendarList.insertBefore(li, items[i]);
     else 
       calendarList.appendChild(li);
     
@@ -1544,7 +1563,7 @@ function appendCalendar(folderName, folderPath) {
 
     // Generate new color
     if (calendarColorIndex == null)
-      calendarColorIndex = lis.length;
+      calendarColorIndex = items.length;
     calendarColorIndex++;
     var colorTable = [1, 1, 1];
     var color;
@@ -1699,7 +1718,7 @@ function configureLists() {
 
    list = $("eventsList");
    list.multiselect = true;
-   //configureSortableTableHeaders(list);
+   configureSortableTableHeaders(list);
    TableKit.Resizable.init(list, {'trueResize' : true, 'keepWidth' : true});
    Event.observe(list, "mousedown",
                 onEventsSelectionChange.bindAsEventListener(list));
@@ -1722,6 +1741,9 @@ function initDateSelectorEvents() {
 }
 
 function initCalendars() {
+   sorting["attribute"] = "start";
+   sorting["ascending"] = true;
+  
    if (!document.body.hasClassName("popup")) {
       initDateSelectorEvents();
       initCalendarSelector();
index e76dfd54cafc4a6607b4b5453f205b298f769c7b..8a9a686f378fc75fb15749db12c2533dc5b137fc 100644 (file)
@@ -44,45 +44,11 @@ function hasAddress(email) {
   return false;
 }
 
-function rememberAddress(email) {
-  var list, span, idx;
-  
-  list    = $('addr_addresses');
-  span    = document.createElement('span');
-  span.id = email;
-  idx     = document.createTextNode(currentIndex);
-  span.appendChild(idx);
-  list.appendChild(span);
-}
-
 function checkAddresses() {
   alert("addressCount: " + this.getAddressCount() + " currentIndex: " + currentIndex + " lastIndex: " + lastIndex);
 }
 
-function addAddress(type, email, uid, sn, cn, dn) {
-  var shouldAddRow, s, e;
-  
-  shouldAddRow = true;
-  if (cn)
-    s = this.sanitizedCn(cn) + ' <' + email + '>';
-  else
-    s = email;
-
-  if(this.hasAddress(email))
-    return;
-  
-  e = $('addr_0');
-  if(e.value == '') {
-    e.value = s;
-    shouldAddRow = false;
-  }
-  if(shouldAddRow) {
-    this.fancyAddRow(false, s);
-  }
-  this.rememberAddress(email);
-}
-
-function fancyAddRow(shouldEdit, text) {
+function fancyAddRow(shouldEdit, text, type) {
   var addr, addressList, lastChild, proto, row, select, input;
   
   addr = $('addr_' + lastIndex);
@@ -106,7 +72,7 @@ function fancyAddRow(shouldEdit, text) {
   var rowNodes = row.childNodesWithTag("td");
   select = $(rowNodes[0]).childNodesWithTag("select")[0];
   select.name = 'popup_' + currentIndex;
-  select.value = proto.down("select").value;
+  select.value = (type? type : proto.down("select").value);
   input = $(rowNodes[1]).childNodesWithTag("input")[0];
   input.name  = 'addr_' + currentIndex;
   input.id = 'addr_' + currentIndex;
@@ -136,6 +102,16 @@ function addressFieldGotFocus(sender) {
 function addressFieldLostFocus(sender) {
   lastIndex = this.getIndexFromIdentifier(sender.id);
   
+  var addresses = sender.value.split(',');
+  if (addresses.length > 0) {
+    sender.value = addresses[0].strip();
+    for (var i = 1; i < addresses.length; i++) {
+      var addr = addresses[i].strip();
+      if (addr.length > 0)
+       fancyAddRow(false, addr, $(sender).up("tr").down("select").value);
+    }
+  }
+
   return false;
 }
 
@@ -148,27 +124,10 @@ function removeLastEditedRowIfEmpty() {
   addr = $('addr_' + lastIndex);
   if (!addr) return;
   if (addr.value.strip() != '') return;
-  addr = this.findAddressWithIndex(lastIndex);
-  if(addr) {
-    var addresses = $('addr_addresses');
-    addresses.removeChild(addr);
-  }
   senderRow = $("row_" + lastIndex);
   addressList.removeChild(senderRow);
 }
 
-function findAddressWithIndex(idx) {
-  var list, i, count, addr, idx
-  list = $('addr_addresses').childNodes;
-  count = list.length;
-  for(i = 0; i < count; i++) {
-    addr = list[i];
-    if(addr.innerHTML == idx)
-      return addr;
-  }
-  return null;
-}
-
 function getIndexFromIdentifier(id) {
   return id.split('_')[1];
 }
index 62776f379a5f74f69faab0c8e33275b6b1cdbece..0200a3222499b0181097dbdc62e718b1eff444fd 100644 (file)
@@ -137,12 +137,17 @@ DIV.linkbanner
   z-index: 100;
   color: #aaa; }
 
-DIV.linkbanner A
-{ top: 0px;
-  left: 0px;
+DIV.linkbanner A,
+DIV.linkbanner SPAN
+{ cursor: default;
   color: #ddd;
   text-decoration: none;
-  padding: .2em .5em; }
+  top: 0px;
+  left: 0px;
+  padding: 2px 5px; }
+
+DIV.linkbanner SPAN
+{ color: #afafff; }
 
 DIV.linkbanner A:hover
 { color: #dd5; }
index 6313cd2ea74df4103e7a2394337ac36686bcd356..054005e0a257c051322baf7b47dfe37d0b1884c6 100644 (file)
@@ -920,7 +920,7 @@ function onSearchKeyDown(event) {
 
   if (event.keyCode == 13) {
     onSearchFormSubmit();
-    event.preventDefault();
+    preventDefault(event);
   }
   else
     this.timer = setTimeout("onSearchFormSubmit()", 1000);
@@ -1070,6 +1070,28 @@ function getSubscribedFolderOwner(serverFolder) {
   return owner;
 }
 
+function getListIndexForFolder(items, owner, folderName) {
+  var i;
+  var previousOwner = null;
+  
+  for (var i = 0; i < items.length; i++) {
+    var currentFolderName = items[i].lastChild.nodeValue.strip();
+    var currentOwner = items[i].readAttribute('owner');
+    if (currentOwner == owner) {
+      previousOwner = currentOwner;
+      if (currentFolderName > folderName)
+       break;
+    }
+    else if (previousOwner || 
+            (currentOwner != UserLogin && currentOwner > owner))
+      break;
+    else if (currentOwner == "nobody")
+      break;
+  }
+  
+  return i;
+}
+
 function listRowMouseDownHandler(event) {
   preventDefault(event);
   //Event.stop(event);