Support images in HTML signatures (#1488676)

This enables image button and file browser in html editor for signatures
pull/200/head
Aleksander Machniak 10 years ago
parent c3bb0d32a5
commit 3cc1afa1c2

@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
- Support images in HTML signatures (#1488676)
- Display full quota information in popup (#1485769, #1486604)
- Mail compose: Selecting contact inserts recipient to previously focused input - to/cc/bcc accordingly (#1489684)
- Add option to set default message list mode - default_list_mode (#1487312)

@ -481,6 +481,10 @@ $config['mdn_use_from'] = false;
// 4 - one identity with possibility to edit only signature
$config['identities_level'] = 0;
// Maximum size of uploaded image in kilobytes
// Images (in html signatures) are stored in database as data URIs
$config['identity_image_size'] = 64;
// Mimetypes supported by the browser.
// attachments of these types will open in a preview window
// either a comma-separated list or an array: 'text/plain,text/html,text/xml,image/jpeg,image/gif,image/png,application/pdf'

@ -1944,8 +1944,10 @@ class rcmail extends rcube
/**
* Initializes file uploading interface.
*
* @param $int Optional maximum file size in bytes
*/
public function upload_init()
public function upload_init($max_size = null)
{
// Enable upload progress bar
if ($seconds = $this->config->get('upload_progress')) {
@ -1973,6 +1975,10 @@ class rcmail extends rcube
$max_filesize = $max_postsize;
}
if ($max_size && $max_size < $max_filesize) {
$max_filesize = $max_size;
}
$this->output->set_env('max_filesize', $max_filesize);
$max_filesize = $this->show_bytes($max_filesize);
$this->output->set_env('filesizeerror', $this->gettext(array(
@ -1981,6 +1987,77 @@ class rcmail extends rcube
return $max_filesize;
}
/**
* Outputs uploaded file content (with image thumbnails support
*
* @param array $file Upload file data
*/
public function display_uploaded_file($file)
{
if (empty($file)) {
return;
}
$file = $this->plugins->exec_hook('attachment_display', $file);
if ($file['status']) {
if (empty($file['size'])) {
$file['size'] = $file['data'] ? strlen($file['data']) : @filesize($file['path']);
}
// generate image thumbnail for file browser in HTML editor
if (!empty($_GET['_thumbnail'])) {
$temp_dir = $this->config->get('temp_dir');
$thumbnail_size = 80;
list(,$ext) = explode('/', $file['mimetype']);
$mimetype = $file['mimetype'];
$file_ident = $file['id'] . ':' . $file['mimetype'] . ':' . $file['size'];
$cache_basename = $temp_dir . '/' . md5($file_ident . ':' . $this->user->ID . ':' . $thumbnail_size);
$cache_file = $cache_basename . '.' . $ext;
// render thumbnail image if not done yet
if (!is_file($cache_file)) {
if (!$file['path']) {
$orig_name = $filename = $cache_basename . '.orig.' . $ext;
file_put_contents($orig_name, $file['data']);
}
else {
$filename = $file['path'];
}
$image = new rcube_image($filename);
if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) {
$mimetype = 'image/' . $imgtype;
if ($orig_name) {
unlink($orig_name);
}
}
}
if (is_file($cache_file)) {
// cache for 1h
$this->output->future_expire_header(3600);
header('Content-Type: ' . $mimetype);
header('Content-Length: ' . filesize($cache_file));
readfile($cache_file);
exit;
}
}
header('Content-Type: ' . $file['mimetype']);
header('Content-Length: ' . $file['size']);
if ($file['data']) {
echo $file['data'];
}
else if ($file['path']) {
readfile($file['path']);
}
}
}
/**
* Initializes client-side autocompletion.
*/

@ -4083,6 +4083,14 @@ function rcube_webmail()
if (upload_id)
this.triggerEvent('fileuploaded', {name: name, attachment: att, id: upload_id});
if (!this.env.attachments)
this.env.attachments = {};
if (upload_id && this.env.attachments[upload_id])
delete this.env.attachments[upload_id];
this.env.attachments[name] = att;
if (!this.gui_objects.attachmentlist)
return false;
@ -4112,11 +4120,6 @@ function rcube_webmail()
var tabindex = $(this.gui_objects.attachmentlist).attr('data-tabindex') || '0';
li.find('a').attr('tabindex', tabindex);
if (upload_id && this.env.attachments[upload_id])
delete this.env.attachments[upload_id];
this.env.attachments[name] = att;
return true;
};
@ -7563,7 +7566,7 @@ function rcube_webmail()
$(form).attr({
target: frame_name,
action: this.url(action, { _id:this.env.compose_id||'', _uploadid:ts }),
action: this.url(action, {_id: this.env.compose_id || '', _uploadid: ts, _from: this.env.action}),
method: 'POST'})
.attr(form.encoding ? 'encoding' : 'enctype', 'multipart/form-data')
.submit();

@ -65,10 +65,12 @@ function rcube_text_editor(config, id)
// minimal editor
if (config.mode == 'identity') {
$.extend(conf, {
plugins: 'autolink charmap code colorpicker hr link paste tabfocus textcolor',
plugins: 'autolink charmap code colorpicker hr image link paste tabfocus textcolor',
toolbar: 'bold italic underline alignleft aligncenter alignright alignjustify'
+ ' | outdent indent charmap hr link unlink code forecolor'
+ ' | fontselect fontsizeselect'
+ ' | outdent indent charmap hr link unlink image code forecolor'
+ ' | fontselect fontsizeselect',
file_browser_callback: function(name, url, type, win) { ref.file_browser_callback(name, url, type); },
file_browser_callback_types: 'image'
});
}
// full-featured editor
@ -610,6 +612,8 @@ function rcube_text_editor(config, id)
}
});
}
// @todo: upload progress indicator
};
// close file browser window
@ -652,7 +656,9 @@ function rcube_text_editor(config, id)
}
if (rx.test(file.mimetype)) {
var href = rcmail.env.comm_path+'&_id='+rcmail.env.compose_id+'&_action=display-attachment&_file='+file_id,
var path = rcmail.env.comm_path + '&_from=' + rcmail.env.action,
action = rcmail.env.compose_id ? '&_id=' + rcmail.env.compose_id + '&_action=display-attachment' : '&_action=upload-display',
href = path + action + '&_file=' + file_id,
img = $('<img>').attr({title: file.name, src: img_src ? img_src : href + '&_thumbnail=1'});
return $('<li>').attr({tabindex: 0})
@ -686,7 +692,7 @@ function rcube_text_editor(config, id)
this.hack_file_input = function(elem, clone_form)
{
var link = $(elem),
file = $('<input>'),
file = $('<input>').attr('name', '_files[]'),
form = $('<form>').attr({method: 'post', enctype: 'multipart/form-data'}),
offset = link.offset();

@ -38,7 +38,7 @@ if (!$COMPOSE) {
// remove an attachment
if ($RCMAIL->action=='remove-attachment') {
if ($RCMAIL->action == 'remove-attachment') {
$id = 'undefined';
if (preg_match('/^rcmfile(\w+)$/', $_POST['_file'], $regs)) {
@ -67,66 +67,7 @@ if ($RCMAIL->action == 'display-attachment') {
$id = $regs[1];
}
if ($attachment = $COMPOSE['attachments'][$id]) {
$attachment = $RCMAIL->plugins->exec_hook('attachment_display', $attachment);
}
if ($attachment['status']) {
if (empty($attachment['size'])) {
$attachment['size'] = $attachment['data'] ? strlen($attachment['data']) : @filesize($attachment['path']);
}
// generate image thumbnail for file browser in HTML editor
if (!empty($_GET['_thumbnail'])) {
$temp_dir = $RCMAIL->config->get('temp_dir');
$thumbnail_size = 80;
list(,$ext) = explode('/', $attachment['mimetype']);
$mimetype = $attachment['mimetype'];
$file_ident = $attachment['id'] . ':' . $attachment['mimetype'] . ':' . $attachment['size'];
$cache_basename = $temp_dir . '/' . md5($file_ident . ':' . $RCMAIL->user->ID . ':' . $thumbnail_size);
$cache_file = $cache_basename . '.' . $ext;
// render thumbnail image if not done yet
if (!is_file($cache_file)) {
if (!$attachment['path']) {
$orig_name = $filename = $cache_basename . '.orig.' . $ext;
file_put_contents($orig_name, $attachment['data']);
}
else {
$filename = $attachment['path'];
}
$image = new rcube_image($filename);
if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) {
$mimetype = 'image/' . $imgtype;
if ($orig_name) {
unlink($orig_name);
}
}
}
if (is_file($cache_file)) {
// cache for 1h
$RCMAIL->output->future_expire_header(3600);
header('Content-Type: ' . $mimetype);
header('Content-Length: ' . filesize($cache_file));
readfile($cache_file);
exit;
}
}
header('Content-Type: ' . $attachment['mimetype']);
header('Content-Length: ' . $attachment['size']);
if ($attachment['data']) {
echo $attachment['data'];
}
else if ($attachment['path']) {
readfile($attachment['path']);
}
}
$RCMAIL->display_uploaded_file($COMPOSE['attachments'][$id]);
exit;
}

@ -176,5 +176,15 @@ function rcube_identity_form($attrib)
$out .= $form_end;
// add image upload form
$max_filesize = $RCMAIL->upload_init($RCMAIL->config->get('identity_image_size', 64) * 1024);
$upload_form_id = 'identityImageUpload';
$out .= '<form id="' . $upload_form_id . '" style="display: none">'
. html::div('hint', $RCMAIL->gettext(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))))
. '</form>';
$RCMAIL->output->add_gui_object('uploadform', $upload_form_id);
return $out;
}

@ -44,6 +44,7 @@ $RCMAIL->register_action_map(array(
'add-response' => 'edit_response.inc',
'save-response' => 'edit_response.inc',
'delete-response' => 'responses.inc',
'upload-display' => 'upload.inc',
));

@ -79,8 +79,11 @@ foreach ($email_checks as $email) {
}
}
// XSS protection in HTML signature (#1489251)
if (!empty($save_data['signature']) && !empty($save_data['html_signature'])) {
// replace uploaded images with data URIs
$save_data['signature'] = rcmail_attach_images($save_data['signature']);
// XSS protection in HTML signature (#1489251)
$save_data['signature'] = rcmail_wash_html($save_data['signature']);
// clear POST data of signature, we want to use safe content
@ -190,6 +193,35 @@ else {
}
/**
* Attach uploaded images into signature as data URIs
*/
function rcmail_attach_images($html)
{
global $RCMAIL;
$offset = 0;
$regexp = '/\s(poster|src)\s*=\s*[\'"]*\S+upload-display\S+file=rcmfile([0-9]+)[\s\'"]*/';
while (preg_match($regexp, $html, $matches, 0, $offset)) {
$file_id = $matches[2];
$data_uri = ' ';
if ($file_id && ($file = $_SESSION['identity']['files'][$file_id])) {
$file = $RCMAIL->plugins->exec_hook('attachment_get', $file);
$data_uri .= 'src="data:' . $file['mimetype'] . ';base64,';
$data_uri .= base64_encode($file['data'] ? $file['data'] : file_get_contents($file['path']));
$data_uri .= '" ';
}
$html = str_replace($matches[0], $data_uri, $html);
$offset += strlen($data_uri) - strlen($matches[0]) + 1;
}
return $html;
}
/**
* Sanity checks/cleanups on HTML body of signature
*/

Loading…
Cancel
Save