+2007-10-22 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * UI/MailerUI/UIxMailActions.m ([UIxMailActions
+ -removeAllLabelsAction]): new method that removes all label flags
+ from the associated message.
+ ([UIxMailActions -addLabel1Action]
+ [UIxMailActions -addLabel2Action]
+ [UIxMailActions -addLabel3Action
+ [UIxMailActions -addLabel4Action]
+ [UIxMailActions -addLabel5Action]): new methods that adds label
+ flags to the associated message.
+ ([UIxMailActions -removeLabel1Action]
+ [UIxMailActions -removeLabel2Action]
+ [UIxMailActions -removeLabel3Action
+ [UIxMailActions -removeLabel4Action]
+ [UIxMailActions -removeLabel5Action]): new methods that removes
+ label flags from the associated message.
+
+ * UI/MailerUI/UIxMailListView.m ([UIxMailListView -msgLabels]):
+ new accessor that returns the labels associated with the message.
+
+ * SoObjects/Mailer/SOGoMailFolder.m ([SOGoMailFolder
+ -allFolderPaths]): new method that returns all the paths of all
+ the subfolders of the folder object.
+ ([SOGoMailFolder -allFolderURLs]): new method replacing
+ subfoldersURL.
+
+ * SoObjects/SOGo/SOGoContentObject.m ([SOGoContentObject
+ -aclsForUser:uid]): object inherits the 'SOGoRole_ObjectEditor'
+ role from its parent folder.
+
2007-10-19 Francis Lachapelle <flachapelle@inverse.ca>
* SoObjects/SOGo/SOGoUser.m
if (getuid() > 0)
{
#if LIB_FOUNDATION_LIBRARY
- [NSProcessInfo initializeWithArguments: argv count: argc environment: env];
+ [NSProcessInfo initializeWithArguments: argv
+ count: argc environment: env];
#endif
ud = [NSUserDefaults standardUserDefaults];
rc = 0;
--------------
- the user can now configure his folders as drafts, trash or sent folder;
- added the ability the move and copy message across mail folders;
-
-0.9.0-200709XX
---------------
+- added the ability to label messages;
- implemented cookie-based identification in the web interface;
- fixed a bug where a false positive happening whenever a wrong user login was
given during an indirect bind;
--- /dev/null
+Index: sope-gdl1/PostgreSQL/PostgreSQL72Channel.h
+===================================================================
+--- sope-gdl1/PostgreSQL/PostgreSQL72Channel.h (révision 1545)
++++ sope-gdl1/PostgreSQL/PostgreSQL72Channel.h (copie de travail)
+@@ -28,6 +28,7 @@
+ #define ___PostgreSQL72_Channel_H___
+
+ #include <GDLAccess/EOAdaptorChannel.h>
++#include <GDLContentStore/EOAdaptorChannel+GCS.h>
+ #include <libpq-fe.h>
+
+ @class NSArray, NSString, NSMutableDictionary;
+@@ -40,7 +41,7 @@
+ int modification;
+ } PostgreSQL72FieldInfo;
+
+-@interface PostgreSQL72Channel : EOAdaptorChannel
++@interface PostgreSQL72Channel : EOAdaptorChannel <GCSEOAdaptorChannel>
+ {
+ // connection is valid after an openChannel call
+ PGConnection *connection;
+Index: sope-gdl1/PostgreSQL/PostgreSQL72Channel.m
+===================================================================
+--- sope-gdl1/PostgreSQL/PostgreSQL72Channel.m (révision 1545)
++++ 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-gdl1/Oracle8/ChangeLog
+===================================================================
+--- sope-gdl1/Oracle8/ChangeLog (révision 1545)
++++ sope-gdl1/Oracle8/ChangeLog (copie de travail)
+@@ -1,3 +1,10 @@
++2007-10-19 Ludovic Marcotte <ludovic@inverse.ca>
++
++ * We call OCITerminate() in OracleAdaptorChannel:
++ -closeChannel:
++ * Some formatting cleanups.
++
++
+ 2007-10-05 Ludovic Marcotte <ludovic@inverse.ca>
+
+ * Fixed otest wrt bundle name.
+Index: sope-gdl1/Oracle8/OracleAdaptorChannel.m
+===================================================================
+--- sope-gdl1/Oracle8/OracleAdaptorChannel.m (révision 1545)
++++ sope-gdl1/Oracle8/OracleAdaptorChannel.m (copie de travail)
+@@ -112,7 +112,7 @@
+ // Oracle's doc says: "If you call OCIStmtFetch2() with the nrows parameter set to 0, this cancels the cursor."
+ if (OCIStmtFetch2(_current_stm, _oci_err, (ub4)0, (ub4)OCI_FETCH_NEXT, (sb4)0, (ub4)OCI_DEFAULT))
+ {
+- NSLog( @"Fetch cancellation failed");
++ NSLog(@"Fetch cancellation failed");
+ }
+
+ [self _cleanup];
+@@ -131,8 +131,13 @@
+ // We logoff from the database.
+ if (OCILogoff(_oci_ctx, _oci_err))
+ {
+- NSLog( @"FAILED: OCILogoff()");
++ NSLog(@"FAILED: OCILogoff()");
+ }
++
++ if (OCITerminate(OCI_DEFAULT))
++ {
++ NSLog(@"FAILED: OCITerminate()");
++ }
+ }
+ }
+
+@@ -369,19 +374,19 @@
+ (dvoid * (*)(dvoid *, dvoid *, size_t))0,
+ (void (*)(dvoid *, dvoid *)) 0 ))
+ {
+- NSLog( @"FAILED: OCIInitialize()");
++ NSLog(@"FAILED: OCIInitialize()");
+ return NO;
+ }
+
+ if (OCIEnvInit((OCIEnv **)&_oci_env, (ub4)OCI_DEFAULT, (size_t)0, (dvoid **)0))
+ {
+- NSLog( @"FAILED: OCIEnvInit()");
++ NSLog(@"FAILED: OCIEnvInit()");
+ return NO;
+ }
+
+ if (OCIHandleAlloc((dvoid *)_oci_env, (dvoid *)&_oci_err, (ub4)OCI_HTYPE_ERROR, (size_t)0, (dvoid **)0))
+ {
+- NSLog( @"FAILED: OCIHandleAlloc() on errhp");
++ NSLog(@"FAILED: OCIHandleAlloc() on errhp");
+ return NO;
+ }
+
+@@ -398,7 +403,7 @@
+ if (OCILogon(_oci_env, _oci_err, &_oci_ctx, (const OraText*)username, strlen(username),
+ (const OraText*)password, strlen(password), (const OraText*)database, strlen(database)))
+ {
+- NSLog( @"FAILED: OCILogon(). username = %s password = %s"
++ NSLog(@"FAILED: OCILogon(). username = %s password = %s"
+ @" database = %s", username, password, database);
+ return NO;
+ }
+Index: sope-mime/NGImap4/NGImap4Connection.m
+===================================================================
+--- sope-mime/NGImap4/NGImap4Connection.m (révision 1545)
++++ sope-mime/NGImap4/NGImap4Connection.m (copie de travail)
+@@ -381,7 +381,7 @@
+
+ if (debugCache) [self logWithFormat:@" no folders cached yet .."];
+
+- result = [[self client] list:(onlyFetchInbox ? @"INBOX" : @"*")
++ result = [[self client] list:(onlyFetchInbox ? @"INBOX" : @"")
+ pattern:@"*"];
+ if (![[result valueForKey:@"result"] boolValue]) {
+ [self errorWithFormat:@"Could not list mailbox hierarchy!"];
+Index: sope-mime/NGImap4/NGImap4ResponseParser.m
+===================================================================
+--- sope-mime/NGImap4/NGImap4ResponseParser.m (révision 1545)
++++ sope-mime/NGImap4/NGImap4ResponseParser.m (copie de travail)
+@@ -84,6 +84,8 @@
+ static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self,
+ BOOL isBodyStructure);
+
++static NSArray *_parseLanguages();
++
+ static NSString *_parseBodyString(NGImap4ResponseParser *self,
+ BOOL _convertString);
+ static NSString *_parseBodyDecodeString(NGImap4ResponseParser *self,
+@@ -1627,6 +1629,29 @@
+ return _parseBodyDecodeString(self, _convertString, NO /* no decode */);
+ }
+
++static NSArray *_parseLanguages(NGImap4ResponseParser *self) {
++ NSMutableArray *languages;
++ NSString *language;
++
++ languages = [NSMutableArray array];
++ if (_la(self, 0) == '(') {
++ while (_la(self, 0) != ')') {
++ _consume(self,1);
++ language = _parseBodyString(self, YES);
++ if ([language length])
++ [languages addObject: language];
++ }
++ _consume(self,1);
++ }
++ else {
++ language = _parseBodyString(self, YES);
++ if ([language length])
++ [languages addObject: language];
++ }
++
++ return languages;
++}
++
+ static NSDictionary *_parseBodyParameterList(NGImap4ResponseParser *self)
+ {
+ NSMutableDictionary *list;
+@@ -1734,10 +1759,11 @@
+ *encoding, *bodysize;
+ NSDictionary *parameterList;
+ NSMutableDictionary *dict;
++ NSArray *languages;
+
+ type = [_parseBodyString(self, YES) lowercaseString];
+ _consumeIfMatch(self, ' ');
+- subtype = _parseBodyString(self, YES);
++ subtype = [_parseBodyString(self, YES) lowercaseString];
+ _consumeIfMatch(self, ' ');
+ parameterList = _parseBodyParameterList(self);
+ _consumeIfMatch(self, ' ');
+@@ -1762,7 +1788,8 @@
+ _consumeIfMatch(self, ' ');
+ [dict setObject:_parseBodyString(self, YES) forKey:@"lines"];
+ }
+- else if ([type isEqualToString:@"message"]) {
++ else if ([type isEqualToString:@"message"]
++ && [subtype isEqualToString:@"rfc822"]) {
+ if (_la(self, 0) != ')') {
+ _consumeIfMatch(self, ' ');
+ _consumeIfMatch(self, '(');
+@@ -1805,14 +1832,9 @@
+ forKey: @"disposition"];
+ if (_la(self, 0) != ')') {
+ _consume(self,1);
+- if (_la(self, 0) == '(') {
+- [dict setObject: _parseBodyParameterList(self)
+- forKey: @"language"];
+- }
+- else {
+- [dict setObject: _parseBodyString(self, YES)
+- forKey: @"language"];
+- }
++ languages = _parseLanguages(self);
++ if ([languages count])
++ [dict setObject: languages forKey: @"languages"];
+ if (_la(self, 0) != ')') {
+ _consume(self,1);
+ [dict setObject: _parseBodyString(self, YES)
+@@ -1829,6 +1851,7 @@
+ static NSDictionary *_parseMultipartBody(NGImap4ResponseParser *self,
+ BOOL isBodyStructure) {
+ NSMutableArray *parts;
++ NSArray *languages;
+ NSString *kind;
+ NSMutableDictionary *dict;
+
+@@ -1854,14 +1877,9 @@
+ forKey: @"disposition"];
+ if (_la(self, 0) != ')') {
+ _consume(self,1);
+- if (_la(self, 0) == '(') {
+- [dict setObject: _parseBodyParameterList(self)
+- forKey: @"language"];
+- }
+- else {
+- [dict setObject: _parseBodyString(self, YES)
+- forKey: @"language"];
+- }
++ languages = _parseLanguages(self);
++ if ([languages count])
++ [dict setObject: languages forKey: @"languages"];
+ if (_la(self, 0) != ')') {
+ _consume(self,1);
+ [dict setObject: _parseBodyString(self, YES)
+Index: sope-mime/NGMime/NGMimeBodyPart.m
+===================================================================
+--- sope-mime/NGMime/NGMimeBodyPart.m (révision 1545)
++++ sope-mime/NGMime/NGMimeBodyPart.m (copie de travail)
+@@ -31,18 +31,6 @@
+ return 2;
+ }
+
+-static NGMimeType *defaultType = nil;
+-
+-+ (void)initialize {
+- static BOOL isInitialized = NO;
+- if (!isInitialized) {
+- isInitialized = YES;
+-
+- defaultType =
+- [[NGMimeType mimeType:@"text/plain; charset=us-ascii"] retain];
+- }
+-}
+-
+ + (id)bodyPartWithHeader:(NGHashMap *)_header {
+ return [[[self alloc] initWithHeader:_header] autorelease];
+ }
+@@ -156,13 +144,12 @@
+ if (!Fields)
+ Fields = (NGMimeHeaderNames *)[NGMimePartParser headerFieldNames];
+
+-
+ type = [self->header objectForKey:Fields->contentType];
+
+ if (![type isKindOfClass:[NGMimeType class]])
+ type = [NGMimeType mimeType:[type stringValue]];
+
+- return (type != nil ? type : (id)defaultType);
++ return type;
+ }
+
+ - (NSString *)contentId {
+Index: sope-mime/NGMime/NGMimeBodyParser.m
+===================================================================
+--- sope-mime/NGMime/NGMimeBodyParser.m (révision 1545)
++++ sope-mime/NGMime/NGMimeBodyParser.m (copie de travail)
+@@ -67,7 +67,10 @@
+ if (_data == nil) return nil;
+
+ ctype = [_part contentType];
+-
++ if (!ctype
++ && [_d respondsToSelector: @selector(parser:contentTypeOfPart:)])
++ ctype = [_d parser: self contentTypeOfPart: _part];
++
+ if (![ctype isKindOfClass:[NGMimeType class]])
+ ctype = [NGMimeType mimeType:[ctype stringValue]];
+
+Index: sope-mime/NGMime/NGMimePartParser.h
+===================================================================
+--- sope-mime/NGMime/NGMimePartParser.h (révision 1545)
++++ sope-mime/NGMime/NGMimePartParser.h (copie de travail)
+@@ -117,6 +117,7 @@
+ BOOL parserParseRawBodyDataOfPart:1;
+ BOOL parserBodyParserForPart:1;
+ BOOL parserDecodeBodyOfPart:1;
++ BOOL parserContentTypeOfPart:1;
+ } delegateRespondsTo;
+
+
+@@ -275,6 +276,9 @@
+ - (id<NGMimeBodyParser>)parser:(NGMimePartParser *)_parser
+ bodyParserForPart:(id<NGMimePart>)_part;
+
++- (NGMimeType *)parser:(id)_parser
++ contentTypeOfPart:(id<NGMimePart>)_part;
++
+ @end /* NSObject(NGMimePartParserDelegate) */
+
+ @interface NSObject(NGMimePartParser)
+Index: sope-mime/NGMime/NGMimePartParser.m
+===================================================================
+--- sope-mime/NGMime/NGMimePartParser.m (révision 1545)
++++ sope-mime/NGMime/NGMimePartParser.m (copie de travail)
+@@ -1091,7 +1091,10 @@
+ id<NGMimeBodyParser> bodyParser = nil;
+
+ ctype = [_p contentType];
+-
++ if (!ctype
++ && self->delegateRespondsTo.parserContentTypeOfPart)
++ ctype = [self->delegate parser: self contentTypeOfPart: _p];
++
+ contentType = ([ctype isKindOfClass:[NGMimeType class]])
+ ? ctype
+ : [NGMimeType mimeType:[ctype stringValue]];
+Index: sope-appserver/NGObjWeb/GNUmakefile.postamble
+===================================================================
+--- sope-appserver/NGObjWeb/GNUmakefile.postamble (révision 1545)
++++ sope-appserver/NGObjWeb/GNUmakefile.postamble (copie de travail)
+@@ -23,14 +23,20 @@
+
+ # install makefiles
+
+-after-install ::
++after-install :: $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make
++
++ifneq ($(GNUSTEP_MAKE_VERSION),1.3.0)
++after-install :: $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make
++endif
++
++$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make: ngobjweb.make
+ $(MKDIRS) $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/
+ $(INSTALL_DATA) ngobjweb.make $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/Additional/ngobjweb.make
+
+-ifneq ($(GNUSTEP_MAKE_VERSION),1.3.0)
+-after-install ::
++$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make: woapp-gs.make
+ $(INSTALL_DATA) woapp-gs.make \
+ $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/woapp.make
++
++$(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make: wobundle-gs.make
+ $(INSTALL_DATA) wobundle-gs.make \
+ $(INSTALL_ROOT_DIR)/$(GNUSTEP_MAKEFILES)/wobundle.make
+-endif
+Index: sope-appserver/NGObjWeb/WOContext.m
+===================================================================
+--- sope-appserver/NGObjWeb/WOContext.m (révision 1545)
++++ sope-appserver/NGObjWeb/WOContext.m (copie de travail)
+@@ -64,11 +64,13 @@
+ static BOOL testNSURLs = NO;
+ static BOOL newCURLStyle = NO;
+ static NSString *WOApplicationSuffix = nil;
++static NSURL *redirectURL = nil;
+
+ + (void)initialize {
+ static BOOL didInit = NO;
+ NSUserDefaults *ud;
+ NSString *cn;
++ NSString *url;
+
+ if (didInit) return;
+
+@@ -91,6 +93,9 @@
+ debugCursor = [ud boolForKey:@"WODebugCursor"] ? 1 : 0;
+ debugComponentAwake = [ud boolForKey:@"WODebugComponentAwake"];
+ WOApplicationSuffix = [[ud stringForKey:@"WOApplicationSuffix"] copy];
++ url = [ud stringForKey:@"WOApplicationRedirectURL"];
++ if (url != nil)
++ redirectURL = [NSURL URLWithString: url];
+ }
+
+ + (id)contextWithRequest:(WORequest *)_r {
+@@ -503,6 +508,11 @@
+ return nil;
+ }
+
++ if (redirectURL) {
++ // Use URL from user defaults (WOApplicationRedirectURL)
++ return redirectURL;
++ }
++
+ if ((serverURL = [rq headerForKey:@"x-webobjects-server-url"]) == nil) {
+ if ((host = [rq headerForKey:@"host"]))
+ serverURL = [@"http://" stringByAppendingString:host];
+Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m
+===================================================================
+--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (révision 1545)
++++ sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.m (copie de travail)
+@@ -216,6 +216,12 @@
+ assocCount++;
+ }
+ }
++ if (count > 0) {
++ if ((self->isAbsolute = OWGetProperty(_config, @"absolute"))) {
++ count--;
++ assocCount++;
++ }
++ }
+
+ self->rest = _config;
+
+Index: sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m
+===================================================================
+--- sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m (révision 1545)
++++ sope-appserver/NGObjWeb/DynamicElements/_WOComplexHyperlink.m (copie de travail)
+@@ -40,6 +40,7 @@
+ WOAssociation *string;
+ WOAssociation *target;
+ WOAssociation *disabled;
++ WOAssociation *isAbsolute;
+ WOElement *template;
+
+ /* new in WO4: */
+@@ -359,6 +360,7 @@
+ {
+ if ((self = [super initWithName:_name hyperlinkInfo:_info template:_t])) {
+ self->href = _info->href;
++ self->isAbsolute = _info->isAbsolute;
+ }
+ return self;
+ }
+@@ -374,6 +376,9 @@
+ // TODO: we need a binding to disable rewriting!
+ NSRange r;
+
++ if ([[self->isAbsolute valueInContext:_ctx] boolValue] == YES)
++ return NO;
++
+ r = [_s rangeOfString:@":"];
+ if (r.length == 0)
+ return YES;
+Index: sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h
+===================================================================
+--- sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h (révision 1545)
++++ sope-appserver/NGObjWeb/DynamicElements/WOHyperlinkInfo.h (copie de travail)
+@@ -41,7 +41,8 @@
+ WOAssociation *pageName;
+ WOAssociation *actionClass;
+ WOAssociation *directActionName;
+-
++ WOAssociation *isAbsolute;
++
+ BOOL sidInUrl;
+
+ /* 'ivar' associations */
+Index: sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m
+===================================================================
+--- sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m (révision 1545)
++++ sope-appserver/NGObjWeb/WOHttpAdaptor/WOHttpTransaction.m (copie de travail)
+@@ -31,6 +31,7 @@
+ #include <NGObjWeb/WOCookie.h>
+ #include <NGExtensions/NSData+gzip.h>
+ #include <NGHttp/NGHttp.h>
++#include <NGMime/NGMimeType.h>
+ #include "common.h"
+
+ #include <string.h>
+@@ -1016,6 +1017,12 @@
+ - (void)parser:(NGMimePartParser *)_parser didParseHeader:(NGHashMap *)_header {
+ }
+
++- (NGMimeType *)parser:(id)_parser
++ contentTypeOfPart:(id<NGMimePart>)_part
++{
++ return [NGMimeType mimeType: @"text/plain; charset=utf-8"];
++}
++
+ @end /* WOHttpAdaptor */
+
+ @implementation WOCoreApplication(SimpleParserSelection)
rawFolders = [[self imap4Connection]
allFoldersForURL: [self imap4URL]];
+#warning FIXME: the folder names should be prefixed
mainFolders = [NSArray arrayWithObjects: inboxFolderName, draftsFolderName,
sentFolder, trashFolder, nil];
newFolders = [NSMutableArray arrayWithArray: rawFolders];
NSDictionary *mailboxACL;
}
+- (NSString *) absoluteImap4Name;
+
/* messages */
-- (NSArray *)fetchUIDsMatchingQualifier:(id)_q sortOrdering:(id)_so;
-- (NSArray *)fetchUIDs:(NSArray *)_uids parts:(NSArray *)_parts;
+- (NSArray *) fetchUIDsMatchingQualifier: (id)_q sortOrdering: (id) _so;
+- (NSArray *) fetchUIDs: (NSArray *) _uids parts: (NSArray *) _parts;
-- (NSException *)postData:(NSData *)_data flags:(id)_flags;
+- (NSException *) postData: (NSData *) _data flags: (id) _flags;
-- (NSException *)expunge;
+- (NSException *) expunge;
/* flags */
-- (NSException *)addFlagsToAllMessages:(id)_f;
+- (NSException *) addFlagsToAllMessages: (id) _f;
/* folder type */
-- (NSString *)outlookFolderClass;
+- (NSString *) outlookFolderClass;
- (NSArray *) subfolders;
-- (NSArray *) subfoldersURL;
+
+- (NSArray *) allFolderPaths;
+- (NSArray *) allFolderURLs;
@end
return [nameInContainer substringFromIndex: 6];
}
+- (NSString *) absoluteImap4Name
+{
+ NSString *name;
+
+ name = [[self imap4URL] path];
+ if (![name hasSuffix: @"/"])
+ name = [name stringByAppendingString: @"/"];
+
+ return name;
+}
- (NSMutableString *) imap4URLString
{
return [[self imap4Connection] subfoldersForURL: [self imap4URL]];
}
-- (NSArray *) subfoldersURL
+- (NSArray *) allFolderPaths
+{
+ NSMutableArray *deepSubfolders;
+ NSEnumerator *folderNames;
+ NSArray *result;
+ NSString *currentFolderName, *prefix;
+
+ deepSubfolders = [NSMutableArray new];
+ [deepSubfolders autorelease];
+
+ prefix = [self absoluteImap4Name];
+
+ result = [[self mailAccountFolder] allFolderPaths];
+ folderNames = [result objectEnumerator];
+ while ((currentFolderName = [folderNames nextObject]))
+ if ([currentFolderName hasPrefix: prefix])
+ [deepSubfolders addObject: currentFolderName];
+ [deepSubfolders sortUsingSelector: @selector (compare:)];
+
+ return deepSubfolders;
+}
+
+- (NSArray *) allFolderURLs
{
NSURL *selfURL, *currentURL;
NSMutableArray *subfoldersURL;
NSEnumerator *subfolders;
- NSString *selfPath, *currentFolder;
+ NSString *currentFolder;
subfoldersURL = [NSMutableArray array];
selfURL = [self imap4URL];
- selfPath = [selfURL path];
- subfolders = [[self subfolders] objectEnumerator];
+ subfolders = [[self allFolderPaths] objectEnumerator];
currentFolder = [subfolders nextObject];
while (currentFolder)
{
currentURL = [[NSURL alloc]
initWithScheme: [selfURL scheme]
host: [selfURL host]
- path: [selfPath stringByAppendingPathComponent:
- currentFolder]];
+ path: currentFolder];
[currentURL autorelease];
[subfoldersURL addObject: currentURL];
currentFolder = [subfolders nextObject];
}
if ([containerAcls containsObject: SOGoRole_ObjectReader])
[acls addObject: SOGoRole_ObjectViewer];
+ if ([containerAcls containsObject: SOGoRole_ObjectEditor])
+ [acls addObject: SOGoRole_ObjectEditor];
}
return acls;
- (void) sendFolderAdvisoryTemplate: (NSString *) template
{
- NSString *language, *pageName;
+ NSString *pageName;
SOGoUser *user;
SOGoFolderAdvisory *page;
- WOApplication *app;
- user = [SOGoUser userWithLogin: [[context activeUser] login] roles: nil];
- language = [user language];
+ user = [context activeUser];
pageName = [NSString stringWithFormat: @"SOGoFolder%@%@Advisory",
- language, template];
+ [user language], template];
- app = [WOApplication application];
- page = [app pageWithName: pageName inContext: context];
+ page = [[WOApplication application] pageWithName: pageName
+ inContext: context];
[page setFolderObject: self];
- [page setRecipientUID: [[context activeUser] login]];
+ [page setRecipientUID: [user login]];
[page send];
}
+
+// if (!result) [self sendFolderAdvisoryTemplate: @"Addition"];
+
- (BOOL) create
{
NSException *result;
withName: displayName
atPath: ocsPath];
- if (!result) [self sendFolderAdvisoryTemplate: @"Addition"];
-
return (result == nil);
}
else
error = [[self folderManager] deleteFolderAtPath: ocsPath];
- if (!error) [self sendFolderAdvisoryTemplate: @"Removal"];
-
return error;
}
+// if (!error) [self sendFolderAdvisoryTemplate: @"Removal"];
+
- (void) renameTo: (NSString *) newName
{
GCSChannelManager *cm;
= "Moving a message into its own folder is impossible!";
"Copying a message into its own folder is impossible!"
= "Copying a message into its own folder is impossible!";
+
+/* Message editing */
+"error_validationfailed" = "Validation failed";
+"error_missingsubject" = "Subject is missing";
+"error_missingrecipients" = "No recipients specified";
= "Le déplacement d'un message dans son propre dossier est impossible.";
"Copying a message into its own folder is impossible!"
= "La copie d'un message dans son propre dossier est impossible.";
+
+/* Message editing */
+"error_validationfailed" = "Message invalide";
+"error_missingsubject" = "- le sujet est manquant";
+"error_missingrecipients" = "- aucun destinataire spécifié";
= "Moving a message into its own folder is impossible!";
"Copying a message into its own folder is impossible!"
= "Copying a message into its own folder is impossible!";
+
+/* Message editing */
+"error_validationfailed" = "Validation failed";
+"error_missingsubject" = "Subject is missing";
+"error_missingrecipients" = "No recipients specified";
* Boston, MA 02111-1307, USA.
*/
+#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
#import <NGObjWeb/WOContext.h>
return response;
}
+- (WOResponse *) _addLabel: (unsigned int) number
+{
+ WOResponse *response;
+ SOGoMailObject *co;
+ NSException *error;
+ NSArray *flags;
+
+ co = [self clientObject];
+ flags = [NSArray arrayWithObject:
+ [NSString stringWithFormat: @"$Label%u", number]];
+ error = [co addFlags: flags];
+ if (error)
+ response = (WOResponse *) error;
+ else
+ response = [self responseWith204];
+
+ return response;
+}
+
+- (WOResponse *) _removeLabel: (unsigned int) number
+{
+ WOResponse *response;
+ SOGoMailObject *co;
+ NSException *error;
+ NSArray *flags;
+
+ co = [self clientObject];
+ flags = [NSArray arrayWithObject:
+ [NSString stringWithFormat: @"$Label%u", number]];
+ error = [co removeFlags: flags];
+ if (error)
+ response = (WOResponse *) error;
+ else
+ response = [self responseWith204];
+
+ return response;
+}
+
+- (WOResponse *) addLabel1Action
+{
+ return [self _addLabel: 1];
+}
+
+- (WOResponse *) addLabel2Action
+{
+ return [self _addLabel: 2];
+}
+
+- (WOResponse *) addLabel3Action
+{
+ return [self _addLabel: 3];
+}
+
+- (WOResponse *) addLabel4Action
+{
+ return [self _addLabel: 4];
+}
+
+- (WOResponse *) addLabel5Action
+{
+ return [self _addLabel: 5];
+}
+
+- (WOResponse *) removeLabel1Action
+{
+ return [self _removeLabel: 1];
+}
+
+- (WOResponse *) removeLabel2Action
+{
+ return [self _removeLabel: 2];
+}
+
+- (WOResponse *) removeLabel3Action
+{
+ return [self _removeLabel: 3];
+}
+
+- (WOResponse *) removeLabel4Action
+{
+ return [self _removeLabel: 4];
+}
+
+- (WOResponse *) removeLabel5Action
+{
+ return [self _removeLabel: 5];
+}
+
+- (WOResponse *) removeAllLabelsAction
+{
+ WOResponse *response;
+ SOGoMailObject *co;
+ NSException *error;
+ NSArray *flags;
+
+ co = [self clientObject];
+ flags = [NSArray arrayWithObjects: @"$Label1", @"$Label2", @"$Label3",
+ @"$Label4", @"$Label5", nil];
+ error = [co removeFlags: flags];
+ if (error)
+ response = (WOResponse *) error;
+ else
+ response = [self responseWith204];
+
+ return response;
+}
+
@end
{
[co flushMailCaches];
connection = [co imap4Connection];
- subfolders = [[co subfoldersURL] objectEnumerator];
+ subfolders = [[co allFolderURLs] objectEnumerator];
currentURL = [subfolders nextObject];
while (currentURL)
{
return [self redirectToLocation:@"view"];
}
+- (NSString *) msgLabels
+{
+ NSMutableArray *labels;
+ NSEnumerator *flags;
+ NSString *currentFlag;
+
+ labels = [NSMutableArray new];
+ [labels autorelease];
+
+ flags = [[message objectForKey: @"flags"] objectEnumerator];
+ while ((currentFlag = [flags nextObject]))
+ if ([currentFlag hasPrefix: @"$label"])
+ [labels addObject: [currentFlag substringFromIndex: 1]];
+
+ return [labels componentsJoinedByString: @" "];
+}
+
@end
/* UIxMailListView */
actionClass = "UIxMailActions";
actionName = "markMessageRead";
};
+ addLabel1 = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "addLabel1";
+ };
+ addLabel2 = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "addLabel2";
+ };
+ addLabel3 = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "addLabel3";
+ };
+ addLabel4 = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "addLabel4";
+ };
+ addLabel5 = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "addLabel5";
+ };
+ removeLabel1 = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "removeLabel1";
+ };
+ removeLabel2 = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "removeLabel2";
+ };
+ removeLabel3 = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "removeLabel3";
+ };
+ removeLabel4 = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "removeLabel4";
+ };
+ removeLabel5 = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "removeLabel5";
+ };
+ removeAllLabels = {
+ protectedBy = "View";
+ actionClass = "UIxMailActions";
+ actionName = "removeAllLabels";
+ };
};
};
protectedBy = "Access Object";
defaultRoles = {
"Access Object" = ( "Owner", "ObjectViewer" );
+ "Access Contents Information" = ( "Owner", "ObjectViewer" );
+ "Change Images And Files" = ( "Owner", "ObjectEditor" );
};
};
SOGoFolder = {
xmlns:label="OGo:label"
>
<div class="menu" id="searchMenu">
- <ul id="searchOptions">
+ <ul id="searchOptions" class="choiceMenu">
<li id="name_or_address"><var:string
label:value="Name or Email"/></li>
</ul>
xmlns:label="OGo:label"
>
<div class="menu" id="searchMenu">
- <ul id="searchOptions">
+ <ul id="searchOptions" class="choiceMenu">
<li id="subject"><var:string label:value="Subject"/></li>
<li id="sender"><var:string label:value="Sender"/></li>
<li id="subject_or_sender"><var:string
</thead>
<tbody>
<var:foreach list="messages" item="message"
- ><tr var:class="messageRowStyleClass" var:id="msgRowID"
+ ><tr var:class="messageRowStyleClass" var:id="msgRowID" var:labels="msgLabels"
><td class="messageFlagColumn"><var:entity name="nbsp"/></td
><td class="messageFlagColumn"
><var:if condition="hasMessageAttachment"
><img rsrc:src="title_attachment_14x14.png"
/></var:if
></td
-
><td
var:class="messageSubjectCellStyleClass"
var:id="msgDivID"
</div>
<div class="menu" id="label-menu">
- <ul id="">
+ <ul id="" class="choiceMenu">
<li><var:string label:value="None" /></li>
<li><!-- separator --></li>
- <li><var:string label:value="Important" /></li>
- <li><var:string label:value="Work" /></li>
- <li><var:string label:value="Personal" /></li>
- <li><var:string label:value="To Do" /></li>
- <li><var:string label:value="Later" /></li>
+ <li class="label1"><var:string label:value="Important" /></li>
+ <li class="label2"><var:string label:value="Work" /></li>
+ <li class="label3"><var:string label:value="Personal" /></li>
+ <li class="label4"><var:string label:value="To Do" /></li>
+ <li class="label5"><var:string label:value="Later" /></li>
</ul>
</div>
xmlns:label="OGo:label"
>
<div class="menu" id="searchMenu">
- <ul id="searchOptions">
+ <ul id="searchOptions" class="choiceMenu">
<li><var:string label:value="Title or Description"/></li>
</ul>
</div>
</script>
<div class="menu" id="privacy-menu">
- <ul id="itemPrivacyList">
+ <ul id="itemPrivacyList" class="choiceMenu">
<var:foreach list="privacyClasses" item="item">
<li var:classification="item"><var:string
var:value="itemPrivacyText" /></li>
bottom: 0px;
overflow: auto; }
+#label-menu LI.label1
+{ color: #f00; }
+
+#label-menu LI.label2
+{ color: #ff9a00; }
+
+#label-menu LI.label3
+{ color: #009a00; }
+
+#label-menu LI.label4
+{ color: #3130ff; }
+
+#label-menu LI.label5
+{ color: #9c309c; }
+
.aptview_title
{
color: #000000;
position: relative;
font-family: monospace, fixed;
white-space: pre;
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ width: 99%;
font-size: inherit;
margin: 0px;
padding: 0px;
TABLE#messageList TR._selected TD
{
- background: #4b6983;
+ background-color: #4b6983;
color: #fff;
}
text-decoration: line-through;
}
+TABLE#messageList TR[labels~="label5"] TD
+{ color: #9c309c; }
+
+TABLE#messageList TR[labels~="label5"]._selected TD
+{ color: #fff;
+ background-color: #9c309c; }
+
+TABLE#messageList TR[labels~="label4"] TD
+{ color: #3130ff; }
+
+TABLE#messageList TR[labels~="label4"]._selected TD
+{ color: #fff;
+ background-color: #3130ff; }
+
+TABLE#messageList TR[labels~="label3"] TD
+{ color: #009a00; }
+
+TABLE#messageList TR[labels~="label3"]._selected TD
+{ color: #fff;
+ background-color: #009a00; }
+
+TABLE#messageList TR[labels~="label2"] TD
+{ color: #ff9a00; }
+
+TABLE#messageList TR[labels~="label2"]._selected TD
+{ color: #fff;
+ background-color: #ff9a00; }
+
+TABLE#messageList TR[labels~="label1"] TD
+{ color: #f00; }
+
+TABLE#messageList TR[labels~="label1"]._selected TD
+{ color: #fff;
+ background-color: #f00; }
+
/* drag handles */
DIV#verticalDragHandle
{
}
/* a model for a futur refactoring of the sortable table headers mechanism */
-
-
function configureMessageListEvents(table) {
if (table) {
table.multiselect = true;
return _onMenuChangeToXXXFolder(event, "Trash");
}
+function onMenuLabelNone() {
+ var rowId = document.menuTarget.getAttribute("id").substr(4);
+ var messageId = currentMailbox + "/" + rowId;
+ var urlstr = ApplicationBaseURL + messageId + "/removeAllLabels";
+ triggerAjaxRequest(urlstr, messageFlagCallback,
+ { mailbox: currentMailbox, msg: rowId, label: null } );
+}
+
+function _onMenuLabelFlagX(flag) {
+ var flags = document.menuTarget.getAttribute("labels").split(" ");
+
+ var rowId = document.menuTarget.getAttribute("id").substr(4);
+ var messageId = currentMailbox + "/" + rowId;
+
+ var operation = "add";
+ if (flags.indexOf("label" + flag) > -1)
+ operation = "remove";
+ var urlstr = (ApplicationBaseURL + messageId
+ + "/" + operation + "Label" + flag);
+ triggerAjaxRequest(urlstr, messageFlagCallback,
+ { mailbox: currentMailbox, msg: rowId,
+ label: operation + flag } );
+}
+
+function onMenuLabelFlag1() {
+ _onMenuLabelFlagX(1);
+}
+
+function onMenuLabelFlag2() {
+ _onMenuLabelFlagX(2);
+}
+
+function onMenuLabelFlag3() {
+ _onMenuLabelFlagX(3);
+}
+
+function onMenuLabelFlag4() {
+ _onMenuLabelFlagX(4);
+}
+
+function onMenuLabelFlag5() {
+ _onMenuLabelFlagX(5);
+}
+
function folderOperationCallback(http) {
if (http.readyState == 4
&& isHttpStatus204(http.status))
window.alert(labels["Operation failed"]);
}
+function messageFlagCallback(http) {
+ if (http.readyState == 4
+ && isHttpStatus204(http.status)) {
+ var data = http.callbackData;
+ if (data["mailbox"] == currentMailbox) {
+ var row = $("row_" + data["msg"]);
+ var operation = data["label"];
+ if (operation) {
+ var labels = row.getAttribute("labels");
+ var flags;
+ if (labels.length > 0)
+ flags = labels.split(" ");
+ else
+ flags = new Array();
+ if (operation.substr(0, 3) == "add")
+ flags.push("label" + operation.substr(3));
+ else {
+ var flag = "label" + operation.substr(6);
+ var idx = flags.indexOf(flag);
+ flags.splice(idx, 1);
+ }
+ row.setAttribute("labels", flags.join(" "));
+ }
+ else
+ row.setAttribute("labels", "");
+ }
+ }
+}
+
+function onLabelMenuPrepareVisibility() {
+ var messageList = $("messageList");
+ var rows = messageList.getSelectedRows();
+
+ var flags = {};
+ for (var i = 1; i < 6; i++)
+ flags["label" + i] = true;
+ for (var i = 0; i < rows.length; i++) {
+ var rowFlags = rows[i].getAttribute("labels").split(" ");
+ for (var flag in flags)
+ if (flags[flag] && rowFlags.indexOf(flag) == -1)
+ flags[flag] = false;
+ }
+
+ var lis = this.childNodesWithTag("ul")[0].childNodesWithTag("li")
+ var isFlagged = false;
+ for (var i = 1; i < 6; i++) {
+ if (flags["label" + i]) {
+ isFlagged = true;
+ lis[1 + i].addClassName("_chosen");
+ }
+ else
+ lis[1 + i].removeClassName("_chosen");
+ }
+ if (isFlagged)
+ lis[0].removeClassName("_chosen");
+ else
+ lis[0].addClassName("_chosen");
+}
+
function getMenus() {
var menus = {}
menus["accountIconMenu"] = new Array(null, null, onMenuCreateFolder, null,
menus["folderTypeMenu"] = new Array(onMenuChangeToSentFolder,
onMenuChangeToDraftsFolder,
onMenuChangeToTrashFolder);
-
- menus["label-menu"] = new Array(null, "-", null , null, null, null , null,
- null);
+
+ menus["label-menu"] = new Array(onMenuLabelNone, "-", onMenuLabelFlag1,
+ onMenuLabelFlag2, onMenuLabelFlag3,
+ onMenuLabelFlag4, onMenuLabelFlag5);
menus["mark-menu"] = new Array(null, null, null, null, "-", null, "-",
null, null, null);
menus["searchMenu"] = new Array(setSearchCriteria, setSearchCriteria,
setSearchCriteria, setSearchCriteria,
setSearchCriteria);
+ $("label-menu").prepareVisibility = onLabelMenuPrepareVisibility;
return menus;
}
{ display: inline;
vertical-align: middle; }
-#privacy-menu LI
-{ list-style-position: inside;
- list-style-image: url("menu-nocheck.gif"); }
-
-#privacy-menu LI._chosen
-{ list-style-image: url("menu-check.gif"); }
-
-#privacy-menu LI._chosen:hover
-{ list-style-image: url("menu-check-hover.gif"); }
-
UL.contactList
{ display: block;
cursor: default;
field = document.pageform.subject;
if (field.value == "")
- errortext = errortext + labels.error_missingsubject + "\n";
+ errortext = errortext + labels["error_missingsubject"] + "\n";
if (!UIxRecipientSelectorHasRecipients())
- errortext = errortext + labels.error_missingrecipients + "\n";
+ errortext = errortext + labels["error_missingrecipients"] + "\n";
if (errortext.length > 0) {
- alert(labels.error_validationfailed + ":\n" + errortext);
+ alert(labels["error_validationfailed"] + ":\n" + errortext);
return false;
}
+
return true;
}
http.open("POST", url, false /* not async */);
http.send("");
}
-
+
Event.stopObserving(window, "beforeunload", onMailEditorClose);
}
{ display: inline;
vertical-align: middle; }
-#privacy-menu LI
-{ list-style-position: inside;
- list-style-image: url("menu-nocheck.gif"); }
-
-#privacy-menu LI._chosen
-{ list-style-image: url("menu-check.gif"); }
-
-#privacy-menu LI._chosen:hover
-{ list-style-image: url("menu-check-hover.gif"); }
-
LABEL, SPAN.checkBoxList
{ display: block;
position: relative;
{ margin: auto; }
/* popups */
+
.menu
{ visibility: hidden;
position: absolute;
padding-right: .2em;
vertical-align: middle; }
+/* popup menu with check marks */
+UL.choiceMenu LI
+{ list-style-position: inside;
+ list-style-image: url("menu-nocheck.gif");
+ padding: 3px 3px;
+ margin: 0px;
+ padding-right: 10px; }
+
+UL.choiceMenu LI._chosen
+{ list-style-image: url("menu-check.gif"); }
+
+UL.choiceMenu LI._chosen:hover
+{ list-style-image: url("menu-check-hover.gif"); }
+
.menu LI:hover, .menu LI.submenu-selected
{ background-color: #4b6983;
color: #fff; }
.menu LI.submenu:hover, .menu LI.submenu-selected
{ background-image: url('submenu-active.gif') !important; }
-/* live search popup menu */
-UL#searchOptions LI
-{ list-style-position: inside;
- list-style-image: url("menu-nocheck.gif");
- padding: 1px 3px;
- margin: 0px; }
-
-UL#searchOptions LI._chosen
-{ list-style-image: url("menu-check.gif"); }
-
-UL#searchOptions LI._chosen:hover
-{ list-style-image: url("menu-check-hover.gif"); }
-
DIV#logConsole
{ position: absolute;
overflow: auto;
if (leftDiff < 0)
menuLeft -= popup.offsetWidth;
+ if (popup.prepareVisibility)
+ popup.prepareVisibility();
popup.setStyle({ top: menuTop + "px",
- left: menuLeft + "px",
- visibility: "visible" });
+ left: menuLeft + "px",
+ visibility: "visible" });
document.currentPopupMenu = popup;
}
function onBodyClickMenuHandler(event) {
- document.body.menuTarget = null;
+ document.menuTarget = null;
hideMenu(document.currentPopupMenu);
Event.stopObserving(document.body, "click", onBodyClickMenuHandler);
parentNode.submenuItem = this;
parentNode.submenu = submenuNode;
+ if (submenuNode.prepareVisibility)
+ submenuNode.prepareVisibility();
+
var menuTop = (parentNode.offsetTop - 1
+ this.offsetTop);
if (window.height()