aboutsummaryrefslogtreecommitdiff
path: root/lib/models/resources/ResourceRequest.class.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/models/resources/ResourceRequest.class.php')
-rw-r--r--lib/models/resources/ResourceRequest.class.php2478
1 files changed, 0 insertions, 2478 deletions
diff --git a/lib/models/resources/ResourceRequest.class.php b/lib/models/resources/ResourceRequest.class.php
deleted file mode 100644
index 6b22b44..0000000
--- a/lib/models/resources/ResourceRequest.class.php
+++ /dev/null
@@ -1,2478 +0,0 @@
-<?php
-
-/**
- * ResourceRequest.class.php - Contains a model class for resource requests.
- *
- * 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 attributes begin and end are only used in simple resource requests.
- * The "traditional" resource requests use either course_id, metadate_id
- * or termin_id to store the time ranges connected to the request.
- *
- * @property string $id database column
- * @property string $course_id database column
- * @property string $termin_id database column
- * @property string $metadate_id database column
- * @property string $user_id database column
- * @property string $last_modified_by database column
- * @property string $resource_id database column
- * @property string|null $category_id database column
- * @property string|null $comment database column
- * @property string|null $reply_comment database column
- * @property string $reply_recipients database column
- * @property int $closed database column
- * @property int|null $mkdate database column
- * @property int|null $chdate database column
- * @property int $begin database column
- * @property int $end database column
- * @property int $preparation_time database column
- * @property int $marked database column
- * @property SimpleORMapCollection|ResourceRequestProperty[] $properties has_many ResourceRequestProperty
- * @property SimpleORMapCollection|ResourceRequestAppointment[] $appointments has_many ResourceRequestAppointment
- * @property Resource $resource belongs_to Resource
- * @property ResourceCategory|null $category belongs_to ResourceCategory
- * @property User $user belongs_to User
- * @property User $last_modifier belongs_to User
- * @property Course $course belongs_to Course
- * @property SeminarCycleDate $cycle belongs_to SeminarCycleDate
- * @property CourseDate $date belongs_to CourseDate
- */
-class ResourceRequest extends SimpleORMap implements PrivacyObject, Studip\Calendar\EventSource
-{
- const MARK_NONE = 0;
- const MARK_RED = 1;
- const MARK_YELLOW = 2;
- const MARK_GREEN = 3;
-
- const REPLY_REQUESTER = 'requester';
- const REPLY_LECTURER = 'lecturer';
-
- const STATE_OPEN = 0; // room-request is open
- const STATE_PENDING = 1; // room-request has been processed, but no confirmation has been sent
- const STATE_CLOSED = 2; // room-request has been processed and a confirmation has been sent
- const STATE_DECLINED = 3; // room-request has been declined
-
- /**
- * The amount of defined marking states.
- */
- const MARKING_STATES = 4;
-
- protected static function configure($config = [])
- {
- $config['db_table'] = 'resource_requests';
-
- $config['belongs_to']['resource'] = [
- 'class_name' => Resource::class,
- 'foreign_key' => 'resource_id',
- 'assoc_func' => 'find'
- ];
-
- $config['belongs_to']['category'] = [
- 'class_name' => ResourceCategory::class,
- 'foreign_key' => 'category_id',
- 'assoc_func' => 'find'
- ];
-
- $config['belongs_to']['user'] = [
- 'class_name' => User::class,
- 'foreign_key' => 'user_id',
- 'assoc_func' => 'find'
- ];
-
- $config['belongs_to']['last_modifier'] = [
- 'class_name' => User::class,
- 'foreign_key' => 'last_modified_by',
- 'assoc_func' => 'find'
- ];
-
- $config['belongs_to']['course'] = [
- 'class_name' => Course::class,
- 'foreign_key' => 'course_id',
- 'assoc_func' => 'find'
- ];
-
- $config['belongs_to']['cycle'] = [
- 'class_name' => SeminarCycleDate::class,
- 'foreign_key' => 'metadate_id'
- ];
-
- $config['belongs_to']['date'] = [
- 'class_name' => CourseDate::class,
- 'foreign_key' => 'termin_id'
- ];
-
- $config['has_many']['properties'] = [
- 'class_name' => ResourceRequestProperty::class,
- 'foreign_key' => 'id',
- 'assoc_foreign_key' => 'request_id',
- 'on_store' => 'store',
- 'on_delete' => 'delete'
- ];
-
- $config['has_many']['appointments'] = [
- 'class_name' => ResourceRequestAppointment::class,
- 'foreign_key' => 'id',
- 'assoc_foreign_key' => 'request_id',
- 'on_store' => 'store',
- 'on_delete' => 'delete'
- ];
-
- //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';
- }
- $config['registered_callbacks']['after_create'][] = 'cbLogNewRequest';
- $config['registered_callbacks']['after_store'][] = 'cbAfterStore';
- $config['registered_callbacks']['after_delete'][] = 'cbAfterDelete';
-
-
- parent::configure($config);
- }
-
- /**
- * @inheritDoc
- */
- public static function exportUserdata(StoredUserData $storage)
- {
- $user = User::find($storage->user_id);
-
- $requests = self::findBySql(
- 'user_id = :user_id ORDER BY mkdate',
- [
- 'user_id' => $storage->user_id
- ]
- );
-
- $request_rows = [];
- foreach ($requests as $request) {
- $request_rows[] = $request->toRawArray();
- }
- $storage->addTabularData(
- _('Ressourcenanfragen'),
- 'resource_requests',
- $request_rows,
- $user
- );
- }
-
- /**
- * Retrieves all resource requests from the database.
- *
- * @return ResourceRequest[] An array of ResourceRequests objects
- * or an empty array, if no resource requests are stored
- * in the database.
- */
- public static function findAll()
- {
- return self::findBySql('TRUE ORDER BY mkdate ASC');
- }
-
- /**
- * Retrieves all open resource requests from the database.
- *
- * @return ResourceRequest[] An array of ResourceRequests objects
- * or an empty array, if no open resource requests are stored
- * in the database.
- */
- public static function findOpen()
- {
- return self::findBySql(
- 'closed = ? ORDER BY mkdate ASC',
- [self::STATE_OPEN]
- );
- }
-
- /**
- * Internal method that generated the SQL query used in
- * findByResourceAndTimeRanges and countByResourceAndTimeRanges.
- *
- * @see findByResourceAndTimeRanges
- * @inheritDoc
- */
- protected static function buildResourceAndTimeRangesSqlQuery(
- Resource $resource,
- $time_ranges = [],
- $closed_status = null,
- $excluded_request_ids = [],
- $additional_conditions = '',
- $additional_parameters = []
- )
- {
- 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 $closed_status
- //variable is set to something different than null.
- $closed_status_sql = '';
- if ($closed_status !== null) {
- $closed_status_sql = ' AND (resource_requests.closed = :status) ';
- $sql_params['status'] = strval($closed_status);
- }
-
- //Then we build the snipped for excluded request IDs, if specified.
- $excluded_request_ids_sql = '';
- if (is_array($excluded_request_ids) && count($excluded_request_ids)) {
- $excluded_request_ids_sql = ' AND resource_requests.id NOT IN ( :excluded_ids ) ';
- $sql_params['excluded_ids'] = $excluded_request_ids;
- }
-
- //Now we build the SQL snippet for the time intervals.
- //These are repeated four times in the query below.
- //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 = '';
- if ($time_ranges) {
- $time_sql = 'AND (';
-
- $i = 1;
- foreach ($time_ranges as $time_range) {
- if ($i > 1) {
- $time_sql .= ' OR ';
- }
- $time_sql .= sprintf('BEGIN < :end%d AND END > :begin%d ', $i, $i);
-
- $sql_params[('begin' . $i)] = $time_range['begin'];
- $sql_params[('end' . $i)] = $time_range['end'];
-
- $i++;
- }
-
- $time_sql .= ') ';
- }
-
- //Check if the request has a start and end timestamp set or if it belongs
- //to a date, a metadate or a course.
- //This is done in the rest of the SQL query:
-
- // FIXME this subselect looks unnecessarily complex
- $whole_sql = '
- SELECT id FROM resource_requests
- WHERE
- resource_id = :resource_id
- '
- . str_replace(
- ['BEGIN', 'END'],
- ['(CAST(begin AS SIGNED) - preparation_time)', 'end'],
- $time_sql
- )
- . $closed_status_sql
- . '
- UNION
- SELECT id FROM resource_requests
- INNER JOIN termine USING (termin_id)
- WHERE
- resource_id = :resource_id
- '
- . str_replace(
- ['BEGIN', 'END'],
- [
- '(CAST(termine.date AS SIGNED) - resource_requests.preparation_time)',
- 'termine.end_time'
- ],
- $time_sql
- )
- . $closed_status_sql
- . '
- UNION
- SELECT id FROM resource_requests
- INNER JOIN termine USING (metadate_id)
- WHERE
- resource_id = :resource_id
- '
- . str_replace(
- ['BEGIN', 'END'],
- [
- '(CAST(termine.date AS SIGNED) - resource_requests.preparation_time)',
- 'termine.end_time'
- ],
- $time_sql
- )
- . $closed_status_sql
- . '
- UNION
- SELECT id FROM resource_requests
- INNER JOIN termine
- ON resource_requests.course_id = termine.range_id
- WHERE
- resource_id = :resource_id
- '
- . str_replace(
- ['BEGIN', 'END'],
- [
- '(CAST(termine.date AS SIGNED) - resource_requests.preparation_time)',
- 'termine.end_time'
- ],
- $time_sql
- )
- . $closed_status_sql
- . '
- GROUP BY id
- '
- . $excluded_request_ids_sql;
- $request_ids = DBManager::get()->fetchFirst($whole_sql, $sql_params);
- $whole_sql = "resource_requests.id IN(:request_ids)";
- $sql_params = ['request_ids' => $request_ids];
- if ($additional_conditions) {
- $whole_sql .= ' AND ' . $additional_conditions;
- if ($additional_parameters) {
- $sql_params = array_merge($sql_params, $additional_parameters);
- }
- }
- $whole_sql .= ' ORDER BY mkdate ASC';
-
- return [
- 'sql' => $whole_sql,
- 'params' => $sql_params
- ];
- }
-
- /**
- * Retrieves all resource requests for the given resource and
- * time range. By default, all requests are returned.
- * To get only open or closed requests set the $closed_status 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 mixed $closed_status An optional status for the closed column in the
- * database. By default this is set to null which means that
- * resource requests are not filtered by the status column field.
- * A value of 0 means only open requests are retrived.
- * A value of 1 means only closed requests are retrieved.
- *
- * @param array $excluded_request_ids An array of strings representing
- * resource request IDs. IDs specified in this array are excluded from
- * the search.
- * @return ResourceRequest[] 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 = [],
- $closed_status = null,
- $excluded_request_ids = [],
- $additional_conditions = '',
- $additional_parameters = []
- )
- {
- //Build the SQL query and the parameter array.
-
- $sql_data = self::buildResourceAndTimeRangesSqlQuery(
- $resource,
- $time_ranges,
- $closed_status,
- $excluded_request_ids,
- $additional_conditions,
- $additional_parameters
- );
-
- //Call findBySql:
- return self::findBySql($sql_data['sql'], $sql_data['params']);
- }
-
- public static function countByResourceAndTimeRanges(
- Resource $resource,
- $time_ranges = [],
- $closed_status = null,
- $excluded_request_ids = [],
- $additional_conditions = '',
- $additional_parameters = []
- )
- {
- $sql_data = self::buildResourceAndTimeRangesSqlQuery(
- $resource,
- $time_ranges,
- $closed_status,
- $excluded_request_ids,
- $additional_conditions,
- $additional_parameters
- );
-
- return self::countBySql($sql_data['sql'], $sql_data['params']);
- }
-
- public static function findByCourse($course_id)
- {
- return self::findOneBySql(
- "termin_id = '' AND metadate_id = '' AND course_id = :course_id",
- [
- 'course_id' => $course_id
- ]
- );
- }
-
- public static function findByDate($date_id)
- {
- return self::findOneBySql(
- 'termin_id = :date_id',
- [
- 'date_id' => $date_id
- ]
- );
- }
-
- public static function findByMetadate($metadate_id)
- {
- return self::findOneBySql(
- 'metadate_id = :metadate_id',
- [
- 'metadate_id' => $metadate_id
- ]
- );
- }
-
- public static function existsByCourse($course_id, $request_is_open = false)
- {
- $parameters = [':course_id' => $course_id];
-
- $sql = '';
- if ($request_is_open) {
- $sql .= "closed = :closed_state AND ";
- $parameters[':closed_state'] = self::STATE_OPEN;
- }
-
- $request = self::findOneBySql(
- $sql . "termin_id = '' AND metadate_id = '' AND course_id = :course_id",
- $parameters
- );
-
- if ($request) {
- return $request->id;
- } else {
- return false;
- }
- }
-
- public static function existsByDate($date_id, $request_is_open = false)
- {
- $parameters = [':date_id' => $date_id];
-
- $sql = '';
- if ($request_is_open) {
- $sql .= "closed = :closed_state AND ";
- $parameters[':closed_state'] = self::STATE_OPEN;
- }
-
- $request = self::findOneBySql(
- $sql . "termin_id = :date_id",
- $parameters
- );
-
- if ($request) {
- return $request->id;
- } else {
- return false;
- }
- }
-
- public static function existsByMetadate($metadate_id, $request_is_open = false)
- {
- $parameters = [':metadate_id' => $metadate_id];
-
- $sql = '';
- if ($request_is_open) {
- $sql .= "closed = :closed_state AND ";
- $parameters[':closed_state'] = self::STATE_OPEN;
- }
-
- $request = self::findOneBySql(
- $sql . "metadate_id = :metadate_id",
- $parameters
- );
-
- if ($request) {
- return $request->id;
- } else {
- return false;
- }
- }
-
- /**
- * A callback method that creates a Stud.IP log entry
- * when a new request has been made.
- */
- public function cbLogNewRequest()
- {
- $this->sendNewRequestMail();
- StudipLog::log('RES_REQUEST_NEW', $this->course_id, $this->resource_id, $this->getLoggingInfoText());
- }
-
- /**
- * A callback method that send a mail
- * when a new request has been udpated.
- */
- public function cbAfterStore()
- {
- if ($this->isFieldDirty('closed')) {
- if ($this->closed == self::STATE_DECLINED) {
- $this->sendRequestDeniedMail();
- StudipLog::log('RES_REQUEST_DENY', $this->course_id, $this->resource_id, $this->getLoggingInfoText());
- } elseif ($this->closed == self::STATE_PENDING || $this->closed == self::STATE_CLOSED) {
- StudipLog::log('RES_REQUEST_RESOLVE', $this->course_id, $this->resource_id, $this->getLoggingInfoText());
- }
- } else {
- StudipLog::log('RES_REQUEST_UPDATE', $this->course_id, $this->resource_id, $this->getLoggingInfoText());
- }
- }
-
- public function cbAfterDelete()
- {
- StudipLog::log('RES_REQUEST_DEL', $this->course_id, $this->resource_id, $this->getLoggingInfoText());
- }
-
- /**
- * This validation method is called before storing an object.
- */
- public function validate()
- {
- if (!$this->resource_id && !$this->category_id) {
- throw new Exception(
- _('Eine Anfrage muss einer konkreten Ressource oder deren Kategorie zugewiesen sein!')
- );
- }
- }
-
- public function getDerivedClassInstance()
- {
- if (!$this->resource) {
- //We cannot determine a derived class.
- return $this;
- }
- $class_name = $this->resource->class_name;
-
- if ($class_name === 'Resource') {
- //This is already the correct class.
- return $this;
- }
-
- if (is_subclass_of($class_name, 'Resource')) {
- //Now we append 'Request' to the class name:
- $class_name = $class_name . 'Request';
- return $class_name::buildExisting(
- $this->toRawArray()
- );
- } else {
- //$class_name does not contain the name of a subclass
- //of Resource. That's an error!
- throw new NoResourceClassException(
- sprintf(
- _('Die Klasse %1$s ist keine Spezialisierung der Ressourcen-Kernklasse!'),
- $class_name
- )
- );
- }
- }
-
- /**
- * Sets the range fields (termin_id, metadate_id, course_id)
- * or the ResourceRequestAppointment objects related to this request
- * according to the range type and its range-IDs specified as parameters
- * for this method. The ResourceRequest object is not stored after
- * setting the fields / related objects.
- *
- * @param string $range_type The range type for this request. One of
- * the following: 'date', 'cycle', 'course' or 'date-multiple'.
- *
- * @param array $range_ids An array of range-IDs to be set for the
- * specified range type. This is mostly an array of size one
- * since the fields termin_id, metadate_id and course_id only
- * accept one ID. The range type 'date-multiple' accepts multiple
- * IDs.
- *
- * @return void No return value.
- */
- public function setRangeFields($range_type = '', $range_ids = [])
- {
- if ($range_type === 'date') {
- $this->termin_id = $range_ids[0];
- $this->metadate_id = '';
- } elseif ($range_type === 'cycle') {
- $this->termin_id = '';
- $this->metadate_id = $range_ids[0];
- } elseif ($range_type === 'date-multiple') {
- $this->termin_id = '';
- $this->metadate_id = '';
- $appointments = [];
- foreach ($range_ids as $range_id) {
- $app = new ResourceRequestAppointment();
- $app->appointment_id = $range_id;
- $appointments[] = $app;
- }
- $this->appointments = $appointments;
- } elseif ($range_type === 'course') {
- $this->termin_id = '';
- $this->metadate_id = '';
- $this->course_id = $range_ids[0];
- }
- }
-
- /**
- * Closes the requests and sends out notification mails.
- * If the request is closed and a resource has been booked,
- * it can be passed as parameter to be included in the notification mails.
- *
- * @param bool $notify_lecturers Whether to notify lecturers of a course
- * (true) or not (false). Defaults to false. Note that this parameter
- * is only useful in case the request is bound to a course, either
- * directly or via a course date or a course cycle date.
- *
- * @param ResourceBooking $bookings The resource bookings that have been
- * created from this request.
- * @return bool @TODO
- */
- public function closeRequest($notify_lecturers = false, $bookings = [])
- {
- if (
- $this->closed == self::STATE_CLOSED
- || $this->closed == self::STATE_DECLINED
- ) {
- //The request has already been closed.
- return true;
- }
-
- $this->closed = self::STATE_PENDING;
- if ($this->isDirty()) {
- $this->store();
- }
-
- //Now we send the confirmation mail to the requester:
- $this->sendCloseRequestMailToRequester($bookings);
-
- if ($notify_lecturers) {
- $this->sendCloseRequestMailToLecturers($bookings);
- }
-
- //Sending successful: The request is closed.
- $this->closed = self::STATE_CLOSED;
- if ($this->isDirty()) {
- return $this->store();
- }
- return true;
- }
-
- /**
- * Returns the resource requests whose time ranges overlap
- * with those of this resource request.
- *
- * @return ResourceRequest[] An array of ResourceRequest objects.
- */
- public function getOverlappingRequests()
- {
- if ($this->resource) {
- return self::findByResourceAndTimeRanges(
- $this->resource,
- $this->getTimeIntervals(true),
- self::STATE_OPEN,
- [$this->id]
- );
- }
- return [];
- }
-
- /**
- * Counts the resource requests whose time ranges overlap
- * with those of this resource request.
- *
- * @return int The amount of overlapping resource requests.
- */
- public function countOverlappingRequests()
- {
- if ($this->resource) {
- return self::countByResourceAndTimeRanges(
- $this->resource,
- $this->getTimeIntervals(true),
- self::STATE_OPEN,
- [$this->id]
- );
- }
- return 0;
- }
-
- /**
- * Returns the resource bookings whose time ranges overlap
- * with those of this resource request.
- *
- * @return ResourceBooking[] An array of ResourceBooking objects.
- */
- public function getOverlappingBookings()
- {
- if ($this->resource) {
- return ResourceBooking::findByResourceAndTimeRanges(
- $this->resource,
- $this->getTimeIntervals(true),
- [ResourceBooking::TYPE_NORMAL, ResourceBooking::TYPE_LOCK]
- );
- }
- return [];
- }
-
- /**
- * Counts the resource bookings whose time ranges overlap
- * with those of this resource request.
- *
- * @return int The amount of overlapping resource bookings.
- */
- public function countOverlappingBookings()
- {
- if ($this->resource) {
- return ResourceBooking::countByResourceAndTimeRanges(
- $this->resource,
- $this->getTimeIntervals(true),
- [ResourceBooking::TYPE_NORMAL, ResourceBooking::TYPE_LOCK]
- );
- }
- return 0;
- }
-
- /**
- * Returns the repetion interval if regular appointments are used
- * for this request.
- *
- * @return DateInterval|null In case regular appointments are used
- * for this request a DateInterval is returned.
- * Otherwise null is returned.
- */
- public function getRepetitionInterval()
- {
- if ($this->metadate_id) {
- //It is a set of regular appointments.
- //We just have to compute the time difference between the first
- //two appointments to get the interval.
-
- $first_date = $this->cycle->dates[0];
- $second_date = $this->cycle->dates[1];
-
- if (!$first_date || !$second_date) {
- //Either only one date is in the set of regular appointments
- //or there is a database error. We cannot continue.
- return null;
- }
-
- $first_datetime = new DateTime();
- $first_datetime->setTimestamp($first_date->date);
- $second_datetime = new DateTime();
- $second_datetime->setTimestamp($second_date->date);
-
- return $first_datetime->diff($second_datetime);
- }
-
- return null;
- }
-
- public function getStartDate()
- {
- $start_date = new DateTime();
- if (count($this->appointments) > 0) {
- $start_date->setTimestamp($this->appointments->first()->appointment->date);
- return $start_date;
- }
-
- if ($this->termin_id) {
- $start_date->setTimestamp($this->date->date);
- return $start_date;
- }
-
- if (isset($this->cycle) && count($this->cycle->dates) > 0) {
- $first_date = $this->cycle->dates->first();
- if ($this->metadate_id && isset($first_date->date)) {
- $start_date->setTimestamp($first_date->date);
- return $start_date;
- }
-
- if ($this->course_id && isset($first_date->date)) {
- $start_date->setTimestamp($first_date->date);
- return $start_date;
- }
- }
-
- if ($this->begin) {
- $start_date->setTimestamp($this->begin);
- return $start_date;
- }
-
- return null;
- }
-
- public function getEndDate()
- {
- $end_date = new DateTime();
- if (count($this->appointments) > 0) {
- $end_date->setTimestamp($this->appointments->last()->appointment->end_time);
- return $end_date;
- }
-
- if ($this->termin_id) {
- $end_date->setTimestamp($this->date->end_time);
- return $end_date;
- }
-
- if ($this->metadate_id) {
- $date = $this->cycle->dates->last();
- if (!isset($date)) {
- return null;
- }
-
- $end_date->setTimestamp($this->cycle->dates->last()->end_time);
- return $end_date;
- }
-
- if ($this->course_id) {
- $date = $this->course->dates->last();
- if (!isset($date)) {
- return null;
- }
-
- $end_date->setTimestamp($this->course->dates->last()->end_time);
- return $end_date;
- }
-
- if ($this->end) {
- $end_date->setTimestamp($this->end);
- return $end_date;
- }
-
- return null;
- }
-
- public function getStartSemester()
- {
- $start_date = $this->getStartDate();
- if ($start_date instanceof DateTime) {
- return Semester::findByTimestamp($start_date->getTimestamp());
- }
- return null;
- }
-
- public function getEndSemester()
- {
- $end_date = $this->getEndDate();
- if ($end_date instanceof DateTime) {
- return Semester::findByTimestamp($end_date->getTimestamp());
- }
- return null;
- }
-
- public function getRepetitionEndDate()
- {
- $repetition_interval = $this->getRepetitionInterval();
-
- if (!$repetition_interval) {
- //There is no repetition.
- return null;
- }
-
- return $this->getEndDate();
- }
-
- /**
- * Retrieves the time intervals by looking at metadate objects
- * and other time interval sources and returns them grouped by metadate.
- * @param bool $with_preparation_time @TODO
- * @return mixed[][][] A three-dimensional array with
- * the following structure:
- * - The first dimension has the metadate-id as index. For single dates
- * an empty string is used as index.
- * - The second dimension contains two elements:
- * - 'metadate' => The metadate object. This is only set, if the
- * request is for a metadate.
- * - 'intervals' => The time intervals.
- * - The third dimension contains a time interval
- * in the following format:
- * [
- * 'begin' => The begin timestamp
- * 'end' => The end timestamp
- * 'range' => The name of the range class that provides the range_id.
- * This is usually the name of the SORM class.
- * 'range_id' => The ID of the single date or ResourceRequestAppointment.
- * ]
- */
- public function getGroupedTimeIntervals($with_preparation_time = false, $with_past_intervals = true)
- {
- $now = time();
- if (count($this->appointments)) {
- $time_intervals = [
- '' => [
- 'metadate' => null,
- 'intervals' => []
- ]
- ];
- foreach ($this->appointments as $appointment) {
- if (!$with_past_intervals && $appointment->appointment->end_time < $now) {
- continue;
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $appointment->appointment->date - $this->preparation_time,
- 'end' => $appointment->appointment->end_time
- ];
- } else {
- $interval = [
- 'begin' => $appointment->appointment->date,
- 'end' => $appointment->appointment->end_time
- ];
- }
-
- $date = CourseDate::find($appointment->appointment_id);
- $interval['range'] = 'CourseDate';
- $interval['range_id'] = $appointment->appointment_id;
- $interval['booked_room'] = $date->room_booking->resource_id;
- $interval['booking_id'] = $date->room_booking->id;
- $time_intervals['']['intervals'][] = $interval;
- }
-
- if (empty($time_intervals['']['intervals'])) {
- return [];
- } else {
- return $time_intervals;
- }
- } elseif ($this->termin_id) {
- if (!$with_past_intervals && $this->date->end_time < $now) {
- return [];
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $this->date->date - $this->preparation_time,
- 'end' => $this->date->end_time
- ];
- } else {
- $interval = [
- 'begin' => $this->date->date,
- 'end' => $this->date->end_time
- ];
- }
-
- $date = CourseDate::find($this->termin_id);
- $interval['range'] = 'CourseDate';
- $interval['range_id'] = $this->termin_id;
- $interval['booked_room'] = $date->room_booking->resource_id;
- $interval['booking_id'] = $date->room_booking->id;
-
- if (!empty($interval)) {
- return [
- '' => [
- 'metadate' => null,
- 'intervals' => [$interval]
- ]
- ];
- } else {
- return [];
- }
- } elseif ($this->metadate_id) {
- $time_intervals = [
- $this->metadate_id => [
- 'metadate' => $this->cycle,
- 'intervals' => []
- ]
- ];
- foreach ($this->cycle->dates as $date) {
- if (!$with_past_intervals && $date->end_time < $now) {
- continue;
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $date->date - $this->preparation_time,
- 'end' => $date->end_time
- ];
- } else {
- $interval = [
- 'begin' => $date->date,
- 'end' => $date->end_time
- ];
- }
- $interval['range'] = 'CourseDate';
- $interval['range_id'] = $date->id;
- $interval['booked_room'] = $date->room_booking->resource_id;
- $interval['booking_id'] = $date->room_booking->id;
- $time_intervals[$this->metadate_id]['intervals'][] = $interval;
- }
- return $time_intervals;
- } elseif ($this->course_id) {
- $time_intervals = [];
- if ($this->course->cycles) {
- foreach ($this->course->cycles as $cycle) {
- $time_intervals[$cycle->id] = [
- 'metadate' => $cycle,
- 'intervals' => []
- ];
- if ($cycle->dates) {
- foreach ($cycle->dates as $date) {
- if (!$with_past_intervals && $date->end_time < $now) {
- continue;
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $date->date - $this->preparation_time,
- 'end' => $date->end_time
- ];
- } else {
- $interval = [
- 'begin' => $date->date,
- 'end' => $date->end_time
- ];
- }
- $interval['range'] = 'CourseDate';
- $interval['range_id'] = $date->id;
- $interval['booked_room'] = $date->room_booking->resource_id;
- $interval['booking_id'] = $date->room_booking->id;
- $time_intervals[$cycle->id]['intervals'][] = $interval;
- }
- }
- }
- }
- if ($this->course->dates) {
- $time_intervals[''] = [
- 'metadate' => null,
- 'intervals' => []
- ];
- foreach ($this->course->dates as $date) {
- if (!$with_past_intervals && $date->end_time < $now) {
- continue;
- }
- if ($date->cycle instanceof SeminarCycleDate) {
- //Metadates are already handled above.
- continue;
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $date->date - $this->preparation_time,
- 'end' => $date->end_time
- ];
- } else {
- $interval = [
- 'begin' => $date->date,
- 'end' => $date->end_time
- ];
- }
- $interval['range'] = 'CourseDate';
- $interval['range_id'] = $date->id;
- $interval['booked_room'] = $date->room_booking->resource_id;
- $interval['booking_id'] = $date->room_booking->id;
- $time_intervals['']['intervals'][] = $interval;
- }
-
- if (empty($time_intervals['']['intervals'])) {
- unset($time_intervals['']);
- }
- }
- return $time_intervals;
- } elseif ($this->begin && $this->end) {
- if (!$with_past_intervals && $this->end < $now) {
- return [];
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $this->begin - $this->preparation_time,
- 'end' => $this->end
- ];
- } else {
- $interval = [
- 'begin' => $this->begin,
- 'end' => $this->end
- ];
- }
- $interval['range'] = 'User';
- $interval['range_id'] = $this->user_id;
-
- return [
- '' => [
- 'metadate' => null,
- 'intervals' => [$interval]
- ]
- ];
- } else {
- return [];
- }
- }
-
- /**
- * Retrieves the time intervals for this request.
- *
- * @param bool $with_preparation_time Whether the preparation time
- * of the request shall be prepended to the begin timestamp (true)
- * or whether it should not be included at all (false).
- * Defaults to false.
- *
- * @param bool $with_range Whether to include data of the Stud.IP range
- * and its corresponding ID to the request (true) or not (false).
- * Defaults to false.
- *
- * @param bool $with_past_intervals Whether to include past intervals (true)
- * or only include intervals from the current time and the future (false).
- * Defaults to true.
- *
- * @return string[][] A two-dimensional array of unix timestamps.
- * The first dimension contains one entry for each date,
- * the second dimension contains the start and end timestamp
- * for the date.
- * The second dimension uses the array keys 'begin' and 'end'
- * for start and end date.
- * If the @with_range parameter is set to true, the second array
- * dimension also contains the key 'range' for specifying the
- * range type and 'range_id' for specifying the ID of the
- * range object.
- * The range can be "CourseDate", "ResourceRequestAppointment"
- * or "User". The last two can only be present for simple requests
- * that are not bound to a course. The range "CourseDate"
- * can only occur on course-bound requests.
- */
- public function getTimeIntervals($with_preparation_time = false, $with_range = false, $with_past_intervals = true)
- {
- $now = time();
- if (count($this->appointments)) {
- $time_intervals = [];
- foreach ($this->appointments as $appointment) {
- if (!$with_past_intervals && $appointment->appointment->end_time < $now) {
- continue;
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $appointment->appointment->date - $this->preparation_time,
- 'end' => $appointment->appointment->end_time
- ];
- } else {
- $interval = [
- 'begin' => $appointment->appointment->date,
- 'end' => $appointment->appointment->end_time
- ];
- }
- if ($with_range) {
- $date = CourseDate::find($appointment->appointment_id);
-
- $interval['range'] = ResourceRequestAppointment::class;
- $interval['range_id'] = $appointment->appointment_id;
- $interval['booked_room'] = $date->room_booking->resource_id ?? null;
- $interval['booking_id'] = $date->room_booking->id ?? null;
-
- }
- $time_intervals[] = $interval;
- }
- return $time_intervals;
- } elseif ($this->termin_id) {
- if (!$with_past_intervals && $this->date->end_time < $now) {
- return [];
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $this->date->date - $this->preparation_time,
- 'end' => $this->date->end_time
- ];
- } else {
- $interval = [
- 'begin' => $this->date->date,
- 'end' => $this->date->end_time
- ];
- }
- if ($with_range) {
- $interval['range'] = CourseDate::class;
- $interval['range_id'] = $this->termin_id;
- $interval['booked_room'] = $this->date->room_booking->resource_id ?? null;
- $interval['booking_id'] = $this->date->room_booking->id ?? null;
- }
- return [$interval];
- } elseif ($this->metadate_id) {
- $time_intervals = [];
- foreach ($this->cycle->dates as $date) {
- if (!$with_past_intervals && $date->end_time < $now) {
- continue;
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $date->date - $this->preparation_time,
- 'end' => $date->end_time
- ];
- } else {
- $interval = [
- 'begin' => $date->date,
- 'end' => $date->end_time
- ];
- }
- if ($with_range) {
- $interval['range'] = CourseDate::class;
- $interval['range_id'] = $date->id;
- $interval['booked_room'] = $date->room_booking->resource_id ?? null;
- $interval['booking_id'] = $date->room_booking->id ?? null;
- }
- $time_intervals[] = $interval;
- }
- return $time_intervals;
- } elseif ($this->course_id) {
- $time_intervals = [];
- if ($this->course->dates) {
- foreach ($this->course->dates as $date) {
- if (!$with_past_intervals && $date->end_time < $now) {
- continue;
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $date->date - $this->preparation_time,
- 'end' => $date->end_time
- ];
- } else {
- $interval = [
- 'begin' => $date->date,
- 'end' => $date->end_time
- ];
- }
- if ($with_range) {
- $interval['range'] = CourseDate::class;
- $interval['range_id'] = $date->id;
- $interval['booked_room'] = $date->room_booking->resource_id ?? null;
- $interval['booking_id'] = $date->room_booking->id ?? null;
- }
- $time_intervals[] = $interval;
- }
- }
- return $time_intervals;
- } elseif ($this->begin && $this->end) {
- if (!$with_past_intervals && $this->end < $now) {
- return [];
- }
- if ($with_preparation_time) {
- $interval = [
- 'begin' => $this->begin - $this->preparation_time,
- 'end' => $this->end
- ];
- } else {
- $interval = [
- 'begin' => $this->begin,
- 'end' => $this->end
- ];
- }
- if ($with_range) {
- $interval['range'] = 'User';
- $interval['range_id'] = $this->user_id;
- }
- return [$interval];
- } else {
- return [];
- }
- }
-
-
- /**
- * Returns a string representation of the time intervals for this request.
- */
- public function getTimeIntervalStrings()
- {
- $strings = [];
- $intervals = $this->getTimeIntervals(false, true);
- foreach ($intervals as $interval) {
- $room = '';
-
- if ($interval['range'] === 'CourseDate') {
- $date = call_user_func([$interval['range'], 'find'], $interval['range_id']);
- if ($date->room_booking) {
- $room_obj = Room::find($date->room_booking->resource_id);
- if ($room_obj) {
- $room = $room_obj->name;
- }
- }
- }
-
- $same_day = date('Ymd', $interval['begin']) === date('Ymd', $interval['end']);
- if ($same_day) {
- $strings[] = strftime('%a. %x %R', $interval['begin'])
- . ' - ' . strftime('%R', $interval['end'])
- . ($room ? ', '. $room : '');
- } else {
- $strings[] = strftime('%a. %x %R', $interval['begin'])
- . ' - ' . strftime('%a %x %R', $interval['end'])
- . ($room ? ', '. $room : '');
- }
-
- }
- return $strings;
- }
-
-
- /**
- * Filters the time intervals for this request
- * by a specified time range.
- *
- * @see ResourceRequest::getTimeIntervals for the return format.
- */
- public function getTimeIntervalsInTimeRange(DateTime $begin, DateTime $end)
- {
- $all_time_intervals = $this->getTimeIntervals();
-
- $included_intervals = [];
- foreach ($all_time_intervals as $interval) {
- $interval_in_range = (
- (
- $interval['begin'] >= $begin->getTimestamp()
- &&
- $interval['begin'] <= $end->getTimestamp()
- )
- ||
- (
- $interval['end'] >= $begin->getTimestamp()
- &&
- $interval['end'] <= $end->getTimestamp()
- )
- );
- if ($interval_in_range) {
- $included_intervals[] = $interval;
- }
- }
-
- return $included_intervals;
- }
-
-
- /**
- * Returns a string representation of the ResourceRequest's type.
- */
- public function getType()
- {
- if (count($this->appointments)) {
- return 'appointments';
- } elseif ($this->termin_id) {
- return 'date';
- } elseif ($this->metadate_id) {
- return 'cycle';
- } elseif ($this->course_id) {
- return 'course';
- }
- return null;
- }
-
- /**
- * Returns a string representation of the status of the ResourceRequest.
- */
- public function getStatus()
- {
- switch ($this->closed) {
- case self::STATE_OPEN:
- return 'open';
- case self::STATE_PENDING:
- return 'pending';
- case self::STATE_CLOSED:
- return 'closed';
- case self::STATE_DECLINED:
- return 'declined';
- default:
- return '';
- }
- }
-
-
- /**
- * Returns a textual representation of the status of the ResourceRequest.
- */
- public function getStatusText()
- {
- if ($this->isNew()) {
- return _('Diese Anfrage wurde noch nicht gespeichert.');
- }
- if ($this->closed == self::STATE_OPEN) {
- return _('Die Anfrage wurde noch nicht bearbeitet.');
- } else if ($this->closed == self::STATE_DECLINED) {
- return _('Die Anfrage wurde bearbeitet und abgelehnt.');
- } else {
- return _('Die Anfrage wurde bearbeitet.');
- }
- }
-
-
- /**
- * Returns a textual representation of the dates for which the request
- * has been created.
- *
- * @param bool $as_array True, if an array with a string for each date
- * (single or cycle date) shall be returned, false otherwise.
- *
- * @returns string|array Depending on the parameter $as_array, the text
- * is returned as one string or as an array of strings for each date
- * (single or cycle date).
- */
- public function getDateString($as_array = false, $with_past_intervals = true)
- {
- $now = time();
- $strings = [];
- $resource_name = '';
- if (count($this->appointments)) {
- $parts = [];
- foreach ($this->appointments as $rra) {
- if (!$with_past_intervals && $rra->appointment->end_time < $now) {
- continue;
- }
- if ($rra->appointment) {
- $parts[] = $rra->appointment->getFullName('include-room');
- }
- }
- $strings[] = implode('; ', $parts);
- } elseif ($this->termin_id) {
- if ($this->date) {
- if ($with_past_intervals || $this->date->end_time >= $now) {
- $strings[] = $this->date->getFullName('include-room');
- }
- }
- } elseif ($this->metadate_id) {
- if ($this->cycle) {
- $this->cycle->dates->filter(function($date) use($with_past_intervals, $now) {
- return $with_past_intervals || $date->end_time >= $now;
- })->map(function($date) use(&$strings) {
- $strings[] = $date->getFullName('include-room');
- });
- }
- } elseif ($this->course_id) {
- $course = new Seminar($this->course_id);
- $strings[] = $course->getDatesTemplate('dates/seminar_html_roomplanning',
- [
- 'shrink' => false,
- 'show_room' => true,
- 'with_past_intervals' => $with_past_intervals
- ]
- );
- } elseif ($this->begin && $this->end) {
- $begin_date = date('Ymd', $this->begin);
- $end_date = date('Ymd', $this->end);
- if($this->resource) {
- $resource_name = htmlReady($this->resource->getFullName());
- }
- if ($begin_date == $end_date) {
- $strings[] = strftime('%a., %x, %R', $this->begin) . ' - '
- . strftime('%R', $this->end) . ' ' . $resource_name;
- } else {
- //Begin and end are on differnt dates
- $strings[] = strftime('%a., %x, %R', $this->begin) . ' - '
- . strftime('%a., %x, %R', $this->end) . ' ' . $resource_name;
- }
- }
-
- if ($as_array) {
- return $strings;
- } else {
- return implode(';', $strings);
- }
- }
-
-
- /**
- * Returns a human-readable string describing the type of the request.
- *
- * @param bool $short If this parameter is set to true, only the
- * type of the request is returned without any information about the
- * appointments. Otherwise, appointment information like the
- * date or the repetition are appended. Defaults to false.
- * @return string
- */
- public function getTypeString($short = false)
- {
- if (count($this->appointments) > 1) {
- if ($short) {
- return _('Einzeltermine');
- } else {
- return sprintf(_('Einzeltermine (%sx)'), count($this->appointments));
- }
- } elseif (count($this->appointments) === 1) {
- $date = $this->appointments[0]->appointment;
- if ($short || !$date) {
- return _('Einzeltermin');
- } else {
- return sprintf(_('Einzeltermin (%s)'), $date->getFullName());
- }
- } elseif ($this->date) {
- if ($short) {
- return _('Einzeltermin');
- } else {
- return sprintf(_('Einzeltermin (%s)'), $this->date->getFullName());
- }
- } elseif ($this->cycle) {
- if ($short) {
- return _('Regelmäßige Termine');
- } else {
- return sprintf(
- _('Regelmäßige Termine (%s)'),
- $this->cycle->toString('full')
- );
- }
- } elseif ($this->course) {
- if ($short) {
- return _('Alle Termine der Veranstaltung');
- } else {
- return sprintf(
- _('Alle Termine der Veranstaltung (%sx)'),
- count($this->course->dates)
- );
- }
- } else {
- return _('Einfache Anfrage');
- }
- }
-
-
- /**
- * Returns an array of date objects which are affected
- * by this ResourceRequest.
- */
- public function getAffectedDates()
- {
- $dates = [];
- switch ($this->getType()) {
- case 'date':
- $dates[] = $this->date;
- break;
- case 'cycle':
- $dates = $this->cycle->dates->getArrayCopy();
- break;
- case 'course':
- $dates = $this->course->dates->getArrayCopy();
- break;
- }
- return $dates;
- }
-
-
- /**
- * @param array $excluded_property_names
- * Returns all resource property definitions for all properties
- * which can be applied for this ResourceRequest by looking at the
- * Resource category. If no resource category ID is set for the request
- * an empty array is returned.
- */
- public function getAvailableProperties($excluded_property_names = [])
- {
- if (!$this->category_id) {
- //Without a category-ID we cannot find any property!
- return [];
- }
- if (count($excluded_property_names)) {
- return ResourcePropertyDefinition::findBySql(
- "INNER JOIN resource_category_properties
- USING (property_id)
- WHERE requestable = '1' AND category_id = :category_id
- AND name NOT IN ( :excluded_property_names )",
- [
- 'category_id' => $this->category_id,
- 'excluded_property_names' => $excluded_property_names
- ]
- );
- } else {
- return ResourcePropertyDefinition::findBySql(
- "INNER JOIN resource_category_properties
- USING (property_id)
- WHERE requestable = '1' AND category_id = :category_id",
- [
- 'category_id' => $this->category_id
- ]
- );
- }
- }
-
-
- /**
- * Returns a "compressed" array of resource request properties.
- * @param array $excluded_property_names
- * @return array An associative array where the keys represent the
- * property names and the values represent the property states.
- * Note that the value can be an array in case of range properties.
- */
- public function getPropertyData($excluded_property_names = [])
- {
- $data = [];
- foreach ($this->properties as $property) {
- if ($property->definition->range_search) {
- //Assume that a minimum value is requested:
- $data[$property->name] = [$property->state];
- } else {
- $data[$property->name] = $property->state;
- }
- }
- return $data;
- }
-
- /**
- * @param $name
- * @return bool
- */
- public function propertyExists($name)
- {
- $db = DBManager::get();
-
- $exists_stmt = $db->prepare(
- "SELECT TRUE FROM resource_request_properties
- INNER JOIN resource_property_definitions rpd
- ON resource_request_properties.property_id = rpd.property_id
- WHERE resource_request_properties.request_id = :request_id
- AND rpd.name = :name");
-
- $exists_stmt->execute(
- [
- 'request_id' => $this->id,
- 'name' => $name
- ]
- );
-
- $exists = $exists_stmt->fetchColumn(0);
-
- return (bool)$exists;
- }
-
-
- /**
- * @param $name
- * Returns the state of the property specified by $name.
- */
- public function getProperty($name)
- {
- if (!$this->propertyExists($name)) {
- //A property with the name $name does not exist for this
- //resource request object.
- //In that case we can only return null, since resource requests
- //store only those properties which are requested:
-
- return null;
- }
-
- $db = DBManager::get();
-
- $value_stmt = $db->prepare(
- "SELECT resource_request_properties.state FROM resource_request_properties
- INNER JOIN resource_property_definitions rpd
- ON resource_request_properties.property_id = rpd.property_id
- WHERE resource_request_properties.request_id = :request_id
- AND rpd.name = :name");
-
- $value_stmt->execute(
- [
- 'request_id' => $this->id,
- 'name' => $name
- ]
- );
-
- $value = $value_stmt->fetchColumn(0);
-
- if (!$value) {
- return null;
- }
-
- return $value;
- }
-
-
- /**
- * @param $name
- * @return ResourceRequestProperty
- * @throws InvalidResourceCategoryException If this resource category
- * doesn't match the category of the resource request object.
- * @throws ResourcePropertyException If the name of the
- * resource request property is not defined for this resource category.
- */
- public function getPropertyObject($name)
- {
- if (!$this->propertyExists($name)) {
- //A property with the name $name does not exist for this
- //resource object. If it is a mandatory property
- //we can still try to create it:
-
- $property = $this->category->createDefinedResourceRequestProperty(
- $this,
- $name
- );
-
- $property->store();
- return $property;
- }
-
- return ResourceRequestProperty::findOneBySql(
- "INNER JOIN resource_property_definitions rpd
- ON resource_request_properties.property_id = rpd.property_id
- WHERE resource_request_properties.request_id = :request_id
- AND rpd.name = :name",
- [
- 'request_id' => $this->id,
- 'name' => $name
- ]
- );
- }
-
-
- /**
- * @param string $name
- * @param string $state
- * @return bool True, if the property state could be set, false otherwise.
- */
- public function setProperty($name, $state = '')
- {
- if (!$this->propertyExists($name)) {
- //A property with the name $name does not exist for this
- //resource object. If it is a mandatory property
- //we can still try to create it:
-
- if ($this->category) {
- $property = $this->category->createDefinedResourceRequestProperty(
- $this,
- $name,
- $state
- );
- return $property->store();
- }
- return false;
- }
-
- $property = $this->getPropertyObject($name);
-
- if ($property) {
- $property->state = $state;
- if ($property->isDirty()) {
- return $property->store();
- }
- return true;
- }
- }
-
-
- /**
- * Sets or unsets the properties for this resource request.
- *
- * @param array $property_list The properties which shall be set
- * or unset. The array has the following structure:
- * [
- * property_name => property_value
- * ]
- *
- * @param bool $accept_null_values True, if a value of null
- * shall be used when setting the property.
- * If $accept_null_values is set to false all properties
- * with a value equal to null will be deleted.
- *
- * @return null
- */
- public function updateProperties($property_list = [], $accept_null_values = false)
- {
- //Delete all properties first then re-create them
- //from the $property_list array:
- $this->properties->delete();
- if (is_array($property_list)) {
- foreach ($property_list as $name => $state) {
- if ($state or $accept_null_values) {
- //State is set or null values are allowed:
- //create/update the property
- $this->setProperty($name, $state);
- }
- }
- }
- $this->resetRelation('properties');
- }
-
-
- public function deletePropertyIfExists($name = '')
- {
- if (!$this->propertyExists($name)) {
- return true;
- } else {
- $property = $this->getPropertyObject($name);
- return $property->delete();
- }
- }
-
-
- public function getRangeName()
- {
- if ($this->getRangeType() === 'course') {
- $name = $this->getRangeObject()->getFullName();
- $name .= ' (' . implode(',', $this->getRangeObject()->getMembersWithStatus('dozent', true)->limit(3)->getValue('nachname')) . ')';
- } else {
- $range_object = $this->getRangeObject();
- if ($range_object instanceof User) {
- if (get_visibility_by_id($range_object->id)) {
- $name = $range_object->getFullName();
- } else if ($this->user_id === $GLOBALS['user']->id) {
- $name = $range_object->getFullName();
- } else {
- $current_user = User::findCurrent();
- if ($current_user instanceof User) {
- //If the current user has at least autor permissions
- //(which are required to see all requests), they can
- //see the name of the requester.
- if ($this->resource_id && ($this->resource instanceof Resource)
- && $this->resource->userHasPermission($current_user, 'autor')) {
- $name = $range_object->getFullName();
- } else if (ResourceManager::userHasGlobalPermission($current_user, 'autor')) {
- $name = $range_object->getFullName();
- } else {
- return '';
- }
- } else {
- return '';
- }
- }
- } else {
- $name = $range_object->getFullName();
- }
- if ($this->comment) {
- $name .= " \n" . $this->comment;
- }
- }
- return $name;
- }
-
-
- public function isSimpleRequest()
- {
- return !$this->course_id && !$this->metadate_id && !$this->termin_id;
- }
-
-
- public function getRangeId()
- {
- //Check if the request belongs to a course:
- if ($this->termin_id) {
- return $this->date->range_id;
- } elseif ($this->metadate_id) {
- return $this->cycle->seminar_id;
- } elseif ($this->course_id) {
- return $this->course_id;
- }
-
- //The request does not belong to a course and therefore
- //belongs to a user:
- return $this->user_id;
- }
-
-
- public function getRangeType()
- {
- if ($this->course_id || $this->termin_id || $this->metadate_id) {
- return 'course';
- }
- return 'user';
- }
-
-
- public function getRangeObject()
- {
- if ($this->course_id) {
- return $this->course;
- }
- if ($this->termin_id) {
- return $this->date->course;
- }
- if ($this->metadate_id) {
- return $this->cycle->course;
- }
- return $this->user;
- }
-
-
- /**
- * This method sends a notification mail to all room administrators
- * that informs them of this new request.
- */
- public function sendNewRequestMail()
- {
- //First we must get all users who have admin permissions in the
- //resource management system. Depending wheter a resource_id is set
- //for this resource request either all admins of a resource or
- //all admins of the resource management system must be informed.
-
- $now = time();
- if ($this->resource_id) {
- //The resource-ID is set for this request:
- //Get all admins of the resource and the resource management system.
- $admin_users = User::findBySql(
- "user_id IN (
- SELECT user_id FROM resource_permissions
- WHERE (
- resource_id = :resource_id
- OR resource_id = 'global'
- )
- AND perms = 'admin'
- UNION
- SELECT user_id FROM resource_temporary_permissions
- WHERE resource_id = :resource_id
- AND perms = 'admin'
- AND begin <= :now AND end >= :now
- )",
- [
- 'resource_id' => $this->resource_id,
- 'now' => $now
- ]
- );
- } else {
- //Get all admins of the resource management system.
- $admin_users = User::findBySql(
- "user_id IN (
- SELECT user_id FROM resource_permissions
- WHERE resource_id = 'global'
- AND perms = 'admin'
- UNION
- SELECT user_id FROM resource_temporary_permissions
- WHERE resource_id = 'global'
- AND perms = 'admin'
- AND begin <= :now AND end >= :now
- GROUP BY user_id
- )",
- [
- 'now' => $now
- ]
- );
- }
-
- if (!$admin_users) {
- return;
- }
-
- $factory = new Flexi_TemplateFactory(
- $GLOBALS['STUDIP_BASE_PATH'] . '/locale/'
- );
-
- foreach ($admin_users as $user) {
- $user_lang_path = getUserLanguagePath($user->id);
-
- $template = $factory->open(
- $user_lang_path . '/LC_MAILS/new_resource_request.php'
- );
- $template->set_attribute('request', $this);
-
- if ($this->resource instanceof Resource) {
- $resource = $this->resource->getDerivedClassInstance();
- if ($resource instanceof Room) {
- $template->set_attribute('requested_room', $resource->name);
- } else {
- $template->set_attribute('requested_resource', $resource->name);
- }
- }
-
- $mail_text = $template->render();
-
- setLocaleEnv($user->preferred_language);
-
- if ($this->resource) {
- $resource = $this->resource->getDerivedClassInstance();
- $template->set_attribute('derived_resource', $resource);
- $mail_title = sprintf(
- _('%1$s: Neue Anfrage in der Raumverwaltung'),
- $resource->getFullName()
- );
- } else {
- $mail_title = sprintf(
- _('Neue Anfrage in der Raumverwaltung')
- );
- }
-
- Message::send(
- User::findCurrent()->id,
- $user->username,
- $mail_title,
- $mail_text
- );
-
- restoreLanguage();
- }
- }
-
-
- /**
- * @param array $bookings
- * This method sends a mail to inform the requester that
- * the request has been closed.
- */
- public function sendCloseRequestMailToRequester($bookings = [])
- {
- $factory = new Flexi_TemplateFactory(
- $GLOBALS['STUDIP_BASE_PATH'] . '/locale/'
- );
-
- $requester_lang = $this->user->preferred_language;
- $requester_lang_path = getUserLanguagePath($this->user->id);
- setLocaleEnv($requester_lang);
-
- $template = $factory->open(
- $requester_lang_path . '/LC_MAILS/close_resource_request.php'
- );
- $template->set_attribute('request', $this);
- if ($this->course) {
- $lecturers = CourseMember::findByCourseAndStatus(
- $this->course->id,
- 'dozent'
- );
- $lecturer_names = [];
- foreach ($lecturers as $lecturer) {
- if ($lecturer->user instanceof User) {
- $lecturer_names[] = $lecturer->user->getFullName();
- }
- }
-
- $lecturer_names = implode(', ', $lecturer_names);
- $template->set_attribute('lecturer_names', $lecturer_names);
- }
- if (is_array($bookings)) {
- $booked_rooms = [];
- $booked_time_intervals = [];
- $metadates = [];
- $single_dates = [];
- foreach ($bookings as $booking) {
- if (!($booking instanceof ResourceBooking)) {
- continue;
- }
- $booked_rooms[] = $booking->resource->name;
- if ($booking->assigned_course_date instanceof CourseDate) {
- $single_date = $booking->assigned_course_date;
- $metadate = $single_date->cycle;
- if ($metadate instanceof SeminarCycleDate) {
- $metadates[$metadate->id] = $metadate;
- } else {
- $single_dates[$single_date->id] = $single_date;
- }
- } else {
- $time_intervals = $booking->getTimeIntervals();
- foreach ($time_intervals as $time_interval) {
- $booked_time_intervals[] = $time_interval->__toString();
- }
- }
- }
- $booked_rooms = array_unique($booked_rooms);
- sort($booked_rooms);
- $template->set_attribute('booked_rooms', implode(', ', $booked_rooms));
- $template->set_attribute('metadates', $metadates);
- $template->set_attribute('single_dates', $single_dates);
- $template->set_attribute('booked_time_intervals', $booked_time_intervals);
- }
-
- $mail_title = _('Ihre Anfrage wurde bearbeitet!');
- $mail_text = $template->render();
-
- Message::send(
- User::findCurrent()->id,
- $this->user->username,
- $mail_title,
- $mail_text
- );
-
- restoreLanguage();
- }
-
-
- /**
- * @param array $bookings
- * This method sends mails to the lecurers of the course (if any)
- * where this request has been assigned to. The sent mail informs them
- * about the closing of the request.
- */
- public function sendCloseRequestMailToLecturers($bookings = [])
- {
- //Notify each lecturer of the course:
- if ($this->course) {
- $lecturers = CourseMember::findByCourseAndStatus(
- $this->course->id,
- 'dozent'
- );
-
- if ($lecturers) {
- $factory = new Flexi_TemplateFactory(
- $GLOBALS['STUDIP_BASE_PATH'] . '/locale/'
- );
-
- $lecturer_names = [];
- foreach ($lecturers as $lecturer) {
- if ($lecturer->user instanceof User) {
- $lecturer_names[] = $lecturer->user->getFullName();
- }
- }
- $lecturer_names = implode(', ', $lecturer_names);
-
- $booked_rooms = [];
- $booked_time_intervals = [];
- $metadates = [];
- $single_dates = [];
- if (is_array($bookings)) {
- foreach ($bookings as $booking) {
- if (!($booking instanceof ResourceBooking)) {
- continue;
- }
- $booked_rooms[] = $booking->resource->name;
- if ($booking->assigned_course_date instanceof CourseDate) {
- $single_date = $booking->assigned_course_date;
- $metadate = $single_date->cycle;
- if ($metadate instanceof SeminarCycleDate) {
- $metadates[$metadate->id] = $metadate;
- } else {
- $single_dates[$single_date->id] = $single_date;
- }
- } else {
- $time_intervals = $booking->getTimeIntervals();
- foreach ($time_intervals as $time_interval) {
- $booked_time_intervals[] = $time_interval->__toString();
- }
- }
- }
- }
- $booked_rooms = array_unique($booked_rooms);
- sort($booked_rooms);
- $booked_rooms = implode(', ', $booked_rooms);
-
- foreach ($lecturers as $lecturer) {
- $lec_lang = $lecturer->user->preferred_language;
- $lec_lang_path = getUserLanguagePath($lecturer->user->id);
-
- setLocaleEnv($lec_lang);
-
- $template = $factory->open(
- $lec_lang_path . '/LC_MAILS/close_resource_request.php'
- );
- $template->set_attribute('request', $this);
- $template->set_attribute('lecturer_names', $lecturer_names);
- $template->set_attribute('booked_rooms', $booked_rooms);
- $template->set_attribute('metadates', $metadates);
- $template->set_attribute('single_dates', $single_dates);
- $template->set_attribute('booked_time_intervals', $booked_time_intervals);
-
- $mail_title = _('Bearbeitung einer Anfrage!');
- $mail_text = $template->render();
-
- Message::send(
- User::findCurrent()->id,
- $lecturer->user->username,
- $mail_title,
- $mail_text
- );
-
- restoreLanguage();
- }
- }
- }
- }
-
-
- /**
- * This method sends a mail to inform the requester
- * about the denial of the request.
- */
- public function sendRequestDeniedMail()
- {
- //Get the user who made the request:
- $user = $this->user;
- if (!($user instanceof User)) {
- //No mail to send.
- return;
- }
-
- //Load the mail template:
- $factory = new Flexi_TemplateFactory(
- $GLOBALS['STUDIP_BASE_PATH'] . '/locale/'
- );
- $user_lang_path = getUserLanguagePath($user->id);
- $template = $factory->open(
- $user_lang_path . '/LC_MAILS/request_denied_mail.inc.php'
- );
-
- $range_object = $this->getRangeObject();
- $mail_title = _('Raumanfrage wurde abgelehnt');
- if($range_object instanceof Course) {
- $mail_title .= ': ' . $range_object->getFullName();
- }
- $mail_text = $template->render(
- [
- 'request' => $this,
- 'range_object' => $range_object
- ]
- );
-
- //Send the mail:
- Message::send(
- User::findCurrent()->id,
- $user->username,
- $mail_title,
- $mail_text
- );
- }
-
-
- public function isReadOnlyForUser(User $user)
- {
- $resource = $this->resource;
- if (!$resource) {
- //We cannot continue with the permission check.
- return false;
- }
- $resource = $resource->getDerivedClassInstance();
-
- return !$resource->userHasPermission($user, 'autor')
- && ($this->user_id != $user->id);
- }
-
- protected function convertToEventData(array $time_intervals, User $user)
- {
- $booking_plan_request_bg =
- ColourValue::find('Resources.BookingPlan.Request.Bg');
- $booking_plan_request_fg =
- ColourValue::find('Resources.BookingPlan.Request.Fg');
- $booking_plan_preparation_bg =
- ColourValue::find('Resources.BookingPlan.PreparationTime.Bg');
- $booking_plan_preparation_fg =
- ColourValue::find('Resources.BookingPlan.PreparationTime.Fg');
-
- $user_is_resource_autor = false;
- if ($this->resource_id && ($this->resource instanceof Resource)) {
- $user_is_resource_autor = $this->resource->userHasPermission(
- $user,
- 'autor'
- );
- }
- $request_is_editable =
- $user_is_resource_autor || ($user->id == $this->user_id);
-
- $request_api_urls = [];
- $request_view_urls = [];
-
- if ($request_is_editable) {
- $request_api_urls = [
- 'resize' => URLHelper::getURL(
- 'api.php/resources/request/'
- . $this->id . '/move',
- [
- 'quiet' => '1'
- ]
- ),
- 'move' => URLHelper::getURL(
- 'api.php/resources/request/'
- . $this->id . '/move',
- [
- 'quiet' => '1'
- ]
- )
- ];
-
- $request_view_urls = [
- 'edit' => URLHelper::getURL(
- 'dispatch.php/resources/room_request/edit/'
- . $this->id
- )
- ];
- if ($this->resource_id && ($this->resource instanceof Resource)) {
- if ($this->resource->userHasBookingRights($user)) {
- $request_view_urls['edit'] = URLHelper::getURL(
- 'dispatch.php/resources/room_request/resolve/'
- . $this->id
- );
- }
- }
- }
-
- $events = [];
-
- foreach ($time_intervals as $interval) {
- $real_begin = $interval['begin'];
- if ($this->preparation_time) {
- $real_begin += (int)$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(),
- $request_is_editable,
- '',
- '',
- 'ResourceRequest',
- $this->id,
- 'Resource',
- $this->resource_id,
- $request_view_urls,
- $request_api_urls
- );
- }
-
- $begin = new DateTime();
- $begin->setTimestamp($real_begin);
- $end = new DateTime();
- $end->setTimestamp($interval['end']);
-
- $events[] = new Studip\Calendar\EventData(
- $begin,
- $end,
- $this->getRangeName(),
- ['resource-request'],
- $booking_plan_request_fg->__toString(),
- $booking_plan_request_bg->__toString(),
- $request_is_editable,
- 'ResourceRequest',
- $this->id,
- 'Resource',
- $this->resource_id,
- 'Resource',
- $this->resource_id,
- $request_view_urls,
- $request_api_urls
- );
- }
-
- return $events;
- }
-
-
- public function getAllEventData()
- {
- return $this->convertToEventData(
- $this->getTimeIntervals(true),
- User::findCurrent()
- );
- }
-
-
- public function getEventDataForTimeRange(DateTime $begin, DateTime $end)
- {
- $intervals = $this->getTimeIntervals(true);
- $time_intervals = [];
-
- $begin_timestamp = $begin->getTimestamp();
- $end_timestamp = $end->getTimestamp();
-
- foreach ($intervals as $interval) {
- if ((($interval['begin'] >= $begin_timestamp)
- && ($interval['begin'] <= $end_timestamp)) ||
- (($interval['end'] >= $begin_timestamp)
- && ($interval['end'] <= $end_timestamp)) ||
- (($interval['begin'] < $begin_timestamp)
- && ($interval['end'] > $end_timestamp))
- ) {
- $time_intervals[] = $interval;
- }
- }
-
- return $this->convertToEventData($time_intervals, User::findCurrent());
- }
-
-
- public function getFilteredEventData(
- $user_id = null,
- $range_id = null,
- $range_type = null,
- $begin = null,
- $end = null
- )
- {
- $intervals = $this->getTimeIntervals(true);
- $time_intervals = [];
-
- if ($begin && $end) {
- $begin_timestamp = $begin;
- $end_timestamp = $end;
- if ($begin instanceof DateTime) {
- $begin_timestamp = $begin->getTimestamp();
- }
- if ($end instanceof DateTime) {
- $end_timestamp = $end->getTimestamp();
- }
-
- foreach ($intervals as $interval) {
- if ((($interval['begin'] >= $begin_timestamp)
- && ($interval['begin'] <= $end_timestamp)) ||
- (($interval['end'] >= $begin_timestamp)
- && ($interval['end'] <= $end_timestamp)) ||
- (($interval['begin'] < $begin_timestamp)
- && ($interval['end'] > $end_timestamp))
- ) {
- $time_intervals[] = $interval;
- }
- }
- } else {
- $time_intervals = $intervals;
- }
-
- if ($user_id) {
- $user = User::find($user_id);
- } else {
- $user = User::findCurrent();
- }
-
- return $this->convertToEventData($time_intervals, $user);
- }
-
- public function getPriority()
- {
-
- $result = $this->getTimeIntervals();
- if (count($result) === 0) {
- return null;
- }
- $first = $result[0];
- return round(($first['begin'] - time()) / 86400);
- }
-
- public function getLoggingInfoText()
- {
- $props = '';
- foreach ($this->getPropertyData() as $name => $state) {
- $props .= $name . '=' . $state . ' ';
- }
- $info['Anfrage'] = $this->getType();
- $info['Status'] = $this->getStatus();
- if ($this->category) {
- $info['Raumtyp'] = $this->category->name;
- }
- if ($this->termin_id) {
- $info['Termin'] = $this->termin_id;
- }
- if ($this->metadate_id) {
- $info['Metadate'] = $this->metadate_id;
- }
- if ($props) {
- $info['Eigenschaften'] = $props;
- }
- if ($this->comment) {
- $info['Kommentar'] = $this->comment;
- }
- $txt = '';
- foreach ($info as $n => $m) {
- $txt .= $n . ': ' . $m . ', ';
- }
- return trim($txt, ' ,');
- }
-}