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

# Check event for things like 'in the past', time conflicts

package Database;
use strict;
use Calendar::TimeConflict;

sub validateEvent {
    my $self = shift;
    my %args = (event           =&gt; undef,
                op              =&gt; undef,
                dateObj         =&gt; undef,
                originalEvent   =&gt; undef,
                originalDate    =&gt; undef,
                ignorePast      =&gt; undef,
                ignoreFuture    =&gt; undef,
                ignoreConflicts =&gt; undef,
                ignoreNoLastMinute =&gt; undef,
                ignoreMaxDuration  =&gt; undef,
                ignoreMinDuration  =&gt; undef,
                @_);
    my $event           = $args{event};
    my $prefs           = Preferences-&gt;new ($self);
    my $dateObj         = $args{dateObj};
    my $ignoreFuture    = $args{ignoreFuture};
    my $ignoreConflicts = $args{ignoreConflicts};
    my $ignorePast      = $args{ignorePast};
    my $ignoreLastMinute = $args{ignoreNoLastMinute};
    my $ignoreMaxTime   = $args{ignoreMaxDuration};
    my $ignoreMinTime   = $args{ignoreMinDuration};
    my $originalEvent   = $args{originalEvent};
    my $originalDate    = $args{originalDate};
    my $hasAdmin = $args{op} &amp;&amp; Permissions-&gt;new ($self)-&gt;permitted
                                            ($args{op}-&gt;getUsername, 'Admin');

    $event-&gt;Prefs ($prefs);     # needed for Time Periods

    my %errHash;

    # Check for required fields
    my $requireds = $prefs-&gt;RequiredFields || '';
    my @reqErrs;
    foreach (split ',', $requireds) {
        if (/category/i) {
            push @reqErrs, $_ unless defined $event-&gt;category;
        }
        if (/details/i) {
            push @reqErrs, $_ unless defined ($event-&gt;popup or $event-&gt;link);
        }
        if (/time/i) {
            push @reqErrs, $_ unless $event-&gt;hasTime;
        }
    }
    # And required custom fields
    my $fields_lr = $prefs-&gt;get_custom_fields;
    my $event_fields_hr = $event-&gt;get_custom_fields; # {id =&gt; value}
    foreach my $field (@$fields_lr) {
        next unless $field-&gt;required;
        my $value = $event_fields_hr-&gt;{$field-&gt;id};
        # If multi-valued, could be empty list, which is no good
        if (!defined $value
            or ($field-&gt;is_multi_valued and !$value)) {
            push @reqErrs, $field-&gt;name;
        }
    }

    $errHash{'missing required fields'} = join (',', @reqErrs)
        if @reqErrs;

    # Make sure repeating stuff actually makes sense
    if ($event-&gt;isRepeating) {
        my $instances = $event-&gt;repeatInfo-&gt;nextNOccurrences
                              ($event, 1, $dateObj, $prefs);
        $errHash{'repeating w/no instances'} = 1
            unless keys %$instances;
    }

    if ($prefs-&gt;NoPastEditing and (!$ignorePast or !$hasAdmin)) {
        my $now = Date-&gt;new;
        # If new event is in past, or editing past event, reject
        if ($dateObj &lt; $now or ($originalDate &amp;&amp; $originalDate &lt; $now)) {
            $errHash{'past event'} = 1;
        }
    }

    $errHash{'future limit'} = 1
        if $self-&gt;_checkFutureLimit ($event, $prefs, $dateObj, $ignoreFuture,
                                     $hasAdmin);

    # Only check 'last minute' restriction if editing an event.
    # But don't even bother if we've already caught a 'no past editing' error
    if (!$errHash{'past event'} and defined $originalEvent) {
        $errHash{'last minute'} =
          $self-&gt;_checkLastMinute ($event, $prefs, $dateObj, $ignoreLastMinute,
                                   $hasAdmin, $originalEvent, $originalDate);
    }

    $errHash{'event too long'} =
        $self-&gt;_checkMaxTime ($event, $prefs, $ignoreMaxTime, $hasAdmin);

    $errHash{'event too short'} =
        $self-&gt;_checkMinTime ($event, $prefs, $ignoreMinTime, $hasAdmin);

    $errHash{'time conflict'} =
        $self-&gt;_checkTimeConflicts ($event, $prefs, $dateObj, $originalEvent,
                                    $ignoreConflicts, $hasAdmin);
    return %errHash;
}

sub _checkFutureLimit {
    my ($self, $event, $prefs, $date, $ignoreFuture, $hasAdmin) = @_;

    return unless $prefs-&gt;FutureLimit;
    return if ($prefs-&gt;FutureLimit =~ /allow/i);
    return if ($prefs-&gt;FutureLimit =~ /warn/i and $ignoreFuture);
    return if ($hasAdmin and $ignoreFuture);       # admin can always do it

    my $futureDate = Date-&gt;new;
    my $amount = ($prefs-&gt;FutureLimitAmount || 0) + 0;

    my $units = $prefs-&gt;FutureLimitUnits;
    ($units =~ /day/i   and $futureDate-&gt;addDays   ($amount)) or
    ($units =~ /week/i  and $futureDate-&gt;addWeeks  ($amount)) or
    ($units =~ /month/i and $futureDate-&gt;addMonths ($amount)) or
    ($units =~ /year/i  and $futureDate-&gt;addYears  ($amount));

    return 1 if ($date &gt; $futureDate or
                 ($event-&gt;isRepeating and
                  $event-&gt;repeatInfo-&gt;endDate &gt; $futureDate));
    return;
}

sub _checkLastMinute {
    my ($self, $event, $prefs, $theDate, $ignoreIt, $hasAdmin,
        $origEvent, $origDate) = @_;

    return unless $prefs-&gt;NoLastMinute;
    return if ($prefs-&gt;NoLastMinute =~ /allow/i);
    return if ($prefs-&gt;NoLastMinute =~ /warn/i and $ignoreIt);
    return if ($hasAdmin and $ignoreIt);       # admin can always do it

    # get the epoch time of the event
    my $hour = int (($origEvent-&gt;startTime || 0) / 60);
    my $min  =      ($origEvent-&gt;startTime || 0) % 60;
    my $seconds = Time::Local::timelocal (0, $min, $hour,
                                          $origDate-&gt;day,
                                          $origDate-&gt;month - 1,
                                          $origDate-&gt;year - 1900);
    # see if it's too late!
    # NoLastMinuteAmount is always in units of hours (so convert to seconds)
    my $now = time;
    if ($now + $prefs-&gt;NoLastMinuteAmount * 3600 &gt;= $seconds) {
        return 1;
    }
    return;
}

# See if event is longer then allowed
sub _checkMaxTime {
    my ($self, $event, $prefs, $ignoreIt, $hasAdmin) = @_;

    return unless $prefs-&gt;MaxDuration;

    # If no time, or no end time (i.e. no duration), we're ok
    return if (!$event-&gt;hasTime or !defined ($event-&gt;endTime));

    return if ($prefs-&gt;MaxDuration =~ /allow/i);
    return if ($prefs-&gt;MaxDuration =~ /warn/i and $ignoreIt);
    return if ($hasAdmin and $ignoreIt);       # admin can always do it

    my $num_minutes = $prefs-&gt;MaxDurationAmount || 0;

    my ($start_time, $end_time) =  ($event-&gt;startTime, $event-&gt;endTime);
    if ($end_time &lt; $start_time) {
        $end_time += 24*60;    # end time is the next day
    }
    my $duration = $end_time - $start_time;
    return ($duration &gt; $num_minutes);
}

# See if event is shorter then allowed
sub _checkMinTime {
    my ($self, $event, $prefs, $ignoreIt, $hasAdmin) = @_;

    return unless $prefs-&gt;MinDuration;

    # If no time, that's ok
    return if !$event-&gt;hasTime;

    return if ($prefs-&gt;MinDuration =~ /allow/i);
    return if ($prefs-&gt;MinDuration =~ /warn/i and $ignoreIt);
    return if ($hasAdmin and $ignoreIt);       # admin can always do it

    my $num_minutes = $prefs-&gt;MinDurationAmount || 0;

    my ($start_time, $end_time) =  ($event-&gt;startTime, $event-&gt;endTime);
    $end_time = $start_time if (!defined $end_time);    # 0 duration
    if ($end_time &lt; $start_time) {
        $end_time += 24*60;    # end time is the next day
    }
    my $duration = $end_time - $start_time;
    return ($duration &lt; $num_minutes);
}

sub _checkTimeConflicts {
    my ($self, $event, $prefs, $date, $origEvent, $ignore, $hasAdmin) = @_;

    return unless defined $event-&gt;hasTime; # if no time, no conflicts!

    my @dateOccurs = ($date);
    my $alreadyFoundRepeaters;

    my @conflicts;

    return unless $prefs-&gt;TimeConflicts;
    return if ($prefs-&gt;TimeConflicts =~ /allow/i);
    return if ($prefs-&gt;TimeConflicts =~ /warn/i and $ignore);
    return if ($hasAdmin and $ignore);       # admin can always do it

    # if we're a repeating event, must check many instances, not just one.
    # check up to 5 years in advance
    if ($event-&gt;isRepeating) {
        my $repeatObject = $event-&gt;repeatInfo;
        my %occurences;
        my $endDate = $repeatObject-&gt;endDate;
        $endDate = $repeatObject-&gt;startDate + 365*5
            if ($repeatObject-&gt;startDate-&gt;deltaDays ($endDate) &gt; 365*5);
        $event-&gt;addToDateHash (\%occurences, $repeatObject-&gt;startDate,
                              $endDate, $prefs);
        my @dates = map {Date-&gt;new ($_)} keys %occurences;
        @dateOccurs = sort {$a &lt;=&gt; $b} @dates;
    }

    my $editedEventID = $origEvent ? $origEvent-&gt;id : undef;

    foreach my $thisDate (@dateOccurs) {
       if (my @conflicters = $event-&gt;conflicts ($self, $thisDate,
                                                $editedEventID)) {
            foreach (@conflicters) {
                push @conflicts, [$_, $thisDate];
            }
        }
    }

    # Return list of conflicting event/date pairs. oy.
    return \@conflicts
        if @conflicts;
    return;
}

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