]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1266 d1b88da0-ebda-0310...
authorwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Mon, 19 Nov 2007 16:52:21 +0000 (16:52 +0000)
committerwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Mon, 19 Nov 2007 16:52:21 +0000 (16:52 +0000)
13 files changed:
ChangeLog
SoObjects/Mailer/SOGoMailBodyPart.m
SoObjects/Mailer/SOGoMailObject.h
SoObjects/Mailer/SOGoMailObject.m
SoObjects/SOGo/SOGoUser.m
UI/MailPartViewers/UIxMailPartICalActions.m
UI/MailPartViewers/UIxMailPartViewer.m
UI/MailPartViewers/product.plist
UI/MailerUI/UIxMailView.m
UI/Scheduler/UIxComponentEditor.m
UI/Scheduler/UIxTaskEditor.m
UI/Scheduler/product.plist
UI/Templates/MailerUI/UIxMailView.wox

index 1f711462f46b8fef09cd80085b55945303c9a4b5..a816760558d7a78e2dc6a008e619e954584d6ab9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+2007-11-19  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * UI/Scheduler/UIxComponentEditor.m ([UIxComponentEditor
+       -toolbar]): rewrote in a way that ensures that each case is
+       handled properly.
+
+       * SoObjects/SOGo/SOGoUser.m ([SOGoUser -isEqual:otherUser]): new
+       override method.
+
+       * UI/Scheduler/UIxTaskEditor.m ([-acceptAction])
+       ([-declineAction]): commented out unused methods.
+
+       * UI/MailPartViewers/UIxMailPartICalActions.m
+       ([UIxMailPartICalActions -deleteFromCalendarAction]): actually
+       delete the found object.
+
+2007-11-18  Ludovic Marcotte <ludovic@inverse.ca>
+
+       * SoObjects/Mailer/SOGoMailBodyPart.m
+       SoObjects/Mailer/SOGoMailObject.m
+       UI/MailPartViewers/UIxMailPartViewer.m
+       Added support of messages containing non-textual
+       content and no parts.
+
+       * UI/MailerUI/UIxMailView.m
+       SoObjects/Mailer/SOGoMailObject.m
+       UI/Templates/MailerUI/UIxMailView.wox
+       Added support for the Reply-To header upon
+       message display.
+
 2007-11-18  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
 
        * UI/Scheduler/UIxTaskEditor.m ([UIxTaskEditor -saveAction]):
index 8082912773530475578703a998a025d371377641..e770fa92ca9563a51d1f9cdbafb8c6e28a136fef 100644 (file)
@@ -179,17 +179,25 @@ static BOOL debugOn = NO;
   if (data == nil) return nil;
 
   /* check for content encodings */
-  
-  if ((enc = [[self partInfo] valueForKey:@"encoding"]) != nil) {
-    enc = [enc uppercaseString];
-    
-    if ([enc isEqualToString:@"BASE64"])
-      data = [data dataByDecodingBase64];
-    else if ([enc isEqualToString:@"7BIT"])
-      ; /* keep data as is */ // TODO: do we need to change encodings?
-    else
-      [self errorWithFormat:@"unsupported encoding: %@", enc];
-  }
+  enc = [[self partInfo] valueForKey: @"encoding"];
+  /* if we haven't found one, check out the main message's encoding
+     as we could be trying to fetch the message's content as a part */
+  if (!enc)
+    enc = [[[[self mailObject] fetchCoreInfos] valueForKey: @"body"]
+           valueForKey: @"encoding"];
+
+  if (enc)
+    {
+      enc = [enc uppercaseString];
+      
+      if ([enc isEqualToString:@"BASE64"])
+       data = [data dataByDecodingBase64];
+      else if ([enc isEqualToString:@"7BIT"])
+       ; /* keep data as is */ // TODO: do we need to change encodings?
+      else
+       [self errorWithFormat:@"unsupported encoding: %@", enc];
+    }
   
   return data;
 }
index e5d11e821ac2f502ee958c0eb6793b9e1b96539d..fa0b5458448af027c7bd5f6170e122e83f9111dc 100644 (file)
   would address the MIME part 1.2.3 of the mail 12345 in the folder INBOX.
 */
 
-@class NSData, NSString, NSArray, NSCalendarDate, NSException, NSDictionary;
-@class NGImap4Envelope, NGImap4EnvelopeAddress;
+@class NSArray;
+@class NSCalendarDate;
+@class NSData;
+@class NSDictionary;
+@class NSException;
+@class NSString;
+
+@class NGImap4Envelope;
+@class NGImap4EnvelopeAddress;
 
 @interface SOGoMailObject : SOGoMailBaseObject
 {
 
 /* core infos */
 
-- (BOOL)doesMailExist;
-- (id)fetchCoreInfos; // TODO: what does it do?
+- (BOOL) doesMailExist;
+- (id) fetchCoreInfos; // TODO: what does it do?
 
-- (NGImap4Envelope *)envelope;
-- (NSString *)subject;
-- (NSString *)decodedSubject;
-- (NSCalendarDate *)date;
-- (NSArray *)fromEnvelopeAddresses;
-- (NSArray *)toEnvelopeAddresses;
-- (NSArray *)ccEnvelopeAddresses;
+- (NGImap4Envelope *) envelope;
+- (NSString *) subject;
+- (NSString *) decodedSubject;
+- (NSCalendarDate *) date;
+- (NSArray *) fromEnvelopeAddresses;
+- (NSArray *) replyToEnvelopeAddresses;
+- (NSArray *) toEnvelopeAddresses;
+- (NSArray *) ccEnvelopeAddresses;
 
 - (NSDictionary *) mailHeaders;
 
-- (id)bodyStructure;
-- (id)lookupInfoForBodyPart:(id)_path;
+- (id) bodyStructure;
+- (id) lookupInfoForBodyPart:(id)_path;
 
 /* content */
 
-- (NSData *)content;
-- (NSString *)contentAsString;
+- (NSData *) content;
+- (NSString *) contentAsString;
 
 /* bulk fetching of plain/text content */
 
-- (NSArray *)plainTextContentFetchKeys;
-- (NSDictionary *)fetchPlainTextParts:(NSArray *)_fetchKeys;
-- (NSDictionary *)fetchPlainTextParts;
-- (NSDictionary *)fetchPlainTextStrings:(NSArray *)_fetchKeys;
+- (NSArray *) plainTextContentFetchKeys;
+- (NSDictionary *) fetchPlainTextParts:(NSArray *)_fetchKeys;
+- (NSDictionary *) fetchPlainTextParts;
+- (NSDictionary *) fetchPlainTextStrings:(NSArray *)_fetchKeys;
 
 /* flags */
 
-- (NSException *)addFlags:(id)_f;
-- (NSException *)removeFlags:(id)_f;
+- (NSException *) addFlags:(id)_f;
+- (NSException *) removeFlags:(id)_f;
 
 /* deletion */
 
-- (BOOL)isDeletionAllowed;
+- (BOOL) isDeletionAllowed;
 - (NSException *) trashInContext:(id)_ctx;
 - (NSException *) copyToFolderNamed: (NSString *) folderName
                           inContext: (id)_ctx;
index 4c44bd993496ca60fd334fee144e377720b55cfd..c2a5bd0e40309c6b29ac4eb0643f9ef795dd3a83 100644 (file)
@@ -296,6 +296,11 @@ static BOOL debugSoParts       = NO;
   return [[self envelope] cc];
 }
 
+- (NSArray *) replyToEnvelopeAddresses
+{
+  return [[self envelope] replyTo];
+}
+
 - (NSData *) mailHeaderData
 {
   return [[self fetchCoreInfos] valueForKey: @"header"];
@@ -460,6 +465,7 @@ static BOOL debugSoParts       = NO;
     {
       if ([content isKindOfClass: [NSData class]])
        {
+#warning we ignore the charset here?
          s = [[NSString alloc] initWithData: content
                                encoding: NSISOLatin1StringEncoding];
          if (s)
@@ -747,6 +753,14 @@ static BOOL debugSoParts       = NO;
   NSString *mimeType;
 
   parts = [[self bodyStructure] objectForKey: @"parts"];
+
+  /* We don't have parts here but we're trying to download the message's
+     content that could be an image/jpeg, as an example */
+  if ([parts count] == 0)
+    {
+      return [SOGoMailBodyPart objectWithName: @"1" inContainer: self];
+    }
+
   part = [_key intValue] - 1;
   if (part > -1 && part < [parts count])
     {
@@ -924,13 +938,13 @@ static BOOL debugSoParts       = NO;
 - (NSException *) copyToFolderNamed: (NSString *) folderName
                           inContext: (id)_ctx
 {
-  SOGoMailAccounts *destFolder;
+  SOGoMailFolder *destFolder;
   NSEnumerator *folders;
   NSString *currentFolderName, *reason;
 
   // TODO: check for safe HTTP method
 
-  destFolder = [self mailAccountsFolder];
+  destFolder = (SOGoMailFolder *) [self mailAccountsFolder];
   folders = [[folderName componentsSeparatedByString: @"/"] objectEnumerator];
   currentFolderName = [folders nextObject];
   currentFolderName = [folders nextObject];
index 95f32ac960cffbb9fb1aaa09ba689cb5100f91ad..e81214d5eec6f8105c2be589883a79da08969eee 100644 (file)
@@ -606,4 +606,10 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
   return rolesForObject;
 }
 
+- (BOOL) isEqual: (id) otherUser
+{
+  return ([otherUser isKindOfClass: [SoUser class]]
+         && [login isEqualToString: [otherUser login]]);
+}
+
 @end /* SOGoUser */
index e51f518cf2b86177431bf0b6a15d3e2606b1ebdf..e695de0df90137b4b351a5f61777e9b30c24f7d5 100644 (file)
 {
   SOGoAppointmentFolder *personalFolder;
   SOGoAppointmentObject *eventObject;
+  NSString *cname;
+
+  eventObject = nil;
 
   personalFolder = [user personalCalendarFolderInContext: context];
-  eventObject = [personalFolder lookupName: uid
-                               inContext: context acquire: NO];
-  if (![eventObject isKindOfClass: [SOGoAppointmentObject class]])
+  cname = [personalFolder resourceNameForEventUID: uid];
+  if (cname)
+    {
+      eventObject = [personalFolder lookupName: cname
+                                   inContext: context acquire: NO];
+      if (![eventObject isKindOfClass: [SOGoAppointmentObject class]])
+       eventObject = nil;
+    }
+
+  if (!eventObject)
     eventObject = [SOGoAppointmentObject objectWithName: uid
                                         inContainer: personalFolder];
   
        chosenEvent = emailEvent;
       else
        {
-         calendarEvent = (iCalEvent *) [*eventObject component: NO secure: NO];
+         calendarEvent = (iCalEvent *) [*eventObject component: NO
+                                                     secure: NO];
          if ([calendarEvent compare: emailEvent] == NSOrderedAscending)
            chosenEvent = emailEvent;
          else
   if (emailEvent)
     {
       eventObject = [self _eventObjectWithUID: [emailEvent uid]];
+      [eventObject delete];
       response = [self responseWith204];
     }
   else
index 78a8fc8ed38a2237db19102cb542022417dcf8fd..3639789c273209f231728c0bdc6272a26d486683 100644 (file)
   if (![url hasSuffix: @"/"])
     url = [url stringByAppendingString: @"/"];
   
-  /* mail relative path to body-part */
-  
-  /* eg this was nil for a draft containing an HTML message */
+  /* if we get a message with an image/* or application/*
+     Content-Type, we must generate a 'fake' part since our
+     decoded mail won't have any. Also see SOGoMailBodyPart: -fetchBLOB
+     and SOGoMailObject: -lookupImap4BodyPartKey: inContext for
+     other workarounds */
+  if (!partPath || [partPath count] == 0)
+    partPath = [NSArray arrayWithObject: @"0"];
+
+  /* mail relative path to body-part
+     eg this was nil for a draft containing an HTML message */
   if ([(n = [partPath componentsJoinedByString:@"/"]) isNotNull])
     url = [url stringByAppendingString:n];
   
index ddf40447a18b09e8d76b1dfd408272c7912faca8..cdf98f67b9a96ba0890d04f935d186668da85796 100644 (file)
@@ -37,7 +37,7 @@
         }; */
         deleteFromCalendar = {
           protectedBy = "View";
-          actionClass = "UIxMailPartICalAction";
+          actionClass = "UIxMailPartICalActions";
           actionName  = "deleteFromCalendar";
        };
       };
index 986b267494e344c1798900cdb948165ee34e2127..56c88a9d64d77d4f0d1effd4f5d363b1ff1c2c65 100644 (file)
@@ -131,6 +131,11 @@ static NSString *mailETag = nil;
   return [[[self clientObject] ccEnvelopeAddresses] count] > 0 ? YES : NO;
 }
 
+- (BOOL) hasReplyTo
+{
+  return [[[self clientObject] replyToEnvelopeAddresses] count] > 0 ? YES : NO;
+}
+
 /* viewers */
 
 - (id) contentViewerComponent
index 04e4ee1af709b73d657c3c4d57a5ed31321ee875..3916100f496436cccfde39107513502f1d705720 100644 (file)
 #import <NGExtensions/NSObject+Logs.h>
 #import <NGExtensions/NSString+misc.h>
 
+#import <SoObjects/Appointments/iCalEntityObject+SOGo.h>
 #import <SoObjects/Appointments/iCalPerson+SOGo.h>
 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
 #import <SoObjects/Appointments/SOGoAppointmentFolders.h>
 #import <SoObjects/Appointments/SOGoAppointmentObject.h>
 #import <SoObjects/Appointments/SOGoTaskObject.h>
+#import <SoObjects/SOGo/iCalEntityObject+Utilities.h>
 #import <SoObjects/SOGo/LDAPUserManager.h>
 #import <SoObjects/SOGo/NSString+Utilities.h>
 #import <SoObjects/SOGo/SOGoUser.h>
   [component setLastModified: now];
 }
 
-- (NSString *) toolbar
+#warning the following methods probably share some code...
+- (NSString *) _toolbarForOwner: (SOGoUser *) ownerUser
 {
-  SOGoCalendarComponent *clientObject;
   NSString *toolbarFilename;
-  iCalPerson *participant;
   iCalPersonPartStat participationStatus;
-  SoSecurityManager *sm;
-  NSString *owner;
-
-  sm = [SoSecurityManager sharedSecurityManager];
-  clientObject = [self clientObject];
 
-  owner = [clientObject ownerInContext: context];
-  participant = [clientObject findParticipantWithUID: owner];
-
-  if (participant
-      && ![sm validatePermission: SOGoCalendarPerm_RespondToComponent
-             onObject: clientObject
-             inContext: context])
+  if ([[component attendees] count]
+      && [component userIsParticipant: ownerUser]
+      && ![component userIsOrganizer: ownerUser])
     {
-      participationStatus = [participant participationStatus];
+      participationStatus
+       = [[component findParticipant: ownerUser] participationStatus];
       /* Lightning does not manage participation status within tasks */
       if (participationStatus == iCalPersonPartStatAccepted)
        toolbarFilename = @"SOGoAppointmentObjectDecline.toolbar";
       else
        toolbarFilename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar";
     }
-  else if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent
-               onObject: clientObject
-               inContext: context])
+  else
     {
-      if ([[clientObject componentTag] isEqualToString: @"vevent"])
+      if ([component isKindOfClass: [iCalEvent class]])
        toolbarFilename = @"SOGoAppointmentObject.toolbar";
       else
        toolbarFilename = @"SOGoTaskObject.toolbar";
     }
+
+  return toolbarFilename;
+}
+
+- (NSString *) _toolbarForDelegate: (SOGoUser *) ownerUser
+{
+  SOGoCalendarComponent *clientObject;
+  SoSecurityManager *sm;
+  NSString *toolbarFilename, *adminToolbar;
+  iCalPersonPartStat participationStatus;
+
+  clientObject = [self clientObject];
+
+  if ([component isKindOfClass: [iCalEvent class]])
+    adminToolbar = @"SOGoAppointmentObject.toolbar";
   else
-    toolbarFilename = @"SOGoComponentClose.toolbar";
+    adminToolbar = @"SOGoTaskObject.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";
+         else
+           toolbarFilename = @"SOGoAppointmentObjectAcceptOrDecline.toolbar";
+       }
+      else
+       toolbarFilename = @"SOGoComponentClose.toolbar";
+    }
+  else
+    {
+      if (![sm validatePermission: SOGoCalendarPerm_ModifyComponent
+              onObject: clientObject
+              inContext: context])
+       toolbarFilename = adminToolbar;
+      else
+       toolbarFilename = @"SOGoComponentClose.toolbar";
+    }
+
+  return toolbarFilename;
+}
+
+- (NSString *) toolbar
+{
+  SOGoCalendarComponent *clientObject;
+  NSString *toolbarFilename;
+  SOGoUser *ownerUser;
+
+  clientObject = [self clientObject];
+  ownerUser = [SOGoUser userWithLogin: [clientObject ownerInContext: context]
+                       roles: nil];
+
+  if ([ownerUser isEqual: [context activeUser]])
+    toolbarFilename = [self _toolbarForOwner: ownerUser];
+  else
+    toolbarFilename = [self _toolbarForDelegate: ownerUser];
+
 
   return toolbarFilename;
 }
index 41c39fc99af5a0449fb41194d25af87c0e0fadef..96d520810c56f58d92ce713e48efe105aca18200 100644 (file)
 
 // TODO: add tentatively
 
-- (id) acceptOrDeclineAction: (BOOL) _accept
-{
-  [[self clientObject] changeParticipationStatus:
-                         _accept ? @"ACCEPTED" : @"DECLINED"];
+// - (id) acceptOrDeclineAction: (BOOL) _accept
+// {
+//   [[self clientObject] changeParticipationStatus:
+//                          _accept ? @"ACCEPTED" : @"DECLINED"];
 
-  return self;
-}
+//   return self;
+// }
 
-- (id) acceptAction
-{
-  return [self acceptOrDeclineAction: YES];
-}
+// - (id) acceptAction
+// {
+//   return [self acceptOrDeclineAction: YES];
+// }
 
-- (id) declineAction
-{
-  return [self acceptOrDeclineAction: NO];
-}
+// - (id) declineAction
+// {
+//   return [self acceptOrDeclineAction: NO];
+// }
 
 - (id) changeStatusAction
 {
index 6ee2a5afb701b41264c78a399c40ce4409cc8761..734735ff860975434f7ca21a307a6a3dc8855f83 100644 (file)
           };
        };
        methods = {
-//        delete = { 
-//           protectedBy = "Delete Objects";
-//           pageName    = "UIxTaskView"; 
-//           actionName  = "delete";
-//        };
           edit = { 
              protectedBy = "ViewAllComponent";
              pageName    = "UIxTaskEditor"; 
              pageName    = "UIxTaskEditor"; 
              actionName  = "changeStatus";
           };
-          accept = { 
-             protectedBy = "RespondToComponent";
-             pageName    = "UIxTaskEditor"; 
-             actionName  = "accept";
-          };
-          decline = { 
-             protectedBy = "RespondToComponent";
-             pageName    = "UIxTaskEditor"; 
-             actionName  = "decline";
-          };
        };
      };
   };
index de0d652674a2c9f55ad41810da6b695df7d926fb..a91268aea3e8d9c60ef016ffbab74dd351f08898 100644 (file)
           </td>
         </tr>
       </var:if>
+      <var:if condition="hasReplyTo">
+        <tr class="mailer_fieldrow">
+          <td class="mailer_fieldname"><var:string label:value="Reply-To"/>:</td>
+          <td class="mailer_fieldvalue">
+            <var:foreach list="clientObject.replyToEnvelopeAddresses" 
+              item="currentAddress">
+              <a var:href="currentAddressLink"
+               ><var:string value="currentAddress"
+                  formatter="context.mailEnvelopeFullAddressFormatter"
+                  /></a>
+            </var:foreach>
+          </td>
+        </tr>
+      </var:if>
     </table>
 
     <div class="mailer_mailcontent">