From 95ee1e2f2d41725d0485d84298a31eacd4eeda25 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Sat, 8 Jun 2024 23:52:59 +0300 Subject: [PATCH] Add support to embed iCalendar to email message --- mailboxes/lang/en | 9 ++ mailboxes/view_mail.cgi | 204 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 211 insertions(+), 2 deletions(-) diff --git a/mailboxes/lang/en b/mailboxes/lang/en index cd387a07d..c2ebb6f34 100644 --- a/mailboxes/lang/en +++ b/mailboxes/lang/en @@ -154,6 +154,15 @@ view_sub=Attached Email view_sub2=Attached email from $1 view_egone=This message no longer exists view_eugone=This user does not exist +view_ical=Event +view_ical_when=When +view_ical_where=Where +view_ical_who=Who +view_ical_orginizertime=Organizer time +view_ical_orginizername=Organizer name +view_ical_orginizeremail=Organizer email +view_ical_attendees=Attendees details +view_ical_desc=Event description view_gnupg=GnuPG signature verification view_gnupg_0=Signature by $1 is valid. diff --git a/mailboxes/view_mail.cgi b/mailboxes/view_mail.cgi index 9eefa8c41..91bfb2c37 100755 --- a/mailboxes/view_mail.cgi +++ b/mailboxes/view_mail.cgi @@ -148,11 +148,209 @@ else { print &ui_table_end(); # Show body attachment, with properly linked URLs -@bodyright = ( ); +my $bodycontents; +my @bodyright = ( ); +my $calendars = { }; +if (@calendars) { + # CSS for HTML version + $calendars->{'html'} .= < +.calendar-table { + width: 100%; + border-collapse: collapse; + border: 1px solid #99999933; + } + .calendar-table td { + padding: 5px; + vertical-align: top; + } + .calendar-table .calendar-cell { + background-color: #99999916; + text-align: center; + vertical-align: top; + padding: 2px; + padding-top: 24px; + width: 100px; + font-weight: bold; + } + .calendar-month { + font-size: 21px; + color: #1d72ff; + text-align: center; + padding: 2px 8px; + } + .calendar-day { + font-size: 24px; + text-align: center; + padding: 4px 8px; + } + .calendar-week { + font-size: 16px; + border-top: 1px dotted #999999aa; + padding: 6px; + display: inline-block; + } + .calendar-details h2 { + margin: 0; + font-size: 18px; + } + .calendar-details p { + margin: 4px 0; + } + .calendar-details .title { + font-size: 20px; + } + .calendar-details .detail strong { + opacity: 0.66; + white-space: nowrap; + } + .calendar-details .detail + .attendees p:first-child { + margin-top: 0; + } + details.calendar-details { + font-size: 90%; + display: inline-block; + margin-left: 9px; + } + details.calendar-details summary { + cursor: help; + } + details.calendar-details tr:has(>.detail+td:empty), + .calendar-details tr:has(>.detail+td:empty) { + display: none; + } + +STYLE + foreach my $calendar (@calendars) { + my $title = $calendar->{'summary'} || $calendar->{'description'}; + my $orginizer = $calendar->{'organizer_name'}; + my @attendees; + foreach my $a (@{$calendar->{'attendees'}}) { + push(@attendees, { name => $a->{'name'}, + email => $a->{'email'} }); + } + my $who = join(", ", map { $_->{'name'} } @attendees); + if ($who && $orginizer) { + $who .= ", ${orginizer}*"; + } + elsif ($orginizer) { + $who = "${orginizer}*"; + } + # HTML version + $calendars->{'html'} .= < + + +
+
+ $calendar->{'_obj_dtstart_local_time'}->{'month'} +
+
+ $calendar->{'_obj_dtstart_local_time'}->{'day'} +
+
+ $calendar->{'_obj_dtstart_local_time'}->{'week'} +
+
+ + + + + + + + + + + + + + + + + + +
+ $title +
+ $text{'view_ical_when'} + $calendar->{'dtwhen_local'}
+ $text{'view_ical_where'} + $calendar->{'location'}
+ $text{'view_ical_who'} + $who
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ $text{'view_ical_orginizertime'} + $calendar->{'dtwhen'}
+ $text{'view_ical_orginizername'} + $calendar->{'organizer_name'}
+ $text{'view_ical_orginizeremail'} + $calendar->{'organizer_email'}
+ $text{'view_ical_attendees'} + @{[join('', map { + "

$_->{'name'}
$_->{'email'}

" + } @attendees)]}
+ $text{'view_ical_desc'} + @{[join('
', + @{$calendar->{'description'}})]}
+
+ + + +HTML + # Text version + my %textical = ( + 'view_ical' => $title, + 'view_ical_when' => $calendar->{'dtwhen_local'}, + 'view_ical_where' => $calendar->{'location'}, + 'view_ical_who' => $who + ); + my $max_label_length = 0; + foreach my $key (sort keys %textical) { + my $label_length = length($text{$key}); + if ($label_length > $max_label_length) { + $max_label_length = $label_length; + } + } + $calendars->{'text'} = "=" x 79 . "\n"; + foreach my $key (sort keys %textical) { + my $label = $text{$key}; + my $value = $textical{$key}; + my $spaces .= " " x ($max_label_length - length($label)); + $calendars->{'text'} .= "$label$spaces : $value\n"; + } + $calendars->{'text'} .= "=" x 79 . "\n"; + } + } if ($body && $body->{'data'} =~ /\S/) { if ($body eq $textbody) { # Show plain text $bodycontents = "
";
+		$bodycontents .= $calendars->{'text'}
+			if ($calendars->{'text'});
 		foreach $l (&wrap_lines(&eucconv($body->{'data'}),
 					$config{'wrap_width'})) {
 			$bodycontents .= &link_urls_and_escape($l,
@@ -166,7 +364,9 @@ if ($body && $body->{'data'} =~ /\S/) {
 		}
 	elsif ($body eq $htmlbody) {
 		# Attempt to show HTML
-		$bodycontents = $body->{'data'};
+		$bodycontents = $calendars->{'html'}
+			if ($calendars->{'html'});
+		$bodycontents .= $body->{'data'};
 		my @imageurls;
 		my $image_mode = int(defined($in{'images'}) ? $in{'images'} : $config{'view_images'});
 		$bodycontents = &disable_html_images($bodycontents, $image_mode, \@imageurls);