diff options
| author | David Siegfried <david.siegfried@uni-vechta.de> | 2024-03-15 16:59:10 +0000 |
|---|---|---|
| committer | Jan-Hendrik Willms <tleilax+studip@gmail.com> | 2024-03-15 16:59:10 +0000 |
| commit | 5d69b2c310f202aa58ab2fba48bf3e3bdffb13a1 (patch) | |
| tree | 9ffe7c0471a0b2f4832af9b01cc3a73b261055f6 /app/controllers/resources | |
| parent | f20f8f193992335531aa2781628155b91e283b7a (diff) | |
move rest-api routes to trails controller, closes #3791
Closes #3791
Merge request studip/studip!2665
Diffstat (limited to 'app/controllers/resources')
| -rw-r--r-- | app/controllers/resources/ajax.php | 659 |
1 files changed, 659 insertions, 0 deletions
diff --git a/app/controllers/resources/ajax.php b/app/controllers/resources/ajax.php new file mode 100644 index 0000000..6ff3942 --- /dev/null +++ b/app/controllers/resources/ajax.php @@ -0,0 +1,659 @@ +<?php + +/** + * ajax.php - contains Resources_AjaxController + * + * 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 David Siegfried <ds.siegfried@gmail.com> + * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 + */ + +class Resources_AjaxController extends AuthenticatedController +{ + public function toggle_marked_action($request_id) + { + $request = \ResourceRequest::find($request_id); + + if (!$request) { + throw new Exception('Resource request object not found!'); + } + + $current_user = \User::findCurrent(); + + if ($request->isReadOnlyForUser($current_user)) { + throw new \AccessDeniedException(); + } + + //Switch to the next marking state or return to the unmarked state + //if the next marking state would be after the last defined + //marking state. + $request->marked = ($request->marked + 1) % \ResourceRequest::MARKING_STATES; + $request->store(); + + $this->render_json($request->toArray()); + } + + public function get_resource_booking_intervals_action($booking_id) + { + $booking = \ResourceBooking::find($booking_id); + if (!$booking) { + throw new Exception('Resource booking object not found!'); + } + + $resource = $booking->resource->getDerivedClassInstance(); + if (!$resource->bookingPlanVisibleForUser(\User::findCurrent())) { + throw new \AccessDeniedException(); + } + + //Get begin and end: + $begin_str = \Request::get('begin'); + $end_str = \Request::get('end'); + $begin = null; + $end = null; + if ($begin_str && $end_str) { + //Try the ISO format first: YYYY-MM-DDTHH:MM:SS±ZZ:ZZ + $begin = \DateTime::createFromFormat(\DateTime::RFC3339, $begin_str); + $end = \DateTime::createFromFormat(\DateTime::RFC3339, $end_str); + if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) { + $tz = new \DateTime(); + $tz = $tz->getTimezone(); + //Try the ISO format without timezone: + $begin = \DateTime::createFromFormat('Y-m-d\TH:i:s', $begin_str, $tz); + $end = \DateTime::createFromFormat('Y-m-d\TH:i:s', $end_str, $tz); + } + } + + $sql = "booking_id = :booking_id "; + $sql_data = ['booking_id' => $booking->id]; + if ($begin instanceof \DateTime && $end instanceof \DateTime) { + $sql .= "AND begin >= :begin AND end <= :end "; + $sql_data['begin'] = $begin->getTimestamp(); + $sql_data['end'] = $end->getTimestamp(); + } + if (\Request::submitted('exclude_cancelled_intervals')) { + $sql .= "AND takes_place = '1' "; + } + $sql .= "ORDER BY begin ASC, end ASC"; + $intervals = \ResourceBookingInterval::findBySql($sql, $sql_data); + + $result = []; + foreach ($intervals as $interval) { + $result[] = $interval->toRawArray(); + } + + $this->render_json($result); + } + + public function toggle_takes_place_field_action($interval_id) + { + $interval = \ResourceBookingInterval::find($interval_id); + if (!$interval) { + throw new Exception('ResourceBookingInterval object not found!'); + } + + //Get the resource and check the permissions of the user: + $resource = $interval->resource; + if (!$resource) { + throw new Exception('ResourceBookingInterval not linked with a resource!'); + } + + $resource = $resource->getDerivedClassInstance(); + + if (!$resource->userHasPermission(\User::findCurrent(), 'autor', [$interval->begin, $interval->end])) { + throw new Exception('You do not have sufficient permissions to modify the interval!'); + } + + if ( + !$interval->takes_place + && $resource->isAssigned(new \DateTime('@' . $interval->begin), new \DateTime('@' . $interval->end)) + ) { + throw new Exception('Already booked'); + } + //Switch the takes_place field: + $interval->takes_place = $interval->takes_place ? '0' : '1'; + + if ($interval->store()) { + $this->render_json([ + 'takes_place' => $interval->takes_place + ]); + } else { + throw new Exception('Error while storing the interval!'); + } + } + + public function get_semester_booking_plan_action($resource_id) + { + $resource = \Resource::find($resource_id); + if (!$resource) { + throw new Exception('Resource object not found!'); + } + + $resource = $resource->getDerivedClassInstance(); + + $current_user = User::findCurrent(); + + if (!$resource->bookingPlanVisibleForUser($current_user)) { + throw new AccessDeniedException(); + } + + $display_requests = Request::get('display_requests'); + $display_all_requests = Request::get('display_all_requests'); + + $begin = new \DateTime(); + $end = new \DateTime(); + + $semester_id = Request::get('semester_id'); + + $semester = $semester_id ? Semester::find($semester_id) : Semester::findCurrent(); + if (!$semester) { + throw new Exception('No semester found!'); + } + + if (Request::get('semester_timerange') !== 'fullsem') { + $begin->setTimestamp($semester->vorles_beginn); + $end->setTimestamp($semester->vorles_ende); + } else { + $begin->setTimestamp($semester->beginn); + $end->setTimestamp($semester->ende); + } + + //Get parameters: + $booking_types = Request::getArray('booking_types'); + + $begin_timestamp = $begin->getTimestamp(); + $end_timestamp = $end->getTimestamp(); + + //Get the event data sources: + $bookings = ResourceBooking::findByResourceAndTimeRanges( + $resource, + [ + [ + 'begin' => $begin_timestamp, + 'end' => $end_timestamp + ] + ], + $booking_types + ); + + $requests = []; + if ($display_all_requests || $display_requests) { + $requests_sql = "JOIN seminar_cycle_dates AS scd USING (metadate_id) + WHERE resource_id = :resource_id + AND closed = 0"; + $requests_sql_params = [ + 'begin' => $begin_timestamp, + 'end' => $end_timestamp, + 'resource_id' => $resource->id + ]; + if (!$display_all_requests) { + $requests_sql .= "AND user_id = :user_id "; + $requests_sql_params['user_id'] = $current_user->id; + } + + $requests = \ResourceRequest::findBySql( + $requests_sql, + $requests_sql_params + ); + } + + $merged_objects = []; + $meta_dates = []; + + foreach ($bookings as $booking) { + $booking->resource = $resource; + $irrelevant_booking = $booking->getRepetitionType() !== 'weekly' + && ( + !\Request::get('display_single_bookings') + || $booking->end < strtotime('today') + ); + if ($booking->getAssignedUserType() === 'course' && in_array($booking->assigned_course_date->metadate_id, $meta_dates)) { + $irrelevant_booking = true; + }; + if (!$irrelevant_booking) { + //It is an booking with repetitions that has to be included + //in the semester plan. + if (in_array($booking->getRepetitionType(), ['single', 'weekly'])) { + $event_list = $booking->convertToEventData( + [ + ResourceBookingInterval::build( + [ + 'interval_id' => md5(uniqid()), + 'begin' => $booking->begin - $booking->preparation_time, + 'end' => $booking->end + ] + ) + ], + $current_user + ); + } else { + $event_list = $booking->getFilteredEventData(null, null, null, strtotime('today'), $end_timestamp); + } + foreach ($event_list as $event_data) { + if ($booking->getAssignedUserType() === 'course' && $booking->assigned_course_date->metadate_id) { + $index = sprintf( + '%s_%s_%s', + $booking->assigned_course_date->metadate_id, + $event_data->begin->format('NHis'), + $event_data->end->format('NHis') + ); + $meta_dates[] = $booking->assigned_course_date->metadate_id; + } else { + $index = sprintf( + '%s_%s_%s', + $booking->id, + $event_data->begin->format('NHis'), + $event_data->end->format('NHis') + ); + } + + //Strip some data that cannot be used effectively in here: + $event_data->api_urls = []; + $event_data->editable = false; + + $merged_objects[$index] = $event_data; + } + } + } + + $relevant_request = false; + foreach ($requests as $request) { + if ($request->cycle instanceof \SeminarCycleDate) { + $cycle_dates = $request->cycle->getAllDates(); + foreach ($cycle_dates as $cycle_date) { + $relevant_request = $semester->beginn <= $cycle_date->date + && $semester->ende >= $cycle_date->date; + if ($relevant_request) { + //We have found a date for the current semester + //that makes the request relevant. + break; + } + } + if (!$relevant_request) { + continue; + } + $event_data_list = $request->getFilteredEventData( + $current_user->id + ); + + foreach ($event_data_list as $event_data) { + $index = sprintf( + '%s_%s_%s', + $request->metadate_id, + $event_data->begin->format('NHis'), + $event_data->end->format('NHis') + ); + + //Strip some data that cannot be used effectively in here: + $event_data->view_urls = []; + $event_data->api_urls = []; + + $merged_objects[$index] = $event_data; + } + } + } + + //Convert the merged events to Fullcalendar events: + $data = []; + foreach ($merged_objects as $obj) { + $data[] = $obj->toFullCalendarEvent(); + } + + $this->render_json($data); + } + + public function get_booking_plan_action($resource_id) + { + $resource = Resource::find($resource_id); + if (!$resource) { + throw new Exception('Resource object not found!'); + } + + $resource = $resource->getDerivedClassInstance(); + + $current_user = User::findCurrent(); + $nobody_access = true; + + if ($current_user instanceof User) { + $nobody_access = false; + if (!$resource->bookingPlanVisibleForUser($current_user)) { + throw new AccessDeniedException(); + } + } else if ($resource instanceof Room) { + if (!$resource->bookingPlanVisibleForUser($current_user)) { + throw new AccessDeniedException(); + } + } + $user_is_resource_user = $current_user && $resource->userHasPermission($current_user); + + $display_requests = $current_user && Request::bool('display_requests'); + $display_all_requests = Request::bool('display_all_requests'); + + if ($display_all_requests && !$user_is_resource_user) { + //The user is not allowed to see all requests. + throw new AccessDeniedException(); + } + + $begin_date = Request::get('start'); + $end_date = Request::get('end'); + if (!$begin_date || !$end_date) { + //No time range specified. + throw new Exception('The parameters "start" and "end" are missing!'); + } + + $begin = DateTime::createFromFormat(DateTime::RFC3339, $begin_date); + $end = DateTime::createFromFormat(DateTime::RFC3339, $end_date); + + if (!($begin instanceof DateTime) || !($end instanceof DateTime)) { + $begin = new DateTime(); + $end = new DateTime(); + //Assume the local timezone and use the Y-m-d format: + $date_regex = '/[0-9]{4}-(0[1-9]|1[0-2])-([0-2][0-9]|3[0-1])/'; + if (preg_match($date_regex, $begin_date)) { + //$begin is specified in the date format YYYY-MM-DD: + $begin_str = explode('-', $begin_date); + $begin->setDate( + intval($begin_str[0]), + intval($begin_str[1]), + intval($begin_str[2]) + ); + $begin->setTime(0, 0, 0); + } else { + $begin->setTimestamp($begin_date); + } + //Now we do the same for $end_timestamp: + if (preg_match($date_regex, $end_date)) { + //$begin is specified in the date formay YYYY-MM-DD: + $end_str = explode('-', $end_date); + $end->setDate( + intval($end_str[0]), + intval($end_str[1]), + intval($end_str[2]) + ); + $end->setTime(23, 59, 59); + } else { + $end->setTimestamp($end_date); + } + } + + //Get parameters: + $booking_types = []; + if (!$nobody_access) { + $booking_types = explode(',', Request::get('booking_types')); + } + + $begin_timestamp = $begin->getTimestamp(); + $end_timestamp = $end->getTimestamp(); + + //Get the event data sources: + $bookings = ResourceBooking::findByResourceAndTimeRanges( + $resource, + [ + [ + 'begin' => $begin_timestamp, + 'end' => $end_timestamp + ] + ], + $booking_types + ); + $requests = []; + if ($display_all_requests) { + $requests = ResourceRequest::findByResourceAndTimeRanges( + $resource, + [ + [ + 'begin' => $begin_timestamp, + 'end' => $end_timestamp + ] + ], + 0 + ); + } else if ($display_requests) { + //Get the users own request only: + $requests = ResourceRequest::findByResourceAndTimeRanges( + $resource, + [ + [ + 'begin' => $begin_timestamp, + 'end' => $end_timestamp + ] + ], + 0, + [], + 'user_id = :user_id', + ['user_id' => $current_user->id] + ); + } + + $objects = array_merge($bookings, $requests); + $event_data = Studip\Fullcalendar::createData($objects, $begin_timestamp, $end_timestamp); + + if ($nobody_access) { + //For nobody users, the code stops here since + //nobody users are not allowed to include additional objects. + $this->render_json($event_data); + return; + } + + //Check if there are additional objects to be displayed: + $additional_objects = Request::getArray('additional_objects'); + $additional_object_colours = Request::getArray('additional_object_colours'); + if ($additional_objects) { + foreach ($additional_objects as $object_class => $object_ids) { + if ( + !is_a($object_class, SimpleORMap::class, true) + || !is_a($object_class, Studip\Calendar\EventSource::class, true) + ) { + continue; + } + + $special_colours = []; + if ($additional_object_colours[$object_class]) { + $special_colours = $additional_object_colours[$object_class]; + } + + $additional_objects = $object_class::findMany($object_ids); + foreach ($additional_objects as $additional_object) { + $event_data = $additional_object->getFilteredEventData( + $current_user->id, + null, + null, + $begin, + $end + ); + + if ($special_colours) { + foreach ($event_data as $data) { + $data->text_colour = $special_colours['fg']; + $data->background_colour = $special_colours['bg']; + $data->editable = false; + $event_data[] = $data->toFullcalendarEvent(); + } + } + } + } + } + $this->render_json($event_data); + } + + public function get_clipboard_semester_plan_action($clipboard_id = null) + { + if (!$clipboard_id) { + throw new Exception('ID of clipboard has not been provided!'); + } + + $clipboard = Clipboard::find($clipboard_id); + + if (!empty($_SESSION['selected_clipboard_id'])) { + $clipboard = \Clipboard::find($_SESSION['selected_clipboard_id']); + } + if (!$clipboard) { + throw new Exception('Clipboard object not found!'); + } + $current_user = User::findCurrent(); + + //Permission check: + if ($clipboard->user_id !== $current_user->id) { + throw new \AccessDeniedException(); + } + + $display_requests = Request::bool('display_requests'); + $display_all_requests = Request::bool('display_all_requests'); + + $begin = new DateTime(); + $end = new DateTime(); + + $semester_id = Request::get('semester_id'); + $semester = $semester_id ? Semester::find($semester_id) : Semester::findCurrent(); + + if (!$semester) { + throw new Exception('No semester found!'); + } + + if (Request::get('semester_timerange') === 'vorles') { + $begin->setTimestamp($semester->vorles_beginn); + $end->setTimestamp($semester->vorles_ende); + } else { + $begin->setTimestamp($semester->beginn); + $end->setTimestamp($semester->ende); + } + + $rooms = Room::findMany($clipboard->getAllRangeIds('Room')); + + //Get parameters: + $booking_types = Request::getArray('booking_types'); + + //Get the event data sources: + $plan_objects = []; + + foreach ($rooms as $room) { + if ($room->bookingPlanVisibleForuser($current_user)) { + $plan_objects = array_merge( + $plan_objects, + ResourceManager::getBookingPlanObjects( + $room, + [ + [ + 'begin' => $begin->getTimestamp(), + 'end' => $end->getTimestamp() + ] + ], + $booking_types, + $display_all_requests ? 'all' : $display_requests + ) + ); + } + } + + $merged_objects = []; + $meta_dates = []; + $relevant_request = false; + foreach ($plan_objects as $plan_object) { + if ($plan_object instanceof ResourceBooking) { + $irrelevant_booking = $plan_object->getRepetitionType() !== 'weekly' + || ( + $plan_object->getAssignedUserType() === 'course' + && in_array($plan_object->assigned_course_date->metadate_id, $meta_dates) + ); + if ($irrelevant_booking) { + continue; + } + + //It is a booking with repetitions that has to be included + //in the semester plan. + + $real_begin = $plan_object->begin; + if ($plan_object->preparation_time > 0) { + $real_begin -= $plan_object->preparation_time; + } + $event_data = $plan_object->convertToEventData( + [ + ResourceBookingInterval::build( + [ + 'interval_id' => md5(uniqid()), + 'begin' => $real_begin, + 'end' => $plan_object->end + ] + ) + ], + $current_user + ); + + //Merge event data from the same booking that have the + //same weekday and begin and end time into one event. + //If no repetition interval is set and the booking belongs + //to a course date, use the corresponding metadate ID or the + //course date ID in the index. Otherwise use the booking's + //ID (specified by event_data->object_id). + foreach ($event_data as $event) { + if ($plan_object->getAssignedUserType() === 'course') { + $index = sprintf( + '%s_%s_%s', + $plan_object->assigned_course_date->metadate_id, + $event->begin->format('NHis'), + $event->end->format('NHis') + ); + $meta_dates[] = $plan_object->assigned_course_date->metadate_id; + } else { + $index = sprintf( + '%s_%s_%s', + $plan_object->id, + $event->begin->format('NHis'), + $event->end->format('NHis') + ); + } + + //Strip some data that cannot be used effectively in here: + $event->api_urls = []; + + $merged_objects[$index] = $event; + } + } else if ($plan_object instanceof ResourceRequest) { + if ($plan_object->cycle instanceof SeminarCycleDate) { + $cycle_dates = $plan_object->cycle->getAllDates(); + foreach ($cycle_dates as $cycle_date) { + $relevant_request = $semester->beginn <= $cycle_date->date + && $semester->ende >= $cycle_date->date; + if ($relevant_request) { + //We have found a date for the current semester + //that makes the request relevant. + break; + } + } + if (!$relevant_request) { + continue; + } + $event_data_list = $plan_object->getFilteredEventData( + $current_user->id + ); + + foreach ($event_data_list as $event_data) { + $index = sprintf( + '%s_%s_%s', + $plan_object->metadate_id, + $event_data->begin->format('NHis'), + $event_data->end->format('NHis') + ); + + //Strip some data that cannot be used effectively in here: + $event_data->view_urls = []; + $event_data->api_urls = []; + + $merged_objects[$index] = $event_data; + } + } + } + } + + //Convert the merged events to Fullcalendar events: + $data = []; + foreach ($merged_objects as $obj) { + $data[] = $obj->toFullCalendarEvent(); + } + + $this->render_json($data); + } +} |
