Skip to content

Commit

Permalink
Fixes #4223 - Zammad ignores CSS white-space: pre-wrap; and displays …
Browse files Browse the repository at this point in the history
…multiline text in a single line.
  • Loading branch information
fliebe92 committed Sep 16, 2022
1 parent d61cbcb commit 695bf14
Show file tree
Hide file tree
Showing 59 changed files with 1,556 additions and 84 deletions.
8 changes: 8 additions & 0 deletions config/initializers/html_sanitizer.rb
Expand Up @@ -48,6 +48,7 @@
'q' => %w[cite],
'span' => %w[style],
'div' => %w[style],
'p' => %w[style],
'time' => %w[datetime pubdate],
}

Expand All @@ -64,6 +65,9 @@
'div' => %w[
color
],
'p' => %w[
white-space
],
'table' => %w[
background background-color color font-size vertical-align
margin margin-top margin-right margin-bottom margin-left
Expand Down Expand Up @@ -129,6 +133,10 @@
'color:#ffffff',
'color:rgb(0,0,0)',
],
'p' => [
'white-space:nowrap',
'white-space:pre',
],
'table' => [
'font-size:0',
'font-size:0px',
Expand Down
7 changes: 6 additions & 1 deletion lib/html_sanitizer/scrubber/cleanup.rb
Expand Up @@ -27,7 +27,12 @@ def update_node_content(node)
def remove_space_if_needed(content)
return content if space_or_nl?(content)

content.gsub(%r{[[:space:]]+}, ' ')
# https://github.com/zammad/zammad/issues/4223
# We are converting multiple line breaks into a more readable format.
# All other whitespace is treated as a single space character.
content.gsub(%r{[[:space:]]+}) do |match|
match.include?("\n\n") ? "\n\n" : ' '
end
end

def strip_if_needed_previous(node, content)
Expand Down
15 changes: 15 additions & 0 deletions spec/lib/html_sanitizer/scrubber/cleanup_spec.rb
Expand Up @@ -69,5 +69,20 @@

it { is_expected.to eq target }
end

context 'when p has style with white-space property' do
let(:input) do
'<p style="white-space: pre-wrap">Hi!
This is a dummy text for Zammad to test multi-line text that is wrapped in a preformatted text block.</p>'
end
let(:target) do
'<p style="white-space: pre-wrap">Hi!
This is a dummy text for Zammad to test multi-line text that is wrapped in a preformatted text block.</p>'
end

it { is_expected.to eq target }
end
end
end
4 changes: 4 additions & 0 deletions spec/models/channel/email_parser_spec.rb
Expand Up @@ -27,6 +27,10 @@
#
# File.write('test/data/mail/mailXXX.yml', Channel::EmailParser.new.parse(File.read('test/data/mail/mailXXX.box')).slice(:from, :from_email, :from_display_name, :to, :cc, :subject, :body, :content_type, :'reply-to', :attachments).to_yaml)
#
# To renew all existing files, you can use the following code:
#
# Dir.glob(Rails.root.join('test/data/mail/mail*.box')).each { |mail_file| File.write(mail_file.gsub('.box', '.yml'), Channel::EmailParser.new.parse(File.read(mail_file)).slice(:from, :from_email, :from_display_name, :to, :cc, :subject, :body, :content_type, :'reply-to', :attachments).to_yaml) }
#
context 'when checking a bunch of stored emails for correct parsing behaviour' do
tests = Dir.glob(Rails.root.join('test/data/mail/mail*.box')).each do |stored_email|
include_examples('parses email correctly', stored_email)
Expand Down
4 changes: 3 additions & 1 deletion test/data/mail/mail002.yml
Expand Up @@ -2,12 +2,14 @@
from: Martin Edenhofer <martin@example.com>
from_email: martin@example.com
from_display_name: Martin Edenhofer
to: metest@znuny.com
subject: aaäöüßad asd
content_type: text/plain
body: |
äöüß ad asd
-Martin
--
Old programmers never die. They just branch to a new address.
content_type: text/plain
attachments: []
5 changes: 4 additions & 1 deletion test/data/mail/mail004.yml
Expand Up @@ -2,8 +2,8 @@
from: '"Günther Katja | Example GmbH" <k.guenther@example.com>'
from_email: k.guenther@example.com
from_display_name: Günther Katja | Example GmbH
to: Martin Edenhofer via Znuny Team <support@example.com>
subject: 'AW: Ticket Templates [Ticket#11168]'
content_type: text/plain
body: |+
Hallo Katja,
Expand All @@ -25,3 +25,6 @@ body: |+
-Martin
content_type: text/plain
attachments: []
...
4 changes: 3 additions & 1 deletion test/data/mail/mail005.yml
Expand Up @@ -2,8 +2,8 @@
from: marc.smith@example.com (Marc Smith)
from_email: marc.smith@example.com
from_display_name: Marc Smith
to: Martin Edenhofer via Znuny Team <support@znuny.com>
subject: 'Re: XXXX Betatest Ticket Templates [Ticket#11162]'
content_type: text/plain
body: "Am 07.05.2012 08:10, schrieb Martin Edenhofer via Znuny Team:\n> Hallo Marc,\n>\n>
super! Ich freu mich!\n>\n> Wir würden gerne die Präsentation/Einführung in die
Ticket Templates per Screensharing oder zumindest per Telefon machen.\n>\n> Mögliche
Expand All @@ -22,3 +22,5 @@ body: "Am 07.05.2012 08:10, schrieb Martin Edenhofer via Znuny Team:\n> Hallo Ma
könnten, da wir von dem \"Internet PC\" nicht auf \nunser XXXX zugreifen können.
Falls ihr sonst noch irgendwas benötigt \neinfach kurz ne Rückmeldung...;-)\n\nGrüße
aus Bonn\n\nJohn & Marc\n\n"
content_type: text/plain
attachments: []
10 changes: 6 additions & 4 deletions test/data/mail/mail006.yml
Expand Up @@ -4,10 +4,12 @@ from_email: me@bogen.net
from_display_name: Hans BÄKOSchönland
to: Namedyński (hans@example.com)
subject: 'utf8: 使って / ISO-8859-1: Priorität" / cp-1251: Сергей Углицких'
body: '<div><p>this is a test</p></div><br><hr> <a href="http://localhost/8HMZENUS/2737??PS="
rel="nofollow noreferrer noopener" target="_blank" title="http://localhost/8HMZENUS/2737??PS=">Compare
Cable, DSL or Satellite plans: As low as $2.95. </a> <br> <br> Test1:– <br> Test2:&amp;
<br> Test3:∋ <br> Test4:&amp; <br> Test5:='
body: |-
<div><p>this is a test</p></div><br><hr> <a href="http://localhost/8HMZENUS/2737??PS=" rel="nofollow noreferrer noopener" target="_blank" title="http://localhost/8HMZENUS/2737??PS=">Compare Cable, DSL or Satellite plans: As low as $2.95. </a>
<br>
<br> Test1:– <br> Test2:&amp; <br> Test3:∋ <br> Test4:&amp; <br> Test5:=
content_type: text/html
attachments:
- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
Expand Down
75 changes: 73 additions & 2 deletions test/data/mail/mail008.yml
Expand Up @@ -2,12 +2,14 @@
from: Franz.Schaefer@example.com
from_email: Franz.Schaefer@example.com
from_display_name: ''
to: support@example.com
subject: 'could not rename: ZZZAAuto'
content_type: text/html
body: |-
<img src="cid:_1_08FC9B5808FC7D5C004AD64FC1257A28">
<br>
<br>Gravierend?<br> <table>
<br>Gravierend?<br>
<table>
<tr>
<td>Mit freundlichen Grüßen</td>
</tr>
Expand Down Expand Up @@ -70,3 +72,72 @@ body: |-
<td>Registergericht / Commercial Register of the Local Court: HRB 0000 AG Hof</td>
</tr>
</table>
content_type: text/html
attachments:
- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
data: |2-
<br><img src=cid:_1_08FC9B5808FC7D5C004AD64FC1257A28>
<br>
<br><font size=2 face="sans-serif">Gravierend?<br>
</font>
<table>
<tr>
<td bgcolor=white><font size=2 face="sans-serif">Mit freundlichen Grüßen</font></table>
<br>
<table>
<tr>
<td bgcolor=white><font size=2 face="sans-serif"><b>Franz Schäfer</b></font>
<tr>
<td bgcolor=white><font size=2 face="sans-serif">Manager Information Systems</font></table>
<br>
<table>
<tr>
<td bgcolor=white><font size=2 face="sans-serif">Telefon &nbsp;</font>
<td bgcolor=white><font size=2 face="sans-serif">+49 000 000 8565</font>
<tr>
<td colspan=2 bgcolor=white><font size=2 face="sans-serif">christian.schaefer@example.com</font></table>
<br>
<table>
<tr>
<td bgcolor=white><font size=2 face="sans-serif"><b>Example Stoff GmbH</b></font>
<tr>
<td bgcolor=white><font size=2 face="sans-serif">Fakultaet</font>
<tr>
<td bgcolor=white><font size=2 face="sans-serif">Düsseldorfer Landstraße
395</font>
<tr>
<td bgcolor=white><font size=2 face="sans-serif">D-00000 Hof</font>
<tr>
<td bgcolor=white><a href=www.example.com><font size=2 color=blue face="sans-serif"><u>www.example.com</u></font></a></table>
<br>
<table>
<tr>
<td bgcolor=white>
<hr>
<tr>
<td bgcolor=white><font size=1 color=#808080 face="sans-serif">Geschäftsführung/Management
Board: Jan Bauer (Vorsitzender/Chairman), Oliver Bauer, Heiko Bauer,
Boudewijn Bauer</font>
<tr>
<td bgcolor=white><font size=1 color=#808080 face="sans-serif">Sitz der
Gesellschaft / Registered Office: Hof</font>
<tr>
<td bgcolor=white><font size=1 color=#808080 face="sans-serif">Registergericht
/ Commercial Register of the Local Court: HRB 0000 AG Hof</font></table>
<br>
filename: message.html
preferences: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
content-alternative: true
original-format: true
Mime-Type: text/html
Charset: ISO-8859-1
- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
data: !binary |-
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
filename: image.gif
preferences: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
Content-Type: image/gif
Content-ID: _1_08FC9B5808FC7D5C004AD64FC1257A28
Mime-Type: image/gif
Charset: UTF-8
15 changes: 13 additions & 2 deletions test/data/mail/mail013.yml
Expand Up @@ -2,7 +2,18 @@
from: thomas.smith@example.com
from_email: thomas.smith@example.com
from_display_name: ''
subject: 'Antwort: Probleme ADB / Anlegen von Tickets [Ticket#111079]'
to: q1@znuny.com
content_type: text/html
subject: 'Antwort: Probleme ADB / Anlegen von Tickets [Ticket#111079]'
body: "<p>JA</p>"
content_type: text/html
attachments:
- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
data: |-
<html><body>
<p><font size="2" face="sans-serif">JA</font></body></html>
filename: message.html
preferences: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
content-alternative: true
original-format: true
Mime-Type: text/html
Charset: US-ASCII
13 changes: 11 additions & 2 deletions test/data/mail/mail015.yml

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion test/data/mail/mail016.yml
Expand Up @@ -4,7 +4,9 @@ from_display_name: ''
to: '"enterprisemobility.apacservice" <enterprisemobility.apacservice@motorola.com>'
subject: "【 直通美国排名第49大学 成功后付费 】"
body: |-
新建网页 3 <span style=" color: blue;">
新建网页 3
<span style=" color: blue;">
<div>您好,这封信如果影响到您的生活和工作,我们向您诚恳的道歉。</div><div>您回复此邮箱即可退订,以后绝对不会再次收到,</div><div>
<strong>再次祝福您工作顺利,婚姻美满,家庭幸福,生意兴隆。</strong> </div><div> </div></span>
<p><span style=" color: blue;"><b>直通美国排名第49大学 成功后付费<br></b><br></span><span> <b> 东北大学</b><br> <a href="http://www.northeastern.edu" rel="nofollow noreferrer noopener" target="_blank">www.northeastern.edu</a><br> </span></p><p>1、桥梁课程:完成后达到毕业标准,即可申请东北大学的学士学位/硕士学位。<br><span lang="EN-US">l </span>TOEFL:61Ibt(单项不低于13分,其中口语16分及写作14分以上)IELTS:6.0 (单项不低于5.5)<br><span lang="EN-US">l </span>在校成绩平均80分以上,其中上商学院所有专业,经济学和建筑学要求平均84分以上<br><br>2、直接申请本科:需要高中毕业或高三在读; TOEFLibt92(reading22,listening22,speaking24,<br>writing22), IELTS6.5;对国际学生而言,SAT或ACT非必须提供。GPA3.0以上<br><br>3、直接申请硕士:需本科毕业有学位或大四在读学生。硕士要求语言成绩100分以上。<br>GPA3.0以上,部分专业要求3.33以上。<br><br>优势专业:<br><br>商业及社会科学,工程学,数学及计算机学,物理及生命科学,商业行政管理,计算机学,<br>行为神经科学,经济学,化学,生物,金融及会计,工程学-土木及环保,生物技术,国际<br>事务,工程学-计算机系统,新闻学,工程学-电机,健康科学,数学,物理等。</p><p><br><b>留学咨询:</b></p><p>&nbsp;</p><p><b>陈老师:qq:477941912 电话:13521481644<br>王老师:qq:1756703114 电话: 13671177089</b><br> </p><p><b>【咨询回复邮箱即可】</b></p><p>&nbsp;</p><p><b>美国投资移民——<span lang="EN-US">50</span>万美元直投项目</b></p><p>&nbsp;</p><p>一、项目简介:<br><br>50万美元 加盟美国知名美发品牌 ,开自己的3家美发厅, 可以提供12-15个全职工作机<br>会,完全满足投资移民的用工需求;自己拥有三家店面的100% 股权, 品牌商推荐经过<br>企业严格考核的高级职业经理主理,财务100%透明, 保底年回报率1%, 5年后回购。<br><br>二、项目特点:</p><p><br><span lang="EN-US">l </span>100%直接投资、直接就业、审批时间短;<br><span lang="EN-US">l </span>自有100%的产权, 财务完全透明;<br><span lang="EN-US">l </span>知名品牌的成熟商业模式,严格考核的职业经理人主理;<br><span lang="EN-US">l </span>五年后安全的收购措施。<br> </p><p>三、运营模式:</p><p><br>品牌经理(相当于项目经理)已经有自己三家盈利的店,投资人自己也开三家店, 委托品牌<br>经理来管理, 如果盈利他拿1/4利润,如果亏损他也要赔1/4. 您觉得这样合理吗? <br><br>对于管理公司(项目总承包)来讲,如果同时管理着200家门店的品牌经理(项目经理), <br>这样的运营会赔钱吗?如果算总账不赔钱,他们就可以按照原价收购每个投资人的店面, <br>然后再按照市场价卖给加盟店的经理. 所以管理公司在享受投资期间3/4的利益的同时,<br>还会享受投资撤出后店面增值的收益。 </p><p>&nbsp;</p><p><b>移民咨询:</b></p><p>&nbsp;</p><p><b>李先生:<span lang="EN-US">qq: 2654035999 </span></b><b>电话:<span lang="EN-US">13391539988</span></b></p><p><br> </p><p>&nbsp;</p>enterprisemobility.apacservice XYCJDMPXYCICHYCICJEOUNSICIAACHXXYCGVTMPYBGVRDLMNSIBEPZGVSJENSIABFTKJFQCJFQBGVQCJDLMNSGWVSICHYCIBDLNRDMPXZFRFQDKJFSHYCHYDKHZGUOUOVRGVRFRDMOWUNTLNQBGUOUOWUQBDLLKJEOTNQBGUPZFREPXABEOVRFTLLKIBFSJENRGVTLKJEPZEOUOUPYCHXZFSHXXZENSHXXYCJFSJENQCJEQACIBFQBFREPXZFRFRGUPXZGUPYCGVTMNRFSHZENTKICHYDKICICIBEQBEOWUNTLKGWTNRDMNTLKHXYCJEPYCHZGWVRFSHZDMQACJENREOWUQC 17625
Expand Down

0 comments on commit 695bf14

Please sign in to comment.