Monotone-Parent: 025330d36e1af8f073a0b0a02b64a65e36da2ffb

Monotone-Revision: 62c54dca797fd3a058e6792e05ab111266735658

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2007-11-05T16:37:17
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Wolfgang Sourdeau 2007-11-05 16:37:17 +00:00
parent dcd79d986a
commit cc21c0c948
23 changed files with 193 additions and 241 deletions

View File

@ -1,3 +1,39 @@
2007-11-05 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/MailerUI/WOContext+UIxMailer.m ([-mailSubjectFormatter]):
removed method.
* UI/MailerUI/UIxMailView.m ([UIxMailView -messageSubject]): new
method replacing -objectTitle, written like the ones below.
* UI/MailerUI/UIxMailListView.m ([UIxMailListView
-messageSubject]): rewrote method to act the one below.
* UI/MailPartViewers/UIxMailPartMessageViewer.m
([UIxMailPartMessageViewer -messageSubject]): new method that
returns the decoded mail subject or "Untitled" if the subject is
empty.
* SoObjects/Mailer/SOGoMailObject.m ([SOGoMailObject
-decodedSubject]): new method that invoke -decodedSubject from the
NSString and NSData class, depending on the identity of the
subject obtained from the envelope.
* SoObjects/Mailer/SOGoMailObject+Draft.m ([SOGoMailObject
-subjectForReply]): take the value of -[SOGoMail decodedSubject]
instead of the subject of the envelope.
([SOGoMailObject -filenameForForward]): same as above.
([SOGoMailObject -subjectForForward]): same as above.
* SoObjects/Mailer/NSString+Mail.m ([NSString -decodedSubject]):
same as below for NSString.
* SoObjects/Mailer/NSData+Mail.m ([NSData -decodedSubject]): new
method that decodes the subject of a mail taken as an NSData
instance. Handling the exceptions and the badly formatted headers.
* UI/MailerUI/UIxSubjectFormatter.[hm]: dropped class module.
2007-11-04 Wolfgang Sourdeau <wsourdeau@inverse.ca> 2007-11-04 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* SoObjects/SOGo/SOGoFolder.m ([SOGoFolder * SoObjects/SOGo/SOGoFolder.m ([SOGoFolder

View File

@ -52,4 +52,50 @@
return decodedData; return decodedData;
} }
- (NSString *) decodedSubject
{
const char *cData, *endFlag;
unsigned int len;
NSString *converted, *decodedSubject;
cData = [self bytes];
len = [self length];
if (len)
{
decodedSubject = nil;
if (len > 6)
{
endFlag = cData + len - 2;
if (*cData == '=' && *(cData + 1) == '?'
&& *endFlag == '?' && *(endFlag + 1) == '=')
{
converted
= [[NSString alloc] initWithData: self
encoding: NSASCIIStringEncoding];
if (converted)
{
[converted autorelease];
decodedSubject = [converted stringByDecodingQuotedPrintable];
}
}
}
if (!decodedSubject)
{
decodedSubject
= [[NSString alloc] initWithData: self
encoding: NSUTF8StringEncoding];
if (!decodedSubject)
decodedSubject
= [[NSString alloc] initWithData: self
encoding: NSISOLatin1StringEncoding];
[decodedSubject autorelease];
}
}
else
decodedSubject = @"";
return decodedSubject;
}
@end @end

View File

@ -29,6 +29,8 @@
- (NSString *) htmlToText; - (NSString *) htmlToText;
- (NSString *) decodedSubject;
@end @end
#endif /* NSSTRING_MAIL_H */ #endif /* NSSTRING_MAIL_H */

View File

@ -28,6 +28,7 @@
#import <SaxObjC/SaxLexicalHandler.h> #import <SaxObjC/SaxLexicalHandler.h>
#import <SaxObjC/SaxXMLReader.h> #import <SaxObjC/SaxXMLReader.h>
#import <SaxObjC/SaxXMLReaderFactory.h> #import <SaxObjC/SaxXMLReaderFactory.h>
#import <NGExtensions/NGQuotedPrintableCoding.h>
#import <NGExtensions/NSString+misc.h> #import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSObject+Logs.h> #import <NGExtensions/NSObject+Logs.h>
@ -348,4 +349,20 @@
return [handler result]; return [handler result];
} }
- (NSString *) decodedSubject
{
NSString *decodedSubject;
if ([self hasPrefix: @"=?"] && [self hasSuffix: @"?="])
{
decodedSubject = [self stringByDecodingQuotedPrintable];
if (!decodedSubject)
decodedSubject = self;
}
else
decodedSubject = self;
return decodedSubject;
}
@end @end

View File

@ -54,7 +54,7 @@
hasPrefix = NO; hasPrefix = NO;
subject = [[self envelope] subject]; subject = [self decodedSubject];
i = 0; i = 0;
while (!hasPrefix && replyPrefixes[i]) while (!hasPrefix && replyPrefixes[i])
if ([subject hasPrefix: replyPrefixes[i]]) if ([subject hasPrefix: replyPrefixes[i]])
@ -145,7 +145,7 @@
}; };
unsigned int count, length; unsigned int count, length;
subject = [[self envelope] subject]; subject = [self decodedSubject];
length = [subject length]; length = [subject length];
if (!length) if (!length)
{ {
@ -173,7 +173,7 @@
{ {
NSString *subject, *newSubject; NSString *subject, *newSubject;
subject = [[self envelope] subject]; subject = [self decodedSubject];
if ([subject length] > 0) if ([subject length] > 0)
newSubject = [NSString stringWithFormat: @"[Fwd: %@]", subject]; newSubject = [NSString stringWithFormat: @"[Fwd: %@]", subject];
else else

View File

@ -58,6 +58,7 @@
- (NGImap4Envelope *)envelope; - (NGImap4Envelope *)envelope;
- (NSString *)subject; - (NSString *)subject;
- (NSString *)decodedSubject;
- (NSCalendarDate *)date; - (NSCalendarDate *)date;
- (NSArray *)fromEnvelopeAddresses; - (NSArray *)fromEnvelopeAddresses;
- (NSArray *)toEnvelopeAddresses; - (NSArray *)toEnvelopeAddresses;

View File

@ -261,6 +261,11 @@ static BOOL debugSoParts = NO;
return [[self envelope] subject]; return [[self envelope] subject];
} }
- (NSString *) decodedSubject
{
return [[self subject] decodedSubject];
}
- (NSCalendarDate *) date - (NSCalendarDate *) date
{ {
NSTimeZone *userTZ; NSTimeZone *userTZ;

View File

@ -11,7 +11,11 @@ add_info_text = "iMIP 'ADD' requests are not yet supported by SOGo.";
publish_info_text = "The sender informs you of the attached event."; publish_info_text = "The sender informs you of the attached event.";
cancel_info_text = "Your invitation or the whole event was canceled."; cancel_info_text = "Your invitation or the whole event was canceled.";
request_info_no_attendee = "is proposing a meeting to the attendees. You receive this mail as a notification, you are not scheduled as a participant."; request_info_no_attendee = "is proposing a meeting to the attendees. You receive this mail as a notification, you are not scheduled as a participant.";
Organizer = "Organizer"; Appointment = "Appointment";
Organizer = "Organisateur";
Time = "Time";
Attendees = "Attendees";
request_info = "invites you to participate in a meeting."; request_info = "invites you to participate in a meeting.";
do_add_to_cal = "add to calendar"; do_add_to_cal = "add to calendar";
do_del_from_cal = "delete from calendar"; do_del_from_cal = "delete from calendar";
@ -21,3 +25,7 @@ do_tentative = "tentative";
do_update_status = "update status in calendar"; do_update_status = "update status in calendar";
reply_info_no_attendee = "You received a reply to a scheduling event but the sender of the reply is not a participant."; reply_info_no_attendee = "You received a reply to a scheduling event but the sender of the reply is not a participant.";
reply_info = "This is a reply to an event invitation done by you."; reply_info = "This is a reply to an event invitation done by you.";
"Untitled" = "Untitled";
"Size" = "Size";

View File

@ -26,4 +26,6 @@ do_update_status = "mettre l'agenda à jour";
reply_info_no_attendee = "Vous avez reçu une réponse à un événement mais l'expéditeur n'est pas un invité."; reply_info_no_attendee = "Vous avez reçu une réponse à un événement mais l'expéditeur n'est pas un invité.";
reply_info = "Ceci est une réponse à un événement que vous avez organisé."; reply_info = "Ceci est une réponse à un événement que vous avez organisé.";
"Untitled" = "Sans titre";
"Size" = "Taille"; "Size" = "Taille";

View File

@ -1,29 +1,31 @@
ACCEPTED = "Accepté"; ACCEPTED = "accepted";
COMPLETED = "Terminé"; COMPLETED = "completed";
DECLINED = "Refusé"; DECLINED = "declined";
DELEGATED = "Délégué"; DELEGATED = "delegated";
IN-PROCESS = "En cours de traitement"; IN-PROCESS = "in process";
NEEDS-ACTION = "Prise de décision nécessaire"; NEEDS-ACTION = "needs action";
TENTATIVE = "Proposition"; TENTATIVE = "tentative";
organized_by_you = "vous êtes l'organisateur"; organized_by_you = "organized by you";
you_are_an_attendee = "vous êtes invité"; you_are_an_attendee = "you are an attendee";
add_info_text = "iMIP 'ADD' requests are not yet supported by SOGo."; add_info_text = "iMIP 'ADD' requests are not yet supported by SOGo.";
publish_info_text = "L'expéditeur vous informe de l'événement attaché."; publish_info_text = "The sender informs you of the attached event.";
cancel_info_text = "Votre invitation ou l'événement au complet a été annulé."; cancel_info_text = "Your invitation or the whole event was canceled.";
request_info_no_attendee = "propose une réunion entre les invités. Ce message tint seulement lieu d'avis, vous n'êtes pas indiqué comme invité."; request_info_no_attendee = "is proposing a meeting to the attendees. You receive this mail as a notification, you are not scheduled as a participant.";
Appointment = "Événement"; Appointment = "Appointment";
Organizer = "Organisateur"; Organizer = "Organisateur";
Time = "Date"; Time = "Time";
Attendees = "Invités"; Attendees = "Attendees";
request_info = "vous invite à une réunion."; request_info = "invites you to participate in a meeting.";
do_add_to_cal = "ajouter à l'agenda"; do_add_to_cal = "add to calendar";
do_del_from_cal = "effacer de l'agenda"; do_del_from_cal = "delete from calendar";
do_accept = "accepter"; do_accept = "accept";
do_decline = "decliner"; do_decline = "decline";
do_tentative = "tentative"; do_tentative = "tentative";
do_update_status = "mettre l'agenda à jour"; do_update_status = "update status in calendar";
reply_info_no_attendee = "Vous avez reçu une réponse à un événement mais l'expéditeur n'est pas un invité."; reply_info_no_attendee = "You received a reply to a scheduling event but the sender of the reply is not a participant.";
reply_info = "Ceci est une réponse à un événement que vous avez organisé."; reply_info = "This is a reply to an event invitation done by you.";
"Size" = "Taille"; "Untitled" = "Untitled";
"Size" = "Size";

View File

@ -22,6 +22,9 @@
#import <NGImap4/NGImap4Envelope.h> #import <NGImap4/NGImap4Envelope.h>
#import <NGImap4/NGImap4EnvelopeAddress.h> #import <NGImap4/NGImap4EnvelopeAddress.h>
#import <SoObjects/Mailer/NSData+Mail.h>
#import <SoObjects/Mailer/NSString+Mail.h>
#import <UI/MailerUI/WOContext+UIxMailer.h> #import <UI/MailerUI/WOContext+UIxMailer.h>
#import "UIxMailRenderingContext.h" #import "UIxMailRenderingContext.h"
@ -144,6 +147,19 @@
return [formattedComponents componentsJoinedByString: @", "]; return [formattedComponents componentsJoinedByString: @", "];
} }
- (NSString *) messageSubject
{
id baseSubject;
NSString *subject;
baseSubject = [[self envelope] subject];
subject = [baseSubject decodedSubject];
if (![subject length])
subject = [self labelForKey: @"Untitled"];
return subject;
}
- (NSString *) fromAddresses - (NSString *) fromAddresses
{ {
NSArray *from; NSArray *from;

View File

@ -127,6 +127,8 @@
"Mark Unread" = "Mark Unread"; "Mark Unread" = "Mark Unread";
"Mark Read" = "Mark Read"; "Mark Read" = "Mark Read";
"Untitled" = "Untitled";
/* Tree */ /* Tree */
"SentFolderName" = "Sent"; "SentFolderName" = "Sent";

View File

@ -122,11 +122,13 @@
"next" = "Suivant"; "next" = "Suivant";
"last" = "Dernier"; "last" = "Dernier";
"msgnumber_to" = "à";
"msgnumber_of" = "de";
"Mark Unread" = "Marquer comme non lu"; "Mark Unread" = "Marquer comme non lu";
"Mark Read" = "Marquer comme lu"; "Mark Read" = "Marquer comme lu";
"msgnumber_to" = "à"; "Untitled" = "Sans titre";
"msgnumber_of" = "de";
/* Tree */ /* Tree */

View File

@ -12,7 +12,6 @@ MailerUI_OBJC_FILES += \
MailerUIProduct.m \ MailerUIProduct.m \
\ \
UIxMailFormatter.m \ UIxMailFormatter.m \
UIxSubjectFormatter.m \
UIxEnvelopeAddressFormatter.m \ UIxEnvelopeAddressFormatter.m \
WOContext+UIxMailer.m \ WOContext+UIxMailer.m \
\ \

View File

@ -105,11 +105,13 @@
"next" = "Nächste"; "next" = "Nächste";
"last" = "Letzter"; "last" = "Letzter";
"msgnumber_to" = "bis";
"msgnumber_of" = "von";
"Mark Unread" = "Als ungelesen markieren"; "Mark Unread" = "Als ungelesen markieren";
"Mark Read" = "Als gelesen markieren"; "Mark Read" = "Als gelesen markieren";
"msgnumber_to" = "bis"; "Untitled" = "Untitled";
"msgnumber_of" = "von";
/* Tree */ /* Tree */

View File

@ -63,32 +63,6 @@
@end @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;
}
/* configuration */
- (unsigned int)maxLength;
/* labels */
- (NSString *)missingSubjectLabel;
/* specific formatters */
- (NSString *)stringForStringValue:(NSString *)_subject;
- (NSString *)stringForDataValue:(NSData *)_subject;
@end
@interface UIxEnvelopeAddressFormatter : UIxMailFormatter @interface UIxEnvelopeAddressFormatter : UIxMailFormatter
{ {
NSString *separator; NSString *separator;

View File

@ -100,18 +100,13 @@
- (NSString *) messageSubject - (NSString *) messageSubject
{ {
id baseSubject;
NSString *subject; NSString *subject;
id envSubject;
envSubject = [[message valueForKey: @"envelope"] subject]; baseSubject = [[message valueForKey: @"envelope"] subject];
if ([envSubject isKindOfClass: [NSData class]]) subject = [baseSubject decodedSubject];
{ if (![subject length])
subject = [[NSString alloc] initWithData: envSubject subject = [self labelForKey: @"Untitled"];
encoding: NSUTF8StringEncoding];
[subject autorelease];
}
else
subject = envSubject;
return subject; return subject;
} }

View File

@ -88,16 +88,22 @@ static NSString *mailETag = nil;
return currentAddress; return currentAddress;
} }
- (NSString *) objectTitle - (NSString *) messageSubject
{ {
return [[self clientObject] subject]; NSString *subject;
subject = [[self clientObject] decodedSubject];
if (![subject length])
subject = [self labelForKey: @"Untitled"];
return subject;
} }
- (NSString *) panelTitle - (NSString *) panelTitle
{ {
return [NSString stringWithFormat: @"%@: %@", return [NSString stringWithFormat: @"%@: %@",
[self labelForKey: @"View Mail"], [self labelForKey: @"View Mail"],
[self objectTitle]]; [self messageSubject]];
} }
/* links (DUP to UIxMailPartViewer!) */ /* links (DUP to UIxMailPartViewer!) */

View File

@ -1,155 +0,0 @@
/*
Copyright (C) 2004-2005 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.
*/
#import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h>
#import <NGMail/NGMimeMessageParser.h>
#import "UIxMailFormatter.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];
else
[self warnWithFormat:@"decoding QP failed: '%@'", t];
}
}
/* continue NSData processing */
[self warnWithFormat:@"NSData subject, using UTF-8 to decode."];
// TODO: exception handler?
s = [[NSString alloc] initWithData:_subject encoding:NSUTF8StringEncoding];
if (s == nil) {
[self errorWithFormat:@"could do not decode NSData subject!"];
return [self labelForKey:@"Error_CouldNotDecodeSubject"];
}
if ([s hasPrefix:@"=?"]) { // TODO: this should never happen?
[self warnWithFormat:@"subject still has QP signature: '%@'", s];
r = [s copy];
}
else
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 */

View File

@ -32,12 +32,11 @@
@class NSFormatter; @class NSFormatter;
@interface WOContext(UIxMailer) @interface WOContext (UIxMailer)
- (NSFormatter *)mailSubjectFormatter; - (NSFormatter *) mailDateFormatter;
- (NSFormatter *)mailDateFormatter; - (NSFormatter *) mailEnvelopeAddressFormatter;
- (NSFormatter *)mailEnvelopeAddressFormatter; - (NSFormatter *) mailEnvelopeFullAddressFormatter;
- (NSFormatter *)mailEnvelopeFullAddressFormatter;
@end @end

View File

@ -33,11 +33,6 @@
// TODO: make configurable // TODO: make configurable
// TODO: cache! // TODO: cache!
- (NSFormatter *) mailSubjectFormatter
{
return [[[UIxSubjectFormatter alloc] init] autorelease];
}
- (NSFormatter *) mailDateFormatter - (NSFormatter *) mailDateFormatter
{ {
return [[self activeUser] dateFormatterInContext: self]; return [[self activeUser] dateFormatterInContext: self];

View File

@ -9,8 +9,7 @@
<!-- TODO: the table is a DUP to UIxMailView, own component? --> <!-- TODO: the table is a DUP to UIxMailView, own component? -->
<div class="bodyFields"> <div class="bodyFields">
<span class="fieldName"><var:string label:value="Subject"/>:</span> <span class="fieldName"><var:string label:value="Subject"/>:</span>
<var:string value="envelope.subject" <var:string value="messageSubject"/><br/>
formatter="context.mailSubjectFormatter"/><br/>
<span class="fieldName"><var:string label:value="From"/>:</span> <span class="fieldName"><var:string label:value="From"/>:</span>
<var:string value="fromAddresses"/><br/> <var:string value="fromAddresses"/><br/>
<span class="fieldName"><var:string label:value="Date"/>:</span> <span class="fieldName"><var:string label:value="Date"/>:</span>

View File

@ -13,8 +13,7 @@
<tr class="mailer_fieldrow"> <tr class="mailer_fieldrow">
<td class="mailer_fieldname" ><var:string label:value="Subject"/>:</td> <td class="mailer_fieldname" ><var:string label:value="Subject"/>:</td>
<td class="mailer_subjectfieldvalue"> <td class="mailer_subjectfieldvalue">
<var:string value="clientObject.subject" <var:string value="messageSubject"/>
formatter="context.mailSubjectFormatter"/>
</td> </td>
</tr> </tr>
<tr class="mailer_fieldrow"> <tr class="mailer_fieldrow">