Skip to content
Snippets Groups Projects
room_request.php 105 KiB
Newer Older
<?php

/**
 * request.php - contains Resources_RequestController
 *
 * 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-2019
 * @category    Stud.IP
 * @since       4.5
 */


/**
 * Resources_RequestController contains resource request functionality.
 */
class Resources_RoomRequestController extends AuthenticatedController
{
    public function before_filter(&$action, &$args)
    {
        parent::before_filter($action, $args);

        $this->current_user = User::findCurrent();

Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        $default_filters = [];
        if (in_array($action, ['resolve', 'decline'])) {
            $default_filters['get_only_request_ids'] = true;
            $default_filters['filter_request_id'] = $args[0];
        } elseif ($action === 'planning') {
            $default_filters['request_periods'] ='periodic';
        }

        $this->filter = $this->getFilters($default_filters);
        $this->available_rooms = [];
        if (in_array($action, ['overview', 'planning', 'export_list', 'resolve', 'decline'])) {
            $user_is_global_resource_autor = ResourceManager::userHasGlobalPermission($this->current_user, 'autor');
            if (!RoomManager::userHasRooms($this->current_user, 'autor', true) && !$user_is_global_resource_autor) {
                throw new AccessDeniedException(_('Ihre Berechtigungen an Räumen reichen nicht aus, um die Anfrageliste anzeigen zu können!'));
            }

            $this->available_rooms = RoomManager::getUserRooms($this->current_user, 'autor', true);
            $this->selected_room_ids = [];

Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
            if (!Semester::find($GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE)) {
                $GLOBALS['user']->cfg->MY_COURSES_SELECTED_CYCLE = Semester::findCurrent()->id;
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        }
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        $this->selected_room_ids = [];
        if (isset($this->filter['room_id'])) {
            $room = Resource::find($this->filter['room_id']);
            if (!$room) {
                PageLayout::postError(
                    _('Der gewählte Raum wurde nicht gefunden!')
                );
                return;
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
            $room = $room->getDerivedClassInstance();
            if (!($room instanceof Room)) {
                PageLayout::postError(
                    _('Es wurde kein Raum ausgewählt!')
                );
                return;
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
            if (!$room->userHasPermission($this->current_user, 'autor')) {
                PageLayout::postError(
                    sprintf(
                        _('Die Berechtigungen für den Raum %s sind nicht ausreichend, um die Anfrageliste anzeigen zu können!'),
                        htmlReady($room->name)
                    )
                );
                return;
            }
            $this->selected_room_ids = [$room->id];
        } elseif (isset($this->filter['group'])) {
            //Filter rooms by the selected room group:
            $clipboard = Clipboard::find($this->filter['group']);
            if (!$clipboard) {
                PageLayout::postError(
                    _('Die gewählte Raumgruppe wurde nicht gefunden!')
                );
                return;
            }
            $room_ids = $clipboard->getAllRangeIds('Room');
            if (!$room_ids) {
                PageLayout::postError(
                    _('Die gewählte Raumgruppe enthält keine Räume!')
                );
                return;
            }
            $rooms = Resource::findMany($room_ids);
            foreach ($rooms as $room) {
                $room = $room->getDerivedClassInstance();
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
                if ($room instanceof Room) {
                    $this->selected_room_ids[] = $room->id;
                }
            }
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        } else {
            //No filter for a room or a room group set:
            //Display requests of all available rooms:
            foreach ($this->available_rooms as $room) {
                $this->selected_room_ids[] = $room->id;
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        }
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        // if sorting according to table column was chosen, set the correct
        // sort order (ascending vs descending)
        $sort_value = Request::int('sorting');
        if ($sort_value) {
            $_SESSION[__CLASS__]['sort'] = [
                'by' => $sort_value,
                'dir' => Request::option('sort_order') === 'desc' ? 'desc' : 'asc',
            ];
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed

        $this->entries_per_page = Config::get()->ENTRIES_PER_PAGE;
    }

    /**
     * @return array
     */
    protected function getFilteredRoomRequests()
    {
Moritz Strohm's avatar
Moritz Strohm committed
        $sql = '';
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        if (!empty($this->filter['request_status']) && $this->filter['request_status'] === 'closed') {
Moritz Strohm's avatar
Moritz Strohm committed
            $sql .= "resource_requests.closed IN ('1', '2') ";
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        } elseif (!empty($this->filter['request_status']) && $this->filter['request_status'] === 'denied') {
Moritz Strohm's avatar
Moritz Strohm committed
            $sql .= "resource_requests.closed = '3' ";
        } else {
            $sql .= "resource_requests.closed < '1' ";
        }
        $sql .= "AND (resource_id IN ( :room_ids) ";
        $sql_params = [
            'room_ids' => $this->selected_room_ids
        ];
Moritz Strohm's avatar
Moritz Strohm committed
        if (empty($this->filter['specific_requests'])) {
            $sql .= "OR resource_id IS NULL or resource_id = ''";
        }
        $sql .= ") ";
        if (!empty($this->filter['own_requests'])) {
            $sql .= " AND resource_requests.user_id = :current_user_id ";
            $sql_params['current_user_id'] = $this->current_user->id;
Moritz Strohm's avatar
Moritz Strohm committed
        if (!empty($this->filter['request_periods']) && $this->filter['request_periods'] == 'periodic') {
Till Glöggler's avatar
Till Glöggler committed
            // get rid of requests for single dates AND requests for multiple single dates
            // also check if there exists cycle dates in case it is a request for the whole seminar
Till Glöggler's avatar
Till Glöggler committed
            $sql .= " AND resource_requests.termin_id = ''
            AND NOT EXISTS
            (
                SELECT * FROM resource_request_appointments
                WHERE resource_request_appointments.request_id = resource_requests.id
            )
            AND EXISTS (
                SELECT * FROM seminar_cycle_dates
                WHERE seminar_cycle_dates.seminar_id = resource_requests.course_id
Till Glöggler's avatar
Till Glöggler committed
            )";
Moritz Strohm's avatar
Moritz Strohm committed
        if (!empty($this->filter['request_periods']) && $this->filter['request_periods'] == 'aperiodic') {
            $sql .= " AND (
                resource_requests.termin_id <> ''
                OR EXISTS
                (
                    SELECT * FROM resource_request_appointments
                    WHERE resource_request_appointments.request_id = resource_requests.id
                )
                OR NOT EXISTS (
                    SELECT * FROM seminar_cycle_dates
                    WHERE seminar_cycle_dates.seminar_id = resource_requests.course_id
                )
            )";
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        $institute_id = $this->filter['institute'] ?? null;
        if ($institute_id) {
            $common_seminar_sql = 'resource_requests.course_id IN (
                SELECT seminar_id FROM seminare
                WHERE %1$s
                )';
            $include_children = false;
            if (preg_match('/_withinst$/', $institute_id)) {
                $include_children = true;
                $institute_id = explode('_', $institute_id)[0];
            }
            $institute = Institute::find($institute_id);
            if ($institute instanceof Institute) {
                $institute_ids = [$institute->id];
                if ($institute->isFaculty() && $include_children) {
                    //Get the requests from courses from the faculty
                    //and its institutes.
                    foreach ($institute->sub_institutes as $sub_inst) {
                        $institute_ids[] = $sub_inst->id;
                    }
                }

                if ($sql) {
                    $sql .= ' AND ';
                }
                $sql .= sprintf(
                    $common_seminar_sql,
                    'seminare.institut_id IN ( :institute_ids )'
                );
                $sql_params['institute_ids'] = $institute_ids;
            }
        }

Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        if (
            isset($this->filter['marked'])
            && $this->filter['marked'] < ResourceRequest::MARKING_STATES
        ) {
            if ($sql) {
                $sql .= ' AND ';
            }
            if ($this->filter['marked'] == 0) {
                $sql .= "resource_requests.marked = '0' ";
            } else {
                $sql .= "(resource_requests.marked > '0' && resource_requests.marked < :max_marked) ";
                $sql_params['max_marked'] = ResourceRequest::MARKING_STATES;
            }
        }

        $semester_id = $this->filter['semester'];
        if ($semester_id) {
            $semester = Semester::find($semester_id);
            if ($semester instanceof Semester) {
                if ($sql) {
                    $sql .= ' AND ';
                }
                $sql .= "(
                (resource_requests.termin_id <> '' AND EXISTS (SELECT * FROM termine WHERE termine.termin_id=resource_requests.termin_id AND termine.date BETWEEN :begin AND :semester_end))
                    (resource_requests.metadate_id <> '' AND EXISTS (SELECT * FROM termine WHERE termine.metadate_id=resource_requests.metadate_id AND termine.date BETWEEN :begin AND :semester_end))
                    (resource_requests.termin_id = '' AND resource_requests.metadate_id = '' AND (
                        EXISTS (SELECT * FROM termine JOIN resource_request_appointments ON termine.termin_id = appointment_id WHERE request_id = resource_requests.id AND termine.date BETWEEN :begin AND :semester_end)
                        OR
                        NOT EXISTS (SELECT * FROM resource_request_appointments WHERE request_id = resource_requests.id) AND EXISTS (SELECT * FROM termine WHERE termine.range_id=resource_requests.course_id AND termine.date BETWEEN :begin AND :semester_end)
                    ))
Moritz Strohm's avatar
Moritz Strohm committed
                if (empty($this->filter['request_periods'])) {
                        CAST(resource_requests.begin AS SIGNED) - resource_requests.preparation_time < :semester_end
                        AND resource_requests.end > :begin
                $sql_params['begin'] = max($semester->beginn, time());
                $sql_params['semester_end'] = $semester->ende;
            }
        }
        if (!empty($this->filter['course_type'])) {
            $course_type = explode('_', $this->filter['course_type']);
            if (empty($course_type[1])) {
                $course_types = array_keys(SemClass::getClasses()[$course_type[0]]->getSemTypes());
            } else {
                $course_types = [$course_type[1]];
            }
            $sql .= " AND EXISTS (SELECT * FROM seminare
                    WHERE resource_requests.course_id=seminare.seminar_id
                    AND seminare.status IN(:course_types)) ";
            $sql_params[':course_types'] = $course_types;
        }

        if (!$sql) {
            //No filtering done
            $sql = 'TRUE ';
        }

        $sql .= " GROUP BY resource_requests.id ";

        // if table should be sorted by marking state
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        if (isset($_SESSION[__CLASS__]['sort'])) {
            switch ($_SESSION[__CLASS__]['sort']['by']) {
                case 1:
                    $sql .= " ORDER BY resource_requests.marked ";
                    break;
                case 10:
                    $sql .= " ORDER BY resource_requests.chdate ";
                    break;
                default:
                    $sql .= " ORDER BY mkdate ";
            }
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
            $sql .= $_SESSION[__CLASS__]['sort']['dir'] === 'desc' ? 'DESC' : 'ASC';

        $requests = RoomRequest::findBySql($sql, $sql_params);
        $result = [];
        if (!empty($this->filter['dow'])) {
            $week_days = [$this->filter['dow']];
            foreach ($requests as $request) {
                $time_intervals = $request->getTimeIntervals(true);

                //Check for each time interval if it lies in at least one
                //of the specified week days.
                foreach ($time_intervals as $time_interval) {
                    $interval_weekdays = []; //The weekdays of the time interval.
                    $weekday_begin = date('N', $time_interval['begin']);
                    $weekday_end = date('N', $time_interval['end']);
                    $interval_weekdays[] = $weekday_begin;
                    if (($weekday_end - $weekday_begin) != 0) {
                        $interval_weekdays[] = $weekday_end;
                        $current_day = $weekday_begin;
                        //In case the end lies on a day after the begin
                        //or even in another week, we must loop until
                        //we reached the weekday of the end timestamp.
                        while ($current_day != $weekday_end) {
                            $interval_weekdays[] = $current_day;
                            if ($current_day == 7) {
                                $current_day = 1;
                            } else {
                                $current_day++;
                            }
                        }
                    }
                    $interval_weekdays = array_unique($interval_weekdays);
                    //We have all relevant weekdays and can now check
                    //if the time interval lies in one of the relevant weekdays:
                    foreach ($interval_weekdays as $iwd) {
                        if (in_array($iwd, $week_days)) {
                            //Add the request to the result set. By using the
                            //request-ID as key, we can make sure one request
                            //is only added once to the result set.
                            $result[$request->id] = $request;
                            //Continue to the next request:
                            continue 2;
                        }
                    }
                }
            }
            if (!empty($this->filter['get_only_request_ids'])) {
                return array_keys($result);
            }
        } else {
            $result = $requests;
            if (!empty($this->filter['get_only_request_ids'])) {
                return SimpleCollection::createFromArray($requests)->pluck('id');
            }
        }
        // sort requests according to display table columns not in the resource request db table
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        if (
            isset($_SESSION[__CLASS__]['sort'])
            && $_SESSION[__CLASS__]['sort']['by'] != 1
            && $_SESSION[__CLASS__]['sort']['by'] != 10
        ) {
            $result = $this->sort_request_table($result, $_SESSION[__CLASS__]['sort']['by'], $_SESSION[__CLASS__]['sort']['dir']);
    /**
    * Sorts the resource requests according to columns not belonging to the
    * resource requests db table.
    *
    * @param array $requests array of ResourceRequest objects
    * @param int $sort_variable property according to which the requests should be sorted
    *               values 1 and 10 are database columns (marked state and chdate) and already dealt with
                    2 = lecture number
                    3 = lecture name
                    4 = dozent name
                    5 = room name
                    6 = available seats
                    7 = requesting person
                    8 = type of date
                    9 = priority
    * @param string $order ascending ('asc') or descending ('desc') order
    *
    * @return array sorted array of resource requests
    */
    protected function sort_request_table($requests, int $sort_variable, string $order)
    {
        usort($requests,
            function ($a, $b) use ($sort_variable, $order) {
                $rangeObjA = $a->getRangeObject();
                $rangeObjB = $b->getRangeObject();

                // lecture number
                if ($sort_variable === 2) {
                    if ($order === 'asc') {
                        return strcmp($rangeObjA->veranstaltungsnummer, $rangeObjB->veranstaltungsnummer);
                    } else {
                        return strcmp($rangeObjB->veranstaltungsnummer, $rangeObjA->veranstaltungsnummer);
                    }
                }
                // lecture name
                if ($sort_variable === 3) {
                    if ($order === 'asc') {
                        return strcmp($rangeObjA->name, $rangeObjB->name);
                    } else {
                        return strcmp($rangeObjB->name, $rangeObjA->name);
                    }
                }
                // dozent name
                if ($sort_variable === 4) {
                    $a_dozent_strings = '';
                    foreach ($rangeObjA->getMembersWithStatus('dozent') as $dozent) {
                        $a_dozent_strings .= $dozent->nachname . ', ' . $dozent->vorname;
                    }

                    $b_dozent_strings = '';
                    foreach ($rangeObjB->getMembersWithStatus('dozent') as $dozent) {
                        $b_dozent_strings .= $dozent->nachname . ', ' . $dozent->vorname;
                    }

                    if ($order === 'asc') {
                        return strcmp($a_dozent_strings, $b_dozent_strings);

                    } else {
                        return strcmp($b_dozent_strings, $a_dozent_strings);
                    }

                }
                // room name
                if ($sort_variable === 5) {
                    if ($order === 'asc') {
                        return strcmp($a->resource->name, $b->resource->name);
                    } else {
                        return strcmp($b->resource->name, $a->resource->name);
                    }
                }
                // available seats
                if ($sort_variable === 6) {
                    return ($order === 'asc' ? (intval($a->getProperty('seats')) - intval($b->getProperty('seats'))) :
                                               (intval($b->getProperty('seats')) - intval($a->getProperty('seats'))));
                }
                // requesting person
                if ($sort_variable === 7) {
                    if ($order === 'asc') {
                        return strcmp($a->user->nachname . $a->user->vorname, $b->user->nachname . $b->user->vorname);
                    } else {
                        return strcmp($b->user->nachname . $b->user->vorname, $a->user->nachname . $a->user->vorname);
                    }
                }
                // type
                if ($sort_variable === 8) {
                    if ($order === 'asc') {
                        return strcmp($a->getTypeString(true) . $a->getStartDate()->format('YnjHis'),
                                      $b->getTypeString(true) . $b->getStartDate()->format('YnjHis'));
                    } else {
                        return strcmp($b->getTypeString(true) . $b->getStartDate()->format('YnjHis'),
                                      $a->getTypeString(true) . $a->getStartDate()->format('YnjHis'));
                    }
                }
                // priority
                if ($sort_variable === 9) {
                    if ($order === 'asc') {
                        return (($a->getPriority()) - $b->getPriority());
                    } else {
                        return (($b->getPriority()) - $a->getPriority());
                    }
                }

                return 0;
        });

        return $requests;
    }

    protected function getRoomAvailability(Room $room, $time_intervals = [])
    {
        $availability = [];
        foreach ($time_intervals as $interval) {
            $begin = new DateTime();
            $end = new DateTime();
            $begin->setTimestamp($interval['begin']);
            $end->setTimestamp($interval['end']);
            $availability[] = $room->isAvailable($begin, $end, !empty($interval['booking_id']) ? [$interval['booking_id']] : []);
        return $availability;
    }


    /**
     * Shows all requests. By default, only open requests are shown.
     */
    public function overview_action(int $page = 0)
    {
        if (Navigation::hasItem('/resources/planning/requests_overview')) {
            Navigation::activateItem('/resources/planning/requests_overview');
        }
        PageLayout::setTitle(_('Anfragenliste'));

Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        $this->setupSidebar('overview');
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        $sidebar = Sidebar::get();
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        $relevant_export_filters = [
            'institut_id'               => 'institute',
            'semester_id'               => 'semester',
            'course_type'               => 'course_type',
            'group'                     => 'group',
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
            'room_id'                   => 'room_id',
            'marked'                    => 'marked',
            'request_periods'           => 'request_periods',
            'toggle_specific_requests'  => 'specific_requests',
            'dow'                       => 'dow'
        ];
        $export_url_params = [];
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        foreach ($relevant_export_filters as $param => $filter_name) {
            if (isset($this->filter[$filter_name])) {
                $export_url_params[$param] = $this->filter[$filter_name];
            }
        }
        $export = new ExportWidget();
        $export->addLink(
            _('Gefilterte Anfragen'),
            $this->export_listURL($export_url_params),
        );
        $export->addLink(
            _('Alle Anfragen'),
            $this->export_listURL(),
        );
        $sidebar->addWidget($export);

        $requests = $this->getFilteredRoomRequests();
        $this->count_requests = count($requests);
        $requests = array_slice(
            $requests,
            $this->entries_per_page * ($page),
            $this->entries_per_page
        );

        $this->pagination = Pagination::create(
            $this->count_requests,
            $page,
            $this->entries_per_page
        );
        $this->requests = $requests;
Jan-Hendrik Willms's avatar
Jan-Hendrik Willms committed
        $this->sort_var = $_SESSION[__CLASS__]['sort']['by'] ?? '';
        $this->sort_order = $_SESSION[__CLASS__]['sort']['dir'] ?? '';
Moritz Strohm's avatar
Moritz Strohm committed

Moritz Strohm's avatar
Moritz Strohm committed
        $this->request_status = $this->filter['request_status'] ?? '';
    }

    public function index_action($request_id = null)
    {
        $this->request = ResourceRequest::find($request_id);
        if (!$this->request) {
            PageLayout::postError(
                _('Die angegebene Anfrage wurde nicht gefunden!')
            );
            return;
        }
        $this->request = $this->request->getDerivedClassInstance();
        $this->resource = $this->request->resource;
        if (!$this->resource) {
            PageLayout::postError(
                _('Die angegebene Ressource wurde nicht gefunden!')
            );
            return;
        }
        $this->resource = $this->resource->getDerivedClassInstance();

        PageLayout::setTitle(
            sprintf(
                _('%s: Details zur Anfrage'),
                $this->resource->getFullName()
            )
        );
    }

    /**
     * This action handles resource requests that are not bound
     * to a course or another Stud.IP object.
     */
    public function add_action($resource_id = null)
    {
        if (!Config::get()->RESOURCES_ALLOW_ROOM_REQUESTS) {
            throw new AccessDeniedException(
                _('Das Erstellen von Raumanfragen ist nicht erlaubt!')
            );
        }
        $this->resource = null;
        $this->request = null;
        $this->show_form = false;
        $this->resource = Resource::find($resource_id);
        if (!$this->resource) {
            PageLayout::postError(
                _('Die angegebene Ressource wurde nicht gefunden!')
            );
            return;
        }
        $this->resource = $this->resource->getDerivedClassInstance();
        PageLayout::setTitle(
            sprintf(
                _('%s: Neue Anfrage erstellen'),
                $this->resource->getFullName()
            )
        );

        if (!$this->resource->userHasRequestRights($this->current_user)) {
            throw new AccessDeniedException();
        }
        $this->form_action_link = $this->link_for('resources/room_request/add/' . $this->resource->id);
        if (!($this->resource instanceof Room)) {
            PageLayout::postError(
                _('Die angegebene Ressource ist kein Raum!')
            );
            return;
        }
        if (!$this->resource->requestable) {
            PageLayout::postError(
                _('Die angegebene Ressource kann nicht angefragt werden!')
            );
            return;
        }


        //Check if the resource is a room and if the room is part of a
        //separable room.
        if ($this->resource instanceof Room) {
            $separable_room = SeparableRoom::findByRoomPart($this->resource);
            if ($separable_room instanceof SeparableRoom) {
                //We must display a warning. But first we check,
                //if we can get the other room parts so that
                //we can make a more informative message.

                $other_room_parts = $separable_room->findOtherRoomParts(
                    [$this->resource]
                );

                if ($other_room_parts) {

                    $other_room_links = [];
                    foreach ($other_room_parts as $room_part) {
                        $other_room_links[] = sprintf(
                            '<a target="_blank" href="%1$s">%2$s</a>',
                            $room_part->getActionLink('show'),
                            htmlReady($room_part->name)
                        );
                    }
                    PageLayout::postInfo(
                        sprintf(
                            _('Der Raum %1$s ist ein Teilraum des Raumes %2$s. Weitere Teilräume sind:'),
                            htmlReady($this->resource->name),
                            htmlReady($separable_room->name)
                        ),
                        $other_room_links
                    );
                } else {
                    PageLayout::postInfo(
                        sprintf(
                            _('Der Raum %1$s ist ein Teilraum des Raumes %2$s.'),
                            htmlReady($this->resource->name),
                            htmlReady($separable_room->name)
                        )
                    );
                }
            }
        }

        $begin = new DateTime();
        $end = new DateTime();

        //Check if a begin and end timestamp are specified:
        if (!Request::submitted('save') && Request::submitted('begin')
            && Request::submitted('end')) {
            $begin->setTimestamp(Request::get('begin'));
            $end->setTimestamp(Request::get('end'));
        } else {
            //Assume a date is requested for tomorrow.
            //Round the current time to hours and substract
            //two hours from $begin.
            $begin->add(new DateInterval('P1D'));
            $begin->setTime(
                $begin->format('H'),
                0
            );
            $end = clone($begin);
            $begin->sub(new DateInterval('PT2H'));
        }

        $config = Config::get();
        $this->begin_date_str = $begin->format('d.m.Y');
        $this->begin_time_str = $begin->format('H:i');
        $this->end_date_str = $end->format('d.m.Y');
        $this->end_time_str = $end->format('H:i');
        $this->preparation_time = 0;
        $this->max_preparation_time = $config->RESOURCES_MAX_PREPARATION_TIME;
        $this->comment = '';

        $this->show_form = true;

        if (Request::submitted('save')) {
            //Get the requested time range and check if the resource
            //is available in that time range. If so, create a new
            //resource request (or a room request if the resource
            //is a room).

            $this->begin_date_str = Request::get('begin_date');
            $this->begin_time_str = Request::get('begin_time');
            $this->end_date_str = Request::get('end_date');
            $this->end_time_str = Request::get('end_time');
David Siegfried's avatar
David Siegfried committed
            $this->preparation_time = Request::int('preparation_time', 0);

            if (!$this->begin_date_str) {
                PageLayout::postError(
                    _('Es wurde kein Startdatum angegeben!')
                );
                return;
            }
            if (!$this->begin_time_str) {
                PageLayout::postError(
                    _('Es wurde kein Startzeitpunkt angegeben!')
                );
                return;
            }
            if (!$this->end_date_str) {
                PageLayout::postError(
                    _('Es wurde kein Enddatum angegeben!')
                );
                return;
            }
            if (!$this->end_time_str) {
                PageLayout::postError(
                    _('Es wurde kein Endzeitpunkt angegeben!')
                );
                return;
            }
            if ($this->preparation_time > $this->max_preparation_time) {
                PageLayout::postError(
                    sprintf(
                        _('Die eingegebene Rüstzeit überschreitet das erlaubte Maximum von %d Minuten!'),
                        $this->max_preparation_time
                    )
                );
            }

            //Comment is optional.
            $this->comment = Request::get('comment');

            //Convert the date and time strings to DateTime objects:

            $begin_date_arr = explode('.', $this->begin_date_str);
            $begin_time_arr = explode(':', $this->begin_time_str);
            $end_date_arr = explode('.', $this->end_date_str);
            $end_time_arr = explode(':', $this->end_time_str);

            $new_begin = new DateTime();
            $new_begin->setDate(
                $begin_date_arr[2],
                $begin_date_arr[1],
                $begin_date_arr[0]
            );
            $new_begin->setTime(
                $begin_time_arr[0],
                $begin_time_arr[1],
                $begin_time_arr[2] ?? 0
            );
            $new_end = new DateTime();
            $new_end->setDate(
                $end_date_arr[2],
                $end_date_arr[1],
                $end_date_arr[0]
            );
            $new_end->setTime(
                $end_time_arr[0],
                $end_time_arr[1],
                $end_time_arr[2] ?? 0
            );

            try {
                //All checks are done in Resource::createSimpleRequest.
                $request = $this->resource->createSimpleRequest(
                    $this->current_user,
                    $new_begin,
                    $new_end,
                    $this->comment,
                    $this->preparation_time * 60
                );

                if ($request) {
                    $this->show_form = false;
                    PageLayout::postSuccess(
                        _('Die Anfrage wurde gespeichert.')
                    );
                }
            } catch (Exception $e) {
                PageLayout::postError($e->getMessage());
            }
            //All other exceptions are not caught since they are not thrown
            //in Resource::createSimpleRequest.
        }
    }


    public function edit_action($request_id = null)
    {
        $this->resource = null;
        $this->request = null;
        $this->show_form = false;

        $this->request = ResourceRequest::find($request_id);
        if (!$this->request) {
            PageLayout::postError(
                _('Die angegebene Anfrage wurde nicht gefunden!')
            );
            return;
        }
        $this->resource = $this->request->resource;
        if (!$this->resource) {
            PageLayout::postError(
                _('Die angegebene Ressource wurde nicht gefunden!')
            );
            return;
        }
        $this->resource = $this->resource->getDerivedClassInstance();

        PageLayout::setTitle(
            sprintf(
                _('%s: Neue Anfrage erstellen'),
                $this->resource->getFullName()
            )
        );
        $this->form_action_link = $this->link_for(
            'resources/room_request/edit/' . $this->request->id
        );

        if (!($this->resource instanceof Room)) {
            PageLayout::postError(
                _('Die angegebene Ressource ist kein Raum!')
            );
            return;
        }

        //Since all Stud.IP users are allowed to create requests,
        //there is no restriction for creating requests.
        $user_may_edit_request = $this->resource->userHasPermission(
                $this->current_user,
            ) || $this->request->user_id === $this->current_user->id;

        if (!$user_may_edit_request) {
            throw new AccessDeniedException();
        }

        //Check if the resource is a room and if the room is part of a
        //separable room.
        if ($this->resource instanceof Room) {
            $separable_room = SeparableRoom::findByRoomPart($this->resource);
            if ($separable_room instanceof SeparableRoom) {
                //We must display a warning. But first we check,
                //if we can get the other room parts so that
                //we can make a more informative message.

                $other_room_parts = $separable_room->findOtherRoomParts(
                    [$this->resource]
                );

                if ($other_room_parts) {

                    $other_room_links = [];
                    foreach ($other_room_parts as $room_part) {
                        $other_room_links[] = sprintf(
                            '<a target="_blank" href="%1$s">%2$s</a>',
                            $room_part->getActionLink('show'),
                            htmlReady($room_part->name)
                        );
                    }
                    PageLayout::postInfo(
                        sprintf(
                            _('Der Raum %1$s ist ein Teilraum des Raumes %2$s. Weitere Teilräume sind:'),
                            htmlReady($this->resource->name),
                            htmlReady($separable_room->name)
                        ),
                        $other_room_links
                    );
                } else {
                    PageLayout::postInfo(
                        sprintf(
                            _('Der Raum %1$s ist ein Teilraum des Raumes %2$s.'),
                            htmlReady($this->resource->name),
                            htmlReady($separable_room->name)
                        )
                    );
                }
            }
        }

        $begin = new DateTime();
        $end = new DateTime();

        //Get begin and end date from the request:
        $begin->setTimestamp($this->request->begin);
        $end->setTimestamp($this->request->end);

        $config = Config::get();
        $this->begin_date_str = $begin->format('d.m.Y');
        $this->begin_time_str = $begin->format('H:i');
        $this->end_date_str = $end->format('d.m.Y');
        $this->end_time_str = $end->format('H:i');
        $this->preparation_time = 0;
        $this->max_preparation_time = $config->RESOURCES_MAX_PREPARATION_TIME;
        $this->comment = '';
        $this->comment = $this->request->comment;
        $this->preparation_time = intval($this->request->preparation_time / 60);

        $this->show_form = true;

        if (Request::submitted('save')) {
            //Get the requested time range and check if the resource
            //is available in that time range. If so, create a new
            //resource request (or a room request if the resource
            //is a room).

            $this->begin_date_str = Request::get('begin_date');
            $this->begin_time_str = Request::get('begin_time');
            $this->end_date_str = Request::get('end_date');
            $this->end_time_str = Request::get('end_time');
            $this->preparation_time = Request::get('preparation_time');

            if (!$this->begin_date_str) {
                PageLayout::postError(
                    _('Es wurde kein Startdatum angegeben!')
                );
                return;
            }
            if (!$this->begin_time_str) {
                PageLayout::postError(
                    _('Es wurde kein Startzeitpunkt angegeben!')
                );
                return;
            }
            if (!$this->end_date_str) {
                PageLayout::postError(
                    _('Es wurde kein Enddatum angegeben!')
                );
                return;
            }
            if (!$this->end_time_str) {
                PageLayout::postError(
                    _('Es wurde kein Endzeitpunkt angegeben!')
                );
                return;
            }
            if ($this->preparation_time > $this->max_preparation_time) {
                PageLayout::postError(
                    sprintf(
                        _('Die eingegebene Rüstzeit überschreitet das erlaubte Maximum von %d Minuten!'),
                        $this->max_preparation_time
                    )
                );
            }

            //Comment is optional.
            $this->comment = Request::get('comment');

            //Convert the date and time strings to DateTime objects:

            $begin_date_arr = explode('.', $this->begin_date_str);
            $begin_time_arr = explode(':', $this->begin_time_str);
            $end_date_arr = explode('.', $this->end_date_str);
            $end_time_arr = explode(':', $this->end_time_str);

            $new_begin = new DateTime();
            $new_begin->setDate(
                $begin_date_arr[2],
                $begin_date_arr[1],
                $begin_date_arr[0]
            );
            $new_begin->setTime(
                $begin_time_arr[0],
                $begin_time_arr[1],
            );
            $new_end = new DateTime();
            $new_end->setDate(
                $end_date_arr[2],
                $end_date_arr[1],
                $end_date_arr[0]
            );
            $new_end->setTime(
                $end_time_arr[0],
                $end_time_arr[1],
            );

            $this->request->begin = $new_begin->getTimestamp();
            $this->request->end = $new_end->getTimestamp();
            $this->request->comment = $this->comment;
            $this->request->preparation_time = $this->preparation_time * 60;

            if ($this->request->isDirty()) {
                $successfully_stored = $this->request->store();
            } else {
                $successfully_stored = true;