aboutsummaryrefslogtreecommitdiff
path: root/lib/models/calendar/CalendarDateAssignment.class.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/models/calendar/CalendarDateAssignment.class.php')
-rw-r--r--lib/models/calendar/CalendarDateAssignment.class.php722
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 '';
- }
-}