|
Server : Apache/2.4.62 System : FreeBSD fbsdweb2.web.rcn.net 14.1-RELEASE FreeBSD 14.1-RELEASE releng/14.1-n267679-10e31f0946d8 GENERIC amd64 User : www ( 80) PHP Version : 8.3.8 Disable Function : NONE Directory : /domains/compasssysweb/calendar/CalciumDir39/Calendar/ |
Upload File : |
# Copyright 1999-2003, Fred Steinberg, Brown Bear Software
package ValidateDate;
use strict;
use Calendar::Date;
use Calendar::Event;
# Return date, start time, end time, end date, error message.
# If times are ok, error message is undef
# Date and times adjusted for user timezone, if set.
sub getAndValidateDateAndTimes {
my $className = shift;
my ($op) = @_;
my ($startTime, $endTime, $endDate);
my ($startHour, $startMinute, $endHour, $endMinute,
$startAmOrPm, $endAmOrPm, $timePeriod) =
$op->getParams (qw (StartHourPopup StartMinutePopup
EndHourPopup EndMinutePopup
StartHourRadio EndHourRadio TimePeriod));
my $i18n = $op->I18N;
my $milTime;
# If Time Period specified, start with the defined times
if (defined $timePeriod and ($timePeriod ne '-')) {
my ($name, $start, $end, $display) =
$op->prefs->getTimePeriod ($timePeriod);
($startTime, $endTime) = ($start, $end);
}
# Otherwise, see if there is a normal time specified.
else {
undef $timePeriod;
# Start/End times are in range [-1..23] for hours, [-1..55] for mins.
if (defined ($startHour) and $startHour >= 0) {
$startAmOrPm ||= ''; # undef if military time
$endAmOrPm ||= '';
$milTime = !defined ($startAmOrPm);
$startMinute = 0 unless ($startMinute and $startMinute >= 0);
$startTime = ($startHour + ($startAmOrPm =~ /pm/i ? 12 : 0)) * 60
+ $startMinute;
if ($endHour < 0) {
$endHour = $endMinute = undef;
} else {
$endMinute = 0 unless ($endMinute and $endMinute > 0);
$endHour = 0 unless ($endHour and $endHour > 0);
$endTime = ($endHour + ($endAmOrPm =~ /pm/i ? 12 : 0)) * 60
+ $endMinute;
}
}
}
# Make sure times and dates make sense.
my $errorMessage;
# We don't check for start < end anymore; event could go to next day.
# if (defined $startTime && defined $endTime) {
# if ($startTime > $endTime) {
# $errorMessage = '<br>';
# $errorMessage .= $i18n->get ('<b>Start Time</b> cannot be later ' .
# 'than <b>End Time</b>');
# $errorMessage .= '<br>';
# $errorMessage .= "<blockquote><table>" .
# "<tr><td align='right'>" .
# $i18n->get ('Start Time:') . '</td>' .
# "<td align='right'>" .
# Event->getTimeString ($startTime, $milTime) .
# "</td>" .
# "<tr><td align='right'>" .
# $i18n->get ('End Time:') . '</td>' .
# "<td align='right'>" .
# Event->getTimeString ($endTime, $milTime) .
# '</td></tr></table></blockquote><br><hr>';
# }
# }
# Get and validate the date
my $date;
my ($dateYear, $dateMonth, $dateDay) =
$op->getParams (qw (DateYearPopup DateMonthPopup DateDayPopup));
if (Date->valid ($dateYear, $dateMonth, $dateDay)) {
$date = Date->new ($dateYear, $dateMonth, $dateDay);
} else {
$errorMessage .= '<br>' . $i18n->get ("Invalid <b>Date</b>: ") .
$i18n->get (Date->monthName ($dateMonth)) .
" $dateDay, $dateYear<br>";
}
my ($repeatType, $untilRadio, $untilYear, $untilMonth, $untilDay) =
$op->getParams (qw (RepeatRadio RepeatUntilRadio UntilYearPopup
UntilMonthPopup UntilDayPopup));
if ($repeatType && $repeatType !~ /none/i && $repeatType ne '') {
if ($untilRadio ne ' ') {
$endDate = Date->openFuture();
} else {
if (Date->valid ($untilYear, $untilMonth, $untilDay)) {
$endDate = Date->new ($untilYear, $untilMonth, $untilDay);
} else {
$errorMessage .= '<br>' .
$i18n->get ('Invalid <b>Repeat Until</b> Date') . ': ' .
$i18n->get (Date->monthName ($untilMonth)) .
" $untilDay, $untilYear<br>";
}
}
if ($endDate && $date && ($date > $endDate)) {
$errorMessage .= '<br>' .
$i18n->get ('<b>Repeat Until Date</b> cannot ' .
'be before the first date of the ' .
'event.') . '<br>';
$errorMessage .= '<br>' . $i18n->get ('Event Start Date:') . ' ' .
$date->pretty ($i18n) . '<br>' .
$i18n->get ('Repeat Until Date:') . ' ' .
$endDate->pretty ($i18n);
}
}
# Convert times and dates based on timezone; always store server time,
# so server times are returned.
# Date is always the date startTime is on.
my $z;
my $dateChange = 0;
if (defined $startTime and $z = $op->prefs->Timezone) {
$startTime -= $z * 60;
$endTime -= $z * 60 if (defined $endTime);
# If start time is yesterday or tomorrow, adjust date
if ($startTime < 0) {
$date -= int ($startTime/-1440) + 1; # 1440 = 24 * 60
$dateChange = -1;
} elsif ($startTime >= 24*60) {
$date += int ($startTime/1440);
$dateChange = 1;
}
$startTime %= 1440;
if (defined $endTime) {
$endTime %= 1440;
}
}
# $dateChange is -1 if we moved to yesterday, +1 if we moved to tomorrow.
return ($date, $startTime, $endTime, $timePeriod, $endDate, $dateChange,
$errorMessage);
}
# Assumes start < end for both
sub _timeConflict {
my ($start, $end, $start2, $end2, $separation) = @_;
return 0 unless defined $start;
$end = $start if !defined $end;
$end2 = $start2 if !defined $end2;
$end += 1440 if ($end < $start); # ends on next day
$end2 += 1440 if ($end2 < $start2);
return 0 if (defined ($end) and ($end + $separation <= $start2));
return 0 if (defined ($end2) and ($start >= $end2 + $separation));
return 1;
}
# Check for future limit, return a VD object
sub futureCheck {
my $className = shift;
my ($prefs, $date, $endDate, $ignoreLimit) = @_;
my $self = {};
bless $self, $className;
# return right away if we can
return $self unless $prefs->FutureLimit;
return $self if ($prefs->FutureLimit =~ /allow/i);
return $self if ($ignoreLimit and ($prefs->FutureLimit =~ /warn/i));
my $futureDate = Date->new;
my $amount = ($prefs->FutureLimitAmount || 0) + 0;
my $units = '';
if ($prefs->FutureLimitUnits =~ /(day|week|month|year)/i) {
$units = $1;
}
($units =~ /day/i and $futureDate->addDays ($amount)) or
($units =~ /week/i and $futureDate->addWeeks ($amount)) or
($units =~ /month/i and $futureDate->addMonths ($amount)) or
($units =~ /year/i and $futureDate->addYears ($amount));
$self->{error}++
if ($date > $futureDate or ($endDate and $endDate > $futureDate));
$self->{amount} = $amount;
$self->{units} = $units;
$self->{isWarning} = $prefs->FutureLimit =~ /warn/i;
$self;
}
sub isBad {
my $self = shift;
exists $self->{error} and $self->{error};
}
sub isWarning {
my $self = shift;
$self->{isWarning};
}
sub futureMessage {
my $self = shift;
my ($op, $calendarName, $i18n) = @_;
my $message = '<br><b>' .
$i18n->get ('The date is too far in the future!') .
'</b><br><br> ' .
$i18n->get ('Calendar') . ": $calendarName<br><br>" .
' ' .
$i18n->get ("Sorry, the calendar is set to not permit you " .
" to add or edit events that far in the future.")
. '<br><br> (' .
$i18n->get ('The maximum is:') . " $self->{amount} " .
$i18n->get ($self->{amount} == 1 ? $self->{units}
: $self->{units} . 's')
. ')<br><br><hr>';
if ($self->{isWarning}) {
my $href = $op->makeURL ({%{$op->{params}},
IgnoreFutureLimit => 1});
$message .= "<a href=$href>" . $i18n->get ('Add it anyway') .
"</a><br>";
}
$message;
}
# ----------------------------------------------------------------------------
# Pass operation and event ID of event we're modifying, if we're modifying
# one. (undef otherwise)
sub timeConflict {
my $className = shift;
my ($db, $prefs, $date, $startTime, $endTime,
$oldEventID, $ignoreTimeConflicts) = @_;
my $self = {};
bless $self, $className;
# return right away if we can
return $self unless defined $startTime;
return $self if ($prefs->TimeConflicts =~ /allow/i);
return $self if ($ignoreTimeConflicts and
($prefs->TimeConflicts =~ /warn/i));
my $separation = $prefs->TimeSeparation || 0;
# these are used for display only; convert for timezone
my $offset = $prefs->Timezone || 0;
$self->{startTime} = $startTime + $offset * 60;
$self->{endTime} = $endTime + $offset * 60
if (defined $endTime);
foreach (qw /startTime endTime/) {
next unless (defined $self->{$_});
$self->{$_} %= 1440; # 1440 = 24 * 60
# if ($self->{$_} < 0) {
# $self->{$_} += 24*60;
# } elsif ($self->{$_} >= 24*60) {
# $self->{$_} -= 24*60;
# }
}
$self->{oldEventID} = $oldEventID;
$self->{separation} = $separation;
$self->{date} = $date;
# Get possibly conflicting events
my @events = $db->getApplicableEvents ($date, $prefs,
'yesterday,noadjust');
foreach my $event (@events) {
# don't check against ourself if we're editing an event
next if ((defined $oldEventID) and ($event->id == $oldEventID));
my ($start, $end);
# if stored as period, get time as defined by the period
if (my $period = $event->timePeriod) {
my $thePrefs = $prefs;
if (my $incFrom = $event->includedFrom) {
$thePrefs = Preferences->new ($incFrom);
}
my ($name, $s, $e, $disp) = $thePrefs->getTimePeriod ($period);
($start, $end) = ($s, $e);
}
# otherwise, get start/end stored w/event
else {
($start, $end) = ($event->startTime, $event->endTime);
}
# if from yesterday, adjust start time to midnight today
my $isYesterday;
if (defined $end and $end < $start and
$event->Date and $event->Date != $date) {
$start = 0;
$isYesterday = 1;
}
if (_timeConflict ($start, $end, $startTime, $endTime, $separation)) {
$self->{error}++;
$self->{isWarning} = $prefs->TimeConflicts =~ /warn/i;
$self->{event} = $event;
$self->{prevNextString} = 'on the previous day'
if ($isYesterday);
return $self;
}
}
# If we extend into next day...
if (defined $endTime and $endTime < $startTime) {
$startTime = 0;
my @tomorrow = $db->getApplicableEvents ($date + 1, $prefs,
'noadjust');
foreach my $event (@tomorrow) {
# don't check against ourself if we're editing an event
next if ((defined $oldEventID) and ($event->id == $oldEventID));
if (_timeConflict ($event->startTime, $event->endTime,
$startTime, $endTime, $separation)) {
$self->{error}++;
$self->{isWarning} = $prefs->TimeConflicts =~ /warn/i;
$self->{event} = $event;
$self->{prevNextString} = 'on the next day';
return $self;
}
}
}
return $self unless $separation;
# if no conflict so far, and a separation is specified, and it offsets
# into the prev/next day, we need to check yesterday (or tomorrow). (Of
# course an event can run from 12:01-23:59, so we might need to check
# both ends.)
my ($previousDay, $nextDay);
if (($startTime - $separation) < 0) {
$previousDay = $date - 1;
}
if ($endTime and ($endTime + $separation) > 1440) {
$nextDay = $date + 1; # 1440 = 24*60
}
return $self unless ($previousDay or $nextDay);
if ($previousDay) {
my @events = $db->getApplicableEvents ($previousDay, $prefs,
'noadjust');
my $startx = $startTime + 1440;
my $endx = $endTime ? $endTime + 1440 : undef;
foreach my $event (@events) {
if (_timeConflict ($event->startTime, $event->endTime,
$startx, $endx, $separation)) {
$self->{error}++;
$self->{isWarning} = $prefs->TimeConflicts =~ /warn/i;
$self->{prevNextString} = 'on the previous day';
return $self;
}
}
}
if ($nextDay) {
my @events = $db->getApplicableEvents ($nextDay, $prefs, 'noadjust');
my $startx = $startTime - 1440;
my $endx = $endTime ? $endTime - 1440 : undef;
foreach my $event (@events) {
if (_timeConflict ($event->startTime, $event->endTime,
$startx, $endx, $separation)) {
$self->{error}++;
$self->{isWarning} = $prefs->TimeConflicts =~ /warn/i;
$self->{prevNextString} = 'on the next day';
return $self;
}
}
}
return $self;
}
# Pass CalName if a multi-cal op
sub conflictMessage {
my $self = shift;
my ($op, $calName) = @_;
my $prefs = $op->prefs;
my $i18n = $op->I18N;
my $prevNext = $self->{prevNextString} || '';
$prevNext = $i18n->get ($prevNext) if $prevNext;
my $military = $prefs->MilitaryTime;
my $start = Event->getTimeString ($self->{startTime}, $military);
my $end = defined $self->{endTime} ?
Event->getTimeString ($self->{endTime}, $military) : '';
my ($evStart, $evEnd) = $self->{event}->getDisplayTime ($prefs->Timezone);
my $evDate = $self->{event}->getDisplayDate ($self->{date},
$prefs->Timezone);
my $timeString2 = Event->getTimeString ($evStart, $military);
$timeString2 .= ' - ' . Event->getTimeString ($evEnd, $military)
if (defined $evEnd);
$timeString2 .= ' (' . $evDate->pretty ($i18n) . ')'
if ($evDate != $self->{date});
my ($string, $string2, $addOrChange);
if (defined $self->{oldEventID}) { # then we're replacing or copying
$string = $i18n->get ('The time of the edited event ' .
'conflicts with an existing event.');
$string2 = $i18n->get ('Edited Event Time');
$addOrChange = $i18n->get ('Change or copy it anyway');
} else {
$string = $i18n->get ('The time of the new event ' .
'conflicts with an existing event.');
$string2 = $i18n->get ('New Event Time');
$addOrChange = $i18n->get ('Add it anyway');
}
my $whichCalendar = '';
if ($calName) {
$whichCalendar = '<tr><td align="right">' .
$i18n->get ("Calendar Name") . ':' .
"</td><td>$calName</td></tr>";
}
my $date = $self->{date}->pretty ($i18n);
my $category = $self->{event}->category;
if (defined $category) {
$category = '<td align="right">' .
$i18n->get ('Conflicting Category') . ':</td>' .
"<td><i>$category</i></td>";
} else {
$category = '<td> </td>';
}
my $text;
if ($self->{event}->isTentative and
!$op->permission->permitted ($op->getUsername, 'Edit')) {
$text = $i18n->get ('Conflicting Event is Pending Approval');
} else {
$text = $self->{event}->text;
}
my $conflictMessage = '<br><b>' . $i18n->get ('Times conflict!') . '</b>' .
"<br><br> $string<br><br>" .
"<blockquote><table>" .
"<tr><td align='right'>" . $i18n->get ('Date:') .
"</td><td><b>$date</b></td></tr>" .
"<tr><td align='right'>$string2:</td>" .
'<td><b>' . $start .
($end && " - $end") .
'</b></td></tr>' .
"<tr><td align='right'>" .
$i18n->get('Conflicting Event Time') . ':' .
"</td><td><b>" . $timeString2 .
'<small><small> ' . $prevNext .
'</small></small></b></td></tr>' .
$whichCalendar .
"<tr><td align='right'>" .
$i18n->get('Conflicting Event Text') . ':' .
"</td><td><i>$text</i></td></tr>" .
"<tr>$category</tr>" .
'</table></blockquote>';
if ($self->{separation}) {
$conflictMessage .= '<br>(' .
$i18n->get ('Note: Event Separation is in effect')
. ": $self->{separation} "
. $i18n->get ('minutes') . ')';
}
$conflictMessage .= '<br><br><hr>';
if (!defined ($calName) and $self->{isWarning}) {
my $href = $op->makeURL ({%{$op->{params}}, IgnoreTimeConflict => 1});
$conflictMessage .= "<a href=$href>" . $i18n->get ($addOrChange) .
"</a><br>";
}
return $conflictMessage;
}
1;