[pmg-devel] [PATCH pmg-api v4 1/2] utils: allow specifying plain and/or html for finalize_report()

Stoiko Ivanov s.ivanov at proxmox.com
Mon Oct 14 20:05:47 CEST 2024


To support both plain-text and HTML emails when sending reports,
PMG::Utils::finalize_report() now checks if a template has a
plain-text equivalent - (`.plain.tt` instead of `.tt` as suffix).

if either template file is empty (or non-existant) the corresponding
part is simply left-out.

This should enable admins top optionally send plain-text only reports,
or multipart mails with both text/plain and text/html, while not
changing the current behavior

Co-developed-by: Christoph Heiss <c.heiss at proxmox.com>
Signed-off-by: Stoiko Ivanov <s.ivanov at proxmox.com>
---
 src/PMG/Backup.pm        |  2 +-
 src/PMG/CLI/pmgqm.pm     |  6 ++--
 src/PMG/CLI/pmgreport.pm |  2 +-
 src/PMG/Utils.pm         | 77 +++++++++++++++++++++++++++++++++-------
 4 files changed, 70 insertions(+), 17 deletions(-)

diff --git a/src/PMG/Backup.pm b/src/PMG/Backup.pm
index ab7e628..c820d6b 100644
--- a/src/PMG/Backup.pm
+++ b/src/PMG/Backup.pm
@@ -418,7 +418,7 @@ sub send_backup_notification {
     my $tt = PMG::Config::get_template_toolkit();
 
     my $mailfrom = "Proxmox Mail Gateway <postmaster>";
-    PMG::Utils::finalize_report($tt, 'backup-notification.tt', $vars, $mailfrom, $email);
+    PMG::Utils::finalize_report($tt, 'backup-notification', $vars, $mailfrom, $email);
 
 }
 
diff --git a/src/PMG/CLI/pmgqm.pm b/src/PMG/CLI/pmgqm.pm
index 987ddc9..7016161 100755
--- a/src/PMG/CLI/pmgqm.pm
+++ b/src/PMG/CLI/pmgqm.pm
@@ -261,14 +261,14 @@ __PACKAGE__->register_method ({
 	my $domains = PVE::INotify::read_file('domains');
 	my $domainregex = PMG::Utils::domain_regex([keys %$domains]);
 
-	my $template = "spamreport-${reportstyle}.tt";
+	my $template = "spamreport-${reportstyle}";
 	my $found = 0;
 	foreach my $path (@$PMG::Config::tt_include_path) {
-	    if (-f "$path/$template") { $found = 1; last; }
+	    if (-f "$path/$template.tt") { $found = 1; last; }
 	}
 	if (!$found) {
 	    warn "unable to find template '$template' - using default\n";
-	    $template = "spamreport-verbose.tt";
+	    $template = "spamreport-verbose";
 	}
 
 	my $sth = $dbh->prepare(
diff --git a/src/PMG/CLI/pmgreport.pm b/src/PMG/CLI/pmgreport.pm
index 3403e44..99adfd2 100644
--- a/src/PMG/CLI/pmgreport.pm
+++ b/src/PMG/CLI/pmgreport.pm
@@ -359,7 +359,7 @@ __PACKAGE__->register_method ({
 	}
 
 	my $mailfrom = "Proxmox Mail Gateway <postmaster>";
-	PMG::Utils::finalize_report($tt, 'pmgreport.tt', $vars, $mailfrom, $email, $param->{debug});
+	PMG::Utils::finalize_report($tt, 'pmgreport', $vars, $mailfrom, $email, $param->{debug});
 
 	return undef;
     }});
diff --git a/src/PMG/Utils.pm b/src/PMG/Utils.pm
index 5d9ded4..9069f81 100644
--- a/src/PMG/Utils.pm
+++ b/src/PMG/Utils.pm
@@ -1248,32 +1248,85 @@ sub format_uptime {
     }
 }
 
+# Sends a report to the given receiver, building the body from templates.
+# if either html or plain template is empty that part is not added.
+# The subject of the mail is derived from the title tag of the HTML template, with a fallback to
+# the value after `subject:` on a line of its own in the plaintext template.
+#
+# The resulting mail is then reinjected with an empty envelope sender.
+#
+# Parameters:
+#   * `tt` - The template toolkit instance to use
+#   * `template_base` - base name of the template. suffixed with 'tt' yields the HTML template.
+#      suffixed with 'plain.tt' yields the plain text template
+#   * `data` - Template variables to pass to the template processor
+#   * `mailfrom` - Sender address
+#   * `receiver` - Recipient address
+#   * `debug` - whether to enable debug mode, resulting mail is only     printed, not reinjected
 sub finalize_report {
-    my ($tt, $template, $data, $mailfrom, $receiver, $debug) = @_;
+    my ($tt, $template_base, $data, $mailfrom, $receiver, $debug) = @_;
+
+    my $html_template;
+    my $html_path;
+    for my $path (@$PMG::Config::tt_include_path) {
+	if (-f "$path/$template_base.tt") {
+	    $html_template = "$template_base.tt";
+	    $html_path = "$path/$template_base.tt";
+	    last;
+	}
+    }
 
     my $html = '';
 
-    $tt->process($template, $data, \$html) ||
-	die $tt->error() . "\n";
-
+    if (defined($html_template) && -s $html_path) {
+	$tt->process($html_template, $data, \$html) ||
+	    die $tt->error() . "\n";
+    }
     my $title;
     if ($html =~ m|^\s*<title>(.*)</title>|m) {
 	$title = $1;
-    } else {
-	die "unable to extract template title\n";
     }
 
+    my $plain_template;
+    my $plain_path;
+    for my $path (@$PMG::Config::tt_include_path) {
+	if (-f "$path/$template_base.plain.tt") {
+	    $plain_template = "$template_base.plain.tt";
+	    $plain_path = "$path/$template_base.plain.tt";
+	    last;
+	}
+    }
+
+    my $plaintext = '';
+    if (defined($plain_template) && -s $plain_path) {
+	$tt->process($plain_template, $data, \$plaintext) ||
+	    die $tt->error() . "\n";
+    }
+
+    if ($plaintext =~ s/^subject: (.+)$//m) {
+	$title //= $1;
+    }
+
+    die "unable to extract template title\n" if !defined($title);
+
     my $top = MIME::Entity->build(
-	Type    => "multipart/related",
+	Type    => ($html && $plaintext) ? 'multipart/alternative' : 'multipart/related',
 	To      => $data->{pmail_raw},
 	From    => $mailfrom,
 	Subject => bencode_header(decode_entities($title)));
 
-    $top->attach(
-	Data     => $html,
-	Type     => "text/html",
-	Encoding => $debug ? 'binary' : 'quoted-printable');
-
+    if ($html) {
+	$top->attach(
+	    Data     => $html,
+	    Type     => "text/html",
+	    Encoding => $debug ? 'binary' : 'quoted-printable');
+    }
+    if ($plaintext) {
+	$top->attach(
+	    Data     => $plaintext,
+	    Type     => 'text/plain; charset=utf-8',
+	    Encoding => '8-bit');
+    }
     if ($debug) {
 	$top->print();
 	return;
-- 
2.39.5





More information about the pmg-devel mailing list