Emoticons-related code refactoring

- Emoticons: All emoticons-related functionality is handled by the plugin now
- Emoticons: Added option to switch on/off emoticons in compose editor (#1485732)
- Emoticons: Added option to switch on/off emoticons in plain text messages
- Plugin API: Added disabled_plugins an disabled_buttons options in html_editor hook
- Plugin API: Added html2text hook
pull/294/head
Aleksander Machniak 9 years ago
parent 3516b02344
commit a63f14ec40

@ -1,6 +1,9 @@
CHANGELOG Roundcube Webmail
===========================
- Emoticons: Added option to switch on/off emoticons in compose editor (#1485732)
- Emoticons: Added option to switch on/off emoticons in plain text messages
- Emoticons: All emoticons-related functionality is handled by the plugin now
- Installer: Add button to save generated config file in system temp directory (#1488149)
- Remove common subject prefixes Re:, Re[x]:, Re-x: on reply (#1490497)
- Added GSSAPI/Kerberos authentication plugin - krb_authentication
@ -13,6 +16,8 @@ CHANGELOG Roundcube Webmail
- Installer: Remove system() function use (#1490139)
- Password plugin: Added 'kpasswd' driver by Peter Allgeyer
- Add initdb.sh to create database from initial.sql script with prefix support (#1490188)
- Plugin API: Added disabled_plugins an disabled_buttons options in html_editor hook
- Plugin API: Added html2text hook
- Plugin API: Added message_part_body hook
- Plugin API: Added message_ready hook
- Plugin API: Add special onload() method to execute plugin actions before startup (session and GUI initialization)

@ -1,9 +1,9 @@
{
"name": "roundcube/emoticons",
"type": "roundcube-plugin",
"description": "Sample plugin to replace emoticons in plain text message body with real icons.",
"description": "Plugin that adds emoticons support.",
"license": "GPLv3+",
"version": "1.4",
"version": "2.0",
"authors": [
{
"name": "Thomas Bruederli",

@ -0,0 +1,7 @@
<?php
// Enable emoticons in plain text messages preview
$config['emoticons_display'] = false;
// Enable emoticons in compose editor (HTML)
$config['emoticons_compose'] = true;

@ -1,9 +1,10 @@
<?php
/**
* Display Emoticons
* Emoticons
*
* Sample plugin to replace emoticons in plain text message body with real icons
* Plugin to replace emoticons in plain text message body with real icons.
* Also it enables emoticons in HTML compose editor. Both features are optional.
*
* @version @package_version@
* @license GNU GPLv3+
@ -13,66 +14,173 @@
*/
class emoticons extends rcube_plugin
{
public $task = 'mail';
public $task = 'mail|settings|utils';
/**
* Plugin initilization.
*/
function init()
{
$this->add_hook('message_part_after', array($this, 'replace'));
$rcube = rcube::get_instance();
$this->add_hook('message_part_after', array($this, 'message_part_after'));
$this->add_hook('message_outgoing_body', array($this, 'message_outgoing_body'));
$this->add_hook('html2text', array($this, 'html2text'));
$this->add_hook('html_editor', array($this, 'html_editor'));
if ($rcube->task == 'settings') {
$this->add_hook('preferences_list', array($this, 'preferences_list'));
$this->add_hook('preferences_save', array($this, 'preferences_save'));
}
}
function replace($args)
/**
* 'message_part_after' hook handler to replace common plain text emoticons
* with emoticon images (<img>)
*/
function message_part_after($args)
{
// This is a lookbehind assertion which will exclude html entities
// E.g. situation when ";)" in "&quot;)" shouldn't be replaced by the icon
// It's so long because of assertion format restrictions
$entity = '(?<!&'
. '[a-zA-Z0-9]{2}' . '|' . '#[0-9]{2}' . '|'
. '[a-zA-Z0-9]{3}' . '|' . '#[0-9]{3}' . '|'
. '[a-zA-Z0-9]{4}' . '|' . '#[0-9]{4}' . '|'
. '[a-zA-Z0-9]{5}' . '|'
. '[a-zA-Z0-9]{6}' . '|'
. '[a-zA-Z0-9]{7}'
. ')';
// map of emoticon replacements
$map = array(
'/(?<!mailto):D/' => $this->img_tag('smiley-laughing.gif', ':D' ),
'/:-D/' => $this->img_tag('smiley-laughing.gif', ':-D' ),
'/:\(/' => $this->img_tag('smiley-frown.gif', ':(' ),
'/:-\(/' => $this->img_tag('smiley-frown.gif', ':-(' ),
'/'.$entity.';\)/' => $this->img_tag('smiley-wink.gif', ';)' ),
'/'.$entity.';-\)/' => $this->img_tag('smiley-wink.gif', ';-)' ),
'/8\)/' => $this->img_tag('smiley-cool.gif', '8)' ),
'/8-\)/' => $this->img_tag('smiley-cool.gif', '8-)' ),
'/(?<!mailto):O/i' => $this->img_tag('smiley-surprised.gif', ':O' ),
'/(?<!mailto):-O/i' => $this->img_tag('smiley-surprised.gif', ':-O' ),
'/(?<!mailto):P/i' => $this->img_tag('smiley-tongue-out.gif', ':P' ),
'/(?<!mailto):-P/i' => $this->img_tag('smiley-tongue-out.gif', ':-P' ),
'/(?<!mailto):@/i' => $this->img_tag('smiley-yell.gif', ':@' ),
'/(?<!mailto):-@/i' => $this->img_tag('smiley-yell.gif', ':-@' ),
'/O:\)/i' => $this->img_tag('smiley-innocent.gif', 'O:)' ),
'/O:-\)/i' => $this->img_tag('smiley-innocent.gif', 'O:-)' ),
'/(?<!O):\)/' => $this->img_tag('smiley-smile.gif', ':)' ),
'/(?<!O):-\)/' => $this->img_tag('smiley-smile.gif', ':-)' ),
'/(?<!mailto):\$/' => $this->img_tag('smiley-embarassed.gif', ':$' ),
'/(?<!mailto):-\$/' => $this->img_tag('smiley-embarassed.gif', ':-$' ),
'/(?<!mailto):\*/i' => $this->img_tag('smiley-kiss.gif', ':*' ),
'/(?<!mailto):-\*/i' => $this->img_tag('smiley-kiss.gif', ':-*' ),
'/(?<!mailto):S/i' => $this->img_tag('smiley-undecided.gif', ':S' ),
'/(?<!mailto):-S/i' => $this->img_tag('smiley-undecided.gif', ':-S' ),
if ($args['type'] == 'plain') {
$this->load_config();
$rcube = rcube::get_instance();
if (!$rcube->config->get('emoticons_display', false)) {
return $args;
}
require_once __DIR__ . '/emoticons_engine.php';
$args['body'] = emoticons_engine::text2icons($args['body']);
}
return $args;
}
/**
* 'message_outgoing_body' hook handler to replace image emoticons from TinyMCE
* editor with image attachments.
*/
function message_outgoing_body($args)
{
if ($args['type'] == 'html') {
$this->load_config();
$rcube = rcube::get_instance();
if (!$rcube->config->get('emoticons_compose', true)) {
return $args;
}
require_once __DIR__ . '/emoticons_engine.php';
// look for "emoticon" images from TinyMCE and change their src paths to
// be file paths on the server instead of URL paths.
$images = emoticons_engine::replace($args['body']);
// add these images as attachments to the MIME message
foreach ($images as $img_name => $img_file) {
$args['message']->addHTMLImage($img_file, 'image/gif', '', true, $img_name);
}
}
return $args;
}
/**
* 'html2text' hook handler to replace image emoticons from TinyMCE
* editor with plain text emoticons.
*
* This is executed on html2text action, i.e. when switching from HTML to text
* in compose window (or similiar place). Also when generating alternative
* text/plain part.
*/
function html2text($args)
{
$rcube = rcube::get_instance();
if ($rcube->action == 'html2text' || $rcube->action == 'send') {
$this->load_config();
if (!$rcube->config->get('emoticons_compose', true)) {
return $args;
}
require_once __DIR__ . '/emoticons_engine.php';
$args['body'] = emoticons_engine::icons2text($args['body']);
}
return $args;
}
/**
* 'html_editor' hook handler, where we enable emoticons in TinyMCE
*/
function html_editor($args)
{
$rcube = rcube::get_instance();
$this->load_config();
if (!$rcube->config->get('emoticons_compose', true)) {
$args['disabled_plugins'][] = 'emoticons';
$args['disabled_buttons'][] = 'emoticons';
}
return $args;
}
/**
* 'preferences_list' hook handler
*/
function preferences_list($args)
{
$rcube = rcube::get_instance();
$dont_override = $rcube->config->get('dont_override', array());
if ($args['section'] == 'mailview' && !in_array('emoticons_display', $dont_override)) {
$this->load_config();
$this->add_texts('localization');
$field_id = 'emoticons_display';
$checkbox = new html_checkbox(array('name' => '_' . $field_id, 'id' => $field_id, 'value' => 1));
$args['blocks']['main']['options']['emoticons_display'] = array(
'title' => $this->gettext('emoticonsdisplay'),
'content' => $checkbox->show(intval($rcube->config->get('emoticons_display', false)))
);
}
else if ($args['section'] == 'compose' && !in_array('emoticons_compose', $dont_override)) {
$this->load_config();
$this->add_texts('localization');
if ($args['type'] == 'plain') {
$args['body'] = preg_replace(
array_keys($map), array_values($map), $args['body']);
$field_id = 'emoticons_compose';
$checkbox = new html_checkbox(array('name' => '_' . $field_id, 'id' => $field_id, 'value' => 1));
$args['blocks']['main']['options']['emoticons_compose'] = array(
'title' => $this->gettext('emoticonscompose'),
'content' => $checkbox->show(intval($rcube->config->get('emoticons_compose', true)))
);
}
return $args;
}
private function img_tag($ico, $title)
/**
* 'preferences_save' hook handler
*/
function preferences_save($args)
{
$path = './program/js/tinymce/plugins/emoticons/img/';
return html::img(array('src' => $path.$ico, 'title' => $title));
$rcube = rcube::get_instance();
$dont_override = $rcube->config->get('dont_override', array());
if ($args['section'] == 'mailview' && !in_array('emoticons_display', $dont_override)) {
$args['prefs']['emoticons_display'] = rcube_utils::get_input_value('_emoticons_display', rcube_utils::INPUT_POST) ? true : false;
}
else if ($args['section'] == 'compose' && !in_array('emoticons_compose', $dont_override)) {
$args['prefs']['emoticons_compose'] = rcube_utils::get_input_value('_emoticons_compose', rcube_utils::INPUT_POST) ? true : false;
}
return $args;
}
}

@ -0,0 +1,152 @@
<?php
/**
* @license GNU GPLv3+
* @author Thomas Bruederli
* @author Aleksander Machniak
*/
class emoticons_engine
{
const IMG_PATH = 'program/js/tinymce/plugins/emoticons/img/';
/**
* Replaces TinyMCE's emoticon images with plain-text representation
*
* @param string $html HTML content
*
* @return string HTML content
*/
public static function icons2text($html)
{
$emoticons = array(
'8-)' => 'smiley-cool',
':-#' => 'smiley-foot-in-mouth',
':-*' => 'smiley-kiss',
':-X' => 'smiley-sealed',
':-P' => 'smiley-tongue-out',
':-@' => 'smiley-yell',
":'(" => 'smiley-cry',
':-(' => 'smiley-frown',
':-D' => 'smiley-laughing',
':-)' => 'smiley-smile',
':-S' => 'smiley-undecided',
':-$' => 'smiley-embarassed',
'O:-)' => 'smiley-innocent',
':-|' => 'smiley-money-mouth',
':-O' => 'smiley-surprised',
';-)' => 'smiley-wink',
);
foreach ($emoticons as $idx => $file) {
// <img title="Cry" src="http://.../program/js/tinymce/plugins/emoticons/img/smiley-cry.gif" border="0" alt="Cry" />
$file = preg_quote(self::IMG_PATH . $file . '.gif', '/');
$search[] = '/<img (title="[a-z ]+" )?src="[^"]+' . $file . '"[^>]+\/>/i';
$replace[] = $idx;
}
return preg_replace($search, $replace, $html);
}
/**
* Replace common plain text emoticons with empticon <img> tags
*
* @param string $text Text
*
* @return string Converted text
*/
public static function text2icons($text)
{
// This is a lookbehind assertion which will exclude html entities
// E.g. situation when ";)" in "&quot;)" shouldn't be replaced by the icon
// It's so long because of assertion format restrictions
$entity = '(?<!&'
. '[a-zA-Z0-9]{2}' . '|' . '#[0-9]{2}' . '|'
. '[a-zA-Z0-9]{3}' . '|' . '#[0-9]{3}' . '|'
. '[a-zA-Z0-9]{4}' . '|' . '#[0-9]{4}' . '|'
. '[a-zA-Z0-9]{5}' . '|'
. '[a-zA-Z0-9]{6}' . '|'
. '[a-zA-Z0-9]{7}'
. ')';
// map of emoticon replacements
$map = array(
'/(?<!mailto):D/' => self::img_tag('smiley-laughing.gif', ':D' ),
'/:-D/' => self::img_tag('smiley-laughing.gif', ':-D' ),
'/:\(/' => self::img_tag('smiley-frown.gif', ':(' ),
'/:-\(/' => self::img_tag('smiley-frown.gif', ':-(' ),
'/'.$entity.';\)/' => self::img_tag('smiley-wink.gif', ';)' ),
'/'.$entity.';-\)/' => self::img_tag('smiley-wink.gif', ';-)' ),
'/8\)/' => self::img_tag('smiley-cool.gif', '8)' ),
'/8-\)/' => self::img_tag('smiley-cool.gif', '8-)' ),
'/(?<!mailto):O/i' => self::img_tag('smiley-surprised.gif', ':O' ),
'/(?<!mailto):-O/i' => self::img_tag('smiley-surprised.gif', ':-O' ),
'/(?<!mailto):P/i' => self::img_tag('smiley-tongue-out.gif', ':P' ),
'/(?<!mailto):-P/i' => self::img_tag('smiley-tongue-out.gif', ':-P' ),
'/(?<!mailto):@/i' => self::img_tag('smiley-yell.gif', ':@' ),
'/(?<!mailto):-@/i' => self::img_tag('smiley-yell.gif', ':-@' ),
'/O:\)/i' => self::img_tag('smiley-innocent.gif', 'O:)' ),
'/O:-\)/i' => self::img_tag('smiley-innocent.gif', 'O:-)' ),
'/(?<!O):\)/' => self::img_tag('smiley-smile.gif', ':)' ),
'/(?<!O):-\)/' => self::img_tag('smiley-smile.gif', ':-)' ),
'/(?<!mailto):\$/' => self::img_tag('smiley-embarassed.gif', ':$' ),
'/(?<!mailto):-\$/' => self::img_tag('smiley-embarassed.gif', ':-$' ),
'/(?<!mailto):\*/i' => self::img_tag('smiley-kiss.gif', ':*' ),
'/(?<!mailto):-\*/i' => self::img_tag('smiley-kiss.gif', ':-*' ),
'/(?<!mailto):S/i' => self::img_tag('smiley-undecided.gif', ':S' ),
'/(?<!mailto):-S/i' => self::img_tag('smiley-undecided.gif', ':-S' ),
);
return preg_replace(array_keys($map), array_values($map), $text);
}
protected static function img_tag($ico, $title)
{
return html::img(array('src' => './' . self::IMG_PATH . $ico, 'title' => $title));
}
/**
* Replace emoticon icons <img> 'src' attribute, so it can
* be replaced with real file by Mail_Mime.
*
* @param string &$html HTML content
*
* @return array List of image files
*/
public static function replace(&$html)
{
// Replace this:
// <img src="http[s]://.../tinymce/plugins/emoticons/img/smiley-cool.gif" ... />
// with this:
// <img src="/path/on/server/.../tinymce/plugins/emoticons/img/smiley-cool.gif" ... />
$rcube = rcube::get_instance();
$assets_dir = $rcube->config->get('assets_dir');
$path = unslashify($assets_dir ?: INSTALL_PATH) . '/' . self::IMG_PATH;
$offset = 0;
$images = array();
// remove any null-byte characters before parsing
$html = preg_replace('/\x00/', '', $html);
if (preg_match_all('# src=[\'"]([^\'"]+)#', $html, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[1] as $m) {
// find emoticon image tags
if (preg_match('#'. self::IMG_PATH . '(.*)$#', $m[0], $imatches)) {
$image_name = $imatches[1];
// sanitize image name so resulting attachment doesn't leave images dir
$image_name = preg_replace('/[^a-zA-Z0-9_\.\-]/i', '', $image_name);
$image_file = $path . $image_name;
// Add the same image only once
$images[$image_name] = $image_file;
$html = substr_replace($html, $image_file, $m[1] + $offset, strlen($m[0]));
$offset += strlen($image_file) - strlen($m[0]);
}
}
}
return $images;
}
}

@ -0,0 +1,23 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/emoticons/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Emoticons plugin |
| Copyright (C) 2012-2015, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-emoticons/
*/
$labels = array();
$labels['emoticonsdisplay'] = 'Display emoticons in plain text messages';
$labels['emoticonscompose'] = 'Enable emoticons';
?>

@ -19,45 +19,4 @@ class Emoticons_Plugin extends PHPUnit_Framework_TestCase
$this->assertInstanceOf('emoticons', $plugin);
$this->assertInstanceOf('rcube_plugin', $plugin);
}
/**
* replace() method tests
*/
function test_replace()
{
$rcube = rcube::get_instance();
$plugin = new emoticons($rcube->api);
$map = array(
':D' => array('smiley-laughing.gif', ':D' ),
':-D' => array('smiley-laughing.gif', ':-D' ),
':(' => array('smiley-frown.gif', ':(' ),
':-(' => array('smiley-frown.gif', ':-(' ),
'8)' => array('smiley-cool.gif', '8)' ),
'8-)' => array('smiley-cool.gif', '8-)' ),
':O' => array('smiley-surprised.gif', ':O' ),
':-O' => array('smiley-surprised.gif', ':-O' ),
':P' => array('smiley-tongue-out.gif', ':P' ),
':-P' => array('smiley-tongue-out.gif', ':-P' ),
':@' => array('smiley-yell.gif', ':@' ),
':-@' => array('smiley-yell.gif', ':-@' ),
'O:)' => array('smiley-innocent.gif', 'O:)' ),
'O:-)' => array('smiley-innocent.gif', 'O:-)' ),
':)' => array('smiley-smile.gif', ':)' ),
':-)' => array('smiley-smile.gif', ':-)' ),
':$' => array('smiley-embarassed.gif', ':$' ),
':-$' => array('smiley-embarassed.gif', ':-$' ),
':*' => array('smiley-kiss.gif', ':*' ),
':-*' => array('smiley-kiss.gif', ':-*' ),
':S' => array('smiley-undecided.gif', ':S' ),
':-S' => array('smiley-undecided.gif', ':-S' ),
);
foreach ($map as $body => $expected) {
$args = array('type' => 'plain', 'body' => $body);
$args = $plugin->replace($args);
$this->assertRegExp('/' . preg_quote($expected[0], '/') . '/', $args['body']);
$this->assertRegExp('/title="' . preg_quote($expected[1], '/') . '"/', $args['body']);
}
}
}

@ -0,0 +1,48 @@
<?php
class EmoticonsEngine extends PHPUnit_Framework_TestCase
{
function setUp()
{
include_once __DIR__ . '/../emoticons_engine.php';
}
/**
* text2icons() method tests
*/
function test_text2icons()
{
$map = array(
':D' => array('smiley-laughing.gif', ':D' ),
':-D' => array('smiley-laughing.gif', ':-D' ),
':(' => array('smiley-frown.gif', ':(' ),
':-(' => array('smiley-frown.gif', ':-(' ),
'8)' => array('smiley-cool.gif', '8)' ),
'8-)' => array('smiley-cool.gif', '8-)' ),
':O' => array('smiley-surprised.gif', ':O' ),
':-O' => array('smiley-surprised.gif', ':-O' ),
':P' => array('smiley-tongue-out.gif', ':P' ),
':-P' => array('smiley-tongue-out.gif', ':-P' ),
':@' => array('smiley-yell.gif', ':@' ),
':-@' => array('smiley-yell.gif', ':-@' ),
'O:)' => array('smiley-innocent.gif', 'O:)' ),
'O:-)' => array('smiley-innocent.gif', 'O:-)' ),
':)' => array('smiley-smile.gif', ':)' ),
':-)' => array('smiley-smile.gif', ':-)' ),
':$' => array('smiley-embarassed.gif', ':$' ),
':-$' => array('smiley-embarassed.gif', ':-$' ),
':*' => array('smiley-kiss.gif', ':*' ),
':-*' => array('smiley-kiss.gif', ':-*' ),
':S' => array('smiley-undecided.gif', ':S' ),
':-S' => array('smiley-undecided.gif', ':-S' ),
);
foreach ($map as $body => $expected) {
$result = emoticons_engine::text2icons($body);
$this->assertRegExp('/' . preg_quote($expected[0], '/') . '/', $result);
$this->assertRegExp('/title="' . preg_quote($expected[1], '/') . '"/', $result);
}
}
}

@ -220,11 +220,6 @@ function rcube_html_editor($mode='')
rcmail::get_instance()->html_editor($mode);
}
function rcmail_replace_emoticons($html)
{
return rcmail::get_instance()->replace_emoticons($html);
}
function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file=null, $smtp_opts=null)
{
return rcmail::get_instance()->deliver_message($message, $from, $mailto, $smtp_error, $body_file, $smtp_opts);

@ -1858,7 +1858,20 @@ class rcmail extends rcube
*/
public function html_editor($mode = '')
{
$hook = $this->plugins->exec_hook('html_editor', array('mode' => $mode));
$spellcheck = intval($this->config->get('enable_spellcheck'));
$spelldict = intval($this->config->get('spellcheck_dictionary'));
$disabled_plugins = array();
$disabled_buttons = array();
if (!$spellcheck) {
$disabled_plugins[] = 'spellchecker';
}
$hook = $this->plugins->exec_hook('html_editor', array(
'mode' => $mode,
'disabled_plugins' => $disabled_plugins,
'disabled_buttons' => $disabled_buttons,
));
if ($hook['abort']) {
return;
@ -1885,8 +1898,10 @@ class rcmail extends rcube
'mode' => $mode,
'lang' => $lang,
'skin_path' => $this->output->get_skin_path(),
'spellcheck' => intval($this->config->get('enable_spellcheck')),
'spelldict' => intval($this->config->get('spellcheck_dictionary'))
'spellcheck' => $spellcheck, // deprecated
'spelldict' => $spelldict,
'disabled_plugins' => $hook['disabled_plugins'],
'disabled_buttons' => $hook['disabled_buttons'],
);
$this->output->add_label('selectimage', 'addimage', 'selectmedia', 'addmedia');
@ -1896,43 +1911,6 @@ class rcmail extends rcube
$this->output->include_script('editor.js');
}
/**
* Replaces TinyMCE's emoticon images with plain-text representation
*
* @param string $html HTML content
*
* @return string HTML content
*/
public static function replace_emoticons($html)
{
$emoticons = array(
'8-)' => 'smiley-cool',
':-#' => 'smiley-foot-in-mouth',
':-*' => 'smiley-kiss',
':-X' => 'smiley-sealed',
':-P' => 'smiley-tongue-out',
':-@' => 'smiley-yell',
":'(" => 'smiley-cry',
':-(' => 'smiley-frown',
':-D' => 'smiley-laughing',
':-)' => 'smiley-smile',
':-S' => 'smiley-undecided',
':-$' => 'smiley-embarassed',
'O:-)' => 'smiley-innocent',
':-|' => 'smiley-money-mouth',
':-O' => 'smiley-surprised',
';-)' => 'smiley-wink',
);
foreach ($emoticons as $idx => $file) {
// <img title="Cry" src="http://.../program/js/tinymce/plugins/emoticons/img/smiley-cry.gif" border="0" alt="Cry" />
$search[] = '/<img title="[a-z ]+" src="https?:\/\/[a-z0-9_.\/-]+\/tinymce\/plugins\/emoticons\/img\/'.$file.'.gif"[^>]+\/>/i';
$replace[] = $idx;
}
return preg_replace($search, $replace, $html);
}
/**
* File upload progress handler.
*/
@ -2319,6 +2297,39 @@ class rcmail extends rcube
return file_get_contents($name, false);
}
/**
* Converts HTML content into plain text
*
* @param string $html HTML content
* @param array $options Conversion parameters (width, links, charset)
*
* @return string Plain text
*/
public function html2text($html, $options)
{
$default_options = array(
'links' => true,
'width' => 75,
'body' => $html,
'charset' => RCUBE_CHARSET,
);
$options = array_merge($default_options, $options);
// Plugins may want to modify HTML in another/additional way
$options = $this->plugins->exec_hook('html2text', $options);
// Convert to text
if (!$options['abort']) {
$converter = new rcube_html2text($options['body'],
false, $options['links'], $options['width'], $options['charset']);
$options['body'] = rtrim($converter->get_text());
}
return $options['body'];
}
/************************************************************************
********* Deprecated methods (to be removed) *********

@ -89,7 +89,7 @@ function rcube_text_editor(config, id)
else {
$.extend(conf, {
plugins: 'autolink charmap code colorpicker directionality emoticons link image media nonbreaking'
+ ' paste table tabfocus textcolor searchreplace' + (config.spellcheck ? ' spellchecker' : ''),
+ ' paste table tabfocus textcolor searchreplace spellchecker',
toolbar: 'bold italic underline | alignleft aligncenter alignright alignjustify'
+ ' | bullist numlist outdent indent ltr rtl blockquote | forecolor backcolor | fontselect fontsizeselect'
+ ' | link unlink table | emoticons charmap image media | code searchreplace undo redo',
@ -102,6 +102,15 @@ function rcube_text_editor(config, id)
});
}
// disable TinyMCE plugins/buttons from Roundcube plugin
$.each(config.disabled_plugins || [], function() {
conf.plugins = conf.plugins.replace(this, '');
});
$.each(config.disabled_plugins || [], function() {
conf.toolbar = conf.toolbar.replace(this, '');
});
conf.toolbar = conf.toolbar.replace(/\|\s+\|/g, '|');
// support external configuration settings e.g. from skin
if (window.rcmail_editor_settings)
$.extend(conf, window.rcmail_editor_settings);

@ -499,8 +499,7 @@ function rcmail_compose_header_from($attrib)
$text = $html = $sql_arr['signature'];
if ($sql_arr['html_signature']) {
$h2t = new rcube_html2text($html, false, true);
$text = trim($h2t->get_text());
$text = $RCMAIL->html2text($html);
}
else {
$t2h = new rcube_text2html($text, false);
@ -875,8 +874,7 @@ function rcmail_compose_part_body($part, $isHtml = false)
// use html part if it has been used for message (pre)viewing
// decrease line length for quoting
$len = $COMPOSE['mode'] == RCUBE_COMPOSE_REPLY ? $LINE_LENGTH-2 : $LINE_LENGTH;
$txt = new rcube_html2text($body, false, true, $len);
$body = $txt->get_text();
$body = $RCMAIL->html2text($body, array('width' => $len));
}
else {
if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') {

@ -884,8 +884,7 @@ function rcmail_print_body($body, $part, $p = array())
$data['body'] = rcube_enriched::to_html($data['body']);
}
$txt = new rcube_html2text($data['body'], false, true);
$body = $txt->get_text();
$body = $RCMAIL->html2text($data['body']);
$part->ctype_secondary = 'plain';
}
// text/html

@ -398,12 +398,8 @@ if ($isHtml) {
$MAIL_MIME->setHTMLBody($plugin['body']);
// replace emoticons
$plugin['body'] = $RCMAIL->replace_emoticons($plugin['body']);
// add a plain text version of the e-mail as an alternative part.
$h2t = new rcube_html2text($plugin['body'], false, true, 0, $message_charset);
$plainTextPart = rcube_mime::wordwrap($h2t->get_text(), $LINE_LENGTH, "\r\n", false, $message_charset);
$plainTextPart = $RCMAIL->html2text($plugin['body'], array('width' => 0, 'charset' => $message_charset));
$plainTextPart = rcube_mime::wordwrap($plainTextPart, $LINE_LENGTH, "\r\n", false, $message_charset);
$plainTextPart = wordwrap($plainTextPart, 998, "\r\n", true);
// make sure all line endings are CRLF (#1486712)
@ -412,12 +408,9 @@ if ($isHtml) {
$plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body',
array('body' => $plainTextPart, 'type' => 'alternative', 'message' => $MAIL_MIME));
// add a plain text version of the e-mail as an alternative part.
$MAIL_MIME->setTXTBody($plugin['body']);
// look for "emoticon" images from TinyMCE and change their src paths to
// be file paths on the server instead of URL paths.
rcmail_fix_emoticon_paths($MAIL_MIME);
// Extract image Data URIs into message attachments (#1488502)
rcmail_extract_inline_images($MAIL_MIME, $from);
}
@ -765,61 +758,6 @@ function rcmail_get_identity($id)
return false;
}
/**
* go from this:
* <img src="http[s]://.../tinymce/plugins/emoticons/img/smiley-cool.gif" border="0" alt="Cool" title="Cool" />
*
* to this:
*
* <img src="/path/on/server/.../tinymce/plugins/emoticons/img/smiley-cool.gif" border="0" alt="Cool" title="Cool" />
*/
function rcmail_fix_emoticon_paths($mime_message)
{
global $RCMAIL;
$body = $mime_message->getHTMLBody();
// remove any null-byte characters before parsing
$body = preg_replace('/\x00/', '', $body);
$searchstr = 'program/js/tinymce/plugins/emoticons/img/';
$assets_dir = $RCMAIL->config->get('assets_dir');
$path = ($assets_dir ?: INSTALL_PATH) . '/' . $searchstr;
$offset = 0;
// keep track of added images, so they're only added once
$included_images = array();
if (preg_match_all('# src=[\'"]([^\'"]+)#', $body, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[1] as $m) {
// find emoticon image tags
if (preg_match('#'.$searchstr.'(.*)$#', $m[0], $imatches)) {
$image_name = $imatches[1];
// sanitize image name so resulting attachment doesn't leave images dir
$image_name = preg_replace('/[^a-zA-Z0-9_\.\-]/i', '', $image_name);
$img_file = $path . $image_name;
if (!in_array($image_name, $included_images)) {
// add the image to the MIME message
$res = $mime_message->addHTMLImage($img_file, 'image/gif', '', true, $image_name);
if (is_a($res, 'PEAR_Error')) {
$RCMAIL->output->show_message("emoticonerror", 'error');
continue;
}
array_push($included_images, $image_name);
}
$body = substr_replace($body, $img_file, $m[1] + $offset, strlen($m[0]));
$offset += strlen($img_file) - strlen($m[0]);
}
}
}
$mime_message->setHTMLBody($body);
}
/**
* Extract image attachments from HTML content (data URIs)
*/

@ -26,15 +26,11 @@ if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) {
$html = stripslashes($html);
}
// Replace emoticon images with its text representation
$html = $RCMAIL->replace_emoticons($html);
$params['links'] = (bool) rcube_utils::get_input_value('_do_links', rcube_utils::INPUT_GET);
$params['width'] = (int) rcube_utils::get_input_value('_width', rcube_utils::INPUT_GET);
$do_links = (bool) rcube_utils::get_input_value('_do_links', rcube_utils::INPUT_GET);
$width = (int) rcube_utils::get_input_value('_width', rcube_utils::INPUT_GET);
$text = $RCMAIL->html2text($html, $params);
// Convert to text
$converter = new rcube_html2text($html, false, $do_links, $width);
header('Content-Type: text/plain; charset=UTF-8');
print rtrim($converter->get_text());
header('Content-Type: text/plain; charset=' . RCUBE_CHARSET);
print $text;
exit;

@ -60,6 +60,7 @@
<file>./../plugins/database_attachments/tests/DatabaseAttachments.php</file>
<file>./../plugins/debug_logger/tests/DebugLogger.php</file>
<file>./../plugins/emoticons/tests/Emoticons.php</file>
<file>./../plugins/emoticons/tests/EmoticonsEngine.php</file>
<file>./../plugins/enigma/tests/Enigma.php</file>
<file>./../plugins/example_addressbook/tests/ExampleAddressbook.php</file>
<file>./../plugins/filesystem_attachments/tests/FilesystemAttachments.php</file>

Loading…
Cancel
Save