diff options
| author | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
|---|---|---|
| committer | Philipp Schüttlöffel <schuettloeffel@zqs.uni-hannover.de> | 2024-09-24 10:53:31 +0200 |
| commit | 4459dd7917f4d1c34f40bb68f0e991e9c3d53e4c (patch) | |
| tree | 5c07151ae61276d334e88f6309c30d439a85c12e /lib/models/resources/ResourceBooking.class.php | |
| parent | da0022e5c1abbf9825ae76debaabdff7e8623bb4 (diff) | |
| parent | 97a188592c679890a25c37ab78463add76a52ff7 (diff) | |
Merge branch 'main' into issue-3911issue-3911
Diffstat (limited to 'lib/models/resources/ResourceBooking.class.php')
| -rw-r--r-- | lib/models/resources/ResourceBooking.class.php | 1946 |
1 files changed, 0 insertions, 1946 deletions
diff --git a/lib/models/resources/ResourceBooking.class.php b/lib/models/resources/ResourceBooking.class.php deleted file mode 100644 index f55b3b3..0000000 --- a/lib/models/resources/ResourceBooking.class.php +++ /dev/null @@ -1,1946 +0,0 @@ -<?php - -/** - * ResourceBooking.class.php - model class for resource bookings - * - * The ResourceBooking class is responsible for storing - * bookings of resources in a specified time range - * or a time interval. - * - * 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> - * @copyright 2017-2019 - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - * @package resources - * @since 4.5 - * - * The repetition_interval column contains a date interval string in a - * format that is accepted by the DateInterval class constructor. - * Examples for values of the repetition_interval column: - * - For an one month interval, the value is "P1M". - * - For an interval of two days, the value is "P2D". - * See the DateInterval documentation for more examples: - * https://secure.php.net/manual/en/class.dateinterval.php - * - * @property string $id database column - * @property string $resource_id database column - * @property string $range_id database column - * @property string|null $description database column - * @property int $begin database column - * @property int $end database column - * @property int|null $repeat_end database column - * @property int $mkdate database column - * @property int $chdate database column - * @property string|null $internal_comment database column - * @property int $preparation_time database column - * @property int $booking_type database column - * @property string $booking_user_id database column - * @property string $repetition_interval database column - * @property SimpleORMapCollection|ResourceBookingInterval[] $time_intervals has_many ResourceBookingInterval - * @property Resource $resource belongs_to Resource - * @property User $assigned_user belongs_to User - * @property CourseDate $assigned_course_date belongs_to CourseDate - * @property User $booking_user belongs_to User - * @property mixed $course_id additional field - * @property mixed $room_name additional field - * @property-read mixed $real_begin additional field - * @property-read mixed $real_begin_dt additional field - */ -class ResourceBooking extends SimpleORMap implements PrivacyObject, Studip\Calendar\EventSource -{ - const TYPE_NORMAL = 0; - const TYPE_RESERVATION = 1; - const TYPE_LOCK = 2; - const TYPE_PLANNED = 3; - - protected static function configure($config = []) - { - $config['db_table'] = 'resource_bookings'; - - $config['belongs_to']['resource'] = [ - 'class_name' => Resource::class, - 'foreign_key' => 'resource_id', - 'assoc_func' => 'find' - ]; - $config['belongs_to']['assigned_user'] = [ - 'class_name' => User::class, - 'foreign_key' => 'range_id', - 'assoc_func' => 'find' - ]; - $config['belongs_to']['assigned_course_date'] = [ - 'class_name' => CourseDate::class, - 'foreign_key' => 'range_id', - 'assoc_func' => 'find' - ]; - $config['has_many']['time_intervals'] = [ - 'class_name' => ResourceBookingInterval::class, - 'assoc_foreign_key' => 'booking_id', - 'on_delete' => 'delete' - ]; - $config['belongs_to']['booking_user'] = [ - 'class_name' => User::class, - 'foreign_key' => 'booking_user_id', - 'assoc_func' => 'find' - ]; - - $config['additional_fields']['course_id'] = ['assigned_course_date', 'range_id']; - $config['additional_fields']['room_name'] = ['resource', 'name']; - - $config['additional_fields']['real_begin'] = [ - 'get' => function (ResourceBooking $booking) { - return $booking->begin - $booking->preparation_time; - } - ]; - $config['additional_fields']['real_begin_dt'] = [ - 'get' => function (ResourceBooking $booking) { - $real_begin = new DateTime(); - $real_begin->setTimestamp($booking->real_begin); - return $real_begin; - } - ]; - - $config['registered_callbacks']['after_store'][] = 'updateIntervals'; - $config['registered_callbacks']['after_store'][] = 'createStoreLogEntry'; - $config['registered_callbacks']['after_delete'][] = 'sendDeleteNotification'; - $config['registered_callbacks']['after_delete'][] = 'createDeleteLogEntry'; - - //In regard to TIC 6460: - //As long as TIC 6460 is not implemented, we must add the validate - //method as a callback before storing the object. - if (!method_exists('SimpleORMap', 'validate')) { - $config['registered_callbacks']['before_store'][] = 'validate'; - } - - parent::configure($config); - } - - private $assigned_user_type; - - public function createStoreLogEntry() - { - if ($this->isSimpleBooking()) { - StudipLog::log( - 'RES_ASSIGN_SINGLE', - $this->resource_id, - null, - $this->__toString(), - null, - $GLOBALS['user']->id - ); - } else { - StudipLog::log( - 'RES_ASSIGN_SEM', - $this->resource_id, - $this->course_id, - $this->__toString(), - null, - $GLOBALS['user']->id - ); - } - } - - - public function createDeleteLogEntry() - { - if ($this->isSimpleBooking()) { - StudipLog::log( - 'RES_ASSIGN_DEL_SINGLE', - $this->resource_id, - null, - $this->__toString(), - null, - $GLOBALS['user']->id - ); - } else { - StudipLog::log( - 'RES_ASSIGN_DEL_SEM', - $this->resource_id, - $this->course_id, - $this->__toString(), - null, - $GLOBALS['user']->id - ); - } - } - - - /** - * Internal method that generated the SQL query used in - * findByResourceAndTimeRanges and countByResourceAndTimeRanges. - * - * @see findByResourceAndTimeRanges - * @inheritDoc - */ - protected static function buildResourceAndTimeRangesSqlQuery( - Resource $resource, - $time_ranges = [], - $booking_types = [], - $excluded_booking_ids = [] - ) - { - if (!is_array($time_ranges)) { - throw new InvalidArgumentException( - _('Es wurde keine Liste mit Zeiträumen angegeben!') - ); - } - - //Check the array: - foreach ($time_ranges as $time_range) { - if ($time_range['begin'] > $time_range['end']) { - throw new InvalidArgumentException( - _('Der Startzeitpunkt darf nicht hinter dem Endzeitpunkt liegen!') - ); - } - - if ($time_range['begin'] == $time_range['end']) { - throw new InvalidArgumentException( - _('Startzeitpunkt und Endzeitpunkt dürfen nicht identisch sein!') - ); - } - } - - $sql_params = [ - 'resource_id' => $resource->id - ]; - - //First we build the SQL snippet for the case that the $booking_type - //variable is set to something different than null. - $booking_type_sql = ''; - if (is_array($booking_types) && count($booking_types)) { - $booking_type_sql = ' AND booking_type IN ( :booking_types )'; - $sql_params['booking_types'] = $booking_types; - } - - //Then we build the snippet for excluded booking IDs, if specified. - $excluded_booking_ids_sql = ''; - if (is_array($excluded_booking_ids) && count($excluded_booking_ids)) { - $excluded_booking_ids_sql = ' AND resource_booking_intervals.booking_id NOT IN ( :excluded_ids ) '; - $sql_params['excluded_ids'] = $excluded_booking_ids; - } - - //Now we build the SQL snippet for the time intervals. - //These are repeated four times in the query below. - //First we use one template ($time_sql_template) and - //replace NUMBER with our counting variable $i. - //BEGIN and END are replaced below since the columns for - //BEGIN and END are different in the four cases where we - //repeat the SQL snippet for the time intervals. - $time_sql_template = ' - (resource_booking_intervals.begin < :endNUMBER AND :beginNUMBER < resource_booking_intervals.end) - '; - - $time_sql = ''; - if ($time_ranges) { - $time_sql = ' AND ('; - - $i = 1; - foreach ($time_ranges as $time_range) { - if ($i > 1) { - $time_sql .= ' OR '; - } - $time_sql .= str_replace( - 'NUMBER', - $i, - $time_sql_template - ); - - if ($time_range['begin'] instanceof DateTime) { - $sql_params[('begin' . $i)] = $time_range['begin']->getTimestamp(); - } else { - $sql_params[('begin' . $i)] = $time_range['begin']; - } - if ($time_range['end'] instanceof DateTime) { - $sql_params[('end' . $i)] = $time_range['end']->getTimestamp(); - } else { - $sql_params[('end' . $i)] = $time_range['end']; - } - - $i++; - } - - $time_sql .= ') '; - } - - //Check if the booking has a start and end timestamp set - //or if it has repetitions that have a matching timestamp. - //This is done in the rest of the SQL query: - - $whole_sql = "resource_bookings.id IN ( - SELECT resource_booking_intervals.booking_id FROM resource_booking_intervals WHERE - resource_booking_intervals.resource_id = :resource_id - AND resource_booking_intervals.takes_place = 1" - . $excluded_booking_ids_sql - . $time_sql - . " GROUP BY resource_booking_intervals.booking_id ORDER BY NULL) - $booking_type_sql -"; - - return [ - 'sql' => $whole_sql, - 'params' => $sql_params - ]; - } - - - /** - * Retrieves all resource booking for the given resource and - * time range. By default, all booking are returned. - * To get only bookings of a certain type - * set the $booking_type parameter. - * - * @param Resource $resource The resource whose requests shall be retrieved. - * @param array $time_ranges An array with time ranges as DateTime objects. - * The array has the following structure: - * [ - * [ - * 'begin' => begin timestamp, - * 'end' => end timestamp - * ], - * ... - * ] - * @param array $booking_types An optional specification for the - * booking_type column in the database. More than one booking - * type can be specified. - * By default this is set to an empty array which means - * that resource booking are not filtered by the type column. - * The allowed resource booking types are specified in the - * class documentation. - * - * @param array $excluded_booking_ids An array of strings representing - * resource booking IDs. IDs specified in this array are excluded - * from the search. - * @return ResourceBooking[] An array of ResourceRequest objects. - * If no requests can be found, the array is empty. - * - * @throws InvalidArgumentException, if the time ranges are either not in an - * array matching the format description from above or if one of the - * following conditions is met in one of the time ranges: - * - begin > end - * - begin == end - */ - public static function findByResourceAndTimeRanges( - Resource $resource, - $time_ranges = [], - $booking_types = [], - $excluded_booking_ids = [] - ) - { - //Build the SQL query and the parameter array. - - $sql_data = self::buildResourceAndTimeRangesSqlQuery( - $resource, - $time_ranges, - $booking_types, - $excluded_booking_ids - ); - - //Call findBySql: - return self::findBySql($sql_data['sql'], $sql_data['params']); - } - - - /** - * Counts all resource bookings for the specified resource and - * time range. By default, all bookings are counted. - * To count only bookings of a certain type - * set the $booking_type parameter. - * - * @see findByResourceAndTimeRanges - * @inheritDoc - */ - public static function countByResourceAndTimeRanges( - Resource $resource, - $time_ranges = [], - $booking_types = [], - $excluded_booking_ids = [] - ) - { - //Build the SQL query and the parameter array. - - $sql_data = self::buildResourceAndTimeRangesSqlQuery( - $resource, - $time_ranges, - $booking_types, - $excluded_booking_ids - ); - - //Call countBySql: - return self::countBySql($sql_data['sql'], $sql_data['params']); - } - - - /** - * Deletes all resource bookings for the specified resource and - * time range. By default, all bookings are counted. - * To count only bookings of a certain type - * set the $booking_type parameter. - * - * @see findByResourceAndTimeRanges - * @inheritDoc - */ - public static function deleteByResourceAndTimeRanges( - Resource $resource, - $time_ranges = [], - $booking_types = [], - $excluded_booking_ids = [] - ) - { - //Build the SQL query and the parameter array. - $sql_data = self::buildResourceAndTimeRangesSqlQuery( - $resource, - $time_ranges, - $booking_types, - $excluded_booking_ids - ); - - //Call deleteBySql: - return self::deleteBySql($sql_data['sql'], $sql_data['params']); - } - - - /** - * The SimpleORMap::store method is overloaded to allow forced booking - * of resource bookings, meaning that all other bookings of the - * resource of a booking are deleted when they overlap with this booking. - * - * @param bool $force_booking Whether booking shall be forced (true) - * or not (false). Defaults to false. - * @return bool - */ - public function store($force_booking = false) - { - if ($force_booking == true) { - $this->deleteOverlappingBookings(); - } - $this->deleteOverlappingReservations(); - - if (parent::store()) { - //Check if the booking is bound to a course date. - //If this is the case, check for existing bookings - //and delete them, so that there is only one booking - //for a course date: - $course_date_exists = CourseDate::exists($this->range_id); - if ($course_date_exists) { - self::deleteBySql( - 'range_id = :range_id AND id <> :this_id', - [ - 'this_id' => $this->id, - 'range_id' => $this->range_id - ] - ); - } - return true; - } - return false; - } - - - /** - * This validation method is called before storing an object. - */ - public function validate() - { - if ((!$this->resource_id) || !($this->resource instanceof Resource)) { - throw new InvalidArgumentException( - _('Es wurde keine Ressource zur Buchung angegeben!') - ); - } - - if (!$this->range_id && !$this->description) { - throw new ResourceBookingRangeException( - _('Es muss eine Person oder ein Text zur Buchung eingegeben werden!') - ); - } - - if ($this->begin >= $this->end) { - throw new InvalidArgumentException( - _('Der Startzeitpunkt darf nicht hinter dem Endzeitpunkt liegen!') - ); - } - - if ($this->repetition_interval) { - if (!$this->repeat_end) { - throw new InvalidArgumentException( - _('Es wurde ein Wiederholungsintervall ohne Begrenzung angegeben!') - ); - } - if ($this->real_begin > $this->repeat_end) { - throw new InvalidArgumentException( - _('Der Startzeitpunkt darf nicht hinter dem Ende der Wiederholungen liegen!') - ); - } - } - - // update the booking user - if (!$this->isNew() || !$this->booking_user_id) { - $this->booking_user = User::findCurrent(); - } - - $derived_resource = $this->resource->getDerivedClassInstance(); - - // check if the user needs booking rights on the resource - if ( - $this->isFieldDirty('resource_id') - || $this->isFieldDirty('repetition_interval') - || $this->begin < $this->getPristineValue('begin') - || $this->end > $this->getPristineValue('end') - || $this->preparation_time > $this->getPristineValue('preparation_time') - || $this->repeat_end > $this->getPristineValue('repeat_end') - ) { - - //Check if the user has booking rights on the resource. - //The user must have either permanent permissions or they have to - //have booking rights by a temporary permission in this moment - $user_has_booking_rights = $derived_resource->userHasBookingRights( - $this->booking_user, $this->begin, $this->end - ); - if (!$user_has_booking_rights) { - throw new ResourcePermissionException( - sprintf( - _('Unzureichende Berechtigungen zum Buchen der Ressource %s!'), - $this->resource->name - ) - ); - } - } - - $time_intervals = $this->calculateTimeIntervals(true); - $time_interval_overlaps = []; - $existing_deleted_intervals = []; - if (!$this->isNew()) { - $existing_deleted_intervals = array_filter( - $this->getTimeIntervals(), - function ($i): bool { - return !$i->takes_place; - } - ); - } - foreach ($time_intervals as $time_interval) { - foreach ($existing_deleted_intervals as $deleted_interval) { - if ( - $time_interval['begin']->getTimestamp() == $deleted_interval['begin'] - && $time_interval['end']->getTimestamp() == $deleted_interval['end'] - ) { - continue 2; - } - } - $is_locked = $derived_resource->isLocked( - $time_interval['begin'], - $time_interval['end'], - ($this->isNew() ? [] : [$this->id]) - ); - if ($is_locked) { - if ($time_interval['begin']->format('Ymd') == $time_interval['end']->format('Ymd')) { - $time_interval_overlaps[] = sprintf( - _('Gesperrt im Bereich vom %1$s bis %2$s'), - $time_interval['begin']->format('d.m.Y H:i'), - $time_interval['end']->format('H:i') - ); - } else { - $time_interval_overlaps[] = sprintf( - _('Gesperrt im Bereich vom %1$s bis zum %2$s'), - $time_interval['begin']->format('d.m.Y H:i'), - $time_interval['end']->format('d.m.Y H:i') - ); - } - } else { - $is_assigned = $derived_resource->isAssigned( - $time_interval['begin'], - $time_interval['end'], - ($this->isNew() ? [] : [$this->id]) - ); - if ($is_assigned) { - //Find the other booking: - $other_booking = self::findByResourceAndTimeRanges( - $derived_resource, - [$time_interval], - [self::TYPE_NORMAL, self::TYPE_LOCK], - [$this->id] - ); - $course = null; - if ( - count($other_booking) >= 1 - && !empty($other_booking[0]->assigned_course_date->course) - ) { - $course = $other_booking[0]->assigned_course_date->course; - } - if ($time_interval['begin']->format('Ymd') == $time_interval['end']->format('Ymd')) { - if ($course) { - $time_interval_overlaps[] = sprintf( - _('Gebucht im Bereich vom %1$s bis %2$s durch die Veranstaltung %3$s.'), - $time_interval['begin']->format('d.m.Y H:i'), - $time_interval['end']->format('H:i'), - $course->getFullName() - ); - } else { - $time_interval_overlaps[] = sprintf( - _('Gebucht im Bereich vom %1$s bis %2$s'), - $time_interval['begin']->format('d.m.Y H:i'), - $time_interval['end']->format('H:i') - ); - } - } else { - if ($course) { - $time_interval_overlaps[] = sprintf( - _('Gebucht im Bereich vom %1$s bis zum %2$s durch die Veranstaltung %3$s.'), - $time_interval['begin']->format('d.m.Y H:i'), - $time_interval['end']->format('d.m.Y H:i'), - $course->getFullName() - ); - } else { - $time_interval_overlaps[] = sprintf( - _('Gebucht im Bereich vom %1$s bis zum %2$s'), - $time_interval['begin']->format('d.m.Y H:i'), - $time_interval['end']->format('d.m.Y H:i') - ); - } - } - } - } - } - if ($time_interval_overlaps) { - throw new ResourceBookingOverlapException( - implode(', ', $time_interval_overlaps) - ); - } - - NotificationCenter::postNotification('ResourceBookingWillValidate', $this); - } - - - /** - * This method updates the intervals of this resource booking - * which are stored in the resource_booking_intervals table. - */ - public function updateIntervals($keep_exceptions = true) - { - if ($keep_exceptions) { - //Delete all intervals with takes_place > 0 - //and update the time intervals of the exceptions: - ResourceBookingInterval::deleteBySql( - "booking_id = :booking_id - AND - takes_place > '0'", - [ - 'booking_id' => $this->id - ] - ); - - //Get all interval exceptions: - $exceptions = ResourceBookingInterval::findBySql( - "booking_id = :booking_id - AND - takes_place < '1'", - [ - 'booking_id' => $this->id - ] - ); - - if ($exceptions) { - //Now we must compare the time intervals of the booking - //with the time intervals of the exceptions. - //If there is only a difference in hours we update - //the exceptions. Otherwise we delete the exceptions. - - $repetition_interval = $this->getRepetitionInterval(); - if (!$repetition_interval) { - //No repetition interval also means nothing left to do. - return; - } - - $repetition_begin = new DateTime(); - $repetition_begin->setTimestamp($this->begin - $this->preparation_time); - $date_end = new DateTime(); - $date_end->setTimestamp($this->end); - $repetition_end = new DateTime(); - $repetition_end->setTimestamp($this->repeat_end); - $repetition_interval = $this->getRepetitionInterval(); - - $duration = $repetition_begin->diff($date_end); - - //Loop over all exceptions and check if they belong to - //one of the repetions: - - $obsolete_exception_ids = []; - foreach ($exceptions as $exception) { - $exception_begin = new DateTime(); - $exception_begin->setTimestamp($exception->begin); - $exception_end = new DateTime(); - $exception_end->setTimestamp($exception->end); - $exc_begin_str = $exception_begin->format('Y-m-d'); - $exc_end_str = $exception_end->format('Y-m-d'); - - $exception_obsolete = true; - - $current_repetition = clone $repetition_begin; - while ($current_repetition < $repetition_end) { - $current_end = clone $current_repetition; - $current_end->add($duration); - - $current_begin_str = $current_repetition->format('Y-m-d'); - $current_end_str = $current_end->format('Y-m-d'); - - if ($current_begin_str == $exc_begin_str && $current_end_str == $exc_end_str) { - //We found one exception which needs to be updated. - $exception_obsolete = false; - $exception->begin = $current_repetition->getTimestamp(); - $exception->end = $current_end->getTimestamp(); - if ($exception->isDirty()) { - $exception->store(); - } - //No need to loop the rest of the repetitions: - break; - } - - $current_repetition->add($repetition_interval); - } - - if ($exception_obsolete) { - $obsolete_exception_ids[] = $exception->id; - } - } - - if ($obsolete_exception_ids) { - ResourceBookingInterval::deleteBySql( - 'interval_id IN ( :ids )', - [ - 'ids' => $obsolete_exception_ids - ] - ); - } - } - } else { - //We delete all existing intervals for this booking - //and re-create them. - ResourceBookingInterval::deleteBySql( - 'booking_id = :booking_id', - [ - 'booking_id' => $this->id - ] - ); - } - - ResourceBookingInterval::createFromBooking($this); - } - - - /** - * Deletes the ResourceBooking object if there are no - * ResourceBookingInterval objects attachted to it. - * - * @return null|bool If the ResourceBooking object still has - * ResourceBookingInterval objects attachted to it, - * null is returned. Otherwise the return value of the - * SimpleORMap::delete method for the ResourceBooking object - * is returned. - */ - public function deleteIfNoInterval() - { - $intervals = ResourceBookingInterval::countBySql( - 'booking_id = :id', - [ - 'id' => $this->id - ] - ); - if ($intervals == 0) { - return $this->delete(); - } - - return null; - } - - - /** - * Deletes all bookings in the time ranges of this resource booking. - * Such bookings would prevent saving this booking. - * - * @return int The amount of deleted bookings. - */ - public function deleteOverlappingBookings() - { - $end = new DateTime(); - $end->setTimestamp($this->end); - $repetition_end = new DateTime(); - $repetition_end->setTimestamp($this->repeat_end); - - $deleted_c = 0; - if ($this->repetition_interval) { - $repetition_interval = $this->getRepetitionInterval(); - - if (!$this->repeat_end) { - throw new InvalidArgumentException( - _('Es wurde ein Wiederholungsintervall ohne Begrenzung angegeben!') - ); - } - if ($this->real_begin > $this->repeat_end) { - throw new InvalidArgumentException( - _('Der Startzeitpunkt darf nicht hinter dem Ende der Wiederholungen liegen!') - ); - } - - //Look in each repetition for overlapping bookings and delete them. - $current_date = $this->real_begin_dt; - while ($current_date <= $repetition_end) { - $current_begin = clone $current_date; - $current_end = clone $current_date; - $current_end->setTime( - intval($end->format('H')), - intval($end->format('i')), - intval($end->format('s')) - ); - $derived_resource = $this->resource->getDerivedClassInstance(); - if ($derived_resource->userHasPermission($this->booking_user, 'tutor', [$current_begin, $current_end])) { - //Sufficient permissions to delete bookings - //in the time frame. - $delete_sql = 'begin < :end AND end > :begin AND resource_id = :resource_id '; - $sql_params = [ - 'begin' => $current_begin->getTimestamp(), - 'end' => $current_end->getTimestamp(), - 'resource_id' => $this->resource->id - ]; - if (!$this->isNew()) { - $delete_sql .= 'booking_id <> :booking_id'; - $sql_params['booking_id'] = $this->id; - } - $intervals = ResourceBookingInterval::findBySQL( - $delete_sql, - $sql_params - ); - - $affected_bookings = []; - foreach ($intervals as $interval) { - if ($interval->booking instanceof ResourceBooking) { - $affected_bookings[$interval->booking_id] = $interval->booking; - } - $deleted_c += $interval->delete(); - } - - foreach ($affected_bookings as $booking) { - $booking->deleteIfNoInterval(); - } - } - $current_date = $current_date->add($repetition_interval); - } - } else { - $derived_resource = $this->resource->getDerivedClassInstance(); - if ($derived_resource->userHasPermission($this->booking_user, 'autor', [$this->real_begin_dt, $end])) { - $delete_sql = 'begin < :end AND end > :begin AND resource_id = :resource_id '; - $sql_params = [ - 'begin' => $this->real_begin, - 'end' => $end->getTimestamp(), - 'resource_id' => $this->resource->id - ]; - if (!$this->isNew()) { - $delete_sql .= 'booking_id <> :booking_id'; - $sql_params['booking_id'] = $this->id; - } - $intervals = ResourceBookingInterval::findBySQL( - $delete_sql, - $sql_params - ); - $affected_bookings = []; - foreach ($intervals as $interval) { - if ($interval->booking instanceof ResourceBooking) { - $affected_bookings[$interval->booking_id] = $interval->booking; - } - $deleted_c += $interval->delete(); - } - - foreach ($affected_bookings as $booking) { - $booking->deleteIfNoInterval(); - } - } - } - - return $deleted_c; - } - - - /** - * Deletes all reservations in the time ranges of this resource booking. - * - * @return int The amount of deleted reservations. - */ - public function deleteOverlappingReservations() - { - //Notify all persons who made reservations or who are assigned - //to the reservation about the new booking which overwrites - //their reservation: - $booking_resource = Resource::find($this->resource_id); - $booking_user = User::find($this->booking_user_id); - - $deleted_c = 0; - - $template_factory = new Flexi_TemplateFactory( - $GLOBALS['STUDIP_BASE_PATH'] . '/locale/' - ); - - $affected_reservations = ResourceBooking::findByResourceAndTimeRanges( - $booking_resource, - [ - [ - 'begin' => $this->real_begin, - 'end' => $this->end, - ] - ], - [self::TYPE_RESERVATION, self::TYPE_PLANNED], - [$this->id] - ); - foreach ($affected_reservations as $reservation) { - if ($reservation->id == $this->id) { - continue; - } - if ($reservation->assigned_user && ( - $reservation->range_id != $reservation->booking_user_id - )) { - //Inform the person who is assigned to the reservation: - setTempLanguage($reservation->assigned_user->id); - $lang_path = getUserLanguagePath($reservation->assigned_user->id); - - $template = $template_factory->open( - $lang_path . '/LC_MAILS/overbooked_reservation.php' - ); - $template->set_attribute('resource', $booking_resource); - $template->set_attribute('reservation', $reservation); - $template->set_attribute('booking_user', $booking_user); - $mail_text = $template->render(); - - Message::send( - User::findCurrent()->id, - [$reservation->assigned_user->username], - _('Reservierung überbucht'), - $mail_text - ); - - restoreLanguage(); - } - if ($reservation->booking_user) { - //Inform the person who made the reservation: - setTempLanguage($reservation->booking_user->id); - $lang_path = getUserLanguagePath($reservation->booking_user->id); - - $template = $template_factory->open( - $lang_path . '/LC_MAILS/overbooked_reservation.php' - ); - $template->set_attribute('resource', $booking_resource); - $template->set_attribute('reservation', $reservation); - $template->set_attribute('booking_user', $booking_user); - $mail_text = $template->render(); - - Message::send( - User::findCurrent()->id, - [$reservation->booking_user->username], - _('Reservierung überbucht'), - $mail_text - ); - - restoreLanguage(); - } - - //Delete the reservation: - $deleted_c += $reservation->delete(); - } - return $deleted_c; - } - - - /** - * Determines whether the resource booking ends on the same timestamp - * like the lecture time of one of the defined semesters. - * - * @return True, if the resource booking ends with a semester, - * false otherwise. - */ - public function endsWithSemester() - { - return Semester::countBySql( - '(beginn <= :begin) AND (ende >= :begin) - AND vorles_ende = :repeat_end', - [ - 'begin' => $this->begin, - 'repeat_end' => $this->repeat_end - ] - ) > 0; - } - - - /** - * Check if the specified user is the owner of the booking. - * - * @param User $user The user whose ownership shall be tested. - * - * @return bool True, if the specified user is the owner of the booking, - * false otherwise. - */ - public function userIsOwner(User $user) - { - return $this->booking_user_id == $user->id; - } - - - /** - * Determines wheter the booking is read only for a specified user. - * - * @param User $user The user whose permissions shall be checked. - * - * @return bool True, if the specified user may only perform reading - * actions on the booking, false otherwise. - */ - public function isReadOnlyForUser(User $user) - { - if ($this->isSimpleBooking()) { - //In case it is a simple booking, one has to be - //either resource tutor or the owner of the request. - if ($this->userIsOwner($user)) { - return false; - } - //Still no answer? Check, if the user is resource tutor. - $derived_resource = $this->resource->getDerivedClassInstance(); - return !$derived_resource->userHasPermission($user, 'tutor'); - } - //Non-simple bookings (course bookings etc.) are always read-only. - return true; - } - - - /** - * Determines whether this resource booking has a repetition in the - * specified time range. - * @param DateTime $begin - * @param DateTime $end - * @return bool True, if the booking has repetitions in the timeframe - * specified by $begin and $end, false otherwise. - */ - public function isRepetitionInTimeframe(DateTime $begin, DateTime $end) - { - return ResourceBookingInterval::countBySql( - 'booking_id = :booking_id AND begin < :end AND end > :begin', - [ - 'booking_id' => $this->id, - 'begin' => $begin->getTimestamp(), - 'end' => $end->getTimestamp() - ] - ) > 0; - } - - - /** - * Returns the DateInterval object according to the set repetition - * interval of this resource booking object. - * - * @return DateInterval|null A DateInterval object or null, - * if this booking has no repetition interval. - */ - public function getRepetitionInterval() - { - $repetition_interval = null; - if ($this->repetition_interval) { - try { - if ($this->repetition_interval instanceof DateInterval) { - $repetition_interval = $this->repetition_interval; - } else { - $repetition_interval = new DateInterval($this->repetition_interval); - } - } catch (Exception $e) { - //Invalid repetition interval string. - throw new InvalidArgumentException( - sprintf( - _('Das Wiederholungsintervall ist in einem ungültigen Format (%s)!'), - $this->repetition_interval - ) - ); - } - } - return $repetition_interval; - } - - - public function getRepeatModeString() - { - $interval = $this->getRepetitionInterval(); - - if ($interval->m) { - switch ($interval->m) { - case 1: return _('jeden Monat'); - case 2: return _('jeden zweiten Monat'); - case 3: return _('jeden dritten Monat'); - case 4: return _('jeden vierten Monat'); - case 5: return _('jeden fünften Monat'); - case 6: return _('jeden sechsten Monat'); - case 7: return _('jeden siebten Monat'); - case 8: return _('jeden achten Monat'); - case 9: return _('jeden neunten Monat'); - case 10: return _('jeden zehnten Monat'); - case 11: return _('jeden elften Monat'); - } - } elseif (($interval->d % 7) == 0) { - $week = round($interval->d / 7); - switch ($week) { - case 1: return _('jede Woche'); - case 2: return _('jede zweite Woche'); - case 3: return _('jede dritte Woche'); - } - } elseif ($interval->d) { - if ($interval->d > 1) { - return sprintf( - _('jeden %d. Tag'), - $interval->d - ); - } elseif ($interval->d == 1) { - return _('jeden Tag'); - } else { - return _('ungültiges Zeitintervall'); - } - } - } - - /** - * Determines if the resource booking overlaps with another - * resource booking. - * - * @return bool True, if there are other bookings which overlap - * with this one, false otherwise. - */ - public function hasOverlappingBookings() - { - //Get all intervals of this booking, loop over them - //and check if there are other intervals in the same - //timeframe for the same resource. - - $intervals = ResourceBookingInterval::findBySql( - 'booking_id = :booking_id', - [ - 'booking_id' => $this->id - ] - ); - - if ($intervals) { - foreach ($intervals as $interval) { - $count = ResourceBookingInterval::countBySql( - 'booking_id <> :booking_id - AND - resource_id = :resource_id - AND takes_place = 1 - AND - ( - begin < :end AND :begin < end - )', - [ - 'booking_id' => $this->id, - 'resource_id' => $this->resource_id, - 'begin' => $interval->begin, - 'end' => $interval->end - ] - ); - - if ($count) { - //We have found an interval which overlaps - //with an interval of this booking. - return true; - } - } - } - - //If this booking has no intervals it means it doesn't have - //any time intervals at all. Therefore there can't be any - //overlapping bookings. - return false; - } - - /** - * Gets the bookings that overlap with this booking. - * - * @return array Array of ResourceBooking objects - */ - public function getOverlappingBookings() - { - //Get all intervals of this booking, loop over them - //and check if there are other intervals in the same - //timeframe for the same resource. If so, collect the - //booking-IDs and get all the bookings. - - $intervals = ResourceBookingInterval::findBybooking_id($this->id); - - if ($intervals) { - $db = DBManager::get(); - - $get_booking_id_stmt = $db->prepare( - 'SELECT DISTINCT booking_id - FROM resource_booking_intervals - WHERE - booking_id <> :booking_id - AND - resource_id = :resource_id - AND takes_place = 1 - AND - ( - begin < :end AND :begin < end - )' - ); - $overlapping_booking_ids = []; - foreach ($intervals as $interval) { - $get_booking_id_stmt->execute( - [ - 'booking_id' => $this->id, - 'resource_id' => $this->resource_id, - 'begin' => $interval->begin, - 'end' => $interval->end - ] - ); - - $overlapping_booking_ids = array_merge( - $overlapping_booking_ids, - $get_booking_id_stmt->fetchAll( - PDO::FETCH_COLUMN, - 0 - ) - ); - } - - if ($overlapping_booking_ids) { - $overlapping_booking_ids = array_unique( - $overlapping_booking_ids - ); - - return ResourceBooking::findMany( - $overlapping_booking_ids - ); - } - } - - //If this booking has no intervals it means it doesn't have - //any time intervals at all. Therefore there can't be any - //overlapping bookings. - return false; - } - - - /** - * Calculates all time intervals as begin and end timestamps - * by looking at the repetition settings, if any. - * - * @param bool $as_datetime Whether to return the timestamps - * as DateTime objects (true) or not (false). Defaults to false. - * - * @return array A two-dimensional array with each time interval - * for this booking. The array has the following structure: - * [ - * [ - * 'begin' => Begin timestamp. - * 'end' => End timestamp. - * ] - * ] - */ - public function calculateTimeIntervals($as_datetime = false) - { - $interval_data = []; - $booking_begin = new DateTime(); - $booking_begin->setTimestamp($this->begin); - if ($this->preparation_time) { - $booking_begin->setTimestamp( - $this->begin - $this->preparation_time - ); - } - $booking_end = new DateTime(); - $booking_end->setTimestamp($this->end); - - //use begin and end to create the first interval: - $interval_data[] = [ - 'begin' => ( - $as_datetime - ? clone $booking_begin - : $booking_begin->getTimestamp() - ), - 'end' => ( - $as_datetime - ? clone $booking_end - : $booking_end->getTimestamp() - ) - ]; - - if ($this->repeat_end) { - //Repetition: we must check which repetition interval has been - //selected and then create entries for each repetition. - //Repetition starts with the begin date and ends with the - //"repeat_end" date. - $repetition_end = new DateTime(); - $repetition_end->setTimestamp($this->repeat_end); - //The DateInterval constructor will throw an exception, - //if it cannot parse the string stored in $this->repetition_interval. - $repetition_interval = $this->getRepetitionInterval(); - - if ($repetition_interval instanceof DateInterval) { - $duration = $booking_begin->diff($booking_end); - - //Check if end is later than begin to avoid - //infinite loops. - if ($repetition_end > $booking_begin) { - $current_begin = clone $booking_begin; - $current_begin->add($repetition_interval); - while ($current_begin < $repetition_end) { - $current_end = clone $current_begin; - $current_end->add($duration); - $interval_data[] = [ - 'begin' => ( - $as_datetime - ? clone $current_begin - : $current_begin->getTimestamp() - ), - 'end' => ( - $as_datetime - ? clone $current_end - : $current_end->getTimestamp() - ) - ]; - $current_begin->add($repetition_interval); - } - } else { - //end timestamp is before begin timestamp: - //return an empty array - return []; - } - } - //Everything went fine. - } - return $interval_data; - } - - - /** - * Retrieves all time intervals for this resource booking. - * - * @return ResourceBookingInterval[] An array of - * ResourceBookingInterval objects. - */ - public function getTimeIntervals($with_exceptions = true) - { - if ($with_exceptions) { - return ResourceBookingInterval::findBySQL( - "booking_id = :booking_id - ORDER BY begin ASC, end ASC", - [ - 'booking_id' => $this->id - ] - ); - } else { - return ResourceBookingInterval::findBySQL( - "booking_id = :booking_id AND takes_place = '1' - ORDER BY begin ASC, end ASC", - [ - 'booking_id' => $this->id - ] - ); - } - } - - - /** - * Retrieves all time intervals for this resource booking - * in a specified time range. - * - * @param DateTime $begin The begin of the time range. - * @param DateTime $end The end of the time range. - * - * @return ResourceBookingInterval[] An array of - * ResourceBookingInterval objects. - */ - public function getTimeIntervalsInTimeRange(DateTime $begin, DateTime $end) - { - if ($begin > $end) { - //We don't serve querys with invalid time ranges here. - return []; - } - - return ResourceBookingInterval::findBySql( - 'booking_id = :booking_id AND begin < :end AND end > :begin ORDER BY begin, end', - [ - 'booking_id' => $this->id, - 'begin' => $begin->getTimestamp(), - 'end' => $end->getTimestamp() - ] - ); - } - - - /** - * Returns all allocating users: Users who are associated with this booking - * through a request or a course date for which this booking has been made - * (all allocating users). If no user could be determined but the booking - * is bound to a course, the course name is returned instead. - * - * @deprecated - * @param bool $only_names Whether only the names of these users shall be - * returned (true) or user objects shall be returned (false). - * - * @return string[]|User[] Depending on the value of the $only_names - * parameter a string array or an user object array is returned. - * In case no user can be found, the array is empty. - * In case the parameter $only_names is set to true, the result - * may also contain a course name, if no users can be found but the - * booking is bound to a course. - */ - public function getAssignedUsers($only_names = true) - { - //The only two cases which shall be handled are that - //a booking is assigned to a course or an user. - if ($this->getAssignedUserType() === 'course') { - //Return all persons associated with that course date. - if (count($this->assigned_course_date->dozenten)) { - if ($only_names) { - $lecturers = []; - foreach ($this->assigned_course_date->dozenten as $lecturer) { - $lecturers[] = $lecturer->getFullName(); - } - return $lecturers; - } else { - return $this->assigned_course_date->dozenten; - } - } else { - $lecturers = User::findBySql( - "INNER JOIN seminar_user - USING (user_id) - WHERE - seminar_user.seminar_id = :course_id - AND - seminar_user.status = 'dozent' - ORDER BY nachname ASC, vorname ASC", - [ - 'course_id' => $this->assigned_course_date->range_id - ] - ); - if (!$lecturers) { - return []; - } - if ($only_names) { - $names = []; - foreach ($lecturers as $lecturer) { - $names[] = $lecturer->getFullName(); - } - return $names; - } else { - return $lecturers; - } - } - } elseif ($this->getAssignedUserType() === 'user') { - if ($only_names) { - return [$this->assigned_user->getFullName()]; - } - return [$this->assigned_user]; - } - - return []; - } - - public function getAssignedUser() - { - if ($this->getAssignedUserType() === 'course') { - return $this->assigned_course_date->course; - } - if ($this->getAssignedUserType() === 'user') { - return $this->assigned_user; - } - } - - public function getAssignedUserType() - { - if (isset($this->assigned_user_type)) { - return $this->assigned_user_type; - } - if ($this->assigned_course_date instanceof CourseDate) { - return $this->assigned_user_type = 'course'; - } - if ($this->assigned_user instanceof User) { - return $this->assigned_user_type = 'user'; - } - return $this->assigned_user_type = 'none'; - } - - public function getAssignedUserName() - { - $name = ''; - if ($this->getAssignedUserType() === 'course') { - $name = $this->assigned_course_date->course->getFullName(); - $name .= ' (' . implode(',', $this->assigned_course_date->course->getMembersWithStatus('dozent', true)->limit(3)->getValue('nachname')) . ')'; - } elseif ($this->getAssignedUserType() === 'user') { - if (get_visibility_by_id($this->assigned_user->id) || - ($this->assigned_user->id == $GLOBALS['user']->id) - ) { - $name = $this->assigned_user->getFullName(); - if ($this->description) { - $name .= " \n" . $this->description; - } - } else { - //Check if the current user has at least user permissions - //on the resource. In that case, even invisible assigned - //users become visible. - $resource = $this->resource->getDerivedClassInstance(); - $current_user = User::findCurrent(); - if (($resource instanceof Resource) && ($current_user instanceof User)) { - if ($resource->userHasPermission($current_user, 'user')) { - $name = $this->assigned_user->getFullName(); - if ($this->description) { - $name .= " \n" . $this->description; - } - } - } - } - } else { - $name = $this->description; - } - return $name; - } - - - /** - * Determines whether the booking is a simple booking - * that is not bound to course dates or similar Stud.IP objects. - */ - public function isSimpleBooking() - { - return !($this->getAssignedUserType() === 'course'); - } - - - /** - * Determins whether the booking has exceptions in repetitions or not. - * When the booking is bound to a course via a CourseDate instance, - * the exceptions are looked up using the corresponding metadate (if any). - * If a metadate exists to the course date and it has cancelled dates - * (metadate has CourseExDate instances assigned) then the booking has - * exceptions. - * If the booking is a simple booking, the exception status is determined - * by checking if the booking has ResourceBookingInterval instances - * that don't take place assigned to it. This is only checked, - * if a repetition interval is set for the booking so that such instances - * can exist. - * - * @return bool True, if the booking has exceptions in the repetition, - * false otherwise. - */ - public function hasExceptions() - { - if ($this->isSimpleBooking()) { - //This is a simple booking: Check the repetition interval: - if ($this->repetition_interval) { - //A repetition interval exists: Check if booking intervals - //exist that don't take place: - return ResourceBookingInterval::countBySql( - "booking_id = :booking_id - AND takes_place = '0'", - [ - 'booking_id' => $this->id - ] - ) > 0; - } else { - return false; - } - } else { - //Check if the course booking has exceptions (via metadate): - $metadate = $this->assigned_course_date->cycle; - if ($metadate instanceof SeminarCycleDate) { - //Check if the metadate has exceptions: - return CourseExDate::countBySql( - 'metadate_id = :metadate_id', - [ - 'metadate_id' => $metadate->id - ] - ) > 0; - } else { - //No metadate is assigned to the course booking. - return false; - } - } - } - - - public function __toString() - { - return date('d.m.Y H:i', $this->begin) - . ' - ' - . date('d.m.Y H:i', $this->end); - } - - - public function getTimeIntervalStrings() - { - $time_intervals = ResourceBookingInterval::findBySQL( - 'booking_id = :booking_id ORDER BY begin ASC, end ASC', - [ - 'booking_id' => $this->id - ] - ); - - $strings = []; - foreach ($time_intervals as $interval) { - if (date('Ymd', $interval->begin) == date('Ymd', $interval->end)) { - $strings[] = sprintf( - '%1$s %2$s - %3$s', - date('d.m.Y', $interval->begin), - date('H:i', $interval->begin), - date('H:i', $interval->end) - ); - } else { - $strings[] = sprintf( - '%1$s - %2$s', - date('d.m.Y H:i', $interval->begin), - date('d.m.Y H:i', $interval->end) - ); - } - } - return $strings; - } - - - /** - * @inheritDoc - */ - public static function exportUserData(StoredUserData $storage) - { - $user = User::find($storage->user_id); - $bookings = self::findBySql( - 'user_id = :user_id ORDER BY mkdate', - [ - 'user_id' => $storage->user_id - ] - ); - - $rows = []; - foreach ($bookings as $booking) { - $rows[] = $booking->toRawArray(); - } - $storage->addTabularData( - _('Buchungen von Ressourcen'), - 'resource_bookings', - $rows, - $user - ); - } - - - public function convertToEventData(array $time_intervals, $user) - { - $booking_plan_booking_bg = - \ColourValue::find('Resources.BookingPlan.Booking.Bg'); - $booking_plan_booking_fg = - \ColourValue::find('Resources.BookingPlan.Booking.Fg'); - $booking_plan_simple_booking_with_exceptions_bg = - ColourValue::find('Resources.BookingPlan.SimpleBookingWithExceptions.Bg'); - $booking_plan_simple_booking_with_exceptions_fg = - ColourValue::find('Resources.BookingPlan.SimpleBookingWithExceptions.Fg'); - $booking_plan_reservation_bg = \ColourValue::find('Resources.BookingPlan.Reservation.Bg'); - $booking_plan_reservation_fg = \ColourValue::find('Resources.BookingPlan.Reservation.Fg'); - $booking_plan_lock_bg = \ColourValue::find('Resources.BookingPlan.Lock.Bg'); - $booking_plan_lock_fg = \ColourValue::find('Resources.BookingPlan.Lock.Fg'); - $booking_plan_planned_booking_bg = \ColourValue::find('Resources.BookingPlan.PlannedBooking.Bg'); - $booking_plan_planned_booking_fg = \ColourValue::find('Resources.BookingPlan.PlannedBooking.Fg'); - $booking_plan_preparation_bg = \ColourValue::find('Resources.BookingPlan.PreparationTime.Bg'); - $booking_plan_preparation_fg = \ColourValue::find('Resources.BookingPlan.PreparationTime.Fg'); - $booking_plan_course_booking_bg = \ColourValue::find('Resources.BookingPlan.CourseBooking.Bg'); - $booking_plan_course_booking_fg = \ColourValue::find('Resources.BookingPlan.CourseBooking.Fg'); - - $colour = $booking_plan_booking_bg->__toString(); - $text_colour = $booking_plan_booking_fg->__toString(); - $event_classes = []; - - if ($this->booking_type == self::TYPE_NORMAL) { - $event_classes[] = 'resource-booking'; - //Check if the booking is a course booking: - if ($this->getAssignedUserType() === 'course') { - //It is a course date. - $event_classes[] = 'for-course'; - $colour = $booking_plan_course_booking_bg->__toString(); - $text_colour = $booking_plan_course_booking_fg->__toString(); - } - } elseif ($this->booking_type == self::TYPE_RESERVATION) { - $event_classes[] = 'resource-reservation'; - $colour = $booking_plan_reservation_bg->__toString(); - $text_colour = $booking_plan_reservation_fg->__toString(); - } elseif ($this->booking_type == self::TYPE_LOCK) { - $event_classes[] = 'resource-lock'; - $colour = $booking_plan_lock_bg->__toString(); - $text_colour = $booking_plan_lock_fg->__toString(); - } elseif ($this->booking_type == self::TYPE_PLANNED) { - $event_classes[] = 'resource-planned-booking'; - $colour = $booking_plan_planned_booking_bg->__toString(); - $text_colour = $booking_plan_planned_booking_fg->__toString(); - } - - $booking_is_editable = false; - if ($user instanceof User) { - $booking_is_editable = !$this->isReadOnlyForUser($user); - } - - $booking_api_urls = []; - $booking_view_urls = [ - 'show' => \URLHelper::getURL( - 'dispatch.php/resources/booking/index/' - . $this->id - ), - ]; - if ($booking_is_editable) { - $booking_view_urls['edit'] = \URLHelper::getURL( - 'dispatch.php/resources/booking/edit/' - . $this->id - ); - } - $events = []; - - foreach ($time_intervals as $interval) { - $real_begin = $interval['begin']; - if ($this->preparation_time) { - $real_begin += $this->preparation_time; - $begin = new DateTime(); - $begin->setTimestamp($interval['begin']); - $end = new DateTime(); - $end->setTimestamp($real_begin); - $events[] = new Studip\Calendar\EventData( - $begin, - $end, - _('Rüstzeit'), - ['preparation-time'], - $booking_plan_preparation_fg->__toString(), - $booking_plan_preparation_bg->__toString(), - $booking_is_editable, - 'ResourceBookingInterval', - $interval->id, - 'ResourceBooking', - $this->id, - 'Resource', - $this->resource_id, - $booking_view_urls - ); - } - - $event_title = ''; - $icon = ''; - - if ($user instanceof User) { - $derived_resource = $this->resource->getDerivedClassInstance(); - if ($derived_resource->userHasPermission($user, 'user') && $this->internal_comment) { - $icon = 'chat2'; - } - } - - if (!$this->isSimpleBooking()) { - if ($this->assigned_course_date->metadate_id) { - $icon = 'refresh'; - } - } elseif ($this->repeat_end > $this->end) { - $icon = 'refresh'; - } - - if ($this->assigned_course_date instanceof CourseDate) { - $course = $this->assigned_course_date->course; - if ($course instanceof Course) { - $has_perms = $GLOBALS['perm']->have_studip_perm('user', $course->id, $user->id); - $vis_perms = $GLOBALS['perm']->have_perm(Config::get()->SEM_VISIBILITY_PERM, $user->id); - if ($has_perms || $vis_perms || $course->visible) { - $event_title = $this->getAssignedUserName(); - } - } - } else { - $event_title = $this->getAssignedUserName(); - } - - $interval_api_urls = $booking_api_urls; - if ($booking_is_editable) { - //A note on the move URL: - //When used in a room group booking plan, the interval_id - //URL parameter is subject to modification in JavaScript. - //(lib/resources.js, method dropEventInRoomGroupBookingPlan) - $interval_api_urls = [ - 'resize' => \URLHelper::getURL( - 'api.php/resources/booking/' - . $this->id . '/move', - [ - 'quiet' => '1', - 'interval_id' => $interval->id - ] - ), - 'move' => \URLHelper::getURL( - 'api.php/resources/booking/' - . $this->id . '/move', - [ - 'quiet' => '1', - 'interval_id' => $interval->id - ] - ) - ]; - } - $begin = new DateTime(); - $begin->setTimestamp($real_begin); - $end = new DateTime(); - $end->setTimestamp($interval['end']); - $events[] = new Studip\Calendar\EventData( - $begin, - $end, - $event_title, - $event_classes, - $text_colour, - $colour, - $booking_is_editable, - 'ResourceBookingInterval', - $interval->id, - 'ResourceBooking', - $this->id, - 'Resource', - $this->resource_id, - $booking_view_urls, - $interval_api_urls, - $icon - ); - } - - return $events; - } - - - public function getAllEventData() - { - return $this->convertToEventData( - $this->getTimeIntervals(false), - User::findCurrent() - ); - } - - - public function getEventDataForTimeRange(DateTime $begin, DateTime $end) - { - - return $this->getFilteredEventData(null, null, null, $begin, $end); - } - - - public function getFilteredEventData( - $user_id = null, - $range_id = null, - $range_type = null, - $begin = null, - $end = null - ) - { - $sql = "booking_id = :booking_id AND takes_place = 1 "; - $sql_array = [ - 'booking_id' => $this->id - ]; - - if ($begin && $end) { - if ($begin instanceof DateTime) { - $begin = $begin->getTimestamp(); - } - if ($end instanceof DateTime) { - $end = $end->getTimestamp(); - } - $sql .= "AND begin < :end AND :begin < end"; - $sql_array['begin'] = $begin; - $sql_array['end'] = $end; - } - $time_intervals = ResourceBookingInterval::findBySQL( - $sql, - $sql_array - ); - - $user = null; - if ($user_id) { - $user = User::find($user_id); - } else { - $user = User::findCurrent(); - } - - return $this->convertToEventData($time_intervals, $user); - } - - /** - * @return string - */ - public function getRepetitionType() - { - if ($this->getAssignedUserType() === 'course') { - if ($this->assigned_course_date->metadate_id) { - return 'weekly'; - } else { - return 'single'; - } - } else { - $intervall = $this->getRepetitionInterval(); - if (!$intervall) { - return 'single'; - } - if ($intervall->m) { - return 'monthly'; - } - if ($intervall->d % 7 === 0) { - return 'weekly'; - } - if ($intervall->d) { - return 'daily'; - } - } - - return ''; - } - - - /** - * Sends a notification that the booking has been deleted - * to the user that created the booking. - */ - public function sendDeleteNotification() - { - if ($this->booking_type != self::TYPE_NORMAL) { - //We only handle real bookings in this method. - return; - } - - if ($this->end < time()) { - //Bookings that lie in the past can be deleted without - //sending notifications. - return; - } - - $booking_resource = Resource::find($this->resource_id); - $booking_user = User::find($this->booking_user_id); - $booking_course = null; - if ($this->course_id) { - $booking_course = Course::find($this->course_id); - } - if (!$booking_resource || !$booking_user) { - //Nothing we can do here. - return; - } - if (User::findCurrent()->id === $booking_user->id) { - return; - } - - $template_factory = new Flexi_TemplateFactory( - $GLOBALS['STUDIP_BASE_PATH'] . '/locale/' - ); - setTempLanguage($booking_user->id); - $lang_path = getUserLanguagePath($booking_user->id); - - $template = $template_factory->open( - $lang_path . '/LC_MAILS/delete_booking_notification.php' - ); - $template->set_attribute('resource', $booking_resource->getDerivedClassInstance()); - $template->set_attribute('begin', $this->begin); - $template->set_attribute('end', $this->end); - $template->set_attribute('deleting_user', User::findCurrent()); - $template->set_attribute('booking_course', $booking_course); - - $mail_text = $template->render(); - - Message::send( - User::findCurrent()->id, - [$booking_user->username], - _('Ihre Buchung wurde gelöscht'), - $mail_text - ); - - restoreLanguage(); - } -} |
