diff --git a/app/controllers/resources/ajax.php b/app/controllers/resources/ajax.php new file mode 100644 index 0000000000000000000000000000000000000000..6ff3942f734391a90ebbf5dfb937c0cd7fc58b59 --- /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); + } +} diff --git a/app/views/resources/print/clipboard_rooms.php b/app/views/resources/print/clipboard_rooms.php index f8e7434a0f28aa047cabf855132936de1f19d69c..3d035a11fa32cae4b9b716c52be7e1a8ad343e59 100644 --- a/app/views/resources/print/clipboard_rooms.php +++ b/app/views/resources/print/clipboard_rooms.php @@ -171,8 +171,7 @@ 'eventSources' => [ [ 'url' => URLHelper::getURL( - 'api.php/resources/resource/' - . $room->id . '/booking_plan' + 'dispatch.php/resources/ajax/get_semester_booking_plan/' . $room->id ), 'method' => 'GET', 'extraParams' => [ diff --git a/app/views/resources/print/individual_booking_plan.php b/app/views/resources/print/individual_booking_plan.php index d8c60867d115fdc41904534dc940b5c39f51f976..630f306127becce4350567cff1a75846189053ed 100644 --- a/app/views/resources/print/individual_booking_plan.php +++ b/app/views/resources/print/individual_booking_plan.php @@ -12,7 +12,7 @@ 'eventSources' => [ [ 'url' => URLHelper::getURL( - 'api.php/resources/resource/' . $resource->id . '/booking_plan' + 'dispatch.php/resources/ajax/get_semester_booking_plan/' . $resource->id ), 'method' => 'GET', 'extraParams' => [ diff --git a/app/views/resources/resource/booking_plan.php b/app/views/resources/resource/booking_plan.php index ced660e773cd1dbef27f7dc9a4e04c6d4068b4d5..b9400dc63436eca1579162517db1b62c8f86bcfa 100644 --- a/app/views/resources/resource/booking_plan.php +++ b/app/views/resources/resource/booking_plan.php @@ -19,8 +19,7 @@ 'eventSources' => [ [ 'url' => URLHelper::getLink( - 'api.php/resources/resource/' - . htmlReady($resource->id) . '/booking_plan' + 'dispatch.php/resources/ajax/get_semester_booking_plan/' . $resource->id ), 'method' => 'GET', 'extraParams' => [ diff --git a/app/views/resources/room_planning/booking_plan.php b/app/views/resources/room_planning/booking_plan.php index 16b76e5ecc9f3fb35604e357ee737398c8aca229..312d9a3dcf8526303da050217583e09a228991f3 100644 --- a/app/views/resources/room_planning/booking_plan.php +++ b/app/views/resources/room_planning/booking_plan.php @@ -76,7 +76,7 @@ 'eventSources' => [ [ 'url' => URLHelper::getURL( - 'api.php/resources/resource/' . $resource->id . '/booking_plan' + 'dispatch.php/resources/ajax/get_booking_plan/' . $resource->id ), 'method' => 'GET', 'extraParams' => [ diff --git a/app/views/resources/room_planning/semester_plan.php b/app/views/resources/room_planning/semester_plan.php index 607891d42d7e0f3e31564f0d6d22ead98f2d5c32..2816328590d98d209fd62204ccbf36f5e9702680 100644 --- a/app/views/resources/room_planning/semester_plan.php +++ b/app/views/resources/room_planning/semester_plan.php @@ -103,10 +103,7 @@ 'eventSources' => [ [ 'url' => URLHelper::getURL( - sprintf( - 'api.php/resources/resource/%s/semester_plan', - htmlReady($resource->id) - ) + 'dispatch.php/resources/ajax/get_semester_booking_plan/' . $resource->id ), 'method' => 'GET', 'extraParams' => [ diff --git a/app/views/resources/room_request/planning.php b/app/views/resources/room_request/planning.php index aeebb655c0b849bd1e48c45c91c47141a1df1e46..0653043278618a375c1da502b91d71fab5b6d421 100644 --- a/app/views/resources/room_request/planning.php +++ b/app/views/resources/room_request/planning.php @@ -82,10 +82,7 @@ 'eventSources' => [ [ 'url' => URLHelper::getURL( - sprintf( - 'api.php/resources/resource/%s/semester_plan', - htmlReady($resource->id) - ) + 'dispatch.php/resources/ajax/get_booking_plan/' . $resource->id ), 'method' => 'GET', 'extraParams' => [ diff --git a/app/views/room_management/planning/index.php b/app/views/room_management/planning/index.php index c5d7d5fa250261d33713ff7b07a45cd6e294b210..4bdee329bdf3a7d394e8535f109aacaef0705676 100644 --- a/app/views/room_management/planning/index.php +++ b/app/views/room_management/planning/index.php @@ -56,8 +56,7 @@ 'eventSources' => [ [ 'url' => URLHelper::getLink( - 'api.php/room_clipboard/' - . htmlReady($clipboard->id) . '/booking_plan' + 'dispatch.php/resources/ajax/get_clipboard_semester_plan/' . $clipboard->id ), 'method' => 'GET', 'extraParams' => [ diff --git a/app/views/room_management/planning/semester_plan.php b/app/views/room_management/planning/semester_plan.php index f5b9c94eb74570ad499b1cc1208d3b1e6fe0f4d6..58e8febb6b505e7bc129b35a3600ed6395210521 100644 --- a/app/views/room_management/planning/semester_plan.php +++ b/app/views/room_management/planning/semester_plan.php @@ -61,8 +61,7 @@ 'eventSources' => [ [ 'url' => URLHelper::getLink( - 'api.php/room_clipboard/' - . htmlReady($clipboard->id) . '/semester_plan' + 'dispatch.php/resources/ajax/get_clipboard_semester_plan/' . $clipboard->id ), 'method' => 'GET', 'extraParams' => [ diff --git a/resources/assets/javascripts/lib/resources.js b/resources/assets/javascripts/lib/resources.js index 0c2980e1a7a342aee6eff9fdfe02514a367b8510..3287b42d6af4d01ec84ec74f901237c71b728bb4 100644 --- a/resources/assets/javascripts/lib/resources.js +++ b/resources/assets/javascripts/lib/resources.js @@ -1,10 +1,9 @@ -import { $gettext } from '../lib/gettext'; +import {$gettext} from '../lib/gettext'; class Resources { - static addUserToPermissionList(user_id, table_element) - { + static addUserToPermissionList(user_id, table_element) { if (!user_id || !table_element) { return; } @@ -37,7 +36,7 @@ class Resources } } } - var insert_function = function(user_id = null, username = null) { + var insert_function = function (user_id = null, username = null) { var new_row = jQuery(template_row).clone(true); jQuery(new_row).removeClass('invisible'); jQuery(new_row).removeClass('resource-permission-list-template'); @@ -137,7 +136,7 @@ class Resources STUDIP.api.GET( `user/${user_id}` - ).done(function(data) { + ).done(function (data) { var username = data.name.family + ', ' + data.name.given; @@ -147,17 +146,16 @@ class Resources if (data.name.suffix) { username += ' ' + data.name.suffix; } - username += ' (' + data.name.username +')' + username += ' (' + data.name.username + ')' + ' (' + data.perms + ')'; insert_function(user_id, username); - }).fail(function() { + }).fail(function () { insert_function(user_id); }); } - static addCourseUsersToPermissionList(course_id, table_element) - { + static addCourseUsersToPermissionList(course_id, table_element) { if (!course_id || !table_element) { return; } @@ -171,7 +169,7 @@ class Resources limit: 1000000 } } - ).done(function(data) { + ).done(function (data) { for (var attribute in data.collection) { var user_id = data.collection[attribute].member.id; STUDIP.Resources.addUserToPermissionList( @@ -183,8 +181,7 @@ class Resources } - static removeUserFromPermissionList(html_node) - { + static removeUserFromPermissionList(html_node) { if (!html_node) { return; } @@ -208,8 +205,7 @@ class Resources //Room search related methods: - static addSearchCriteriaToRoomSearchWidget(select_node) - { + static addSearchCriteriaToRoomSearchWidget(select_node) { if (!select_node) { return; } @@ -331,13 +327,13 @@ class Resources jQuery(date_inputs[1]).attr('name', option_value + '_end_date'); jQuery(date_inputs[0]).val( now.getFullYear() + '-' - + (now.getMonth() + 1) + '-' - + (now.getDate() + 1) + + (now.getMonth() + 1) + '-' + + (now.getDate() + 1) ); jQuery(date_inputs[1]).val( now.getFullYear() + '-' - + (now.getMonth() + 1) + '-' - + (now.getDate() + 2) + + (now.getMonth() + 1) + '-' + + (now.getDate() + 2) ); } else { //One date field, two time fields. @@ -348,8 +344,8 @@ class Resources jQuery(date_inputs[0]).attr('name', option_value + '_date'); jQuery(date_inputs[0]).val( now.getFullYear() + '-' - + (now.getMonth() + 1) + '-' - + (now.getDate() + 1) + + (now.getMonth() + 1) + '-' + + (now.getDate() + 1) ); } @@ -381,8 +377,7 @@ class Resources } - static removeSearchCriteriaFromRoomSearchWidget(icon_node) - { + static removeSearchCriteriaFromRoomSearchWidget(icon_node) { if (!icon_node) { return; } @@ -412,8 +407,7 @@ class Resources } - static submitRoomSearchWidgetForm(input_node) - { + static submitRoomSearchWidgetForm(input_node) { if (!input_node) { return; } @@ -431,8 +425,7 @@ class Resources //Resource request related methods: - static addPropertyToRequest(event) - { + static addPropertyToRequest(event) { var select = jQuery(event.target).siblings('select.requestable-properties-select')[0]; if (!select) { return; @@ -486,48 +479,32 @@ class Resources //ResourceBookingInterval methods: - static toggleBookingIntervalStatus(event) - { + static toggleBookingIntervalStatus(event) { event.preventDefault(); - var li = jQuery(event.target).parents('tr')[0]; - if (!li) { - //Something is wrong with the HTML. - return; - } - var interval_id = jQuery(li).data('interval_id'); - if (!interval_id) { + let button = event.target.closest('button'); + let intervalId = button.dataset.interval_id; + + if (!intervalId) { return; } - STUDIP.api.POST( - `resources/booking_interval/${interval_id}/toggle_takes_place` - ).done(function(data) { - if (data['takes_place'] === undefined) { - //Something went wrong: do nothing. - return; - } + const url = STUDIP.URLHelper.getURL(`dispatch.php/resources/ajax/toggle_takes_place_field/${intervalId}`); + fetch(url) + .then(response => response.json()) + .then(response => { + if (response['takes_place'] === undefined) { + //Something went wrong: do nothing. + return; + } - if (data['takes_place'] === '1') { - //Switch on the icons and text for the "takes place" - //status and switch off the other ones: - jQuery(li).find('.takes-place-revive').addClass('invisible'); - jQuery(li).find('.takes-place-delete').removeClass('invisible'); - jQuery(li).find('.booking-list-interval-date').removeClass('not-taking-place'); - } else { - //Do the opposite of the if-block above: - jQuery(li).find('.takes-place-delete').addClass('invisible'); - jQuery(li).find('.takes-place-revive').removeClass('invisible'); - jQuery(li).find('.booking-list-interval-date').addClass('not-taking-place'); - } - }); + const cell = button.closest('td'); + cell.previousElementSibling.classList.toggle('not-taking-place'); + cell.querySelectorAll('button').forEach(node => node.classList.toggle('invisible')); + }); } - //Methods for the resource category form: - - - static addResourcePropertyToTable(event) - { + static addResourcePropertyToTable(event) { var select = jQuery(event.target).siblings('select')[0]; if (!select) { //Something is wrong with the HTML @@ -607,22 +584,20 @@ class Resources //Methods for opening or closing of ressource tree elements: - static toggleTreeNode(treenode) - { + static toggleTreeNode(treenode) { var arr = treenode.children("img"); if (arr.hasClass('rotated')) { arr.attr('style', 'transform: rotate(0deg)'); } else { arr.attr('style', 'transform: rotate(90deg)'); } - arr.toggleClass('rotated') ; + arr.toggleClass('rotated'); treenode.children(".resource-tree").children("li").toggle(); } - static moveTimeOptions(bookingtype_val) - { - if(bookingtype_val === 'single') { + static moveTimeOptions(bookingtype_val) { + if (bookingtype_val === 'single') { $(".time-option-container").hide(); $(".block-booking-item").hide(); $(".repetition-booking-item").hide(); @@ -632,7 +607,7 @@ class Resources } else { var time_options = $(".time-option-container"); $(".time-option-container").detach(); - if(bookingtype_val === 'block') { + if (bookingtype_val === 'block') { $("#BlockBookingFieldset").prepend(time_options); $("#BlockEndLabel").show(); @@ -657,83 +632,81 @@ class Resources //Fullcalendar specialisations: - static updateEventUrlsInCalendar(calendar_event) - { - if (!calendar_event) { + static updateEventUrlsInCalendar(calendarEvent) { + if (!calendarEvent) { return; } - STUDIP.api.GET( - `resources/booking/${calendar_event.extendedProps.studip_parent_object_id}/intervals`, - { - data: { - begin: STUDIP.Fullcalendar.toRFC3339String(calendar_event.start), - end: STUDIP.Fullcalendar.toRFC3339String(calendar_event.end) + let parentObjectId = calendarEvent.extendedProps.studip_parent_object_id; + let begin = STUDIP.Fullcalendar.toRFC3339String(calendarEvent.start); + let end = STUDIP.Fullcalendar.toRFC3339String(calendarEvent.end); + + const url = STUDIP.URLHelper.getURL( + `dispatch.php/resources/ajax/get_resource_booking_intervals/${parentObjectId}`, + {begin, end} + ); + fetch(url) + .then(response => response.json()) + .then(response => { + if (!response || response.length === 0) { + return; } - } - ).done(function (data) { - if (!data || data.length === 0) { - return; - } - var new_interval_id = data[0].interval_id; - calendar_event.setExtendedProp('studip_object_id', new_interval_id); - if (new_interval_id) { - var move_url = calendar_event.extendedProps.studip_api_urls['move']; - var resize_url = calendar_event.extendedProps.studip_api_urls['resize']; - move_url = move_url.replace( - /&interval_id=([0-9a-f]{32})/, - '&interval_id=' + new_interval_id - ); - resize_url = resize_url.replace( - /&interval_id=([0-9a-f]{32})/, - '&interval_id=' + new_interval_id - ); - var studip_api_urls = calendar_event.extendedProps.studip_api_urls; - studip_api_urls['move'] = move_url; - studip_api_urls['resize'] = resize_url; - calendar_event.setExtendedProp('studip_api_urls', studip_api_urls); - } - }); + let newIntervalId = response[0].intervalId; + calendarEvent.setExtendedProp('studip_object_id', newIntervalId); + if (newIntervalId) { + let moveUrl = calendarEvent.extendedProps.studip_api_urls['move']; + let resizeUrl = calendarEvent.extendedProps.studip_api_urls['resize']; + moveUrl = moveUrl.replace( + /&interval_id=([0-9a-f]{32})/, + '&interval_id=' + newIntervalId + ); + resizeUrl = resizeUrl.replace( + /&interval_id=([0-9a-f]{32})/, + '&interval_id=' + newIntervalId + ); + let studipApiUrls = calendarEvent.extendedProps.studip_api_urls; + studipApiUrls['move'] = moveUrl; + studipApiUrls['resize'] = resizeUrl; + calendarEvent.setExtendedProp('studip_api_urls', studipApiUrls); + } + }) } - static resizeEventInRoomGroupBookingPlan(info) - { + static resizeEventInRoomGroupBookingPlan(info) { STUDIP.Fullcalendar.defaultResizeEventHandler(info); STUDIP.Resources.updateEventUrlsInCalendar(info.event); } - static dropEventInRoomGroupBookingPlan(info) - { + static dropEventInRoomGroupBookingPlan(info) { STUDIP.Fullcalendar.defaultDropEventHandler(info); STUDIP.Resources.updateEventUrlsInCalendar(info.event); } - static toggleRequestMarked(source_node) - { - if (!source_node) { + static toggleRequestMarked(sourceNode) { + if (!sourceNode) { return; } - var request_id = jQuery(source_node).data('request_id'); - if (!request_id) { + let requestId = sourceNode.dataset.request_id + + if (!requestId) { return; } - STUDIP.api.POST( - `resources/request/${request_id}/toggle_marked` - ).done(function(data) { - jQuery(source_node).attr('data-marked', data.marked); - jQuery(source_node).parent().attr('data-sort-value', data.marked); - jQuery(source_node).parents('table.request-list').trigger('update'); - }); + fetch(STUDIP.URLHelper.getURL(`dispatch.php/resources/ajax/toggle_marked/${requestId}`)) + .then(response => response.json()) + .then(response => { + sourceNode.dataset.marked = response.marked; + sourceNode.parentNode.dataset.sortValue = response.marked; + sourceNode.closest('table.request-list').dispatchEvent(new Event('update')); + }) } - static bookAllCalendarRequests() - { - var calendarSektion = $('*[data-resources-fullcalendar="1"]')[0]; - if (calendarSektion) { - var calendar = calendarSektion.calendar; + static bookAllCalendarRequests() { + let calendarSection = $('*[data-resources-fullcalendar="1"]')[0]; + if (calendarSection) { + let calendar = calendarSection.calendar; if (calendar) { if (!$('#loading-spinner').length) { jQuery('#content').append( @@ -746,14 +719,14 @@ class Resources ) ); } - $('.fc-request-event').each(function(){ - var objectData = $(this).data(); - var existingRequestEvent = calendar.getEventById(objectData.eventId); + $('.fc-request-event').each(function () { + let objectData = $(this).data(); + let existingRequestEvent = calendar.getEventById(objectData.eventId); if (existingRequestEvent) { - var bookingURL = 'dispatch.php/resources/room_request/quickbook/' - + objectData.eventRequest +'/' - + objectData.eventResource +'/' - + objectData.eventMetadate; + let bookingURL = 'dispatch.php/resources/room_request/quickbook/' + + objectData.eventRequest + '/' + + objectData.eventResource + '/' + + objectData.eventMetadate; jQuery.ajax( STUDIP.URLHelper.getURL(bookingURL), { @@ -780,10 +753,8 @@ Resources.definedResourceClasses = [ ]; -class Messages -{ - static selectRoom(room_id, room_name) - { +class Messages { + static selectRoom(room_id, room_name) { if (!room_id) { return; } @@ -806,30 +777,29 @@ class Messages jQuery(selection_area).append(new_room); } } + Resources.Messages = Messages; -class BookingPlan -{ - static insertEntry(new_entry, date, begin_hour, end_hour) - { +class BookingPlan { + static insertEntry(new_entry, date, begin_hour, end_hour) { //Get the resource-ID from the current URL: - var results = window.location.href.match( - /dispatch.php\/resources\/resource\/booking_plan\/([a-z0-9]{1,32})/ + let results = window.location.href.match( + /dispatch.php\/resources\/resource\/booking_plan\/([a-z0-9]{1,32})/ ); if (results.length === 0) { //No resource-ID found. jQuery(new_entry).remove(); return; } - var resource_id = results[1]; + let resource_id = results[1]; //Now we re-format the time from begin_hour and end_hour. //In case the data-dragged attribute is set for the //calendar entry we just add two hours to the start time //to get the end time. - var dragged = jQuery(new_entry).data('dragged'); + let dragged = jQuery(new_entry).data('dragged'); if (dragged) { end_hour = begin_hour + 2; } @@ -840,7 +810,7 @@ class BookingPlan end_hour += ':00'; } - var result = STUDIP.Dialog.fromURL( + let result = STUDIP.Dialog.fromURL( STUDIP.URLHelper.getURL( 'dispatch.php/resources/booking/add/' + resource_id, { @@ -853,6 +823,7 @@ class BookingPlan ); } } + Resources.BookingPlan = BookingPlan;