diff options
Diffstat (limited to 'lib/models/calendar/CalendarDateAssignment.class.php')
| -rw-r--r-- | lib/models/calendar/CalendarDateAssignment.class.php | 722 |
1 files changed, 0 insertions, 722 deletions
diff --git a/lib/models/calendar/CalendarDateAssignment.class.php b/lib/models/calendar/CalendarDateAssignment.class.php deleted file mode 100644 index fbd21a7..0000000 --- a/lib/models/calendar/CalendarDateAssignment.class.php +++ /dev/null @@ -1,722 +0,0 @@ -<?php -/** - * CalendarDateAssignment.class.php - Model class for calendar date assignments. - * - * CalendarDateAssignment represents the assignment of a calendar date - * to a specific calendar. The calendar is represented by a range-ID - * since it can be a personal calendar, course calendar or institute - * calendar. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * @author Moritz Strohm <strohm@data-quest.de> - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - * @since 5.5 - * - * @property string range_id The range-ID for the assignment. - * @property string calendar_date_id The ID of the calendar date for the assignment. - * @property string participation The participation status of the receiver (range_id). - * This column is an enum with the following values: - * - empty string: Participation status is unknown. - * - "ACCEPTED": The calendar owner accepted the date. - * - "DECLINED": The calendar owner declined the date. - * - "ACKNOWLEDGED": The calendar owner only acknowledged that the date exists - * but doesn't necessarily participate in it. - * @property string mkdate The creation date of the assignment. - * @property string chdate The modification date of the assignment. - * @property CalendarDate|null calendar_date The associated calendar date object. - */ -class CalendarDateAssignment extends SimpleORMap implements Event -{ - /** - * @var bool This attribute allows the suppression of automatic mail sending - * when storing or deleting the calendar date assignment. - * By default, mails are sent. - */ - public $suppress_mails = false; - - protected static function configure($config = []) - { - $config['db_table'] = 'calendar_date_assignments'; - - $config['belongs_to']['calendar_date'] = [ - 'class_name' => CalendarDate::class, - 'foreign_key' => 'calendar_date_id', - 'assoc_func' => 'find' - ]; - $config['belongs_to']['user'] = [ - 'class_name' => User::class, - 'foreign_key' => 'range_id', - 'assoc_func' => 'find' - ]; - $config['belongs_to']['course'] = [ - 'class_name' => Course::class, - 'foreign_key' => 'range_id', - 'assoc_func' => 'find' - ]; - - $config['registered_callbacks']['after_create'][] = 'cbSendNewDateMail'; - $config['registered_callbacks']['after_delete'][] = 'cbSendDateDeletedMail'; - - parent::configure($config); - } - - - public function cbSendNewDateMail() - { - if ($this->suppress_mails) { - return; - } - if ($this->range_id === $this->calendar_date->editor_id) { - return; - } - if (!$this->calendar_date || !$this->user) { - //Wrong calendar range (not a user) or invalid data set. - return; - } - - $template_factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); - - setTempLanguage($this->range_id); - $lang_path = getUserLanguagePath($this->range_id); - $template = $template_factory->open($lang_path . '/LC_MAILS/date_created.php'); - $template->set_attribute('date', $this->calendar_date); - $template->set_attribute('receiver', $this->user); - $mail_text = $template->render(); - Message::send( - '____%system%____', - [$this->user->username], - sprintf(_('%s hat einen Termin im Kalender eingetragen'), $this->calendar_date->editor->getFullName()), - $mail_text - ); - - restoreLanguage(); - } - - public function cbSendDateDeletedMail() - { - if ($this->suppress_mails) { - return; - } - $actor = User::findCurrent() ?? $this->calendar_date->editor; - if ($this->range_id === $actor->id) { - //The user who deleted the date shall not get notified about this. - return; - } - if (!$this->calendar_date || !$this->user) { - //Wrong calendar range (not a user) or invalid data set. - return; - } - - $template_factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); - - setTempLanguage($this->range_id); - $lang_path = getUserLanguagePath($this->range_id); - $template = $template_factory->open($lang_path . '/LC_MAILS/date_deleted.php'); - $template->set_attribute('date', $this->calendar_date); - $template->set_attribute('actor', $actor); - $template->set_attribute('receiver', $this->user); - $mail_text = $template->render(); - Message::send( - '____%system%____', - [$this->user->username], - sprintf(_('%s hat einen Termin im Kalender gelöscht'), $actor->getFullName()), - $mail_text - ); - - restoreLanguage(); - } - - /** - * Sends the participation status of the calendar the date - * is assigned to. This is only done for user calendars - * and not for course calendars. - * - * @return void - */ - public function sendParticipationStatus() : void - { - if (!($this->user instanceof User)) { - //The calendar date is assigned to a course calendar. - return; - } - - if (!$this->participation || $this->participation === 'ACKNOWLEDGED') { - //Nothing shall be done in these two cases. - return; - } - - if (empty($this->calendar_date->author->username)) { - //The calendar date has no author. - return; - } - if ($this->range_id === $this->calendar_date->author_id) { - //The author of the date changed their participation status. - //So they know what they did and do not have to be notified. - return; - } - - $template_factory = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/locale/'); - - setTempLanguage($this->range_id); - $lang_path = getUserLanguagePath($this->range_id); - $template = $template_factory->open($lang_path . '/LC_MAILS/date_participation.php'); - $template->set_attribute('date_assignment', $this); - $mail_text = $template->render(); - - $subject = ''; - if ($this->participation === 'ACCEPTED') { - $subject = sprintf( - _('%1$s hat Ihren Termin am %2$s angenommen'), - $this->user->getFullName(), - date('d.m.Y', $this->calendar_date->begin) - ); - } elseif ($this->participation === 'DECLINED') { - $subject = sprintf( - _('%1$s hat Ihren Termin am %2$s abgelehnt'), - $this->user->getFullName(), - date('d.m.Y', $this->calendar_date->begin) - ); - } - - Message::send( - '____%system%____', - [$this->calendar_date->author->username], - $subject, - $mail_text - ); - - restoreLanguage(); - } - - /** - * Retrieves calendar dates inside a specified time range that are present in the calendar of a - * course or user. They can additionally be filtered by the access level and declined events - * can be filtered out, too. - * - * @param DateTime $begin The beginning of the time range. - * - * @param DateTime $end The end of the time range. - * - * @param string $range_id The ID of the course or user whose calendar dates shall be retrieved. - * - * @param array $access_levels The access level filter: Only include calendar dates that have one of the - * access levels in the list. - * - * @param bool $with_declined Include declined calendar dates (true) or filter them out (false). - * Defaults to false. - * - * @return CalendarDateAssignment[] A list of calendar date assignments in the time range that match the filters. - */ - public static function getEvents( - DateTime $begin, - DateTime $end, - string $range_id, - array $access_levels = ['PUBLIC', 'PRIVATE', 'CONFIDENTIAL'], - bool $with_declined = false - ) : array - { - // Always use the timezone of the server: - $local_timezone = (new DateTime())->getTimezone(); - $begin->setTimezone($local_timezone); - $end->setTimezone($local_timezone); - - // one whole day as minimum (begin and end time stamp at the same day) - $begin->modify('midnight'); - $end->modify('tomorrow -1 second'); - - $sql = "JOIN `calendar_dates` - ON calendar_date_id = `calendar_dates`.`id` - WHERE - `calendar_date_assignments`.`range_id` = :range_id - AND - `access` IN ( :access_levels ) "; - if (!$with_declined) { - $sql .= "AND `calendar_date_assignments`.`participation` <> 'DECLINED' "; - } - $sql_single = $sql . " AND - `calendar_dates`.`begin` < :end AND :begin < `calendar_dates`.`end` - "; - - $events = self::findBySql($sql_single, [ - 'range_id' => $range_id, - 'begin' => $begin->getTimestamp(), - 'end' => $end->getTimestamp(), - 'access_levels' => $access_levels - ]); - - - $sql_repetition = $sql . " AND `calendar_dates`.`begin` < :end AND `calendar_dates`.`repetition_type` IN ('DAYLY', 'WEEKLY', 'MONTHLY', 'YEARLY') - AND `calendar_dates`.`repetition_end` > :begin - "; - - $events = array_merge($events, self::findBySql($sql_repetition, [ - 'range_id' => $range_id, - 'begin' => $begin->getTimestamp(), - 'end' => $end->getTimestamp(), - 'access_levels' => $access_levels - ])); - - $m_start = clone $begin; - $m_end = clone $end; - $events_created = []; - while ($m_start < $m_end) { - - foreach ($events as $event) { - $e_start = clone $event->getBegin(); - $e_end = clone $event->getEnd(); - $e_expire = $event->getExpire(); - - $cal_start = DateTimeImmutable::createFromMutable($m_start); - $cal_end = DateTimeImmutable::createFromMutable($m_start)->modify('tomorrow -1 second'); - $cal_noon = $cal_start->modify('noon'); - // single events or first event - if ( - ($e_start >= $cal_start && $e_end <= $cal_end) - || ($e_start >= $cal_start && $e_start <= $cal_end) - || ($e_start < $cal_start && $e_end > $cal_end) - || ($e_end > $cal_start && $e_start <= $cal_end) - ) { - // exception for first event or single event - if (!$event->calendar_date->exceptions->findOneBy('date', $cal_start->format('Y-m-d')) - && !isset($events_created[$event->calendar_date->id])) { - $events_created[$event->calendar_date->id . '_' . $event->calendar_date->begin] = $event; - } - } elseif ($e_expire > $cal_start) { - $events_created = array_merge($events_created, self::getRepetition($event, $cal_noon)); - } - } - - $m_start->modify('+1 day'); - } - - return $events_created; - } - - private static function getRepetition( - CalendarDateAssignment $date, - DateTimeImmutable $cal_noon, - bool $calc_prev = true - ): array - { - $rep_dates = []; - $ts = $date->getNoonDate(); - if ($cal_noon >= $ts) { - if ($date->isRepeatedAtDate($cal_noon)) { - $rep_dates = array_merge($rep_dates, self::createRecurrentDate($date, $cal_noon)); - } - if ($calc_prev) { - $rep_noon = $cal_noon->modify(sprintf('-%s days', $date->getDurationDays())); - $rep_dates = array_merge( - $rep_dates, - self::getRepetition( - $date, - $rep_noon, - false - ) - ); - } - } - return $rep_dates; - } - - private function isRepeatedAtDate(DateTimeImmutable $cal_date): bool - { - $ts = $this->getNoonDate(); - $pos = 1; - switch ($this->getRepetitionType()) { - case 'DAILY': - $pos = $cal_date->diff($ts)->days % $this->calendar_date->interval; - break; - case 'WEEKLY': - $cal_ts = $cal_date->modify('monday this week noon'); - if ($cal_date >= $this->getBegin()) { - $pos = $cal_ts->diff($ts)->days % ($this->calendar_date->interval * 7); - if ( - $pos === 0 - && strpos($this->calendar_date->days, $cal_date->format('N')) === false - ) { - $pos = 1; - } - } - break; - case 'MONTHLY': - $cal_ts = $cal_date->modify('first day of this month noon'); - $diff = $cal_ts->diff($ts); - $pos = ($diff->m + $diff->y * 12) % $this->calendar_date->interval; - if ($pos === 0) { - if (strlen($this->calendar_date->days)) { - $cal_ts_dom = $cal_ts->modify(sprintf('%s %s of this month noon', - $this->calendar_date->getOrdinalName(), - $this->calendar_date->getWeekdayName())); - if ($cal_ts_dom != $cal_date->setTime(12, 0)) { - $pos = 1; - } - } elseif ($this->calendar_date->offset !== $cal_date->format('j')) { - $pos = 1; - } - } - break; - case 'YEARLY': - $cal_ts = $cal_date->modify('first day of this year noon'); - $diff = $cal_ts->diff($ts); - $pos = $diff->y % $this->calendar_date->interval; - if ($pos === 0) { - if (strlen($this->calendar_date->days)) { - $ts_doy = $ts->modify(sprintf('%s %s of %s-%s noon', - $this->calendar_date->getOrdinalName(), - $this->calendar_date->getWeekdayName(), - $cal_date->format('Y'), - $this->calendar_date->month)); - if ($ts_doy->format('n-j') !== $cal_date->format('n-j')) { - $pos = 1; - } - } elseif ( - $cal_date->format('n-j') !== sprintf( - '%s-%s', - $this->calendar_date->month, - $this->calendar_date->offset - ) - ) { - $pos = 1; - } - } - break; - default: - $pos = 1; - } - //Also check for exceptions before returning: - return $pos === 0 - && !$this->calendar_date->exceptions->findOneBy( - 'date', - $cal_date->format('Y-m-d')); - } - - private static function createRecurrentDate( - CalendarDateAssignment $date, - DateTimeImmutable $date_time - ) : array - { - $date_begin = $date->getBegin(); - $date_end = $date->getEnd(); - - $rec_date = clone $date; - $time_begin = $date_begin->format('H:i:s'); - $time_end = $date_end->format('H:i:s'); - - $rec_date_begin = $date_time->modify(sprintf('today %s', $time_begin)); - $rec_date_end = $rec_date_begin->add($date->getDuration())->modify($time_end); - - $rec_date->calendar_date->begin = $rec_date_begin->getTimestamp(); - $rec_date->calendar_date->end = $rec_date_end->getTimestamp(); - $index = $date->calendar_date->id . '_' . $rec_date_begin->getTimestamp(); - return [$index => $rec_date]; - } - - //Event interface implementation: - - public function getObjectId() : string - { - return (string)$this->id; - } - - public function getPrimaryObjectID(): string - { - return $this->calendar_date_id; - } - - public function getObjectClass(): string - { - return static::class; - } - - public function getTitle() : string - { - return $this->calendar_date->title ?? ''; - } - - public function getBegin(): DateTime - { - $begin = new DateTime(); - $begin->setTimestamp($this->calendar_date->begin ?? 0); - return $begin; - } - - public function getEnd(): DateTime - { - $end = new DateTime(); - $end->setTimestamp($this->calendar_date->end ?? 0); - return $end; - } - - public function getDuration(): DateInterval - { - $begin = $this->getBegin(); - $end = $this->getEnd(); - return $begin->diff($end); - } - - /** - * Returns the "extent" in days of this date. - * - * @return int The "extent" in days of this date. - */ - public function getDurationDays(): int - { - return self::getExtent($this->getEnd(), $this->getBegin()); - } - - /** - * Returns the "extent" in days of this date. - * The extent is the number of days a date is displayed in a calendar. - * - * @return int The "extent" in days of this date. - */ - public static function getExtent(DateTimeInterface $date_begin, DateTimeInterface $date_end): int - { - $days_duration = $date_end->diff($date_begin)->days; - if ($date_begin->format('His') > $date_end->format('His')) { - $days_duration += 1; - } - return $days_duration; - } - - public function getLocation(): string - { - return $this->calendar_date->location ?? ''; - } - - public function getUniqueId(): string - { - return $this->calendar_date->unique_id ?? ''; - } - - public function getDescription(): string - { - return $this->calendar_date->description ?? ''; - } - - public function getAdditionalDescriptions(): array - { - return [ - _('Kategorie') => $this->calendar_date->getCategoryAsString(), - _('Sichtbarkeit') => $this->calendar_date->getVisibilityAsString(), - _('Wiederholung') => $this->calendar_date->getRepetitionAsString() - ]; - } - - public function isAllDayEvent(): bool - { - $begin = $this->getBegin(); - if ($begin->format('His') !== '000000') { - return false; - } - $end = $this->getEnd(); - return $end->format('His') === '235959'; - } - - public function isWritable(string $user_id): bool - { - if ($this->calendar_date->author_id === $user_id) { - //The author may always modify one of their dates: - return true; - } - if ($this->calendar_date->isWritable($user_id)) { - //The date is writable. - return true; - } - - //The user referenced by $user_id is not the author of the date. - //Check if they have write permissions to the calendar where the date is assigned to: - if ($this->user instanceof User) { - //It is a personal calendar. Check if the owner of the calendar has granted write permissions - //to the user: - return Contact::countBySQL( - "`owner_id` = :owner_id AND `user_id` = :user_id - AND `calendar_permissions` = 'WRITE'", - ['owner_id' => $this->range_id, 'user_id' => $user_id] - ) > 0; - } elseif ($this->course instanceof Course) { - //It is a course calendar. - return $GLOBALS['perm']->have_studip_perm('dozent', $this->range_id, $user_id); - } - - //No write permissions are granted. - return false; - } - - public function getCreationDate(): DateTime - { - $mkdate = new DateTime(); - $mkdate->setTimestamp($this->calendar_date->mkdate ?? 0); - return $mkdate; - } - - public function getModificationDate(): DateTime - { - $chdate = new DateTime(); - $chdate->setTimestamp($this->calendar_date->chdate ?? 0); - return $chdate; - } - - public function getImportDate(): DateTime - { - $import_date = new DateTime(); - $import_date->setTimestamp($this->calendar_date->import_date ?? 0); - return $import_date; - } - - public function getAuthor(): ?User - { - return $this->calendar_date->author ?? null; - } - - public function getEditor(): ?User - { - return $this->calendar_date->editor ?? null; - } - - /** - * TODO calculate end of repetition for different types of repetition - * @return float|int|object - */ - public function getExpire() - { - if ($this->calendar_date->repetition_end > 0) { - $expire = $this->calendar_date->repetition_end; - } else { - $expire = CalendarDate::NEVER_ENDING; - } - - $end = new DateTime(); - $end->setTimestamp($expire); - return $end; - } - - // TODO calculate ts for monthly and yearly repetition - public function getNoonDate() - { - $ts = DateTimeImmutable::createFromMutable($this->getBegin()); - switch ($this->calendar_date->repetition_type) { - case 'DAILY': - return $ts->modify('noon'); - case 'WEEKLY': - return $ts->modify('monday this week noon'); - case 'MONTHLY': - return $ts->modify('first day of this month noon'); - case 'YEARLY': - return $ts->modify('first day of this year noon'); - default: - return $ts; - } - } - - /** - * Returns the type of repetition. - * - * @return string The type of repetition. - */ - public function getRepetitionType(): string - { - return $this->calendar_date->repetition_type; - } - - public function toEventData(string $user_id): \Studip\Calendar\EventData - { - $begin = $this->getBegin(); - $end = $this->getEnd(); - - $all_day = $this->isAllDayEvent(); - - $hide_confidential_data = $this->calendar_date->access === 'CONFIDENTIAL' - && $user_id !== $this->calendar_date->author_id; - - $event_classes = ['user-date']; - - $text_colour = '#000000'; - $background_colour = '#ffffff'; - $border_colour = '#000000'; - if (!$hide_confidential_data) { - if ($this->calendar_date->user_category) { - //The date belongs to a personal category that gets a grey colour. - $background_colour = '#a7abaf'; - $border_colour = '#a7abaf'; - } else { - //The date belongs to a system category that has its own colours. - $text_colour = $GLOBALS['PERS_TERMIN_KAT'][$this->calendar_date->category]['fgcolor'] ?? $text_colour; - $background_colour = $GLOBALS['PERS_TERMIN_KAT'][$this->calendar_date->category]['bgcolor'] ?? $background_colour; - $border_colour = $GLOBALS['PERS_TERMIN_KAT'][$this->calendar_date->category]['border_color'] ?? $border_colour; - $event_classes[] = sprintf('user-date-category%d', $this->calendar_date->category); - } - } - - $show_url_params = []; - if ($this->calendar_date->repetition_type) { - $show_url_params['selected_date'] = $begin->format('Y-m-d'); - } - - return new \Studip\Calendar\EventData( - $begin, - $end, - !$hide_confidential_data ? $this->getTitle() : '', - $event_classes, - $text_colour, - $background_colour, - $this->isWritable($user_id), - CalendarDateAssignment::class, - $this->id, - CalendarDate::class, - $this->calendar_date_id, - 'user', - $this->range_id ?? '', - [ - 'show' => URLHelper::getURL('dispatch.php/calendar/date/index/' . $this->calendar_date_id, $show_url_params) - ], - [ - 'resize_dialog' => URLHelper::getURL('dispatch.php/calendar/date/move/' . $this->calendar_date_id), - 'move_dialog' => URLHelper::getURL('dispatch.php/calendar/date/move/' . $this->calendar_date_id) - ], - $this->participation === 'DECLINED' ? 'decline-circle-full' : '', - $border_colour, - $all_day - ); - } - - public function getRangeName() : string - { - if ($this->course instanceof Course) { - return $this->course->getFullName(); - } elseif ($this->user instanceof User) { - return $this->user->getFullName(); - } - return ''; - } - - public function getRangeAvatar() : ?Avatar - { - if ($this->course instanceof Course) { - return CourseAvatar::getAvatar($this->range_id); - } elseif ($this->user instanceof User) { - return Avatar::getAvatar($this->range_id); - } - return null; - } - - public function getParticipationAsString() : string - { - if ($this->participation === '') { - return _('Abwartend'); - } elseif ($this->participation === 'ACKNOWLEDGED') { - return _('Angenommen (keine Teilnahme)'); - } elseif ($this->participation === 'ACCEPTED') { - return _('Angenommen'); - } elseif ($this->participation === 'DECLINED') { - return _('Abgelehnt'); - } - return ''; - } -} |
