From d5609160657829b0077fbb0db3b76b6096033652 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Thu, 08 Oct 2015 10:55:43 -0400
Subject: [PATCH] Added possibility to drag-n-drop attachments from mail preview to compose window
---
CHANGELOG | 1
program/steps/mail/attachments.inc | 108 ++++++++++++-----
program/steps/mail/compose.inc | 99 ---------------
program/localization/en_US/messages.inc | 2
program/steps/mail/func.inc | 74 ++++++++++++
program/steps/mail/show.inc | 2
program/js/app.js | 48 +++++++-
7 files changed, 199 insertions(+), 135 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index 444447d..040abb9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
+- Added possibility to drag-n-drop attachments from mail preview to compose window
- Implemented mail messages searching with predefined date interval
- PGP encryption support via Mailvelope integration
- PGP encryption support via Enigma plugin
diff --git a/program/js/app.js b/program/js/app.js
index 46e0857..b8713a7 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -6,8 +6,8 @@
* @licstart The following is the entire license notice for the
* JavaScript code in this file.
*
- * Copyright (C) 2005-2014, The Roundcube Dev Team
- * Copyright (C) 2011-2014, Kolab Systems AG
+ * Copyright (C) 2005-2015, The Roundcube Dev Team
+ * Copyright (C) 2011-2015, Kolab Systems AG
*
* The JavaScript code in this page is free software: you can
* redistribute it and/or modify it under the terms of the GNU
@@ -274,6 +274,23 @@
this.enable_command('compose', 'add-contact', false);
parent.rcmail.show_contentframe(true);
}
+
+ // initialize drag-n-drop on attachments, so they can e.g.
+ // be dropped into mail compose attachments in another window
+ if (this.gui_objects.attachments)
+ $('li > a', this.gui_objects.attachments).not('.drop').on('dragstart', function(e) {
+ var n, href = this.href, dt = e.originalEvent.dataTransfer;
+ if (dt) {
+ // inject username to the uri
+ href = href.replace(/^https?:\/\//, function(m) { return m + urlencode(ref.env.username) + '@'});
+ // cleanup the node to get filename without the size test
+ n = $(this).clone();
+ n.children().remove();
+
+ dt.setData('roundcube-uri', href);
+ dt.setData('roundcube-name', $.trim(n.text()));
+ }
+ });
}
else if (this.env.action == 'compose') {
this.env.address_group_stack = [];
@@ -8410,15 +8427,32 @@
this.file_drag_hover(e, false);
// prepare multipart form data composition
- var files = e.target.files || e.dataTransfer.files,
+ var uri, files = e.target.files || e.dataTransfer.files,
formdata = window.FormData ? new FormData() : null,
fieldname = (this.env.filedrop.fieldname || '_file') + (this.env.filedrop.single ? '' : '[]'),
boundary = '------multipartformboundary' + (new Date).getTime(),
dashdash = '--', crlf = '\r\n',
- multipart = dashdash + boundary + crlf;
+ multipart = dashdash + boundary + crlf,
+ args = {_id: this.env.compose_id || this.env.cid || '', _remote: 1, _from: this.env.action};
- if (!files || !files.length)
+ if (!files || !files.length) {
+ // Roundcube attachment, pass its uri to the backend and attach
+ if (uri = e.dataTransfer.getData('roundcube-uri')) {
+ var ts = new Date().getTime(),
+ // jQuery way to escape filename (#1490530)
+ content = $('<span>').text(e.dataTransfer.getData('roundcube-name') || this.gettext('attaching')).html();
+
+ args._uri = uri;
+ args._uploadid = ts;
+
+ // add to attachments list
+ if (!this.add2attachment_list(ts, {name: '', html: content, classname: 'uploading', complete: false}))
+ this.file_upload_id = this.set_busy(true, 'attaching');
+
+ this.http_post(this.env.filedrop.action || 'upload', args);
+ }
return;
+ }
// inline function to submit the files to the server
var submit_data = function() {
@@ -8434,10 +8468,12 @@
// complete multipart content and post request
multipart += dashdash + boundary + dashdash + crlf;
+ args._uploadid = ts;
+
$.ajax({
type: 'POST',
dataType: 'json',
- url: ref.url(ref.env.filedrop.action || 'upload', {_id: ref.env.compose_id||ref.env.cid||'', _uploadid: ts, _remote: 1, _from: ref.env.action}),
+ url: ref.url(ref.env.filedrop.action || 'upload', args),
contentType: formdata ? false : 'multipart/form-data; boundary=' + boundary,
processData: false,
timeout: 0, // disable default timeout set in ajaxSetup()
diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc
index cc8d5ea..313d6ae 100644
--- a/program/localization/en_US/messages.inc
+++ b/program/localization/en_US/messages.inc
@@ -41,6 +41,7 @@
$messages['refreshing'] = 'Refreshing...';
$messages['loading'] = 'Loading...';
$messages['uploading'] = 'Uploading file...';
+$messages['attaching'] = 'Attaching file...';
$messages['uploadingmany'] = 'Uploading files...';
$messages['loadingdata'] = 'Loading data...';
$messages['checkingmail'] = 'Checking for new messages...';
@@ -114,6 +115,7 @@
$messages['deletedsuccessfully'] = 'Successfully deleted.';
$messages['converting'] = 'Removing formatting...';
$messages['messageopenerror'] = 'Could not load message from server.';
+$messages['filelinkerror'] = 'Attaching the file failed.';
$messages['fileuploaderror'] = 'File upload failed.';
$messages['filesizeerror'] = 'The uploaded file exceeds the maximum size of $size.';
$messages['copysuccess'] = 'Successfully copied $nr contacts.';
diff --git a/program/steps/mail/attachments.inc b/program/steps/mail/attachments.inc
index fe5684c..c39b25c 100644
--- a/program/steps/mail/attachments.inc
+++ b/program/steps/mail/attachments.inc
@@ -68,7 +68,6 @@
}
$RCMAIL->display_uploaded_file($COMPOSE['attachments'][$id]);
-
exit;
}
@@ -77,8 +76,41 @@
// clear all stored output properties (like scripts and env vars)
$OUTPUT->reset();
-$uploadid = rcube_utils::get_input_value('_uploadid', rcube_utils::INPUT_GET);
+$uploadid = rcube_utils::get_input_value('_uploadid', rcube_utils::INPUT_GPC);
+$uri = rcube_utils::get_input_value('_uri', rcube_utils::INPUT_POST);
+// handle dropping a reference to an attachment part of some message
+if ($uri) {
+ $url = parse_url($uri);
+ parse_str($url['query'], $params);
+
+ if (strlen($params['_mbox']) && $params['_uid'] && $params['_part']) {
+ // @TODO: at some point we might support drag-n-drop between
+ // two different accounts on the same server, for now make sure
+ // this is the same server and the same user
+ list($host, $port) = explode(':', $_SERVER['HTTP_HOST']);
+ if ($host == $url['host'] && $port == $url['port']
+ && $RCMAIL->get_user_name() == rawurldecode($url['user'])
+ ) {
+ $message = new rcube_message($params['_uid'], $params['_mbox']);
+ }
+ }
+
+ if ($message && !empty($message->headers)
+ && ($attachment = rcmail_save_attachment($message, $params['_part'], $COMPOSE_ID))
+ ) {
+ rcmail_attachment_success($attachment, $uploadid);
+ }
+ else {
+ $OUTPUT->command('display_message', $RCMAIL->gettext('filelinkerror'), 'error');
+ $OUTPUT->command('remove_from_attachment_list', $uploadid);
+ }
+
+ $OUTPUT->send();
+ return;
+}
+
+// handle file(s) upload
if (is_array($_FILES['_attachments']['tmp_name'])) {
$multiple = count($_FILES['_attachments']['tmp_name']) > 1;
@@ -97,41 +129,11 @@
}
if (!$err && $attachment['status'] && !$attachment['abort']) {
- $id = $attachment['id'];
-
// store new attachment in session
unset($attachment['status'], $attachment['abort']);
- $RCMAIL->session->append($SESSION_KEY.'.attachments', $id, $attachment);
+ $RCMAIL->session->append($SESSION_KEY . '.attachments', $attachment['id'], $attachment);
- if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) {
- $button = html::img(array(
- 'src' => $icon,
- 'alt' => $RCMAIL->gettext('delete')
- ));
- }
- else if ($COMPOSE['textbuttons']) {
- $button = rcube::Q($RCMAIL->gettext('delete'));
- }
- else {
- $button = '';
- }
-
- $content = html::a(array(
- 'href' => "#delete",
- 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", rcmail_output::JS_OBJECT_NAME, $id),
- 'title' => $RCMAIL->gettext('delete'),
- 'class' => 'delete',
- 'aria-label' => $RCMAIL->gettext('delete') . ' ' . $attachment['name'],
- ), $button);
-
- $content .= rcube::Q($attachment['name']);
-
- $OUTPUT->command('add2attachment_list', "rcmfile$id", array(
- 'html' => $content,
- 'name' => $attachment['name'],
- 'mimetype' => $attachment['mimetype'],
- 'classname' => rcube_utils::file2class($attachment['mimetype'], $attachment['name']),
- 'complete' => true), $uploadid);
+ rcmail_attachment_success($attachment, $uploadid);
}
else { // upload failed
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
@@ -172,3 +174,41 @@
// send html page with JS calls as response
$OUTPUT->command('auto_save_start', false);
$OUTPUT->send('iframe');
+
+
+function rcmail_attachment_success($attachment, $uploadid)
+{
+ global $RCMAIL, $COMPOSE;
+
+ $id = $attachment['id'];
+
+ if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) {
+ $button = html::img(array(
+ 'src' => $icon,
+ 'alt' => $RCMAIL->gettext('delete')
+ ));
+ }
+ else if ($COMPOSE['textbuttons']) {
+ $button = rcube::Q($RCMAIL->gettext('delete'));
+ }
+ else {
+ $button = '';
+ }
+
+ $content = html::a(array(
+ 'href' => "#delete",
+ 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", rcmail_output::JS_OBJECT_NAME, $id),
+ 'title' => $RCMAIL->gettext('delete'),
+ 'class' => 'delete',
+ 'aria-label' => $RCMAIL->gettext('delete') . ' ' . $attachment['name'],
+ ), $button);
+
+ $content .= rcube::Q($attachment['name']);
+
+ $RCMAIL->output->command('add2attachment_list', "rcmfile$id", array(
+ 'html' => $content,
+ 'name' => $attachment['name'],
+ 'mimetype' => $attachment['mimetype'],
+ 'classname' => rcube_utils::file2class($attachment['mimetype'], $attachment['name']),
+ 'complete' => true), $uploadid);
+}
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 0b047d4..0e61e26 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -88,7 +88,7 @@
'selectimportfile', 'messageissent', 'loadingdata', 'nopubkeyfor', 'nopubkeyforsender',
'encryptnoattachments','encryptedsendialog','searchpubkeyservers', 'importpubkeys',
'encryptpubkeysfound', 'search', 'close', 'import', 'keyid', 'keylength', 'keyexpired',
- 'keyrevoked', 'keyimportsuccess', 'keyservererror');
+ 'keyrevoked', 'keyimportsuccess', 'keyservererror', 'attaching');
$OUTPUT->set_pagetitle($RCMAIL->gettext('compose'));
@@ -1294,10 +1294,8 @@
}
if (($attachment = $loaded_attachments[rcmail_attachment_name($part) . $part->mimetype])
- || ($attachment = rcmail_save_attachment($message, $pid))
+ || ($attachment = rcmail_save_attachment($message, $pid, $COMPOSE['id']))
) {
- $COMPOSE['attachments'][$attachment['id']] = $attachment;
-
if ($bodyIsHtml && ($part->content_id || $part->content_location)) {
$url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
$RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']);
@@ -1329,8 +1327,7 @@
foreach ((array)$message->mime_parts as $pid => $part) {
if (($part->content_id || $part->content_location) && $part->filename) {
- if ($attachment = rcmail_save_attachment($message, $pid)) {
- $COMPOSE['attachments'][$attachment['id']] = $attachment;
+ if ($attachment = rcmail_save_attachment($message, $pid, $COMPOSE['id'])) {
$url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
$RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']);
@@ -1398,46 +1395,11 @@
$names[$name] = 1;
$name .= '.eml';
- $data = $path = null;
-
if (!empty($loaded_attachments[$name . 'message/rfc822'])) {
continue;
}
- // don't load too big attachments into memory
- if (!rcube_utils::mem_check($message->size)) {
- $temp_dir = unslashify($RCMAIL->config->get('temp_dir'));
- $path = tempnam($temp_dir, 'rcmAttmnt');
- if ($fp = fopen($path, 'w')) {
- $storage->get_raw_body($message->uid, $fp);
- fclose($fp);
- }
- else {
- return false;
- }
- }
- else {
- $data = $storage->get_raw_body($message->uid);
- }
-
- $attachment = array(
- 'group' => $COMPOSE['id'],
- 'name' => $name,
- 'mimetype' => 'message/rfc822',
- 'data' => $data,
- 'path' => $path,
- 'size' => $path ? filesize($path) : strlen($data),
- );
-
- $attachment = $RCMAIL->plugins->exec_hook('attachment_save', $attachment);
-
- if ($attachment['status']) {
- unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
- $COMPOSE['attachments'][$attachment['id']] = $attachment;
- }
- else if ($path) {
- @unlink($path);
- }
+ rcmail_save_attachment($message, null, $COMPOSE['id'], array('filename' => $name));
if ($message->headers->messageID) {
$refs[] = $message->headers->messageID;
@@ -1451,59 +1413,6 @@
if (!empty($refs)) {
$COMPOSE['references'] = implode(' ', $refs);
}
-}
-
-
-function rcmail_save_attachment(&$message, $pid)
-{
- global $COMPOSE;
-
- $rcmail = rcmail::get_instance();
- $part = $message->mime_parts[$pid];
- $data = $path = null;
-
- // don't load too big attachments into memory
- if (!rcube_utils::mem_check($part->size)) {
- $temp_dir = unslashify($rcmail->config->get('temp_dir'));
- $path = tempnam($temp_dir, 'rcmAttmnt');
-
- if ($fp = fopen($path, 'w')) {
- $message->get_part_body($pid, false, 0, $fp);
- fclose($fp);
- }
- else {
- return false;
- }
- }
- else {
- $data = $message->get_part_body($pid);
- }
-
- $mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
- $filename = rcmail_attachment_name($part);
-
- $attachment = array(
- 'group' => $COMPOSE['id'],
- 'name' => $filename,
- 'mimetype' => $mimetype,
- 'content_id' => $part->content_id,
- 'data' => $data,
- 'path' => $path,
- 'size' => $path ? filesize($path) : strlen($data),
- 'charset' => $part->charset,
- );
-
- $attachment = $rcmail->plugins->exec_hook('attachment_save', $attachment);
-
- if ($attachment['status']) {
- unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
- return $attachment;
- }
- else if ($path) {
- @unlink($path);
- }
-
- return false;
}
function rcmail_save_image($path, $mimetype = '', $data = null)
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index d949cf6..80b618c 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -2199,3 +2199,77 @@
return $jsresult;
}
+
+function rcmail_save_attachment($message, $pid, $compose_id, $params = array())
+{
+ $rcmail = rcmail::get_instance();
+ $storage = $rcmail->get_storage();
+
+ if ($pid) {
+ // attachment requested
+ $part = $message->mime_parts[$pid];
+ $size = $part->size;
+ $mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
+ $filename = $params['filename'] ?: rcmail_attachment_name($part);
+ }
+ else {
+ // the whole message requested
+ $size = $message->size;
+ $mimetype = 'message/rfc822';
+ $filename = $params['filename'] ?: 'message_rfc822.eml';
+ }
+
+ // don't load too big attachments into memory
+ if (!rcube_utils::mem_check($size)) {
+ $temp_dir = unslashify($rcmail->config->get('temp_dir'));
+ $path = tempnam($temp_dir, 'rcmAttmnt');
+
+ if ($fp = fopen($path, 'w')) {
+ if ($pid) {
+ // part body
+ $message->get_part_body($pid, false, 0, $fp);
+ }
+ else {
+ // complete message
+ $storage->get_raw_body($message->uid, $fp);
+ }
+
+ fclose($fp);
+ }
+ else {
+ return false;
+ }
+ }
+ else if ($pid) {
+ // part body
+ $data = $message->get_part_body($pid);
+ }
+ else {
+ // complete message
+ $data = $storage->get_raw_body($message->uid);
+ }
+
+ $attachment = array(
+ 'group' => $compose_id,
+ 'name' => $filename,
+ 'mimetype' => $mimetype,
+ 'content_id' => $part ? $part->content_id : null,
+ 'data' => $data,
+ 'path' => $path,
+ 'size' => $path ? filesize($path) : strlen($data),
+ 'charset' => $part ? $part->charset : null,
+ );
+
+ $attachment = $rcmail->plugins->exec_hook('attachment_save', $attachment);
+
+ if ($attachment['status']) {
+ unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
+ $rcmail->session->append('compose_data_' . $compose_id . '.attachments', $attachment['id'], $attachment);
+ return $attachment;
+ }
+ else if ($path) {
+ @unlink($path);
+ }
+
+ return false;
+}
diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc
index e5bfcf0..82bcb41 100644
--- a/program/steps/mail/show.inc
+++ b/program/steps/mail/show.inc
@@ -68,6 +68,7 @@
$OUTPUT->set_env('safemode', $MESSAGE->is_safe);
$OUTPUT->set_env('sender', $MESSAGE->sender['string']);
$OUTPUT->set_env('mailbox', $mbox_name);
+ $OUTPUT->set_env('username', $RCMAIL->get_user_name());
$OUTPUT->set_env('permaurl', $RCMAIL->url(array('_action' => 'show', '_uid' => $MESSAGE->uid, '_mbox' => $mbox_name)));
if ($MESSAGE->headers->get('list-post', false)) {
@@ -240,6 +241,7 @@
$out = html::tag('ul', $attrib, $ol, html::$common_attrib);
$RCMAIL->output->set_env('attachments', $attachments);
+ $RCMAIL->output->add_gui_object('attachments', $attrib['id']);
}
return $out;
--
Gitblit v1.9.1