<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># Copyright 2002-2006, Fred Steinberg, Brown Bear Software

package MailSubscription;
use strict;

use Calendar::Date;
use Calendar::EventFormatter;
use Calendar::I18N;
use Calendar::Mail::MailSender;
use Calendar::MasterDB;
use Calendar::User;

# 'whichOnes' is which subs to process:
#    - c:  by category
#    - e:  by specific event
#    - a:  all in calendar
sub new {
    my $class = shift;
    my %args = (verbose     =&gt; undef,
                baseURL     =&gt; undef,     # URL to Calcium script
                defaultDays =&gt; 3,
                whichOnes   =&gt; undef,     # which subs to process, def. all
                dryRun      =&gt; undef,     # for test, debug
                deliverTo   =&gt; undef,     # for test, debug
                @_);
    bless \%args, $class;
}

sub sendForToday {
    my $self = shift;

    $self-&gt;{mPrefs} = MasterDB-&gt;new-&gt;getPreferences;
    $self-&gt;{calAnonOffsets} = {};

    my $whichOnes = $self-&gt;{whichOnes} || 'cea';

    my $today = Date-&gt;new;

    my @calendars = MasterDB-&gt;getAllCalendars;

    foreach my $calName (@calendars) {
        my $db    = Database-&gt;new ($calName);
        my $prefs = $db-&gt;getPreferences;

        next unless $prefs-&gt;RemindersOn; # skip cals that are off

        $self-&gt;{calAnonOffsets}-&gt;{$calName} = $prefs-&gt;DefaultTimezone || 0;

        my $numDays = $prefs-&gt;RemindDays || $self-&gt;{defaultDays};
        my @numDays = split ' ', $numDays;

        my %eventsByAddress;        # each address gets 1 message w/possibly
                                    #  many events

        # Get reminder mail addresses
        my @toAll = ($whichOnes =~ /a/) ? $prefs-&gt;getRemindAllAddresses : ();
        my $byCat = ($whichOnes =~ /c/) ? $prefs-&gt;getRemindByCategory   : {};

        foreach my $days (@numDays) {
            my $theDate = $today + $days;

            my @events = $db-&gt;getApplicableEvents ($theDate, $prefs);

            if ($self-&gt;{verbose}) {
                my $m = "Calendar: '$calName'; $days days in advance " .
                           "- $theDate;";
                my $e = 'event' . (@events == 1 ? '' : 's');
                warn "$m ", @events+0, " $e found.\n";
            }

            next unless @events;

            foreach my $event (@events) {
                my %sendTo;

                my @addresses;

                # people who want all events
                @addresses = @toAll;

                # people who want events in any of these categories
                if ($event-&gt;getCategoryList) {
                    foreach my $cat (keys %$byCat) {
                        push @addresses, @{$byCat-&gt;{$cat}}
                            if $event-&gt;inCategory ($cat);
                    }
                }

                # people who want this particular event for this calendar (as
                #  opposed to every calendar this event might be included into)
                if ($whichOnes =~ /e/) {
                    my $addrs = $event-&gt;getSubscribers ($calName);
                    if ($addrs) {
                        push @addresses, split ',', $addrs;
                    }
                }

                next unless @addresses;

                # skip it if it's a daily repeating event and this isn't
                # the first day
                if ($event-&gt;isRepeating and
                    $event-&gt;repeatInfo-&gt;frequency == 1   and
                    $event-&gt;repeatInfo-&gt;period =~ /day/i and
                    $event-&gt;repeatInfo-&gt;startDate != $theDate) {
                    warn "skipping non-first daily repeating event\n"
                        if $self-&gt;{verbose};
                    next;
                }

                foreach (@addresses) {
                    $sendTo{lc($_)}++;
                }

                foreach my $addr (keys %sendTo) {
                    $eventsByAddress{$addr} ||= {};
                    $eventsByAddress{$addr}-&gt;{"$theDate"} ||= [];
                    push @{$eventsByAddress{$addr}-&gt;{"$theDate"}}, $event;
                }
            }
        }
        $self-&gt;_sendSubscriptions ($calName, $prefs, \%eventsByAddress);
        warn "\n" if $self-&gt;{verbose};
    }
    return;
}

sub _sendSubscriptions {
    my ($self, $calName, $prefs, $theHash) = @_;
    return unless ($calName and $prefs and keys %$theHash);

    my $i18n = I18N-&gt;new ($prefs-&gt;Language);

    my %mailParams = (From =&gt; $prefs-&gt;MailFrom
                              || (defined ($self-&gt;{mPrefs}-&gt;MailFrom) &amp;&amp;
                                           $self-&gt;{mPrefs}-&gt;MailFrom)
                              || "calcium\@$ENV{SERVER_NAME}",
                      SMTP =&gt; $self-&gt;{mPrefs}-&gt;MailSMTP,
                      );

    foreach my $addr (keys %$theHash) {
        # if this address has a user associated with it, use that tz offset
        # otherwise, use default offset for the calendar
        my $offset;
        if (my $user = User-&gt;userFromAddress ($addr)) {
            $offset = $user-&gt;timezone || 0;
        } else {
            $offset = $self-&gt;{calAnonOffsets}-&gt;{$calName} || 0;
        }

        my ($firstEvent, $firstDate);         # for custom subject:

        my $count = 0;
        my ($textmail, $htmlmail) = $self-&gt;_subMailHeadings;
        my $numDates = keys %{$theHash-&gt;{$addr}};
        foreach my $date (sort map {sprintf "%4s/%02s/%02s", split '/'}
                               keys %{$theHash-&gt;{$addr}}) {
            $date = Date-&gt;new ($date);
            my $events = $theHash-&gt;{$addr}-&gt;{$date};
            my $numToday = @$events;
            $count += $numToday;
            foreach (@$events) {
                $firstEvent ||= $_;
                $firstDate  ||= $date;
                my $event = $_-&gt;copy; # copy so timezone changes don't stick
                my $dateChange = $event-&gt;adjustForTimezone ($date, $offset);
                my $theDate = $date + $dateChange;
                my ($text, $html) = $event-&gt;formatForMail ($theDate, undef,
                                                           $prefs, $i18n);
#                $_-&gt;TZoffset (undef); # must clear for other addresses

                $textmail .= "$text";
                $htmlmail .= $html;

                if (--$numToday) { # if any more following
                    $textmail .= "----\n";
                    $htmlmail .= '&lt;hr width="5%" align="left"&gt;';
                }
            }
            if (--$numDates) { # if any more following
                $textmail .= "----\n";
                $htmlmail .= '&lt;hr width="15%" align="left"&gt;';
            }
            $textmail .= "\n";
            $htmlmail .= '&lt;br&gt;';
        }
        my ($textfoot, $htmlfoot) = $self-&gt;_subMailFootings ($calName, $addr,
                                                             $prefs, $i18n);
        $textmail .= $textfoot;
        $htmlmail .= $htmlfoot;

        if ($self-&gt;{verbose}) {
            warn "  Mailing to $addr\n";
            warn "    TZ Offset: $offset hours\n" if $offset;
        }
        next if $self-&gt;{dryRun};

        $addr = $self-&gt;{deliverTo} if $self-&gt;{deliverTo};
        #print "  Mailing to $addr: \n\n$textmail\n\n$htmlmail\n\n";

        $mailParams{To} = $addr;
        my $mailer = MailSender-&gt;new (%mailParams);
        my $subj = $prefs-&gt;SubscribeSubject;
        if ($subj) {
            $subj = $firstEvent-&gt;expandString ($subj, $firstDate, $i18n);
            $subj =~ s/[\r\n]/ /g; # remove newlines or mail gets munged
        } else {
            $subj = ($count &gt; 1) ? 'Upcoming Events from Calendar: '
                                 : 'Upcoming Event from Calendar: ';
            $subj = $i18n-&gt;get ($subj) . "'$calName'";
        }
        my $format = $prefs-&gt;MailFormat || 'both';
        my %contents = (text =&gt; $textmail,
                        html =&gt; $htmlmail);
        if (lc ($format) eq 'text') {
            delete $contents{html};
        } elsif (lc ($format) eq 'html') {
            delete $contents{text};
        }
        my $ok = $mailer-&gt;send ($subj, \%contents);
        warn "Mail notification failed! " . $mailer-&gt;error . "\n"
            unless $ok;
    }
}

sub _subMailHeadings {
    my $self = shift;
    my $text = '';
    my $html =
          qq {&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"} .
          "\n" .
          qq {        "http://www.w3.org/TR/REC-html40/loose.dtd"&gt;} .
          "\n" .
          "&lt;html&gt;&lt;head&gt;&lt;title&gt;Calcium Subscription Mail&lt;/title&gt;&lt;/head&gt;&lt;body&gt;" .
          "\n";
    return ($text, $html);
}

sub _subMailFootings {
    my ($self, $calName, $email, $prefs, $i18n) = @_;
    my $text = '';
    my $html = '';
    if ($self-&gt;{baseURL}) {
        my (@labels, @urls, @links);

        my $view   = $i18n-&gt;get ('View the calendar:');
        my $change = $i18n-&gt;get ('Change your subscription:');
        my $link   = $i18n-&gt;get ('Subscription Settings');

        my $max = length (length ($change) &gt; length ($view) ? $change : $view);

        push @labels, sprintf ("%-*s\n", $max, $view);
        push @labels, sprintf ("%-*s\n", $max, $change);

        push @urls,   "$self-&gt;{baseURL}?CalendarName=$calName";
        push @links,  qq {&lt;a href="$urls[0]"&gt;$calName&lt;/a&gt;};

        push @urls,   "$self-&gt;{baseURL}?CalendarName=$calName&amp;" .
                          "Op=OptionSubscribe&amp;EmailAddress=$email";
        push @links,  qq {&lt;a href="$urls[1]"&gt;$link&lt;/a&gt;};

        $html .= "&lt;table&gt;";
        foreach my $i (0..(@labels - 1)) {
            $text .= "$labels[$i] $urls[$i]\n";
            $html .= "&lt;tr&gt;&lt;td&gt;$labels[$i]&lt;/td&gt;&lt;td&gt;$links[$i]&lt;/td&gt;&lt;/tr&gt;";
        }
        $html .= "&lt;/table&gt;";

    }
    if (my $sig = $prefs-&gt;MailSignature || $self-&gt;{mPrefs}-&gt;MailSignature) {
        $text .= "----\n$sig";
        $html .= '&lt;hr width="25%" align="left"/&gt;&lt;p&gt;' .
                 Event::_escapeThis ($sig, undef, 1) . '&lt;/p&gt;';
    }
    $html .= qq {&lt;/body&gt;&lt;/html&gt;};
    return ($text, $html);
}

1;
</pre></body></html>