aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorM Somerville <matthew-github@dracos.co.uk>2020-09-29 14:36:14 +0100
committerM Somerville <matthew-github@dracos.co.uk>2020-09-30 16:20:28 +0100
commitdffc4acfe67fb05f928806601bab8ce1057e7b67 (patch)
tree55375f629d9330cec844d9e5fc31965ba59c9f59
parent245f12237ad2c796667d5d4736483474c1b481ce (diff)
Translate HTML to text for update alert emails.
-rw-r--r--perllib/FixMyStreet/Template.pm61
-rw-r--r--t/app/controller/alert_new.t4
-rw-r--r--templates/email/default/_email_comment_list.txt2
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) %]
------