aboutsummaryrefslogtreecommitdiff
path: root/lib/classes/calendar/ICalendarExport.class.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/classes/calendar/ICalendarExport.class.php')
-rw-r--r--lib/classes/calendar/ICalendarExport.class.php644
1 files changed, 0 insertions, 644 deletions
diff --git a/lib/classes/calendar/ICalendarExport.class.php b/lib/classes/calendar/ICalendarExport.class.php
deleted file mode 100644
index ce50f87..0000000
--- a/lib/classes/calendar/ICalendarExport.class.php
+++ /dev/null
@@ -1,644 +0,0 @@
-<?php
-/**
- * ICalendarExport.class.php
- *
- * 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 Peter Thienel <thienel@data-quest.de>
- * @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
- */
-
-
-class ICalendarExport
-{
- /**
- * Line break used in iCalendar
- */
- const NEWLINE = "\r\n";
-
- /**
- * Default start of the week
- */
- const WEEKSTART = 'MO';
-
- /**
- * Holds the time (as unix timestamp) used for
- * the timestamp in every exported iCalendar object.
- *
- * @var int $time
- */
- private $time = 0;
-
- public function __construct()
- {
- $this->default_filename_suffix = "ics";
- $this->format = "iCalendar";
- }
-
- public function exportCalendarDates(string $range_id, DateTimeInterface $start, DateTimeInterface $end): string
- {
- if ($this->time === 0) {
- $this->time = time();
- }
- $dates = CalendarDate::findBySQL(
- "LEFT JOIN `calendar_date_assignments`
- ON `calendar_dates`.`id` = `calendar_date_assignments`.`calendar_date_id`
- WHERE
- `calendar_date_assignments`.`range_id` = :range_id
- AND (
- (`calendar_dates`.`begin` <= :end
- AND `calendar_dates`.`end` >= :begin)
- OR (`calendar_dates`.`repetition_type` != 'SINGLE'
- AND (`calendar_dates`.`repetition_end` >= :end
- OR `calendar_dates`.`repetition_end` = 0)
- AND `calendar_dates`.`begin` < :end))",
- [
- ':range_id' => $range_id,
- ':begin' => $start->getTimestamp(),
- ':end' => $end->getTimestamp(),
- ]
- );
- $ical = '';
- foreach ($dates as $date) {
- $ical .= $this->writeICalEvent($this->prepareCalendarDate($date));
- }
- return $ical;
- }
-
- public function exportCourseDates(string $user_id, DateTimeInterface $start, DateTimeInterface $end)
- {
- if ($this->time === 0) {
- $this->time = time();
- }
- $dates = CourseDate::findBySql(
- "LEFT JOIN `seminar_user`
- ON `termine`.`range_id` = `seminar_user`.`Seminar_id`
- WHERE
- `seminar_user`.`user_id` = :user_id
- AND `seminar_user`.`bind_calendar` = 1
- AND (`termine`.`date` <= :end
- AND `termine`.`end_time` >= :begin)",
- [
- ':user_id' => $user_id,
- ':begin' => $start->getTimestamp(),
- ':end' => $end->getTimestamp(),
- ]
- );
- $ical = '';
- foreach ($dates as $date) {
- $ical .= $this->writeICalEvent($this->prepareCourseDate($date));
- }
- return $ical;
- }
-
- public function exportCourseExDates(string $user_id, DateTimeInterface $start, DateTimeInterface $end)
- {
- if ($this->time === 0) {
- $this->time = time();
- }
- $dates = CourseExDate::findBySql(
- "LEFT JOIN `seminar_user`
- ON `ex_termine`.`range_id` = `seminar_user`.`Seminar_id`
- WHERE
- `seminar_user`.`user_id` = :user_id
- AND `seminar_user`.`bind_calendar` = 1
- AND (`ex_termine`.`date` <= :end
- AND `ex_termine`.`end_time` >= :begin)",
- [
- ':user_id' => $user_id,
- ':begin' => $start->getTimestamp(),
- ':end' => $end->getTimestamp(),
- ]
- );
- $ical = '';
- foreach ($dates as $date) {
- $ical .= $this->writeICalEvent($this->prepareCourseDate($date));
- }
- return $ical;
- }
-
- /**
- * @param CalendarDate | CourseExDate $date
- * @return array
- */
- public function prepareCalendarDate($date): array
- {
- return [
- 'SUMMARY' => $date->title,
- 'DESCRIPTION' => $date->description,
- 'LOCATION' => $date->location,
- 'CATEGORIES' => $date->getCategoryAsString(),
- 'LAST-MODIFIED' => $date->chdate,
- 'CREATED' => $date->mkdate,
- 'DTSTAMP' => $this->time,
- 'DTSTART' => $date->begin,
- 'DTEND' => $date->end,
- 'EXDATE' => implode(',', $date->exceptions->pluck('date')),
- 'PRIORITY' => 5,
- 'RRULE' => [
- 'type' => $date->repetition_type,
- 'offset' => $date->offset,
- 'interval' => $date->interval,
- 'days' => $date->days,
- 'count' => $date->number_of_dates,
- 'expire' => $date->repetition_end,
- 'month' => $date->month
- ],
- 'UID' => $date->unique_id
- ];
- }
-
- /**
- * @param CalendarDate | CourseExDate $date
- * @return array
- */
- public function prepareCourseDate($date): array
- {
- $summary = $date->course->getFullName();
- $categories = $date->getTypeName();
- if ($date instanceof CourseExDate) {
- $summary .= ' ' . _('(fällt aus)');
- $categories = '';
- }
- return [
- 'SUMMARY' => $summary,
- 'DESCRIPTION' => '',
- 'LOCATION' => $date->getRoomName(),
- 'CATEGORIES' => $categories,
- 'LAST-MODIFIED' => $date->chdate,
- 'CREATED' => $date->mkdate,
- 'DTSTAMP' => $this->time,
- 'DTSTART' => $date->date,
- 'DTEND' => $date->end_time,
- 'PRIORITY' => '',
- 'UID' => 'Stud.IP-SEM-' . $date->id . '@' . ($_SERVER['SERVER_NAME'] ?? '')
- ];
- }
-
- /**
- * Returns an iCalendar header with a rudimentary time zone definition.
- *
- * @return string The iCalendar header.
- */
- public function writeHeader()
- {
- // Default values
- $header = "BEGIN:VCALENDAR" . self::NEWLINE;
- $header .= "VERSION:2.0" . self::NEWLINE;
- if (isset($this->client_identifier)) {
- $header .= "PRODID:" . $this->client_identifier . self::NEWLINE;
- } else {
- $server_name = $_SERVER['SERVER_NAME'] ?? 'unknown';
-
- $header .= "PRODID:-//Stud.IP@{$server_name}//Stud.IP_iCalendar Library";
- $header .= " //EN" . self::NEWLINE;
- }
- $header .= "METHOD:PUBLISH" . self::NEWLINE;
-
- // time zone definition CET/CEST
- $header .= 'CALSCALE:GREGORIAN' . self::NEWLINE
- . 'BEGIN:VTIMEZONE' . self::NEWLINE
- . 'TZID:Europe/Berlin' . self::NEWLINE
- . 'BEGIN:DAYLIGHT' . self::NEWLINE
- . 'TZOFFSETFROM:+0100' . self::NEWLINE
- . 'TZOFFSETTO:+0200' . self::NEWLINE
- . 'TZNAME:CEST' . self::NEWLINE
- . 'DTSTART:19700329T020000' . self::NEWLINE
- . 'RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3' . self::NEWLINE
- . 'END:DAYLIGHT' . self::NEWLINE
- . 'BEGIN:STANDARD' . self::NEWLINE
- . 'TZOFFSETFROM:+0200' . self::NEWLINE
- . 'TZOFFSETTO:+0100' . self::NEWLINE
- . 'TZNAME:CET' . self::NEWLINE
- . 'DTSTART:19701025T030000' . self::NEWLINE
- . 'RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10' . self::NEWLINE
- . 'END:STANDARD' . self::NEWLINE
- . 'END:VTIMEZONE' .self::NEWLINE;
-
- return $header;
- }
-
- /**
- * Returns the footer.
- *
- * @return string
- */
- public function writeFooter()
- {
- return "END:VCALENDAR" . self::NEWLINE;
- }
-
- /**
- * Export prepared calendar data as iCalendar.
- *
- * @param array $properties The event to export.
- * @return string iCalendar formatted data
- */
- public function writeICalEvent(array $properties): string
- {
- $result = "BEGIN:VEVENT" . self::NEWLINE;
-
- foreach ($properties as $name => $value) {
- $params = [];
- $params_str = '';
- if ($value === '' || is_null($value)) {
- continue;
- }
- switch ($name) {
- // not supported event properties
- case 'SEMNAME':
- continue 2;
-
- // Text fields
- case 'SUMMARY':
- $value = $this->quoteText($value);
- break;
- case 'DESCRIPTION':
- $value = $this->quoteText($value);
- break;
- case 'LOCATION':
- $value = $this->quoteText($value);
- break;
- case 'CATEGORIES':
- $value = $this->quoteText($value);
- break;
-
- // Date fields
- case 'LAST-MODIFIED':
- case 'CREATED':
- case 'COMPLETED':
- $value = $this->_exportDateTime($value, true);
- break;
-
- case 'DTSTAMP':
- $value = $this->_exportDateTime(time(), true);
- break;
-
- case 'DTSTART':
- $exdate_time = $value;
- case 'DTEND':
- case 'DUE':
- case 'RECURRENCE-ID':
- if (array_key_exists('VALUE', $params)) {
- if ($params['VALUE'] == 'DATE') {
- $value = $this->_exportDate($value);
- } else {
- $value = $this->_exportDateTime($value);
- $params_str = ';TZID=Europe/Berlin';
- }
- } else {
- $value = $this->_exportDateTime($value);
- $params_str = ';TZID=Europe/Berlin';
- }
- break;
-
- case 'EXDATE':
- if (array_key_exists('VALUE', $params)) {
- $value = $this->exportExDate($value);
- } else {
- $value = $this->exportExDateTime($value);
- }
- $params_str = ';TZID=Europe/Berlin';
- break;
-
- // Integer fields
- case 'PERCENT-COMPLETE':
- case 'REPEAT':
- case 'SEQUENCE':
- $value = "$value";
- break;
-
- case 'PRIORITY':
- switch ($value) {
- case 1:
- $value = '1';
- break;
- case 2:
- $value = '5';
- break;
- case 3:
- $value = '9';
- break;
- default:
- $value = '0';
- }
- break;
-
- // Geo fields
- case 'GEO':
- $value = $value['latitude'] . ',' . $value['longitude'];
- break;
-
- // Recursion fields
- case 'EXRULE':
- case 'RRULE':
- if ($value['type'] !== 'SINGLE') {
- $value = $this->_exportRecurrence($value);
- }
- break;
-
- case "UID":
- $value = "$value";
- }
- if ($name && !is_array($value)) {
- $attr_string = $name . $params_str . ':' . $value;
- $result .= $this->foldLine($attr_string) . self::NEWLINE;
- }
- }
- if (isset($properties['GROUP_EVENT'])) {
- $result .= $this->exportGroupEventProperties($properties['GROUP_EVENT']);
- }
- $result .= "END:VEVENT" . self::NEWLINE;
-
- return $result;
- }
-
- /**
- * Quotes some characters accordingly to iCalendar format.
- *
- * @param string $text The text to quote.
- * @return string The quoted text.
- */
- public function quoteText(string $text): string
- {
- $match = ['\\', '\n', ';', ','];
- $replace = ['\\\\', '\\n', '\;', '\,'];
- return str_replace($match, $replace, $text);
- }
-
- /**
- * Export a DateTime field
- *
- * @param int $value Unix timestamp
- * @return String Date and time (UTC) iCalendar formatted
- */
- public function _exportDateTime($value, $utc = false)
- {
- $date_time = new DateTime();
- $date_time->setTimestamp($value);
- //transform local time in UTC
- if ($utc) {
- $tz_utc = new DateTimeZone('UTC');
- $date_time->setTimezone($tz_utc);
- return $date_time->format('Ymd\THis\Z');
- }
- return $date_time->format('Ymd\THis');
- }
-
- /**
- * Export a Time field
- *
- * @param int $value Unix timestamp
- * @return String Time (UTC) iCalendar formatted
- */
- public function _exportTime($value, $utc = false)
- {
- $time = date("His", $value);
- if ($utc) {
- $time .= 'Z';
- }
-
- return $time;
- }
-
- /**
- * Export a Date field
- */
- public function _exportDate($value)
- {
- return date("Ymd", $value);
- }
-
- /**
- * Export a recurrence rule
- */
- public function _exportRecurrence($value)
- {
- $rrule = [];
- // the last day of week in a MONTHLY or YEARLY recurrence in the
- // Stud.IP calendar is 5, in iCalendar it is -1
- if ($value['offset'] == '5') {
- $value['offset'] = '-1';
- }
-
- if ($value['count']) {
- unset($value['expire']);
- }
-
- foreach ($value as $r_param => $r_value) {
- if ($r_value) {
- switch ($r_param) {
- case 'type':
- $rrule[] = 'FREQ=' . $r_value;
- break;
- case 'expire':
- if ($r_value < CalendarDate::NEVER_ENDING)
- $rrule[] = 'UNTIL=' . $this->_exportDateTime($r_value, true);
- break;
- case 'interval':
- $rrule[] = 'INTERVAL=' . $r_value;
- break;
- case 'days':
- switch ($value['type']) {
- case 'WEEKLY':
- $rrule[] = 'BYDAY=' . $this->_exportWdays($r_value);
- break;
- // Some CUAs (e.g. Outlook) don't understand the nWDAY syntax
- // (where n is the nth ocurrence of the day in a given period of
- // time and WDAY is the day of week) the RRULE uses the BYSETPOS
- // rule.
- case 'MONTHLY':
- case 'YEARLY':
- $rrule[] = 'BYDAY=' . $value['offset'] . $this->_exportWdays($r_value);
- $rrule[] = 'BYDAY=' . $this->_exportWdays($r_value);
- if ($value['offset']) {
- $rrule[] = 'BYSETPOS=' . $value['offset'];
- }
- break;
- }
- break;
- case 'day':
- $rrule[] = 'BYMONTHDAY=' . $r_value;
- break;
- case 'month':
- $rrule[] = 'BYMONTH=' . $r_value;
- break;
- case 'count':
- $rrule[] = 'COUNT=' . $r_value;
- break;
- }
- }
- }
-
- if ($value['type'] === 'WEEKLY' && self::WEEKSTART != 'MO') {
- $rrule[] = 'WKST=' . self::WEEKSTART;
- }
-
- return implode(';', $rrule);
- }
-
- /**
- * Return the days from CalendarDate::days as attribute of a event recurrence.
- *
- * @param string $value
- * @return string
- */
- public function _exportWdays(string $value): string
- {
- $wdays_map = ['1' => 'MO', '2' => 'TU', '3' => 'WE', '4' => 'TH', '5' => 'FR',
- '6' => 'SA', '7' => 'SU'];
- $wdays = [];
- preg_match_all('/(\d)/', $value, $matches);
- foreach ($matches[1] as $match) {
- $wdays[] = $wdays_map[$match];
- }
- return implode(',', $wdays);
- }
-
- /**
- * Formats dates of exception.
- *
- * @param string $value Unix timestamps as csv list.
- * @return string The formatted Exceptions.
- */
- public function exportExDate(string $value): string
- {
- $exdates = [];
- $date_times = explode(',', $value);
- foreach ($date_times as $date_time) {
- $exdates[] = $this->_exportDate($date_time);
- }
- return implode(',', $exdates);
- }
-
- /**
- * Formats date times of exception.
- *
- * @param string $value Unix timestamps as csv list.
- * @return string The formatted Exceptions.
- */
- public function exportExDateTime(string $value): string
- {
- $ex_dates = [];
- $ex_date_times = explode(',', $value);
- foreach ($ex_date_times as $ex_date_time) {
- $date_time = new DateTime();
- $date_time->setTimestamp($ex_date_time);
- $ex_dates[] = $date_time->format('Ymd\THis');
- }
- return implode(',', $ex_dates);
- }
-
- /**
- * Returns iCalendar group event properties if the date has mor than one attendee.
- *
- * @param CalendarDate $date The date object to extract the group data from.
- * @return string The formatted group event properties.
- */
- private function exportGroupEventProperties(CalendarDate $date): string
- {
- if (!count($date->calendars)) {
- return '';
- }
- $organizer = $date->author;
- if ($organizer) {
- $properties = $this->foldLine('ORGANIZER;CN="'
- . $organizer->getFullName()
- . '":mailto:' . $organizer->Email)
- . self::NEWLINE;
- } else {
- $properties = $this->foldLine('ORGANIZER;CN="'
- . _('unbekannt')
- . '":mailto:' . $GLOBALS['user']->email)
- . self::NEWLINE;
- }
- foreach ($date->calendars as $calendar) {
- if ($date->author_id === $calendar->range_id) {
- if ($calendar->user) {
- $properties .= $this->foldLine('ATTENDEE;'
- . 'ROLE=REQ-PARTICIPANT;'
- . 'CN="' . $calendar->user->getFullName()
- . '":mailto:' . $calendar->user->Email)
- . self::NEWLINE;
- } else {
- $properties = '';
- }
- } else {
- if ($calendar->user) {
- switch ($calendar->participation) {
- case 'ACCEPTED' :
- $attendee = 'ATTENDEE;ROLE=REQ-PARTICIPANT'
- . ';PARTSTAT=ACCEPTED';
- break;
- case 'ACKNOWLEDGED' :
- $attendee = 'ATTENDEE;ROLE=NON-PARTICIPANT'
- . ';PARTSTAT=ACCEPTED'
- . ';DELEGATED-TO="mailto:'
- . $this->getFacultyEmail($organizer->id)
- . '"';
- break;
- case 'DECLINED' :
- $attendee = 'ATTENDEE;ROLE=REQ-PARTICIPANT'
- . ';PARTSTAT=DECLINED';
- break;
- default :
- $attendee = 'ATTENDEE;ROLE=REQ-PARTICIPANT';
- $attendee .= ';PARTSTAT=TENTATIVE';
- $attendee .= ';RSVP=TRUE';
-
- }
- $attendee .= ';CN="' . $calendar->user->getFullName()
- . '":mailto:' . $calendar->user->Email;
- $properties .= $this->foldLine($attendee) . self::NEWLINE;
- }
- }
- }
- return $properties;
- }
-
- /**
- * @param string $user_id
- * @return string
- */
- private function getFacultyEmail(string $user_id): string
- {
- return DBManager::get()->fetchColumn('
- SELECT `email`
- FROM `Institute`
- LEFT JOIN `user_inst` USING(`institut_id`)
- WHERE `Institute`.`Institut_id` = `fakultaets_id`
- AND `user_id` = ?', [$user_id]);
- }
-
- /**
- * Returns the folded version of a text line.
- *
- * @param string $line
- * @return string
- */
- private function foldLine(string $line): string
- {
- $line = preg_replace('/(\r\n|\n|\r)/', '\n', $line);
- if (mb_strlen($line) > 75) {
- $foldedline = '';
- while ($line !== '') {
- $maxLine = mb_substr($line, 0, 75);
- $cutPoint = max(60, max(mb_strrpos($maxLine, ';'), mb_strrpos($maxLine, ':')) + 1);
-
- $foldedline .= ( empty($foldedline)) ?
- mb_substr($line, 0, $cutPoint) :
- self::NEWLINE . ' ' . mb_substr($line, 0, $cutPoint);
-
- $line = (mb_strlen($line) <= $cutPoint) ? '' : mb_substr($line, $cutPoint);
- }
- return $foldedline;
- }
- return $line;
- }
-}