MailerUI_OBJC_FILES += \
MailerUIProduct.m \
\
- UIxMailMainFrame.m \
- UIxMailTree.m \
UIxMailFormatter.m \
+ UIxSubjectFormatter.m \
WOContext+UIxMailer.m \
\
+ UIxMailMainFrame.m \
+ UIxMailTree.m \
+ \
UIxMailAccountsView.m \
UIxMailAccountView.m \
UIxMailListView.m \
MailerUI_RESOURCE_FILES += \
screenshots/*.png \
Images/tbtv_*.gif \
+ Images/icon_*.gif \
Images/tbtb_*.png \
MailerUI_LOCALIZED_RESOURCE_FILES += \
#import <Foundation/NSFormatter.h>
-@class NSString, NSCalendarDate, NSTimeZone;
+/*
+ UIxMailFormatter
+
+ Formatters which render various mail related fields.
+*/
+
+@class NSData, NSString, NSCalendarDate, NSTimeZone;
@interface UIxMailFormatter : NSFormatter
{
- (NSString *)missingSubjectLabel;
+/* specific formatters */
+
+- (NSString *)stringForStringValue:(NSString *)_subject;
+- (NSString *)stringForDataValue:(NSData *)_subject;
+
@end
@interface UIxEnvelopeAddressFormatter : UIxMailFormatter
@end /* UIxMailDateFormatter */
-@implementation UIxSubjectFormatter
-
-- (id)init {
- if ((self = [super init])) {
- self->maxLength = 64;
- }
- return self;
-}
-
-/* configuration */
-
-- (unsigned int)maxLength {
- return self->maxLength;
-}
-
-/* labels */
-
-- (NSString *)missingSubjectLabel {
- return [self labelForKey:@"no_subject"];
-}
-
-/* specific formatters */
-
-- (NSString *)stringForStringValue:(NSString *)_subject {
- NSString *s;
-
- if ([_subject hasPrefix:@"=?"]) { /* quoted printable */
-#warning TODO: work on QP decoding
- /* =?iso-8859-1?q?Yannick=20DAmboise?= */
- if ((s = [_subject stringByDecodingQuotedPrintable]))
- _subject = s;
- }
-
- if ([_subject length] == 0)
- return [self missingSubjectLabel];
-
- if ([_subject length] <= [self maxLength])
- return _subject;
-
- s = [_subject substringToIndex:([self maxLength] - 3)];
- return [s stringByAppendingString:@"..."];
-}
-
-- (NSString *)stringForDataValue:(NSData *)_subject {
- NSString *s, *r;
-
- if ([_subject length] == 0)
- return [self missingSubjectLabel];
-
- [self debugWithFormat:@"WARNING: NSData subject! (using UTF-8 to decode!)"];
-
- // TODO: exception handler?
- s = [[NSString alloc] initWithData:_subject encoding:NSUTF8StringEncoding];
- if (s == nil) {
- [self logWithFormat:@"ERROR: could do not decode NSData subject!"];
- return [self labelForKey:@"Error_CouldNotDecodeSubject"];
- }
-
- r = [[self stringForStringValue:s] copy];
- [s release];
- return [r autorelease];
-}
-
-/* formatter entry function */
-
-- (NSString *)stringForObjectValue:(id)_subject {
- if (![_subject isNotNull])
- return [self missingSubjectLabel];
-
- if ([_subject isKindOfClass:StrClass])
- return [self stringForStringValue:_subject];
- if ([_subject isKindOfClass:[NSData class]])
- return [self stringForDataValue:_subject];
-
- return [self stringForStringValue:[_subject stringValue]];
-}
-
-@end /* UIxSubjectFormatter */
-
#include <NGImap4/NGImap4EnvelopeAddress.h>
@implementation UIxEnvelopeAddressFormatter
return self->message;
}
+- (BOOL)showToAddress {
+ // TODO: switch for Sent folder
+ return NO;
+}
+
/* derived accessors */
- (BOOL)isMessageRead {
flags = [[self message] valueForKey:@"flags"];
return [flags containsObject:@"seen"];
}
+- (NSString *)messageUidString {
+ return [[[self message] valueForKey:@"uid"] stringValue];
+}
- (NSArray *)messages {
NSArray *uids;
return [msgs valueForKey:@"fetch"];
}
+/* URL processing */
+
+- (NSString *)messageViewTarget {
+ return [@"SOGo_msg_" stringByAppendingString:[self messageUidString]];
+}
+- (NSString *)messageViewURL {
+ // TODO: noframe only when view-target is empty
+ // TODO: markread only if the message is unread
+ NSString *s;
+
+ s = [[self messageUidString] stringByAppendingString:@"/view?noframe=1"];
+ if (![self isMessageRead]) s = [s stringByAppendingString:@"&markread=1"];
+ return s;
+}
+- (NSString *)markReadURL {
+ return [@"markMessageRead?uid=" stringByAppendingString:
+ [self messageUidString]];
+}
+- (NSString *)markUnreadURL {
+ return [@"markMessageUnread?uid=" stringByAppendingString:
+ [self messageUidString]];
+}
+
/* actions */
- (id)defaultAction {
return self;
}
+- (id)markMessageUnreadAction {
+ [self logWithFormat:@"TODO: mark message unread!"];
+ return [self redirectToLocation:@"view"];
+}
+- (id)markMessageReadAction {
+ [self logWithFormat:@"TODO: mark message read!"];
+ return [self redirectToLocation:@"view"];
+}
+
@end /* UIxMailListView */
title="name"
>
<div class="titlediv">
- View:
- <select name="viewfilter">
+ <a rsrc:href="tbird_073_mailwelcome.png">View:</a>, <!-- TODO ;-) -->
+ <select name="viewfilter"> <!-- var:popup? -->
<option value="all" >All</option>
<option value="unread">Unread</option>
</select>
- <!-- var:popup -->
+
Subject or Sender contains:
<input name="searchtext" type="text" />
<input name="clear" type="submit" value="Clear" />
<div class="embedwhite_out">
<div class="embedwhite_in">
- <a rsrc:href="tbird_073_mailwelcome.png">screenshot</a>
-
- <table border="0" width="100%">
+ <table border="0" width="100%" cellspacing="0" cellpadding="1">
<tr class="tableview">
- <td>Date</td>
- <td>Subject</td>
- <td>From</td>
- <td>To</td>
- <td>Flags</td>
+ <!-- TODO: see AB for sorting -->
+
+ <td class="tbtv_headercell" width="50%">
+ <a href="?sort=subject"><var:string label:value="Subject" /></a>
+ </td>
+ <td class="tbtv_headercell">
+ <var:if condition="showToAddress" const:negate="YES">
+ <a href="?sort=from"><var:string label:value="From" /></a>
+ </var:if>
+ <var:if condition="showToAddress">
+ <a href="?sort=to"><var:string label:value="To" /></a>
+ </var:if>
+ </td>
+ <td class="tbtv_headercell">
+ <!-- TODO: fix me, use the proper Thunderbird Icon -->
+ <img rsrc:src="icon_read.gif" />
+ </td>
+ <td class="tbtv_headercell">
+ <a href="?sort=date"><var:string label:value="Date" /></a>
+ </td>
</tr>
<var:foreach list="messages" item="message">
<tr class="tableview">
- <td>
- <var:string value="message.envelope.date"
- formatter="context.mailDateFormatter"/>
- </td>
<td>
<var:if condition="isMessageRead">
- <span class="readmailsubject">
- <var:string value="message.envelope.subject"
- formatter="context.mailSubjectFormatter"/>
- </span>
+ <div class="mailer_readmailsubject">
+ <a var:href="messageViewURL" var:target="messageViewTarget">
+ <var:string value="message.envelope.subject"
+ formatter="context.mailSubjectFormatter"/>
+ </a>
+ </div>
</var:if>
<var:if condition="isMessageRead" const:negate="YES">
- <span class="unreadmailsubject">
- <var:string value="message.envelope.subject"
- formatter="context.mailSubjectFormatter"/>
- </span>
+ <div class="mailer_unreadmailsubject">
+ <a var:href="messageViewURL" var:target="messageViewTarget">
+ <var:string value="message.envelope.subject"
+ formatter="context.mailSubjectFormatter"/>
+ </a>
+ </div>
+ </var:if>
+ </td>
+ <td>
+ <!-- TODO: show compose links -->
+ <var:if condition="showToAddress" const:negate="YES">
+ <var:string value="message.envelope.from"
+ formatter="context.mailEnvelopeAddressFormatter" />
+ </var:if>
+ <var:if condition="showToAddress">
+ <var:string value="message.envelope.to"
+ formatter="context.mailEnvelopeAddressFormatter" />
</var:if>
</td>
+
<td>
- <var:string value="message.envelope.from"
- formatter="context.mailEnvelopeAddressFormatter" />
+ <var:if condition="isMessageRead">
+ <div class="mailer_readicon">
+ <a href="markMessageUnread" var:_uid="message.uid"
+ label:title="Mark Unread"> </a>
+ </div>
+ </var:if>
+ <var:if condition="isMessageRead" const:negate="YES">
+ <div class="mailer_unreadicon">
+ <a href="markMessageRead" var:_uid="message.uid"
+ label:title="Mark Read"> </a>
+ </div>
+ </var:if>
</td>
+
<td>
- <var:string value="message.envelope.to"
- formatter="context.mailEnvelopeAddressFormatter" />
+ <span class="mailer_datefield">
+ <var:string value="message.envelope.date"
+ formatter="context.mailDateFormatter"/>
+ </span>
</td>
- <td><var:string value="message.flags"/></td>
</tr>
</var:foreach>
</table>
- Folder List
</div>
</div>
</var:component>
--- /dev/null
+/*
+ Copyright (C) 2004 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ OGo is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "UIxMailFormatter.h"
+#include "common.h"
+
+#include <NGMail/NGMimeMessageParser.h>
+
+@implementation UIxSubjectFormatter
+
+static Class StrClass = Nil;
+static Class DataClass = Nil;
+
++ (void)initialize {
+ StrClass = [NSString class];
+ DataClass = [NSData class];
+}
+
+- (id)init {
+ if ((self = [super init])) {
+ self->maxLength = 64;
+ }
+ return self;
+}
+
+/* configuration */
+
+- (unsigned int)maxLength {
+ return self->maxLength;
+}
+
+- (BOOL)shouldDecodeQP {
+ return YES;
+}
+
+/* labels */
+
+- (NSString *)missingSubjectLabel {
+ return [self labelForKey:@"no_subject"];
+}
+
+/* specific formatters */
+
+- (NSString *)stringForStringValue:(NSString *)_subject {
+ NSString *s;
+
+ /* quoted printable */
+ if ([self shouldDecodeQP] && [_subject hasPrefix:@"=?"]) {
+ /*
+ Now this is interesting. An NSString should not contain QP markers since
+ it is already 'charset decoded'. This is also why the NGMime parser
+ expects an NSData.
+
+ Sample:
+ =?iso-8859-1?q?Yannick=20DAmboise?=
+
+ Note: -stringByDecodingQuotedPrintable only expands =D0 like charcodes!
+ */
+ NSData *data;
+
+ /* header field data should always be ASCII */
+ data = [_subject dataUsingEncoding:NSUTF8StringEncoding];
+ return [self stringForDataValue:data];
+ }
+
+ if ([_subject length] == 0)
+ return [self missingSubjectLabel];
+
+ if ([_subject length] <= [self maxLength])
+ return _subject;
+
+ s = [_subject substringToIndex:([self maxLength] - 3)];
+ return [s stringByAppendingString:@"..."];
+}
+
+- (NSString *)stringForDataValue:(NSData *)_subject {
+ NSString *s, *r;
+ unsigned len;
+
+ if ((len = [_subject length] == 0))
+ return [self missingSubjectLabel];
+
+ /* check for quoted printable */
+
+ if (len > 6 && [self shouldDecodeQP]) {
+ const unsigned char *b;
+
+ b = [_subject bytes];
+ if (b[0] == '=' && b[1] == '?') {
+ /* eg: '=?iso-8859-1?q?Yannick=20DAmboise?=' */
+ id t;
+
+ t = [_subject decodeQuotedPrintableValueOfMIMEHeaderField:@"subject"];
+ if ([t isNotNull])
+ return [self stringForObjectValue:t];
+ }
+ }
+
+ /* continue NSData processing */
+
+ [self debugWithFormat:@"WARNING: NSData subject! (using UTF-8 to decode!)"];
+
+ // TODO: exception handler?
+ s = [[NSString alloc] initWithData:_subject encoding:NSUTF8StringEncoding];
+ if (s == nil) {
+ [self logWithFormat:@"ERROR: could do not decode NSData subject!"];
+ return [self labelForKey:@"Error_CouldNotDecodeSubject"];
+ }
+
+ r = [[self stringForStringValue:s] copy];
+ [s release];
+ return [r autorelease];
+}
+
+/* formatter entry function */
+
+- (NSString *)stringForObjectValue:(id)_subject {
+ if (![_subject isNotNull])
+ return [self missingSubjectLabel];
+
+ if ([_subject isKindOfClass:StrClass])
+ return [self stringForStringValue:_subject];
+ if ([_subject isKindOfClass:DataClass])
+ return [self stringForDataValue:_subject];
+
+ return [self stringForStringValue:[_subject stringValue]];
+}
+
+@end /* UIxSubjectFormatter */
.tableview {
font-size: 9pt;
font-family: Arial, Helvetica, Verdana, Geneva, Tahoma, sans-serif;
+ vertical-align: top;
}
-span.readmailsubject {
+td.tbtv_headercell {
+ border-width: 1;
+ border-style: solid;
+ border-top-color: white;
+ border-left-color: white;
+ border-bottom-color: #808080;
+ border-right-color: #808080;
+ padding-top: 4px;
+ padding-bottom: 3px;
+ padding-left: 4px;
+ padding-right: 4px;
+
+ background-color: #D4D0C8;
+}
+
+td.tbtv_headercell a {
+ margin: 0px auto;
+ display: block;
+ color: black;
}
-span.unreadmailsubject {
- font-weight: bold;
+td.tbtv_headercell a:hover {
+ margin: 0px auto;
+ display: block;
+ color: black;
+ text-decoration: none;
+ /* background-color: #C4C0B8; */
+}
+
+span.mailer_datefield {
+ white-space: nowrap;
+}
+
+div.mailer_readmailsubject {
+ /* TODO: use proper read icon */
+ background-image: url(tbtv_leaf_corner_17x17.gif);
+ background-repeat: no-repeat;
+ background-position: 0px 0px;
+ padding-top: 1px;
+ padding-left: 20px;
+}
+div.mailer_unreadmailsubject {
+ /* TODO: use proper unread icon */
+ background-image: url(tbtv_leaf_corner_17x17.gif);
+ background-repeat: no-repeat;
+ background-position: 0px 0px;
+ padding-left: 20px;
+ padding-top: 1px;
+ font-weight: bold;
+}
+div.mailer_readmailsubject a {
+ color: black;
+ text-decoration: none;
+}
+div.mailer_unreadmailsubject a {
+ color: black;
+ text-decoration: none;
+}
+
+div.mailer_readicon {
+ /* TODO: use Thunderbird icon */
+ background-image: url(icon_read.gif);
+ background-repeat: no-repeat;
+ background-position: 0px 4px;
+}
+div.mailer_readicon a {
+ width: 17px;
+ height: 17px;
+ margin: 0px auto;
+ display: block;
+}
+div.mailer_unreadicon {
+ /* TODO: use Thunderbird icon */
+ background-image: url(icon_unread.gif);
+ background-repeat: no-repeat;
+ background-position: 0px 4px;
+}
+div.mailer_unreadicon a {
+ width: 17px;
+ height: 17px;
+ margin: 0px auto;
+ display: block;
}
"tbtb_replyall.png",
"tbtb_search.png",
"tbtb_trash.png",
+
+ "icon_mark_flagged.gif",
+ "icon_mark_read.gif",
+ "icon_mark_unflagged.gif",
+ "icon_mark_unread.gif",
+ "icon_read.gif",
+ "icon_unread.gif",
"tbird_073_accountview.png",
"tbird_073_compose.png",
protectedBy = "View";
pageName = "UIxMailListView";
};
+
+ markMessageUnread = {
+ protectedBy = "View";
+ pageName = "UIxMailListView";
+ actionName = "markMessageUnread";
+ };
+ markMessageRead = {
+ protectedBy = "View";
+ pageName = "UIxMailListView";
+ actionName = "markMessageRead";
+ };
};
};