diff options
Diffstat (limited to 'lib/models/resources/ResourceRequest.class.php')
| -rw-r--r-- | lib/models/resources/ResourceRequest.class.php | 2478 |
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, ' ,'); - } -} |
