aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/Email.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/Email.pm')
-rw-r--r--perllib/FixMyStreet/Email.pm126
1 files changed, 122 insertions, 4 deletions
diff --git a/perllib/FixMyStreet/Email.pm b/perllib/FixMyStreet/Email.pm
index d955f6f72..34ac1514c 100644
--- a/perllib/FixMyStreet/Email.pm
+++ b/perllib/FixMyStreet/Email.pm
@@ -8,6 +8,7 @@ package FixMyStreet::Email;
use Email::MIME;
use Encode;
+use File::Spec;
use POSIX qw();
use Template;
use Digest::HMAC_SHA1 qw(hmac_sha1_hex);
@@ -72,10 +73,77 @@ sub is_abuser {
sub _render_template {
my ($tt, $template, $vars, %options) = @_;
my $var;
- $tt->process($template, $vars, \$var);
+ $tt->process($template, $vars, \$var) || print "Template processing error: " . $tt->error() . "\n";
return $var;
}
+sub _unique_id {
+ sprintf('fms-%s-%s@%s',
+ time(), unpack('h*', random_bytes(5, 1)),
+ FixMyStreet->config('EMAIL_DOMAIN'));
+}
+
+sub message_id {
+ '<' . _unique_id() . '>'
+}
+
+sub add_inline_image {
+ my ($inline_images, $obj, $name) = @_;
+ if (ref $obj eq 'HASH') {
+ return _add_inline($inline_images, $name, $obj->{data}, $obj->{content_type});
+ } else {
+ my $file = FixMyStreet->path_to($obj);
+ return _add_inline($inline_images, $file->basename, scalar $file->slurp);
+ }
+}
+
+sub _add_inline {
+ my ($inline_images, $name, $data, $type) = @_;
+
+ return unless $data;
+
+ $name ||= 'photo';
+ if ($type) {
+ if ($name !~ /\./) {
+ my ($suffix) = $type =~ m{image/(.*)};
+ $name .= ".$suffix";
+ }
+ } else {
+ my ($b, $t) = split /\./, $name;
+ $type = "image/$t";
+ }
+
+ my $cid = _unique_id();
+ push @$inline_images, {
+ body => $data,
+ attributes => {
+ id => $cid,
+ filename => $name,
+ content_type => $type,
+ encoding => 'base64',
+ name => $name,
+ },
+ };
+ return "cid:$cid";
+}
+
+# We only want an HTML template from the same directory as the .txt
+sub get_html_template {
+ my ($template, @include_path) = @_;
+ push @include_path, FixMyStreet->path_to( 'templates', 'email', 'default' );
+ (my $html_template = $template) =~ s/\.txt$/\.html/;
+ my $template_dir = find_template_dir($template, @include_path);
+ my $html_template_dir = find_template_dir($html_template, @include_path);
+ return $html_template if $template_dir eq $html_template_dir;
+}
+
+sub find_template_dir {
+ my ($template, @include_path) = @_;
+ foreach (@include_path) {
+ return $_ if -e File::Spec->catfile($_, $template);
+ }
+}
+
sub send_cron {
my ( $schema, $template, $vars, $hdrs, $env_from, $nomail, $cobrand, $lang_code ) = @_;
@@ -88,11 +156,11 @@ sub send_cron {
return 1 if is_abuser($schema, $hdrs->{To});
- $hdrs->{'Message-ID'} = sprintf('<fms-cron-%s-%s@%s>', time(),
- unpack('h*', random_bytes(5, 1)), FixMyStreet->config('EMAIL_DOMAIN')
- );
+ $hdrs->{'Message-ID'} = message_id();
my @include_path = @{ $cobrand->path_to_email_templates($lang_code) };
+ my $html_template = get_html_template($template, @include_path);
+
push @include_path, FixMyStreet->path_to( 'templates', 'email', 'default' );
my $tt = Template->new({
ENCODING => 'utf8',
@@ -102,6 +170,14 @@ sub send_cron {
$vars->{site_name} = Utils::trim_text(_render_template($tt, 'site-name.txt', $vars));
$hdrs->{_body_} = _render_template($tt, $template, $vars);
+ if ($html_template) {
+ my @inline_images;
+ $vars->{inline_image} = sub { add_inline_image(\@inline_images, @_) };
+ $vars->{file_exists} = sub { -e FixMyStreet->path_to(@_) };
+ $hdrs->{_html_} = _render_template($tt, $html_template, $vars);
+ $hdrs->{_html_images_} = \@inline_images;
+ }
+
my $email = mySociety::Locale::in_gb_locale { construct_email($hdrs) };
if ($nomail) {
@@ -236,6 +312,47 @@ sub construct_email ($) {
),
];
+ my $overall_type;
+ if ($p->{_html_}) {
+ my $html = _mime_create(
+ body_str => $p->{_html_},
+ attributes => {
+ charset => 'utf-8',
+ encoding => 'quoted-printable',
+ content_type => 'text/html',
+ },
+ );
+ if ($p->{_html_images_} || $p->{_attachments_}) {
+ $parts = [ _mime_create(
+ attributes => { content_type => 'multipart/alternative' },
+ parts => [ $parts->[0], $html ]
+ ) ];
+ } else {
+ # The top level will be the alternative multipart if there are
+ # no images and no other attachments
+ push @$parts, $html;
+ $overall_type = 'multipart/alternative';
+ }
+ if ($p->{_html_images_}) {
+ foreach (@{$p->{_html_images_}}) {
+ my $cid = delete $_->{attributes}->{id};
+ my $part = _mime_create(%$_);
+ $part->header_set('Content-ID' => "<$cid>");
+ push @$parts, $part;
+ }
+ if ($p->{_attachments_}) {
+ $parts = [ _mime_create(
+ attributes => { content_type => 'multipart/related' },
+ parts => $parts,
+ ) ];
+ } else {
+ # The top level will be the related multipart if there are
+ # images but no other attachments
+ $overall_type = 'multipart/related';
+ }
+ }
+ }
+
if ($p->{_attachments_}) {
push @$parts, map { _mime_create(%$_) } @{$p->{_attachments_}};
}
@@ -245,6 +362,7 @@ sub construct_email ($) {
parts => $parts,
attributes => {
charset => 'utf-8',
+ $overall_type ? (content_type => $overall_type) : (),
},
);