Add possibility to preview and download attachments in mail compose (#5053)

pull/5374/merge
Aleksander Machniak 8 years ago
parent dcf877c692
commit 8a13615e9e

@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
- Add possibility to preview and download attachments in mail compose (#5053)
- Remove backward compatibility "layer" of bc.php (#4902)
- Support WEBP images in mail messages (#5362)
- Support MathML in HTML message preview (#5182)

@ -2247,11 +2247,16 @@ class rcmail extends rcube
}
else {
$size = $part->size;
if ($part->encoding == 'base64') {
$size = $size / 1.33;
}
$size = '~' . $this->show_bytes($size);
$size = $this->show_bytes($size);
}
if (!$this->exact_size) {
$size = '~' . $size;
}
return $size;

@ -296,7 +296,8 @@ function rcube_webmail()
this.env.address_group_stack = [];
this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel',
'toggle-editor', 'list-addresses', 'pushgroup', 'search', 'reset-search', 'extwin',
'insert-response', 'save-response', 'menu-open', 'menu-close'];
'insert-response', 'save-response', 'menu-open', 'menu-close', 'load-attachment',
'download-attachment', 'open-attachment'];
if (this.env.drafts_mailbox)
this.env.compose_commands.push('savedraft')
@ -777,6 +778,8 @@ function rcube_webmail()
case 'menu-open':
if (props && props.menu == 'attachmentmenu') {
var mimetype = this.env.attachments[props.id];
if (mimetype && mimetype.mimetype) // in compose format is different
mimetype = mimetype.mimetype;
this.enable_command('open-attachment', mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0);
}
this.show_menu(props, props.show || undefined, event);
@ -1018,16 +1021,29 @@ function rcube_webmail()
case 'load-attachment':
case 'open-attachment':
case 'download-attachment':
var qstring = '_mbox='+urlencode(this.env.mailbox)+'&_uid='+this.env.uid+'&_part='+props,
mimetype = this.env.attachments[props];
var params, mimetype = this.env.attachments[props];
if (this.env.action == 'compose') {
params = {_file: props, _id: this.env.compose_id};
mimetype = mimetype ? mimetype.mimetype : '';
}
else {
params = {_mbox: this.env.mailbox, _uid: this.env.uid, _part: props};
}
// open attachment in frame if it's of a supported mimetype
if (command != 'download-attachment' && mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0) {
if (this.open_window(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1'))
if (this.open_window(this.url('get', $.extend({_frame: 1}, params))))
break;
}
this.goto_url('get', qstring+'&_download=1', false, true);
params._download = 1;
// prevent from page unload warning in compose
this.compose_skip_unsavedcheck = 1;
this.goto_url('get', params, false, true);
this.compose_skip_unsavedcheck = 0;
break;
case 'select-all':
@ -4864,6 +4880,8 @@ function rcube_webmail()
var tabindex = $(this.gui_objects.attachmentlist).attr('data-tabindex') || '0';
li.find('a').attr('tabindex', tabindex);
this.triggerEvent('fileappended', {name: name, attachment: att, id: upload_id, item: li});
return true;
};
@ -4880,7 +4898,7 @@ function rcube_webmail()
if (name && this.env.attachments[name])
this.http_post('remove-attachment', { _id:this.env.compose_id, _file:name });
return true;
return false;
};
this.cancel_attachment_upload = function(name, frame_name)

@ -195,16 +195,23 @@ function rcmail_attachment_success($attachment, $uploadid)
$button = '';
}
$content = html::a(array(
$link_content = sprintf('%s <span class="attachment-size">(%s)</span>',
rcube::Q($attachment['name']), $RCMAIL->show_bytes($attachment['size']));
$content_link = html::a(array(
'href' => "#load",
'onclick' => sprintf("return %s.command('load-attachment','rcmfile%s', this, event)", rcmail_output::JS_OBJECT_NAME, $id),
), $link_content);
$delete_link = html::a(array(
'href' => "#delete",
'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", rcmail_output::JS_OBJECT_NAME, $id),
'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this, event)", rcmail_output::JS_OBJECT_NAME, $id),
'title' => $RCMAIL->gettext('delete'),
'class' => 'delete',
'aria-label' => $RCMAIL->gettext('delete') . ' ' . $attachment['name'],
), $button);
$content .= sprintf('%s <span class="attachment-size">(%s)</span>',
rcube::Q($attachment['name']), $RCMAIL->show_bytes($attachment['size']));
$content = $COMPOSE['icon_pos'] == 'left' ? $delete_link.$content_link : $content_link.$delete_link;
$RCMAIL->output->command('add2attachment_list', "rcmfile$id", array(
'html' => $content,

@ -100,6 +100,7 @@ $OUTPUT->set_env('sig_below', $RCMAIL->config->get('sig_below'));
$OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ',')));
$OUTPUT->set_env('save_localstorage', (bool)$RCMAIL->config->get('compose_save_localstorage'));
$OUTPUT->set_env('is_sent', false);
$OUTPUT->set_env('mimetypes', rcmail_supported_mimetypes());
$drafts_mbox = $RCMAIL->config->get('drafts_mbox');
$config_show_sig = $RCMAIL->config->get('show_sig', 1);
@ -1549,6 +1550,9 @@ function rcmail_compose_attachment_list($attrib)
$jslist = array();
$button = '';
if ($attrib['icon_pos'] == 'left')
$COMPOSE['icon_pos'] = 'left';
if (is_array($COMPOSE['attachments'])) {
if ($attrib['deleteicon']) {
$button = html::img(array(
@ -1565,24 +1569,29 @@ function rcmail_compose_attachment_list($attrib)
continue;
}
$content = sprintf('%s <span class="attachment-size">(%s)</span>',
$link_content = sprintf('%s <span class="attachment-size">(%s)</span>',
rcube::Q($a_prop['name']), $RCMAIL->show_bytes($a_prop['size']));
$content_link = html::a(array(
'href' => "#load",
'onclick' => sprintf("return %s.command('load-attachment','rcmfile%s', this, event)", rcmail_output::JS_OBJECT_NAME, $id),
), $link_content);
$delete_link = html::a(array(
'href' => "#delete",
'title' => $RCMAIL->gettext('delete'),
'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this, event)", rcmail_output::JS_OBJECT_NAME, $id),
'class' => 'delete',
'tabindex' => $attrib['tabindex'] ?: '0',
'aria-label' => $RCMAIL->gettext('delete') . ' ' . $a_prop['name'],
), $button);
$out .= html::tag('li', array(
'id' => 'rcmfile'.$id,
'class' => rcube_utils::file2class($a_prop['mimetype'], $a_prop['name']),
'onmouseover' => "rcube_webmail.long_subject_title_ex(this, 0)",
),
html::a(array(
'href' => "#delete",
'title' => $RCMAIL->gettext('delete'),
'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", rcmail_output::JS_OBJECT_NAME, $id),
'class' => 'delete',
'tabindex' => $attrib['tabindex'] ?: '0',
'aria-label' => $RCMAIL->gettext('delete') . ' ' . $a_prop['name'],
),
$button
) . $content
$COMPOSE['icon_pos'] == 'left' ? $delete_link.$content_link : $content_link.$delete_link
);
$jslist['rcmfile'.$id] = array(

@ -769,7 +769,7 @@ function rcmail_get_unseen_count($mbox_name)
*
* @param object rcube_message Message
*/
function rcmail_check_safe(&$message)
function rcmail_check_safe($message)
{
global $RCMAIL;
@ -797,6 +797,8 @@ function rcmail_check_safe(&$message)
break;
}
}
return !empty($message->is_safe);
}
/**
@ -807,7 +809,7 @@ function rcmail_check_safe(&$message)
* @param array CID map replaces (inline images)
* @return string Clean HTML
*/
function rcmail_wash_html($html, $p, $cid_replaces)
function rcmail_wash_html($html, $p, $cid_replaces = array())
{
global $REMOTE_OBJECTS;
@ -2312,3 +2314,38 @@ function rcmail_save_attachment($message, $pid, $compose_id, $params = array())
return false;
}
// Return mimetypes supported by the browser
function rcmail_supported_mimetypes()
{
$rcmail = rcube::get_instance();
// mimetypes supported by the browser (default settings)
$mimetypes = (array) $rcmail->config->get('client_mimetypes');
// Remove unsupported types, which makes that attachment which cannot be
// displayed in a browser will be downloaded directly without displaying an overlay page
if (empty($_SESSION['browser_caps']['pdf']) && ($key = array_search('application/pdf', $mimetypes)) !== false) {
unset($mimetypes[$key]);
}
if (empty($_SESSION['browser_caps']['flash']) && ($key = array_search('application/x-shockwave-flash', $mimetypes)) !== false) {
unset($mimetypes[$key]);
}
foreach (array('tiff', 'webp') as $type) {
if (empty($_SESSION['browser_caps'][$type]) && ($key = array_search('image/' . $type, $mimetypes)) !== false) {
// can we convert it to jpeg?
if (!rcube_image::is_convertable('image/' . $type)) {
unset($mimetypes[$key]);
}
}
}
// @TODO: support mail preview for compose attachments
if ($rcmail->action != 'compose' && !in_array('message/rfc822', $mimetypes)) {
$mimetypes[] = 'message/rfc822';
}
return array_values($mimetypes);
}

File diff suppressed because it is too large Load Diff

@ -79,6 +79,7 @@ if ($uid) {
// set environment
$OUTPUT->set_env('delimiter', $RCMAIL->storage->get_hierarchy_delimiter());
$OUTPUT->set_env('mimetypes', rcmail_supported_mimetypes());
// set configuration
$RCMAIL->set_env_config(array('delete_junk', 'flag_for_deletion', 'read_when_deleted',
@ -91,31 +92,6 @@ if ($uid) {
}
}
// mimetypes supported by the browser (default settings)
$mimetypes = (array)$RCMAIL->config->get('client_mimetypes');
// Remove unsupported types, which makes that attachment which cannot be
// displayed in a browser will be downloaded directly without displaying an overlay page
if (empty($_SESSION['browser_caps']['pdf']) && ($key = array_search('application/pdf', $mimetypes)) !== false) {
unset($mimetypes[$key]);
}
if (empty($_SESSION['browser_caps']['flash']) && ($key = array_search('application/x-shockwave-flash', $mimetypes)) !== false) {
unset($mimetypes[$key]);
}
foreach (array('tiff', 'webp') as $type) {
if (empty($_SESSION['browser_caps'][$type]) && ($key = array_search('image/' . $type, $mimetypes)) !== false) {
// can we convert it to jpeg?
if (!rcube_image::is_convertable('image/' . $type)) {
unset($mimetypes[$key]);
}
}
}
if (!in_array('message/rfc822', $mimetypes)) {
$mimetypes[] = 'message/rfc822';
}
$OUTPUT->set_env('mimetypes', array_values($mimetypes));
if ($MESSAGE->has_html_part()) {
$prefer_html = $RCMAIL->config->get('prefer_html');
$OUTPUT->set_env('optional_format', $prefer_html ? 'text' : 'html');
@ -238,7 +214,7 @@ function rcmail_message_attachments($attrib)
'onclick' => sprintf('return %s.command(\'load-attachment\',\'%s\',this)',
rcmail_output::JS_OBJECT_NAME, $attach_prop->mime_id),
'onmouseover' => $title ? '' : 'rcube_webmail.long_subject_title_ex(this, 0)',
'title' => rcube::Q($title),
'title' => $title,
), rcube::Q($filename) . $size);
$ol .= html::tag('li', array('class' => $class, 'id' => $id), $link);

@ -984,6 +984,18 @@ function percent_indicator(obj, data)
$('#quotaimg').attr('title', data.title);
};
function attachment_menu_append(item)
{
$(item).append(
$('<a class="drop"></a>').on('click keypress', function(e) {
if (e.type != 'keypress' || e.which == 13) {
rcmail_ui.show_attachmentmenu(this, e);
return false;
}
})
);
};
// Optional parameters used by TinyMCE
var rcmail_editor_settings = {};
@ -1034,17 +1046,18 @@ function rcube_init_mail_ui()
$("select[name='editorSelector']").prop('disabled', e.active);
$('a.button.attach, a.button.responses, a.button.attach, #uploadmenulink')[(e.active ? 'addClass' : 'removeClass')]('buttonPas disabled');
$('#responseslist a.insertresponse')[(e.active ? 'removeClass' : 'addClass')]('active');
});
rcmail.addEventListener('fileappended', function(e) { attachment_menu_append(e.item); });
// add menu link for each attachment
$('#attachmentslist > li').each(function() {
attachment_menu_append(this);
});
}
else if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') {
// add menu link for each attachment
$('#attachment-list > li[id^="attach"]').each(function() {
$(this).append($('<a class="drop"></a>').on('click keypress', function(e) {
if (e.type != 'keypress' || e.which == 13) {
rcmail_ui.show_attachmentmenu(this, e);
return false;
}
}));
attachment_menu_append(this);
});
$(window).resize(function() {

@ -245,7 +245,8 @@
}
#markmessagemenu li a,
#compose-attachments li a
#compose-attachments li a.delete,
#compose-attachments li a.cancelupload
{
background: url(images/messageicons.png) no-repeat;
}
@ -1147,11 +1148,12 @@ table.headers-table tr td.header span
#attachment-list li a.drop {
background: url(images/icons/down_small.gif) no-repeat center 6px;
width: 12px;
height: 7px;
height: 16px;
cursor: pointer;
padding: 5px 0 0;
padding: 0;
margin-left: 3px;
display: inline-block;
vertical-align: middle;
}
#messagebody
@ -1547,10 +1549,11 @@ input.from_address
#compose-attachments ul li
{
position: relative;
height: 18px;
line-height: 16px;
font-size: 11px;
padding: 2px 2px 1px 2px;
padding: 2px 16px 1px 2px;
border-bottom: 1px solid #EBEBEB;
white-space: nowrap;
overflow: hidden;
@ -1558,7 +1561,8 @@ input.from_address
-o-text-overflow: ellipsis;
}
#compose-attachments li a
#compose-attachments li a.delete,
#compose-attachments li a.cancelupload
{
text-indent: -5000px;
width: 17px;
@ -1567,6 +1571,7 @@ input.from_address
display: inline-block;
text-decoration: none;
vertical-align: middle;
background-position: 0px -392px;
}
#compose-attachments li img
@ -1574,18 +1579,23 @@ input.from_address
vertical-align: middle;
}
#compose-attachments li a.delete,
#compose-attachments li a.cancelupload
{
background-position: 0px -392px;
}
#compose-attachments li span
{
line-height: 18px;
vertical-align: middle;
}
#compose-attachments li a.drop {
background: url(images/icons/down_small.gif) no-repeat center 8px;
width: 16px;
height: 22px;
cursor: pointer;
display: block;
position: absolute;
right: 0;
top: 0;
}
#upload-form,
#attachment-form
{

@ -161,7 +161,7 @@
<div id="compose-attachments">
<div class="boxtitle"><roundcube:label name="attachments" /></div>
<div class="boxlistcontent">
<roundcube:object name="composeAttachmentList" id="attachmentslist" loadingIcon="/images/display/loading_blue.gif" />
<roundcube:object name="composeAttachmentList" id="attachmentslist" loadingIcon="/images/display/loading_blue.gif" icon_pos="left" />
</div>
<div class="boxfooter">
<roundcube:button name="uploadmenulink" id="uploadmenulink" type="link" title="addattachment" class="button addgroup" onclick="rcmail_ui.show_popup('uploadmenu', true);return false" content=" " />
@ -216,6 +216,14 @@
<roundcube:object name="composeAttachmentForm" id="attachment-form" attachmentFieldSize="40" class="popupmenu" />
<div id="attachmentmenu" class="popupmenu">
<ul class="toolbarmenu">
<li><roundcube:button command="open-attachment" id="attachmenuopen" type="link" label="open" class="openlink" classAct="openlink active" innerclass="openlink" /></li>
<li><roundcube:button command="download-attachment" id="attachmenudownload" type="link" label="download" class="downloadlink" classAct="downloadlink active" innerclass="downloadlink" /></li>
<roundcube:container name="attachmentmenu" id="attachmentmenu" />
</ul>
</div>
<script type="text/javascript">
rcube_init_mail_ui();
</script>

@ -2916,6 +2916,7 @@ ul.toolbarmenu li span.copy {
position: relative;
background: url(images/filetypes.png) 0 0 no-repeat;
margin-bottom: 1px;
line-height: 24px;
}
.attachmentslist li.txt,
@ -3018,8 +3019,7 @@ ul.toolbarmenu li span.copy {
background-position: 0 -546px;
}
.attachmentslist li a,
#compose-attachments ul li {
.attachmentslist li a {
display: block;
color: #333;
font-weight: bold;
@ -3047,6 +3047,10 @@ ul.toolbarmenu li span.copy {
outline: none;
}
#compose-attachments .attachmentslist li a.drop {
right: 24px;
}
.attachmentslist li a:focus,
.attachmentslist li a.drop:focus {
background-color: rgba(30,150,192, 0.5);
@ -3054,7 +3058,7 @@ ul.toolbarmenu li span.copy {
}
#compose-attachments ul li {
padding-right: 28px;
padding-right: 24px;
}
.attachmentslist li a:hover {
@ -3062,7 +3066,8 @@ ul.toolbarmenu li span.copy {
}
.attachmentslist li.uploading {
background: url(images/ajaxloader.gif) 2px 6px no-repeat;
background: url(images/ajaxloader.gif) 4px 4px no-repeat;
padding-left: 30px;
}
.attachmentslist li a.delete,

@ -220,6 +220,14 @@
</ul>
</div>
<div id="attachmentmenu" class="popupmenu" aria-hidden="true">
<ul class="toolbarmenu" id="attachmentoptionsmenu" role="menu">
<roundcube:button command="open-attachment" id="attachmenuopen" type="link-menuitem" label="open" class="icon" classAct="icon active" innerclass="icon extwin" />
<roundcube:button command="download-attachment" id="attachmenudownload" type="link-menuitem" label="download" class="icon" classAct="icon active" innerclass="icon download" />
<roundcube:container name="attachmentmenu" id="attachmentoptionsmenu" />
</ul>
</div>
<roundcube:include file="/includes/footer.html" />
</body>

@ -159,14 +159,7 @@ function rcube_mail_ui()
// add menu link for each attachment
$('#attachment-list > li').each(function() {
$(this).append($('<a class="drop" tabindex="0" aria-haspopup="true">Show options</a>')
.on('click keypress', function(e) {
if (e.type != 'keypress' || rcube_event.get_keycode(e) == 13) {
attachmentmenu(this, e);
return false;
}
})
);
attachmentmenu_append(this);
});
if (get_pref('previewheaders') == '1') {
@ -179,8 +172,9 @@ function rcube_mail_ui()
}
else if (rcmail.env.action == 'compose') {
rcmail.addEventListener('aftersend-attachment', show_uploadform)
.addEventListener('fileappended', function(e) { attachmentmenu_append(e.item); })
.addEventListener('aftertoggle-editor', function(e) {
window.setTimeout(function(){ layout_composeview() }, 200);
window.setTimeout(function() { layout_composeview() }, 200);
if (e && e.mode)
$("select[name='editorSelector']").val(e.mode);
})
@ -226,6 +220,11 @@ function rcube_mail_ui()
new rcube_splitter({ id:'composesplitterv', p1:'#composeview-left', p2:'#composeview-right',
orientation:'v', relative:true, start:206, min:170, size:12, render:layout_composeview }).init();
// add menu link for each attachment
$('#attachment-list > li').each(function() {
attachmentmenu_append(this);
});
}
else if (rcmail.env.action == 'list' || !rcmail.env.action) {
var previewframe = $('#mailpreviewframe').is(':visible');
@ -878,6 +877,20 @@ function rcube_mail_ui()
});
}
// append drop-icon to attachments list item (to invoke attachment menu)
function attachmentmenu_append(item)
{
item = $(item);
if (!item.children('.drop').length)
item.append($('<a class="drop" tabindex="0" aria-haspopup="true">Show options</a>')
.on('click keypress', function(e) {
if (e.type != 'keypress' || rcube_event.get_keycode(e) == 13) {
attachmentmenu(this, e);
return false;
}
}));
}
/**
*

Loading…
Cancel
Save