diff --git a/CHANGELOG b/CHANGELOG
index 5ddd45dfb..6e72efbd1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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)
diff --git a/plugins/emoticons/composer.json b/plugins/emoticons/composer.json
index d1679cfe9..72f160217 100644
--- a/plugins/emoticons/composer.json
+++ b/plugins/emoticons/composer.json
@@ -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",
diff --git a/plugins/emoticons/config.inc.php.dist b/plugins/emoticons/config.inc.php.dist
new file mode 100644
index 000000000..1af6f6763
--- /dev/null
+++ b/plugins/emoticons/config.inc.php.dist
@@ -0,0 +1,7 @@
+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 ()
+ */
+ function message_part_after($args)
{
- // This is a lookbehind assertion which will exclude html entities
- // E.g. situation when ";)" in "")" shouldn't be replaced by the icon
- // It's so long because of assertion format restrictions
- $entity = '(? $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-)' ),
- '/(? $this->img_tag('smiley-surprised.gif', ':O' ),
- '/(? $this->img_tag('smiley-surprised.gif', ':-O' ),
- '/(? $this->img_tag('smiley-tongue-out.gif', ':P' ),
- '/(? $this->img_tag('smiley-tongue-out.gif', ':-P' ),
- '/(? $this->img_tag('smiley-yell.gif', ':@' ),
- '/(? $this->img_tag('smiley-yell.gif', ':-@' ),
- '/O:\)/i' => $this->img_tag('smiley-innocent.gif', 'O:)' ),
- '/O:-\)/i' => $this->img_tag('smiley-innocent.gif', 'O:-)' ),
- '/(? $this->img_tag('smiley-smile.gif', ':)' ),
- '/(? $this->img_tag('smiley-smile.gif', ':-)' ),
- '/(? $this->img_tag('smiley-embarassed.gif', ':$' ),
- '/(? $this->img_tag('smiley-embarassed.gif', ':-$' ),
- '/(? $this->img_tag('smiley-kiss.gif', ':*' ),
- '/(? $this->img_tag('smiley-kiss.gif', ':-*' ),
- '/(? $this->img_tag('smiley-undecided.gif', ':S' ),
- '/(? $this->img_tag('smiley-undecided.gif', ':-S' ),
- );
-
if ($args['type'] == 'plain') {
- $args['body'] = preg_replace(
- array_keys($map), array_values($map), $args['body']);
+ $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;
}
- private function img_tag($ico, $title)
+ /**
+ * 'message_outgoing_body' hook handler to replace image emoticons from TinyMCE
+ * editor with image attachments.
+ */
+ function message_outgoing_body($args)
{
- $path = './program/js/tinymce/plugins/emoticons/img/';
- return html::img(array('src' => $path.$ico, 'title' => $title));
+ 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');
+
+ $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;
+ }
+
+ /**
+ * 'preferences_save' hook handler
+ */
+ function preferences_save($args)
+ {
+ $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;
}
}
diff --git a/plugins/emoticons/emoticons_engine.php b/plugins/emoticons/emoticons_engine.php
new file mode 100644
index 000000000..4d534cdc1
--- /dev/null
+++ b/plugins/emoticons/emoticons_engine.php
@@ -0,0 +1,152 @@
+ '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) {
+ //
+ $file = preg_quote(self::IMG_PATH . $file . '.gif', '/');
+ $search[] = '/]+\/>/i';
+ $replace[] = $idx;
+ }
+
+ return preg_replace($search, $replace, $html);
+ }
+
+ /**
+ * Replace common plain text emoticons with empticon 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 "")" shouldn't be replaced by the icon
+ // It's so long because of assertion format restrictions
+ $entity = '(? 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-)' ),
+ '/(? self::img_tag('smiley-surprised.gif', ':O' ),
+ '/(? self::img_tag('smiley-surprised.gif', ':-O' ),
+ '/(? self::img_tag('smiley-tongue-out.gif', ':P' ),
+ '/(? self::img_tag('smiley-tongue-out.gif', ':-P' ),
+ '/(? self::img_tag('smiley-yell.gif', ':@' ),
+ '/(? self::img_tag('smiley-yell.gif', ':-@' ),
+ '/O:\)/i' => self::img_tag('smiley-innocent.gif', 'O:)' ),
+ '/O:-\)/i' => self::img_tag('smiley-innocent.gif', 'O:-)' ),
+ '/(? self::img_tag('smiley-smile.gif', ':)' ),
+ '/(? self::img_tag('smiley-smile.gif', ':-)' ),
+ '/(? self::img_tag('smiley-embarassed.gif', ':$' ),
+ '/(? self::img_tag('smiley-embarassed.gif', ':-$' ),
+ '/(? self::img_tag('smiley-kiss.gif', ':*' ),
+ '/(? self::img_tag('smiley-kiss.gif', ':-*' ),
+ '/(? self::img_tag('smiley-undecided.gif', ':S' ),
+ '/(? 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 '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:
+ //
+ // with this:
+ //
+
+ $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;
+ }
+}
diff --git a/plugins/emoticons/localization/en_US.inc b/plugins/emoticons/localization/en_US.inc
new file mode 100644
index 000000000..04b10964d
--- /dev/null
+++ b/plugins/emoticons/localization/en_US.inc
@@ -0,0 +1,23 @@
+.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';
+
+?>
diff --git a/plugins/emoticons/tests/Emoticons.php b/plugins/emoticons/tests/Emoticons.php
index 14c7fd0e6..bc21ceb1d 100644
--- a/plugins/emoticons/tests/Emoticons.php
+++ b/plugins/emoticons/tests/Emoticons.php
@@ -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']);
- }
- }
}
diff --git a/plugins/emoticons/tests/EmoticonsEngine.php b/plugins/emoticons/tests/EmoticonsEngine.php
new file mode 100644
index 000000000..f343450e6
--- /dev/null
+++ b/plugins/emoticons/tests/EmoticonsEngine.php
@@ -0,0 +1,48 @@
+ 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);
+ }
+ }
+}
diff --git a/program/include/bc.php b/program/include/bc.php
index 47cca0d78..259ebc32d 100644
--- a/program/include/bc.php
+++ b/program/include/bc.php
@@ -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);
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 10a9d64f8..db5fce617 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -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) {
- //
- $search[] = '/]+\/>/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) *********
diff --git a/program/js/editor.js b/program/js/editor.js
index 6446dc56a..2415b7a3b 100644
--- a/program/js/editor.js
+++ b/program/js/editor.js
@@ -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);
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 402b32cf3..85e5e27d2 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -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);
@@ -874,9 +873,8 @@ function rcmail_compose_part_body($part, $isHtml = false)
if ($part->ctype_secondary == 'html') {
// 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();
+ $len = $COMPOSE['mode'] == RCUBE_COMPOSE_REPLY ? $LINE_LENGTH-2 : $LINE_LENGTH;
+ $body = $RCMAIL->html2text($body, array('width' => $len));
}
else {
if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') {
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 37298f54b..6ba8ce136 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -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
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index c791c3d41..7b74e88c3 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -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:
- *
- *
- * to this:
- *
- *
- */
-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)
*/
diff --git a/program/steps/utils/html2text.inc b/program/steps/utils/html2text.inc
index 7fa834063..251eaf7c4 100644
--- a/program/steps/utils/html2text.inc
+++ b/program/steps/utils/html2text.inc
@@ -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;
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
index 07d8d4181..bb2e9bc2c 100644
--- a/tests/phpunit.xml
+++ b/tests/phpunit.xml
@@ -60,6 +60,7 @@
./../plugins/database_attachments/tests/DatabaseAttachments.php
./../plugins/debug_logger/tests/DebugLogger.php
./../plugins/emoticons/tests/Emoticons.php
+ ./../plugins/emoticons/tests/EmoticonsEngine.php
./../plugins/enigma/tests/Enigma.php
./../plugins/example_addressbook/tests/ExampleAddressbook.php
./../plugins/filesystem_attachments/tests/FilesystemAttachments.php