diff --git a/app/controllers/calendar/calendar.php b/app/controllers/calendar/calendar.php index dfc6706071636873b9c7c62a979a75ac379b2195..468c35d9155d9702640a94e8223ddd2eaea69aa9 100644 --- a/app/controllers/calendar/calendar.php +++ b/app/controllers/calendar/calendar.php @@ -35,6 +35,13 @@ class Calendar_CalendarController extends AuthenticatedController $actions = new ActionsWidget(); if ($schedule) { + //Add the semester selector widget first: + $semester_widget = new SemesterSelectorWidget( + $this->url_for('calendar/calendar/schedule') + ); + $sidebar->addWidget($semester_widget); + + //Then add the actions for the action widget: $actions->addLink( _('Neuer Eintrag'), $this->url_for('calendar/calendar/add_schedule_entry'), @@ -762,7 +769,7 @@ class Calendar_CalendarController extends AuthenticatedController } } PageLayout::postSuccess(_('Die Zuordnung von Veranstaltungen zum Kalender wurde aktualisiert.')); - $this->redirect('calendar/calendar'); + $this->redirect('calendar/schedule/index'); } } diff --git a/app/controllers/calendar/schedule.php b/app/controllers/calendar/schedule.php index 668ed7739562146582c4d1f75c9c9ac37afc8579..33c9f6dd00beb118d8e106e0b96f3c1396e0f11e 100644 --- a/app/controllers/calendar/schedule.php +++ b/app/controllers/calendar/schedule.php @@ -1,498 +1,568 @@ <?php -# Lifter010: TODO /** - * This class displays a seminar-schedule for - * users on a seminar-based view and for admins on an institute based view + * 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 Till Glöggler <tgloeggl@uos.de> + * @author Moritz Strohm <strohm@data-quest.de> * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 * @category Stud.IP - * @since 2.0 + * @package calender + * @since 6.0 */ - -// Needs to be required due to the use of constants -require_once 'lib/classes/calendar/CalendarScheduleModel.php'; - class Calendar_ScheduleController extends AuthenticatedController { - - /** - * Callback function being called before an action is executed. If this - * function does not return FALSE, the action will be called, otherwise - * an error will be generated and processing will be aborted. If this function - * already #rendered or #redirected, further processing of the action is - * withheld. - * - * @param string Name of the action to perform. - * @param array An array of arguments to the action. - * - */ public function before_filter(&$action, &$args) { parent::before_filter($action, $args); - $zoom = Request::int('zoom'); - $this->my_schedule_settings = UserConfig::get($GLOBALS['user']->id)->SCHEDULE_SETTINGS; - // bind zoom, show_hidden and semester_id for all actions, even preserving them after redirect - if (isset($zoom)) { - URLHelper::addLinkParam('zoom', Request::int('zoom')); - $this->my_schedule_settings['zoom'] = Request::int('zoom'); - UserConfig::get($GLOBALS['user']->id)->store('SCHEDULE_SETTINGS', $this->my_schedule_settings); - } - - URLHelper::bindLinkParam('semester_id', $this->current_semester['semester_id']); - URLHelper::bindLinkParam('show_hidden', $this->show_hidden); - PageLayout::setHelpKeyword('Basis.MyStudIPStundenplan'); - PageLayout::setTitle(_('Mein Stundenplan')); + if (!Context::isCourse() && Navigation::hasItem('/calendar')) { + Navigation::activateItem('/calendar'); + } } - /** - * this action is the main action of the schedule-controller, setting the environment - * for the timetable, accepting a comma-separated list of days. - * - * @param string $days a list of an arbitrary mix of the numbers 0-6, separated - * with a comma (e.g. 1,2,3,4,5 (for Monday to Friday, the default)) - */ - public function index_action($days = false) + public function index_action() { - $schedule_settings = CalendarScheduleModel::getScheduleSettings(); - $inst_mode = false; - $institute_id = null; - if ($GLOBALS['perm']->have_perm('admin')) { - $inst_mode = true; - } - if ($inst_mode) { - // try to find the correct institute-id - $institute_id = Request::option('institute_id', Context::getId()); - if (!$institute_id) { - $institute_id = UserConfig::get($GLOBALS['user']->id)->MY_INSTITUTES_DEFAULT; - } - if (!$institute_id || !in_array(get_object_type($institute_id), ['fak', 'inst'])) { - throw new Exception('Cannot display institute-calender. No valid ID given!'); - } - Navigation::activateItem('/browse/my_courses/schedule'); - } else { + PageLayout::setTitle(_('Stundenplan')); + + if (Navigation::hasItem('/calendar/schedule')) { Navigation::activateItem('/calendar/schedule'); } - // check, if the hidden seminar-entries shall be shown - $show_hidden = Request::int('show_hidden', 0); - - // load semester-data and current semester - $this->semesters = array_reverse(Semester::findAllVisible(false)); - $this->current_semester = Semester::findCurrent(); - - $semester_id = Request::option('semester_id', $schedule_settings['semester_id'] ?? null); - if ($semester_id && Semester::exists($semester_id)) { - $this->current_semester = Semester::find($semester_id); - - $schedule_settings['semester_id'] = $this->current_semester->id; - User::findCurrent()->getConfiguration()->store( - 'SCHEDULE_SETTINGS', - $schedule_settings + $show_hidden = Request::bool('show_hidden', false); + + //Build the sidebar: + + $sidebar = Sidebar::get(); + + //Add the semester selector widget first: + $semester_widget = new SemesterSelectorWidget( + $this->indexURL(['show_hidden' => $show_hidden ?: null]) + ); + $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' => ''] + ); + if ($show_hidden) { + $actions->addLink( + _('Ausgeblendete Veranstaltungen verstecken'), + $this->indexURL(['semester_id' => Request::get('semester_id')]), + Icon::create('visibility-invisible') ); - } - - // check type-safe if days is false otherwise sunday (0) cannot be chosen - if ($days === false) { - if (Request::getArray('days')) { - $this->days = array_keys(Request::getArray('days')); - } else { - $this->days = CalendarScheduleModel::getDisplayedDays($schedule_settings['glb_days']); - } } else { - $this->days = explode(',', $days); + $actions->addLink( + _('Ausgeblendete Veranstaltungen anzeigen'), + $this->indexURL([ + 'show_hidden' => true, + 'semester_id' => Request::get('semester_id'), + ]), + Icon::create('visibility-visible') + ); } - $this->controller = $this; - - $this->calendar_view = $inst_mode - ? CalendarScheduleModel::getInstCalendarView($institute_id, $show_hidden, $this->current_semester, $this->days) - : CalendarScheduleModel::getUserCalendarView($GLOBALS['user']->id, $show_hidden, $this->current_semester, $this->days);; - - // have we chosen an entry to display? - if (!empty($this->flash['entry'])) { - if ($inst_mode) { - $this->show_entry = $this->flash['entry']; - } else if ($this->flash['entry']['id'] == null) { - $this->show_entry = $this->flash['entry']; - } else { - foreach ($this->calendar_view->getColumns() as $entry_days) { - foreach ($entry_days->getEntries() as $entry) { - if ($this->flash['entry']['cycle_id']) { - if ($this->flash['entry']['id'] . '-' . $this->flash['entry']['cycle_id'] == $entry['id']) { - $this->show_entry = $entry; - $entry_ids = explode('-', $this->show_entry['id']); - $this->show_entry['id'] = reset($entry_ids); - } - } else { - if ($entry['id'] == $this->flash['entry']['id']) { - $this->show_entry = $entry; - } - } - } - } - } + $actions->addLink( + _('Drucken'), + '#', + Icon::create('print'), + ['onclick' => 'window.print(); return false;'] + ); + $actions->addLink( + _('Einstellungen'), + $this->url_for('settings/calendar'), + Icon::create('settings'), + ['data-dialog' => 'size=auto;reload-on-close'] + ); + $sidebar->addWidget($actions); + + //Handle the selected semester and create a Fullcalendar instance. + + $semester = null; + if (Request::submitted('semester_id')) { + $semester = Semester::find(Request::option('semester_id')); } - - $style_parameters = [ - 'whole_height' => $this->calendar_view->getOverallHeight(), - 'entry_height' => $this->calendar_view->getHeight() - ]; - - $factory = new Flexi\Factory($this->dispatcher->trails_root . '/views'); - PageLayout::addStyle($factory->render('calendar/schedule/stylesheet', $style_parameters), 'screen, print'); - - if (Request::option('printview')) { - $this->calendar_view->setReadOnly(); - PageLayout::addStylesheet('print.css'); - - // remove all stylesheets that are not used for printing to have a more reasonable printing preview - PageLayout::addHeadElement('script', [], "$('head link[media=screen]').remove();"); - } else { - PageLayout::addStylesheet('print.css', ['media' => 'print']); + if (!$semester) { + $semester = Semester::findCurrent(); } - $this->show_hidden = $show_hidden; - - $inst = get_object_name($institute_id, 'inst'); - $this->inst_mode = $inst_mode; - $this->institute_name = $inst['name']; - $this->institute_id = $institute_id; - $this->show_settings = Request::bool('show_settings', false); + $fullcalendar = \Studip\Calendar\Helper::getScheduleFullcalendar( + $semester->id ?? '', + Request::bool('show_hidden', false) + ); + $this->fullcalendar = $fullcalendar->render(); } - public function new_entry_action() + public function data_action() { - $this->layout = null; - - if (!Request::isXhr()) { - $this->render_nothing(); + //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!'); } - } - /** - * this action is called whenever a new entry shall be modified or added to the schedule - * - * @param string $id optional, if id given, the entry with this id is updated - */ - public function addEntry_action($id = null) - { - if ($id) { - $data['id'] = $id; - } + $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 + ] + ); - $error = false; - $data['start'] = (int)str_replace(':', '', Request::get('entry_start')); - $data['end'] = (int)str_replace(':', '', Request::get('entry_end')); - $data['day'] = Request::int('entry_day'); + 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; + } - if ($data['start'] >= $data['end'] - || !Request::int('entry_day') - || !$this->validate_datetime(Request::get('entry_start')) - || !$this->validate_datetime(Request::get('entry_end'))) { - $error = true; - } + //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 = []; + $event_title = $cycle_date->course->getFullName(); + if ($course_membership) { + $event_classes[] = sprintf('course-color-%u', $course_membership->gruppe); + } elseif ($schedule_course) { + $event_classes[] = 'marked-course'; + $event_title = studip_interpolate( + _('%{course_name} (vorgemerkt)'), + ['course_name' => $cycle_date->course->getFullName()] + ); + } - if ($error) { - PageLayout::postError( - _('Eintrag konnte nicht gespeichert werden, da die Start- und/oder Endzeit ungültig ist!') - ); - } else { - $data['title'] = Request::get('entry_title'); - $data['content'] = Request::get('entry_content'); - $data['user_id'] = $GLOBALS['user']->id; - if (Request::get('entry_color')) { - $data['color'] = Request::get('entry_color'); - } else { - $data['color'] = DEFAULT_COLOR_NEW; + $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(); } + } - CalendarScheduleModel::storeEntry($data); + //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->redirect('calendar/schedule'); + $this->render_json($result); } - /** - * this action keeps the entry of the submitted_id and enables displaying of the entry-dialog. - * If no id is submitted, an empty entry_dialog is displayed. + * This action handles adding and editing schedule entries. * - * @param string $id the id of the entry to edit (if any), false otherwise. - * @param string $cycle_id an optional cycle's ID + * @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($id = null, $cycle_id = null) + public function entry_action(string $entry_id) { - if (Request::isXhr()) { - $this->response->add_header('Content-Type', 'text/html; charset=utf-8'); - $this->layout = null; - - $this->entry = [ - 'id' => $id, - 'cycle_id' => $cycle_id - ]; - - if ($cycle_id) { - $seminar_ids = CalendarScheduleModel::getSeminarEntry($id, $GLOBALS['user']->id, $cycle_id); - $this->show_entry = array_pop($seminar_ids); - $this->show_entry['id'] = $id; - $this->render_template('calendar/schedule/_entry_course'); - } else if ($id) { - $entry_columns = CalendarScheduleModel::getScheduleEntries($GLOBALS['user']->id, 0, 0, $id); - if (count($entry_columns) > 0) { - $entries = array_pop($entry_columns)->getEntries(); - $this->show_entry = array_pop($entries); - } else { - $this->show_entry = null; - } - $this->render_template('calendar/schedule/_entry_schedule'); + $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->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')))); } + PageLayout::setTitle(_('Neuer Termin')); } else { - $this->flash['entry'] = [ - 'id' => $id, - 'cycle_id' => $cycle_id - ]; - - $this->redirect('calendar/schedule/'); + //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()); } - } - /** - * Return an HTML fragment containing a form to edit an entry - * - * @param string the ID of a course - * @param string an optional cycle's ID - * @return void - */ - public function entryajax_action($id, $cycle_id = null) - { - $this->response->add_header('Content-Type', 'text/html; charset=utf-8'); - if ($cycle_id) { - $seminar_ids = CalendarScheduleModel::getSeminarEntry($id, $GLOBALS['user']->id, $cycle_id); - $this->show_entry = array_pop($seminar_ids); - $this->show_entry['id'] = $id; - $this->render_template('calendar/schedule/_entry_course'); - } else { - $entry_columns = CalendarScheduleModel::getScheduleEntries($GLOBALS['user']->id, 0, 0, $id); - $entries = array_pop($entry_columns)->getEntries(); - $this->show_entry = array_pop($entries); - $this->render_template('calendar/schedule/_entry_schedule'); + if (Request::submitted('save')) { + CSRFProtection::verifyUnsafeRequest(); + $this->saveEntry($entry_id); + } elseif (Request::submitted('delete')) { + CSRFProtection::verifyUnsafeRequest(); + $this->deleteEntry(); } } /** - * Returns an HTML fragment of a grouped entry in the schedule of an institute. - * - * @param string $start the start time of the group, e.g. "1000" - * @param string $end the end time of the group, e.g. "1200" - * @param string $course_ids the IDs of the courses - * @param string $day numeric day to show - * - * @return void + * Handles storing a schedule entry. */ - public function groupedentry_action($start, $end, $course_ids, $day) + public function save_entry_action(string $entry_id) { - $this->response->add_header('Content-Type', 'text/html; charset=utf-8'); - $course_ids = explode(',', $course_ids); - foreach ($course_ids as $course_id) { - $zw = explode('-', $course_id); - $this->courses[$zw[0]] = Course::find($zw[0]); + $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->timespan = mb_substr($start, 0, 2) . ':' . mb_substr($start, 2, 2) - . ' - ' . mb_substr($end, 0, 2) . ':' . mb_substr($end, 2, 2); - $this->start = $start; - $this->end = $end; - - $day_names = [ - _('Montag'), - _('Dienstag'), - _('Mittwoch'), - _('Donnerstag'), - _('Freitag'), - _('Samstag'), - _('Sonntag') - ]; - - $this->day = (int)$day; - $this->day_name = $day_names[$this->day]; - - - $this->render_template('calendar/schedule/_entry_inst'); - } - - /** - * delete the entry of the submitted id (only entry belonging to the current - * use can be deleted) - * - * @param string $id the id of the entry to delete - * @return void - */ - public function delete_action($id) - { - CalendarScheduleModel::deleteEntry($id); - $this->redirect('calendar/schedule'); - } - - /** - * store the color-settings for the seminar - * - * @param string $seminar_id - * @param string $cycle_id - * @return void - */ - public function editseminar_action($seminar_id, $cycle_id) - { - $data = [ - 'id' => $seminar_id, - 'cycle_id' => $cycle_id, - 'color' => Request::get('entry_color') - ]; - - CalendarScheduleModel::storeSeminarEntry($data); + $this->entry->dow = Request::int('dow', date('N')); + $this->entry->setFormattedStart(Request::get('start')); + $this->entry->setFormattedEnd(Request::get('end')); + $this->entry->label = Request::get('label', ''); + $this->entry->content = Request::get('content', ''); - $this->redirect('calendar/schedule'); - } - - /** - * Adds the appointments of a course to your schedule. - * - * @param string $seminar_id the ID of the course - * @return void - */ - public function addvirtual_action($seminar_id) - { - $regular_dates = SeminarCycleDate::findBySeminar($seminar_id); - foreach ($regular_dates as $cycle) { - $data = [ - 'id' => $seminar_id, - 'cycle_id' => $cycle->id, - 'color' => false - ]; - - CalendarScheduleModel::storeSeminarEntry($data); + 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; } - $this->redirect('calendar/schedule'); + 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(); } - /** - * Set the visibility of the course. - * - * @param string $seminar_id the ID of the course - * @param string $cycle_id the ID of the cycle - * @param string $visible visibility; either '1' or '0' - * @param string $ajax if you give this optional param, it signals an Ajax request - * @return void + * Handles deleting a schedule entry. */ - public function adminbind_action($seminar_id, $cycle_id, $visible, $ajax = null) + public function delete_entry_action(string $entry_id) { - CalendarScheduleModel::adminBind($seminar_id, $cycle_id, $visible); - - if (!$ajax) { - $this->redirect('calendar/schedule'); + 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 { - $this->render_nothing(); + 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(); } /** - * Hide the give appointment. + * Displays information about a course in the schedule. * - * @param string $seminar_id the ID of the course - * @param string $cycle_id the ID of the cycle - * @param string $ajax if you give this optional param, it signals an Ajax request - * @return void + * @param string $course_id The ID of the course. */ - function unbind_action($seminar_id, $cycle_id = null, $ajax = null) + public function course_info_action(string $course_id) { - CalendarScheduleModel::unbind($seminar_id, $cycle_id); - - if (!$ajax) { - $this->redirect('calendar/schedule'); - } else { - $this->render_nothing(); + $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()); } /** - * Show the given appointment. + * Hides a course in the schedule. * - * @param string $seminar_id the ID of the course - * @param string $cycle_id the ID of the cycle - * @param string $ajax if you give this optional param, it signals an Ajax request - * @return void + * @param string $course_id The ID of the course. */ - public function bind_action($seminar_id, $cycle_id, $ajax = null) + public function hide_course_action(string $course_id) { - CalendarScheduleModel::bind($seminar_id, $cycle_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 (!$ajax) { - $this->redirect('calendar/schedule'); - } else { - $this->render_nothing(); + //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(); } /** - * Show the settings' form. + * Makes a hidden course visible again in the schedule. * - * @return void + * @param string $course_id The ID of the course. */ - public function settings_action() + public function show_course_action(string $course_id) { - $this->settings = UserConfig::get($GLOBALS['user']->id)->SCHEDULE_SETTINGS; + 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(); } /** - * Store the settings + * 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 the start time of the calendar to show, e.g. "1000" - * @param string the end time of the calendar to show, e.g. "1200" - * @param string the days to show - * @param string the ID of the semester - * @return void + * @param string $course_id The ID of the course. */ - public function storesettings_action($start_hour = false, $end_hour = false, $days = false, $semester_id = false) + public function save_course_info_action(string $course_id) { - if ($start_hour === false) { - $start_hour = Request::int('start_hour'); - $end_hour = Request::int('end_hour'); - $days = Request::getArray('days'); + 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 ($start_hour > $end_hour) { - $end_hour = $start_hour + 1; - PageLayout::postError(_('Die Endzeit darf nicht vor der Startzeit liegen!')); + if ($success) { + PageLayout::postSuccess(_('Die Farbe der Veranstaltung wurde geändert.')); + } else { + PageLayout::postError(_('Die Farbe der Veranstaltung konnte nicht geändert werden.')); } - - $this->my_schedule_settings = [ - 'glb_start_time' => $start_hour, - 'glb_end_time' => $end_hour, - 'glb_days' => $days, - 'converted' => true - ]; - - if ($semester_id) { - $this->my_schedule_settings['semester_id'] = $semester_id; - } else if ($semester = UserConfig::get($GLOBALS['user']->id)->SCHEDULE_SETTINGS['semester_id']) { - $this->my_schedule_settings['semester_id'] = $semester; + if ($success) { + if (Request::isDialog()) { + $this->response->add_header('X-Dialog-Close', '1'); + } else { + $this->redirect('calendar/schedule/index'); + } } + $this->render_nothing(); + } - UserConfig::get($GLOBALS['user']->id)->store('SCHEDULE_SETTINGS', $this->my_schedule_settings); - - if (Context::isInstitute()) { - $this->redirect('calendar/instschedule'); + 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 { - $this->redirect('calendar/schedule'); + $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'); } } diff --git a/app/controllers/course/details.php b/app/controllers/course/details.php index 10f3f57ccc2b50c2bdec26d355ad0b7c01788f48..7f9c078255f96fb550b4becd87784679e6521869 100644 --- a/app/controllers/course/details.php +++ b/app/controllers/course/details.php @@ -247,22 +247,22 @@ class Course_DetailsController extends AuthenticatedController && count($this->course->cycles) ) { $query = "SELECT 1 - FROM `schedule_seminare` - WHERE `seminar_id` = ? AND `user_id` = ?"; + FROM `schedule_courses` + WHERE `course_id` = ? AND `user_id` = ?"; $penciled = DBManager::Get()->fetchColumn($query, [ $this->course->id, $GLOBALS['user']->id, ]); if (!$penciled) { $links->addLink( - _('Nur im Stundenplan vormerken'), - $this->url_for("calendar/schedule/addvirtual/{$this->course->id}"), + _('Zum Stundenplan hinzufügen'), + $this->url_for('calendar/schedule/mark_course', $this->course), Icon::create('info') ); $this->links[] = [ - 'label' => _('Nur im Stundenplan vormerken'), - 'url' => $this->url_for("calendar/schedule/addvirtual/{$this->course->id}"), + 'label' => _('Zum Stundenplan hinzufügen'), + 'url' => $this->url_for('calendar/schedule/mark_course', $this->course), 'attributes' => [], ]; } diff --git a/app/views/calendar/calendar/add_courses.php b/app/views/calendar/calendar/add_courses.php index cf3d282ca6498decfea1f89a5bd58a1aea415a21..85d4cd01552fa48633042882014eaac03ae24568 100644 --- a/app/views/calendar/calendar/add_courses.php +++ b/app/views/calendar/calendar/add_courses.php @@ -1,3 +1,11 @@ +<?php +/** + * @var Trails_Controller $controller The controller. + * @var array $selected_course_ids The IDs of the selected courses. + * @var string $selected_semester_id The ID of the selected semester. + * @var array $available_semester_data The data of all available semesters. + */ +?> <form class="default" method="post" action="<?= $controller->link_for('calendar/calendar/add_courses') ?>"> <?= CSRFProtection::tokenTag() ?> <fieldset class="simplevue"> diff --git a/app/views/calendar/schedule/_colorpicker.php b/app/views/calendar/schedule/_colorpicker.php deleted file mode 100644 index e1f12667286a84a0559def5671aef2c03eec4337..0000000000000000000000000000000000000000 --- a/app/views/calendar/schedule/_colorpicker.php +++ /dev/null @@ -1,15 +0,0 @@ -<section id="color_picker"> - <?= _('Farbe des Termins') ?> - <div> - <? foreach ($GLOBALS['PERS_TERMIN_KAT'] as $index => $data): ?> - <span> - <input type="radio" name="entry_color" value="<?= $index ?>" id="color-<?= $index ?>" - <?= $index === $selected ? 'checked' : '' ?>> - <label class="undecorated schedule-category<?= $index ?> enter-accessible" - for="color-<?= $index ?>" - aria-label="<?= sprintf(_('Farbe %u zuordnen'), $index) ?>" - title="<?= sprintf(_('Farbe %u zuordnen'), $index) ?>"></label> - </span> - <? endforeach; ?> - </div> -</section> diff --git a/app/views/calendar/schedule/_dialog.php b/app/views/calendar/schedule/_dialog.php deleted file mode 100644 index 0b64f9db25e1b2aa190548709b6274deb296d331..0000000000000000000000000000000000000000 --- a/app/views/calendar/schedule/_dialog.php +++ /dev/null @@ -1,12 +0,0 @@ -<div class="ui-dialog ui-widget ui-widget-content ui-corner-all ui-draggable ui-resizable ui-dialog-buttons <?= $class ?: 'schedule-dialog' ?>" tabindex="-1" role="dialog" aria-labelledby="ui-id-2" id="schedule_new_entry" style="width: 600px; height: auto; z-index: 1002;"> - <div class="ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix" style="z-index: 1001"> - <span id="ui-id-2" class="ui-dialog-title"><?= $title ?></span> - <a class="ui-dialog-titlebar-close ui-corner-all" href="<?= $controller->link_for('calendar/schedule') ?>" role="button"> - <span class="ui-icon ui-icon-closethick">close</span> - </a> - </div> - - <div class="ui-widget-content" style="display: block; width: auto; min-height: 0px; height: 100%;" scrolltop="0" scrollleft="0"> - <?= $content_for_layout ?> - </div> -</div> diff --git a/app/views/calendar/schedule/_entry_course.php b/app/views/calendar/schedule/_entry_course.php deleted file mode 100644 index f6002184daf8d37443e13f397b55132ddcdb9fa6..0000000000000000000000000000000000000000 --- a/app/views/calendar/schedule/_entry_course.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php -$course = Course::find($show_entry['id']); -?> -<form class="default" - action="<?= $controller->link_for('calendar/schedule/editseminar/' . $show_entry['id'] . '/' . $show_entry['cycle_id']) ?>" - method="post" name="edit_entry"> - <?= CSRFProtection::tokenTag() ?> - <fieldset> - <legend> - <?= _('Stundenplaneintrag') ?> - </legend> - - <?= $this->render_partial('calendar/schedule/_colorpicker.php', [ - 'selected' => $show_entry['color'], - ]) ?> - - <? if ($show_entry['type'] == 'virtual') : ?> - <section> - <span - style="color: red; font-weight: bold"><?= _('Dies ist lediglich eine vorgemerkte Veranstaltung') ?></span><br><br> - </section> - <? endif ?> - - <section> - <strong><?= _('Veranstaltungsnummer') ?></strong><br> - <?= htmlReady($course->veranstaltungsnummer) ?> - </section> - - <section> - <strong><?= _('Name') ?></strong><br> - <?= htmlReady($course->name) ?> - </section> - - <section> - <strong><?= _('Lehrende') ?></strong><br> - <? - $pos = 0; - $lecturers = CourseMember::findByCourseAndStatus($course->id, 'dozent'); - foreach ($lecturers as $lecturer) :?> - <?= $pos > 0 ? ', ' : '' ?> - <a href="<?= URLHelper::getLink('dispatch.php/profile', ['username' => $lecturer->user->username]) ?>"> - <?= htmlReady($lecturer->user->getFullName()) ?> - </a> - <? $pos++ ?> - <? endforeach ?> - </section> - - <section> - <strong><?= _('Veranstaltungszeiten') ?></strong><br> - <?= $course->getAllDatesInSemester()->toHtml(true) ?><br> - </section> - - <section> - <?= Icon::create('link-intern') ?> - <? if ($show_entry['type'] == 'virtual') : ?> - <a href="<?= URLHelper::getLink('dispatch.php/course/details', ['sem_id' => $show_entry['id']]) ?>"> - <?= _('Zur Veranstaltung') ?> - </a> - <br> - <? else : ?> - <a href="<?= URLHelper::getLink('seminar_main.php', ['auswahl' => $show_entry['id']]) ?>"> - <?= _('Zur Veranstaltung') ?> - </a> - <br> - <? endif ?> - </section> - </fieldset> - - <footer data-dialog-button> - <?= Studip\Button::createAccept(_('Speichern'), ['style' => 'margin-right: 20px']) ?> - - <? if (!$show_entry['visible']) : ?> - <?= Studip\LinkButton::create( - _('Einblenden'), - $controller->url_for( - 'calendar/schedule/bind/' . $show_entry['id'] . '/' . $show_entry['cycle_id'] . '/', - ['show_hidden' => '1'] - ), - ['style' => 'margin-right: 20px']) ?> - <? else : ?> - <?= Studip\LinkButton::create( - $show_entry['type'] == 'virtual' ? _('Löschen') : _('Ausblenden'), - $controller->url_for('calendar/schedule/unbind/' . $show_entry['id'] . '/' . $show_entry['cycle_id']), - ['style' => 'margin-right: 20px']) ?> - <? endif ?> - - <?= Studip\LinkButton::createCancel( - _('Abbrechen'), - $controller->url_for('calendar/schedule'), - ['onclick' => "jQuery('#edit_sem_entry').fadeOut('fast'); STUDIP.Calendar.click_in_progress = false; return false"]) ?> - </footer> -</form> diff --git a/app/views/calendar/schedule/_entry_inst.php b/app/views/calendar/schedule/_entry_inst.php deleted file mode 100644 index 228751f47f1b18bcbfd2e49da8db6f908abaecb7..0000000000000000000000000000000000000000 --- a/app/views/calendar/schedule/_entry_inst.php +++ /dev/null @@ -1,59 +0,0 @@ -<table class="default"> - <colgroup> - <col style="width: 15%"> - <col style="width: 45%"> - <col> - </colgroup> - <caption> - <?= sprintf(_('Veranstaltungen mit regelmäßigen Zeiten am %s, %s Uhr'), htmlReady($day), htmlReady($timespan)) ?> - </caption> - <thead> - <tr> - <th><?= _('Nummer') ?></th> - <th><?= _('Name') ?></th> - <th></th> - </tr> - </thead> - <tbody> - <? foreach ($courses as $course) : ?> - <tr> - <td><?= htmlReady($course->veranstaltungsnummer) ?></td> - <td> - <a href="<?= URLHelper::getLink('dispatch.php/course/details/', ['sem_id' => $course->id]) ?>"> - <?= Icon::create('link-intern') ?> - <?= htmlReady($course->name) ?> - </a> - </td> - <td class="schedule-adminbind"> - <? $cycles = CalendarScheduleModel::getSeminarCycleId($course->id, $start, $end, $day) ?> - - <? foreach ($cycles as $cycle) : ?> - <span><?= $cycle->toString() ?></span> - - <? $visible = CalendarScheduleModel::isSeminarVisible($course->id, $cycle->getMetadateId()) ?> - - <?= Studip\LinkButton::create( - _('Ausblenden'), - $controller->url_for('calendar/schedule/adminbind/' . $course->id . '/' . $cycle->getMetadateId() . '/0'), - [ - 'id' => $course->id . '_' . $cycle->getMetadateId() . '_hide', - 'onclick' => "STUDIP.Schedule.instSemUnbind('" . $course->id . "','" . $cycle->getMetadateId() . "'); return false;", - 'style' => ($visible ? '' : 'display: none') - ]) ?> - - <?= Studip\LinkButton::create( - _('Einblenden'), - $controller->url_for('calendar/schedule/adminbind/' . $course->id . '/' . $cycle->getMetadateId() . '/1'), - [ - 'id' => $course->id . '_' . $cycle->getMetadateId() . '_show', - 'onclick' => "STUDIP.Schedule.instSemBind('" . $course->id . "','" . $cycle->getMetadateId() . "'); return false;", - 'style' => ($visible ? 'display: none' : '') - ]) ?> - <br> - <? endforeach ?> - </td> - </tr> - <? endforeach ?> - </tbody> -</table> -<br> diff --git a/app/views/calendar/schedule/_entry_schedule.php b/app/views/calendar/schedule/_entry_schedule.php deleted file mode 100644 index ad5bbcd6503b3d3297543ec036a24b50e82c0aae..0000000000000000000000000000000000000000 --- a/app/views/calendar/schedule/_entry_schedule.php +++ /dev/null @@ -1,73 +0,0 @@ -<form class="default" - action="<?= $controller->link_for('calendar/schedule/addentry', $show_entry['id'] ?? null) ?>" - method="post" name="edit_entry" onSubmit="return STUDIP.Schedule.checkFormFields()"> - <?= CSRFProtection::tokenTag() ?> - <fieldset> - <legend> - <?= _('Stundenplaneintrag') ?> - </legend> - - <label class="col-2"> - <?= _('Tag') ?> - <select name="entry_day" class="size-s"> - <? foreach ([1, 2, 3, 4, 5, 6, 7] as $index) : ?> - <option - value="<?= $index ?>" <?= (isset($show_entry['day']) && $show_entry['day'] == $index) ? 'selected="selected"' : '' ?>> - <?= getWeekDay($index % 7, false) ?> - </option> - <? endforeach ?> - </select> - </label> - - <label class="col-2"> - <?= _('von') ?> - <input class="size-s studip-timepicker" placeholder="HH:mm" type="text" size="2" name="entry_start" - value="<?= !empty($show_entry['start']) ? $show_entry['start_formatted'] : '' ?>" - id="entry-start" data-time-picker> - </label> - - <label class="col-2"> - <?= _('bis') ?> - <input class="size-s studip-timepicker" placeholder="HH:mm" type="text" size="2" name="entry_end" - value="<?= !empty($show_entry['end']) ? $show_entry['end_formatted'] : '' ?>" - id="entry-end" data-time-picker> - </label> - - <span class="invalid_message"><?= _('Die Endzeit liegt vor der Startzeit!') ?></span> - - <?= $this->render_partial('calendar/schedule/_colorpicker.php', [ - 'selected' => $show_entry['color'] ?? null, - ]) ?> - - <label> - <?= _('Titel') ?> - <input type="text" name="entry_title" value="<?= htmlReady($show_entry['title'] ?? '') ?>"> - </label> - - <label> - <?= _('Beschreibung') ?> - <textarea name="entry_content" - rows="7"><?= htmlReady($show_entry['content'] ?? '') ?></textarea> - </label> - </fieldset> - - <footer data-dialog-button> - <?= Studip\Button::createAccept(_('Speichern'), ['style' => 'margin-right: 20px']) ?> - <? if (isset($show_entry['id'])) : ?> - <?= Studip\LinkButton::create( - _('Löschen'), - $controller->url_for('calendar/schedule/delete/'. $show_entry['id']), - ['style' => 'margin-right: 20px'] - ) ?> - <? endif ?> - - <? if (!empty($show_entry)) : ?> - <?= Studip\LinkButton::createCancel( - _('Abbrechen'), - $controller->url_for('calendar/schedule'), - ['onclick' => 'STUDIP.Schedule.cancelNewEntry(); STUDIP.Calendar.click_in_progress = false;return false;']) ?> - <? else: ?> - <?= Studip\LinkButton::createCancel(_('Abbrechen'), 'javascript:STUDIP.Schedule.cancelNewEntry()') ?> - <? endif ?> - </footer> -</form> diff --git a/app/views/calendar/schedule/_semester_chooser.php b/app/views/calendar/schedule/_semester_chooser.php deleted file mode 100644 index a8a783e41b72f768de758cb8635c5d8598ee839d..0000000000000000000000000000000000000000 --- a/app/views/calendar/schedule/_semester_chooser.php +++ /dev/null @@ -1,23 +0,0 @@ -<form method="post" class="default" action="<?= $controller->link_for( - isset($inst_mode) && $inst_mode == true ? 'calendar/instschedule/index' : 'calendar/schedule/index' -) ?>"> - <label for="semester_id" class="sr-only"><?= _('Angezeigtes Semester') ?></label> - <select name="semester_id" class="submit-upon-select" id="semester_id"> - <? foreach ($semesters as $semester) : ?> - <? if ($semester['ende'] > time() - strtotime('1year 1day')) : ?> - <option - value="<?= $semester['semester_id'] ?>" <?= $current_semester['semester_id'] == $semester['semester_id'] ? 'selected="selected"' : '' ?>> - <?= htmlReady($semester['name']) ?> - <?= $semester['beginn'] < time() && $semester['ende'] > time() ? _('*') : '' ?> - </option> - <? endif ?> - <? endforeach ?> - </select> - <noscript> - <?= Icon::create( - 'accept', - Icon::ROLE_ACCEPT, - ['title' => _('auswählen')] - )->asInput(['type' => 'image', 'class' => 'middle']) ?> - </noscript> -</form> diff --git a/app/views/calendar/schedule/course_info.php b/app/views/calendar/schedule/course_info.php new file mode 100644 index 0000000000000000000000000000000000000000..b8135926a3ca896b4213240f07593ef12be7a6db --- /dev/null +++ b/app/views/calendar/schedule/course_info.php @@ -0,0 +1,77 @@ +<?php +/** + * @var AuthenticatedController $controller + * @var Course $course + * @var CourseMember $membership + * @var ScheduleCourseDate $schedule_course_entry + */ +?> +<? if ($course) : ?> + <h2><?= htmlReady($course->getFullName()) ?></h2> + <form class="default" method="post" data-dialog="reload-on-close" + action="<?= $controller->link_for('calendar/schedule/course_info/' . $course->id) ?>"> + <?= CSRFProtection::tokenTag() ?> + <? if ($membership) : ?> + <fieldset> + <legend><?= _('Farbe') ?></legend> + <table class="default mycourses-group-selector"> + <tr> + <?= $this->render_partial( + 'my_courses/group_selector', + [ + 'course_id' => $course->id, + 'selected_group_id' => $membership->gruppe + ] + ) ?> + </tr> + </table> + </fieldset> + <? endif ?> + <fieldset> + <legend><?= _('Informationen') ?></legend> + <section> + <h3><?= _('Veranstaltungsnummer') ?></h3> + <p><?= htmlReady($course->veranstaltungsnummer) ?></p> + <h3><?= _('Lehrende') ?></h3> + <ul class="default"> + <? + $lecturers = CourseMember::findByCourseAndStatus($course->id, 'dozent'); + ?> + <? foreach ($lecturers as $lecturer) : ?> + <li> + <a href="<?= URLHelper::getLink('dispatch.php/profile', ['username' => $lecturer->username]) ?>"> + <?= htmlReady($lecturer->user->getFullName()) ?> + </a> + </li> + <? endforeach ?> + </ul> + <h3><?= _('Veranstaltungszeiten') ?></h3> + <?= $course->getAllDatesInSemester()->toHtml() ?> + </section> + </fieldset> + <div data-dialog-button> + <?= \Studip\Button::create( + _('Speichern'), + 'save', + ['formaction' => $controller->url_for('calendar/schedule/save_course_info/' . $course->id)] + ) ?> + <? if ($schedule_course_entry && !$schedule_course_entry->visible) : ?> + <?= \Studip\Button::create( + _('Veranstaltung einblenden'), + 'show', + ['formaction' => $controller->url_for('calendar/schedule/show_course/' . $course->id)] + ) ?> + <? else : ?> + <?= \Studip\Button::create( + _('Veranstaltung ausblenden'), + 'hide', + ['formaction' => $controller->url_for('calendar/schedule/hide_course/' . $course->id)] + ) ?> + <? endif ?> + <?= \Studip\LinkButton::create( + _('Direkt zur Veranstaltung'), + URLHelper::getURL('dispatch.php/course/overview', ['cid' => $course->id]) + ) ?> + </div> + </form> +<? endif ?> diff --git a/app/views/calendar/schedule/entry.php b/app/views/calendar/schedule/entry.php index 54d44e9cef40c4f8f6c315305598ae8b07721074..676f07308c17ef4d1f7e8cb0bbe731035201aaff 100644 --- a/app/views/calendar/schedule/entry.php +++ b/app/views/calendar/schedule/entry.php @@ -1,9 +1,77 @@ -<? if (!empty($show_entry) && in_array($show_entry['type'], ['sem', 'virtual'])): ?> - <?= $this->render_partial('calendar/schedule/_entry_course.php') ?> - <? unset($show_entry) ?> -<? elseif (!empty($show_entry) && $show_entry['type'] === 'inst'): ?> - <?= $this->render_partial('calendar/schedule/_entry_inst.php') ?> - <? unset($show_entry) ?> -<? else : ?> - <?= $this->render_partial('calendar/schedule/_entry_schedule.php') ?> -<? endif ?> +<?php +/** + * @var AuthenticatedController $controller + * @var ScheduleEntry $entry The schedule entry to be created/modified. + */ +?> +<form class="default" method="post" action="<?= $controller->link_for('calendar/schedule/entry/' . ($entry->isNew() ? 'add' : $entry->id)) ?>" + data-dialog="reload-on-close"> + <?= CSRFProtection::tokenTag() ?> + <fieldset> + <legend><?= _('Zeit') ?></legend> + <section class="flex-row"> + <label> + <?= _('Wochentag') ?> + <select name="dow"> + <option value="1" <?= $entry->dow === 1 ? 'selected' : '' ?>> + <?= _('Montag') ?> + </option> + <option value="2" <?= $entry->dow === 2 ? 'selected' : '' ?>> + <?= _('Dienstag') ?> + </option> + <option value="3" <?= $entry->dow === 3 ? 'selected' : '' ?>> + <?= _('Mittwoch') ?> + </option> + <option value="4" <?= $entry->dow === 4 ? 'selected' : '' ?>> + <?= _('Donnerstag') ?> + </option> + <option value="5" <?= $entry->dow === 5 ? 'selected' : '' ?>> + <?= _('Freitag') ?> + </option> + <option value="6" <?= $entry->dow === 6 ? 'selected' : '' ?>> + <?= _('Samstag') ?> + </option> + <option value="7" <?= $entry->dow === 7 ? 'selected' : '' ?>> + <?= _('Sonntag') ?> + </option> + </select> + </label> + <label> + <?= _('Startuhrzeit') ?> + <input type="text" class="has-time-picker" name="start" + value="<?= htmlReady($entry->getFormattedStart()) ?>"> + </label> + <label> + <?= _('Enduhrzeit') ?> + <input type="text" class="has-time-picker" name="end" + value="<?= htmlReady($entry->getFormattedEnd()) ?>"> + </label> + </section> + </fieldset> + <fieldset> + <legend><?= _('Inhalt') ?></legend> + <label> + <?= _('Titel') ?> + <input type="text" name="label" value="<?= htmlReady($entry->label) ?>"> + </label> + <label> + <?= _('Beschreibung') ?> + <textarea name="content"><?= htmlReady($entry->content) ?></textarea> + </label> + </fieldset> + <div data-dialog-button> + <?= \Studip\Button::create( + _('Speichern'), + 'save', + ['formaction' => $controller->url_for('calendar/schedule/save_entry/' . ($entry->isNew() ? 'add' : $entry->id))] + ) ?> + <? if (!$entry->isNew()) : ?> + <?= \Studip\Button::create( + _('Löschen'), + 'delete', + ['formaction' => $controller->url_for('calendar/schedule/delete_entry/' . $entry->id)] + ) ?> + <? endif ?> + <?= \Studip\Button::createCancel(_('Abbrechen')) ?> + </div> +</form> diff --git a/app/views/calendar/schedule/index.php b/app/views/calendar/schedule/index.php index fe022a2317765a8f19cd109eb701b755c29ce609..390a0161ec105c4da38b2c5e617196c5770d6bc1 100644 --- a/app/views/calendar/schedule/index.php +++ b/app/views/calendar/schedule/index.php @@ -1,84 +1,6 @@ <?php -# Lifter010: TODO -$zoom = $my_schedule_settings['zoom'] ?? 0; - -$sidebar = Sidebar::get(); - -$semester_widget = new SidebarWidget(); -$semester_widget->setTitle(_('Angezeigtes Semester')); -$semester_widget->addElement( - new WidgetElement($this->render_partial('calendar/schedule/_semester_chooser')), - 'semester' -); -$sidebar->addWidget($semester_widget, 'calendar/schedule/semester'); - -$actions = new ActionsWidget(); -if (!$inst_mode) { - $actions->addLink( - _('Neuer Eintrag'), - $controller->url_for('calendar/schedule/entry'), - Icon::create('add'), - ['data-dialog' => 'size=auto'] - ); -} -$actions->addLink( - _('Darstellung ändern'), - $controller->url_for('calendar/schedule/settings'), - Icon::create('admin'), - ['data-dialog' => 'size=auto'] -); -if (!$show_hidden) { - $actions->addLink( - _('Ausgeblendete Veranstaltungen anzeigen'), - $controller->url_for('calendar/schedule', ['show_hidden' => '1']), - Icon::create('visibility-invisible') - ); -} else { - $actions->addLink( - _('Ausgeblendete Veranstaltungen verbergen'), - $controller->url_for('calendar/schedule', ['show_hidden' => '0']), - Icon::create('visibility-visible') - ); -} -$sidebar->addWidget($actions, 'calendar/schedule/actions'); - -$widget = new ExportWidget(); -$widget->addLink(_('Druckansicht'), - $controller->url_for( - 'calendar/schedule/index/' . implode(',', $days), - [ - 'printview' => 'true', - 'semester_id' => $current_semester['semester_id'], - ] - ), - Icon::create('print'), - ['target' => '_blank']); -$sidebar->addWidget($widget, 'calendar/schedule/print'); - -$options = new OptionsWidget(); -$options->setTitle(_('Darstellungsgröße')); -$options->addRadioButton(_('klein'), URLHelper::getURL('', ['zoom' => 0]), $zoom == 0); -$options->addRadioButton(_('mittel'), URLHelper::getURL('', ['zoom' => 1]), $zoom == 1); -$options->addRadioButton(_('groß'), URLHelper::getURL('', ['zoom' => 2]), $zoom == 2); -$sidebar->addWidget($options, 'calendar/schedule/options'); - +/** + * @var \Studip\Fullcalendar $fullcalendar The fullcalendar instance to be rendered. + */ ?> -<div style="text-align: center; font-weight: bold; font-size: 1.2em"> - <? if ($inst_mode) : ?> - <?= htmlReady($institute_name) ?>: <?= _('Stundenplan im') ?> - <? else : ?> - <?= _('Mein Stundenplan im') ?> - <? endif ?> - <?= htmlReady($current_semester['name']) ?> -</div> - -<? if (!empty($show_entry)) : ?> - <div class="ui-widget-overlay" style="width: 100%; height: 100%; z-index: 1001;"></div> - <?= $this->render_partial('calendar/schedule/_dialog', [ - 'content_for_layout' => $this->render_partial('calendar/schedule/entry', [ - 'show_entry' => $show_entry]), - 'title' => _('Termindetails') - ]) ?> -<? endif ?> - -<?= $calendar_view->render(['show_hidden' => $show_hidden]) ?> +<?= $fullcalendar ?> diff --git a/app/views/calendar/schedule/settings.php b/app/views/calendar/schedule/settings.php deleted file mode 100644 index 0e2674e6e00f27bd1310d863621b8157689405d2..0000000000000000000000000000000000000000 --- a/app/views/calendar/schedule/settings.php +++ /dev/null @@ -1,44 +0,0 @@ -<form class="default" method="post" action="<?= $controller->link_for('calendar/schedule/storesettings') ?>"> - <?= CSRFProtection::tokenTag() ?> - <fieldset> - <legend> - <?= _('Angezeigter Zeitraum') ?> - </legend> - <section> - <section class="hgroup"> - <label> - <?= _('von') ?> - <input type="text" name="start_hour" id="start-hour" class="size-s" - value="<?= sprintf('%02u:00', $settings['glb_start_time']) ?>" - data-time-picker> - </label> - <label> - <?= _('bis') ?> - <input type="text" name="end_hour" id="end-hour" class="size-s" - value="<?= sprintf('%02u:00', $settings['glb_end_time']) ?>" - data-time-picker> - </label> - <?= _('Uhr') ?><br> - </section> - </section> - </fieldset> - <fieldset> - <legend> - <?= _('Angezeigte Wochentage') ?> - </legend> - <section class='settings'> - <? foreach ([1, 2, 3, 4, 5, 6, 0] as $day) : ?> - <label> - <input type="checkbox" name="days[]" value="<?= $day ?>" - <?= in_array($day, $settings['glb_days']) !== false ? 'checked' : '' ?>> - <?= getWeekDay($day, false) ?> - </label> - <? endforeach ?> - <span class="invalid_message"><?= _('Bitte mindestens einen Wochentag auswählen.') ?></span><br> - </section> - </fieldset> - <footer data-dialog-button> - <?= Studip\Button::createSuccess(_('Speichern'), ['onclick' => "return STUDIP.Calendar.validateNumberOfDays();"]) ?> - <?= Studip\LinkButton::createCancel(_('Abbrechen'), $controller->url_for('calendar/schedule/#')) ?> - </footer> -</form> diff --git a/app/views/calendar/schedule/stylesheet.php b/app/views/calendar/schedule/stylesheet.php deleted file mode 100644 index aaf7334c04bf9fe840bcd99f4f9bd6638c9e7db1..0000000000000000000000000000000000000000 --- a/app/views/calendar/schedule/stylesheet.php +++ /dev/null @@ -1,13 +0,0 @@ -div.schedule_day { - height: <?= $whole_height ?>px; -} - -div.schedule_marker { - height: <?= floor($entry_height / 2) ?>px; - line-height: <?= floor($entry_height / 2) ?>px; - margin-bottom: <?= floor($entry_height / 2) ?>px; -} - -div.schedule_hours { - height: <?= $entry_height ?>px; -} diff --git a/app/views/my_courses/group_selector.php b/app/views/my_courses/group_selector.php new file mode 100644 index 0000000000000000000000000000000000000000..8d99a64060890dfa8c0d6e7639732c43e8dc6609 --- /dev/null +++ b/app/views/my_courses/group_selector.php @@ -0,0 +1,20 @@ +<?php +/** + * @var string $course_id + * @var string $selected_group_id + */ +?> +<? for ($i = 0; $i < 9; $i++) : ?> + <td class="gruppe<?= $i ?> mycourses-group-selector" onclick="this.querySelector('input').checked = true;"> + <input type="radio" name="gruppe[<?= htmlReady($course_id) ?>]" value="<?= $i ?>" + aria-label="<?= sprintf(_('Gruppe %u zuordnen'), $i + 1) ?>" + id="course-group-<?= htmlReady($course_id) ?>-<?= $i ?>" + <?= $selected_group_id == $i ? 'checked' : '' ?>> + <label for="course-group-<?= htmlReady($course_id) ?>-<?= $i ?>"> + <span class="group-number"><?= $i + 1 ?></span> + <span class="checked-icon"> + <?= Icon::create('accept', Icon::ROLE_INFO)->asImg(20) ?> + </span> + </label> + </td> +<? endfor ?> diff --git a/app/views/my_courses/groups.php b/app/views/my_courses/groups.php index 4476aeb20df2c48cda15b0c61c33d91529237bc6..fff9d63e9452bf9d0cd608e5964dd9a9c642f9f3 100644 --- a/app/views/my_courses/groups.php +++ b/app/views/my_courses/groups.php @@ -55,20 +55,13 @@ <?= _('(versteckt)') ?> <? endif; ?> </td> - <? for ($i = 0; $i < 9; $i++): ?> - <td class="gruppe<?= $i ?> mycourses-group-selector" onclick="this.querySelector('input').checked = true;"> - <input type="radio" name="gruppe[<?= $member['seminar_id'] ?>]" value="<?= $i ?>" - aria-label="<?= sprintf(_('Gruppe %u zuordnen'), $i + 1) ?>" - id="course-group-<?= htmlReady($member['seminar_id']) ?>-<?= $i ?>" - <? if ($my_sem[$member['seminar_id']]['gruppe'] == $i) echo 'checked'; ?>> - <label for="course-group-<?= htmlReady($member['seminar_id']) ?>-<?= $i ?>"> - <span class="group-number"><?= $i + 1 ?></span> - <span class="checked-icon"> - <?= Icon::create('accept', Icon::ROLE_INFO)->asImg(20) ?> - </span> - </label> - </td> - <? endfor; ?> + <?= $this->render_partial( + 'my_courses/group_selector', + [ + 'course_id' => $member['seminar_id'], + 'selected_group_id' => $my_sem[$member['seminar_id']]['gruppe'] + ] + ) ?> </tr> <? endforeach; ?> </tbody> diff --git a/db/migrations/6.0.13_alter_schedule_table.php b/db/migrations/6.0.13_alter_schedule_table.php new file mode 100644 index 0000000000000000000000000000000000000000..d27bf81f5e4c564babf17b05390ac1026d8342c4 --- /dev/null +++ b/db/migrations/6.0.13_alter_schedule_table.php @@ -0,0 +1,65 @@ +<?php + + +class AlterScheduleTable extends Migration +{ + public function description() + { + return 'Renames and alters the schedule table'; + } + + protected function up() + { + $db = DBManager::get(); + + $db->exec("RENAME TABLE `schedule` TO `schedule_entries`"); + + $db->exec( + "ALTER TABLE `schedule_entries` + DROP COLUMN color, + CHANGE COLUMN start start_time SMALLINT(6) NOT NULL, + CHANGE COLUMN end end_time SMALLINT(6) NOT NULL, + CHANGE COLUMN day dow TINYINT(1) NOT NULL, + CHANGE COLUMN title label VARCHAR(255) NOT NULL DEFAULT '', + CHANGE COLUMN content content TEXT, + ADD COLUMN mkdate BIGINT(10) NOT NULL DEFAULT 0, + ADD COLUMN chdate BIGINT(10) NOT NULL DEFAULT 0" + ); + + $db->exec("RENAME TABLE `schedule_seminare` TO `schedule_courses`"); + $db->exec( + "ALTER TABLE `schedule_courses` + DROP COLUMN color, + CHANGE COLUMN seminar_id course_id CHAR(32) NOT NULL, + ADD COLUMN mkdate BIGINT(10) NOT NULL DEFAULT 0, + ADD COLUMN chdate BIGINT(10) NOT NULL DEFAULT 0" + ); + } + + protected function down() + { + $db = DBManager::get(); + + $db->exec( + "ALTER TABLE `schedule_courses` + ADD COLUMN color TINYINT(4) NULL DEFAULT NULL, + CHANGE COLUMN course_id seminar_id CHAR(32) NOT NULL, + DROP COLUMN mkdate, + DROP COLUMN chdate" + ); + $db->exec("RENAME TABLE `schedule_courses` TO `schedule_seminare`"); + + $db->exec( + "ALTER TABLE `schedule_entries` + ADD COLUMN color TINYINT(4) NULL DEFAULT NULL, + CHANGE COLUMN start_time start SMALLINT(6) NOT NULL, + CHANGE COLUMN end_time end SMALLINT(6) NOT NULL, + CHANGE COLUMN dow day TINYINT(1) NOT NULL, + CHANGE COLUMN label title VARCHAR(255) NOT NULL, + CHANGE COLUMN content content VARCHAR(255) NOT NULL, + DROP COLUMN mkdate, + DROP COLUMN chdate" + ); + $db->exec("RENAME TABLE `schedule_entries` TO `schedule`"); + } +} diff --git a/lib/calendar/CalendarColumn.php b/lib/calendar/CalendarColumn.php deleted file mode 100644 index 78a38090e6d0d7bb1cd2b13e468681268c00dcb8..0000000000000000000000000000000000000000 --- a/lib/calendar/CalendarColumn.php +++ /dev/null @@ -1,371 +0,0 @@ -<?php -# Lifter010: TODO -/** - * CalendarColumn.php - a column for a CalendarView - * - * This class represents an entry-column like "monday" in the calendar - * - * 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 Rasmus Fuhse <fuhse@data-quest.de> - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - * - * @deprecated since Stud.IP 5.5 - */ - -class CalendarColumn -{ - protected static $number = 0; - protected $title = ""; - protected $id = ""; - public $entries = []; - protected $url = ""; - protected $grouped = false; - protected $sorted_entries = null; - - /** - * creates instance of type CalendarColumn - * - * @param string $id necessary if you want JavaScript enabled for this calendar - * @return CalendarColumn - */ - static public function create($id = null) { - $column = new CalendarColumn($id); - return $column; - } - - /** - * constructor - * - * @param string $id necessary if you want JavaScript enabled for this column - */ - public function __construct($id = null) { - $id !== null || $id = md5(uniqid("CalendarColumn_".self::$number++)); - $this->setId($id); - } - - /** - * returns the id of the column - * - * @return string - */ - public function getId() { - return $this->id; - } - - /** - * sets the id for this column, which is only necessary if you want - * Javascript to be enabled for this calendar - * - * @param string $id new id for this column - * @return CalendarColumn - */ - public function setId($id) { - $this->id = $id; - return $this; - } - - /** - * sets a title like "monday" for this column, which will be displayed in the calendar - * - * @param string $new_title new title - * @return CalendarColumn - */ - public function setTitle($new_title) { - $this->title = $new_title; - return $this; - } - - /** - * returns the title of this column like "monday" - * - * @return string title of column - */ - public function getTitle() { - return $this->title; - } - - /** - * sets the url to be directed to when clicking on the title of the column. - * Usually this is a single-day-view of the calendar. - * - * @param string $new_url an url - * @return CalendarColumn - */ - public function setURL($new_url) { - $this->url = $new_url; - return $this; - } - - /** - * returns the URL of the column (see setURL) - * - * @return string an url - */ - public function getURL() { - return $this->url; - } - - /** - * adds a new entry in the column. The entry needs to be an associative array - * with parameters as follows: - * - * @param array $entry_array associative array for an entry in the column like - * array ( - * 'color' => the color in hex (css-like, without the #) - * 'start' => the (start hour * 100) + (start minute) - * 'end' => the (end hour * 100) + (end minute) - * 'title' => the entry`s title - * 'content' => whatever shall be the content of the entry as a string - * ) - */ - public function addEntry($entry_array) { - if (!isset($entry_array['start']) || !isset($entry_array['end']) - || !isset($entry_array['title']) ) { - throw new InvalidArgumentException('The entry '. print_r($entry_array, true) .' does not follow the specifications!'); - } else { - $this->entries[] = $entry_array; - } - return $this; - } - - /** - * adds many entries to the column. For the syntax of an entry see addEntry() - * - * @param array $entries_array - * @return CalendarColumn - */ - public function addEntries($entries_array = []) { - foreach ($entries_array as $entry_array) { - $this->addEntry($entry_array); - } - return $this; - } - - /** - * returns all entries of this column - * - * @return array of arrays like - * array ( - * 'color' => the color in hex (css-like, without the #) - * 'start' => the (start hour * 100) + (start minute) - * 'end' => the (end hour * 100) + (end minute) - * 'title' => the entry`s title - * 'content' => whatever shall be the content of the entry as a string - * ) - */ - public function getEntries() { - return $this->entries; - } - - /** - * deletes all entries of this column. So the only way to edit an entry is - * getting all entries with getEntries, edit this entry, eraseEntries() and - * addEntries(). Not very short, but at least it works. - * - * @return CalendarColumn - */ - public function eraseEntries() { - $this->entries = []; - return $this; - } - - /** - * Returns an array of calendar-entries, grouped by day and additionally grouped by same start and end - * if groupEntries(true) has been called. - * - * @return mixed the (double-)grouped entries - */ - public function getGroupedEntries() - { - if (empty($this->sorted_entries)) { - if ($this->isGrouped()) { - $this->sorted_entries = $this->sortAndGroupEntries(); - } else { - $this->sorted_entries = $this->sortEntries(); - } - } - - return $this->sorted_entries; - } - - /** - * sorts and groups entries and returns them - * only used by columns with grouped entries like instituteschedules - * - * @return array - */ - public function sortAndGroupEntries() - { - $day = $this->getTitle(); - - $entries_for_column = $this->getEntries(); - $result = []; - $new_entries = []; - - // 1st step - group all entries with the same duration - foreach ($entries_for_column as $entry_id => $entry) { - $new_entries[$entry['start'] .'_'. $entry['end']][] = $entry; - } - - $column = 0; - - // 2nd step - optimize the groups - while (sizeof($new_entries) > 0) { - $lstart = 2399; $lend = 0; - - foreach ($new_entries as $time => $grouped_entries) { - list($start, $end) = explode('_', $time); - if ($start < $lstart /*&& ($end - $start) >= ($lend - $lstart)*/ ) { - $lstart = $start; - $lend = $end; - } - } - - $result['col_'. $column][] = $new_entries[$lstart .'_'. $lend]; - unset($new_entries[$lstart .'_'. $lend]); - - $hit = true; - - while ($hit) { - $hit = false; - $hstart = 2399; $hend = 2399; - - // check, if there is something, that can be placed after - foreach ($new_entries as $time => $grouped_entries) { - list($start, $end) = explode('_', $time); - - if ( ($start >= $lend) && ($start < $hstart) ) { - $hstart = $start; - $hend = $end; - $hit = true; - } - } - - if ($hit) { - $lend = $hend; - $result['col_'. $column][] = $new_entries[$hstart .'_'. $hend]; - unset($new_entries[$hstart .'_'. $hend]); - } - } - - $column++; - } // 2nd step - - return $result; - - } - - /** - * sorts entries and returns them - * - * @return array - */ - public function sortEntries() - { - $entries_for_column = $this->getEntries(); - - $result = []; - $column = 0; - - // 2nd step - optimize the groups - while (sizeof($entries_for_column) > 0) { - $lstart = 2399; $lend = 0; $lkey = null; - - foreach ($entries_for_column as $entry_key => $entry) { - if ($entry['start'] < $lstart /*&& ($end - $start) >= ($lend - $lstart)*/ ) { - $lstart = $entry['start']; - $lend = $entry['end']; - $lkey = $entry_key; - } - } - - $result['col_'. $column][] = $entries_for_column[$lkey]; - unset($entries_for_column[$lkey]); - - $hit = true; - - while ($hit) { - $hit = false; - $hstart = 2399; $hend = 2399; $hkey = null; - - // check, if there is something, that can be placed after - foreach ($entries_for_column as $entry_key => $entry) { - if ( ($entry['start'] >= $lend) && ($entry['start'] < $hstart) ) { - // && (($end - $start) > ($hend - $hstart)) ) { - $hstart = $entry['start']; - $hend = $entry['end']; - $hkey = $entry_key; - $hit = true; - } - } - - if ($hit) { - $lend = $hend; - $result['col_'. $column][] = $entries_for_column[$hkey]; - unset($entries_for_column[$hkey]); - } - } - - $column++; - } // 2nd step - return $result; - - } - - /** - * returns a matrix that tells the number of entries for a given timeslot - * - * @return array - */ - public function getMatrix() { - $group_matrix = []; - foreach ($this->getGroupedEntries() as $groups) { - foreach ($groups as $group) { - if (isset($group[0]) && is_array($group[0])) { - $data = $group[0]; - } else { - $data = $group; - } - - for ($i = floor($data['start'] / 100); $i <= floor($data['end'] / 100); $i++) { - for ($j = 0; $j < 60; $j++) { - if (($i * 100) + $j >= $data['start'] && ($i * 100) + $j < $data['end']) { - if (!isset($group_matrix[$i * 100 + $j])) { - $group_matrix[$i * 100 + $j] = 0; - } - $group_matrix[$i * 100 + $j]++; - } - } - } - } - } - return $group_matrix; - } - - /** - * check, if a grouped view of the entries is requested - * - * @return bool true if grouped, false otherwise - */ - public function isGrouped() - { - return $this->grouped; - } - - /** - * Call this function th enable/disable the grouping of entries with the same start and end. - * - * @param bool $group optional, defaults to true - * @return void - */ - public function groupEntries($grouped = true) - { - $this->grouped = $grouped; - } - -} diff --git a/lib/calendar/CalendarView.php b/lib/calendar/CalendarView.php deleted file mode 100644 index 9d9ebc29556baedc579e292a6037d3c9b0431f11..0000000000000000000000000000000000000000 --- a/lib/calendar/CalendarView.php +++ /dev/null @@ -1,344 +0,0 @@ -<?php -# Lifter010: TODO - - /** - * CalendarView.php - generates a calendar - * - * This class takes and checks all necessary parameters to display a calendar/schedule/time-table. - * - * 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 Rasmus Fuhse <fuhse@data-quest.de> - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - */ - -/** - * Kind of bean class for the calendar view. - * - * Example of use: - * - * // create a calendar-view and add a column - * $plan = new CalendarView(); - * $plan->addColumn(_('Spalte 1')) - * ->addEntry(array( - * 'id' => 1, - * 'color' => '#5C2D64', - * 'start' => '0930', - * 'end' => '1100', - * 'title' => 'Mathe 2', - * 'content' => 'Die Mathematiker kreiden sich mal wieder was an.' - * ) - * ); - * - * // display the calendar (containing one column) - * print $plan->render(); - * - * @since 2.0 - * - * @deprecated since Stud.IP 5.5 - */ - -class CalendarView -{ - - protected $entries = []; - protected $entry_columns = []; - protected $height = 40; - protected $grouped = false; - protected $start_hour = 8; - protected $end_hour = 21; - protected $insertFunction = ""; - protected $templates = []; - protected $read_only = false; - - protected static $number_of_instances = 1; - protected $view_id; - - public $sorted_entries = []; - - - /** - * You need to pass an instance of this class to the template. The constructor - * expects an array of entries of the following type: - * array( - * $day_number => array(array ( - * 'color' => the color in hex (css-like, without the #) - * 'start' => the (start hour * 100) + (start minute) - * 'end' => the (end hour * 100) + (end minute) - * //'day' => day of week (0 = Sunday, ... , 6 = Saturday) - * 'title' => the entry`s title - * 'content' => whatever shall be the content of the entry as a string - * ) ...) ... - * ) - * - * @param mixed $entries an array of entries (see above) - * @param string $controller the name of the controller. Used to create links. - */ - public function __construct($entries = []) - { - if (!is_array($entries)) { - throw new Exception('You need to pass some entries to the CalendarView!'); - } - $this->view_id = self::$number_of_instances++; - $this->checkEntries($entries); - $this->entries = $entries; - } - - /** - * set the height for one hour. This value is used to calculate the whole height of the schedule. - * - * @param int $entry_height the height of one hour in the schedule - */ - public function setHeight($height) - { - $this->height = $height; - } - - /** - * set the range of hours to be displayed. the start_hour has to be smaller than the end_hour - * - * @param int $start_hour the hour to start displaying at - * @param int $end_hour the hour to stop displaying at - */ - public function setRange($start_hour, $end_hour) - { - $this->start_hour = $start_hour; - $this->end_hour = $end_hour; - } - - /** - * does some plausability checks on an array of calendar-entries - * - * @param mixed $entries an array of calendar-entries - * - * @return bool false if check failed, true otherwise - */ - protected function checkEntries($entries) - { - foreach ($entries as $column) { - if (!$column instanceof CalendarColumn) { - throw new Exception('A column of the entries in the CalenarView is not of type CalendarColumn.'); - } - } - return true; - } - - /** - * adds a new column to this view. All entries created with addEntry will be - * added to this column. - * - * @param string $title like "monday" to be displayed on top of the column - * @param string $url to be called when clicked on the title of the column - * @param string $id any kind of id of the column - * @return CalendarView - */ - public function addColumn($title, $url = "", $id = null) - { - $this->entries[] = CalendarColumn::create($id) - ->setTitle($title) - ->setURL($url); - return $this; - } - - - /** - * adds a new entry to the last current column. The entry needs to be an - * associative array with parameters as follows: - * @param array $entry_array: associative array for an entry in the column like - * array ( - * 'color' => the color in hex (css-like, without the #) - * 'start' => the (start hour * 100) + (start minute) - * 'end' => the (end hour * 100) + (end minute) - * 'title' => the entry`s title - * 'content' => whatever shall be the content of the entry as a string - * ) - * @return CalendarView - */ - public function addEntry($entry_array) - { - if (count($this->entries)) { - $this->entries[count($this->entries)-1]->addEntry($entry_array); - } else { - throw new InvalidArgumentException(_("Es existiert noch keine Spalte in der Ansicht, zu der der Eintrag hinzugefügt werden kann.")); - } - return $this; - } - - - /** - * Call this function to enable/disable the grouping of entries with the same start and end. - * - * @param bool $group optional, defaults to true - */ - public function groupEntries($grouped = true) - { - $this->grouped = $grouped; - foreach($this->getColumns() as $entry_column) { - $entry_column->groupEntries(); - } - } - - /** - * When a column is clicked at no entry this function is called. - * First the templates generates a new entry at the clicked time. Then this - * js-function is called which gets the parameters - * "function (new_entry_dom_object, column_id, hour) { ... }" - * with new_entry_dom_object: a real dom-object of the div of the entry - * column_id: id of the column - * hour: integer number from 0 to 23 - * If js_function_object is an empty string, nothing will be done. - * - * @param string $js_function_object name of js-function or anonymous js-function - * @return CalendarView - */ - public function setInsertFunction($js_function_object) - { - $this->insertFunction = $js_function_object; - return $this; - } - - /** - * outputs the CalendarView with all (grouped) dates in columns. - * - * @param array $params you can pass some additional variables to the templates - * - * @return string - */ - public function render($params = []) - { - $style_parameters = [ - 'whole_height' => $this->getOverallHeight(), - 'entry_height' => $this->getHeight() - ]; - $factory = new Flexi\Factory(dirname(__file__).'/../../app/views'); - PageLayout::addStyle($factory->render('calendar/schedule/stylesheet', $style_parameters)); - - $template = $GLOBALS['template_factory']->open("calendar/calendar_view.php"); - $template->set_attribute("calendar_view", $this); - $template->set_attribute("view_id", $this->view_id); - return $template->render($params); - } - - - /* * * * * * * * * * * * * * * - * * * G E T T E R S * * * - * * * * * * * * * * * * * * */ - - /** - * Returns an array of calendar-entries, grouped by day and additionally grouped by same start and end - * if groupEntries(true) has been called. - * - * @return mixed the (double-)grouped entries - */ - public function getEntries() - { - $this->sorted_entries = []; - foreach ($this->getColumns() as $entry_column) { - $this->sorted_entries['day_'. $entry_column->getId()] = $entry_column->getGroupedEntries(); - } - return $this->sorted_entries; - } - - /** - * Returns an array where for each hour the number of concurrent entries is denoted. - * Used by the calendar to display the entries in parallel. - * - * @return mixed concurrent entries at each hour - */ - public function getMatrix() - { - $matrix = []; - foreach ($this->getColumns() as $day => $entry_column) { - $matrix['day_'.$day] = $entry_column->getMatrix(); - } - return $matrix; - } - - - /** - * returns the previously set start- and end-hour, denoting the - * range of entries to be displayed in the current calendar-view - * - * @return array consisting of the start and end hour - */ - public function getRange() - { - return [$this->start_hour, $this->end_hour]; - } - - /** - * the calendar can be used in two modes. Use this function to check, - * if the grouping-mode is enabled for the current calendar-view - * - * @return bool true if grouped, false otherwise - */ - public function isGrouped() - { - return $this->grouped; - } - - /** - * returns the previously set height for one hour - * - * @return mixed the height - */ - public function getHeight() - { - return $this->height; - } - - /** - * returns the overall height of the calendar - * - * @return mixed the overall height - */ - public function getOverallHeight() - { - return $this->height * ($this->end_hour - $this->start_hour) + $this->height - + 2 + ($this->end_hour - $this->start_hour) * 2; - } - - /** - * returns the previously set javasscript insert-function - * - * @return string name of js-function or anonymous js-function - */ - public function getInsertFunction() { - return $this->insertFunction; - } - - /** - * returns all columns of the calendar-view - * - * @return array of CalendarColumn - */ - public function getColumns() { - return $this->entries; - } - - /** - * Set the read-only status of the calendar - * - * @param bool $readonly true to make it read only, false otherwise - * - * @return void - */ - public function setReadOnly($readonly = true) - { - $this->read_only = $readonly; - } - - /** - * Return the read-only status of this calendar - * - * @return bool the read-only status of this calendar - */ - public function getReadOnly() { - return $this->read_only; - } - -} diff --git a/lib/calendar/CalendarWeekView.php b/lib/calendar/CalendarWeekView.php deleted file mode 100644 index 1d14ca2188f6defe8fa4294987e0e1d4ea1cb3da..0000000000000000000000000000000000000000 --- a/lib/calendar/CalendarWeekView.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php -# Lifter010: TODO - -/** - * CalendarWeekView.php - a specialized calendar view for displaying weeks - * - * This class takes and checks all necessary parameters to display a calendar/schedule/time-table. - * - * 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 Till Glöggler <tgloeggl@uos.de> & Rasmus Fuhse <fuhse@data-quest.de> - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - */ - -/** - * Kind of bean class for the calendar view. - * - * @since 2.0 - * - * @deprecated since Stud.IP 5.5 - */ - -class CalendarWeekView extends CalendarView -{ - protected $days = [1,2,3,4,5]; - protected $context; - - - /** - * You need to pass an instance of this class to the template. The constructor - * expects an array of entries of the following type: - * array( - * $day_number => array(array ( - * 'color' => the color in hex (css-like, without the #) - * 'start' => the (start hour * 100) + (start minute) - * 'end' => the (end hour * 100) + (end minute) - * //'day' => day of week (0 = Sunday, ... , 6 = Saturday) - * 'title' => the entry`s title - * 'content' => whatever shall be the content of the entry as a string - * ) ...) ... - * ) - * - * @param mixed $entries an array of entries (see above) - * @param string $controller the name of the controller. Used to create links. - */ - public function __construct($entries, $controller) - { - parent::__construct($entries); - $this->context = $controller; - } - - /** - * Call this function th enable/disable the grouping of entries with the same start and end. - * - * @param bool $group optional, defaults to true - */ - public function groupEntries($grouped = true) - { - $this->grouped = $grouped; - foreach($this->entries as $entry_column) { - $entry_column->groupEntries(); - } - } - - /* * * * * * * * * * * * * * * - * * * G E T T E R S * * * - * * * * * * * * * * * * * * */ - - /** - * @return mixed the context - */ - public function getContext() - { - return $this->context; - } - - /** - * @return mixed the days - */ - public function getDays() - { - return $this->days; - } - - /** - * returns the previously set javasscript insert-function only - * if read_only is not set. - * - * @return string name of js-function or anonymous js-function - */ - public function getInsertFunction() { - if (!$this->read_only) { - return parent::getInsertFunction(); - } - - return false; - } - - /** - * returns all columns of the calendar-view nad removes the url if - * read_only is set - * - * @return array of CalendarColumn - */ - public function getColumns() { - // remove links and urls if calendar-view is read-only - if ($this->read_only) { - foreach ($this->entries as $column) { - $column->setURL(false); - foreach ($column->entries as $key => $entry) { - unset($column->entries[$key]['url']); - unset($column->entries[$key]['onClick']); - unset($column->entries[$key]['icons']); - } - } - } - - return parent::getColumns(); - } -} diff --git a/lib/calendar/CalendarWidgetView.php b/lib/calendar/CalendarWidgetView.php deleted file mode 100644 index df980ff3953e529e720c799566d975ed670d08f5..0000000000000000000000000000000000000000 --- a/lib/calendar/CalendarWidgetView.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -/** - * Calendar widget view, links to details page of courses. - * - * @author Jan-Hendrik Willms <tleilax+studip@gmail.com> - * @license GPL2 or any later version - * @since Stud.IP 3.4 - * - * @deprecated since Stud.IP 5.5 - */ -class CalendarWidgetView extends CalendarWeekView -{ - /** - * Creates a widget view from a week view. - * - * @param CalendarWeekView $view The CalendarWeekView object - * @return CalendarWidgetView object with the data from the - * CalendarWeekView - */ - public static function createFromWeekView(CalendarWeekView $view) - { - $new_view = new self($view->getColumns(), $view->getContext()); - $new_view->setReadOnly(true); - return $new_view; - } - - /** - * Returns all columns of the calendar-view and removes everything that - * is not needed and links the entry to the details page of the course. - * - * @return array of CalendarColumn - */ - public function getColumns() - { - foreach ($this->entries as $column) { - $column->setURL(false); - foreach ($column->entries as $key => $entry) { - if (isset($entry['cycle_id'])) { - list($course_id, $cycle_id) = explode('-', $entry['id']); - - $url = URLHelper::getLink('dispatch.php/course/details/?sem_id=' . $course_id); - $column->entries[$key]['url'] = $url; - } else { - unset($column->entries[$key]['url']); - } - - unset($column->entries[$key]['onClick']); - unset($column->entries[$key]['icons']); - } - } - - return $this->entries; - } -} diff --git a/lib/classes/JsonApi/Models/ScheduleEntry.php b/lib/classes/JsonApi/Models/ScheduleEntry.php deleted file mode 100644 index 37001ff1b56de81d23541e22ee3d30c521fb9dc6..0000000000000000000000000000000000000000 --- a/lib/classes/JsonApi/Models/ScheduleEntry.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -namespace JsonApi\Models; - -class ScheduleEntry extends \SimpleORMap -{ - protected static function configure($config = array()) - { - $config['db_table'] = 'schedule'; - - $config['belongs_to']['user'] = array( - 'class_name' => 'User', - 'foreign_key' => 'user_id', - ); - - parent::configure($config); - } -} diff --git a/lib/classes/JsonApi/Routes/Schedule/ScheduleEntriesShow.php b/lib/classes/JsonApi/Routes/Schedule/ScheduleEntriesShow.php index 37c1c422642f68c7a1a82107c1ebb23c2fcb68eb..b92a1e847679c49379328ff0ccd96fab7dcdac93 100644 --- a/lib/classes/JsonApi/Routes/Schedule/ScheduleEntriesShow.php +++ b/lib/classes/JsonApi/Routes/Schedule/ScheduleEntriesShow.php @@ -5,7 +5,7 @@ namespace JsonApi\Routes\Schedule; use JsonApi\Errors\AuthorizationFailedException; use JsonApi\Errors\RecordNotFoundException; use JsonApi\JsonApiController; -use JsonApi\Models\ScheduleEntry; +use \ScheduleEntry; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; diff --git a/lib/classes/JsonApi/Routes/Schedule/SeminarCycleDatesShow.php b/lib/classes/JsonApi/Routes/Schedule/SeminarCycleDatesShow.php index 6e5bdf42bd9aa774f05c2f8dab65daa1182aa3cb..d1fb02406d0518987e5c5513f20c9fb989594685 100644 --- a/lib/classes/JsonApi/Routes/Schedule/SeminarCycleDatesShow.php +++ b/lib/classes/JsonApi/Routes/Schedule/SeminarCycleDatesShow.php @@ -5,7 +5,6 @@ namespace JsonApi\Routes\Schedule; use JsonApi\Errors\AuthorizationFailedException; use JsonApi\Errors\RecordNotFoundException; use JsonApi\JsonApiController; -use JsonApi\Models\ScheduleEntry; use JsonApi\Routes\Courses\Authority as CourseAuthority; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; diff --git a/lib/classes/JsonApi/Routes/Schedule/UserScheduleShow.php b/lib/classes/JsonApi/Routes/Schedule/UserScheduleShow.php index 85cf9113e08fa9947ec72832678e3577ca231bf9..033916cc408f7a585dce4379c1ed490a9feaac28 100644 --- a/lib/classes/JsonApi/Routes/Schedule/UserScheduleShow.php +++ b/lib/classes/JsonApi/Routes/Schedule/UserScheduleShow.php @@ -5,7 +5,7 @@ namespace JsonApi\Routes\Schedule; use JsonApi\Errors\AuthorizationFailedException; use JsonApi\Errors\RecordNotFoundException; use JsonApi\JsonApiController; -use JsonApi\Models\ScheduleEntry; +use \ScheduleEntry; use JsonApi\Routes\Users\Authority; use Neomerx\JsonApi\Schema\Link; use Psr\Http\Message\ResponseInterface as Response; @@ -57,8 +57,8 @@ class UserScheduleShow extends JsonApiController { // get all virtually added seminars $stmt = \DBManager::get()->prepare( - 'SELECT c.seminar_id FROM schedule_seminare as c - LEFT JOIN seminare USING (seminar_id) + 'SELECT c.course_id FROM schedule_courses as c + LEFT JOIN seminare ON seminare.seminar_id = c.course_id WHERE user_id = ? AND start_time = ?' ); $stmt->execute([$user->id, $semester['beginn']]); diff --git a/lib/classes/JsonApi/SchemaMap.php b/lib/classes/JsonApi/SchemaMap.php index ff5040dc57fd1c0993965a6e5163693c8ddb310e..a5c1213e689e3ba30d5f4e8c5655e14b0980118e 100644 --- a/lib/classes/JsonApi/SchemaMap.php +++ b/lib/classes/JsonApi/SchemaMap.php @@ -14,7 +14,7 @@ class SchemaMap return [ \Slim\Routing\Route::class => Schemas\SlimRoute::class, - \JsonApi\Models\ScheduleEntry::class => Schemas\ScheduleEntry::class, + \ScheduleEntry::class => Schemas\ScheduleEntry::class, \Avatar::class => Schemas\Avatar::class, diff --git a/lib/classes/JsonApi/Schemas/ScheduleEntry.php b/lib/classes/JsonApi/Schemas/ScheduleEntry.php index 3318f358b64ce2af734ad17a315f02a6ada2f93c..0dfbc7d2cfe359c513263c138c22cafea8e1bfad 100644 --- a/lib/classes/JsonApi/Schemas/ScheduleEntry.php +++ b/lib/classes/JsonApi/Schemas/ScheduleEntry.php @@ -20,14 +20,14 @@ class ScheduleEntry extends SchemaProvider public function getAttributes($entry, ContextInterface $context): iterable { return [ - 'title' => $entry->title, + 'title' => $entry->label, 'description' => mb_strlen(trim($entry->content)) ? $entry->content : null, - 'start' => $this->formatTime($entry->start), - 'end' => $this->formatTime($entry->end), - 'weekday' => (int) $entry->day, + 'start' => $this->formatTime($entry->start_time), + 'end' => $this->formatTime($entry->end_time), + 'weekday' => (int) $entry->dow, - 'color' => $entry->color, + 'color' => '', ]; } diff --git a/lib/classes/UserManagement.php b/lib/classes/UserManagement.php index ed349a2d31591d58f1e2394ec0b34cd506bb7af5..53b567555788a4ff474387a5fb50f1cc26b0e32a 100644 --- a/lib/classes/UserManagement.php +++ b/lib/classes/UserManagement.php @@ -1203,7 +1203,7 @@ class UserManagement "DELETE FROM auto_insert_user WHERE user_id = ?", "DELETE FROM roles_user WHERE userid = ?", "DELETE FROM schedule WHERE user_id = ?", - "DELETE FROM schedule_seminare WHERE user_id = ?", + "DELETE FROM schedule_courses WHERE user_id = ?", "DELETE FROM termin_related_persons WHERE user_id = ?", "DELETE FROM priorities WHERE user_id = ?", "DELETE FROM help_tour_user WHERE user_id = ?", diff --git a/lib/classes/calendar/CalendarScheduleModel.php b/lib/classes/calendar/CalendarScheduleModel.php deleted file mode 100644 index bc884bbfc06b5239e8b8c448fbd82c06f211d0e7..0000000000000000000000000000000000000000 --- a/lib/classes/calendar/CalendarScheduleModel.php +++ /dev/null @@ -1,813 +0,0 @@ -<?php -# Lifter010: TODO - -/* - * This class is the module for the seminar-schedules in Stud.IP - * - * 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 Till Glöggler <tgloeggl@uos.de> - * @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2 - * @category Stud.IP - */ - -require_once __DIR__ . '/default_color_definitions.php'; - -/** - * Pseudo-namespace containing helper methods for the schedule. - * - * @since 2.0 - * - * @deprecated since Stud.IP 5.5 - */ -class CalendarScheduleModel -{ - - /** - * update an existing entry or -if $data['id'] is not set- create a new entry - * - * @param mixed $data - */ - static function storeEntry($data) - { - if (!empty($data['id'])) { // update - $stmt = DBManager::get()->prepare("UPDATE schedule - SET start = ?, end = ?, day = ?, title = ?, content = ?, color = ?, user_id = ? - WHERE id = ?"); - $stmt->execute([$data['start'], $data['end'], $data['day'], $data['title'], - $data['content'], $data['color'], $data['user_id'], $data['id']]); - - NotificationCenter::postNotification('ScheduleDidUpdate', $GLOBALS['user']->id ?? null, ['values' => $data]); - - } else { - $stmt = DBManager::get()->prepare("INSERT INTO schedule - (start, end, day, title, content, color, user_id) - VALUES (?, ?, ?, ?, ?, ?, ?)"); - $stmt->execute([$data['start'], $data['end'], $data['day'], $data['title'], - $data['content'], $data['color'], $data['user_id']]); - NotificationCenter::postNotification('ScheduleDidCreate', $GLOBALS['user']->id ?? null, ['values' => $data]); - } - } - - /** - * Update an existing entry of a course or create a new entry if $data['id'] is not set - * - * @param mixed $data the data to store - * @return void - */ - static function storeSeminarEntry($data) - { - $stmt = DBManager::get()->prepare("REPLACE INTO schedule_seminare - (seminar_id, user_id, metadate_id, color) VALUES(?, ? ,?, ?)"); - - $stmt->execute([$data['id'], $GLOBALS['user']->id, $data['cycle_id'], $data['color']]); - NotificationCenter::postNotification('ScheduleSeminarDidCreate', $GLOBALS['user']->id, $data['cycle_id']); - } - - /** - * delete the entry with the submitted id, belonging to the current user - * - * @param string $id - * @return void - */ - static function deleteEntry($id) - { - $stmt = DBManager::get()->prepare("DELETE FROM schedule - WHERE id = ? AND user_id = ?"); - $stmt->execute([$id, $GLOBALS['user']->id]); - NotificationCenter::postNotification('ScheduleDidDelete', $GLOBALS['user']->id, $id); - } - - - /** - * Returns an array of CalendarColumn's containing the - * schedule entries (optionally of a given id only). - * The start- and end-hour are used to constrain the returned - * entries to the passed time-period. - * If you pass an id, there will be only the single entry with that id in - * the CalendarColumn - * - * @param string $user_id the ID of the user - * @param int $start_hour the start hour - * @param int $end_hour the end hour - * @param string $id optional; the ID of the schedule-entry - * @return array an array containing the entries - */ - static function getScheduleEntries($user_id, $start_hour, $end_hour, $id = false) - { - $ret = []; - - // fetch user-generated entries - if (!$id) { - $stmt = DBManager::get()->prepare("SELECT * FROM schedule - WHERE user_id = ? AND ( - (start >= ? AND end <= ?) - OR (start <= ? AND end >= ?) - OR (start <= ? AND end >= ?) - )"); - $start = $start_hour * 100; - $end = $end_hour * 100; - $stmt->execute([$user_id, $start, $end, $start, $start, $end, $end]); - } else { - $stmt = DBManager::get()->prepare("SELECT * FROM schedule - WHERE user_id = ? AND id = ?"); - $stmt->execute([$user_id, $id]); - } - - $entries = $stmt->fetchAll(PDO::FETCH_ASSOC); - foreach($entries as $entry) { - $entry['start_formatted'] = sprintf("%02d", floor($entry['start'] / 100)) .':'. sprintf("%02d", floor($entry['start'] % 100)); - $entry['end_formatted'] = sprintf("%02d", floor($entry['end'] / 100)) .':'. sprintf("%02d", floor($entry['end'] % 100)); - $entry['title'] = $entry['title']; - $entry['content'] = $entry['content']; - $entry['start_hour'] = sprintf("%02d", floor($entry['start'] / 100)); - $entry['start_minute'] = sprintf("%02d", $entry['start'] % 100); - $entry['end_hour'] = sprintf("%02d", floor($entry['end'] / 100)); - $entry['end_minute'] = sprintf("%02d", $entry['end'] % 100); - $entry['url'] = URLHelper::getLink('dispatch.php/calendar/schedule/entry/' . $entry['id']); - $entry['onClick'] = "function (id) { STUDIP.Schedule.showScheduleDetails('". $entry['id'] ."'); }"; - $entry['visible'] = true; - - $day_number = ($entry['day']-1) % 7; - if (!isset($ret[$day_number])) { - $ret[$day_number] = CalendarColumn::create($day_number); - } - $ret[$day_number]->addEntry($entry); - } - - return $ret; - } - - /** - * Return an entry for the specified course. - * - * @param string $seminar_id the ID of the course - * @param string $user_id the ID of the user - * @param mixed $cycle_id either false or the ID of the cycle - * @param mixed $semester filter for this semester - * - * @return array the course's entry - */ - static function getSeminarEntry($seminar_id, $user_id, $cycle_id = false, $semester = false) - { - $ret = []; - $filterStart = 0; - $filterEnd = 0; - - // filter dates (and their rooms) if semester is passed - if ($semester) { - $filterStart = $semester['vorles_beginn']; - $filterEnd = $semester['vorles_ende']; - } - - $course = Course::find($seminar_id); - $regular_dates = SeminarCycleDate::findBySeminar($seminar_id); - foreach ($regular_dates as $cycle) { - if (!$cycle_id || $cycle->getMetaDateID() == $cycle_id) { - - $entry = []; - $entry['id'] = $seminar_id .'-'. $cycle->getMetaDateId(); - $entry['cycle_id'] = $cycle->getMetaDateId(); - $entry['start_formatted'] = preg_replace('/\:00$/', '', $cycle->start_time); - $entry['end_formatted'] = preg_replace('/\:00$/', '', $cycle->end_time); - - $start_parts = explode(':', $cycle->start_time); - $end_parts = explode(':', $cycle->end_time); - $entry['start'] = $start_parts[0] * 100 + $start_parts[1]; - $entry['end'] = $end_parts[0] * 100 + $end_parts[1]; - $entry['day'] = $cycle->weekday; - $entry['content'] = $course->veranstaltungsnummer . ' ' . $course->name; - - $entry['title'] = $cycle->description; - - // check, if the date is assigned to a room - if ($room = $cycle->getMostBookedRoom()) { - $entry['title'] .= $room->getFullName(); - } else if ($rooms = $cycle->getFreeTextPredominantRoom($filterStart, $filterEnd)) { - //TODO: replace - unset($rooms['']); - if (!empty($rooms)) { - $entry['title'] .= '('. implode('), (', array_slice(array_keys($rooms), 0, 3)) .')'; - } - } - - // add the lecturer - $db = DBManager::get(); - $stmt = $db->prepare( - "SELECT `Nachname` - FROM `auth_user_md5` - JOIN `seminar_user` USING (`user_id`) - WHERE `seminar_id` = :course_id - ORDER BY `Nachname` - LIMIT 4" - ); - $stmt->execute(['course_id' => $course->id]); - $lecturers = $stmt->fetchAll(PDO::FETCH_COLUMN, 0); - - $entry['content'] .= " (". implode(', ', array_slice($lecturers, 0, 3)) - . (count($lecturers) > 3 ? ' et al.' : '').')'; - - - $entry['url'] = URLHelper::getLink('dispatch.php/calendar/schedule/entry/' . $seminar_id - . '/' . $cycle->getMetaDateId()); - $entry['onClick'] = "function (id) { - var ids = id.split('-'); - STUDIP.Schedule.showSeminarDetails(ids[0], ids[1]); - }"; - - - // check the settings for this entry - $member = CourseMember::find([$course->id, $user_id]); - $entry['type'] = $member ? 'sem' : 'virtual'; - - $stmt = $db->prepare('SELECT * FROM schedule_seminare WHERE seminar_id = ? AND user_id = ? AND metadate_id = ?'); - $stmt->execute([$course->id, $user_id, $cycle->getMetaDateId()]); - $details = $stmt->fetch(); - - if ($entry['type'] === 'virtual') { - $entry['color'] = $details ? ($details['color'] ?: DEFAULT_COLOR_VIRTUAL) : DEFAULT_COLOR_VIRTUAL; - $entry['icons'][] = [ - 'image' => Icon::create('tag', Icon::ROLE_INFO_ALT)->asImagePath(), - 'title' => _("Dies ist eine vorgemerkte Veranstaltung") - ]; - } else { - $entry['color'] = !empty($details['color']) ? $details['color'] : ($member->gruppe % 9 + 1); - } - $entry['visible'] = $details ? $details['visible'] : 1; - - // show an unhide icon if entry is invisible - if (!$entry['visible']) { - $entry['url'] .= '/?show_hidden=1'; - - $bind_url = URLHelper::getLink('dispatch.php/calendar/schedule/bind/' - . $seminar_id . '/' . $cycle->getMetaDateId() . '/?show_hidden=1'); - - $entry['icons'][] = [ - 'url' => $bind_url, - 'image' => Icon::create('visibility-invisible', Icon::ROLE_INFO_ALT)->asImagePath(), - 'onClick' => "function(id) { window.location = '". $bind_url ."'; }", - 'title' => _("Diesen Eintrag wieder einblenden"), - ]; - } - - // show a hide-icon if the entry is not virtual - else if ($entry['type'] != 'virtual') { - $unbind_url = URLHelper::getLink('dispatch.php/calendar/schedule/unbind/' - . $seminar_id . '/' . $cycle->getMetaDateId()); - $entry['icons'][] = [ - 'url' => $unbind_url, - 'image' => Icon::create('visibility-visible', Icon::ROLE_INFO_ALT)->asImagePath(), - 'onClick' => "function(id) { window.location = '". $unbind_url ."'; }", - 'title' => _("Diesen Eintrag ausblenden"), - ]; - - } - - $ret[] = $entry; - } - } - - return $ret; - } - - /** - * Deletes the schedule entries of one user for one seminar. - * - * @param string $user_id the user of the schedule - * @param string $seminar_id the seminar which entries should be deleted - */ - static function deleteSeminarEntries($user_id, $seminar_id) - { - $stmt = DBManager::get()->prepare($query = "DELETE FROM schedule_seminare - WHERE user_id = ? AND seminar_id = ?"); - $stmt->execute([$user_id, $seminar_id]); - NotificationCenter::postNotification('ScheduleSeminarDidDelete', $GLOBALS['user']->id, $seminar_id); - } - - /** - * Returns an array of CalendarColumn's, containing the seminar-entries - * for the passed user in the passed semester. - * The start- and end-hour are used to constrain the returned - * entries to the passed time-period. - * Seminar-entries can be hidden, so you can opt-in to fetch the hidden - * ones as well. - * - * @param string $user_id the ID of the user - * @param string $semester an array containing the "beginn" of the semester - * @param int $start_hour the start hour - * @param int $end_hour the end hour - * @param string $show_hidden optional; true to show hidden, false otherwise - * @return array an array containing the properties of the entry - */ - static function getSeminarEntries($user_id, $semester, $start_hour, $end_hour, $show_hidden = false) - { - $seminars = []; - - // get all virtually added seminars - $stmt = DBManager::get()->prepare("SELECT * FROM schedule_seminare as c - LEFT JOIN seminare as s ON (s.Seminar_id = c.Seminar_id) - LEFT JOIN semester_courses ON (semester_courses.course_id = s.Seminar_id) - WHERE c.user_id = ? AND s.start_time <= ? AND - (semester_courses.semester_id IS NULL OR semester_courses.semester_id = ?)"); - $stmt->execute([$user_id, $semester['beginn'], $semester['id']]); - - while ($entry = $stmt->fetch()) { - $seminars[$entry['seminar_id']] = [ - 'Seminar_id' => $entry['seminar_id'] - ]; - } - - // fetch seminar-entries - $stmt = DBManager::get()->prepare("SELECT s.Seminar_id FROM seminar_user as su - LEFT JOIN seminare as s USING (Seminar_id) - LEFT JOIN semester_courses ON (semester_courses.course_id = s.Seminar_id) - WHERE su.user_id = :userid - AND s.start_time <= :begin - AND (semester_courses.semester_id IS NULL OR semester_courses.semester_id = :semester_id) - "); - $stmt->bindValue(':begin', $semester['beginn']); - $stmt->bindValue(':semester_id', $semester['semester_id']); - $stmt->bindValue(':userid', $user_id); - $stmt->execute(); - - while ($entry = $stmt->fetch(PDO::FETCH_ASSOC)) { - $seminars[$entry['Seminar_id']] = [ - 'Seminar_id' => $entry['Seminar_id'] - ]; - } - - $ret = []; - foreach ($seminars as $data) { - $entries = self::getSeminarEntry($data['Seminar_id'], $user_id, false, $semester); - - foreach ($entries as $entry) { - if (($entry['start'] >= $start_hour * 100 && $entry['start'] <= $end_hour * 100 - || $entry['end'] >= $start_hour * 100 && $entry['end'] <= $end_hour * 100) - && ($show_hidden || (!$show_hidden && $entry['visible']))) { - $day_number = ($entry['day'] + 6) % 7; - if (!isset($ret[$day_number])) { - $ret[$day_number] = new CalendarColumn(); - } - - $ret[$day_number]->addEntry($entry); - } - } - } - - return $ret; - - } - - - /** - * Returns an array of CalendarColumn's, containing the seminar-entries - * for the passed user in the passed semester belonging to the passed institute. - * The start- and end-hour are used to constrain the returned - * entries to the passed time-period. - * - * @param string $user_id the ID of the user - * @param array $semester an array containing the "beginn" of the semester - * @param int $start_hour the start hour - * @param int $end_hour the end hour - * @param string $institute_id the ID of the institute - * @return array an array containing the entries - */ - static function getSeminarEntriesForInstitute($user_id, $semester, $start_hour, $end_hour, $institute_id) - { - $ret = []; - - // fetch seminar-entries - $visibility_perms = $GLOBALS['perm']->have_perm(Config::get()->SEM_VISIBILITY_PERM); - $stmt = DBManager::get()->prepare("SELECT * - FROM seminare - LEFT JOIN semester_courses ON (semester_courses.course_id = seminare.Seminar_id) - WHERE Institut_id = :institute - AND start_time <= :begin - AND (semester_courses.semester_id IS NULL OR semester_courses.semester_id = :semester_id) " - . (!$visibility_perms ? " AND visible='1'" : "")); - - $stmt->bindParam(':begin', $semester['beginn']); - $stmt->bindParam(':semester_id', $semester['semester_id']); - $stmt->bindParam(':institute', $institute_id); - $stmt->execute(); - - $seminars = $stmt->fetchAll(PDO::FETCH_ASSOC); - - foreach ($seminars as $data) { - $entries = self::getSeminarEntry($data['Seminar_id'], $user_id, false, $semester); - - foreach ($entries as $entry) { - unset($entry['url']); - $entry['onClick'] = 'function(id) { STUDIP.Schedule.showInstituteDetails(id); }'; - - if (($entry['start'] >= $start_hour * 100 && $entry['start'] <= $end_hour * 100 - || $entry['end'] >= $start_hour * 100 && $entry['end'] <= $end_hour * 100)) { - - $entry['color'] = DEFAULT_COLOR_SEM; - - $day_number = ($entry['day'] + 6) % 7; - if (!isset($ret[$day_number])) { - $ret[$day_number] = CalendarColumn::create($entry['day']); - } - - $ret[$day_number]->addEntry($entry); - } - } - } - - return $ret; - } - - - /** - * Returns the ID of the cycle of a course specified by start and end. - * - * @param string $course_id The ID of a course. - * @param string $start the start of the cycle - * @param string $end the end of the cycle - * @return string $day numeric day - */ - static function getSeminarCycleId($course_id, $start, $end, $day) - { - $ret = []; - - $day = ($day + 1) % 7; - - $regular_dates = SeminarCyCleDate::findBySeminar($course_id); - - foreach ($regular_dates as $cycle) { - $cycle_start = preg_replace('/\:00$/', '', $cycle->start_time); - $cycle_end = preg_replace('/\:00$/', '', $cycle->end_time); - if ( - leadingZero($cycle_start) == $start - && leadingZero($cycle_end) == $end - && $cycle->weekday == $day - ) { - $ret[] = $cycle; - } - } - - return $ret; - } - - /** - * check if the passed cycle of the passed id is visible - * for the currently logged in user int the schedule - * - * @param string the ID of the course - * @param string the ID of the cycle - * @return bool true if visible, false otherwise - */ - static function isSeminarVisible($seminar_id, $cycle_id) - { - $stmt = DBManager::get()->prepare("SELECT visible - FROM schedule_seminare - WHERE seminar_id = ? AND user_id = ? AND metadate_id = ?"); - $stmt->execute([$seminar_id, $GLOBALS['user']->id, $cycle_id]); - if (!$data = $stmt->fetch()) { - return true; - } else { - return $data['visible'] ? true : false; - } - } - - /** - * Returns an array of CalendarColumn's, containing the seminar-entries - * for the passed user (in the passed semester belonging to the passed institute) - * and the user-defined schedule-entries. - * The start- and end-hour are used to constrain the returned - * entries to the passed time-period. The passed days constrain the entries - * to these. - * Seminar-entries can be hidden, so you can opt-in to fetch the hidden - * ones as well. - * - * @param string $user_id the user's ID - * @param string $semester the data for the semester to be displayed - * @param int $start_hour the start hour of the entries - * @param int $end_hour the end hour of the entries - * @param string $institute_id the institute's ID - * @param array $days days to be displayed - * @param bool $show_hidden filters hidden entries - * @return array an array of entries - */ - static function getInstituteEntries($user_id, $semester, $start_hour, $end_hour, $institute_id, $days, $show_hidden = false) - { - // merge the schedule and seminar-entries - $entries = self::getScheduleEntries($user_id, $start_hour, $end_hour, false); - $seminar = self::getSeminarEntriesForInstitute($user_id, $semester, $start_hour, $end_hour, $institute_id, $show_hidden); - - foreach($seminar as $day => $entry_column) { - foreach ($entry_column->getEntries() as $entry) { - if (!isset($entries[$day])) { - $entries[$day] = CalendarColumn::create($day); - } - $entries[$day]->addEntry($entry); - } - } - - return self::addDayChooser($entries, $days); - } - - /** - * - * - * @param string $user_id - * @param mixed $semester the data for the semester to be displayed - */ - - /** - * Returns an array of CalendarColumn's, containing the seminar-entries - * for the passed user (in the passed semester) and the user-defined schedule-entries. - * The start- and end-hour are used to constrain the returned - * entries to the passed time-period. The passed days constrain the entries - * to these. - * Seminar-entries can be hidden, so you can opt-in to fetch the hidden - * ones as well. - * - * @param string $user_id the user's ID - * @param string $semester the data for the semester to be displayed - * @param int $start_hour the start hour of the entries - * @param int $end_hour the end hour of the entries - * @param array $days days to be displayed - * @param bool $show_hidden filters hidden entries - * @return array - */ - static function getEntries($user_id, $semester, $start_hour, $end_hour, $days, $show_hidden = false) - { - // merge the schedule and seminar-entries - $entries = self::getScheduleEntries($user_id, $start_hour, $end_hour, false); - $seminar = self::getSeminarEntries($user_id, $semester, $start_hour, $end_hour, $show_hidden); - foreach($seminar as $day => $entry_column) { - foreach ($entry_column->getEntries() as $entry) { - if (!isset($entries[$day])) { - $entries[$day] = CalendarColumn::create($day); - } - $entries[$day]->addEntry($entry); - } - } - - return self::addDayChooser($entries, $days); - } - - /** - * adds title and link to CalendarColumn-objects and sorts the objects to be - * displayed correctly in the calendar-view - * - * @param array $entries an array of CalendarColumn-objects - * @param array $days an array of int's, denoting the days to be displayed - * @return array - */ - static function addDayChooser($entries, $days, $controller = 'schedule') { - $day_names = [_("Mo"),_("Di"),_("Mi"), - _("Do"),_("Fr"),_("Sa"),_("So")]; - - $ret = []; - - foreach ($days as $day) { - if (!isset($entries[$day])) { - $ret[$day] = CalendarColumn::create($day); - } else { - $ret[$day] = $entries[$day]; - } - - if (sizeof($days) == 1) { - $ret[$day]->setTitle($day_names[$day] .' ('. _('zurück zur Wochenansicht') .')') - ->setURL('dispatch.php/calendar/'. $controller .'/index'); - } else { - $ret[$day]->setTitle($day_names[$day]) - ->setURL('dispatch.php/calendar/'. $controller .'/index/'. $day); - } - } - - return $ret; - } - - /** - * Toggle entries' visibility - * - * @param string $seminar_id the course's ID - * @param string $cycle_id the cycle's ID - * @param bool $visible the value to switch to - * @return void - */ - static function adminBind($seminar_id, $cycle_id, $visible = true) - { - $stmt = DBManager::get()->prepare("SELECT * FROM schedule_seminare - WHERE seminar_id = ? AND user_id = ? AND metadate_id = ?"); - $stmt->execute([$seminar_id, $GLOBALS['user']->id, $cycle_id]); - - if ($stmt->fetch()) { - $stmt = DBManager::get()->prepare("UPDATE schedule_seminare - SET visible = ? - WHERE seminar_id = ? AND user_id = ? AND metadate_id = ?"); - } else { - $stmt = DBManager::get()->prepare("INSERT INTO schedule_seminare - (visible, seminar_id, user_id, metadate_id) - VALUES(?, ?, ?, ?)"); - } - - $stmt->execute([$visible ? '1' : '0', $seminar_id, $GLOBALS['user']->id, $cycle_id]); - - } - - /** - * Switch a seminars' cycle to invisible. - * - * @param string $seminar_id the course's ID - * @param string $cycle_id the cycle's ID - * @return void - */ - public static function unbind($seminar_id, $cycle_id = null) - { - $stmt = DBManager::get()->prepare("SELECT su.*, sc.seminar_id as present - FROM seminar_user as su - LEFT JOIN schedule_seminare as sc ON (su.Seminar_id = sc.seminar_id - AND sc.user_id = su.user_id AND sc.metadate_id = ?) - WHERE su.Seminar_id = ? AND su.user_id = ?"); - $stmt->execute([$cycle_id, $seminar_id, $GLOBALS['user']->id]); - - // if we are participant of the seminar, just hide the entry - if ($data = $stmt->fetch()) { - if ($data['present']) { - $stmt = DBManager::get()->prepare("UPDATE schedule_seminare - SET visible = 0 - WHERE seminar_id = ? AND user_id = ? AND metadate_id = ?"); - } else { - $stmt = DBManager::get()->prepare("INSERT IGNORE INTO schedule_seminare - (seminar_id, user_id, metadate_id, visible) - VALUES(?, ?, ?, 0)"); - } - $stmt->execute([$seminar_id, $GLOBALS['user']->id, $cycle_id]); - } - - // otherwise delete the entry - else { - $stmt = DBManager::get()->prepare("DELETE FROM schedule_seminare - WHERE seminar_id = ? AND user_id = ?"); - $stmt->execute([$seminar_id, $GLOBALS['user']->id]); - NotificationCenter::postNotification('ScheduleSeminarDidDelete', $GLOBALS['user']->id, $seminar_id); - } - } - - /** - * Switch a seminars' cycle to visible. - * - * @param string $seminar_id the course's ID - * @param string $cycle_id the cycle's ID - * @return void - */ - static function bind($seminar_id, $cycle_id) - { - $stmt = DBManager::get()->prepare("UPDATE schedule_seminare - SET visible = 1 - WHERE seminar_id = ? AND user_id = ? AND metadate_id = ?"); - - $stmt->execute([$seminar_id, $GLOBALS['user']->id, $cycle_id]); - } - - /** - * Get the schedule_settings from the user's config - * - * @param string $user_id the user to get the settings for, defaults - * to current user - * @return mixed the settings - */ - static function getScheduleSettings($user_id = false) - { - if (!$user_id) { - $user_id = $GLOBALS['user']->id; - } - - $schedule_settings = UserConfig::get($user_id)->SCHEDULE_SETTINGS; - - // convert old settings, if necessary (mein_stundenplan.php) - if (!$schedule_settings['converted']) { - $schedule_settings['glb_days'] = [0, 1, 2, 3, 4]; - $schedule_settings['converted'] = true; - } - - return $schedule_settings; - } - - /** - * Transforms day settings from SCHEDULE_SETTINGS::glb_days to valid - * days that can be displayed. - * - * @param array $input Input from SCHEDULE_SETTINGS - * @return array - */ - public static function getDisplayedDays(array $input) - { - $days = []; - foreach ($input as $key => $value) { - // Fallback for old entries (["mo": true, ...]) - if (!is_numeric($key) || !is_numeric($value)) { - $days = [6, 0, 1, 2, 3]; - break; - } - $days[$key] = ($value + 6) % 7; - } - return $days; - } - - /** - * Return the semester-entry for the current semester - * - * @return mixed the current semester - */ - static function getCurrentSemester() - { - return Semester::findCurrent(); - } - - /** - * Create a CalendarWeekView (a schedule) for an institute - * for the current user and return it. - * - * @param string $institute_id the institute to get the calendar for - * @param bool $show_hidden show hidden entries - * @param mixed $semester the semester to use - * @param mixed $days the days to consider - * - * @return CalendarWeekView - */ - static function getInstCalendarView($institute_id, $show_hidden = false, $semester = false, $days = false) - { - $schedule_settings = self::getScheduleSettings(); - - if (!$semester) { - $semester = self::getCurrentSemester(); - } - - if (!$days) { - $days = self::getDisplayedDays($schedule_settings['glb_days']); - } - - $user_id = $GLOBALS['user']->id; - - $entries = CalendarScheduleModel::getInstituteEntries( - $user_id, - $semester, - $schedule_settings['glb_start_time'], - $schedule_settings['glb_end_time'], - $institute_id, - $days, - $show_hidden - ); - - $view = new CalendarWeekView($entries, 'schedule'); - - $view->setHeight(40 + (20 * $schedule_settings['zoom'])); - $view->setRange($schedule_settings['glb_start_time'], $schedule_settings['glb_end_time']); - - // group entries in institute calendar - $view->groupEntries(); // if enabled, group entries with same start- and end-date - - return $view; - } - - /** - * Create a CalendarWeekView (a schedule) for the current user and return it. - * - * @param string $user_id the institute to get the calendar for - * @param bool $show_hidden show hidden entries - * @param mixed $semester the semester to use - * @param mixed $days the days to consider - * - * @return CalendarWeekView - */ - static function getUserCalendarView($user_id, $show_hidden = false, $semester = false, $days = false) - { - $schedule_settings = self::getScheduleSettings($user_id); - - if (!$semester) { - $semester = self::getCurrentSemester(); - } - - if (!$days) { - $days = self::getDisplayedDays($schedule_settings['glb_days']); - } - - $entries = CalendarScheduleModel::getEntries( - $user_id, - $semester, - $schedule_settings['glb_start_time'], - $schedule_settings['glb_end_time'], - $days, - $show_hidden - ); - - $view = new CalendarWeekView($entries, 'schedule'); - - $view->setHeight(40 + (20 * ($schedule_settings['zoom'] ?? 0))); - $view->setRange($schedule_settings['glb_start_time'], $schedule_settings['glb_end_time']); - $view->setInsertFunction("function (entry, column, hour, end_hour) { - STUDIP.Schedule.newEntry(entry, column, hour, end_hour) - }"); - - return $view; - } -} diff --git a/lib/classes/calendar/Helper.php b/lib/classes/calendar/Helper.php index cd3163afe2cabfa7932df9c23e0f1e4bb2ddf2b2..dd860070982fd6cf0c243dcfb5e136837156efec 100644 --- a/lib/classes/calendar/Helper.php +++ b/lib/classes/calendar/Helper.php @@ -108,4 +108,65 @@ class Helper return $default_date; } + + /** + * Constructs a Fullcalendar instance of the schedule for the current user. + * + * @param string $semester_id The ID of the semester to be used. Defaults to an empty string + * which in turn means that the current semester shall be used. + * + * @param bool $show_hidden_courses Whether to include hidden courses in the schedule (true) + * or not (false). Defaults to false. + * + * @return \Studip\Fullcalendar A fullcalendar instance for the schedule of the current user. + */ + public static function getScheduleFullcalendar( + string $semester_id = '', + bool $show_hidden_courses = false + ) : \Studip\Fullcalendar + { + if (!$semester_id) { + $semester_id = \Semester::findCurrent()?->id ?? ''; + } + $calendar_settings = \User::findCurrent()->getConfiguration()->CALENDAR_SETTINGS ?? []; + + return new \Studip\Fullcalendar( + _('Stundenplan'), + [ + 'editable' => false, + 'selectable' => false, + 'dialog_size' => 'auto', + 'minTime' => sprintf('%02u:00', $calendar_settings['start'] ?? 8), + 'maxTime' => sprintf('%02u:00', $calendar_settings['end'] ?? 20), + 'allDaySlot' => false, + 'header' => [ + 'left' => '', + 'right' => '' + ], + 'views' => [ + 'timeGridWeek' => [ + 'columnHeaderFormat' => ['weekday' => 'long'], + 'weekends' => $calendar_settings['type_week'] === 'LONG', + 'slotDuration' => self::getCalendarSlotDuration('week'), + ] + ], + 'defaultView' => 'timeGridWeek', + 'defaultDate' => date('Y-m-d'), + 'timeGridEventMinHeight' => 20, + 'eventSources' => [ + [ + 'url' => \URLHelper::getURL( + 'dispatch.php/calendar/schedule/data', + ['show_hidden' => $show_hidden_courses] + ), + 'method' => 'GET', + 'extraParams' => [ + 'semester_id' => $semester_id, + 'full_semester_time_range' => false + ] + ] + ] + ] + ); + } } diff --git a/lib/models/Course.php b/lib/models/Course.php index ea6676c14b6902e14e86542afacdcaef0e2d5862..b252063120b20b88ada5d2c94f2ed553ad61e7cb 100644 --- a/lib/models/Course.php +++ b/lib/models/Course.php @@ -334,7 +334,7 @@ class Course extends SimpleORMap implements Range, PrivacyObject, StudipItem, Fe WikiPageConfig::deleteByRange_id($course->id); //Remove all entries of the course in calendars: - $query = 'DELETE FROM `schedule_seminare` WHERE `seminar_id` = ?'; + $query = 'DELETE FROM `schedule_courses` WHERE `course_id` = ?'; $statement = DBManager::get()->execute($query, [$course->id]); //Remove connections to other e-learning systems: @@ -1114,7 +1114,13 @@ class Course extends SimpleORMap implements Range, PrivacyObject, StudipItem, Fe } //Delete course entries in the schedule: - CalendarScheduleModel::deleteSeminarEntries($user->id, $this->id); + ScheduleCourseDate::deleteBySQL( + 'user_id = :user_id AND course_id = :course_id', + [ + 'user_id' => $user->id, + 'course_id' => $this->id + ] + ); //Log the event: StudipLog::log('SEM_USER_ADD', $this->id, $user->id, $permission_level, 'Wurde in die Veranstaltung eingetragen'); diff --git a/lib/models/CourseDate.php b/lib/models/CourseDate.php index 3f139045effe3b1708da83a0996eca9260251585..943bf2378550c00d4ce43fb51b668e4c9a8b4c60 100644 --- a/lib/models/CourseDate.php +++ b/lib/models/CourseDate.php @@ -549,7 +549,7 @@ class CourseDate extends SimpleORMap implements PrivacyObject, Event IFNULL(`termine`.`metadate_id`, '') = '' OR `termine`.`metadate_id` NOT IN ( SELECT `metadate_id` - FROM `schedule_seminare` + FROM `schedule_courses` WHERE `user_id` = :user_id AND `visible` = 0 ) diff --git a/lib/models/CourseExDate.php b/lib/models/CourseExDate.php index 2d3afea2e92479e530507548b125d10a6de2ed52..eb0f90a8e10c072e43f0d526039fe5d0c5553632 100644 --- a/lib/models/CourseExDate.php +++ b/lib/models/CourseExDate.php @@ -261,7 +261,7 @@ class CourseExDate extends SimpleORMap implements PrivacyObject, Event IFNULL(`ex_termine`.`metadate_id`, '') = '' OR `ex_termine`.`metadate_id` NOT IN ( SELECT `metadate_id` - FROM `schedule_seminare` + FROM `schedule_courses` WHERE `user_id` = :user_id AND `visible` = 0 ) diff --git a/lib/models/SeminarCycleDate.php b/lib/models/SeminarCycleDate.php index ae851217178890fae9e976c1e858f39422dbbe32..13b2520883c4b0b4f5f5ca954ab3b10754a0fbe2 100644 --- a/lib/models/SeminarCycleDate.php +++ b/lib/models/SeminarCycleDate.php @@ -442,7 +442,7 @@ class SeminarCycleDate extends SimpleORMap $result = parent::delete(); if ($result) { - $stmt = DBManager::get()->prepare('DELETE FROM schedule_seminare WHERE metadate_id = :metadate_id'); + $stmt = DBManager::get()->prepare('DELETE FROM schedule_courses WHERE metadate_id = :metadate_id'); $stmt->execute(['metadate_id' => $metadate_id]); StudipLog::log('SEM_DELETE_CYCLE', $seminar_id, null, $cycle_info); diff --git a/lib/models/calendar/CalendarCourseDate.php b/lib/models/calendar/CalendarCourseDate.php index 370dcd14449dc1b7bca932ea410efcdf63290f22..b312bda3094601c00d168acf9c1e159d705f1c63 100644 --- a/lib/models/calendar/CalendarCourseDate.php +++ b/lib/models/calendar/CalendarCourseDate.php @@ -24,7 +24,7 @@ class CalendarCourseDate extends CourseDate IFNULL(`termine`.`metadate_id`, '') = '' OR `termine`.`metadate_id` NOT IN ( SELECT `metadate_id` - FROM `schedule_seminare` + FROM `schedule_courses` WHERE `user_id` = :user_id AND `visible` = 0 ) diff --git a/lib/models/calendar/CalendarCourseExDate.php b/lib/models/calendar/CalendarCourseExDate.php index eb23aeb79d53964681157a1f9d63c9753791d774..5e979a816f48554afb467f74e3ba0ae7b1702f9f 100644 --- a/lib/models/calendar/CalendarCourseExDate.php +++ b/lib/models/calendar/CalendarCourseExDate.php @@ -19,7 +19,7 @@ class CalendarCourseExDate extends CourseExDate IFNULL(`ex_termine`.`metadate_id`, '') = '' OR `ex_termine`.`metadate_id` NOT IN ( SELECT `metadate_id` - FROM `schedule_seminare` + FROM `schedule_courses` WHERE `user_id` = :user_id AND `visible` = 0 ) diff --git a/lib/models/calendar/ScheduleCourseDate.php b/lib/models/calendar/ScheduleCourseDate.php new file mode 100644 index 0000000000000000000000000000000000000000..9d05a7231b2e57e51a688d54e991efaae72c3cd8 --- /dev/null +++ b/lib/models/calendar/ScheduleCourseDate.php @@ -0,0 +1,42 @@ +<?php +/** + * ScheduleCourseDate.php - Model class for regular course dates + * in the schedule view. + * + * 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 + * @since 6.0 + * + * @property string $user_id database column + * @property string $course_id database column + * @property string $metadate_id database_column + * @property string $visible database column + * @property string $mkdate database column + * @property string $chdate database column + */ +class ScheduleCourseDate extends SimpleORMap +{ + protected static function configure($config = []) + { + $config['db_table'] = 'schedule_courses'; + $config['belongs_to']['user'] = [ + 'class_name' => User::class, + 'foreign_key' => 'user_id', + ]; + $config['belongs_to']['course'] = [ + 'class_name' => Course::class, + 'foreign_key' => 'course_id', + ]; + $config['belongs_to']['regular_date'] = [ + 'class_name' => SeminarCycleDate::class, + 'foreign_key' => 'metadate_id' + ]; + parent::configure($config); + } +} diff --git a/lib/models/calendar/ScheduleEntry.php b/lib/models/calendar/ScheduleEntry.php new file mode 100644 index 0000000000000000000000000000000000000000..f1e23fddaff6be45ca025417b242cd74e346b72b --- /dev/null +++ b/lib/models/calendar/ScheduleEntry.php @@ -0,0 +1,341 @@ +<?php +/** + * ScheduleEntry.php - Model class for regular dates + * in the schedule view that are not bound to a course. + * + * 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 + * @since 6.0 + * + * @property string $id database column + * @property string $start_time database column + * @property string $end_time database column + * @property string $dow database column + * @property string $label database column + * @property string $content database column + * @property string $user_id database column + * @property string $mkdate database column + * @property string $chdate database column + */ +class ScheduleEntry extends SimpleORMap implements Event +{ + protected static function configure($config = []) + { + $config['db_table'] = 'schedule_entries'; + $config['belongs_to']['user'] = [ + 'class_name' => User::class, + 'foreign_key' => 'user_id' + ]; + parent::configure($config); + } + + /** + * A helper method to set the content of the start attribute by a formatted date + * in the format HH:mm. + * + * @param string $formatted_start The formatted date in the format HH:mm. + */ + public function setFormattedStart(string $formatted_start) : void + { + $this->start_time = str_replace(':', '', $formatted_start); + } + + /** + * A helper method to set the content of the end attribute by a formatted date + * in the format HH:mm. + * + * @param string $formatted_end The formatted date in the format HH:mm. + */ + public function setFormattedEnd(string $formatted_end) : void + { + $this->end_time = str_replace(':', '', $formatted_end); + } + + /** + * Formats the start time for human-readable output. + * + * @return string The start time in the format HH:mm or an empty string in case + * the format stored in the start attribute is not supported. + */ + public function getFormattedStart() : string + { + if (strlen($this->start_time) === 3) { + return '0' . substr($this->start_time, 0, 1) . ':' . substr($this->start_time, 1, 2); + } + + if (strlen($this->start_time) === 4) { + return substr($this->start_time, 0, 2) . ':' . substr($this->start_time, 2, 2); + } + + //Invalid date format: + return ''; + } + + /** + * Formats the end time for human-readable output. + * + * @return string The end time in the format HH:mm or an empty string in case + * the format stored in the end attribute is not supported. + */ + public function getFormattedEnd() : string + { + if (strlen($this->end_time) === 3) { + return '0' . substr($this->end_time, 0, 1) . ':' . substr($this->end_time, 1, 2); + } + + if (strlen($this->end_time) === 4) { + return substr($this->end_time, 0, 2) . ':' . substr($this->end_time, 2, 2); + } + + //Invalid date format: + return ''; + } + + /** + * @inheritDoc + */ + public static function getEvents(DateTime $begin, DateTime $end, string $range_id): array + { + return self::findBySQL( + "`user_id` = :range_id + AND `start` < :end AND `end` > :start + AND `day` >= :start_day AND day <= :end_day", + [ + 'range_id' => $range_id, + 'start' => $begin->format('Hi'), + 'end' => $end->format('Hi'), + 'start_day' => $begin->format('N'), + 'end_day' => $end->format('N') + ] + ); + } + + /** + * @inheritDoc + */ + public function getObjectId(): string + { + return $this->id; + } + + /** + * @inheritDoc + */ + public function getPrimaryObjectID(): string + { + return $this->user_id; + } + + /** + * @inheritDoc + */ + public function getObjectClass(): string + { + return self::class; + } + + /** + * @inheritDoc + */ + public function getTitle(): string + { + return $this->label; + } + + /** + * @inheritDoc + */ + public function getBegin(): DateTime + { + //Map the entry to the current week: + $date = new DateTime(); + $date->setTimestamp(strtotime('midnight this week')); + if ($this->dow > 1) { + $days_to_add = $this->dow - 1; + $date = $date->add(new DateInterval(sprintf('P%dD', $days_to_add))); + } + $time_parts = explode(':', $this->getFormattedStart()); + $date->setTime($time_parts[0], $time_parts[1]); + return $date; + } + + /** + * @inheritDoc + */ + public function getEnd(): DateTime + { + //Map the entry to the current week: + $date = new DateTime(); + $date->setTimestamp(strtotime('midnight this week')); + if ($this->dow > 1) { + $days_to_add = $this->dow - 1; + $date = $date->add(new DateInterval(sprintf('P%dD', $days_to_add))); + } + $time_parts = explode(':', $this->getFormattedEnd()); + $date->setTime($time_parts[0],$time_parts[1]); + return $date; + } + + /** + * @inheritDoc + */ + public function getDuration(): DateInterval + { + return $this->getEnd()->diff($this->getBegin()); + } + + /** + * @inheritDoc + */ + public function getLocation(): string + { + //No location supported. + return ''; + } + + /** + * @inheritDoc + */ + public function getUniqueId(): string + { + return implode('_', [ + Config::get()->STUDIP_INSTALLATION_ID, + self::class, + $this->id, + ]); + } + + /** + * @inheritDoc + */ + public function getDescription(): string + { + return $this->content; + } + + /** + * @inheritDoc + */ + public function getAdditionalDescriptions(): array + { + //No additional description supported. + return []; + } + + /** + * @inheritDoc + */ + public function isAllDayEvent(): bool + { + return $this->start_time === '000' && $this->end_time === '2359'; + } + + /** + * @inheritDoc + */ + public function isWritable(string $user_id): bool + { + //Only the owner and root may edit the entry: + return $user_id === $this->user_id + || $GLOBALS['perm']->have_perm('root', $user_id); + } + + /** + * @inheritDoc + */ + public function getCreationDate(): DateTime + { + $date = new DateTime(); + $date->setTimestamp($this->mkdate); + return $date; + } + + /** + * @inheritDoc + */ + public function getModificationDate(): DateTime + { + $date = new DateTime(); + $date->setTimestamp($this->chdate); + return $date; + } + + /** + * @inheritDoc + */ + public function getImportDate(): DateTime + { + //The import date is not supported. Use mkdate instead. + $date = new DateTime(); + $date->setTimestamp($this->mkdate); + return $date; + } + + /** + * @inheritDoc + */ + public function getAuthor(): ?User + { + return $this->user; + } + + /** + * @inheritDoc + */ + public function getEditor(): ?User + { + return $this->user; + } + + /** + * @inheritDoc + */ + public function toEventData(string $user_id): \Studip\Calendar\EventData + { + return new \Studip\Calendar\EventData( + $this->getBegin(), + $this->getEnd(), + $this->label, + ['schedule-entry'], + '#000000', + '#ffffff', + $this->isWritable($user_id), + self::class, + $this->id, + User::class, + $this->user_id, + User::class, + $this->user_id, + [ + 'show' => URLHelper::getURL('dispatch.php/calendar/schedule/entry/' . $this->id) + ], + [], + '', + '#000000', + $this->isAllDayEvent() + ); + } + + /** + * Creates a string representation of the schedule entry. + * + * @return string A human-readable string describing the schedule entry. + */ + public function toString() : string + { + return studip_interpolate( + _('Termin jeden %{dow} von %{start_time} bis %{end_time} Uhr'), + [ + 'dow' => getWeekday($this->dow % 7, false), + 'start_time' => $this->getFormattedStart(), + 'end_time' => $this->getFormattedEnd() + ] + ); + } +} diff --git a/lib/modules/ScheduleWidget.php b/lib/modules/ScheduleWidget.php index c4ab769fe5d2c7a502c3fb1baded55b5c99b1b80..42a08146d9cba441ab631f29d6a9a8eec3b0325e 100644 --- a/lib/modules/ScheduleWidget.php +++ b/lib/modules/ScheduleWidget.php @@ -39,16 +39,8 @@ class ScheduleWidget extends CorePlugin implements PortalPlugin */ public function getPortalTemplate() { - $view = CalendarScheduleModel::getUserCalendarView( - $GLOBALS['user']->id, - false, - false, - $days = [0, 1, 2, 3, 4] - ); - - $template = $GLOBALS['template_factory']->open('shared/string'); - $template->content = CalendarWidgetView::createFromWeekView($view)->render(); - + $template = $GLOBALS['template_factory']->open('start/schedule_widget'); + $template->fullcalendar = \Studip\Calendar\Helper::getScheduleFullcalendar()->render(); return $template; } } diff --git a/lib/navigation/CalendarNavigation.php b/lib/navigation/CalendarNavigation.php index 6b59d02d19695125a343179c6a61a2e191d2799c..8df16b3c4b260b05a37b5a33effa55402f90e80a 100644 --- a/lib/navigation/CalendarNavigation.php +++ b/lib/navigation/CalendarNavigation.php @@ -24,7 +24,7 @@ class CalendarNavigation extends Navigation $main_url = URLHelper::getURL('dispatch.php/calendar/calendar', ['defaultDate' => date('Y-m-d')]); if (!$GLOBALS['perm']->have_perm('admin') && Config::get()->SCHEDULE_ENABLE) { $title = _('Stundenplan'); - $main_url = URLHelper::getURL('dispatch.php/calendar/schedule'); + $main_url = URLHelper::getURL('dispatch.php/calendar/schedule/index'); } parent::__construct($title, $main_url); @@ -37,12 +37,10 @@ class CalendarNavigation extends Navigation */ public function initSubNavigation() { - global $perm, $atime; - parent::initSubNavigation(); - if (!$perm->have_perm('admin') && Config::get()->SCHEDULE_ENABLE) { - $navigation = new Navigation(_('Stundenplan'), 'dispatch.php/calendar/schedule'); + if (!$GLOBALS['perm']->have_perm('admin') && Config::get()->SCHEDULE_ENABLE) { + $navigation = new Navigation(_('Stundenplan'), 'dispatch.php/calendar/schedule/index'); $this->addSubNavigation('schedule', $navigation); } diff --git a/resources/assets/stylesheets/scss/calendar.scss b/resources/assets/stylesheets/scss/calendar.scss index 4ad94b885ce967c5d1fb052ff7343695503413df..0effce153d3e9acbc0d73fdf6fb9141d9126e088 100644 --- a/resources/assets/stylesheets/scss/calendar.scss +++ b/resources/assets/stylesheets/scss/calendar.scss @@ -125,6 +125,22 @@ border-bottom: 1px solid $group-color-8; } } + + &.marked-course { + border-color: var(--black); + background-color: var(--white); + + .fc-time { + border-bottom: 1px solid var(--black); + } + } + + &.marked-course, + &.hidden-course { + .fc-title > img { + filter: #{"invert()"}; + } + } } } diff --git a/resources/assets/stylesheets/scss/my_courses.scss b/resources/assets/stylesheets/scss/my_courses.scss index 62c3be813679fcf9c09d2036db86ee47bc3a3a9e..d47fb58de7b6435f4c8229eadd6325f4328ed4b7 100644 --- a/resources/assets/stylesheets/scss/my_courses.scss +++ b/resources/assets/stylesheets/scss/my_courses.scss @@ -9,7 +9,16 @@ background: var(--white); } -form.default .mycourses-group-selector { +form.default table.mycourses-group-selector { + table-layout: fixed; + width: unset; + + td { + width: 2em; + } +} + +form.default td.mycourses-group-selector { position: relative; background-clip: padding-box; diff --git a/templates/start/schedule_widget.php b/templates/start/schedule_widget.php new file mode 100644 index 0000000000000000000000000000000000000000..23602dbb62490dc895f4651d4220c631f8af4c01 --- /dev/null +++ b/templates/start/schedule_widget.php @@ -0,0 +1 @@ +<?= $fullcalendar ?> diff --git a/tests/jsonapi/ScheduleEntriesShowTest.php b/tests/jsonapi/ScheduleEntriesShowTest.php index 99dc8950a9e6b92947b6eb6ba71c585cf55e0047..64dcbc85214689856b6bba9738ef5aeaa71de367 100644 --- a/tests/jsonapi/ScheduleEntriesShowTest.php +++ b/tests/jsonapi/ScheduleEntriesShowTest.php @@ -24,17 +24,14 @@ class ScheduleEntriesShowTest extends \Codeception\Test\Unit { $credentials = $this->tester->getCredentialsForTestAutor(); - \CalendarScheduleModel::storeEntry( - [ - 'start' => 9, - 'end' => 10, - 'day' => 1, - 'title' => 'test title', - 'content' => 'test content', - 'user_id' => $credentials['id'], - 'color' => DEFAULT_COLOR_NEW - ] + $stmt = DBManager::get()->prepare( + "INSERT INTO schedule_entries + (start_time, end_time, dow, label, content, user_id, mkdate, chdate) + VALUES + (9, 10, 1, 'test title', 'test content', :user_id, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())" ); + $stmt->execute(['user_id' => $credentials['id']]); + $scheduleEntryId = \DBManager::get()->lastInsertId(); $app = $this->tester->createApp($credentials, 'get', '/schedule-entries/{id}', ScheduleEntriesShow::class); diff --git a/tests/jsonapi/UserScheduleShowTest.php b/tests/jsonapi/UserScheduleShowTest.php index 9f5abd222337440fbd173db076d45a7a332aaa17..ab002ae4f26c2b611ad8ab74efd97ba03b645329 100644 --- a/tests/jsonapi/UserScheduleShowTest.php +++ b/tests/jsonapi/UserScheduleShowTest.php @@ -25,8 +25,8 @@ class UserScheduleShowTest extends \Codeception\Test\Unit $credentials = $this->tester->getCredentialsForTestAutor(); $stmt = \DBManager::get()->prepare( - "INSERT INTO schedule (start, end, day, title, content, color, user_id) - VALUES (?, ?, ?, ?, ?, ?, ?)" + "INSERT INTO schedule_entries (start_time, end_time, dow, label, content, user_id, mkdate, chdate) + VALUES (?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())" ); $stmt->execute([ 1000, @@ -34,8 +34,7 @@ class UserScheduleShowTest extends \Codeception\Test\Unit 1, 'a title', 'some content', - 1, - $credentials['id'], + $credentials['id'] ]); $scheduleId = \DBManager::get()->lastInsertId(); diff --git a/tests/unit/lib/CalendarcolumnClassTest.php b/tests/unit/lib/CalendarcolumnClassTest.php deleted file mode 100644 index d2ac9e0f97e7c7cffeaa6db80acb771a6a73e26b..0000000000000000000000000000000000000000 --- a/tests/unit/lib/CalendarcolumnClassTest.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -/* - * Copyright (C) 2011 - Rasmus Fuhse <fuhse@data-quest.de> - * - * 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. - */ - -require_once 'lib/calendar/CalendarColumn.php'; - -class CalendarColumnCase extends \Codeception\Test\Unit { - - - function setUp(): void { - } - - - function tearDown(): void { - } - - - function test_class_should_exist() { - $this->assertTrue(class_exists('CalendarColumn')); - } - - function test_create() { - $this->assertInstanceOf("CalendarColumn", CalendarColumn::create()); - } - - function test_get_id() { - $id = "test_id"; - $column = new CalendarColumn($id); - $this->assertEquals($id, $column->getId()); - } - - function test_set_id() { - $id = "test_id"; - $column = new CalendarColumn("falsche id"); - $column->setId($id); - $this->assertEquals($id, $column->getId()); - } - - function test_set_title() { - $title = "test_title"; - $column = new CalendarColumn(); - $column->setTitle($title); - $this->assertEquals($title, $column->getTitle()); - } - - function test_set_url() { - $url = URLHelper::getURL("dispatch.php/profile", ["username" => get_username()]); - $column = CalendarColumn::create()->setURL($url); - $this->assertEquals($url, $column->getURL()); - } - - function test_add_entry() { - $entry = ['start' => "0800", 'end' => "1000", 'title' => "test_title"]; - $column = CalendarColumn::create()->addEntry($entry); - $entry = ['start' => "1200", 'end' => "1230", 'title' => "test_title_number_2"]; - $column->addEntry($entry); - $entries = $column->getEntries(); - $this->assertIsArray($entries); - $this->assertEquals(2, count($entries)); - $this->assertNotEquals($entries[0], $entry); - $this->assertEquals($entry, $entries[1]); - $this->assertIsArray($entries[1]); - } - - function test_wrong_entry() { - $this->expectException(InvalidArgumentException::class); - $entry1 = ['start' => "0800", 'end' => "1000"]; - $entry2 = ['start' => "1000", 'title' => "test_title"]; - $entry3 = ['end' => "1500", 'title' => "test_title"]; - $column = CalendarColumn::create()->addEntry($entry1); - $column = CalendarColumn::create()->addEntry($entry2); - $column = CalendarColumn::create()->addEntry($entry3); - } - - function test_add_entries() { - $entries = [ - ['start' => "0800", 'end' => "1000", 'title' => "test_title"], - ['start' => "1200", 'end' => "1400", 'title' => "test_title"] - ]; - $column = CalendarColumn::create()->addEntries($entries); - $this->assertIsArray($column->getEntries()); - } - - function test_erase_entries() { - $entry = ['start' => "0800", 'end' => "1000", 'title' => "test_title"]; - $column = CalendarColumn::create()->addEntry($entry); - $column->eraseEntries(); - $entries = $column->getEntries(); - $this->assertIsArray($entries); - $this->assertEquals(0, count($entries)); - } - - - //Die anderen Methoden muss Till testen. - -} diff --git a/tests/unit/lib/CalendarviewClassTest.php b/tests/unit/lib/CalendarviewClassTest.php deleted file mode 100644 index d810575fefb9403fcdbc72ddbac2215b32b5d450..0000000000000000000000000000000000000000 --- a/tests/unit/lib/CalendarviewClassTest.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -/* - * Copyright (C) 2011 - Rasmus Fuhse <fuhse@data-quest.de> - * - * 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. - */ - -require_once 'lib/calendar/CalendarView.php'; - -class CalendarViewCase extends \Codeception\Test\Unit { - - - function setUp(): void { - } - - - function tearDown(): void { - } - - - function test_class_should_exist() { - $this->assertTrue(class_exists('CalendarView')); - } - - function test_constructor() { - $this->assertInstanceOf("CalendarView", new CalendarView()); - } - - function test_setHeight() { - $height = 75; - $cview = new CalendarView(); - $cview->setHeight($height); - $this->assertEquals($height, $cview->getHeight()); - } - - function test_setRange() { - $start_hour = 6; - $end_hour = 12; - $cview = new CalendarView(); - $cview->setRange($start_hour, $end_hour); - $result = $cview->getRange(); - $this->assertEquals($start_hour, $result[0]); - $this->assertEquals($end_hour, $result[1]); - } - - function test_addColumn() { - $view = new CalendarView(); - $title1 = "Mittwoch"; - $id1 = 3; - $view->addColumn($title1, "", $id1); - $title2 = "Donnerstag"; - $id2 = 4; - $view->addColumn($title2, "", $id2); - $columns = $view->getColumns(); - $this->assertIsArray($columns); - $this->assertInstanceOf("CalendarColumn", $columns[0]); - $this->assertEquals($title1, $columns[0]->getTitle()); - $this->assertEquals($id1, $columns[0]->getId()); - $this->assertInstanceOf("CalendarColumn", $columns[1]); - $this->assertEquals($title2, $columns[1]->getTitle()); - $this->assertEquals($id2, $columns[1]->getId()); - } - - public function test_negative_addEntry() { - $this->expectException(InvalidArgumentException::class); - $view = new CalendarView(); - $entry = [ - 'title' => "Test Eintrag", - 'start' => "0800", - 'end' => "0900" - ]; - $view->addEntry($entry); - } - - public function test_addEntry_getEntries() { - $view = new CalendarView(); - $id = 3; - $view->addColumn("Montag", "", $id); - $entry = [ - 'title' => "Test Eintrag", - 'start' => "0800", - 'end' => "0900" - ]; - $view->addEntry($entry); - $entries = $view->getEntries(); - $this->assertIsArray($entries); - $this->assertNotNull($entries['day_'.$id]); - } - - public function test_insertFunction() { - $view = new CalendarView(); - $js_function_object = 'function () { alert("Watch out, Gringo!"); }'; - $view->setInsertFunction($js_function_object); - $this->assertEquals($js_function_object, $view->getInsertFunction()); - } - - //Die anderen Methoden muss Till testen. - -}