diff options
author | Aleksander Machniak <alec@alec.pl> | 2022-01-09 18:29:09 +0300 |
---|---|---|
committer | Aleksander Machniak <alec@alec.pl> | 2022-01-09 18:29:09 +0300 |
commit | a5fd2117128a1e81912fcfb6671ce4a16cd7f6d4 (patch) | |
tree | 2375e87a4a417b667b39e2464db9261d42226cb3 | |
parent | 953be4bbe1a15e324714dcc3289c177425af618b (diff) |
Improve/Fix wrapping of plain text messages on preview and reply (#6974, #8391, #8378, #8289)
In short, we always wrap, but we detect patches/diffs in the text and make them unwrappable.
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | program/actions/mail/index.php | 1 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_text2html.php | 52 | ||||
-rw-r--r-- | skins/elastic/styles/styles.less | 12 | ||||
-rw-r--r-- | tests/Framework/Text2Html.php | 39 |
5 files changed, 91 insertions, 15 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bc3cca6d..8145d677a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,7 +33,7 @@ - Add option to purge deleted mails older than 30, 60 or 90 days (#5493) - Add ability to mark multiple messages as not deleted at once (#5133) - Add possibility to disable line-wrapping of sent mail body (#5101) -- Improve auto-wrapping of plain text messages on preview and reply, don't wrap non-format=flowed content (#6974) +- Improve/Fix wrapping of plain text messages on preview and reply (#6974, #8391, #8378, #8289) - Improve searching by sender/recipient headers, support Reply-To and Followup-To (#6582) - Add option to control links handling behavior on html to text conversion (#6485) - Add 'loginform_content' plugin hook (#8273, #6569) diff --git a/program/actions/mail/index.php b/program/actions/mail/index.php index 76f1c5da2..428329449 100644 --- a/program/actions/mail/index.php +++ b/program/actions/mail/index.php @@ -1066,7 +1066,6 @@ class rcmail_action_mail_index extends rcmail_action { $options = [ 'flowed' => $flowed, - 'wrap' => $flowed, 'replacer' => 'rcmail_string_replacer', 'delsp' => $delsp ]; diff --git a/program/lib/Roundcube/rcube_text2html.php b/program/lib/Roundcube/rcube_text2html.php index 7d751b6f1..86cfc1405 100644 --- a/program/lib/Roundcube/rcube_text2html.php +++ b/program/lib/Roundcube/rcube_text2html.php @@ -56,7 +56,10 @@ class rcube_text2html ]; /** @var bool Internal state */ - protected $_converted = false; + protected $converted = false; + + /** @var bool Internal no-wrap mode state */ + protected $nowrap = false; /** @@ -96,7 +99,7 @@ class rcube_text2html $this->text = $source; } - $this->_converted = false; + $this->converted = false; } /** @@ -106,8 +109,8 @@ class rcube_text2html */ function get_html() { - if (!$this->_converted) { - $this->_convert(); + if (!$this->converted) { + $this->convert(); } return $this->html; @@ -122,13 +125,13 @@ class rcube_text2html } /** - * Workhorse function that does actual conversion (calls _converter() method). + * Workhorse function that does actual conversion (calls converter() method). */ - protected function _convert() + protected function convert() { // Convert TXT to HTML - $this->html = $this->_converter($this->text); - $this->_converted = true; + $this->html = $this->converter($this->text); + $this->converted = true; } /** @@ -138,7 +141,7 @@ class rcube_text2html * * @return string HTML content */ - protected function _converter($text) + protected function converter($text) { // make links and email-addresses clickable $attribs = ['link_attribs' => ['rel' => 'noreferrer', 'target' => '_blank']]; @@ -167,7 +170,7 @@ class rcube_text2html if ($first == '>' && preg_match('/^(>+ {0,1})+/', $text[$n], $regs)) { $q = substr_count($regs[0], '>'); $text[$n] = substr($text[$n], strlen($regs[0])); - $text[$n] = $this->_convert_line($text[$n]); + $text[$n] = $this->convert_line($text[$n]); $_length = strlen(str_replace(' ', '', $text[$n])); if ($q > $quote_level) { @@ -199,7 +202,7 @@ class rcube_text2html } } else { - $text[$n] = $this->_convert_line($text[$n]); + $text[$n] = $this->convert_line($text[$n]); $q = 0; $_length = strlen(str_replace(' ', '', $text[$n])); @@ -260,7 +263,7 @@ class rcube_text2html * * @return string Converted text */ - protected function _convert_line($text) + protected function convert_line($text) { static $table; @@ -273,16 +276,39 @@ class rcube_text2html $table["\t"] = ' '; } + // empty line? + if ($text === '') { + return $text; + } + // skip signature separator if ($text == '-- ') { return '--' . $this->config['space']; } + if ($this->nowrap) { + if (!in_array($text[0], [' ', '-', '+', '@'])) { + $this->nowrap = false; + } + } + else { + // Detect start of a unified diff + // TODO: Support normal diffs + // TODO: Support diff header and comment + if ( + ($text[0] === '-' && preg_match('/^--- \S+/', $text)) + || ($text[0] === '+' && preg_match('/^\+\+\+ \S+/', $text)) + || ($text[0] === '@' && preg_match('/^@@ [0-9 ,+-]+ @@/', $text)) + ) { + $this->nowrap = true; + } + } + // replace HTML special and whitespace characters $text = strtr($text, $table); $nbsp = $this->config['space']; - $wrappable = $this->config['flowed'] || $this->config['wrap']; + $wrappable = !$this->nowrap && ($this->config['flowed'] || $this->config['wrap']); // make the line wrappable if ($wrappable) { diff --git a/skins/elastic/styles/styles.less b/skins/elastic/styles/styles.less index 827df9dfc..b6932edbd 100644 --- a/skins/elastic/styles/styles.less +++ b/skins/elastic/styles/styles.less @@ -405,6 +405,18 @@ body.task-error-login #layout { font-family: monospace; font-size: 13px; } + + // This is needed for proper display of quoted plain text + blockquote { + display: inline-block; + min-width: 100%; + + & + br { + // compensate the spacing "removed" by the inline-block style above + display: block; + margin-top: 1em; + } + } } #compose-attachments { diff --git a/tests/Framework/Text2Html.php b/tests/Framework/Text2Html.php index 619e7d03a..c035350b8 100644 --- a/tests/Framework/Text2Html.php +++ b/tests/Framework/Text2Html.php @@ -157,4 +157,43 @@ class Framework_Text2Html extends PHPUnit\Framework\TestCase $this->assertEquals($expected, $html); } + + /** + * Test patches/diffs handling + */ + function test_text2html_patches_handling() + { + $input = "Start\n" + . "diff --git a/test.txt b/test.txt\n" + . "index 7642f44b9..6ce0170aa 100644\n" + . "--- a/test.txt\n" + . "+++ b/test.txt\n" + . "@@ -1982,7 +1982,7 @@ class test\n" + . " test1\n" + . " test2\n" + . " test3\n" + . "-test4\n" + . "+test5\n" + . " \n" + . "End"; + + $expected = "<div class=\"pre\">Start<br>\n" + . "diff --git a/test.txt b/test.txt<br>\n" + . "index 7642f44b9..6ce0170aa 100644<br>\n" + . "<span style=\"white-space:nowrap\">---_a/test.txt</span><br>\n" + . "<span style=\"white-space:nowrap\">+++_b/test.txt</span><br>\n" + . "<span style=\"white-space:nowrap\">@@_-1982,7_+1982,7_@@_class_test</span><br>\n" + . "<span style=\"white-space:nowrap\">_test1</span><br>\n" + . "<span style=\"white-space:nowrap\">_test2</span><br>\n" + . "<span style=\"white-space:nowrap\">_test3</span><br>\n" + . "<span style=\"white-space:nowrap\">-test4</span><br>\n" + . "<span style=\"white-space:nowrap\">+test5</span><br>\n" + . "<span style=\"white-space:nowrap\">_</span><br>\n" + . "End</div>"; + + $t2h = new rcube_text2html($input, false, ['space' => '_']); + $html = $t2h->get_html(); + + $this->assertEquals($expected, $html); + } } |