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:
parent
55bd55f9ae
commit
e47039bb31
|
@ -1,5 +1,9 @@
|
|||
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])
|
||||
([UIxMailPartMessageViewer -toAddresses])
|
||||
([UIxMailPartMessageViewer -ccAddresses]): new methods returning
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
( /* first group */
|
||||
{ link = "#";
|
||||
isSafe = NO;
|
||||
onclick = "clickedEditorSend(this);return false;";
|
||||
onclick = "return clickedEditorSend(this);";
|
||||
image = "tb-compose-send-flat-24x24.png";
|
||||
cssClass = "tbicon_send";
|
||||
label = "Send"; },
|
||||
|
@ -13,13 +13,13 @@
|
|||
label = "Contacts"; },
|
||||
{ link = "#";
|
||||
isSafe = NO;
|
||||
onclick = "clickedEditorAttach(this)";
|
||||
onclick = "return clickedEditorAttach(this)";
|
||||
image = "tb-compose-attach-flat-24x24.png";
|
||||
cssClass = "tbicon_attach";
|
||||
label = "Attach"; },
|
||||
{ link = "#";
|
||||
isSafe = NO;
|
||||
onclick = "clickedEditorSave(this);return false;";
|
||||
onclick = "return clickedEditorSave(this);";
|
||||
image = "tb-mail-file-flat-24x24.png";
|
||||
cssClass = "tbicon_save";
|
||||
label = "Save"; },
|
||||
|
|
|
@ -25,14 +25,17 @@
|
|||
#import <Foundation/NSUserDefaults.h>
|
||||
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
#import <NGMail/NGMimeMessage.h>
|
||||
#import <NGMail/NGMimeMessageGenerator.h>
|
||||
#import <NGObjWeb/SoSubContext.h>
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+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/SOGoMailFolder.h>
|
||||
|
@ -77,12 +80,13 @@ static BOOL useLocationBasedSentFolder = NO;
|
|||
static NSDictionary *internetMailHeaders = nil;
|
||||
static NSArray *infoKeys = nil;
|
||||
|
||||
+ (void)initialize {
|
||||
+ (void) initialize
|
||||
{
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
infoKeys = [[NSArray alloc] initWithObjects:
|
||||
@"subject", @"text", @"to", @"cc", @"bcc",
|
||||
@"from", @"replyTo",
|
||||
@"from", @"replyTo",
|
||||
nil];
|
||||
|
||||
keepMailTmpFile = [ud boolForKey:@"SOGoMailEditorKeepTmpFile"];
|
||||
|
@ -95,18 +99,18 @@ static NSArray *infoKeys = nil;
|
|||
/* Internet mail settings */
|
||||
|
||||
showInternetMarker = [ud boolForKey:@"SOGoShowInternetMarker"];
|
||||
if (!showInternetMarker) {
|
||||
if (!showInternetMarker)
|
||||
NSLog(@"Note: visual Internet marker on mail editor disabled "
|
||||
@"(SOGoShowInternetMarker)");
|
||||
}
|
||||
|
||||
internetMailHeaders =
|
||||
[[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]);
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
- (void) dealloc
|
||||
{
|
||||
[sentFolder release];
|
||||
[fromEMails release];
|
||||
[from release];
|
||||
|
@ -123,68 +127,94 @@ static NSArray *infoKeys = nil;
|
|||
|
||||
/* accessors */
|
||||
|
||||
- (void)setFrom:(NSString *)_value {
|
||||
- (void) setFrom: (NSString *) _value
|
||||
{
|
||||
ASSIGNCOPY(from, _value);
|
||||
}
|
||||
- (NSString *)from {
|
||||
|
||||
- (NSString *) from
|
||||
{
|
||||
if (![from isNotEmpty])
|
||||
return [[[self context] activeUser] primaryEmail];
|
||||
|
||||
return from;
|
||||
}
|
||||
|
||||
- (void)setReplyTo:(NSString *)_ignore {
|
||||
- (void) setReplyTo: (NSString *) _ignore
|
||||
{
|
||||
}
|
||||
- (NSString *)replyTo {
|
||||
|
||||
- (NSString *) replyTo
|
||||
{
|
||||
/* we are here for future extensibility */
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (void)setSubject:(NSString *)_value {
|
||||
- (void) setSubject: (NSString *) _value
|
||||
{
|
||||
ASSIGNCOPY(subject, _value);
|
||||
}
|
||||
- (NSString *)subject {
|
||||
|
||||
- (NSString *) subject
|
||||
{
|
||||
return subject ? subject : @"";
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)_value {
|
||||
- (void) setText: (NSString *) _value
|
||||
{
|
||||
ASSIGNCOPY(text, _value);
|
||||
}
|
||||
- (NSString *)text {
|
||||
|
||||
- (NSString *) text
|
||||
{
|
||||
return [text isNotNull] ? text : @"";
|
||||
}
|
||||
|
||||
- (void)setTo:(NSArray *)_value {
|
||||
- (void) setTo: (NSArray *)_value
|
||||
{
|
||||
ASSIGNCOPY(to, _value);
|
||||
}
|
||||
- (NSArray *)to {
|
||||
|
||||
- (NSArray *) to
|
||||
{
|
||||
return [to isNotNull] ? to : [NSArray array];
|
||||
}
|
||||
|
||||
- (void)setCc:(NSArray *)_value {
|
||||
- (void) setCc: (NSArray *) _value
|
||||
{
|
||||
ASSIGNCOPY(cc, _value);
|
||||
}
|
||||
- (NSArray *)cc {
|
||||
|
||||
- (NSArray *) cc
|
||||
{
|
||||
return [cc isNotNull] ? cc : [NSArray array];
|
||||
}
|
||||
|
||||
- (void)setBcc:(NSArray *)_value {
|
||||
- (void) setBcc: (NSArray *) _value
|
||||
{
|
||||
ASSIGNCOPY(bcc, _value);
|
||||
}
|
||||
- (NSArray *)bcc {
|
||||
|
||||
- (NSArray *) bcc
|
||||
{
|
||||
return [bcc isNotNull] ? bcc : [NSArray array];
|
||||
}
|
||||
|
||||
- (BOOL)hasOneOrMoreRecipients {
|
||||
- (BOOL) hasOneOrMoreRecipients
|
||||
{
|
||||
if ([[self to] count] > 0) return YES;
|
||||
if ([[self cc] count] > 0) return YES;
|
||||
if ([[self bcc] count] > 0) return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)setAttachmentName:(NSString *)_attachmentName {
|
||||
- (void) setAttachmentName: (NSString *) _attachmentName
|
||||
{
|
||||
ASSIGN(attachmentName, _attachmentName);
|
||||
}
|
||||
- (NSString *)attachmentName {
|
||||
|
||||
- (NSString *) attachmentName
|
||||
{
|
||||
return attachmentName;
|
||||
}
|
||||
|
||||
|
@ -403,23 +433,96 @@ static NSArray *infoKeys = nil;
|
|||
|
||||
/* actions */
|
||||
|
||||
- (BOOL)_saveFormInfo {
|
||||
NSDictionary *info;
|
||||
|
||||
if ((info = [self storeInfo]) != nil) {
|
||||
NSException *error;
|
||||
|
||||
if ((error = [[self clientObject] storeInfo:info]) != nil) {
|
||||
[self errorWithFormat:@"failed to store draft: %@", error];
|
||||
// TODO: improve error handling
|
||||
return NO;
|
||||
- (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody
|
||||
{
|
||||
NSMutableDictionary *filenames;
|
||||
NSDictionary *attachment;
|
||||
NSArray *parts;
|
||||
unsigned int count, max;
|
||||
NGMimeBodyPart *part;
|
||||
NGMimeContentDispositionHeaderField *header;
|
||||
NSString *mimeType;
|
||||
|
||||
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
|
||||
|
||||
return YES;
|
||||
return success;
|
||||
}
|
||||
|
||||
- (id)failedToSaveFormResponse {
|
||||
// TODO: improve error handling
|
||||
return [NSException exceptionWithHTTPStatus:500 /* server error */
|
||||
|
@ -443,23 +546,8 @@ static NSArray *infoKeys = nil;
|
|||
return [[self attachmentNames] count] > 0 ? YES : NO;
|
||||
}
|
||||
|
||||
- (NSString *)initialLeftsideStyle {
|
||||
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 {
|
||||
- (id) defaultAction
|
||||
{
|
||||
#if 0
|
||||
[self logWithFormat:@"edit action, load content from: %@",
|
||||
[self clientObject]];
|
||||
|
|
|
@ -9,16 +9,27 @@
|
|||
className="UIxPageFrame"
|
||||
title="panelTitle"
|
||||
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="attachmentsArea">
|
||||
<var:string label:value="Attachments:" />
|
||||
<div id="compose_attachments_list"
|
||||
onclick="clickedEditorAttach(this);"
|
||||
><var:foreach list="attachmentNames" item="attachmentName">
|
||||
<var:string value="attachmentName" /><br />
|
||||
</var:foreach>
|
||||
</div>
|
||||
<ul id="attachments">
|
||||
<var:foreach list="attachmentNames" item="attachmentName"
|
||||
><img rsrc:img="attachment.gif"
|
||||
/><var:string value="attachmentName"
|
||||
/></var:foreach>
|
||||
</ul>
|
||||
</div>
|
||||
<span class="headerField"><var:string label:value="From" />:</span>
|
||||
<var:popup const:name="from"
|
||||
|
|
|
@ -43,14 +43,13 @@ DIV#subjectRow INPUT
|
|||
width: 38em;
|
||||
padding-left: .5em; }
|
||||
|
||||
div#compose_internetmarker {
|
||||
padding: 8px;
|
||||
div#compose_internetmarker
|
||||
{ padding: 8px;
|
||||
text-align: center;
|
||||
background-color: white;
|
||||
border-color: red;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
}
|
||||
border-style: solid; }
|
||||
|
||||
div#headerArea
|
||||
{ border-top: 1px solid #fff;
|
||||
|
@ -66,11 +65,16 @@ div#attachmentsArea
|
|||
padding-left: 5px;
|
||||
border-left: 1px solid #888; }
|
||||
|
||||
input.currentAttachment
|
||||
{ position: absolute;
|
||||
top: 1em;
|
||||
right: 1em; }
|
||||
|
||||
input.attachment
|
||||
{ display: none; }
|
||||
|
||||
div#compose_attachments_list
|
||||
{
|
||||
width: 100%;
|
||||
height: 10em;
|
||||
background-color: #ffffff;
|
||||
{ background-color: #ffffff;
|
||||
margin-left: 0px;
|
||||
padding: 2px;
|
||||
border-bottom: 1px solid #fff;
|
||||
|
@ -78,8 +82,32 @@ div#compose_attachments_list
|
|||
border-top: 2px solid #222;
|
||||
border-left: 2px solid #222;
|
||||
-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;
|
||||
}
|
||||
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
|
||||
{ position: absolute;
|
||||
|
@ -87,4 +115,3 @@ DIV.pageContent TEXTAREA
|
|||
right: 0em;
|
||||
bottom: 0em;
|
||||
top: 17em; }
|
||||
|
||||
|
|
|
@ -120,3 +120,150 @@ function updateInlineAttachmentList(sender, attachments) {
|
|||
else
|
||||
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);
|
||||
|
|
Loading…
Reference in a new issue