Add links to download one or all attachments

Also removed the contextual menu over file attachments and changed the
label color when moving over the file attachments.
This commit is contained in:
Francis Lachapelle 2013-12-20 15:37:01 -05:00
parent dc21c723f6
commit 5f369f201d
11 changed files with 172 additions and 74 deletions

1
NEWS
View file

@ -6,6 +6,7 @@ New features
using SOGoCalendarDefaultReminder using SOGoCalendarDefaultReminder
- select multiple files to attach to a message or drag'n'drop files onto the - select multiple files to attach to a message or drag'n'drop files onto the
mail editor; will also now display progress of uploads mail editor; will also now display progress of uploads
- new popup menu to download all attachments of a mail
Enhancements Enhancements
- we now automatically convert <img src=data...> into file attachments - we now automatically convert <img src=data...> into file attachments

View file

@ -97,11 +97,11 @@
"Reply-To" = "Reply-To"; "Reply-To" = "Reply-To";
"Add address" = "Add address"; "Add address" = "Add address";
"Attachments:" = "Attachments:";
"Open" = "Open"; "Open" = "Open";
"Select All" = "Select All"; "Select All" = "Select All";
"Attach Web Page..." = "Attach Web Page..."; "Attach Web Page..." = "Attach Web Page...";
"Attach File(s)..." = "Attach File(s)..."; "file" = "file";
"files" = "files";
"to" = "To"; "to" = "To";
"cc" = "Cc"; "cc" = "Cc";

View file

@ -163,6 +163,11 @@ static NSArray *infoKeys = nil;
return item; return item;
} }
- (NSString *) uid
{
return [[self clientObject] nameInContainer];
}
- (NSArray *) priorityClasses - (NSArray *) priorityClasses
{ {
static NSArray *priorities = nil; static NSArray *priorities = nil;
@ -382,16 +387,16 @@ static NSArray *infoKeys = nil;
ASSIGN (attachment, newAttachment); ASSIGN (attachment, newAttachment);
} }
- (NSFormatter *) sizeFormatter
{
return [UIxMailSizeFormatter sharedMailSizeFormatter];
}
- (NSDictionary *) attachment - (NSDictionary *) attachment
{ {
return attachment; return attachment;
} }
- (NSFormatter *) sizeFormatter
{
return [UIxMailSizeFormatter sharedMailSizeFormatter];
}
/* from addresses */ /* from addresses */
- (NSArray *) fromEMails - (NSArray *) fromEMails
@ -672,11 +677,6 @@ static NSArray *infoKeys = nil;
return [[self attachmentAttrs] count] > 0 ? YES : NO; return [[self attachmentAttrs] count] > 0 ? YES : NO;
} }
- (NSString *) uid
{
return [[self clientObject] nameInContainer];
}
- (id) defaultAction - (id) defaultAction
{ {
SOGoDraftObject *co; SOGoDraftObject *co;

View file

@ -47,11 +47,12 @@
#import <SOGo/SOGoUser.h> #import <SOGo/SOGoUser.h>
#import <SOGo/SOGoUserDefaults.h> #import <SOGo/SOGoUserDefaults.h>
#import <SOGo/SOGoUserManager.h> #import <SOGo/SOGoUserManager.h>
#import <SOGoUI/UIxComponent.h>
#import <Mailer/SOGoMailObject.h> #import <Mailer/SOGoMailObject.h>
#import <Mailer/SOGoMailAccount.h> #import <Mailer/SOGoMailAccount.h>
#import <Mailer/SOGoMailFolder.h> #import <Mailer/SOGoMailFolder.h>
#import <SOGoUI/UIxComponent.h>
#import <MailPartViewers/UIxMailRenderingContext.h> // cyclic #import <MailPartViewers/UIxMailRenderingContext.h> // cyclic
#import <MailPartViewers/UIxMailSizeFormatter.h>
#import "WOContext+UIxMailer.h" #import "WOContext+UIxMailer.h"
@ -60,6 +61,8 @@
id currentAddress; id currentAddress;
NSString *shouldAskReceipt; NSString *shouldAskReceipt;
NSString *matchingIdentityEMail; NSString *matchingIdentityEMail;
NSDictionary *attachment;
NSArray *attachmentAttrs;
} }
@end @end
@ -80,6 +83,8 @@ static NSString *mailETag = nil;
- (void) dealloc - (void) dealloc
{ {
[matchingIdentityEMail release]; [matchingIdentityEMail release];
[attachment release];
[attachmentAttrs release];
[super dealloc]; [super dealloc];
} }
@ -111,6 +116,16 @@ static NSString *mailETag = nil;
[self messageSubject]]; [self messageSubject]];
} }
- (void) setAttachment: (NSDictionary *) newAttachment
{
ASSIGN (attachment, newAttachment);
}
- (NSDictionary *) attachment
{
return attachment;
}
/* links (DUP to UIxMailPartViewer!) */ /* links (DUP to UIxMailPartViewer!) */
- (NSString *) linkToEnvelopeAddress: (NGImap4EnvelopeAddress *) _address - (NSString *) linkToEnvelopeAddress: (NGImap4EnvelopeAddress *) _address
@ -146,6 +161,36 @@ static NSString *mailETag = nil;
return [[[self clientObject] replyToEnvelopeAddresses] count] > 0 ? YES : NO; return [[[self clientObject] replyToEnvelopeAddresses] count] > 0 ? YES : NO;
} }
/* attachment helper */
- (NSArray *) attachmentAttrs
{
if (!attachmentAttrs)
{
ASSIGN (attachmentAttrs, [[self clientObject] fetchFileAttachmentKeys]);
}
return attachmentAttrs;
}
- (BOOL) hasAttachments
{
return [[self attachmentAttrs] count] > 0 ? YES : NO;
}
- (NSFormatter *) sizeFormatter
{
return [UIxMailSizeFormatter sharedMailSizeFormatter];
}
- (NSString *) attachmentsText
{
if ([[self attachmentAttrs] count] > 1)
return [self labelForKey: @"files"];
else
return [self labelForKey: @"file"];
}
/* viewers */ /* viewers */
- (id) contentViewerComponent - (id) contentViewerComponent

View file

@ -211,12 +211,6 @@
</ul> </ul>
</div> </div>
<div class="menu" id="attachmentMenu">
<ul>
<li id="save_attachment"><var:string label:value="Save Attachment"/></li>
</ul>
</div>
<div id="leftPanel"> <div id="leftPanel">
<div class="titlediv"><var:string label:value="Folders" /></div> <div class="titlediv"><var:string label:value="Folders" /></div>
<div id="folderTreeContent"><!-- space --></div> <div id="folderTreeContent"><!-- space --></div>

View file

@ -13,9 +13,7 @@
var messageName = '<var:string value="clientObject.relativeImap4Name"/>'; var messageName = '<var:string value="clientObject.relativeImap4Name"/>';
var mailboxName = '/<var:string value="clientObject.container.container.nameInContainer"/>/<var:string value="clientObject.container.nameInContainer"/>'; var mailboxName = '/<var:string value="clientObject.container.container.nameInContainer"/>/<var:string value="clientObject.container.nameInContainer"/>';
</script> </script>
<span id="messageContent">
<var:component className="UIxMailView" /> <var:component className="UIxMailView" />
</span>
<div class="menu" id="addressMenu"> <div class="menu" id="addressMenu">
<ul> <ul>
<li id="add_to_addressbook"><var:string label:value="Add to Address Book..."/></li> <li id="add_to_addressbook"><var:string label:value="Add to Address Book..."/></li>
@ -27,10 +25,5 @@
<ul> <ul>
<li id="save_image"><var:string label:value="Save Image"/></li> <li id="save_image"><var:string label:value="Save Image"/></li>
</ul> </ul>
</div>
<div class="menu" id="attachmentMenu">
<ul>
<li id="save_attachment"><var:string label:value="Save Attachment"/></li>
</ul>
</div> </div>
</var:component> </var:component>

View file

@ -5,14 +5,25 @@
xmlns:uix="OGo:uix" xmlns:uix="OGo:uix"
xmlns:rsrc="OGo:url" xmlns:rsrc="OGo:url"
xmlns:label="OGo:label"> xmlns:label="OGo:label">
<var:if condition="clientObject.hasAttachment">
<div class="menu" const:id="attachmentsMenu">
<ul>
<li><var:string label:value="Save all"/></li>
<li class="separator"><!-- separator --></li
><var:foreach list="attachmentAttrs" item="attachment">
<li var:data-url="attachment.urlAsAttachment"><img rsrc:src="attachment.gif"/><var:string value="attachment.filename"
/> <span class="muted">(<var:string value="attachment.size" formatter="sizeFormatter"/>)</span></li>
</var:foreach
></ul>
</div>
</var:if>
<span id="messageContent">
<input type="hidden" const:id="shouldAskReceipt" var:value="shouldAskReceipt"/> <input type="hidden" const:id="shouldAskReceipt" var:value="shouldAskReceipt"/>
<var:if var:condition="mailIsDraft" <var:if var:condition="mailIsDraft"
><input const:name="editDraftButton" const:id="editDraftButton" ><a href="#" const:id="editDraftButton" class="button"><span><var:string label:value="Edit Draft..."/></span></a
type="button" class="button" label:value="Edit Draft..." ></var:if>
/></var:if> <a href="#" const:name="loadImagesButton" const:id="loadImagesButton"
<input const:name="loadImagesButton" const:id="loadImagesButton" class="button"><span><var:string label:value="Load Images"/></span></a>
type="button" class="button" label:value="Load Images"
/>
<table class="mailer_fieldtable"> <table class="mailer_fieldtable">
<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>
@ -93,11 +104,19 @@
</td> </td>
</tr> </tr>
</var:if> </var:if>
<var:if condition="clientObject.hasAttachment">
<tr class="mailer_fieldrow">
<td class="mailer_fieldname"><img rsrc:src="title_attachment_14x14.png"/></td>
<td class="mailer_fieldvalue">
<a href="#" const:id="attachmentsHref"><var:string value="attachmentAttrs.count"/> <var:string value="attachmentsText"/></a>
</td>
</tr>
</var:if>
</table> </table>
<div class="mailer_mailcontent"> <div class="mailer_mailcontent">
<var:component value="contentViewerComponent" <var:component value="contentViewerComponent"
bodyInfo="clientObject.bodyStructure" /> bodyInfo="clientObject.bodyStructure" />
</div> </div>
</span>
</container> </container>

View file

@ -374,21 +374,21 @@ TR.mailer_listcell_regular TD A
} }
/* mail viewer */ /* mail viewer */
INPUT#editDraftButton #editDraftButton
{ {
position: absolute; position: absolute;
top: 2.5em; top: 2.5em;
right: 1em; right: 1em;
} }
INPUT#loadImagesButton #loadImagesButton
{ {
position: absolute; position: absolute;
top: 2.5em; top: 2.5em;
right: 1em; right: 1em;
} }
.popup INPUT#loadImagesButton .popup #loadImagesButton
{ {
top: 9.0em; top: 9.0em;
right: 1em; right: 1em;
@ -434,7 +434,8 @@ DIV.mailer_mailcontent TABLE
} }
/* collapsable header */ /* collapsable header */
TD.mailer_fieldname IMG TD.mailer_fieldname IMG.collapse,
TD.mailer_fieldname IMG.expand
{ cursor: pointer; { cursor: pointer;
padding-right: 5px; } padding-right: 5px; }
TD.mailer_fieldvalue SPAN.collapse TD.mailer_fieldvalue SPAN.collapse
@ -577,7 +578,7 @@ DIV.linked_attachment_meta
{ {
color: #444444; color: #444444;
border-width: 0; border-width: 0;
padding: 2px; padding: 2px 4px;
} }
TABLE.linked_attachment_meta TABLE.linked_attachment_meta
@ -585,6 +586,25 @@ TABLE.linked_attachment_meta
color: #444444; color: #444444;
} }
.linked_attachment_body a:hover
{
text-decoration: none;
}
.linked_attachment_body a:hover .linked_attachment_meta
{
background-color: #9ABCD8;
color: #fff;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
.linked_attachment_body a:hover .muted
{
color: #fff !important;
}
DIV.linked_attachment_body HR DIV.linked_attachment_body HR
{ {
border: 0px; border: 0px;

View file

@ -1357,13 +1357,27 @@ function configureLinksInMessage() {
anchor.observe("contextmenu", onEmailAddressClick); anchor.observe("contextmenu", onEmailAddressClick);
anchor.writeAttribute("moz-do-not-send", false); anchor.writeAttribute("moz-do-not-send", false);
} }
else else if (!anchor.id)
anchor.observe("click", onMessageAnchorClick); anchor.observe("click", onMessageAnchorClick);
} }
var attachments = messageDiv.select ("DIV.linked_attachment_body"); var attachmentsMenu = $("attachmentsMenu");
for (var i = 0; i < attachments.length; i++) if (attachmentsMenu) {
$(attachments[i]).observe("contextmenu", onAttachmentClick); var options = attachmentsMenu.select("li");
var callbacks = [];
for (var i = 0; i < options.length; i++) {
if (options[i].className == 'separator')
callbacks.push(null);
else
callbacks.push(saveAttachment);
}
initMenu(attachmentsMenu, callbacks);
$("attachmentsHref").on("click", function (event) {
popupMenu(event, 'attachmentsMenu', this);
preventDefault(event);
return false;
});
}
var images = messageDiv.select("IMG.mailer_imagecontent"); var images = messageDiv.select("IMG.mailer_imagecontent");
for (var i = 0; i < images.length; i++) for (var i = 0; i < images.length; i++)
@ -1371,12 +1385,11 @@ function configureLinksInMessage() {
var editDraftButton = $("editDraftButton"); var editDraftButton = $("editDraftButton");
if (editDraftButton) if (editDraftButton)
editDraftButton.observe("click", editDraftButton.on("click", onMessageEditDraft);
onMessageEditDraft.bindAsEventListener(editDraftButton));
var loadImagesButton = $("loadImagesButton"); var loadImagesButton = $("loadImagesButton");
if (loadImagesButton) if (loadImagesButton)
$(loadImagesButton).observe("click", onMessageLoadImages); loadImagesButton.on("click", onMessageLoadImages);
configureiCalLinksInMessage(); configureiCalLinksInMessage();
} }
@ -1562,12 +1575,13 @@ function onMessageContentMenu(event) {
} }
function onMessageEditDraft(event) { function onMessageEditDraft(event) {
Event.stop(event);
return openMessageWindowsForSelection("edit", true); return openMessageWindowsForSelection("edit", true);
} }
function onMessageLoadImages(event) { function onMessageLoadImages(event) {
loadRemoteImages();
Event.stop(event); Event.stop(event);
loadRemoteImages();
} }
function loadRemoteImages() { function loadRemoteImages() {
@ -1613,12 +1627,6 @@ function onImageClick(event) {
return false; return false;
} }
function onAttachmentClick (event) {
popupMenu (event, 'attachmentMenu', this);
preventDefault (event);
return false;
}
function handleReturnReceipt() { function handleReturnReceipt() {
var input = $("shouldAskReceipt"); var input = $("shouldAskReceipt");
if (input) { if (input) {
@ -1773,13 +1781,29 @@ function saveImage(event) {
window.location.href = urlAsAttachment; window.location.href = urlAsAttachment;
} }
function saveAttachment(event) { /* Download a file using a temporary iframe that we delete once the download is started */
var div = document.menuTarget; function download(url) {
var link = div.select ("a").first (); var form = createElement('form', null, 'hidden', { action: url, method: 'GET'});
var url = link.getAttribute("href"); $(document.body).appendChild(form);
var urlAsAttachment = url.replace(/(\/[^\/]*)$/,"/asAttachment$1"); var div = AIM.submit(form);
form.submit();
setTimeout(function () {
form.remove();
div.remove();
}, 2000);
}
window.location.href = urlAsAttachment; function saveAttachment(event) {
var url = $(this).readAttribute('data-url');
if (url) {
download(url);
}
else {
$(this).up('ul').select('li[data-url]').each(function (item) {
url = $(item).readAttribute('data-url');
download(url);
});
}
} }
/* contacts */ /* contacts */
@ -2820,7 +2844,6 @@ function getMenus() {
saveAs, null, null, saveAs, null, null,
onMenuDeleteMessage ], onMenuDeleteMessage ],
imageMenu: [ saveImage ], imageMenu: [ saveImage ],
attachmentMenu: [ saveAttachment ],
messageContentMenu: [ onMenuReplyToSender, messageContentMenu: [ onMenuReplyToSender,
onMenuReplyToAll, onMenuReplyToAll,
onMenuForwardMessage, onMenuForwardMessage,

View file

@ -251,7 +251,8 @@ DIV.contactSelector DIV.contactList
padding-bottom: .15em; padding-bottom: .15em;
margin: 0px; margin: 0px;
width: auto; width: auto;
white-space: nowrap; } white-space: nowrap;
cursor: pointer; }
.menu LI.disabled, .menu LI.disabled,
.popuMenu LI.disabled, .popuMenu LI.disabled,
@ -283,10 +284,11 @@ UL.choiceMenu LI._chosen:hover
{ list-style-image: url("menu-check-hover.gif"); } { list-style-image: url("menu-check-hover.gif"); }
.menu LI:hover, .menu LI:hover,
.menu LI:hover .muted,
.menu LI.selected, .menu LI.selected,
.menu LI.submenu-selected .menu LI.submenu-selected
{ background-color: #9ABCD8; { background-color: #9ABCD8;
color: #fff; } color: #fff !important; }
.menu LI.disabled:hover .menu LI.disabled:hover
{ background-color: inherit; } { background-color: inherit; }

View file

@ -1925,7 +1925,7 @@ AIM = {
d.innerHTML = '<iframe class="hidden" src="about:blank" id="' d.innerHTML = '<iframe class="hidden" src="about:blank" id="'
+ n + '" name="' + n + '" onload="AIM.loaded(\'' + n + '\')"></iframe>'; + n + '" name="' + n + '" onload="AIM.loaded(\'' + n + '\')"></iframe>';
document.body.appendChild(d); document.body.appendChild(d);
var i = $(n); // TODO: useful? var i = $(n);
if (c && typeof(c.onComplete) == 'function') if (c && typeof(c.onComplete) == 'function')
i.onComplete = c.onComplete; i.onComplete = c.onComplete;
return n; return n;
@ -1936,27 +1936,28 @@ AIM = {
}, },
submit: function(f, c) { submit: function(f, c) {
AIM.form(f, AIM.frame(c)); var id = AIM.frame(c);
AIM.form(f, id);
if (c && typeof(c.onStart) == 'function') if (c && typeof(c.onStart) == 'function')
return c.onStart(); return c.onStart();
else else
return true; return $(id);
}, },
loaded: function(id) { loaded: function(id) {
var i = $(id); var i = $(id);
var d;
if (i.contentDocument) { if (i.contentDocument) {
var d = i.contentDocument; d = i.contentDocument;
} }
else if (i.contentWindow) { else if (i.contentWindow) {
var d = i.contentWindow.document; d = i.contentWindow.document;
} }
else { else {
var d = window.frames[id].document; d = window.frames[id].document;
} }
if (d.location.href == "about:blank") if (d.location.href == "about:blank")
return; return;
if (typeof(i.onComplete) == 'function') { if (typeof(i.onComplete) == 'function') {
i.onComplete(Element.allTextContent(d.body)); i.onComplete(Element.allTextContent(d.body));
} }