+2005-01-30 Helge Hess <helge.hess@opengroupware.org>
+
+ * v0.9.59
+
+ * SOGoDraftObject.m: improved attachment/info API to return exceptions,
+ minor improvement to attachment name check, properly generate
+ message/rfc822 mime type for mail attachments, properly generate
+ "text/plain; utf8" header
+
+ * SOGoMailAccount.m: use a constant for INBOX folder name, disabled
+ 'Filters' folder unless the 'SOGoEnableSieveFolder' is set (since
+ Sieve support is incomplete)
+
2005-01-28 Helge Hess <helge.hess@skyrix.com>
* v0.9.58
The SOGoDraftObject is used for composing new messages. It is necessary
because we can't cache objects in a session. So the contents of the drafts
folder are some kind of "mail creation transaction".
+
+ TODO: store-info should be an own object, not NSDictionary.
*/
@class NSString, NSArray, NSDictionary, NSData, NSException;
/* contents */
- (NSDictionary *)fetchInfo;
-- (BOOL)storeInfo:(NSDictionary *)_info;
+- (NSException *)storeInfo:(NSDictionary *)_info;
+
+/* attachments */
- (NSArray *)fetchAttachmentNames;
- (BOOL)isValidAttachmentName:(NSString *)_name;
-- (BOOL)saveAttachment:(NSData *)_attachment withName:(NSString *)_name;
-- (BOOL)deleteAttachmentWithName:(NSString *)_name;
+- (NSException *)saveAttachment:(NSData *)_attach withName:(NSString *)_name;
+- (NSException *)deleteAttachmentWithName:(NSString *)_name;
/* NGMime representations */
/* contents */
-- (BOOL)storeInfo:(NSDictionary *)_info {
+- (NSException *)storeInfo:(NSDictionary *)_info {
if (_info == nil) {
- [self warnWithFormat:@"got no info to write for draft!"];
- return NO;
+ return [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason:@"got no info to write for draft!"];
}
if (![self _ensureDraftFolderPath]) {
[self errorWithFormat:@"could not create folder for draft: '%@'",
[self draftFolderPath]];
- return NO;
+ return [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason:@"could not create folder for draft!"];
}
if (![_info writeToFile:[self infoPath] atomically:YES]) {
[self errorWithFormat:@"could not write info: '%@'", [self infoPath]];
- return NO;
+ return [NSException exceptionWithHTTPStatus:500 /* server error */
+ reason:@"could not write draft info!"];
}
- return YES;
+
+ /* reset info cache */
+ [self->info release]; self->info = nil;
+
+ return nil /* everything is excellent */;
}
- (NSDictionary *)fetchInfo {
NSString *p;
}
- (BOOL)isValidAttachmentName:(NSString *)_name {
- NSRange r;
+ static NSString *sescape[] = { @"/", @"..", @"~", @"\"", @"'", @" ", nil };
+ unsigned i;
+ NSRange r;
if (![_name isNotNull]) return NO;
if ([_name length] == 0) return NO;
if ([_name hasPrefix:@"."]) return NO;
- r = [_name rangeOfString:@"/"];
- if (r.length > 0) return NO;
- r = [_name rangeOfString:@".."];
- if (r.length > 0) return NO;
- r = [_name rangeOfString:@"~"];
- if (r.length > 0) return NO;
- r = [_name rangeOfString:@"\""];
- if (r.length > 0) return NO;
-
+ for (i = 0; sescape[i] != nil; i++) {
+ r = [_name rangeOfString:sescape[i]];
+ if (r.length > 0) return NO;
+ }
return YES;
}
return [[self draftFolderPath] stringByAppendingPathComponent:_name];
}
-- (BOOL)saveAttachment:(NSData *)_attachment withName:(NSString *)_name {
- NSString *p;
+- (NSException *)invalidAttachmentNameError:(NSString *)_name {
+ return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
+ reason:@"Invalid attachment name!"];
+}
- if (_attachment == nil)
- return NO;
+- (NSException *)saveAttachment:(NSData *)_attach withName:(NSString *)_name {
+ NSString *p;
+
+ if (![_attach isNotNull]) {
+ return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
+ reason:@"Missing attachment content!"];
+ }
if (![self _ensureDraftFolderPath]) {
- [self errorWithFormat:@"could not create folder for draft!"];
- return NO;
+ return [NSException exceptionWithHTTPStatus:500 /* Server Error */
+ reason:@"Could not create folder for draft!"];
}
if (![self isValidAttachmentName:_name])
- return NO;
+ return [self invalidAttachmentNameError:_name];
p = [self pathToAttachmentWithName:_name];
- return [_attachment writeToFile:p atomically:YES];
+ if (![_attach writeToFile:p atomically:YES]) {
+ return [NSException exceptionWithHTTPStatus:500 /* Server Error */
+ reason:@"Could not write attachment to draft!"];
+ }
+
+ return nil; /* everything OK */
}
-- (BOOL)deleteAttachmentWithName:(NSString *)_name {
+- (NSException *)deleteAttachmentWithName:(NSString *)_name {
NSFileManager *fm;
NSString *p;
if (![self isValidAttachmentName:_name])
- return NO;
+ return [self invalidAttachmentNameError:_name];
fm = [self spoolFileManager];
p = [self pathToAttachmentWithName:_name];
if (![fm fileExistsAtPath:p])
- return YES; /* well, doesn't exist, so its deleted ;-) */
+ return nil; /* well, doesn't exist, so its deleted ;-) */
- return [fm removeFileAtPath:p handler:nil];
+ if (![fm removeFileAtPath:p handler:nil]) {
+ [self logWithFormat:@"ERROR: failed to delete file: %@", p];
+ return [NSException exceptionWithHTTPStatus:500 /* Server Error */
+ reason:@"Could not delete attachment from draft!"];
+ }
+ return nil; /* everything OK */
}
/* NGMime representations */
NGMutableHashMap *map;
NGMimeBodyPart *bodyPart;
NSDictionary *lInfo;
-
+ id body;
+
if ((lInfo = [self fetchInfo]) == nil)
return nil;
/* prepare header of body part */
-
+
map = [[[NGMutableHashMap alloc] initWithCapacity:2] autorelease];
+
+ // TODO: set charset in header!
[map setObject:@"text/plain" forKey:@"content-type"];
+ if ((body = [lInfo objectForKey:@"text"]) != nil) {
+ if ([body isKindOfClass:[NSString class]]) {
+ [map setObject:@"text/plain; charset=utf-8" forKey:@"content-type"];
+ body = [body dataUsingEncoding:NSUTF8StringEncoding];
+ }
+ }
/* prepare body content */
bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease];
- [bodyPart setBody:[lInfo objectForKey:@"text"]];
+ [bodyPart setBody:body];
return bodyPart;
}
- (NGMimeMessage *)mimeMessageForContentWithHeaderMap:(NGMutableHashMap *)map {
NSDictionary *lInfo;
NGMimeMessage *message;
+ id body;
if ((lInfo = [self fetchInfo]) == nil)
return nil;
[map setObject:@"text/plain" forKey:@"content-type"];
+ if ((body = [lInfo objectForKey:@"text"]) != nil) {
+ if ([body isKindOfClass:[NSString class]]) {
+ /* Note: just 'utf8' is displayed wrong in Mail.app */
+ [map setObject:@"text/plain; charset=utf-8" forKey:@"content-type"];
+ body = [body dataUsingEncoding:NSUTF8StringEncoding];
+ }
+ }
+
message = [[[NGMimeMessage alloc] initWithHeader:map] autorelease];
- [message setBody:[lInfo objectForKey:@"text"]];
+ [message setBody:body];
return message;
}
- (NSString *)mimeTypeForExtension:(NSString *)_ext {
// TODO: make configurable
+ // TODO: use /etc/mime-types
if ([_ext isEqualToString:@"txt"]) return @"text/plain";
if ([_ext isEqualToString:@"html"]) return @"text/html";
if ([_ext isEqualToString:@"htm"]) return @"text/html";
if ([_ext isEqualToString:@"gif"]) return @"image/gif";
if ([_ext isEqualToString:@"jpg"]) return @"image/jpeg";
if ([_ext isEqualToString:@"jpeg"]) return @"image/jpeg";
+ if ([_ext isEqualToString:@"mail"]) return @"message/rfc822";
return @"application/octet-stream";
}
if ([type hasPrefix:@"text/"])
cdtype = @"inline";
- else if ([type hasPrefix:@"image/"])
+ else if ([type hasPrefix:@"image/"] || [type hasPrefix:@"message"])
cdtype = @"inline";
else
cdtype = @"attachment";
-
+
cd = [cdtype stringByAppendingString:@"; filename=\""];
cd = [cd stringByAppendingString:_name];
cd = [cd stringByAppendingString:@"\""];
NGMimeBodyPart *bodyPart;
NSString *s;
NSData *content;
- BOOL attachAsString;
+ BOOL attachAsString, is7bit;
NSString *p;
id body;
return nil;
}
attachAsString = NO;
+ is7bit = NO;
/* prepare header of body part */
[map setObject:s forKey:@"content-type"];
if ([s hasPrefix:@"text/"])
attachAsString = YES;
+ else if ([s hasPrefix:@"message/rfc822"])
+ is7bit = YES;
}
if ((s = [self contentDispositionForAttachmentWithName:_name]))
[map setObject:s forKey:@"content-disposition"];
content = nil;
}
}
+ else if (is7bit) {
+ /*
+ Note: Apparently NGMimeFileData objects are not processed by the MIME
+ generator!
+ */
+ body = [[NGMimeFileData alloc] initWithPath:p removeFile:NO];
+ [map setObject:@"7bit" forKey:@"content-transfer-encoding"];
+ [map setObject:[NSNumber numberWithInt:[body length]]
+ forKey:@"content-length"];
+ }
else {
/*
Note: in OGo this is done in LSWImapMailEditor.m:2477. Apparently
message = [self mimeMessage];
if (![message isNotNull])
return nil;
+ if ([message isKindOfClass:[NSException class]]) {
+ [self errorWithFormat:@"error: %@", message];
+ return nil;
+ }
gen = [[NGMimeMessageGenerator alloc] init];
tmpPath = [[gen generateMimeFromPartToFile:message] copy];
/*
- Copyright (C) 2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
-// $Id$
#include "SOGoDraftsFolder.h"
#include "SOGoDraftObject.h"
@implementation SOGoMailAccount
+static NSString *inboxFolderName = @"INBOX";
static NSString *draftsFolderName = @"Drafts";
static NSString *sieveFolderName = @"Filters";
+static NSArray *rootFolderNames = nil;
+
++ (void)initialize {
+ NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+
+ if ([ud boolForKey:@"SOGoEnableSieveFolder"]) {
+ rootFolderNames = [[NSArray alloc] initWithObjects:
+ inboxFolderName,
+ draftsFolderName,
+ sieveFolderName,
+ nil];
+ }
+ else {
+ rootFolderNames = [[NSArray alloc] initWithObjects:
+ inboxFolderName,
+ draftsFolderName,
+ nil];
+ }
+}
/* listing the available folders */
- (NSArray *)toManyRelationshipKeys {
// TODO: hardcoded, if we want to support shared fldrs, this needs to change
- static NSArray *accFolderNames = nil;
- if (accFolderNames == nil) {
- accFolderNames = [[NSArray alloc] initWithObjects:
- @"INBOX", @"Drafts", @"Filters", nil];
- }
- return accFolderNames;
+ return rootFolderNames;
}
/* hierarchy */
/* special folders */
- (SOGoMailFolder *)inboxFolderInContext:(id)_ctx {
- // TODO: use some profile to determine real location
+ // TODO: use some profile to determine real location, use a -traverse lookup
SOGoMailFolder *folder;
if (self->inboxFolder != nil)
return self->inboxFolder;
- folder = [self lookupName:@"INBOX" inContext:_ctx acquire:NO];
+ folder = [self lookupName:inboxFolderName inContext:_ctx acquire:NO];
if ([folder isKindOfClass:[NSException class]]) return folder;
return ((self->inboxFolder = [folder retain]));
}
- (SOGoMailFolder *)sentFolderInContext:(id)_ctx {
- // TODO: use some profile to determine real location
+ // TODO: use some profile to determine real location, use a -traverse lookup
SOGoMailFolder *folder;
if (self->sentFolder != nil)
/* core infos */
-- (id)fetchCoreInfos;
+- (id)fetchCoreInfos; // TODO: what does it do?
- (NGImap4Envelope *)envelope;
- (NSString *)subject;
# Version file
-SUBMINOR_VERSION:=58
+SUBMINOR_VERSION:=59
# v0.9.55 requires NGExtensions v4.5.136
# v0.9.44 requires libNGMime v4.3.194
+2005-01-30 Helge Hess <helge.hess@opengroupware.org>
+
+ * v0.9.80
+
+ * UIxMailEditorAction.m: work on forwarding and reply
+
+ * UIxFilterList.wox, UIxMailAccountView.wox, UIxMailAccountsView.wox,
+ UIxSieveEditor.wox: fixed title (needs localization)
+
+ * UIxMailMainFrame.m: always generate title as given by the parent
+ component (titles need to get fixed!), was fixed to OpenGroupware.org
+ before unless in debug mode
+
+ * UIxMailEditor.m: use new storeInfo API, generate a proper panelTitle
+ (needs localization)
+
+ * UIxMailEditorAttach.m: use new attachment API
+
+ * UIxMailView.wox: remoted link to screenshot, use mail subject as
+ title
+
+ * UIxMailListView.m: disabled a log
+
2005-01-28 Helge Hess <helge.hess@skyrix.com>
* v0.9.79
/*
- Copyright (C) 2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
return self->filters;
}
+- (NSString *)panelTitle {
+ return [self labelForKey:@"Mail Filters"];
+}
+
/* JavaScript code */
- (NSString *)clickedFilterJS {
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
className="UIxMailMainFrame"
- title="name"
+ title="panelTitle"
>
<div class="titlediv" style="white-space: nowrap;">
/*
- Copyright (C) 2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
-// $Id: UIxMailAccountView.m 308 2004-09-20 10:51:12Z helge $
#include <SOGoUI/UIxComponent.h>
@implementation UIxMailAccountView
+/* title */
+
+- (NSString *)objectTitle {
+ return [[self clientObject] nameInContainer];
+}
+- (NSString *)panelTitle {
+ NSString *s;
+
+ s = [self labelForKey:@"Mail Account"];
+ s = [s stringByAppendingString:@": "];
+ s = [s stringByAppendingString:[self objectTitle]];
+ return s;
+}
+
@end /* UIxMailAccountView */
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
className="UIxMailMainFrame"
- title="name"
+ title="panelTitle"
>
<div class="embedwhite_out">
<div class="embedwhite_in">
/*
- Copyright (C) 2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
-// $Id: UIxMailAccountsView.m 308 2004-09-20 10:51:12Z helge $
#include <SOGoUI/UIxComponent.h>
@implementation UIxMailAccountsView
+- (NSString *)panelTitle {
+ return [self labelForKey:@"SOGo Mail Accounts"];
+}
+
@end /* UIxMailAccountsView */
xmlns:label="OGo:label"
xmlns:rsrc="OGo:url"
className="UIxMailMainFrame"
- title="name"
+ title="panelTitle"
>
<div class="embedwhite_out">
<div class="embedwhite_in" style="height: 300px">
+ <var:string label:value="Welcome to the SOGo Mailer. Use the folder tree on the left to browse your mail accounts!" />
+<!--
<h3>SOGo Mail - Available Accounts</h3>
<a rsrc:href="tbird_073_accountview.png">screenshot</a>
+-->
</div>
</div>
</var:component>
return [self->bcc isNotNull] ? self->bcc : [NSArray array];
}
+/* title */
+
+- (NSString *)panelTitle {
+ return [self labelForKey:@"Compose Mail"];
+}
+
/* info loading */
- (void)loadInfo:(NSDictionary *)_info {
NSDictionary *info;
if ((info = [self storeInfo]) != nil) {
- if (![[self clientObject] storeInfo:info]) {
- [self errorWithFormat:@"failed to store draft!"];
+ NSException *error;
+
+ if ((error = [[self clientObject] storeInfo:info]) != nil) {
+ [self errorWithFormat:@"failed to store draft: %@", error];
// TODO: improve error handling
return NO;
}
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
className="UIxMailMainFrame"
- title="name"
+ title="panelTitle"
const:hideFolderTree="1"
>
<div id="compose_panel">
/*
- Copyright (C) 2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
-// $Id$
#include <NGObjWeb/WODirectAction.h>
needs to be created in advance.
*/
+@class SOGoDraftObject;
+
@interface UIxMailEditorAction : WODirectAction
+{
+ SOGoDraftObject *newDraft;
+}
+
@end
#include <SOGo/SoObjects/Mailer/SOGoDraftsFolder.h>
+#include <SOGo/SoObjects/Mailer/SOGoDraftObject.h>
#include <SOGo/SoObjects/Mailer/SOGoMailAccount.h>
+#include <SOGo/SoObjects/Mailer/SOGoMailObject.h>
#include "common.h"
@implementation UIxMailEditorAction
+- (void)dealloc {
+ [self->newDraft release];
+ [super dealloc];
+}
+
+/* caches */
+
+- (void)reset {
+ [self->newDraft release]; self->newDraft = nil;
+}
+
/* lookups */
- (SOGoDraftsFolder *)draftsFolder {
/* errors */
- (id)didNotFindDraftsError {
- // TODO: make a nice page
+ // TODO: make a nice error page
return [@"did not find drafts folder in object: "
stringByAppendingString:[[self clientObject] description]];
}
return [@"could not create a new draft in folder: "
stringByAppendingString:[_draftsFolder description]];
}
+- (id)didNotFindMailError {
+ return [NSException exceptionWithHTTPStatus:404 /* Not Found */
+ reason:@"Did not find mail for operation!"];
+}
-/* actions */
+/* compose */
- (id)composeAction {
SOGoDraftsFolder *drafts;
if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
url = [url stringByAppendingString:@"edit"];
+ // TODO: debug log
[self logWithFormat:@"compose on %@: %@", drafts, url];
r = [[self context] response];
[r setStatus:302 /* moved */];
[r setHeader:url forKey:@"location"];
+ [self reset];
return r;
}
+/* creating new draft object */
+
+- (id)newDraftObject {
+ SOGoDraftsFolder *drafts;
+
+ drafts = [self draftsFolder];
+ if (![drafts isNotNull])
+ return [self didNotFindDraftsError];
+ if ([drafts isKindOfClass:[NSException class]])
+ return drafts;
+
+ return [drafts newObjectInContext:[self context]];
+}
+
+- (NSException *)_setupNewDraft {
+ SOGoDraftObject *tmp;
+
+ /* create draft object */
+
+ if ([(tmp = [self newDraftObject]) isKindOfClass:[NSException class]])
+ return (NSException *)tmp;
+ if (![tmp isNotNull]) { /* Note: should never happen? */
+ [self logWithFormat:@"WARNING: got no new draft object and no error!"];
+ return [self didNotFindDraftsError]; // TODO: not exact
+ }
+
+ ASSIGN(self->newDraft, tmp);
+ [self logWithFormat:@"NEW DRAFT: %@", self->newDraft];
+
+ return nil;
+}
+
+- (WOResponse *)redirectToEditNewDraft {
+ WOResponse *r;
+ NSString *url;
+
+ if (![self->newDraft isNotNull]) {
+ [self logWithFormat:@"ERROR(%s): missing new draft (already -reset?)",
+ __PRETTY_FUNCTION__];
+ return nil;
+ }
+
+ url = [self->newDraft baseURLInContext:[self context]];
+ if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
+ url = [url stringByAppendingString:@"edit"];
+
+ // TODO: debug log
+ [self logWithFormat:@"compose on %@", url];
+
+ r = [[self context] response];
+ [r setStatus:302 /* moved */];
+ [r setHeader:url forKey:@"location"];
+ [self reset];
+ return r;
+}
+
+/* response actions */
+
+- (id)replyToAll:(BOOL)_replyToAll {
+ NSException *error;
+ id tmp;
+
+ /* ensure mail exists and is filled */
+
+ // TODO: we could transport the body structure in a hidden field of the mail
+ // viewer to avoid refetching the core-info?
+ tmp = [[self clientObject] fetchCoreInfos];
+ if ([tmp isKindOfClass:[NSException class]])
+ return tmp;
+ if (![tmp isNotNull])
+ return [self didNotFindMailError];
+
+ /* setup draft */
+
+ if ((error = [self _setupNewDraft]) != nil)
+ return error;
+
+#if 0
+ [self logWithFormat:@"CORE: %@", [[self clientObject] fetchCoreInfos]];
+#endif
+ [self reset];
+ return [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
+ reason:@"Sorry, reply is not yet implemented!"];
+}
+
+- (id)replyAction {
+ return [self replyToAll:NO];
+}
+- (id)replyallAction {
+ return [self replyToAll:YES];
+}
+
+- (NSString *)getAttachmentNameForSubject:(NSString *)_subject {
+ /* SOGoDraftObject disallows some strings - anything else required? */
+ static NSString *sescape[] = {
+ @"/", @"..", @"~", @"\"", @"'", @" ", @".", nil
+ };
+ static int maxFilenameLength = 64;
+ NSString *s;
+ unsigned i;
+
+ if (![_subject isNotNull] || [_subject length] == 0)
+ return _subject;
+ s = _subject;
+
+ if ([s length] > maxFilenameLength)
+ s = [s substringToIndex:maxFilenameLength];
+
+ for (i = 0; sescape[i] != nil; i++)
+ s = [s stringByReplacingString:sescape[i] withString:@"_"];
+
+ return [s stringByAppendingString:@".mail"];
+}
+
+- (NSString *)forwardSubject:(NSString *)_subject {
+ if (![_subject isNotNull] || [_subject length] == 0)
+ return _subject;
+
+ /* Note: this is how Thunderbird 1.0 creates the subject */
+ _subject = [@"[Fwd: " stringByAppendingString:_subject];
+ _subject = [_subject stringByAppendingString:@"]"];
+ return _subject;
+}
+
+- (id)forwardAction {
+ NSException *error;
+ NSData *content;
+ NSDictionary *info;
+ id result;
+
+ /* fetch message */
+
+ if ((content = [[self clientObject] content]) == nil)
+ return [self didNotFindMailError];
+ if ([content isKindOfClass:[NSException class]])
+ return content;
+
+ /* setup draft */
+
+ if ((error = [self _setupNewDraft]) != nil)
+ return error;
+
+ /* set subject (do we need to set anything else?) */
+
+ info = [NSDictionary dictionaryWithObjectsAndKeys:
+ [self forwardSubject:[[self clientObject] subject]],
+ @"subject",
+ nil];
+ if ((error = [self->newDraft storeInfo:info]) != nil)
+ return error;
+
+ /* attach message */
+
+ // TODO: use subject for filename?
+ error = [self->newDraft saveAttachment:content withName:@"forward.mail"];
+ if (error != nil)
+ return error;
+
+ // TODO: we might want to pass the original URL to the editor for a final
+ // redirect back to the message?
+ result = [self redirectToEditNewDraft];
+ [self reset];
+ return result;
+}
+
@end /* UIxMailEditorAction */
/*
- Copyright (C) 2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
-// $Id$
#include <SOGoUI/UIxComponent.h>
}
- (BOOL)saveFileData:(NSData *)_data name:(NSString *)_name {
+ NSException *error;
+
if (_data == nil)
return NO;
if ([_name length] == 0) {
return NO;
// TODO: add size limit?
- return [[self clientObject] saveAttachment:_data withName:_name];
+ error = [[self clientObject] saveAttachment:_data withName:_name];
+ if (error != nil) {
+ [self logWithFormat:@"ERROR: could not save: %@", error];
+ return NO;
+ }
+ return YES;
}
/* actions */
}
- (id)deleteAttachmentAction {
- if (![[self clientObject] deleteAttachmentWithName:[self attachmentName]]) {
- // TODO: improve error handling
- return [NSException exceptionWithHTTPStatus:500 /* server error */
- reason:@"failed to delete attachment ..."];
- }
+ NSException *error;
+
+ error = [[self clientObject] deleteAttachmentWithName:[self attachmentName]];
+
+ if (error != nil)
+ return error;
return [self redirectToLocation:@"viewAttachments"];
}
/*
- Copyright (C) 2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
@end
+/*
+ TODO: the subject formatter should deal with the various 're:' like prefixes
+ and translate them into the native languages?
+ (or something like Re(5): ?)
+*/
+
@interface UIxSubjectFormatter : UIxMailFormatter
{
unsigned maxLength;
return NO;
}
+/* title */
+
+- (NSString *)objectTitle {
+ return [[self clientObject] nameInContainer];
+}
+- (NSString *)panelTitle {
+ NSString *s;
+
+ s = [self labelForKey:@"View Mail Folder"];
+ s = [s stringByAppendingString:@": "];
+ s = [s stringByAppendingString:[self objectTitle]];
+ return s;
+}
+
/* derived accessors */
- (BOOL)isMessageDeleted {
/* actions */
- (id)defaultAction {
- // TODO: remove log
+#if 0
[self logWithFormat:@"default action ..."];
+#endif
self->firstMessageNumber =
[[[[self context] request] formValueForKey:@"idx"] intValue];
return self;
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
className="UIxMailMainFrame"
- title="name"
+ title="panelTitle"
>
<div class="titlediv" style="white-space: nowrap;">
<!-- TODO: enable once implemented: #1209, #1210
ASSIGNCOPY(self->title, _value);
}
- (NSString *)title {
- if ([self isUIxDebugEnabled])
- return self->title;
+ if ([self->title length] == 0)
+ return @"OpenGroupware.org";
- return [self labelForKey:@"OpenGroupware.org"];
+ return self->title;
}
- (void)setItem:(id)_item {
/*
- Copyright (C) 2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
-// $Id$
#include <SOGoUI/UIxComponent.h>
return self->currentAddress;
}
+- (NSString *)objectTitle {
+ return [[self clientObject] subject];
+}
+- (NSString *)panelTitle {
+ NSString *s;
+
+ s = [self labelForKey:@"View Mail"];
+ s = [s stringByAppendingString:@": "];
+ s = [s stringByAppendingString:[self objectTitle]];
+ return s;
+}
+
/* fetching */
- (id)message {
/* viewers */
- (id)contentViewerComponent {
+ // TODO: I would prefer to flatten the body structure prior rendering,
+ // using some delegate to decide which parts to select for alternative.
id info;
info = [[self clientObject] bodyStructure];
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
className="UIxMailMainFrame"
- title="name"
+ title="panelTitle"
const:hideFolderTree="1"
>
<!--
<var:string value="clientObject.date"
formatter="context.mailDateFormatter"/>
- <!-- TODO: -->
+ <!-- TODO:
(<a rsrc:href="tbird_073_viewer.png">screenshot</a>)
+ -->
</td>
</tr>
return [self->scriptText isNotNull] ? self->scriptText : @"";
}
+- (NSString *)panelTitle {
+ return [self labelForKey:@"Edit Mail Filter"];
+}
+
/* requests */
- (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c {
xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"
className="UIxMailMainFrame"
- title="name"
+ title="panelTitle"
const:hideFolderTree="1"
>
<div id="compose_panel">
/*
- Copyright (C) 2004 SKYRIX Software AG
+ Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org.
# version file
-SUBMINOR_VERSION:=79
+SUBMINOR_VERSION:=80
+# v0.9.80 requires SoObjects/Mailer v0.9.59
# v0.9.78 requires SoObjects/Mailer v0.9.58
# v0.9.77 requires SoObjects/Mailer v0.9.57
# v0.9.74 requires SoObjects/Mailer v0.9.56
( // third group
{ link = "delete";
cssClass = "tbicon_delete"; label = "Delete"; },
- ),
-/* TODO: enable when implemented
- ( // third group
-// TODO: enable when delete works (#1212)
- { link = "#";
- cssClass = "tbicon_delete"; label = "Delete"; },
-// TODO: enable when we know how to mark junk (#971)
+/* TODO: enable when we know how to mark junk (#971)
{ link = "#";
cssClass = "tbicon_junk"; label = "Junk"; },
- ),
*/
+ ),
( /* fourth group */
/* TODO: enable when we can print (#1207)
{ link = "#"; cssClass = "tbicon_print"; label = "Print"; },