diff options
| author | Jan-Hendrik Willms <tleilax+github@gmail.com> | 2021-07-22 16:07:19 +0200 |
|---|---|---|
| committer | Jan-Hendrik Willms <tleilax+github@gmail.com> | 2021-07-22 16:19:12 +0200 |
| commit | a3da1483a9e689846179159355badfec8073dbec (patch) | |
| tree | 770dcca6bdf5f6f2a11b0e7fcbbeda6919a3fc52 /app/controllers/room_management/planning.php | |
current code from svn, revision 62608
Diffstat (limited to 'app/controllers/room_management/planning.php')
| -rw-r--r-- | app/controllers/room_management/planning.php | 1492 |
1 files changed, 1492 insertions, 0 deletions
diff --git a/app/controllers/room_management/planning.php b/app/controllers/room_management/planning.php new file mode 100644 index 0000000..ecfba9e --- /dev/null +++ b/app/controllers/room_management/planning.php @@ -0,0 +1,1492 @@ +<?php + +/** + * planning.php - contains RoomManagement_PlanningController + * + * 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> + * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 + * @copyright 2017 + * @category Stud.IP + * @since 4.1 + */ + + +/** + * RoomManagement_PlanningController contains room planning functionality. + */ +class RoomManagement_PlanningController extends AuthenticatedController +{ + public function index_action($selected_clipboard_id = null) + { + PageLayout::setTitle( + _('Raumgruppen-Belegungsplan') + ); + + if (Navigation::hasItem('/resources/planning/index')) { + Navigation::activateItem('/resources/planning/index'); + } + + if ($selected_clipboard_id) { + $_SESSION['selected_clipboard_id'] = $selected_clipboard_id; + $this->redirect( + $this->url_for('room_management/planning/index', $_GET) + ); + } + + $this->display_all_requests = Request::get('display_all_requests'); + + //Build sidebar: + $sidebar = Sidebar::get(); + + $views = new ViewsWidget(); + if ($GLOBALS['user']->id && ($GLOBALS['user']->id != 'nobody')) { + $views->addLink( + _('Standard Zeitfenster'), + URLHelper::getURL( + 'dispatch.php/room_management/planning/index', + [ + 'defaultDate' => Request::get('defaultDate', date('Y-m-d')) + ] + ), + null, + ['class' => 'booking-plan-std_view'] + )->setActive(!Request::get('allday')); + + $views->addLink( + _('Ganztägiges Zeitfenster'), + URLHelper::getURL( + 'dispatch.php/room_management/planning/index', + [ + 'allday' => true, + 'defaultDate' => Request::get('defaultDate', date('Y-m-d')) + ] + ), + null, + ['class' => 'booking-plan-allday_view'] + )->setActive(Request::get('allday')); + } + $sidebar->addWidget($views); + + $dpicker = new SidebarWidget(); + $dpicker->setTitle('Datum'); + $picker_html = $this->get_template_factory()->render( + 'resources/room_planning/_sidebar_date_selection.php' + ); + $dpicker->addElement(new WidgetElement($picker_html)); + $sidebar->addWidget($dpicker); + + //Add clipboard widget: + $clipboard_widget = new RoomClipboardWidget(); + $clipboard_widget->setReadonly(true); + $clipboard_widget->setApplyButtonTitle( + _('Anzeigen') + ); + $sidebar->addWidget($clipboard_widget); + + //Check if a clipboard is selected: + $selected_clipboard_id = $_SESSION['selected_clipboard_id']; + if (!$selected_clipboard_id) { + $clipboards = Clipboard::getClipboardsForUser($GLOBALS['user']->id); + if (!empty($clipboards)) { + $selected_clipboard_id = $clipboards[0]->id; + } + } + $selected_clipboard_item_ids = $_SESSION['selected_clipboard_items']; + + $rooms = []; + if ($selected_clipboard_id) { + $clipboard = Clipboard::find($selected_clipboard_id); + $this->clipboard = $clipboard; + if ($clipboard) { + PageLayout::setTitle( + $clipboard->name . ': ' . _('Raumgruppen-Belegungsplan') + ); + if ($selected_clipboard_item_ids) { + //The array of current clipboard item IDs is not empty. + //This means that at least one but not necessarily all + //clipboard items are selected. + $room_ids = $clipboard->getSomeRangeIds( + 'Room', + $selected_clipboard_item_ids + ); + } else { + //Use all items from the clipboard: + $room_ids = $clipboard->getAllRangeIds('Room'); + } + + $rooms = Room::findMany($room_ids); + } else { + $this->no_clipboard = true; + return; + } + } + + if (!$rooms) { + //No rooms could be found. + $this->no_rooms = true; + return; + } + + //Generate the resources array for the fullcalendar scheduler plugin: + $this->scheduler_resources = []; + foreach ($room_ids as $room_id) { + $room = Room::find($room_id); + $this->scheduler_resources[] = [ + 'id' => $room->id, + 'parent_name' => $room->building->name, + 'title' => $room->name + ]; + } + + $current_user = User::findCurrent(); + + $room_c = count($rooms); + $requestable_rooms_c = 0; + $request_rights_c = 0; + $booking_rights_c = 0; + $admin_rights_c = 0; + $this->booking_types = [0, 1, 2]; + + foreach ($rooms as $room) { + if ($room->userHasRequestRights($current_user)) { + $request_rights_c++; + } + if ($room->userHasBookingRights($current_user)) { + $booking_rights_c++; + } + if ($room->userHasPermission($current_user, 'admin')) { + $admin_rights_c++; + } + if ($room->requestable) { + $requestable_rooms_c++; + } + + //Check the permissions for the room: + //The booking plan must be visible for the user. + $sufficient_permissions = + $room->bookingPlanVisibleForUser($current_user); + if (!$sufficient_permissions) { + throw new AccessDeniedException( + sprintf( + _('Der Belegungsplan des Raumes %s ist für Sie nicht zugänglich!'), + $room->name + ) + ); + } + } + + $this->all_rooms_booking_rights = ($room_c == $booking_rights_c); + $all_rooms_admin = ($room_c == $admin_rights_c); + if ($all_rooms_admin) { + //Display planned bookings, too: + $this->booking_types[] = 3; + } + if (!$this->all_rooms_booking_rights && $this->display_all_requests) { + throw new AccessDeniedException( + _('Sie sind nicht dazu berechtigt, alle Anfragen im Belegungsplan zu sehen!') + ); + } + + if (Config::get()->RESOURCES_ALLOW_ROOM_REQUESTS && $this->all_rooms_booking_rights) { + $options = new OptionsWidget(); + $options->addCheckbox( + _('Alle Anfragen anzeigen'), + $this->display_all_requests ? 'checked' : '', + $this->url_for( + 'room_management/planning/index/' . $_SESSION['selected_clipboard_id'], + [ + 'display_all_requests' => '1' + ] + ), + $this->url_for( + 'room_management/planning/index/' . $_SESSION['selected_clipboard_id'] + ), + [] + ); + $sidebar->insertWidget($options, 'roomclipboard'); + } + + $this->fullcalendar_studip_urls = []; + if ($this->all_rooms_booking_rights) { + $this->fullcalendar_studip_urls['add'] = URLHelper::getURL( + 'dispatch.php/resources/booking/add' + ); + } + + $booking_colour = ColourValue::find('Resources.BookingPlan.Booking.Bg'); + $course_booking_colour = ColourValue::find('Resources.BookingPlan.CourseBooking.Bg'); + $lock_colour = ColourValue::find('Resources.BookingPlan.Lock.Bg'); + $preparation_colour = ColourValue::find('Resources.BookingPlan.PreparationTime.Bg'); + $reservation_colour = ColourValue::find('Resources.BookingPlan.Reservation.Bg'); + $request_colour = ColourValue::find('Resources.BookingPlan.Request.Bg'); + $this->table_keys = [ + [ + 'colour' => $booking_colour->__toString(), + 'text' => _('Manuelle Buchung') + ], + [ + 'colour' => $course_booking_colour->__toString(), + 'text' => _('Veranstaltungsbezogene Buchung') + ], + [ + 'colour' => $lock_colour->__toString(), + 'text' => _('Sperrbuchung') + ], + [ + 'colour' => $preparation_colour->__toString(), + 'text' => _('Rüstzeit') + ], + [ + 'colour' => $reservation_colour->__toString(), + 'text' => _('Reservierung') + ], + ]; + if ($all_rooms_admin) { + $planned_booking_colour = ColourValue::find('Resources.BookingPlan.PlannedBooking.Bg'); + $this->table_keys[] = [ + 'colour' => $planned_booking_colour->__toString(), + 'text' => _('Geplante Buchung') + ]; + } + if ($this->display_all_requests) { + $this->table_keys[] = [ + 'colour' => $request_colour->__toString(), + 'text' => _('Anfrage') + ]; + } + } + + public function semester_plan_action($selected_clipboard_id = null) + { + PageLayout::setTitle( + _('Raumgruppen-Semester-Belegungsplan') + ); + + if (Navigation::hasItem('/resources/planning/semestergroup_plan')) { + Navigation::activateItem('/resources/planning/semestergroup_plan'); + } + + if ($selected_clipboard_id) { + $_SESSION['selected_clipboard_id'] = $selected_clipboard_id; + $this->redirect( + $this->url_for('room_management/planning/semester_plan', $_GET) + ); + } + + $this->display_all_requests = Request::get('display_all_requests'); + + //Build sidebar: + $sidebar = Sidebar::get(); + + $this->semester = Semester::findCurrent(); + //For the semester selector: + if (Request::submitted('semester_id')) { + $this->semester = Semester::find(Request::get('semester_id')); + if (!$this->semester) { + PageLayout::postError( + _('Das ausgewählte Semester wurde nicht in der Datenbank gefunden!') + ); + return; + } + } + + $actions = new ActionsWidget(); + $actions->addLink( + _('Buchungen kopieren'), + $this->url_for('room_management/planning/copy_bookings'), + Icon::create('assessment+add'), + ['data-dialog' => 'size=auto'] + ); + $sidebar->addWidget($actions); + + + if ($GLOBALS['user']->id && ($GLOBALS['user']->id != 'nobody')) { + $views = new ViewsWidget(); + $views->setTitle(_('Zeitfenster')); + $views->addLink( + _('Standard Zeitfenster'), + URLHelper::getURL( + 'dispatch.php/room_management/planning/semester_plan', + [ + 'defaultDate' => Request::get('defaultDate', date('Y-m-d')), + 'semester_id' => $this->semester->id, + 'semester_timerange' => Request::get('semester_timerange', 'vorles') + ] + ), + null, + ['class' => 'booking-plan-std_view'] + )->setActive(!Request::get('allday')); + + $views->addLink( + _('Ganztägiges Zeitfenster'), + URLHelper::getURL( + 'dispatch.php/room_management/planning/semester_plan', + [ + 'allday' => true, + 'defaultDate' => Request::get('defaultDate', date('Y-m-d')), + 'semester_id' => $this->semester->id, + 'semester_timerange' => Request::get('semester_timerange', 'vorles') + ] + ), + null, + ['class' => 'booking-plan-allday_view'] + )->setActive(Request::get('allday')); + $sidebar->addWidget($views); + + $views2 = new ViewsWidget(); + $views2->setTitle(_('Semesterzeitraum')); + $views2->addLink( + _('Vorlesungszeit'), + URLHelper::getURL( + 'dispatch.php/room_management/planning/semester_plan', + [ + 'allday' => Request::get('allday'), + 'defaultDate' => Request::get('defaultDate', date('Y-m-d')), + 'semester_id' => $this->semester->id, + 'semester_timerange' => 'vorles' + ] + ), + null, + ['class' => 'booking-plan-vorles_view'] + )->setActive(Request::get('semester_timerange') != 'fullsem'); + $views2->addLink( + _('gesamtes Semester'), + URLHelper::getURL( + 'dispatch.php/room_management/planning/semester_plan', + [ + 'allday' => Request::get('allday'), + 'defaultDate' => Request::get('defaultDate', date('Y-m-d')), + 'semester_id' => $this->semester->id, + 'semester_timerange' => 'fullsem' + ] + ), + null, + ['class' => 'booking-plan-fullsem_view'] + )->setActive(Request::get('semester_timerange') == 'fullsem'); + $sidebar->addWidget($views2); + } + $semester_selector = new SemesterSelectorWidget( + URLHelper::getURL( + 'dispatch.php/room_management/planning/semester_plan/' . $this->resource->id, + [ + 'allday' => Request::get('allday', false) + ] + ) + ); + $sidebar->addWidget($semester_selector); + + //Add clipboard widget: + $clipboard_widget = new RoomClipboardWidget(); + $clipboard_widget->setReadonly(true); + $clipboard_widget->setApplyButtonTitle( + _('Anzeigen') + ); + $sidebar->addWidget($clipboard_widget); + + //Check if a clipboard is selected: + $selected_clipboard_id = $_SESSION['selected_clipboard_id']; + $selected_clipboard_item_ids = $_SESSION['selected_clipboard_items']; + + $rooms = []; + if ($selected_clipboard_id) { + $clipboard = Clipboard::find($selected_clipboard_id); + $this->clipboard = $clipboard; + if ($clipboard) { + PageLayout::setTitle( + $clipboard->name . ': ' . _('Raumgruppen-Semester-Belegungsplan') + ); + if ($selected_clipboard_item_ids) { + //The array of current clipboard item IDs is not empty. + //This means that at least one but not necessarily all + //clipboard items are selected. + $room_ids = $clipboard->getSomeRangeIds( + 'Room', + $selected_clipboard_item_ids + ); + } else { + //Use all items from the clipboard: + $room_ids = $clipboard->getAllRangeIds('Room'); + } + + $rooms = Room::findMany($room_ids); + } else { + $this->no_clipboard = true; + return; + } + } + + if (!$rooms) { + //No rooms could be found. + $this->no_rooms = true; + return; + } + + //Generate the resources array for the fullcalendar scheduler plugin: + $this->scheduler_resources = []; + foreach ($room_ids as $room_id) { + $room = Room::find($room_id); + $this->scheduler_resources[] = [ + 'id' => $room->id, + 'parent_name' => $room->building->name, + 'title' => $room->name + ]; + } + + $current_user = User::findCurrent(); + + $room_c = count($rooms); + $requestable_rooms_c = 0; + $booking_rights_c = 0; + $admin_rights_c = 0; + $this->booking_types = [0, 1, 2]; + + foreach ($rooms as $room) { + if ($room->userHasBookingRights($current_user)) { + $booking_rights_c++; + } + if ($room->userHasPermission($current_user, 'admin')) { + $admin_rights_c++; + } + if ($room->requestable) { + $requestable_rooms_c++; + } + + //Check the permissions for the room: + $sufficient_permissions = + $room->userHasPermission($current_user, 'user') + || $room->booking_plan_is_public; + if (!$sufficient_permissions) { + throw new AccessDeniedException(); + } + } + + $all_rooms_requestable = ($room_c == $requestable_rooms_c); + $all_rooms_booking_rights = ($room_c == $booking_rights_c); + $all_rooms_admin = ($room_c == $admin_rights_c); + if ($all_rooms_admin) { + //Display planned bookings, too: + $this->booking_types[] = 3; + } + + if (!$all_rooms_booking_rights && $this->display_all_requests) { + throw new AccessDeniedException( + _('Sie sind nicht dazu berechtigt, alle Anfragen im Belegungsplan zu sehen!') + ); + } + + if ($all_rooms_booking_rights) { + $options = new OptionsWidget(); + $options->addCheckbox( + _('Alle Anfragen anzeigen'), + $this->display_all_requests ? 'checked' : '', + $this->url_for( + 'room_management/planning/semester_plan/' . $_SESSION['selected_clipboard_id'], + [ + 'display_all_requests' => '1', + 'semester_id' => Request::option('semester_id') + ] + ), + $this->url_for( + 'room_management/planning/semester_plan/' . $_SESSION['selected_clipboard_id'], + [ + 'semester_id' => Request::option('semester_id') + ] + ), + [] + ); + $sidebar->insertWidget($options, 'roomclipboard'); + } + + $booking_colour = ColourValue::find('Resources.BookingPlan.Booking.Bg'); + $simple_booking_exception_colour = ColourValue::find('Resources.BookingPlan.SimpleBookingWithExceptions.Bg'); + $course_booking_colour = ColourValue::find('Resources.BookingPlan.CourseBooking.Bg'); + $course_booking_with_exceptions_colour = ColourValue::find('Resources.BookingPlan.CourseBookingWithExceptions.Bg'); + $lock_colour = ColourValue::find('Resources.BookingPlan.Lock.Bg'); + $preparation_colour = ColourValue::find('Resources.BookingPlan.PreparationTime.Bg'); + $reservation_colour = ColourValue::find('Resources.BookingPlan.Reservation.Bg'); + $request_colour = ColourValue::find('Resources.BookingPlan.Request.Bg'); + $this->table_keys = [ + [ + 'colour' => $booking_colour->__toString(), + 'text' => _('Manuelle Buchung') + ], + [ + 'colour' => $course_booking_colour->__toString(), + 'text' => _('Veranstaltungsbezogene Buchung') + ], + [ + 'colour' => $lock_colour->__toString(), + 'text' => _('Sperrbuchung') + ], + [ + 'colour' => $preparation_colour->__toString(), + 'text' => _('Rüstzeit') + ], + [ + 'colour' => $reservation_colour->__toString(), + 'text' => _('Reservierung') + ], + ]; + if ($all_rooms_admin) { + $planned_booking_colour = ColourValue::find('Resources.BookingPlan.PlannedBooking.Bg'); + $this->table_keys[] = [ + 'colour' => $planned_booking_colour->__toString(), + 'text' => _('Geplante Buchung') + ]; + } + if ($this->display_all_requests) { + $this->table_keys[] = [ + 'colour' => $request_colour->__toString(), + 'text' => _('Anfrage') + ]; + } + + } + + public function copy_bookings_action($clipboard_id = null) + { + PageLayout::setTitle( + _('Buchungen kopieren') + ); + + if (Navigation::hasItem('/resources/planning/copy_bookings')) { + Navigation::activateItem('/resources/planning/copy_bookings'); + } + + //Check if the clipboard is selected: + $selected_clipboard_id = $_SESSION['selected_clipboard_id']; + $selected_clipboard_item_ids = $_SESSION['selected_clipboard_items']; + + $user = User::findCurrent(); + + $this->clipboard = null; + if ($selected_clipboard_id) { + $this->clipboard = Clipboard::find($selected_clipboard_id); + } else { + $this->clipboard = Clipboard::find($clipboard_id); + if (!$clipboard_id) { + PageLayout::postError( + _('Es wurde keine Raumgruppe ausgewählt!') + ); + return; + } + } + if (!$this->clipboard) { + PageLayout::postError( + _('Die gewählte Raumgruppe wurde nicht gefunden!') + ); + return; + } + if ($this->clipboard->user_id != $GLOBALS['user']->id) { + throw new AccessDeniedException(); + } + + PageLayout::setTitle( + $this->clipboard->name . ': ' . _('Buchungen kopieren') + ); + + //Step 1: Room selection + $this->step = 1; + + //Get all Room items from the clipboard where the user has at least + //user permissions: + $all_room_ids = $this->clipboard->getAllRangeIds('Room'); + $unfiltered_rooms = Room::findMany($all_room_ids); + $this->rooms = []; + $this->available_room_ids = []; + foreach ($unfiltered_rooms as $room) { + if ($room->userHasPermission($user, 'autor')) { + $this->rooms[] = $room; + $this->available_room_ids[] = $room->id; + } + } + + $this->selected_room_ids = []; + if ($selected_clipboard_id == $this->clipboard->id) { + $this->selected_room_ids = $selected_clipboard_item_ids; + } + + //Get all available semesters: + $this->available_semesters = Semester::getAll(); + $this->sem_week_selected = false; + $this->selected_sem_week = 1; + + if (Request::isPost()) { + CSRFProtection::verifyUnsafeRequest(); + if (Request::submitted('select_rooms') || Request::submitted('step1')) { + $this->step = 2; + } elseif (Request::submitted('test_copy') || Request::submitted('step2') + || Request::submitted('download_booking_list')) { + $this->step = 3; + } elseif (Request::submitted('copy')) { + $this->step = 4; + } + } + + if ($this->step >= 2) { + //Step 2: Select and verify bookings and semester + $this->source_semester_id = Request::get('source_semester_id'); + $this->sem_week_selected = Request::get('sem_week_selected'); + $this->selected_sem_week = Request::get('selected_sem_week'); + $this->selected_room_ids = Request::getArray('selected_room_ids'); + if (!$this->source_semester_id) { + PageLayout::postError( + _('Es wurde kein Semester ausgewählt!') + ); + $this->step = 1; + return; + } + $this->source_semester = Semester::find($this->source_semester_id); + if (!$this->source_semester) { + PageLayout::postError( + _('Das gewählte Semester wurde nicht gefunden!') + ); + $this->step = 1; + return; + } + if ($this->sem_week_selected) { + $last_sem_week_number = $this->source_semester->getSemWeekNumber( + $this->source_semester->vorles_ende + ); + if (($this->selected_sem_week < 1) || ($this->selected_sem_week > $last_sem_week_number)) { + PageLayout::postError( + _('Die gewählte Semesterwoche liegt außerhalb des gewählten Semesters!') + ); + $this->step = 1; + return; + } + } + if (!$this->selected_room_ids) { + PageLayout::postError( + _('Es wurden keine Räume ausgewählt!') + ); + $this->step = 1; + return; + } + + foreach ($this->selected_room_ids as $room_id) { + if (!in_array($room_id, $all_room_ids)) { + PageLayout::postError( + _('Es wurde ein Raum ausgewählt, der nicht Teil der Raumgruppe ist!') + ); + $this->step = 1; + return; + } + if (!in_array($room_id, $this->available_room_ids)) { + PageLayout::postError( + _('Es wurde ein Raum ausgewählt, an dem die Berechtigungen zum Kopieren von Buchungen nicht ausreichend sind!') + ); + $this->step = 1; + return; + } + } + + if (Request::submitted('step1')) { + $this->step = 1; + return; + } + + $this->selected_rooms = Room::findMany($this->selected_room_ids); + + $this->available_target_semesters = Semester::findBySql( + 'beginn > :source_semester_end ORDER BY beginn ASC', + ['source_semester_end' => $this->source_semester->ende] + ); + if (!$this->available_target_semesters) { + PageLayout::postError( + _('Es sind keine Semester vorhanden, die nach dem ausgewählten Semester starten!') + ); + $this->step = 1; + return; + } + + $unfiltered_bookings = []; + foreach ($this->selected_rooms as $room) { + $room_bookings = []; + if ($this->sem_week_selected) { + $selected_week_begin = $this->source_semester->vorles_beginn; + if ($this->selected_sem_week > 1) { + $selected_week_begin = strtotime( + sprintf('+%d weeks', $this->selected_sem_week), + $this->source_semester->vorles_beginn + ); + } + $room_bookings = ResourceBooking::findByResourceAndTimeRanges( + $room, + [ + [ + 'begin' => $selected_week_begin, + 'end' => $this->source_semester->ende + ] + ] + ); + } else { + $room_bookings = ResourceBooking::findByResourceAndTimeRanges( + $room, + [ + [ + 'begin' => $this->source_semester->beginn, + 'end' => $this->source_semester->ende + ] + ] + ); + } + if ($room_bookings) { + $unfiltered_bookings = array_merge( + $unfiltered_bookings, + $room_bookings + ); + } + } + $this->bookings = []; + $this->available_booking_ids = []; + $this->booking_time_ranges = []; + foreach ($unfiltered_bookings as $booking) { + if (!$booking->repetition_interval || !$booking->isSimpleBooking()) { + //We only regard simple bookings with repetitions here. + continue; + } + $this->bookings[] = $booking; + $this->available_booking_ids[] = $booking->id; + $this->booking_time_ranges[$booking->id] = + $booking->getTimeIntervalStrings(); + } + + if (!$this->available_booking_ids) { + PageLayout::postError( + sprintf( + _('Die gewählten Räume haben im Semester %s keine einfachen Buchungen mit Wiederholungen!'), + htmlReady($this->source_semester->name) + ) + ); + $this->step = 1; + return; + } + } + if ($this->step >= 3) { + //Step 3: Test copying into the target semester + $this->show_copy_button = false; + $this->target_semester_id = Request::get('target_semester_id'); + $this->selected_booking_ids = Request::getArray('selected_booking_ids'); + if (!$this->target_semester_id) { + PageLayout::postError( + _('Es wurde kein Zielsemester ausgewählt!') + ); + $this->step = 2; + return; + } + $this->target_semester = Semester::find($this->target_semester_id); + if (!$this->target_semester) { + PageLayout::postError( + _('Das gewählte Zielsemester wurde nicht gefunden!') + ); + $this->step = 2; + return; + } + + if (!$this->selected_booking_ids) { + PageLayout::postError( + _('Es wurden keine Buchungen ausgewählt!') + ); + $this->step = 2; + return; + } + + foreach ($this->selected_booking_ids as $booking_id) { + if (!in_array($booking_id, $this->available_booking_ids)) { + PageLayout::postError( + _('Es wurde eine Buchung ausgewählt, die nicht Teil der Raumgruppe ist!') + ); + $this->step = 2; + return; + } + } + + if (Request::submitted('step2')) { + $this->step = 2; + return; + } + + //Retrieve booking objects: + $this->selected_bookings = ResourceBooking::findMany($this->selected_booking_ids); + + if (!$this->selected_bookings) { + PageLayout::postError( + _('Die gewählten Buchungen wurden nicht in der Datenbank gefunden!') + ); + $this->step = 2; + return; + } + + //$booking_copy_data is an associative array where the items have + //the following strucutre: + //[ + // 'sem_week' => The week number of the target semester. + // 'begin' => The timestamp of the begin of the copied booking. + // 'end' => The timestamp of the end of the copied booking. + // 'available' => Whether the resource is available + // on the specified time range (true) or not (false). + //] + $this->booking_copy_data = []; + + //Loop over each booking and do the following: + //1. Calculate the week number and the week day of the booking + // in the semester, unless the week number has been explicitly + // specified in step 1. + //2. Calculate the date for the copy of the booking and store it + // in an array. + //3. Check if the resource of the booking is available on the + // calculcated date in the time range of the original booking. + //4. Add the availability information to the array + // with the booking copies. + //5. Count the number of bookings and how many of them can be + // copied in the target semester. If more that 50% of bookings + // cannot be copied, do not show the copy action and instead + // provide a download button to download the list of bookings. + + $available_booking_c = 0; + foreach ($this->selected_bookings as $booking) { + $begin_sem_week_number = 0; + if ($this->sem_week_selected) { + $begin_sem_week_number = $this->selected_sem_week + + $this->source_semester->getSemWeekNumber($booking->begin) - 1; + } else { + $begin_sem_week_number = $this->source_semester->getSemWeekNumber($booking->begin); + } + if (!$begin_sem_week_number) { + PageLayout::postError( + sprintf( + _('Eine Buchung (%1$s) liegt außerhalb des Semesters %2$s!'), + htmlReady($booking->__toString()), + htmlReady($this->source_semester->name) + ) + ); + $this->step = 2; + return; + } + $begin_week_day = date('N', $booking->begin); + $begin_time = explode(':', date('H:i:s', $booking->begin)); + $end_time = explode(':', date('H:i:s', $booking->end)); + + //Calculate the duration (begin-end-difference): + $booking_begin = new DateTime(); + $booking_begin->setTimestamp($booking->begin); + $booking_end = new DateTime(); + $booking_end->setTimestamp($booking->end); + + $duration = $booking_begin->diff($booking_end); + $booking_repeat_end = new DateTime(); + $booking_repeat_end->setTimestamp($booking->repeat_end); + $repeat_duration = $booking_end->diff($booking_repeat_end); + + //Calculate the new begin date: + $target_sem_week_begin = new DateTime(); + $target_sem_week_begin->setTimestamp($this->target_semester->beginn); + $target_sem_week_begin = $target_sem_week_begin->add( + new DateInterval('P' . ($begin_sem_week_number - 1) . 'W') + ); + $target_begin = clone $target_sem_week_begin; + $begin_week_day_diff = $begin_week_day - $target_sem_week_begin->format('N'); + if ($begin_week_day_diff < 0) { + $target_begin = $target_begin->sub( + new DateInterval('P' . abs($begin_week_day_diff) . 'D') + ); + } elseif ($begin_week_day_diff > 0) { + $target_begin = $target_begin->add( + new DateInterval('P' . $begin_week_day_diff . 'D') + ); + } + $target_begin->setTime( + intval($begin_time[0]), + intval($begin_time[1]), + intval($begin_time[2]) + ); + + //Calculcate the new end date using the duration: + $target_end = clone $target_begin; + $target_end = $target_end->add($duration); + + //Calculcate the new repeat end using the repeat duration + //or the end of the semester, if repeat_end of the original + //booking is the same timestamp as the course end of the + //source semester. + $target_repeat_end = clone $target_end; + if ($booking->repeat_end >= $this->source_semester->vorles_ende) { + $target_repeat_end->setTimestamp( + $this->target_semester->vorles_ende + ); + } else { + $target_repeat_end = $target_repeat_end->add( + $repeat_duration + ); + if ($target_repeat_end >= $this->target_semester->vorles_ende) { + $target_repeat_end->setTimestamp( + $this->target_semester->vorles_ende + ); + } + } + + $copy_data = [ + 'sem_week_number' => $begin_sem_week_number, + 'copy' => null, + 'available' => null, + 'original' => $booking, + 'time_intervals' => [] + ]; + + $copy = new ResourceBooking(); + $copy->resource_id = $booking->resource_id; + $copy->range_id = $booking->range_id; + $copy->booking_user_id = $GLOBALS['user']->id; + $copy->description = $booking->description; + $copy->begin = $target_begin->getTimestamp() + + $booking->preparation_time; + $copy->end = $target_end->getTimestamp(); + $copy->preparation_time = $booking->preparation_time; + $copy->booking_type = $booking->booking_type; + $copy->repeat_end = $target_repeat_end->getTimestamp(); + $copy->repeat_quantity = $booking->repeat_quantity; + $copy->repetition_interval = $booking->repetition_interval; + $copy->internal_comment = $booking->internal_comment; + if ($this->step == 3) { + //We only need to call validate when we are really + //trying to check if the booking can be made. + //After step 3, we don't need to call validate manually + //since it is automatically called before storing. + //Furthermore, the availability flag isn't important + //anymore after step 3. + $time_intervals = $copy->calculateTimeIntervals(); + if (!$time_intervals) { + //The copied booking will have no time intervals. + //So we can skip to the next one. + continue; + } + $copy_data['time_intervals'] = $copy->calculateTimeIntervals(); + try { + $copy->validate(); + $copy_data['available'] = true; + $available_booking_c++; + } catch (Exception $e) { + $copy_data['available'] = false; + } + } + $copy_data['copy'] = $copy; + $this->booking_copy_data[$booking->id] = $copy_data; + } + if (Request::submitted('download_booking_list')) { + $csv_data = [ + [ + _('Buchungsnummer'), + _('Buchungszeitraum'), + _('Raum'), + _('Verfügbar') + ] + ]; + $booking_c = 1; + foreach ($this->booking_copy_data as $data) { + foreach ($data['time_intervals'] as $interval) { + $time_range = sprintf( + '%1$s - %2$s', + date('d.m.Y H:i', $interval['begin']), + date('d.m.Y H:i', $interval['end']) + ); + $csv_data[] = [ + $booking_c, + $time_range, + $data['original']->resource->name, + $data['available'] ? _('ja') : _('nein') + ]; + } + $booking_c++; + } + $filename = sprintf( + _('Zu kopierende Buchungen am %s') . '.csv', + date('d.m.Y') + ); + $this->render_csv($csv_data, $filename); + return; + } elseif ($this->step < 4) { + $booking_c = count($this->selected_bookings); + if ($booking_c) { + if (($available_booking_c / $booking_c) < 0.5) { + PageLayout::postInfo( + _('Weniger als die Hälfte der Buchungen können in das Zielsemester kopiert werden!') + ); + return; + } else { + $this->show_copy_button = true; + } + } + } + } + if ($this->step >= 4) { + $errors = []; + $count = 0; + //Step 4: Copy the bookings + foreach ($this->booking_copy_data as $copy_data) { + try { + $copy_data['copy']->store(); + } catch (Exception $e) { + $errors[] = $e->getMessage(); + } + $count++; + } + + if (!$errors) { + PageLayout::postSuccess( + _('Alle Buchungen wurden kopiert!') + ); + } else { + if (count($errors) < $count) { + PageLayout::postWarning( + _('Es konnten nicht alle Buchungen kopiert werden!'), + $errors + ); + } else { + PageLayout::postError( + _('Keine der ausgewählten Buchungen konnte kopiert werden!'), + $errors + ); + } + } + } + } + + public function booking_comments_action($selected_clipboard_id = null) + { + PageLayout::setTitle(_('Buchungen mit Kommentaren')); + + if (Navigation::hasItem('/resources/planning/booking_comments')) { + Navigation::activateItem('/resources/planning/booking_comments'); + } + + if ($selected_clipboard_id) { + $_SESSION['selected_clipboard_id'] = $selected_clipboard_id; + $this->redirect( + $this->url_for('room_management/planning/booking_comments', $_GET) + ); + } + + //Get the selected date or use the current date, if none specified: + $this->date = Request::getDateTime('date', 'd.m.Y', null, null, new DateTime()); + if ($this->date === false) { + //Format parsing error. Try the YYYY-mm-dd format: + $this->date = Request::getDateTime('date', 'Y-m-d', null, null, new DateTime()); + if ($this->date === false) { + //Fallback to the current date: + $this->date = new DateTime(); + } + } + + //Build sidebar: + $sidebar = Sidebar::get(); + + //Add the date selection widget: + $date_search = new SearchWidget( + $this->url_for('room_management/planning/booking_comments') + ); + $date_search->setTitle(_('Datum')); + $date_search->setMethod('get'); + $date_search->addNeedle( + _('Datum'), + 'date', + 'DD.MM.YYYY', + null, + null, + $this->date->format('d.m.Y'), + ['class' => 'with-datepicker'] + ); + $sidebar->addWidget($date_search); + + //Add clipboard widget: + $clipboard_widget = new RoomClipboardWidget(); + $clipboard_widget->setReadonly(true); + $clipboard_widget->setApplyButtonTitle( + _('Anzeigen') + ); + $sidebar->addWidget($clipboard_widget); + + $selected_clipboard_id = $_SESSION['selected_clipboard_id']; + if (!$selected_clipboard_id) { + $clipboards = Clipboard::getClipboardsForUser($GLOBALS['user']->id); + if (!empty($clipboards)) { + $selected_clipboard_id = $clipboards[0]->id; + } + } + $selected_clipboard_item_ids = $_SESSION['selected_clipboard_items']; + + $this->current_user = User::findCurrent(); + $this->room_ids = []; + if ($selected_clipboard_id) { + $clipboard = Clipboard::find($selected_clipboard_id); + $this->clipboard = $clipboard; + if ($clipboard) { + PageLayout::setTitle( + $clipboard->name . ': ' . _('Buchungen mit Kommentaren') + ); + if ($selected_clipboard_item_ids) { + //The array of current clipboard item IDs is not empty. + //This means that at least one but not necessarily all + //clipboard items are retrieved, checked for user + //resource permissions and then added, if the user's + //permissions are high enough. + $room_ids = $clipboard->getSomeRangeIds( + 'Room', + $selected_clipboard_item_ids + ); + } else { + //Use all items from the clipboard: + $room_ids = $clipboard->getAllRangeIds('Room'); + } + + $rooms = Resource::findMany($room_ids); + foreach ($rooms as $room) { + $room = $room->getDerivedClassInstance(); + if ($room instanceof Room) { + if ($room->userHasPermission($this->current_user, 'user')) { + $this->room_ids[] = $room->id; + } + } + } + } + } + + //Add the actions widget: + + $actions = new ActionsWidget(); + $actions->addLink( + _('Export für Word'), + $this->url_for( + 'room_management/planning/booking_comments', + [ + 'export' => 'html', + 'date' => $this->date->format('d.m.Y') + ] + ), + Icon::create('file-text') + ); + $actions->addLink( + _('Export als CSV'), + $this->url_for( + 'room_management/planning/booking_comments', + [ + 'export' => 'csv', + 'date' => $this->date->format('d.m.Y') + ] + ), + Icon::create('file-excel') + ); + $sidebar->addWidget($actions); + + //Calculate week begin and end: + $week_end = new DateTime(); + $week_end->setTimestamp(strtotime('next sunday', $this->date->getTimestamp())); + $week_end->setTime(23,59,59); + $week_begin = clone $week_end; + $week_begin = $week_begin->sub(new DateInterval('P1W'))->add(new DateInterval('PT1S')); + + //Get bookings: + + $booking_intervals = ResourceBookingInterval::findBySql( + "INNER JOIN resource_bookings rb + ON resource_booking_intervals.booking_id = rb.id + WHERE rb.internal_comment <> '' + AND rb.resource_id IN ( :room_ids ) + AND ( + resource_booking_intervals.begin BETWEEN :begin AND :end + OR + resource_booking_intervals.end BETWEEN :begin AND :end + ) + ORDER BY resource_booking_intervals.begin ASC, resource_booking_intervals.end ASC", + [ + 'room_ids' => $this->room_ids, + 'begin' => $week_begin->getTimestamp(), + 'end' => $week_end->getTimestamp() + ] + ); + + //Array structure: + //Layer 1: keys: resource-IDs, content: Array + //Layer 2: keys: 0 = name (resource name), 1-7: weekdays: Array + //Layer 3 (weekdays): Array + //Layer 4 (weekdays): 0 = time, 1 = comment + $this->data = [ + ]; + + foreach ($booking_intervals as $interval) { + $l1_index = $interval->booking->resource_id; //Layer 1 index + if (!is_array($this->data[$l1_index])) { + $this->data[$l1_index] = [ + $interval->booking->resource->name, + [], + [], + [], + [], + [], + [], + [] + ]; + } + + $booking_text_items = []; + if ($interval->booking->description) { + $booking_text_items[] = $interval->booking->description; + } + if ($interval->booking->assigned_user instanceof User) { + $booking_text_items[] = + $interval->booking->assigned_user->getFullName(); + } + if ($interval->booking->internal_comment) { + $booking_text_items[] = $interval->booking->internal_comment; + } + $booking_text_string = implode('; ', $booking_text_items); + + $interval_begin = new DateTime(); + $interval_begin->setTimestamp($interval->begin); + $interval_end = new DateTime(); + $interval_end->setTimestamp($interval->end); + $begin_weekday = date('N', $interval->begin); + $end_weekday = date('N', $interval->end); + + if ($interval_begin->format('Ymd') != $interval_end->format('Ymd')) { + //The interval is spread over several days. + //It must be displayd on each of them. + $current_day = clone $interval_begin; + if ($interval_begin < $week_begin) { + $current_day->setTime(0,0,0); + //The interval starts before the current week. + //We have to move the current day to the begin + //of the week to calculate the begin time. + while ($current_day < $week_begin) { + $current_day = $current_day->add( + new DateInterval('P1D') + ); + } + } + + //At this point we have reached the begin of the selected week. + //Add an entry to the data array: + $l2_index = intval($current_day->format('N')); + if (!is_array($this->data[$l1_index][$l2_index])) { + $this->data[$l1_index][$l2_index] = []; + } + + if ($current_day->format('Ymd') == $interval_end->format('Ymd')) { + //The interval ends on the first day of the week. + $time_string = sprintf( + _('%1$s - %2$s Uhr'), + $current_day->format('H:i'), + $interval_end->format('H:i') + ); + $this->data[$l1_index][$l2_index][] = [ + $time_string, + $booking_text_string + ]; + //There is nothing else to do for this interval. + } else { + //The interval ends on another day of the week. + $time_string = sprintf( + _('%1$s - %2$s Uhr'), + $current_day->format('H:i'), + '23:59' + ); + + $this->data[$l1_index][$l2_index][] = [ + $time_string, + $booking_text_string + ]; + + $current_day = $current_day->add(new DateInterval('P1D')); + + //Now we loop over each day until we have reached the end day. + //We compare date strings, because the time may differ + //between $current_day and $interval_end. + while ($current_day->format('Ymd') < $interval_end->format('Ymd')) { + if ($current_day > $week_end) { + //out of range + break; + } + if ($current_day >= $week_begin) { + $l2_index = intval($current_day->format('N')); + if (!is_array($this->data[$l1_index][$l2_index])) { + $this->data[$l1_index][$l2_index] = []; + } + $time_string = sprintf( + _('%1$s - %2$s Uhr'), + '0:00', + '23:59' + ); + $this->data[$l1_index][$l2_index][] = [ + $time_string, + $booking_text_string + ]; + } + $current_day = $current_day->add( + new DateInterval('P1D') + ); + } + if ($current_day->format('Ymd') <= $week_end->format('Ymd')) { + //We have reached the last day of the interval, + //which lies inside the week. + $time_string = sprintf( + _('%1$s - %2$s Uhr'), + '0:00', + $interval_end->format('H:i') + ); + $l2_index = intval($interval_end->format('N')); + if (!is_array($this->data[$l1_index][$l2_index])) { + $this->data[$l1_index][$l2_index] = []; + } + $this->data[$l1_index][$l2_index][] = [ + $time_string, + $booking_text_string + ]; + } + } + } else { + $l2_index = intval($begin_weekday); //Layer 2 index + if (!is_array($this->data[$l1_index][$l2_index])) { + $this->data[$l1_index][$l2_index] = []; + } + $time_string = sprintf( + _('%1$s - %2$s Uhr'), + date('H:i', $interval->booking->begin), + date('H:i', $interval->booking->end) + ); + $this->data[$l1_index][$l2_index][] = [ + $time_string, + $booking_text_string + ]; + } + } + + //Sort the data array by the room name: + usort($this->data, function ($a, $b) + { + if ($a[0] == $b[0]) { + return 0; + } + return ($a[0] < $b[0]) ? -1 : 1; + } + ); + + $export = Request::get('export'); + if ($export == 'html') { + //Load the export template: + $factory = new Flexi_TemplateFactory( + $GLOBALS['STUDIP_BASE_PATH'] . '/app/views/room_management/planning/' + ); + + $template = $factory->open('booking_comments_html_export_frame.php'); + + $template->set_attribute( + 'data', + $this->data + ); + $template->set_attribute('date', $this->date); + + $html = $template->render(); + + $file_name = sprintf( + _('Buchungen, KW %d.doc'), + $this->date->format('W') + ); + + $this->set_content_type('application/msword; charset=utf-8'); + header('Content-Disposition: attachment;filename="' . $file_name . '"'); + $this->render_text($html); + } elseif ($export == 'csv') { + $csv_data = [ + [ + sprintf( + _('%d. Kalenderwoche'), + $this->date->format('W') + ), + sprintf( + '%1$s' . "\n" . '%2$s', + _('Montag'), + date( + 'd.m.Y', + strtotime('this week monday', $this->date->getTimestamp()) + ) + ), + sprintf( + '%1$s' . "\n" . '%2$s', + _('Dienstag'), + date( + 'd.m.Y', + strtotime('this week tuesday', $this->date->getTimestamp()) + ) + ), + sprintf( + '%1$s' . "\n" . '%2$s', + _('Mittwoch'), + date( + 'd.m.Y', + strtotime('this week wednesday', $this->date->getTimestamp()) + ) + ), + sprintf( + '%1$s' . "\n" . '%2$s', + _('Donnerstag'), + date( + 'd.m.Y', + strtotime('this week thursday', $this->date->getTimestamp()) + ) + ), + sprintf( + '%1$s' . "\n" . '%2$s', + _('Freitag'), + date( + 'd.m.Y', + strtotime('this week friday', $this->date->getTimestamp()) + ) + ), + sprintf( + '%1$s' . "\n" . '%2$s', + _('Samstag'), + date( + 'd.m.Y', + strtotime('this week saturday', $this->date->getTimestamp()) + ) + ), + sprintf( + '%1$s' . "\n" . '%2$s', + _('Sonntag'), + date( + 'd.m.Y', + strtotime('this week sunday', $this->date->getTimestamp()) + ) + ) + ] + ]; + + foreach ($this->data as $row) { + $csv_row = []; + foreach ($row as $i => $cell) { + if ($i == 0) { + $csv_row[0] = $cell; + } else { + $csv_row[$i] = ''; + if ($cell) { + $items = []; + foreach ($cell as $day_item) { + $items[] = $day_item[0] . ': ' . $day_item[1]; + } + $csv_row[$i] = implode("\n\n", $items); + } + } + } + $csv_data[] = $csv_row; + } + + $this->set_content_type('text/csv'); + $this->render_text(array_to_csv($csv_data)); + } + } +} |
