- Applied fixes from trunk up to r5498

release-0.7
alecpl 13 years ago
parent 2a3e02769d
commit 77449d011b

@ -1,6 +1,13 @@
CHANGELOG Roundcube Webmail
===========================
- Use strpos() instead of strstr() when possible (#1488211)
- Fix handling HTML entities when converting HTML to text (#1488212)
- Fix fit_string_to_size() renders browser and ui unresponsive (#1488207)
- Fix handling of invalid characters in request (#1488124)
- Fix merging some configuration options in update.sh script (#1485864)
- Fix so TEXT key will remove all HEADER keys in IMAP SEARCH (#1488208)
- Fix handling contact photo url with https:// prefix (#1488202)
- Fix possible infinite redirect on attachment preview (#1488199)
- Improved clickjacking protection for browsers which don't support X-Frame-Options headers
- Fixed bug where similiar folder names were highlighted wrong (#1487860)

@ -653,8 +653,8 @@ $rcmail_config['pagesize'] = 40;
// use this timezone to display date/time
$rcmail_config['timezone'] = 'auto';
// is daylight saving On?
$rcmail_config['dst_active'] = (bool)date('I');
// is daylight saving On? Default: (bool)date('I');
$rcmail_config['dst_active'] = null;
// prefer displaying HTML messages
$rcmail_config['prefer_html'] = true;

@ -142,20 +142,22 @@ class rcube_install
foreach ($this->config as $prop => $default) {
$value = (isset($_POST["_$prop"]) || $this->bool_config_props[$prop]) ? $_POST["_$prop"] : $default;
$is_default = !isset($_POST["_$prop"]);
$value = !$is_default || $this->bool_config_props[$prop] ? $_POST["_$prop"] : $default;
// convert some form data
if ($prop == 'debug_level') {
$val = 0;
if (is_array($value))
if ($prop == 'debug_level' && !$is_default) {
if (is_array($value)) {
$val = 0;
foreach ($value as $dbgval)
$val += intval($dbgval);
$value = $val;
$value = $val;
}
}
else if ($which == 'db' && $prop == 'db_dsnw' && !empty($_POST['_dbtype'])) {
if ($_POST['_dbtype'] == 'sqlite')
$value = sprintf('%s://%s?mode=0646', $_POST['_dbtype'], $_POST['_dbname']{0} == '/' ? '/' . $_POST['_dbname'] : $_POST['_dbname']);
else
else if ($_POST['_dbtype'])
$value = sprintf('%s://%s:%s@%s/%s', $_POST['_dbtype'],
rawurlencode($_POST['_dbuser']), rawurlencode($_POST['_dbpass']), $_POST['_dbhost'], $_POST['_dbname']);
}
@ -177,9 +179,9 @@ class rcube_install
$value = '%p';
}
else if ($prop == 'default_imap_folders') {
$value = Array();
$value = array();
foreach ($this->config['default_imap_folders'] as $_folder) {
switch($_folder) {
switch ($_folder) {
case 'Drafts': $_folder = $this->config['drafts_mbox']; break;
case 'Sent': $_folder = $this->config['sent_mbox']; break;
case 'Junk': $_folder = $this->config['junk_mbox']; break;
@ -206,7 +208,7 @@ class rcube_install
// replace the matching line in config file
$out = preg_replace(
'/(\$rcmail_config\[\''.preg_quote($prop).'\'\])\s+=\s+(.+);/Uie',
"'\\1 = ' . rcube_install::_dump_var(\$value) . ';'",
"'\\1 = ' . rcube_install::_dump_var(\$value, \$prop) . ';'",
$out);
}
@ -299,7 +301,7 @@ class rcube_install
$current = $this->config;
$this->config = array();
$this->load_defaults();
foreach ($this->replaced_config as $prop => $replacement) {
if (isset($current[$prop])) {
if ($prop == 'skin_path')
@ -328,9 +330,9 @@ class rcube_install
if ($current['keep_alive'] && $current['session_lifetime'] < $current['keep_alive'])
$current['session_lifetime'] = max(10, ceil($current['keep_alive'] / 60) * 2);
$this->config = array_merge($this->config, $current);
foreach ((array)$current['ldap_public'] as $key => $values) {
$this->config['ldap_public'][$key] = $current['ldap_public'][$key];
}
@ -614,7 +616,22 @@ class rcube_install
}
static function _dump_var($var) {
static function _dump_var($var, $name=null) {
// special values
switch ($name) {
case 'syslog_facility':
$list = array(32 => 'LOG_AUTH', 80 => 'LOG_AUTHPRIV', 72 => ' LOG_CRON',
24 => 'LOG_DAEMON', 0 => 'LOG_KERN', 128 => 'LOG_LOCAL0',
136 => 'LOG_LOCAL1', 144 => 'LOG_LOCAL2', 152 => 'LOG_LOCAL3',
160 => 'LOG_LOCAL4', 168 => 'LOG_LOCAL5', 176 => 'LOG_LOCAL6',
184 => 'LOG_LOCAL7', 48 => 'LOG_LPR', 16 => 'LOG_MAIL',
56 => 'LOG_NEWS', 40 => 'LOG_SYSLOG', 8 => 'LOG_USER', 64 => 'LOG_UUCP');
if ($val = $list[$var])
return $val;
break;
}
if (is_array($var)) {
if (empty($var)) {
return 'array()';

@ -640,20 +640,23 @@ function JQ($str)
function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
{
$value = NULL;
if ($source==RCUBE_INPUT_GET && isset($_GET[$fname]))
$value = $_GET[$fname];
else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname]))
$value = $_POST[$fname];
else if ($source==RCUBE_INPUT_GPC)
{
if ($source == RCUBE_INPUT_GET) {
if (isset($_GET[$fname]))
$value = $_GET[$fname];
}
else if ($source == RCUBE_INPUT_POST) {
if (isset($_POST[$fname]))
$value = $_POST[$fname];
}
else if ($source == RCUBE_INPUT_GPC) {
if (isset($_POST[$fname]))
$value = $_POST[$fname];
else if (isset($_GET[$fname]))
$value = $_GET[$fname];
else if (isset($_COOKIE[$fname]))
$value = $_COOKIE[$fname];
}
}
return parse_input_value($value, $allow_html, $charset);
}
@ -661,7 +664,7 @@ function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
/**
* Parse/validate input value. See get_input_value()
* Performs stripslashes() and charset conversion if necessary
*
*
* @param string Input value
* @param boolean Allow HTML tags in field value
* @param string Charset to convert into
@ -687,15 +690,21 @@ function parse_input_value($value, $allow_html=FALSE, $charset=NULL)
else if (get_magic_quotes_gpc() || get_magic_quotes_runtime())
$value = stripslashes($value);
// remove HTML tags if not allowed
// remove HTML tags if not allowed
if (!$allow_html)
$value = strip_tags($value);
$output_charset = is_object($OUTPUT) ? $OUTPUT->get_charset() : null;
// remove invalid characters (#1488124)
if ($output_charset == 'UTF-8')
$value = rc_utf8_clean($value);
// convert to internal charset
if (is_object($OUTPUT) && $charset)
return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
else
return $value;
if ($charset && $output_charset)
$value = rcube_charset_convert($value, $output_charset, $charset);
return $value;
}
/**
@ -711,10 +720,10 @@ function request2param($mode = RCUBE_INPUT_GPC, $ignore = 'task|action')
$src = $mode == RCUBE_INPUT_GET ? $_GET : ($mode == RCUBE_INPUT_POST ? $_POST : $_REQUEST);
foreach ($src as $key => $value) {
$fname = $key[0] == '_' ? substr($key, 1) : $key;
if ($ignore && !preg_match("/($ignore)/", $fname))
if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname))
$out[$fname] = get_input_value($key, $mode);
}
return $out;
}

@ -33,19 +33,19 @@ class rcube_browser
$HTTP_USER_AGENT = strtolower($_SERVER['HTTP_USER_AGENT']);
$this->ver = 0;
$this->win = strstr($HTTP_USER_AGENT, 'win');
$this->mac = strstr($HTTP_USER_AGENT, 'mac');
$this->linux = strstr($HTTP_USER_AGENT, 'linux');
$this->unix = strstr($HTTP_USER_AGENT, 'unix');
$this->win = strpos($HTTP_USER_AGENT, 'win') != false;
$this->mac = strpos($HTTP_USER_AGENT, 'mac') != false;
$this->linux = strpos($HTTP_USER_AGENT, 'linux') != false;
$this->unix = strpos($HTTP_USER_AGENT, 'unix') != false;
$this->opera = strstr($HTTP_USER_AGENT, 'opera');
$this->ns4 = strstr($HTTP_USER_AGENT, 'mozilla/4') && !stristr($HTTP_USER_AGENT, 'msie');
$this->ns = ($this->ns4 || strstr($HTTP_USER_AGENT, 'netscape'));
$this->ie = !$this->opera && stristr($HTTP_USER_AGENT, 'compatible; msie');
$this->mz = !$this->ie && strstr($HTTP_USER_AGENT, 'mozilla/5');
$this->chrome = strstr($HTTP_USER_AGENT, 'chrome');
$this->khtml = strstr($HTTP_USER_AGENT, 'khtml');
$this->safari = !$this->chrome && ($this->khtml || strstr($HTTP_USER_AGENT, 'safari'));
$this->opera = strpos($HTTP_USER_AGENT, 'opera') !== false;
$this->ns4 = strpos($HTTP_USER_AGENT, 'mozilla/4') !== false && strpos($HTTP_USER_AGENT, 'msie') === false;
$this->ns = ($this->ns4 || strpos($HTTP_USER_AGENT, 'netscape') !== false);
$this->ie = !$this->opera && strpos($HTTP_USER_AGENT, 'compatible; msie') !== false;
$this->mz = !$this->ie && strpos($HTTP_USER_AGENT, 'mozilla/5') !== false;
$this->chrome = strpos($HTTP_USER_AGENT, 'chrome') !== false;
$this->khtml = strpos($HTTP_USER_AGENT, 'khtml') !== false;
$this->safari = !$this->chrome && ($this->khtml || strpos($HTTP_USER_AGENT, 'safari') !== false);
if ($this->ns || $this->chrome) {
$test = preg_match('/(mozilla|chrome)\/([0-9.]+)/', $HTTP_USER_AGENT, $regs);

@ -90,11 +90,14 @@ class rcube_config
// enable display_errors in 'show' level, but not for ajax requests
ini_set('display_errors', intval(empty($_REQUEST['_remote']) && ($this->prop['debug_level'] & 4)));
// set timezone auto settings values
if ($this->prop['timezone'] == 'auto') {
$this->prop['dst_active'] = intval(date('I'));
$this->prop['_timezone_value'] = date('Z') / 3600 - $this->prop['dst_active'];
$this->prop['_timezone_value'] = date('Z') / 3600 - $this->prop['dst_active'];
}
else if ($this->prop['dst_active'] === null) {
$this->prop['dst_active'] = intval(date('I'));
}
// export config data

@ -381,7 +381,7 @@ class rcube_smtp
$from = $addresses[0];
// Reject envelope From: addresses with spaces.
if (strstr($from, ' '))
if (strpos($from, ' ') !== false)
return false;
$lines[] = $key . ': ' . $value;

@ -5743,10 +5743,13 @@ function rcube_webmail()
});
};
this.plain2html = function(plainText, id)
this.plain2html = function(plain, id)
{
var lock = this.set_busy(true, 'converting');
$('#'+id).val(plainText ? '<pre>'+plainText+'</pre>' : '');
plain = plain.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
$('#'+id).val(plain ? '<pre>'+plain+'</pre>' : '');
this.set_busy(false, null, lock);
};

@ -145,7 +145,6 @@ class html2text
var $search = array(
"/\r/", // Non-legal carriage return
"/[\n\t]+/", // Newlines and tabs
'/[ ]{2,}/', // Runs of spaces, pre-handling
'/<script[^>]*>.*?<\/script>/i', // <script>s -- which strip_tags supposedly has problems with
'/<style[^>]*>.*?<\/style>/i', // <style>s -- which strip_tags supposedly has problems with
'/<p[^>]*>/i', // <P>
@ -161,22 +160,6 @@ class html2text
'/(<table[^>]*>|<\/table>)/i', // <table> and </table>
'/(<tr[^>]*>|<\/tr>)/i', // <tr> and </tr>
'/<td[^>]*>(.*?)<\/td>/i', // <td> and </td>
'/&(nbsp|#160);/i', // Non-breaking space
'/&(quot|rdquo|ldquo|#8220|#8221|#147|#148);/i',
// Double quotes
'/&(apos|rsquo|lsquo|#8216|#8217);/i', // Single quotes
'/&gt;/i', // Greater-than
'/&lt;/i', // Less-than
'/&(copy|#169);/i', // Copyright
'/&(trade|#8482|#153);/i', // Trademark
'/&(reg|#174);/i', // Registered
'/&(mdash|#151|#8212);/i', // mdash
'/&(ndash|minus|#8211|#8722);/i', // ndash
'/&(bull|#149|#8226);/i', // Bullet
'/&(pound|#163);/i', // Pound sign
'/&(euro|#8364);/i', // Euro sign
'/&(amp|#38);/i', // Ampersand: see _converter()
'/[ ]{2,}/' // Runs of spaces, post-handling
);
/**
@ -189,7 +172,6 @@ class html2text
var $replace = array(
'', // Non-legal carriage return
' ', // Newlines and tabs
' ', // Runs of spaces, pre-handling
'', // <script>s -- which strip_tags supposedly has problems with
'', // <style>s -- which strip_tags supposedly has problems with
"\n\n", // <P>
@ -205,6 +187,43 @@ class html2text
"\n\n", // <table> and </table>
"\n", // <tr> and </tr>
"\t\t\\1\n", // <td> and </td>
);
/**
* List of preg* regular expression patterns to search for,
* used in conjunction with $ent_replace.
*
* @var array $ent_search
* @access public
* @see $ent_replace
*/
var $ent_search = array(
'/&(nbsp|#160);/i', // Non-breaking space
'/&(quot|rdquo|ldquo|#8220|#8221|#147|#148);/i',
// Double quotes
'/&(apos|rsquo|lsquo|#8216|#8217);/i', // Single quotes
'/&gt;/i', // Greater-than
'/&lt;/i', // Less-than
'/&(copy|#169);/i', // Copyright
'/&(trade|#8482|#153);/i', // Trademark
'/&(reg|#174);/i', // Registered
'/&(mdash|#151|#8212);/i', // mdash
'/&(ndash|minus|#8211|#8722);/i', // ndash
'/&(bull|#149|#8226);/i', // Bullet
'/&(pound|#163);/i', // Pound sign
'/&(euro|#8364);/i', // Euro sign
'/&(amp|#38);/i', // Ampersand: see _converter()
'/[ ]{2,}/', // Runs of spaces, post-handling
);
/**
* List of pattern replacements corresponding to patterns searched.
*
* @var array $ent_replace
* @access public
* @see $ent_search
*/
var $ent_replace = array(
' ', // Non-breaking space
'"', // Double quotes
"'", // Single quotes
@ -219,7 +238,7 @@ class html2text
'£',
'EUR', // Euro sign. € ?
'|+|amp|+|', // Ampersand: see _converter()
' ' // Runs of spaces, post-handling
' ', // Runs of spaces, post-handling
);
/**
@ -303,7 +322,7 @@ class html2text
* @see _build_link_list()
*/
var $_link_list = '';
/**
* Number of valid links detected in the text, used for plain text
* display (rendered similar to footnotes).
@ -314,15 +333,15 @@ class html2text
*/
var $_link_count = 0;
/**
* Boolean flag, true if a table of link URLs should be listed after the text.
*
* @var boolean $_do_links
* @access private
* @see html2text()
/**
* Boolean flag, true if a table of link URLs should be listed after the text.
*
* @var boolean $_do_links
* @access private
* @see html2text()
*/
var $_do_links = true;
/**
* Constructor.
*
@ -492,15 +511,21 @@ class html2text
// Convert <PRE>
$this->_convert_pre($text);
// Run our defined search-and-replace
// Run our defined tags search-and-replace
$text = preg_replace($this->search, $this->replace, $text);
// Run our defined tags search-and-replace with callback
$text = preg_replace_callback($this->callback_search, array('html2text', '_preg_callback'), $text);
// Strip any other HTML tags
$text = strip_tags($text, $this->allowed_tags);
// Run our defined entities/characters search-and-replace
$text = preg_replace($this->ent_search, $this->ent_replace, $text);
// Replace known html entities
$text = html_entity_decode($text, ENT_COMPAT, 'UTF-8');
// Run our defined search-and-replace with callback
$text = preg_replace_callback($this->callback_search, array('html2text', '_preg_callback'), $text);
// Remove unknown/unhandled entities (this cannot be done in search-and-replace block)
$text = preg_replace('/&([a-zA-Z0-9]{2,6}|#[0-9]{2,4});/', '', $text);
@ -508,15 +533,12 @@ class html2text
// This properly handles situation of "&amp;quot;" in input string
$text = str_replace('|+|amp|+|', '&', $text);
// Strip any other HTML tags
$text = strip_tags($text, $this->allowed_tags);
// Bring down number of empty lines to 2 max
$text = preg_replace("/\n\s+\n/", "\n\n", $text);
$text = preg_replace("/[\n]{3,}/", "\n\n", $text);
// remove leading empty lines (can be produced by eg. P tag on the beginning)
$text = preg_replace('/^\n+/', '', $text);
$text = ltrim($text, "\n");
// Wrap the text to a readable format
// for PHP versions >= 4.0.2. Default width is 75
@ -544,9 +566,7 @@ class html2text
if ( !$this->_do_links )
return $display;
if ( substr($link, 0, 7) == 'http://' || substr($link, 0, 8) == 'https://' ||
substr($link, 0, 7) == 'mailto:'
) {
if ( preg_match('!^(https?://|mailto:)!', $link) ) {
$this->_link_count++;
$this->_link_list .= '[' . $this->_link_count . "] $link\n";
$additional = ' [' . $this->_link_count . ']';

@ -705,7 +705,7 @@ function rcmail_contact_photo($attrib)
$RCMAIL->output->set_env('photo_placeholder', $photo_img);
unset($attrib['placeholder']);
if (strpos($record['photo'], 'http:') === 0)
if (preg_match('!^https?://!i', $record['photo']))
$photo_img = $record['photo'];
else if ($record['photo'])
$photo_img = $RCMAIL->url(array('_action' => 'photo', '_cid' => $record['ID'], '_source' => $SOURCE_ID));

@ -31,6 +31,7 @@ $str = get_input_value('_q', RCUBE_INPUT_GET, true);
$mbox = get_input_value('_mbox', RCUBE_INPUT_GET, true);
$filter = get_input_value('_filter', RCUBE_INPUT_GET);
$headers = get_input_value('_headers', RCUBE_INPUT_GET);
$subject = array();
$search_request = md5($mbox.$filter.$str);
@ -70,14 +71,19 @@ else if (preg_match("/^body:.*/i", $str))
list(,$srch) = explode(":", $str);
$subject['text'] = "TEXT";
}
else if(trim($str))
else if (strlen(trim($str)))
{
if ($headers) {
foreach(explode(',', $headers) as $header)
switch ($header) {
case 'text': $subject['text'] = 'TEXT'; break;
default: $subject[$header] = 'HEADER '.strtoupper($header);
foreach (explode(',', $headers) as $header) {
if ($header == 'text') {
// #1488208: get rid of other headers when searching by "TEXT"
$subject = array('text' => 'TEXT');
break;
}
else {
$subject[$header] = 'HEADER '.strtoupper($header);
}
}
// save search modifiers for the current folder to user prefs
$search_mods = $RCMAIL->config->get('search_mods', $SEARCH_MODS_DEFAULT);
@ -89,9 +95,9 @@ else if(trim($str))
}
}
$search = $srch ? trim($srch) : trim($str);
$search = isset($srch) ? trim($srch) : trim($str);
if ($subject) {
if (!empty($subject)) {
$search_str .= str_repeat(' OR', count($subject)-1);
foreach ($subject as $sub)
$search_str .= sprintf(" %s {%d}\r\n%s", $sub, strlen($search), $search);

@ -192,29 +192,32 @@ searchmenu: function(show)
if (show && ref) {
var pos = $(ref).offset();
obj.css({ left:pos.left, top:(pos.top + ref.offsetHeight + 2)})
.find(':checked').prop('checked', false);
obj.css({left:pos.left, top:(pos.top + ref.offsetHeight + 2)});
if (rcmail.env.search_mods) {
var n, mbox = rcmail.env.mailbox, mods = rcmail.env.search_mods;
var n, all,
list = $('input:checkbox[name="s_mods[]"]', obj),
mbox = rcmail.env.mailbox,
mods = rcmail.env.search_mods;
if (rcmail.env.task != 'addressbook') {
if (rcmail.env.task == 'mail') {
mods = mods[mbox] ? mods[mbox] : mods['*'];
all = 'text';
}
else {
all = '*';
}
if (mods[all])
list.map(function() {
this.checked = true;
this.disabled = this.value != all;
});
else {
list.prop('disabled', false).prop('checked', false);
for (n in mods)
$('#s_mod_' + n).prop('checked', true);
}
else {
if (mods['*'])
$('input:checkbox[name="s_mods[]"]').map(function() {
this.checked = true;
this.disabled = this.value != '*';
});
else {
for (n in mods)
$('#s_mod_' + n).prop('checked', true);
}
}
}
}
obj[show?'show':'hide']();
@ -222,7 +225,7 @@ searchmenu: function(show)
set_searchmod: function(elem)
{
var task = rcmail.env.task,
var all, m, task = rcmail.env.task,
mods = rcmail.env.search_mods,
mbox = rcmail.env.mailbox;
@ -232,36 +235,37 @@ set_searchmod: function(elem)
if (task == 'mail') {
if (!mods[mbox])
mods[mbox] = rcube_clone_object(mods['*']);
if (!elem.checked)
delete(mods[mbox][elem.value]);
else
mods[mbox][elem.value] = 1;
m = mods[mbox];
all = 'text';
}
else { //addressbook
if (!elem.checked)
delete(mods[elem.value]);
else
mods[elem.value] = 1;
m = mods;
all = '*';
}
// mark all fields
if (elem.value == '*') {
$('input:checkbox[name="s_mods[]"]').map(function() {
if (this == elem)
return;
if (!elem.checked)
delete(m[elem.value]);
else
m[elem.value] = 1;
if (elem.checked) {
mods[this.value] = 1;
this.checked = true;
this.disabled = true;
}
else {
this.disabled = false;
}
});
}
}
// mark all fields
if (elem.value != all)
return;
rcmail.env.search_mods = mods;
$('input:checkbox[name="s_mods[]"]').map(function() {
if (this == elem)
return;
this.checked = true;
if (elem.checked) {
this.disabled = true;
delete m[this.value];
}
else {
this.disabled = false;
m[this.value] = 1;
}
});
},
listmenu: function(show)
@ -566,7 +570,6 @@ function rcube_init_mail_ui()
rcmail.addEventListener('responseaftergetunread', rcube_render_mailboxlist);
rcmail.addEventListener('responseaftercheck-recent', rcube_render_mailboxlist);
rcmail.addEventListener('aftercollapse-folder', rcube_render_mailboxlist);
rcube_render_mailboxlist();
}
if (rcmail.env.action == 'compose')
@ -588,12 +591,16 @@ function iframe_events()
// Abbreviate mailbox names to fit width of the container
function rcube_render_mailboxlist()
{
if (bw.ie6) // doesn't work well on IE6
var list = $('#mailboxlist > li a, #mailboxlist ul:visible > li a');
// it's too slow with really big number of folders, especially on IE
if (list.length > 500 * (bw.ie ? 0.2 : 1))
return;
$('#mailboxlist > li a, #mailboxlist ul:visible > li a').each(function(){
var elem = $(this);
var text = elem.data('text');
list.each(function(){
var elem = $(this),
text = elem.data('text');
if (!text) {
text = elem.text().replace(/\s+\(.+$/, '');
elem.data('text', text);
@ -611,34 +618,45 @@ function rcube_render_mailboxlist()
// inspired by https://gist.github.com/24261/7fdb113f1e26111bd78c0c6fe515f6c0bf418af5
function fit_string_to_size(str, elem, len)
{
var result = str;
var ellip = '...';
var span = $('<b>').css({ visibility:'hidden', padding:'0px' }).appendTo(elem).get(0);
// on first run, check if string fits into the length already.
span.innerHTML = result;
if (span.offsetWidth > len) {
var cut = Math.max(1, Math.floor(str.length * ((span.offsetWidth - len) / span.offsetWidth) / 2)),
mid = Math.floor(str.length / 2);
var offLeft = mid, offRight = mid;
while (true) {
offLeft = mid - cut;
offRight = mid + cut;
span.innerHTML = str.substring(0,offLeft) + ellip + str.substring(offRight);
// break loop if string fits size
if (span.offsetWidth <= len || offLeft < 3)
break;
cut++;
}
// build resulting string
result = str.substring(0,offLeft) + ellip + str.substring(offRight);
var w, span, result = str, ellip = '...';
if (!rcmail.env.tmp_span) {
// it should be appended to elem to use the same css style
// but for performance reasons we'll append it to body (once)
span = $('<b>').css({visibility: 'hidden', padding: '0px'})
.appendTo($('body', document)).get(0);
rcmail.env.tmp_span = span;
}
else {
span = rcmail.env.tmp_span;
}
span.innerHTML = result;
// on first run, check if string fits into the length already.
w = span.offsetWidth;
if (w > len) {
var cut = Math.max(1, Math.floor(str.length * ((w - len) / w) / 2)),
mid = Math.floor(str.length / 2),
offLeft = mid,
offRight = mid;
while (true) {
offLeft = mid - cut;
offRight = mid + cut;
span.innerHTML = str.substring(0,offLeft) + ellip + str.substring(offRight);
// break loop if string fits size
if (offLeft < 3 || span.offsetWidth)
break;
cut++;
}
span.parentNode.removeChild(span);
return result;
// build resulting string
result = str.substring(0,offLeft) + ellip + str.substring(offRight);
}
return result;
}
// Optional parameters used by TinyMCE

@ -106,7 +106,7 @@ class rcube_test_mailfunc extends UnitTestCase
$part = $this->get_html_part('src/invalidchars.html');
$washed = rcmail_print_body($part);
$this->assertPattern('/<p>сОЌвПл<\/p>/', $washed, "Remove non-unicode characters from HTML message body");
$this->assertPattern('/<p>символ<\/p>/', $washed, "Remove non-unicode characters from HTML message body");
}
/**

Loading…
Cancel
Save