Added possibility to drag-n-drop attachments from mail preview to compose window

pull/299/merge
Aleksander Machniak 9 years ago
parent 4d369549bd
commit d560916065

@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail CHANGELOG Roundcube Webmail
=========================== ===========================
- Added possibility to drag-n-drop attachments from mail preview to compose window
- Implemented mail messages searching with predefined date interval - Implemented mail messages searching with predefined date interval
- PGP encryption support via Mailvelope integration - PGP encryption support via Mailvelope integration
- PGP encryption support via Enigma plugin - PGP encryption support via Enigma plugin

@ -6,8 +6,8 @@
* @licstart The following is the entire license notice for the * @licstart The following is the entire license notice for the
* JavaScript code in this file. * JavaScript code in this file.
* *
* Copyright (C) 2005-2014, The Roundcube Dev Team * Copyright (C) 2005-2015, The Roundcube Dev Team
* Copyright (C) 2011-2014, Kolab Systems AG * Copyright (C) 2011-2015, Kolab Systems AG
* *
* The JavaScript code in this page is free software: you can * The JavaScript code in this page is free software: you can
* redistribute it and/or modify it under the terms of the GNU * redistribute it and/or modify it under the terms of the GNU
@ -274,6 +274,23 @@ function rcube_webmail()
this.enable_command('compose', 'add-contact', false); this.enable_command('compose', 'add-contact', false);
parent.rcmail.show_contentframe(true); 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') { else if (this.env.action == 'compose') {
this.env.address_group_stack = []; this.env.address_group_stack = [];
@ -8410,15 +8427,32 @@ function rcube_webmail()
this.file_drag_hover(e, false); this.file_drag_hover(e, false);
// prepare multipart form data composition // 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, formdata = window.FormData ? new FormData() : null,
fieldname = (this.env.filedrop.fieldname || '_file') + (this.env.filedrop.single ? '' : '[]'), fieldname = (this.env.filedrop.fieldname || '_file') + (this.env.filedrop.single ? '' : '[]'),
boundary = '------multipartformboundary' + (new Date).getTime(), boundary = '------multipartformboundary' + (new Date).getTime(),
dashdash = '--', crlf = '\r\n', 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) {
// 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();
if (!files || !files.length) 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; return;
}
// inline function to submit the files to the server // inline function to submit the files to the server
var submit_data = function() { var submit_data = function() {
@ -8434,10 +8468,12 @@ function rcube_webmail()
// complete multipart content and post request // complete multipart content and post request
multipart += dashdash + boundary + dashdash + crlf; multipart += dashdash + boundary + dashdash + crlf;
args._uploadid = ts;
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
dataType: 'json', 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, contentType: formdata ? false : 'multipart/form-data; boundary=' + boundary,
processData: false, processData: false,
timeout: 0, // disable default timeout set in ajaxSetup() timeout: 0, // disable default timeout set in ajaxSetup()

@ -41,6 +41,7 @@ $messages['nomessages'] = 'No messages';
$messages['refreshing'] = 'Refreshing...'; $messages['refreshing'] = 'Refreshing...';
$messages['loading'] = 'Loading...'; $messages['loading'] = 'Loading...';
$messages['uploading'] = 'Uploading file...'; $messages['uploading'] = 'Uploading file...';
$messages['attaching'] = 'Attaching file...';
$messages['uploadingmany'] = 'Uploading files...'; $messages['uploadingmany'] = 'Uploading files...';
$messages['loadingdata'] = 'Loading data...'; $messages['loadingdata'] = 'Loading data...';
$messages['checkingmail'] = 'Checking for new messages...'; $messages['checkingmail'] = 'Checking for new messages...';
@ -114,6 +115,7 @@ $messages['folderexpunged'] = 'Folder has successfully been compacted.';
$messages['deletedsuccessfully'] = 'Successfully deleted.'; $messages['deletedsuccessfully'] = 'Successfully deleted.';
$messages['converting'] = 'Removing formatting...'; $messages['converting'] = 'Removing formatting...';
$messages['messageopenerror'] = 'Could not load message from server.'; $messages['messageopenerror'] = 'Could not load message from server.';
$messages['filelinkerror'] = 'Attaching the file failed.';
$messages['fileuploaderror'] = 'File upload failed.'; $messages['fileuploaderror'] = 'File upload failed.';
$messages['filesizeerror'] = 'The uploaded file exceeds the maximum size of $size.'; $messages['filesizeerror'] = 'The uploaded file exceeds the maximum size of $size.';
$messages['copysuccess'] = 'Successfully copied $nr contacts.'; $messages['copysuccess'] = 'Successfully copied $nr contacts.';

@ -68,7 +68,6 @@ if ($RCMAIL->action == 'display-attachment') {
} }
$RCMAIL->display_uploaded_file($COMPOSE['attachments'][$id]); $RCMAIL->display_uploaded_file($COMPOSE['attachments'][$id]);
exit; exit;
} }
@ -77,8 +76,41 @@ if ($RCMAIL->action == 'display-attachment') {
// clear all stored output properties (like scripts and env vars) // clear all stored output properties (like scripts and env vars)
$OUTPUT->reset(); $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'])) { if (is_array($_FILES['_attachments']['tmp_name'])) {
$multiple = count($_FILES['_attachments']['tmp_name']) > 1; $multiple = count($_FILES['_attachments']['tmp_name']) > 1;
@ -97,41 +129,11 @@ if (is_array($_FILES['_attachments']['tmp_name'])) {
} }
if (!$err && $attachment['status'] && !$attachment['abort']) { if (!$err && $attachment['status'] && !$attachment['abort']) {
$id = $attachment['id'];
// store new attachment in session // store new attachment in session
unset($attachment['status'], $attachment['abort']); 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)) { rcmail_attachment_success($attachment, $uploadid);
$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);
} }
else { // upload failed else { // upload failed
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
@ -172,3 +174,41 @@ else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// send html page with JS calls as response // send html page with JS calls as response
$OUTPUT->command('auto_save_start', false); $OUTPUT->command('auto_save_start', false);
$OUTPUT->send('iframe'); $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);
}

@ -88,7 +88,7 @@ $OUTPUT->add_label('nosubject', 'nosenderwarning', 'norecipientwarning', 'nosubj
'selectimportfile', 'messageissent', 'loadingdata', 'nopubkeyfor', 'nopubkeyforsender', 'selectimportfile', 'messageissent', 'loadingdata', 'nopubkeyfor', 'nopubkeyforsender',
'encryptnoattachments','encryptedsendialog','searchpubkeyservers', 'importpubkeys', 'encryptnoattachments','encryptedsendialog','searchpubkeyservers', 'importpubkeys',
'encryptpubkeysfound', 'search', 'close', 'import', 'keyid', 'keylength', 'keyexpired', 'encryptpubkeysfound', 'search', 'close', 'import', 'keyid', 'keylength', 'keyexpired',
'keyrevoked', 'keyimportsuccess', 'keyservererror'); 'keyrevoked', 'keyimportsuccess', 'keyservererror', 'attaching');
$OUTPUT->set_pagetitle($RCMAIL->gettext('compose')); $OUTPUT->set_pagetitle($RCMAIL->gettext('compose'));
@ -1294,10 +1294,8 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
} }
if (($attachment = $loaded_attachments[rcmail_attachment_name($part) . $part->mimetype]) 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)) { if ($bodyIsHtml && ($part->content_id || $part->content_location)) {
$url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s', $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
$RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']); $RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']);
@ -1329,8 +1327,7 @@ function rcmail_write_inline_attachments(&$message)
foreach ((array)$message->mime_parts as $pid => $part) { foreach ((array)$message->mime_parts as $pid => $part) {
if (($part->content_id || $part->content_location) && $part->filename) { if (($part->content_id || $part->content_location) && $part->filename) {
if ($attachment = rcmail_save_attachment($message, $pid)) { if ($attachment = rcmail_save_attachment($message, $pid, $COMPOSE['id'])) {
$COMPOSE['attachments'][$attachment['id']] = $attachment;
$url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s', $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
$RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']); $RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']);
@ -1398,46 +1395,11 @@ function rcmail_write_forward_attachments()
$names[$name] = 1; $names[$name] = 1;
$name .= '.eml'; $name .= '.eml';
$data = $path = null;
if (!empty($loaded_attachments[$name . 'message/rfc822'])) { if (!empty($loaded_attachments[$name . 'message/rfc822'])) {
continue; continue;
} }
// don't load too big attachments into memory rcmail_save_attachment($message, null, $COMPOSE['id'], array('filename' => $name));
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);
}
if ($message->headers->messageID) { if ($message->headers->messageID) {
$refs[] = $message->headers->messageID; $refs[] = $message->headers->messageID;
@ -1453,59 +1415,6 @@ function rcmail_write_forward_attachments()
} }
} }
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) function rcmail_save_image($path, $mimetype = '', $data = null)
{ {
global $COMPOSE; global $COMPOSE;

@ -2199,3 +2199,77 @@ function rcmail_compose_contact_groups($abook, $source_id, $search = null, $sear
return $jsresult; 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;
}

@ -68,6 +68,7 @@ if ($uid) {
$OUTPUT->set_env('safemode', $MESSAGE->is_safe); $OUTPUT->set_env('safemode', $MESSAGE->is_safe);
$OUTPUT->set_env('sender', $MESSAGE->sender['string']); $OUTPUT->set_env('sender', $MESSAGE->sender['string']);
$OUTPUT->set_env('mailbox', $mbox_name); $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))); $OUTPUT->set_env('permaurl', $RCMAIL->url(array('_action' => 'show', '_uid' => $MESSAGE->uid, '_mbox' => $mbox_name)));
if ($MESSAGE->headers->get('list-post', false)) { if ($MESSAGE->headers->get('list-post', false)) {
@ -240,6 +241,7 @@ function rcmail_message_attachments($attrib)
$out = html::tag('ul', $attrib, $ol, html::$common_attrib); $out = html::tag('ul', $attrib, $ol, html::$common_attrib);
$RCMAIL->output->set_env('attachments', $attachments); $RCMAIL->output->set_env('attachments', $attachments);
$RCMAIL->output->add_gui_object('attachments', $attrib['id']);
} }
return $out; return $out;

Loading…
Cancel
Save