Monotone-Parent: e8bfb90da0079ca2c3e5079818a7e7532b26ea31

Monotone-Revision: 7d281c6a5f9cd3517f1a818c99070b3bba45594c

Monotone-Author: wsourdeau@inverse.ca
Monotone-Date: 2007-07-27T20:03:05
Monotone-Branch: ca.inverse.sogo
This commit is contained in:
Wolfgang Sourdeau 2007-07-27 20:03:05 +00:00
parent 55bd55f9ae
commit e47039bb31
6 changed files with 351 additions and 74 deletions

View file

@ -1,5 +1,9 @@
2007-07-27 Wolfgang Sourdeau <wsourdeau@inverse.ca> 2007-07-27 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* UI/MailerUI/UIxMailEditor.m ([UIxMailEditor -saveAction]): added
code to save the attached filenames with the filename returned by
the web server as well as their mime types.
* UI/MailPartViewers/UIxMailPartMessageViewer.m ([UIxMailPartMessageViewer -fromAddresses]) * UI/MailPartViewers/UIxMailPartMessageViewer.m ([UIxMailPartMessageViewer -fromAddresses])
([UIxMailPartMessageViewer -toAddresses]) ([UIxMailPartMessageViewer -toAddresses])
([UIxMailPartMessageViewer -ccAddresses]): new methods returning ([UIxMailPartMessageViewer -ccAddresses]): new methods returning

View file

@ -2,7 +2,7 @@
( /* first group */ ( /* first group */
{ link = "#"; { link = "#";
isSafe = NO; isSafe = NO;
onclick = "clickedEditorSend(this);return false;"; onclick = "return clickedEditorSend(this);";
image = "tb-compose-send-flat-24x24.png"; image = "tb-compose-send-flat-24x24.png";
cssClass = "tbicon_send"; cssClass = "tbicon_send";
label = "Send"; }, label = "Send"; },
@ -13,13 +13,13 @@
label = "Contacts"; }, label = "Contacts"; },
{ link = "#"; { link = "#";
isSafe = NO; isSafe = NO;
onclick = "clickedEditorAttach(this)"; onclick = "return clickedEditorAttach(this)";
image = "tb-compose-attach-flat-24x24.png"; image = "tb-compose-attach-flat-24x24.png";
cssClass = "tbicon_attach"; cssClass = "tbicon_attach";
label = "Attach"; }, label = "Attach"; },
{ link = "#"; { link = "#";
isSafe = NO; isSafe = NO;
onclick = "clickedEditorSave(this);return false;"; onclick = "return clickedEditorSave(this);";
image = "tb-mail-file-flat-24x24.png"; image = "tb-mail-file-flat-24x24.png";
cssClass = "tbicon_save"; cssClass = "tbicon_save";
label = "Save"; }, label = "Save"; },

View file

@ -25,14 +25,17 @@
#import <Foundation/NSUserDefaults.h> #import <Foundation/NSUserDefaults.h>
#import <NGObjWeb/WORequest.h> #import <NGObjWeb/WORequest.h>
#import <NGMail/NGMimeMessage.h>
#import <NGMail/NGMimeMessageGenerator.h>
#import <NGObjWeb/SoSubContext.h> #import <NGObjWeb/SoSubContext.h>
#import <NGObjWeb/NSException+HTTP.h> #import <NGObjWeb/NSException+HTTP.h>
#import <NGExtensions/NSNull+misc.h> #import <NGExtensions/NSNull+misc.h>
#import <NGExtensions/NSObject+Logs.h> #import <NGExtensions/NSObject+Logs.h>
#import <NGExtensions/NSString+misc.h> #import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSException+misc.h> #import <NGExtensions/NSException+misc.h>
#import <NGMail/NGMimeMessage.h>
#import <NGMail/NGMimeMessageGenerator.h>
#import <NGMime/NGMimeBodyPart.h>
#import <NGMime/NGMimeHeaderFields.h>
#import <NGMime/NGMimeMultipartBody.h>
#import <SoObjects/Mailer/SOGoDraftObject.h> #import <SoObjects/Mailer/SOGoDraftObject.h>
#import <SoObjects/Mailer/SOGoMailFolder.h> #import <SoObjects/Mailer/SOGoMailFolder.h>
@ -77,12 +80,13 @@ static BOOL useLocationBasedSentFolder = NO;
static NSDictionary *internetMailHeaders = nil; static NSDictionary *internetMailHeaders = nil;
static NSArray *infoKeys = nil; static NSArray *infoKeys = nil;
+ (void)initialize { + (void) initialize
{
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
infoKeys = [[NSArray alloc] initWithObjects: infoKeys = [[NSArray alloc] initWithObjects:
@"subject", @"text", @"to", @"cc", @"bcc", @"subject", @"text", @"to", @"cc", @"bcc",
@"from", @"replyTo", @"from", @"replyTo",
nil]; nil];
keepMailTmpFile = [ud boolForKey:@"SOGoMailEditorKeepTmpFile"]; keepMailTmpFile = [ud boolForKey:@"SOGoMailEditorKeepTmpFile"];
@ -95,18 +99,18 @@ static NSArray *infoKeys = nil;
/* Internet mail settings */ /* Internet mail settings */
showInternetMarker = [ud boolForKey:@"SOGoShowInternetMarker"]; showInternetMarker = [ud boolForKey:@"SOGoShowInternetMarker"];
if (!showInternetMarker) { if (!showInternetMarker)
NSLog(@"Note: visual Internet marker on mail editor disabled " NSLog(@"Note: visual Internet marker on mail editor disabled "
@"(SOGoShowInternetMarker)"); @"(SOGoShowInternetMarker)");
}
internetMailHeaders = internetMailHeaders =
[[ud dictionaryForKey:@"SOGoInternetMailHeaders"] copy]; [[ud dictionaryForKey:@"SOGoInternetMailHeaders"] copy];
NSLog(@"Note: specified %d headers for mails send via the Internet.", NSLog (@"Note: specified %d headers for mails send via the Internet.",
[internetMailHeaders count]); [internetMailHeaders count]);
} }
- (void)dealloc { - (void) dealloc
{
[sentFolder release]; [sentFolder release];
[fromEMails release]; [fromEMails release];
[from release]; [from release];
@ -123,68 +127,94 @@ static NSArray *infoKeys = nil;
/* accessors */ /* accessors */
- (void)setFrom:(NSString *)_value { - (void) setFrom: (NSString *) _value
{
ASSIGNCOPY(from, _value); ASSIGNCOPY(from, _value);
} }
- (NSString *)from {
- (NSString *) from
{
if (![from isNotEmpty]) if (![from isNotEmpty])
return [[[self context] activeUser] primaryEmail]; return [[[self context] activeUser] primaryEmail];
return from; return from;
} }
- (void)setReplyTo:(NSString *)_ignore { - (void) setReplyTo: (NSString *) _ignore
{
} }
- (NSString *)replyTo {
- (NSString *) replyTo
{
/* we are here for future extensibility */ /* we are here for future extensibility */
return @""; return @"";
} }
- (void)setSubject:(NSString *)_value { - (void) setSubject: (NSString *) _value
{
ASSIGNCOPY(subject, _value); ASSIGNCOPY(subject, _value);
} }
- (NSString *)subject {
- (NSString *) subject
{
return subject ? subject : @""; return subject ? subject : @"";
} }
- (void)setText:(NSString *)_value { - (void) setText: (NSString *) _value
{
ASSIGNCOPY(text, _value); ASSIGNCOPY(text, _value);
} }
- (NSString *)text {
- (NSString *) text
{
return [text isNotNull] ? text : @""; return [text isNotNull] ? text : @"";
} }
- (void)setTo:(NSArray *)_value { - (void) setTo: (NSArray *)_value
{
ASSIGNCOPY(to, _value); ASSIGNCOPY(to, _value);
} }
- (NSArray *)to {
- (NSArray *) to
{
return [to isNotNull] ? to : [NSArray array]; return [to isNotNull] ? to : [NSArray array];
} }
- (void)setCc:(NSArray *)_value { - (void) setCc: (NSArray *) _value
{
ASSIGNCOPY(cc, _value); ASSIGNCOPY(cc, _value);
} }
- (NSArray *)cc {
- (NSArray *) cc
{
return [cc isNotNull] ? cc : [NSArray array]; return [cc isNotNull] ? cc : [NSArray array];
} }
- (void)setBcc:(NSArray *)_value { - (void) setBcc: (NSArray *) _value
{
ASSIGNCOPY(bcc, _value); ASSIGNCOPY(bcc, _value);
} }
- (NSArray *)bcc {
- (NSArray *) bcc
{
return [bcc isNotNull] ? bcc : [NSArray array]; return [bcc isNotNull] ? bcc : [NSArray array];
} }
- (BOOL)hasOneOrMoreRecipients { - (BOOL) hasOneOrMoreRecipients
{
if ([[self to] count] > 0) return YES; if ([[self to] count] > 0) return YES;
if ([[self cc] count] > 0) return YES; if ([[self cc] count] > 0) return YES;
if ([[self bcc] count] > 0) return YES; if ([[self bcc] count] > 0) return YES;
return NO; return NO;
} }
- (void)setAttachmentName:(NSString *)_attachmentName { - (void) setAttachmentName: (NSString *) _attachmentName
{
ASSIGN(attachmentName, _attachmentName); ASSIGN(attachmentName, _attachmentName);
} }
- (NSString *)attachmentName {
- (NSString *) attachmentName
{
return attachmentName; return attachmentName;
} }
@ -403,23 +433,96 @@ static NSArray *infoKeys = nil;
/* actions */ /* actions */
- (BOOL)_saveFormInfo { - (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody
NSDictionary *info; {
NSMutableDictionary *filenames;
if ((info = [self storeInfo]) != nil) { NSDictionary *attachment;
NSException *error; NSArray *parts;
unsigned int count, max;
if ((error = [[self clientObject] storeInfo:info]) != nil) { NGMimeBodyPart *part;
[self errorWithFormat:@"failed to store draft: %@", error]; NGMimeContentDispositionHeaderField *header;
// TODO: improve error handling NSString *mimeType;
return NO;
parts = [httpBody parts];
max = [parts count];
filenames = [NSMutableDictionary dictionaryWithCapacity: max];
for (count = 0; count < max; count++)
{
part = [parts objectAtIndex: count];
header = [part headerForKey: @"content-disposition"];
mimeType = [[part headerForKey: @"content-type"] stringValue];
attachment = [NSDictionary dictionaryWithObjectsAndKeys:
[header filename], @"filename",
mimeType, @"mime-type", nil];
[filenames setObject: attachment
forKey: [header name]];
} }
}
return filenames;
}
- (BOOL) _saveAttachments
{
WORequest *request;
NSEnumerator *allKeys;
NSString *key;
BOOL success;
NSDictionary *filenames;
id httpBody;
SOGoDraftObject *co;
success = YES;
request = [context request];
httpBody = [[request httpRequest] body];
filenames = [self _scanAttachmentFilenamesInRequest: httpBody];
co = [self clientObject];
allKeys = [[request formValueKeys] objectEnumerator];
key = [allKeys nextObject];
while (key && success)
{
if ([key hasPrefix: @"attachment"])
success
= (![co saveAttachment: (NSData *) [request formValueForKey: key]
withMetadata: [filenames objectForKey: key]]);
key = [allKeys nextObject];
}
return success;
}
- (BOOL) _saveFormInfo
{
NSDictionary *info;
NSException *error;
BOOL success;
success = YES;
if ([self _saveAttachments])
{
info = [self storeInfo];
if (info)
{
error = [[self clientObject] storeInfo:info];
if (error)
{
[self errorWithFormat:@"failed to store draft: %@", error];
// TODO: improve error handling
success = NO;
}
}
}
else
success = NO;
// TODO: wrap content // TODO: wrap content
return YES; return success;
} }
- (id)failedToSaveFormResponse { - (id)failedToSaveFormResponse {
// TODO: improve error handling // TODO: improve error handling
return [NSException exceptionWithHTTPStatus:500 /* server error */ return [NSException exceptionWithHTTPStatus:500 /* server error */
@ -443,23 +546,8 @@ static NSArray *infoKeys = nil;
return [[self attachmentNames] count] > 0 ? YES : NO; return [[self attachmentNames] count] > 0 ? YES : NO;
} }
- (NSString *)initialLeftsideStyle { - (id) defaultAction
if ([self hasAttachments]) {
return @"width: 67%";
return @"width: 100%";
}
- (NSString *)initialRightsideStyle {
if ([self hasAttachments])
return @"display: block";
return @"display: none";
}
- (id)defaultAction {
return [self redirectToLocation:@"edit"];
}
- (id)editAction {
#if 0 #if 0
[self logWithFormat:@"edit action, load content from: %@", [self logWithFormat:@"edit action, load content from: %@",
[self clientObject]]; [self clientObject]];

View file

@ -9,16 +9,27 @@
className="UIxPageFrame" className="UIxPageFrame"
title="panelTitle" title="panelTitle"
const:popup="YES"> const:popup="YES">
<form name="pageform"> <div class="menu" id="attachmentsMenu">
<ul>
<li><var:string label:value="Open"/></li>
<li><var:string label:value="Delete" /></li>
<li><var:string label:value="Select All" /></li>
<li><!-- separator --></li>
<li><var:string label:value="Attach File(s)..." /></li>
<li><var:string label:value="Attach Web Page..." /></li>
</ul>
</div>
<form name="pageform" enctype="multipart/form-data">
<div id="headerArea"> <div id="headerArea">
<div id="attachmentsArea"> <div id="attachmentsArea">
<var:string label:value="Attachments:" /> <var:string label:value="Attachments:" />
<div id="compose_attachments_list" <ul id="attachments">
onclick="clickedEditorAttach(this);" <var:foreach list="attachmentNames" item="attachmentName"
><var:foreach list="attachmentNames" item="attachmentName"> ><img rsrc:img="attachment.gif"
<var:string value="attachmentName" /><br /> /><var:string value="attachmentName"
</var:foreach> /></var:foreach>
</div> </ul>
</div> </div>
<span class="headerField"><var:string label:value="From" />:</span> <span class="headerField"><var:string label:value="From" />:</span>
<var:popup const:name="from" <var:popup const:name="from"

View file

@ -43,14 +43,13 @@ DIV#subjectRow INPUT
width: 38em; width: 38em;
padding-left: .5em; } padding-left: .5em; }
div#compose_internetmarker { div#compose_internetmarker
padding: 8px; { padding: 8px;
text-align: center; text-align: center;
background-color: white; background-color: white;
border-color: red; border-color: red;
border-width: 2px; border-width: 2px;
border-style: solid; border-style: solid; }
}
div#headerArea div#headerArea
{ border-top: 1px solid #fff; { border-top: 1px solid #fff;
@ -66,11 +65,16 @@ div#attachmentsArea
padding-left: 5px; padding-left: 5px;
border-left: 1px solid #888; } border-left: 1px solid #888; }
input.currentAttachment
{ position: absolute;
top: 1em;
right: 1em; }
input.attachment
{ display: none; }
div#compose_attachments_list div#compose_attachments_list
{ { background-color: #ffffff;
width: 100%;
height: 10em;
background-color: #ffffff;
margin-left: 0px; margin-left: 0px;
padding: 2px; padding: 2px;
border-bottom: 1px solid #fff; border-bottom: 1px solid #fff;
@ -78,8 +82,32 @@ div#compose_attachments_list
border-top: 2px solid #222; border-top: 2px solid #222;
border-left: 2px solid #222; border-left: 2px solid #222;
-moz-border-top-colors: #9c9a94 #000 transparent; -moz-border-top-colors: #9c9a94 #000 transparent;
-moz-border-left-colors: #9c9a94 #000 transparent; }
UL#attachments
{ cursor: default;
margin: 0px;
padding: 0px;
width: 100%;
height: 10em;
border-bottom: 1px solid #fff;
border-right: 1px solid #fff;
border-top: 2px solid #222;
border-left: 2px solid #222;
background-color: #fff;
-moz-border-top-colors: #9c9a94 #000 transparent;
-moz-border-left-colors: #9c9a94 #000 transparent; -moz-border-left-colors: #9c9a94 #000 transparent;
} list-style-type: none;
list-style-image: none;
overflow: auto;
overflow-x: hidden; }
UL#attachments LI
{ white-space: nowrap;
padding-bottom: 1px; }
UL#attachments LI IMG
{ vertical-align: bottom; }
DIV.pageContent TEXTAREA DIV.pageContent TEXTAREA
{ position: absolute; { position: absolute;
@ -87,4 +115,3 @@ DIV.pageContent TEXTAREA
right: 0em; right: 0em;
bottom: 0em; bottom: 0em;
top: 17em; } top: 17em; }

View file

@ -120,3 +120,150 @@ function updateInlineAttachmentList(sender, attachments) {
else else
div.style.display = ""; div.style.display = "";
} }
/* mail editor */
function validateEditorInput(sender) {
var errortext = "";
var field;
field = document.pageform.subject;
if (field.value == "")
errortext = errortext + labels.error_missingsubject + "\n";
if (!UIxRecipientSelectorHasRecipients())
errortext = errortext + labels.error_missingrecipients + "\n";
if (errortext.length > 0) {
alert(labels.error_validationfailed.decodeEntities() + ":\n"
+ errortext.decodeEntities());
return false;
}
return true;
}
function clickedEditorSend(sender) {
if (!validateEditorInput(sender))
return false;
document.pageform.action = "send";
document.pageform.submit();
window.alert("cocou");
return false;
}
function clickedEditorAttach(sender) {
var area = $("attachmentsArea");
area.setStyle({ display: "block" });
var inputs = area.getElementsByTagName("input");
var attachmentName = "attachment" + inputs.length;
var newAttachment = createElement("input", attachmentName,
"currentAttachment", null,
{ type: "file",
name: attachmentName },
area);
Event.observe(newAttachment, "change",
onAttachmentChange.bindAsEventListener(newAttachment));
return false;
}
function onAddAttachment() {
var area = $("attachmentsArea");
var inputs = area.getElementsByTagName("input");
var attachmentName = "attachment" + inputs.length;
var newAttachment = createElement("input", attachmentName,
"currentAttachment", null,
{ type: "file",
name: attachmentName },
area);
Event.observe(newAttachment, "change",
onAttachmentChange.bindAsEventListener(newAttachment));
}
function onAttachmentChange(event) {
if (this.value == "")
this.parentNode.removeChild(this);
else {
this.addClassName("attachment");
this.removeClassName("currentAttachment");
var list = $("attachments");
createAttachment(this, list);
}
}
function createAttachment(node, list) {
var attachment = createElement("li", null, null, { node: node }, null, list);
createElement("img", null, null, { src: ResourcesURL + "/attachment.gif" },
null, attachment);
Event.observe(attachment, "click", onRowClick);
var filename = node.value;
var separator;
if (navigator.appVersion.indexOf("Windows") > -1)
separator = "\\";
else
separator = "/";
var fileArray = filename.split(separator);
var attachmentName = document.createTextNode(fileArray[fileArray.length-1]);
attachment.appendChild(attachmentName);
}
function clickedEditorSave(sender) {
document.pageform.action = "save";
document.pageform.submit();
refreshOpener();
return false;
}
function clickedEditorDelete(sender) {
document.pageform.action = "delete";
document.pageform.submit();
refreshOpener();
window.close();
return false;
}
function initMailEditor() {
var list = $("attachments");
$(list).attachMenu("attachmentsMenu");
var elements = list.childNodesWithTag("li");
for (var i = 0; i < elements.length; i++)
Event.observe(elements[i], "click",
onRowClick.bindAsEventListener(elements[i]));
}
function getMenus() {
return { "attachmentsMenu": new Array(null, onRemoveAttachments,
onSelectAllAttachments,
"-",
onAddAttachment, null) };
}
function onRemoveAttachments() {
var list = $("attachments");
var nodes = list.getSelectedNodes();
for (var i = nodes.length-1; i > -1; i--) {
var input = $(nodes[i]).node;
if (input) {
input.parentNode.removeChild(input);
list.removeChild(nodes[i]);
}
else
window.alert("Server attachments not handled");
}
}
function onSelectAllAttachments() {
var list = $("attachments");
var nodes = list.childNodesWithTag("li");
for (var i = 0; i < nodes.length; i++)
nodes[i].select();
}
window.addEventListener("load", initMailEditor, false);