diff options
author | M Somerville <matthew-github@dracos.co.uk> | 2020-09-29 14:36:14 +0100 |
---|---|---|
committer | M Somerville <matthew-github@dracos.co.uk> | 2020-09-30 16:20:28 +0100 |
commit | dffc4acfe67fb05f928806601bab8ce1057e7b67 (patch) | |
tree | 55375f629d9330cec844d9e5fc31965ba59c9f59 | |
parent | 245f12237ad2c796667d5d4736483474c1b481ce (diff) |
Translate HTML to text for update alert emails.
-rw-r--r-- | perllib/FixMyStreet/Template.pm | 61 | ||||
-rw-r--r-- | t/app/controller/alert_new.t | 4 | ||||
-rw-r--r-- | templates/email/default/_email_comment_list.txt | 2 |
3 files changed, 64 insertions, 3 deletions
diff --git a/perllib/FixMyStreet/Template.pm b/perllib/FixMyStreet/Template.pm index 7d6798415..35efcc1cf 100644 --- a/perllib/FixMyStreet/Template.pm +++ b/perllib/FixMyStreet/Template.pm @@ -7,6 +7,7 @@ use FixMyStreet; use mySociety::Locale; use Attribute::Handlers; use HTML::Scrubber; +use HTML::TreeBuilder; use FixMyStreet::Template::SafeString; use FixMyStreet::Template::Context; use FixMyStreet::Template::Stash; @@ -161,6 +162,66 @@ sub sanitize { } +=head2 email_sanitize_text + +Intended for use in the _email_comment_list.txt template to allow HTML +in updates from staff/superusers. Sanitizes the HTML and then converts +it all to text. + +=cut + +sub email_sanitize_text : Fn('email_sanitize_text') { + my $update = shift; + + my $text = $update->{item_text}; + my $extra = $update->{item_extra}; + $extra = $extra ? RABX::wire_rd(new IO::String($extra)) : {}; + + my $staff = $extra->{is_superuser} || $extra->{is_body_user}; + + return $text unless $staff; + + $text = FixMyStreet::Template::sanitize($text); + + my $tree = HTML::TreeBuilder->new_from_content($text); + _sanitize_elt($tree); + + return $tree->as_text; +} + +my $list_type; +my $list_num; +my $sanitize_text_subs = { + b => [ '*', '*' ], + strong => [ '*', '*' ], + i => [ '_', '_' ], + em => [ '_', '_' ], + p => [ '', "\n\n" ], + li => [ '', "\n\n" ], +}; +sub _sanitize_elt { + my $elt = shift; + foreach ($elt->content_list) { + next unless ref $_; + $list_type = $_->tag, $list_num = 1 if $_->tag eq 'ol' || $_->tag eq 'ul'; + _sanitize_elt($_); + $_->replace_with("\n") if $_->tag eq 'br'; + $_->replace_with('[image: ', $_->attr('alt'), ']') if $_->tag eq 'img'; + $_->replace_with($_->as_text, ' [', $_->attr('href'), ']') if $_->tag eq 'a'; + $_->replace_with_content if $_->tag eq 'span' || $_->tag eq 'font'; + $_->replace_with_content if $_->tag eq 'ul' || $_->tag eq 'ol'; + if ($_->tag eq 'li') { + $sanitize_text_subs->{li}[0] = $list_type eq 'ol' ? "$list_num. " : '* '; + $list_num++; + } + if (my $sub = $sanitize_text_subs->{$_->tag}) { + $_->preinsert($sub->[0]); + $_->postinsert($sub->[1]); + $_->replace_with_content; + } + } +} + =head2 email_sanitize_html Intended for use in the _email_comment_list.html template to allow HTML diff --git a/t/app/controller/alert_new.t b/t/app/controller/alert_new.t index 16455c7c4..31d8c52ed 100644 --- a/t/app/controller/alert_new.t +++ b/t/app/controller/alert_new.t @@ -878,7 +878,7 @@ subtest 'check staff updates can include sanitized HTML' => sub { user => $user1, }); - my $update1 = $mech->create_comment_for_problem($report, $user2, 'Staff User', 'This is some update text with <strong>HTML</strong> and *italics*. <script>not allowed</script>', 't', 'confirmed', undef, { confirmed => $r_dt->clone->add( minutes => 8 ) }); + my $update1 = $mech->create_comment_for_problem($report, $user2, 'Staff User', '<p>This is some update text with <strong>HTML</strong> and *italics*.</p> <ul><li>Even a list</li><li>Which might work</li><li>In the <a href="https://www.fixmystreet.com/">text</a> part</li></ul> <script>not allowed</script>', 't', 'confirmed', undef, { confirmed => $r_dt->clone->add( minutes => 8 ) }); $update1->set_extra_metadata(is_body_user => $user2->from_body->id); $update1->update; @@ -896,7 +896,7 @@ subtest 'check staff updates can include sanitized HTML' => sub { FixMyStreet::DB->resultset('AlertType')->email_alerts(); my $email = $mech->get_email; my $plain = $mech->get_text_body_from_email($email); - like $plain, qr/This is some update text with <strong>HTML<\/strong> and \*italics\*\./, 'plain text part contains exactly what was entered'; + like $plain, qr/This is some update text with \*HTML\* and \*italics\*\.\r\n\r\n\* Even a list\r\n\r\n\* Which might work\r\n\r\n\* In the text \[https:\/\/www.fixmystreet.com\/\] part/, 'plain text part contains no HTML tags from staff update'; like $plain, qr/Public users <i>cannot<\/i> use HTML\./, 'plain text part contains exactly what was entered'; my $html = $mech->get_html_body_from_email($email); diff --git a/templates/email/default/_email_comment_list.txt b/templates/email/default/_email_comment_list.txt index dbf00640f..3e01580c3 100644 --- a/templates/email/default/_email_comment_list.txt +++ b/templates/email/default/_email_comment_list.txt @@ -1,7 +1,7 @@ [% FOR row IN data -%] [% row.item_name _ ' : ' IF row.item_name AND NOT row.item_anonymous -%] [% '(' _ cobrand.prettify_dt(row.confirmed) _ ') ' IF cobrand.include_time_in_update_alerts -%] -[% row.item_text %] +[% email_sanitize_text(row) %] ------ |