-2007-12-17 Francis Lachapelle <flachapelle@inverse.ca>
+2007-12-17 Wolfgang Sourdeau <wsourdeau@inverse.ca>
- * UI/MailerUI/UIxMailMainFrame.m ([UIxMailMainFrame
- -composeAction]): fixed URL when no recipient is specified.
+ * UI/MailerUI/UIxMailEditor.m ([UIxMailEditor -attachmentNames]):
+ initialize "attachmentNames" if it was NOT initialized... I think
+ this was a typo.
-2007-12-17 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ * SoObjects/Mailer/SOGoDraftObject.m ([SOGoDraftObject
+ -fetchMailForForwarding:sourceMail]): append the signature to
+ mails forwarded as attachment.
+
+ * UI/MailPartViewers/UIxMailPartViewer.m ([UIxMailPartViewer
+ -clientPart]): new method that returns the corresponding
+ SOGoMailPart instance.
+ ([UIxMailPartViewer -pathToAttachment]): simplified method, ensure
+ the filename is present. If the part has none, create one with the
+ "Untitled-" prefix. Add an extension if none is found.
+
+ * UI/MailPartViewers/UIxMailPartImageViewer.m
+ ([UIxMailPartImageViewer -pathToImage]): removed method since it
+ implementents exactly the same functionality as
+ [UIxMailPartViewer -pathToAttachment].
+
+ * SoObjects/Mailer/SOGoMailBodyPart.m ([SOGoMailBodyPart
+ -filename]): new method that returns the filename to the current
+ part by taking the different mime implementations into account.
+ [SOGoMailBodyPart -isBodyPartKey:inContext:]: commented out method
+ because we need to handle the part recognition in a different way,
+ by using the prefix "part" for example. One day we will do that.
* UI/Scheduler/NSArray+Scheduler.m ([NSMutableArray
-reverseArray]): new category method.
+2007-12-17 Francis Lachapelle <flachapelle@inverse.ca>
+
+ * UI/MailerUI/UIxMailMainFrame.m ([UIxMailMainFrame
+ -composeAction]): fixed URL when no recipient is specified.
+
2007-12-14 Ludovic Marcotte <ludovic@inverse.ca>
* SoObjects/Appointments/SOGoCalendarComponent.m
-Index: sope-gdl1/PostgreSQL/PostgreSQL72Channel.m
-===================================================================
---- sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (révision 1557)
-+++ sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (copie de travail)
-@@ -713,6 +713,39 @@
- return ms;
- }
-
-+/* GCSEOAdaptorChannel protocol */
-+static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" \
-+ @" c_name VARCHAR (256) NOT NULL,\n"
-+ @" c_content VARCHAR (100000) NOT NULL,\n"
-+ @" c_creationdate INT4 NOT NULL,\n"
-+ @" c_lastmodified INT4 NOT NULL,\n"
-+ @" c_version INT4 NOT NULL,\n"
-+ @" c_deleted INT4 NULL\n"
-+ @")");
-+static NSString *sqlFolderACLFormat = (@"CREATE TABLE %@ (\n" \
-+ @" c_uid VARCHAR (256) NOT NULL,\n"
-+ @" c_object VARCHAR (256) NOT NULL,\n"
-+ @" c_role VARCHAR (80) NOT NULL\n"
-+ @")");
-+
-+- (NSException *) createGCSFolderTableWithName: (NSString *) tableName
-+{
-+ NSString *sql;
-+
-+ sql = [NSString stringWithFormat: sqlFolderFormat, tableName];
-+
-+ return [self evaluateExpressionX: sql];
-+}
-+
-+- (NSException *) createGCSFolderACLTableWithName: (NSString *) tableName
-+{
-+ NSString *sql;
-+
-+ sql = [NSString stringWithFormat: sqlFolderACLFormat, tableName];
-+
-+ return [self evaluateExpressionX: sql];
-+}
-+
- @end /* PostgreSQL72Channel */
-
- @implementation PostgreSQL72Channel(PrimaryKeyGeneration)
Index: sope-mime/NGImap4/NGImap4Connection.m
===================================================================
--- sope-mime/NGImap4/NGImap4Connection.m (révision 1557)
return address;
}
-@@ -1627,6 +1650,29 @@
+@@ -1620,13 +1643,35 @@
+ return str;
+ }
+
+-
+ static NSString *_parseBodyString(NGImap4ResponseParser *self,
+ BOOL _convertString)
+ {
return _parseBodyDecodeString(self, _convertString, NO /* no decode */);
}
static NSDictionary *_parseBodyParameterList(NGImap4ResponseParser *self)
{
NSMutableDictionary *list;
-@@ -1734,10 +1780,11 @@
+@@ -1646,7 +1691,7 @@
+ _consumeIfMatch(self, ' ');
+ value = _parseBodyDecodeString(self, YES, YES);
+
+- [list setObject:value forKey:[key lowercaseString]];
++ if (value) [list setObject:value forKey:[key lowercaseString]];
+ }
+ _consumeIfMatch(self, ')');
+ }
+@@ -1734,10 +1779,11 @@
*encoding, *bodysize;
NSDictionary *parameterList;
NSMutableDictionary *dict;
_consumeIfMatch(self, ' ');
parameterList = _parseBodyParameterList(self);
_consumeIfMatch(self, ' ');
-@@ -1762,7 +1809,8 @@
+@@ -1762,7 +1808,8 @@
_consumeIfMatch(self, ' ');
[dict setObject:_parseBodyString(self, YES) forKey:@"lines"];
}
if (_la(self, 0) != ')') {
_consumeIfMatch(self, ' ');
_consumeIfMatch(self, '(');
-@@ -1805,14 +1853,9 @@
+@@ -1805,14 +1852,9 @@
forKey: @"disposition"];
if (_la(self, 0) != ')') {
_consume(self,1);
if (_la(self, 0) != ')') {
_consume(self,1);
[dict setObject: _parseBodyString(self, YES)
-@@ -1829,6 +1872,7 @@
+@@ -1829,6 +1871,7 @@
static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self,
BOOL isBodyStructure) {
NSMutableArray *parts;
NSString *kind;
NSMutableDictionary *dict;
-@@ -1854,14 +1898,9 @@
+@@ -1854,14 +1897,9 @@
forKey: @"disposition"];
if (_la(self, 0) != ')') {
_consume(self,1);
if (_la(self, 0) != ')') {
_consume(self,1);
[dict setObject: _parseBodyString(self, YES)
-@@ -2170,6 +2209,21 @@
+@@ -2170,6 +2208,21 @@
}
}
}
return data;
}
+Index: sope-gdl1/PostgreSQL/PostgreSQL72Channel.m
+===================================================================
+--- sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (révision 1557)
++++ sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (copie de travail)
+@@ -713,6 +713,39 @@
+ return ms;
+ }
+
++/* GCSEOAdaptorChannel protocol */
++static NSString *sqlFolderFormat = (@"CREATE TABLE %@ (\n" \
++ @" c_name VARCHAR (256) NOT NULL,\n"
++ @" c_content VARCHAR (100000) NOT NULL,\n"
++ @" c_creationdate INT4 NOT NULL,\n"
++ @" c_lastmodified INT4 NOT NULL,\n"
++ @" c_version INT4 NOT NULL,\n"
++ @" c_deleted INT4 NULL\n"
++ @")");
++static NSString *sqlFolderACLFormat = (@"CREATE TABLE %@ (\n" \
++ @" c_uid VARCHAR (256) NOT NULL,\n"
++ @" c_object VARCHAR (256) NOT NULL,\n"
++ @" c_role VARCHAR (80) NOT NULL\n"
++ @")");
++
++- (NSException *) createGCSFolderTableWithName: (NSString *) tableName
++{
++ NSString *sql;
++
++ sql = [NSString stringWithFormat: sqlFolderFormat, tableName];
++
++ return [self evaluateExpressionX: sql];
++}
++
++- (NSException *) createGCSFolderACLTableWithName: (NSString *) tableName
++{
++ NSString *sql;
++
++ sql = [NSString stringWithFormat: sqlFolderACLFormat, tableName];
++
++ return [self evaluateExpressionX: sql];
++}
++
+ @end /* PostgreSQL72Channel */
+
+ @implementation PostgreSQL72Channel(PrimaryKeyGeneration)
Index: sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m
===================================================================
--- sope-core/NGExtensions/FdExt.subproj/NSString+Encoding.m (révision 1557)
if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) {
if ((host = [rq headerForKey:@"host"]))
serverURL = [@"http://" stringByAppendingString:host];
+Index: sope-appserver/NGObjWeb/DAVPropMap.plist
+===================================================================
+--- sope-appserver/NGObjWeb/DAVPropMap.plist (révision 1557)
++++ sope-appserver/NGObjWeb/DAVPropMap.plist (copie de travail)
+@@ -123,11 +123,14 @@
+
+ /* CalDAV */
+ "{urn:ietf:params:xml:ns:caldav}calendar-home-set" = davCalendarHomeSet;
++ "{urn:ietf:params:xml:ns:caldav}calendar-user-address-set" = davCalendarUserAddressSet;
++ "{urn:ietf:params:xml:ns:caldav}schedule-inbox-URL" = davCalendarScheduleInboxURL;
++ "{urn:ietf:params:xml:ns:caldav}schedule-outbox-URL" = davCalendarScheduleOutboxURL;
+
+ /* Apple CalServer */
+- "{http://apple.com/ns/calendarserver/}dropbox-home-URL" =
++ "{http://calendarserver.org/ns/}dropbox-home-URL" =
+ davDropboxHomeURL;
+- "{http://apple.com/ns/calendarserver/}notifications-URL" =
++ "{http://calendarserver.org/ns/}notifications-URL" =
+ davNotificationsURL;
+ "{com.apple.ical:}calendarcolor" = davCalendarColor;
+ }
Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m
===================================================================
--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (révision 1557)
{
NSDictionary *info, *attachment;
SOGoUser *currentUser;
+ NSString *signature;
[sourceMail fetchCoreInfos];
{
// TODO: use subject for filename?
// error = [newDraft saveAttachment:content withName:@"forward.mail"];
+ signature = [currentUser signature];
+ if ([signature length])
+ [self setText: [NSString stringWithFormat: @"\n-- \n%@", signature]];
attachment = [NSDictionary dictionaryWithObjectsAndKeys:
[sourceMail filenameForForward], @"filename",
@"message/rfc822", @"mimetype",
[self saveAttachment: [sourceMail content]
withMetadata: attachment];
}
+
[self storeInfo];
}
- (NSString *) bodyPartName;
- (NSArray *) bodyPartPath;
- (NSString *) bodyPartIdentifier;
+- (NSString *) filename;
/* part info */
- (void) dealloc
{
- [self->partInfo release];
- [self->identifier release];
- [self->pathToPart release];
+ [partInfo release];
+ [identifier release];
+ [pathToPart release];
[super dealloc];
}
NSMutableArray *p;
id obj;
- if (self->pathToPart != nil)
- return [self->pathToPart isNotNull] ? self->pathToPart : nil;
+ if (pathToPart != nil)
+ return [pathToPart isNotNull] ? pathToPart : nil;
p = [[NSMutableArray alloc] initWithCapacity:8];
for (obj = self; [obj isKindOfClass:[SOGoMailBodyPart class]];
[p insertObject:[obj bodyPartName] atIndex:0];
}
- self->pathToPart = [p copy];
+ pathToPart = [p copy];
[p release];
- return self->pathToPart;
+ return pathToPart;
}
- (NSString *)bodyPartIdentifier {
- if (self->identifier != nil)
- return [self->identifier isNotNull] ? self->identifier : nil;
+ if (identifier != nil)
+ return [identifier isNotNull] ? identifier : nil;
- self->identifier =
+ identifier =
[[[self bodyPartPath] componentsJoinedByString:@"."] copy];
- return self->identifier;
+ return identifier;
}
- (NSURL *)imap4URL {
/* part info */
- (id)partInfo {
- if (self->partInfo != nil)
- return [self->partInfo isNotNull] ? self->partInfo : nil;
+ if (partInfo != nil)
+ return [partInfo isNotNull] ? partInfo : nil;
- self->partInfo =
+ partInfo =
[[[self mailObject] lookupInfoForBodyPart:[self bodyPartPath]] retain];
- return self->partInfo;
+ return partInfo;
}
/* name lookup */
return [clazz objectWithName: _key inContainer: self];
}
+- (NSString *) filename
+{
+ NSString *filename;
+ NSDictionary *parameters;
+
+ [self partInfo];
+
+ filename = [[partInfo objectForKey: @"parameterList"]
+ objectForKey: @"name"];
+ if (!filename)
+ {
+ parameters = [[partInfo objectForKey: @"disposition"]
+ objectForKey: @"parameterList"];
+ filename = [parameters objectForKey: @"filename"];
+ }
+
+ return filename;
+}
+
/* We overwrite the super's class method in order to make sure
we aren't dealing with our actual filename as the _key. That
could lead to problems if we weren't doing this as our filename
could start with a digit, leading to a wrong assumption in
the super class
*/
-- (BOOL)isBodyPartKey:(NSString *)_key inContext:(id)_ctx
-{
- NSString *s;
-
- s = [[[self partInfo] objectForKey: @"parameterList"] objectForKey: @"name"];
+// - (BOOL)isBodyPartKey:(NSString *)_key inContext:(id)_ctx
+// {
+// NSString *s;
- if (!s)
- s = [[[[self partInfo] objectForKey: @"disposition"] objectForKey: @"parameterList"] objectForKey: @"filename"];
-
- if (s && [s isEqualToString: _key]) return NO;
+// s = [self filename];
+// if (s && [s isEqualToString: _key]) return NO;
- return [super isBodyPartKey: _key inContext: _ctx];
-}
+// return [super isBodyPartKey: _key inContext: _ctx];
+// }
- (id) lookupName: (NSString *) _key
inContext: (id) _ctx
[r setHeader: [NSString stringWithFormat:@"%d", [data length]]
forKey: @"content-length"];
- if (asAttachment) {
- fileName = [[[self partInfo] objectForKey: @"parameterList"] objectForKey: @"name"];
- if (!fileName)
- fileName = [[[[self partInfo] objectForKey: @"disposition"]
- objectForKey: @"parameterList"]
- objectForKey: @"filename"];
- if ([fileName length])
- [r setHeader: [NSString stringWithFormat: @"attachment; filename=%@", fileName]
- forKey: @"content-disposition"];
- }
+ if (asAttachment)
+ {
+ fileName = [self filename];
+ if ([fileName length])
+ [r setHeader: [NSString stringWithFormat: @"attachment; filename=%@", fileName]
+ forKey: @"content-disposition"];
+ }
if ((etag = [self davEntityTag]) != nil)
[r setHeader:etag forKey:@"etag"];
02111-1307, USA.
*/
+#import <SoObjects/Mailer/SOGoMailBodyPart.h>
+
#import "UIxMailPartViewer.h"
@interface UIxMailPartImageViewer : UIxMailPartViewer
@implementation UIxMailPartImageViewer
-/* URLs */
-
-- (NSString *) pathToImage
-{
- NSMutableString *url;
- NSString *s;
-
- s = [[self clientObject] baseURLInContext: [self context]];
- url = [NSMutableString stringWithString: s];
- if (![url hasSuffix: @"/"])
- [url appendString: @"/"];
-
- s = [[self partPath] componentsJoinedByString:@"/"];
- [url appendString: s];
-
- s = [self preferredPathExtension];
- if (s)
- [url appendFormat: @".%@", s];
-
- return url;
-}
-
@end /* UIxMailPartImageViewer */
content.
*/
-@class NSData, NSArray, NSFormatter;
+@class NSArray;
+@class NSData;
+@class NSFormatter;
+
+@class SOGoMailBodyPart;
@interface UIxMailPartViewer : UIxComponent
{
- (void)setBodyInfo:(id)_info;
- (id)bodyInfo;
+- (SOGoMailBodyPart *) clientPart;
+
- (NSData *)flatContent;
- (NSData *)decodedFlatContent;
- (NSString *)flatContentAsString;
return [self flatContent];
}
-- (NSData *) content
+- (SOGoMailBodyPart *) clientPart
{
- NSData *content;
- NSEnumerator *parts;
id currentObject;
NSString *currentPart;
-
- content = nil;
+ NSEnumerator *parts;
currentObject = [self clientObject];
parts = [partPath objectEnumerator];
- currentPart = [parts nextObject];
- while (currentPart)
- {
- currentObject = [currentObject lookupName: currentPart
- inContext: context
- acquire: NO];
- currentPart = [parts nextObject];
- }
+ while ((currentPart = [parts nextObject]))
+ currentObject = [currentObject lookupName: currentPart
+ inContext: context
+ acquire: NO];
- content = [currentObject fetchBLOB];
+ return currentObject;
+}
- return content;
+- (NSData *) content
+{
+ return [[self clientPart] fetchBLOB];
}
- (NSString *) flatContentAsString
return url;
}
-- (NSString *) pathToAttachment
+- (NSString *) _filenameForAttachment: (SOGoMailBodyPart *) bodyPart
{
- /* this generates a more beautiful 'download' URL for a part */
- NSString *fn;
- NSMutableString *url;
+ NSMutableString *filename;
+ NSString *extension;
+
+ filename = [NSMutableString stringWithString: [bodyPart filename]];
+ if (![filename length])
+ [filename appendFormat: @"%@-%@",
+ [self labelForKey: @"Untitled"],
+ [bodyPart nameInContainer]];
- fn = [self filename];
- if ([fn length] > 0)
+ if (![[filename pathExtension] length])
{
- /* get basic URL */
- url = [NSMutableString stringWithString: [self pathToAttachmentObject]];
-
- /*
- If we have an attachment name, we attach it, this is properly handled by
- SOGoMailBodyPart.
- */
-
- if (![url hasSuffix: @"/"])
- [url appendString: @"/"];
- if (isdigit([url characterAtIndex: 0]))
- [url appendString: @"fn-"];
- [url appendString: [fn stringByEscapingURL]];
- // TODO: should we check for a proper extension?
+ extension = [self preferredPathExtension];
+ if (extension)
+ [filename appendFormat: @".%@", extension];
}
- else
- url = nil;
+
+ return [filename stringByEscapingURL];
+}
+
+- (NSString *) pathToAttachment
+{
+ NSMutableString *url;
+ NSString *s;
+ SOGoMailBodyPart *bodyPart;
+
+ bodyPart = [self clientPart];
+ s = [bodyPart baseURLInContext: [self context]];
+ url = [NSMutableString stringWithString: s];
+ if (![url hasSuffix: @"/"])
+ [url appendString: @"/"];
+
+// s = [[self partPath] componentsJoinedByString: @"/"];
+ [url appendString: [self _filenameForAttachment: bodyPart]];
return url;
}
{
NSArray *a;
- if (attachmentNames)
+ if (!attachmentNames)
{
a = [[self clientObject] fetchAttachmentNames];
- a = [a sortedArrayUsingSelector: @selector (compare:)];
- attachmentNames = [a copy];
+ ASSIGN (attachmentNames,
+ [a sortedArrayUsingSelector: @selector (compare:)]);
}
return attachmentNames;
{
[newInfo setObject: [currentFolder nameInContainer]
forKey: @"c_folder"];
+ [newInfo setObject: [currentFolder ownerInContext: context]
+ forKey: @"c_owner"];
if (![[newInfo objectForKey: @"c_title"] length])
[self _fixComponentTitle: newInfo withType: component];
[infos addObject: [newInfo objectsForKeys: fields
newEvents = [NSMutableArray array];
fields = [NSArray arrayWithObjects: @"c_name", @"c_folder", @"c_status",
@"c_title", @"c_startdate", @"c_enddate", @"c_location",
- @"c_isallday", @"c_classification", @"c_partmails", @"c_partstates", nil];
+ @"c_isallday", @"c_classification", @"c_partmails", @"c_partstates",
+ @"c_owner", nil];
events = [[self _fetchFields: fields
forComponentOfType: @"vevent"] objectEnumerator];
- user = [[self context] activeUser];
oldEvent = [events nextObject];
while (oldEvent)
{
participants = [[oldEvent objectAtIndex: 9] componentsSeparatedByString: @"\n"];
states = [[oldEvent objectAtIndex: 10] componentsSeparatedByString: @"\n"];
for (i = 0; i < [participants count]; i++) {
+ user = [SOGoUser userWithLogin: [oldEvent objectAtIndex: 11] roles: nil];
participant = [participants objectAtIndex: i];
if ([user hasEmail: participant]) {
switch ([[states objectAtIndex: i] intValue]) {
if (participants == nil || i == [participants count])
[newEvent replaceObjectAtIndex: 9 withObject: @""];
[newEvent removeObjectAtIndex: 10];
+ [newEvent removeObjectAtIndex: 11];
[newEvents addObject: newEvent];
xmlns="http://www.w3.org/1999/xhtml"
xmlns:var="http://www.skyrix.com/od/binding"
- var:src="pathToImage"
+ var:src="pathToAttachment"
var:title="filenameForDisplay"
class="mailer_imagecontent"
></img>
</div>
<form const:href="send" name="pageform" enctype="multipart/form-data">
- <div id="attachmentsArea">
- <var:string label:value="Attachments:" />
- <ul id="attachments">
- <var:foreach list="attachmentNames" item="attachmentName"
- ><li><img rsrc:src="attachment.gif"
- /><var:string value="attachmentName"
- /></li></var:foreach>
- </ul>
- </div>
+ <div id="attachmentsArea">
+ <var:string label:value="Attachments:" />
+ <ul id="attachments">
+ <var:foreach list="attachmentNames" item="attachmentName"
+ ><li><img rsrc:src="attachment.gif"
+ /><var:string value="attachmentName"
+ /></li></var:foreach>
+ </ul>
+ </div>
<div id="headerArea">
<span class="headerField"><var:string label:value="From" />:</span>
<var:popup const:name="from"
><span class="headerField"><var:string label:value="Subject"
/>:</span
>
- <input name="subject"
- type="text"
- class="textField"
- var:value="subject"
- /></div>
- <!-- separator line --><hr/>
+ <input name="subject"
+ type="text"
+ class="textField"
+ var:value="subject"
+ /></div>
+ <!-- separator line --><hr/>
</div>
<textarea id="text" name="text" rows="30" var:value="text"/>
<!-- img rsrc:src="tbird_073_compose.png" alt="screenshot" / -->