Skip to content
Snippets Groups Projects
Select Git revision
  • 2835b3b966dc60ebcf85e7bb77be9d85e24d9920
  • main default protected
  • step-3263
  • feature/plugins-cli
  • feature/vite
  • step-2484-peerreview
  • biest/issue-5051
  • tests/simplify-jsonapi-tests
  • fix/typo-in-1a70031
  • feature/broadcasting
  • database-seeders-and-factories
  • feature/peer-review-2
  • feature-feedback-jsonapi
  • feature/peerreview
  • feature/balloon-plus
  • feature/stock-images-unsplash
  • tic-2588
  • 5.0
  • 5.2
  • biest/unlock-blocks
  • biest-1514
21 results

schedule.php

Blame
  • Forked from Stud.IP / Stud.IP
    802 commits behind the upstream repository.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    schedule.php 26.85 KiB
    <?php
    
    /**
     * schedule.php - Calender schedule controller
     *
     * 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
     * @category    Stud.IP
     * @package     calender
     * @since       6.0
     */
    class Calendar_ScheduleController extends AuthenticatedController
    {
        public function before_filter(&$action, &$args)
        {
            parent::before_filter($action, $args);
    
            if (!Context::isCourse() && Navigation::hasItem('/calendar')) {
                Navigation::activateItem('/calendar');
            }
        }
    
        public function index_action()
        {
            PageLayout::setTitle(_('Stundenplan'));
    
            if (Navigation::hasItem('/calendar/schedule')) {
                Navigation::activateItem('/calendar/schedule');
            }
    
            $show_hidden = Request::bool('show_hidden', false);
    
            //Handle the selected semester and create a Fullcalendar instance.
    
            $this->semester = null;
            if (Request::submitted('semester_id')) {
                $this->semester = Semester::find(Request::option('semester_id'));
                if ($this->semester) {
                    //Store the new semester-ID in the session:
                    $_SESSION['schedule_semester_id'] = $this->semester->id;
                }
            }
            if (!$this->semester) {
                //Load the semester from the session:
                $semester_id = $_SESSION['schedule_semester_id'] ?? '';
                if ($semester_id) {
                    $this->semester = Semester::find($semester_id);
                } else {
                    $this->semester = Semester::findCurrent();
                }
            }
    
            //Build the sidebar:
    
            $sidebar = Sidebar::get();
    
            //Add the semester selector widget first:
            $semester_widget = new SemesterSelectorWidget(
                $this->indexURL(['show_hidden' => $show_hidden ?: null])
            );
            $semester_widget->setSelection($semester->id ?? '');
            $sidebar->addWidget($semester_widget);
    
            //Then add the actions for the action widget:
            $actions = new ActionsWidget();
            $actions->addLink(
                _('Neuer Termin'),
                $this->url_for('calendar/schedule/entry/add'),
                Icon::create('add'),
                ['data-dialog' => 'size=auto']
            );
            if ($show_hidden) {
                $actions->addLink(
                    _('Ausgeblendete Veranstaltungen verstecken'),
                    $this->indexURL(['semester_id' => Request::get('semester_id')]),
                    Icon::create('visibility-visible')
                )->asButton();
            } else {
                $actions->addLink(
                    _('Ausgeblendete Veranstaltungen anzeigen'),
                    $this->indexURL([
                        'show_hidden' => true,
                        'semester_id' => Request::get('semester_id'),
                    ]),
                    Icon::create('visibility-invisible')
                )->asButton();
            }
    
            $actions->addLink(
                _('Drucken'),
                '#',
                Icon::create('print'),
                ['onclick' => 'window.print(); return false;']
            );
            $actions->addLink(
                _('Einstellungen'),
                $this->url_for('calendar/schedule/settings'),
                Icon::create('settings'),
                ['data-dialog' => 'size=auto;reload-on-close']
            );
            $sidebar->addWidget($actions);
    
            $schedule_settings = UserConfig::get()->getValue('SCHEDULE_SETTINGS');
            $size = $schedule_settings['size'] ?? 'medium';
            if (Request::submitted('size')) {
                $size = Request::option('size');
                if (in_array($size, ['small', 'medium', 'large'])) {
                    //Set the new size in the schedule settings:
                    $schedule_settings['size'] = $size;
                    UserConfig::get()->store('SCHEDULE_SETTINGS', $schedule_settings);
                } else {
                    $size = 'medium';
                }
            }
            $views = new ViewsWidget();
            $views->setTitle(_('Größe'));
            $views->addLink(
                _('Klein'),
                $this->url_for('calendar/schedule/index', ['size' => 'small'])
            )->setActive($size === 'small');
            $views->addLink(
                _('Mittel'),
                $this->url_for('calendar/schedule/index', ['size' => 'medium'])
            )->setActive($size === 'medium');
            $views->addLink(
                _('Groß'),
                $this->url_for('calendar/schedule/index', ['size' => 'large'])
            )->setActive($size === 'large');
            $sidebar->addWidget($views);
    
            $fullcalendar = \Studip\Calendar\Helper::getScheduleFullcalendar(
                $semester->id ?? '',
                Request::bool('show_hidden', false)
            );
            $fullcalendar->setResponsiveDefaultView('timeGridDay');
            $this->fullcalendar = $fullcalendar->render();
        }
    
        public function data_action()
        {
            //Fullcalendar sets the week time range in which to put the course dates
            //of the semester. Therefore, start and end are handled in here.
            $begin = Request::getDateTime('start', \DateTime::RFC3339);
            $end = Request::getDateTime('end', \DateTime::RFC3339);
            if (!($begin instanceof \DateTime) || !($end instanceof \DateTime)) {
                //No time range specified.
                throw new InvalidArgumentException('Invalid parameters!');
            }
    
            $result = [];
    
            $semester_id = Request::option('semester_id');
            $semester = Semester::find($semester_id);
            $show_hidden = Request::bool('show_hidden', false);
    
            if ($semester) {
                //Get all regular course dates for that semester:
                $cycle_dates = SeminarCycleDate::findBySql(
                    'JOIN `termine` USING (`metadate_id`)
                     JOIN `seminare` USING (`seminar_id`)
                    WHERE
                    `seminar_id` IN (
                        SELECT `seminar_id` FROM `seminar_user`
                        WHERE `user_id` = :user_id
                        UNION
                        SELECT `course_id` FROM `schedule_courses`
                        WHERE `user_id` = :user_id
                    )
                    AND
                    (
                    `termine`.`date` BETWEEN :begin AND :end
                    OR `termine`.`end_time` BETWEEN :begin AND :end
                    )
                    GROUP BY `metadate_id`',
                    [
                        'user_id' => $GLOBALS['user']->id,
                        'begin' => $semester->beginn,
                        'end' => $semester->ende
                    ]
                );
    
                foreach ($cycle_dates as $cycle_date) {
                    //Calculate a fake begin and end that lies in the week
                    //fullcalendar has specified.
                    $fake_begin = clone $begin;
                    $fake_end = clone $begin;
                    if ($cycle_date->weekday > 1) {
                        $fake_begin = $fake_begin->add(new DateInterval('P' . ($cycle_date->weekday - 1) . 'D'));
                        $fake_end = $fake_end->add(new DateInterval('P' . ($cycle_date->weekday - 1) . 'D'));
                    }
                    $start_time_parts = explode(':', $cycle_date->start_time);
                    $end_time_parts = explode(':', $cycle_date->end_time);
                    $fake_begin->setTime(
                        $start_time_parts[0],
                        $start_time_parts[1],
                        $start_time_parts[2]
                    );
                    $fake_end->setTime(
                        $end_time_parts[0],
                        $end_time_parts[1],
                        $end_time_parts[2]
                    );
    
                    $schedule_course = ScheduleCourseDate::findOneBySQL(
                        '`course_id` = :course_id AND `user_id` = :user_id',
                        [
                            'course_id' => $cycle_date->seminar_id,
                            'user_id' => $GLOBALS['user']->id
                        ]
                    );
                    $is_hidden = $schedule_course && !$schedule_course->visible;
                    if (!$show_hidden && $is_hidden) {
                        //The regular date belongs to a course that has been hidden in the schedule.
                        //The flag to include hidden courses is not set which means that the regular
                        //date shall not be included.
                        continue;
                    }
    
                    //Get the course colour:
                    $course_membership = CourseMember::findOneBySQL(
                        '`seminar_id` = :course_id AND `user_id` = :user_id',
                        [
                            'course_id' => $cycle_date->seminar_id,
                            'user_id' => $GLOBALS['user']->id
                        ]
                    );
    
                    $event_classes = ['schedule'];
                    $event_title   = $cycle_date->course->getFullName('number-name');
    
                    if ($course_membership) {
                        $event_classes[] = sprintf('course-color-%u', $course_membership->gruppe);
    
                        $lecturer_names = array_map(
                            fn($lecturer) => $lecturer->user->nachname,
                            CourseMember::findByCourseAndStatus($course_membership->seminar_id, 'dozent')
                        );
                        sort($lecturer_names);
                        $event_title = studip_interpolate(
                            '%{course_name} (%{lecturer_names})',
                            [
                                'course_name'    => $cycle_date->course->getFullName('number-name'),
                                'lecturer_names' => implode(', ', $lecturer_names)
                            ]
                        );
                    } elseif ($schedule_course) {
                        $event_classes[] = 'marked-course';
                        $event_title = studip_interpolate(
                            _('%{course_name} (vorgemerkt)'),
                            ['course_name' => $cycle_date->course->getFullName()]
                        );
                    }
    
                    $event_icon = '';
                    if ($schedule_course && !$course_membership) {
                        $event_icon = 'tag';
                    } elseif ($show_hidden && $is_hidden) {
                        $event_icon = 'visibility-invisible';
                        $event_classes[] = 'hidden-course';
                    }
    
                    $event = new \Studip\Calendar\EventData(
                        $fake_begin,
                        $fake_end,
                        $event_title,
                        $event_classes,
                        '',
                        '',
                        false,
                        'SeminarCycleDate',
                        $cycle_date->id,
                        '',
                        '',
                        'course',
                        $cycle_date->seminar_id,
                        [
                            'show' => $this->url_for('calendar/schedule/course_info/' . $cycle_date->seminar_id)
                        ],
                        [],
                        $event_icon ?: ''
                    );
    
                    $result[] = $event->toFullcalendarEvent();
                }
            }
    
            //Add all schedule entries to the result set:
            $weekly_dates = ScheduleEntry::findByUser_id($GLOBALS['user']->id);
            foreach ($weekly_dates as $date) {
                $event_data = $date->toEventData($GLOBALS['user']->id);
                //Disable fullcalendar drag & drop actions:
                $event_data->editable = false;
                $result[] = $event_data->toFullcalendarEvent();
            }
    
            $this->render_json($result);
        }
    
        /**
         * This action handles adding and editing schedule entries.
         *
         * @param string $entry_id The ID of the entry to be modified. In case the ID is set to "add", a new entry
         *     will be created. In all other cases, an existing entry will be loaded.
         */
        public function entry_action(string $entry_id)
        {
            $this->entry = null;
            if ($entry_id === 'add') {
                //Add mode
                $this->entry = new ScheduleEntry();
                $this->entry->user_id = $GLOBALS['user']->id;
                if (!Request::submitted('save')) {
                    //Provide good default values:
                    $this->entry->colour_id = 1;
                    if (Request::submitted('start')) {
                        //String format
                        $this->entry->dow = Request::int('dow',date('N'));
                        $this->entry->setFormattedStart(Request::get('start', date('H:00', strtotime('+1 hour'))));
                        $this->entry->setFormattedEnd(Request::get('end', date('H:00', strtotime('+2 hours'))));
                    } elseif (Request::submitted('begin')) {
                        //Fullcalendar: Timestamps
                        $begin = Request::get('begin');
                        $end   = Request::get('end');
                        if ($begin && $end) {
                            $this->entry->dow = intval(date('N', $begin));
                            $this->entry->setFormattedStart(date('H:i', $begin));
                            $this->entry->setFormattedEnd(date('H:i', $end));
                        }
                    } else {
                        $begin = time() + 3600;
                        $end   = $begin + 3600;
                        $this->entry->dow = intval(date('N', $begin));
                        $this->entry->setFormattedStart(date('H:00', $begin));
                        $this->entry->setFormattedEnd(date('H:00', $end));
                    }
                }
                PageLayout::setTitle(_('Neuer Termin'));
            } else {
                //Edit mode
                $this->entry = ScheduleEntry::find($entry_id);
                if (!$this->entry) {
                    PageLayout::postError(_('Der Termin wurde nicht gefunden.'));
                }
                if (!$this->entry->isWritable($GLOBALS['user']->id)) {
                    throw new AccessDeniedException(_('Sie dürfen diesen Termin nicht bearbeiten!'));
                }
                PageLayout::setTitle($this->entry->toString());
            }
    
            if (Request::submitted('save')) {
                CSRFProtection::verifyUnsafeRequest();
                $this->saveEntry($entry_id);
            } elseif (Request::submitted('delete')) {
                CSRFProtection::verifyUnsafeRequest();
                $this->deleteEntry();
            }
        }
    
        /**
         * Handles storing a schedule entry.
         */
        public function save_entry_action(string $entry_id)
        {
            $this->entry = null;
            if ($entry_id === 'add') {
                //Add mode
                $this->entry = new ScheduleEntry();
                $this->entry->user_id = $GLOBALS['user']->id;
                PageLayout::setTitle(_('Neuer Termin'));
            } else {
                //Edit mode
                $this->entry = ScheduleEntry::find($entry_id);
                if (!$this->entry) {
                    PageLayout::postError(_('Der Termin wurde nicht gefunden.'));
                }
                if (!$this->entry->isWritable($GLOBALS['user']->id)) {
                    throw new AccessDeniedException(_('Sie dürfen diesen Termin nicht bearbeiten!'));
                }
                PageLayout::setTitle($this->entry->toString());
            }
    
            $this->entry->dow = Request::int('dow', date('N'));
            $this->entry->setFormattedStart(Request::get('start'));
            $this->entry->setFormattedEnd(Request::get('end'));
            $this->entry->colour_id = Request::get('colour_id') ?? '';
            $this->entry->label   = Request::get('label', '');
            $this->entry->content = Request::get('content', '');
    
            if ($this->entry->start_time >= $this->entry->end_time) {
                PageLayout::postError(_('Der Startzeitpunkt darf nicht nach dem Endzeitpunkt liegen!'));
                $this->redirect('calendar/schedule/entry/' . $entry_id);
                return;
            }
    
            if ($this->entry->store() !== false) {
                if ($entry_id === 'add') {
                    PageLayout::postSuccess(_('Der Termin wurde hinzugefügt.'));
                } else {
                    PageLayout::postSuccess(_('Der Termin wurde bearbeitet.'));
                }
                if (Request::isDialog()) {
                    $this->response->add_header('X-Dialog-Close', '1');
                } else {
                    $this->redirect('calendar/schedule/index');
                }
            } else {
                if ($entry_id === 'add') {
                    PageLayout::postError(_('Der Termin konnte nicht hinzugefügt werden.'));
                } else {
                    PageLayout::postError(_('Der Termin konnte nicht bearbeitet werden.'));
                }
                $this->redirect('calendar/schedule/entry/' . $entry_id);
            }
            $this->render_nothing();
        }
    
        /**
         * Handles deleting a schedule entry.
         */
        public function delete_entry_action(string $entry_id)
        {
            CSRFProtection::verifyUnsafeRequest();
            $this->entry = ScheduleEntry::find($entry_id);
            if (!$this->entry) {
                PageLayout::postError(_('Der Termin wurde nicht gefunden.'));
            }
            if (!$this->entry->isWritable($GLOBALS['user']->id)) {
                throw new AccessDeniedException(_('Sie dürfen diesen Termin nicht bearbeiten!'));
            }
            if ($this->entry->delete()) {
                PageLayout::postSuccess(_('Der Termin wurde gelöscht.'));
            } else {
                PageLayout::postError(_('Der Termin konnte nicht gelöscht werden.'));
            }
            if (Request::isDialog()) {
                $this->response->add_header('X-Dialog-Close', '1');
            } else {
                $this->redirect('calendar/schedule/index');
            }
            $this->render_nothing();
        }
    
        /**
         * Displays information about a course in the schedule.
         *
         * @param string $course_id The ID of the course.
         */
        public function course_info_action(string $course_id)
        {
            $this->course = Course::find($course_id);
            if (!$this->course) {
                PageLayout::postError(_('Die Veranstaltung wurde nicht gefunden.'));
                return;
            }
            $this->membership = CourseMember::findOneBySQL(
                '`seminar_id` = :course_id AND `user_id` = :user_id',
                [
                    'course_id' => $this->course->id,
                    'user_id' => $GLOBALS['user']->id
                ]
            );
            $this->schedule_course_entry = ScheduleCourseDate::findOneBySQL(
                '`course_id` = :course_id AND `user_id` = :user_id',
                [
                    'course_id' => $this->course->id,
                    'user_id' => $GLOBALS['user']->id
                ]
            );
    
            PageLayout::setTitle($this->course->getFullName());
        }
    
        /**
         * Hides a course in the schedule.
         *
         * @param string $course_id The ID of the course.
         */
        public function hide_course_action(string $course_id)
        {
            CSRFProtection::verifyUnsafeRequest();
            $success = false;
    
            $course = Course::find($course_id);
            if ($course) {
                $this->membership = CourseMember::findOneBySQL(
                    '`seminar_id` = :course_id AND `user_id` = :user_id',
                    [
                        'course_id' => $course->id,
                        'user_id' => $GLOBALS['user']->id
                    ]
                );
    
                //Hide the course.
                if ($this->membership) {
                    //Hide the course in the schedule by creating a new schedule course entry
                    //with the visibility set to 0:
                    $entry = ScheduleCourseDate::findOneBySQL(
                        '`user_id` = :user_id AND `course_id` = :course_id',
                        ['user_id' => $GLOBALS['user']->id, 'course_id' => $course->id]
                    );
                    if (!$entry) {
                        $entry = new ScheduleCourseDate();
                        $entry->user_id = $GLOBALS['user']->id;
                        $entry->course_id = $course->id;
                        $entry->metadate_id = '';
                    }
                    $entry->visible = false;
                    $success = $entry->store() !== false;
                } else {
                    //Remove the entry of the marked course from the schedule.
                    $success = ScheduleCourseDate::deleteBySQL(
                            '`user_id` = :user_id AND `course_id` = :course_id',
                            ['user_id' => $GLOBALS['user']->id, 'course_id' => $course->id]
                        ) > 0;
                }
            }
            if ($success) {
                if (Request::isDialog()) {
                    $this->response->add_header('X-Dialog-Close', '1');
                } else {
                    $this->redirect('calendar/schedule/index');
                }
            }
            $this->render_nothing();
        }
    
        /**
         * Makes a hidden course visible again in the schedule.
         *
         * @param string $course_id The ID of the course.
         */
        public function show_course_action(string $course_id)
        {
            CSRFProtection::verifyUnsafeRequest();
            $success = false;
    
            $course = Course::find($course_id);
            if ($course) {
                //Make a hidden course visible again.
                $entry = ScheduleCourseDate::findOneBySQL(
                    '`user_id` = :user_id AND `course_id` = :course_id',
                    ['user_id' => $GLOBALS['user']->id, 'course_id' => $course_id]
                );
                if ($entry) {
                    $entry->visible = true;
                    $success = $entry->store() !== false;
                } else {
                    $success = true;
                }
                //In case no entry exists, the course is not hidden since an entry in schedule_courses
                //must exist with its visible set to zero to make a course disappear from the schedule.
            }
            if ($success) {
                if (Request::isDialog()) {
                    $this->response->add_header('X-Dialog-Close', '1');
                } else {
                    $this->redirect('calendar/schedule/index');
                }
            }
            $this->render_nothing();
        }
    
        /**
         * Saves the data that are specific to displaying a course in the schedule.
         * Currently, this means saving only the colour of the course.
         *
         * @param string $course_id The ID of the course.
         */
        public function save_course_info_action(string $course_id)
        {
            CSRFProtection::verifyUnsafeRequest();
            $success = false;
    
            $course = Course::find($course_id);
            if ($course) {
                $this->membership = CourseMember::findOneBySQL(
                    '`seminar_id` = :course_id AND `user_id` = :user_id',
                    [
                        'course_id' => $course->id,
                        'user_id' => $GLOBALS['user']->id
                    ]
                );
                if (!$this->membership) {
                    throw new AccessDeniedException();
                }
                //Save the selected group.
                $selected_groups = Request::getArray('gruppe');
                if (array_key_exists($course->id, $selected_groups)) {
                    $this->membership->gruppe = $selected_groups[$course->id] ?? '0';
                }
                $success = $this->membership->store() !== false;
            }
            if ($success) {
                PageLayout::postSuccess(_('Die Farbe der Veranstaltung wurde geändert.'));
            } else {
                PageLayout::postError(_('Die Farbe der Veranstaltung konnte nicht geändert werden.'));
            }
            if ($success) {
                if (Request::isDialog()) {
                    $this->response->add_header('X-Dialog-Close', '1');
                } else {
                    $this->redirect('calendar/schedule/index');
                }
            }
            $this->render_nothing();
        }
    
        public function mark_course_action(string $course_id)
        {
            $course = Course::find($course_id);
            if ($course->isStudygroup()) {
                throw new AccessDeniedException();
            }
            $entry = ScheduleCourseDate::findOneBySQL(
                '`course_id` = :course_id AND `user_id` = :user_id',
                [
                    'course_id' => $course_id,
                    'user_id'   => $GLOBALS['user']->id
                ]
            );
            if ($entry) {
                PageLayout::postInfo(_('Die Veranstaltung wurde bereits zum Stundenplan hinzugefügt.'));
            } else {
                $entry = new ScheduleCourseDate();
                $entry->course_id   = $course->id;
                $entry->user_id     = $GLOBALS['user']->id;
                $entry->metadate_id = '';
                $entry->visible     = true;
                if ($entry->store() !== false) {
                    PageLayout::postSuccess(_('Die Veranstaltung wurde zum Stundenplan hinzugefügt.'));
                } else {
                    PageLayout::postError(_('Die Veranstaltung konnte nicht zum Stundenplan hinzugefügt werden.'));
                }
            }
            $this->redirect('calendar/schedule/index');
        }
    
        /**
         * Shows the settings dialog for the schedule.
         */
        public function settings_action()
        {
            $user_config = UserConfig::get($GLOBALS['user']->id);
            $this->schedule_settings = $user_config->getValue('SCHEDULE_SETTINGS');
    
            //Provide good defaults:
            $default_config = [
                'start_time'   => '08:00',
                'end_time'     => '20:00',
                'weekdays'     => 5,
                'visible_days' => [1, 2, 3, 4, 5]
            ];
            if (
                empty($this->schedule_settings['start_time'])
                && empty($this->schedule_settings['end_time'])
                && empty($this->schedule_settings['weekdays'])
                && empty($this->schedule_settings['visible_days'])
            ) {
                //Use the defaults:
                $this->schedule_settings = $default_config;
            }
        }
    
        /**
         * Saves the schedule settings from the settings dialog.
         */
        public function save_settings_action()
        {
            CSRFProtection::verifyUnsafeRequest();
    
            $start_time       = Request::get('start_time', '08:00');
            $end_time         = Request::get('end_time', '20:00');
            $weekdays         = Request::int('weekdays', 5);
            $visible_days    = Request::intArray('visible_days');
            if ($start_time >= $end_time) {
                PageLayout::postError(_('Die Startuhrzeit muss vor der Enduhrzeit liegen.'));
                $this->redirect('calendar/schedule/settings');
                return;
            }
            if (!in_array($weekdays, [5, 7])) {
                PageLayout::postError(_('Der Stundenplan kann nur 5 oder 7 Tage anzeigen.'));
                $this->redirect('calendar/schedule/settings');
                return;
            }
            if (empty($visible_days)) {
                PageLayout::postError(_('Es wurde kein Wochentag ausgewählt.'));
                $this->redirect('calendar/schedule/settings');
                return;
            }
    
    
            $schedule_settings = [
                'start_time'   => $start_time,
                'end_time'     => $end_time,
                'weekdays'     => $weekdays,
                'visible_days' => $visible_days
            ];
    
            UserConfig::get($GLOBALS['user']->id)->store('SCHEDULE_SETTINGS', $schedule_settings);
    
            PageLayout::postSuccess(_('Die Einstellungen wurden gespeichert.'));
            if (Request::isDialog()) {
                $this->response->add_header('X-Dialog-Close', '1');
            } else {
                $this->redirect('calendar/schedule/index');
            }
            $this->render_nothing();
        }
    }