From 061f8168db6de9c1dded98c176d756cbbf24cb57 Mon Sep 17 00:00:00 2001 From: Peter Thienel Date: Wed, 18 Dec 2024 11:13:59 +0000 Subject: =?UTF-8?q?Resolve=20"Stundenplanansicht=20f=C3=BCr=20=C3=9Cbersch?= =?UTF-8?q?neidungsfreiheitschecks"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #3323 Merge request studip/studip!2337 --- app/controllers/admin/overlapping.php | 462 +++++++++++++++------ app/controllers/search/studiengaenge.php | 4 +- app/views/admin/overlapping/admin_info.php | 1 + app/views/admin/overlapping/buttons.php | 46 ++ app/views/admin/overlapping/conflicts.php | 173 ++++---- app/views/admin/overlapping/course_conflict.php | 2 + app/views/admin/overlapping/courses.php | 31 +- app/views/admin/overlapping/index.php | 5 +- app/views/admin/overlapping/info_dialog.php | 7 + app/views/admin/overlapping/modul.php | 7 +- app/views/admin/overlapping/overlapping.php | 12 +- app/views/admin/overlapping/planer.php | 27 ++ app/views/admin/overlapping/selection.php | 56 +-- app/views/search/studiengaenge/verlauf.php | 9 +- lib/models/MvvOverlappingSelection.php | 141 +++---- lib/models/StgteilVersion.php | 2 +- resources/assets/javascripts/lib/overlapping.js | 26 +- resources/assets/stylesheets/scss/overlapping.scss | 64 +-- 18 files changed, 688 insertions(+), 387 deletions(-) create mode 100644 app/views/admin/overlapping/buttons.php create mode 100644 app/views/admin/overlapping/course_conflict.php create mode 100644 app/views/admin/overlapping/info_dialog.php create mode 100644 app/views/admin/overlapping/planer.php diff --git a/app/controllers/admin/overlapping.php b/app/controllers/admin/overlapping.php index 8072ff5..101292b 100644 --- a/app/controllers/admin/overlapping.php +++ b/app/controllers/admin/overlapping.php @@ -17,6 +17,8 @@ class Admin_OverlappingController extends AuthenticatedController { + private ?string $view = null; + /** * Common before filter for all actions. * @@ -25,10 +27,13 @@ class Admin_OverlappingController extends AuthenticatedController */ public function before_filter(&$action, &$args) { + if (!$GLOBALS['perm']->have_perm('admin')) { + throw new AccessDeniedException(); + } parent::before_filter($action, $args); Navigation::activateItem('/browse/my_courses/overlapping'); - + URLHelper::bindLinkParam('view', $this->view); if (Request::option('sem_select')) { $GLOBALS['user']->cfg->store('MY_COURSE_SELECTED_CYCLE', Request::option('sem_select')); } @@ -41,21 +46,26 @@ class Admin_OverlappingController extends AuthenticatedController /** * Main view: Shows selection form and result. + * + * @return void */ public function index_action() { + $this->view = 'index'; $this->setSidebar(); + $selection_id = Request::option('selection', $_SESSION['MVV_OVL_SELECTION_ID'] ?? null); $selections = SimpleORMapCollection::createFromArray( MvvOverlappingSelection::findBySQL('`selection_id` = ? AND `user_id` = ?', [ - Request::option('selection'), + $selection_id, $GLOBALS['user']->id ]) ); - $this->selection_id = null; + $_SESSION['MVV_OVL_SELECTION_ID'] = $selection_id; + $this->selection_id = ''; if (count($selections)) { $this->base_version = StgteilVersion::find($selections->first()->base_version_id); - $this->fachsems = (array) explode(',', $selections->first()->fachsems); - $this->semtypes = (array) explode(',', $selections->first()->semtypes); + $this->fachsems = explode(',', $selections->first()->fachsems); + $this->semtypes = explode(',', $selections->first()->semtypes); $this->comp_versions = StgteilVersion::findMany($selections->pluck('comp_version_id')); $this->selection_id = $selections->first()->selection_id; if (Request::int('show_hidden') !== null) { @@ -67,6 +77,9 @@ class Admin_OverlappingController extends AuthenticatedController $this->fachsems = Request::intArray('fachsems'); $this->semtypes = Request::intArray('semtypes'); } + $this->base_version_id = $this->base_version->id ?? ''; + $this->comp_versions_ids = SimpleCollection::createFromArray($this->comp_versions)->pluck('id'); + $this->stgteil_versions = $this->getStgteilVersions(); $this->conflicts = MvvOverlappingSelection::getConflictsBySelection( $this->selection_id, empty($_SESSION['MVV_OVL_HIDDEN']) @@ -75,21 +88,25 @@ class Admin_OverlappingController extends AuthenticatedController /** * Resets form and shows index view. + * + * @return void */ public function reset_action() { - $this->setSidebar(); + $this->setSidebar('index'); $_SESSION['MVV_OVL_HIDDEN'] = 0; + $_SESSION['MVV_OVL_SELECTION_ID'] = ''; $this->conflicts = []; - $this->render_action('index'); + $this->redirect('admin/overlapping/index'); } /** * Calculates the conflicts and redirects to index view. + * + * @return void */ public function check_action() { - $this->setSidebar(); $this->base_version = StgteilVersion::find(Request::option('base_version')); if ($this->base_version) { $this->comp_versions = []; @@ -108,7 +125,8 @@ class Admin_OverlappingController extends AuthenticatedController $this->base_version, $this->comp_versions, $this->fachsems, - $this->semtypes + $this->semtypes, + $this->selected_semester->id ); // refresh conflicts @@ -127,7 +145,7 @@ class Admin_OverlappingController extends AuthenticatedController $selection[$comp_version->id]->base_version_id = $this->base_version->id; $selection[$comp_version->id]->comp_version_id = $comp_version->id; $selection[$comp_version->id]->setFachsemester($this->fachsems); - $selection[$comp_version->id]->setCoursetypes($this->semtypes); + $selection[$comp_version->id]->setCourseTypes($this->semtypes); $selection[$comp_version->id]->user_id = $GLOBALS['user']->id; $selection[$comp_version->id]->store(); } @@ -168,204 +186,386 @@ class Admin_OverlappingController extends AuthenticatedController /** * Shows the responsible admin of the course. * - * @param type $course_id The id of the course. + * @param string $conflict_id The id of the conflict. + * @return void */ - public function admin_info_action($course_id) + public function admin_info_action(string $conflict_id) { - $this->course = Course::find($course_id); - if ($this->course) { + $this->conflict = MvvOverlappingConflict::find($conflict_id); + $this->version = $this->conflict->comp_abschnitt->version; + $this->course = $this->conflict->comp_course; + if ($this->course && $this->version) { $this->admins = InstituteMember::findByInstituteAndStatus($this->course->institut_id, 'admin'); } else { PageLayout::postMessage(MessageBox::error(_('Unbekannte Veranstaltung.'))); } + $this->selected_view = 'admin_info'; } /** * Shows the course details. * - * @param type $course_id The id of the course. + * @param string $conflict_id The id of the conflict. + * @return void */ - public function course_info_action($course_id) + public function course_info_action(string $conflict_id) { - $course = Course::find($course_id); - if ($course) { - Request::set('sem_id', $course->id); - $this->redirect('course/details' . '?sem_id=' . $course->id); + $this->conflict = MvvOverlappingConflict::find($conflict_id); + $this->course = $this->conflict->comp_course; + $this->version = $this->conflict->comp_abschnitt->version; + if ($this->course && $this->version) { + $response = $this->relay('course/details'); + $this->content = $response->body; } else { - PageLayout::postMessage(MessageBox::error(_('Unbekannte Veranstaltung.'))); + PageLayout::postMessage(MessageBox::error(_('Unbekannte Veranstaltung oder Version.'))); } + $this->selected_view = 'course_info'; + $this->render_template('admin/overlapping/info_dialog'); } /** * Sets a course as hidden. + * + * @param int $conflict_id The id of the conflict. + * @return void */ - public function set_exclude_action() + public function exclude_action(int $conflict_id) { - $selection = MvvOverlappingSelection::find(Request::int('selection_id')); - if ($selection->user_id == $GLOBALS['user']->id) { - $exclude = new MvvOverlappingExclude([$selection->selection_id, Request::option('course_id')]); - if (Request::int('excluded')) { - $success = $exclude->delete(); - } else { + $conflict = MvvOverlappingConflict::find($conflict_id); + if ($conflict->selection->user_id === $GLOBALS['user']->id) { + $exclude = new MvvOverlappingExclude( + [ + $conflict->selection->selection_id, + $conflict->comp_course_id + ] + ); + if ($exclude->isNew()) { $success = $exclude->store(); + } else { + $success = $exclude->delete(); } $this->set_status($success ? 204 : 400); } else { $this->set_status(403); } - $this->render_nothing(); + $this->relocate('admin/overlapping/' . $this->view); } /** - * Shows detailed information about the studiengangteil version. + * Shows information of the study course version. * - * @param type $version_id The id of the studiengangteil version. + * @param string $conflict_id The id of the conflict. + * @return void + * @throws \Flexi\TemplateNotFoundException + * @throws \Trails\Exceptions\DoubleRenderError */ - public function version_info_action($version_id) + public function version_info_action(string $conflict_id) { - $version = StgteilVersion::find($version_id); - if ($version) { - Request::set('version', $version->id); - $this->redirect($this->url_for('search/studiengaenge/verlauf/' . $version->stgteil_id, - ['semester' => $this->selected_semester, - 'version' => $version->id])); - return; + $this->conflict = MvvOverlappingConflict::find($conflict_id); + if (empty($this->conflict)) { + throw new InvalidArgumentException(); + } + $this->version = $this->conflict->comp_abschnitt->version; + $this->course = $this->conflict->comp_course; + if ($this->version && $this->course) { + $response = $this->relay('search/studiengaenge/verlauf/' . $this->version->stgteil_id + . "/?semester={$this->selected_semester->id}&version={$this->version->id}"); + $this->content = $response->body; } else { - PageLayout::postMessage(MessageBox::error(_('Unbekannte Studiengangteil-Version.'))); + PageLayout::postError(_('Unbekannte Studiengangteil-Version.')); } + $this->selected_view = 'info'; + $this->render_template('admin/overlapping/info_dialog'); } /** - * Init the sidebar. + * Shows the planer view of conflicts. + * + * @param string $selection_id The id of the selection. + * @return void */ - private function setSidebar() + public function planer_action(string $selection_id = '') { - $sidebar = Sidebar::Get(); + $this->view = 'planer'; + $this->setSidebar(); - $widget = new SelectWidget( - _('Semesterauswahl'), - $this->url_for('admin/overlapping/index'), - 'sem_select' + $selection_id = $selection_id ?: $_SESSION['MVV_OVL_SELECTION_ID'] ?? null; + + $this->fullcalendar = Studip\Fullcalendar::create( + _('Kalender'), + [ + 'editable' => false, + 'selectable' => false, + 'studip_urls' => '', + 'dialog_size' => 'auto', + 'minTime' => sprintf('%02u:00', 8), + 'maxTime' => sprintf('%02u:00', 21), + 'defaultDate' => date('Y-m-d', $this->selected_semester->vorles_beginn), + 'allDaySlot' => false, + 'allDayText' => '', + 'header' => [ + 'left' => false, + 'center' => $this->selected_semester->name, + 'right' => false, + ], + 'weekNumbers' => false, + 'views' => [ + 'timeGridWeek' => [ + 'columnHeaderFormat' => ['weekday' => 'short', 'omitCommas' => true], + 'weekends' => true, + 'slotDuration' => '00:30:00' + ], + ], + 'defaultView' => 'timeGridWeek', + 'timeGridEventMinHeight' => 20, + 'eventSources' => [ + [ + 'url' => $this->conflictsURL($selection_id), + 'method' => 'GET', + 'extraParams' => [] + ] + ], + 'nowIndicator' => false + ], + ['class' => 'resource-plan semester-plan'] ); - foreach (array_reverse(Semester::getAll()) as $semester) { - $widget->addElement(new SelectElement( - $semester->id, - $semester->name, - $semester->id === $this->selected_semester->id - ), 'sem_select-' . $semester->id - ); + + // get selected StgteilVersions colors + $this->selections = MvvOverlappingSelection::findBySQL( + '`selection_id` = ? ORDER BY `comp_version_id`', + [$selection_id] + ); + } + + /** + * Retrieves all conflicts for the given selection. + * + * @param $selection_id The id of the selection. + * @return void + */ + public function conflicts_action($selection_id) + { + $selections = MvvOverlappingSelection::findBySQL( + '`selection_id` = ? ORDER BY `comp_version_id`', + [$selection_id] + ); + $conflicting_metadates = []; + foreach ($selections as $selection) { + foreach ($selection->conflicts as $conflict) { + $event_data = $this->createEventFromConflict($conflict, true); + $base_index = $conflict->base_course->id . $event_data->begin->getTimestamp(); + $conflicting_metadates[$base_index] = $event_data->toFullcalendarEvent(); + $event_data = $this->createEventFromConflict($conflict); + $comp_index = $conflict->comp_course->id . $event_data->begin->getTimestamp(); + $conflicting_metadates[$comp_index] = $event_data->toFullcalendarEvent(); + } } - $sidebar->addWidget($widget); + $this->render_json(array_values($conflicting_metadates)); } /** - * Search for base version by given search term. + * Shows a serialized view of the conflict. + * + * @param string $conflict_id The id of the conflict. + * @return void */ - public function base_version_action() + public function course_conflict_action(string $conflict_id) { - $sword = Request::get('term'); - $this->render_text(json_encode($this->getResult($sword))); + $this->conflict = MvvOverlappingConflict::find($conflict_id); + if (empty($this->conflict)) { + throw new InvalidArgumentException(); + } + $this->conflicts = SimpleORMapCollection::createFromArray([$this->conflict]); + $this->base_version = $this->conflict->base_abschnitt->version; + $this->version = $this->conflict->comp_abschnitt->version; + $this->course = $this->conflict->comp_course; + $this->selected_view = 'conflict'; } /** - * Search für comparison version by given search term. + * Shows the conflict in a dialog. + * + * @param string $conflict_id The id of the conflict. + * @return void + * @throws \Flexi\TemplateNotFoundException + * @throws \Trails\Exceptions\DoubleRenderError */ - public function comp_versions_action() + public function conflict_action(string $conflict_id) { - $sword = Request::get('term'); - $version_id = Config::get()->MVV_OVERLAPPING_SHOW_VERSIONS_INSIDE_MULTIPLE_STUDY_COURSES - ? Request::option('version_id') - : null; - $version_ids = $this->getRelatedVersions($version_id); - $this->render_text(json_encode($this->getResult($sword, $version_ids))); + $this->conflict = MvvOverlappingConflict::find($conflict_id); + if (empty($this->conflict)) { + throw new InvalidArgumentException(); + } + $this->version = $this->conflict->comp_abschnitt->version; + $this->course = $this->conflict->comp_course; + PageLayout::setTitle($this->course->getFullName( + Config::get()->IMPORTANT_SEMNUMBER + ? 'number-type-name' + : 'type-name' + ) + ); + $this->content = ''; + if (empty($this->conflict->comp_course)) { + PageLayout::postError(_('Unbekannte Veranstaltung.')); + } else { + Request::set('sem_id', $this->conflict->comp_course_id); + $this->course = $this->conflict->comp_course; + $this->version = $this->conflict->comp_abschnitt->version; + $response = $this->relayWithRedirect('course/details/index'); + $this->content = $response->body; + } + $this->selected_view = 'conflict'; + $this->render_template('admin/overlapping/info_dialog'); } /** - * Returns versions related to the base version. + * Creates EventData from conflicts. * - * @param type $version_id - * @return type + * @param MvvOverlappingConflict $conflict The conflict object. + * @return \Studip\Calendar\EventData The event data. */ - private function getRelatedVersions($version_id) + private function createEventFromConflict(MvvOverlappingConflict $conflict, $base = false): \Studip\Calendar\EventData { - $version_ids = []; - $version = StgteilVersion::find($version_id); - if ($version) { - $studiengaenge = Studiengang::findByStgTeil($version->stgteil_id); + static $color_mapping = []; + + $weekday_mapping =[ + 1 => 'mon', + 2 => 'tue', + 3 => 'wed', + 4 => 'thu', + 5 => 'fri', + 6 => 'sat', + 7 => 'sun', + ]; + if ($base) { + $version = $conflict->selection->comp_version; + $col_version_id = $conflict->selection->base_version->id; + $cycle = $conflict->base_cycle; + $course = $conflict->base_course; } else { - return null; + $version = $conflict->selection->base_version; + $col_version_id = $conflict->selection->comp_version->id; + $cycle = $conflict->comp_cycle; + $course = $conflict->comp_course; } - foreach ($studiengaenge as $studiengang) { - if ($studiengang->typ == 'mehrfach') { - foreach ($studiengang->studiengangteile as $studiengangteil) { - $version_ids = array_merge( - $version_ids, - $studiengangteil->versionen->pluck('version_id') - ); - } - } + if (empty($color_mapping[$col_version_id])) { + $color_mapping[$col_version_id] = count($color_mapping) + 1; } - return count($version_ids) ? array_diff($version_ids, [$version_id]) : null; + $color_pos = $color_mapping[$col_version_id]; + $text_color = Config::get()->PERS_TERMIN_KAT[$color_pos]['fgcolor']; + $background_color = Config::get()->PERS_TERMIN_KAT[$color_pos]['bgcolor']; + $border_color = Config::get()->PERS_TERMIN_KAT[$color_pos]['border_color']; + $begin = new DateTime(); + $begin->setTimestamp($this->selected_semester->vorles_beginn); + $begin->modify( + $weekday_mapping[$cycle->weekday] + . ' this week ' + . $cycle->start_time + ); + $end = clone $begin; + $end->modify('today ' . $cycle->end_time); + return new \Studip\Calendar\EventData( + $begin, + $end, + Config::get()->IMPORTANT_SEMNUMBER + ? $course->getFullName('number-type-name') + : $course->getFullName('type-name'), + ['user-date', 'user-date-category1'], + $text_color ?? '#ffffff', + $background_color ?? '#000000', + false, + 'MvvOverlappingConflict', + $conflict->id, + 'MvvOverlappingSelection', + $conflict->selection->id, + 'user', + $conflict->selection->user_id, + [ + 'show' => $this->course_conflictURL($conflict->id) + ], + [], + '', + $border_color ?? '#ffffff' + ); } /** - * Search for studiengangteil versionen by given keyword. The result can be - * filtered by version ids. + * Init the sidebar content. * - * @param string $keyword The keyword to search for. - * @param array $version_ids An array of version ids. - * @return array An array of studiengangteil versionen. + * @return void */ - private function getResult($keyword, $version_ids = null) { - $version_query = ''; + private function setSidebar() + { + $sidebar = Sidebar::Get(); - if (!is_null($version_ids)) { - $version_query = ' AND `mvv_stgteilversion`.`version_id` IN (:version_ids) '; - } + $views = new ViewsWidget(); + $views->addLink( + _('Listenansicht'), + $this->indexURL() + )->setActive($this->view === 'index'); + $views->addLink( + _('Planeransicht'), + $this->planerURL() + )->setActive($this->view === 'planer'); + $sidebar->addWidget($views); - $query = "SELECT `version_id`, `fach`.`name`, `mvv_stgteil`.`kp` - FROM `fach` - INNER JOIN `mvv_stgteil` USING(`fach_id`) - INNER JOIN `mvv_stgteilversion` USING(`stgteil_id`) - INNER JOIN `semester_data` AS `start_sem` - ON (`mvv_stgteilversion`.`start_sem` = `start_sem`.`semester_id`) - LEFT JOIN `semester_data` AS `end_sem` - ON (`mvv_stgteilversion`.`end_sem` = `end_sem`.`semester_id`) - WHERE (`fach`.`name` LIKE :keyword - OR `mvv_stgteil`.`zusatz` LIKE :keyword - OR `mvv_stgteilversion`.`code` LIKE :keyword) + $semester_selector = new SelectWidget( + _('Semesterauswahl'), + $this->url_for('admin/overlapping/reset'), + 'sem_select' + ); + foreach (array_reverse(Semester::getAll()) as $semester) { + $semester_selector->addElement(new SelectElement( + $semester->id, + $semester->name, + $semester->id === $this->selected_semester->id + ), 'sem_select-' . $semester->id + ); + } + $sidebar->addWidget($semester_selector); + } - AND (`start_sem`.`beginn` <= :sem_end - OR ISNULL(`start_sem`.`beginn`)) - AND (`end_sem`.`ende` >= :sem_start - OR ISNULL(`end_sem`.`ende`)) - " . $version_query . " - ORDER BY `name` ASC, `kp` ASC"; + /** + * Search for base version by given search term. + */ + public function base_version_action() + { + $sword = Request::get('term'); + $this->render_text(json_encode($this->getResult($sword))); + } - $stat = array_keys(array_filter( + /** + * Get Studiengangteilversionen for selection, filtered by start and end semester and status (only public). + * + * @return StgteilVersion[] + */ + private function getStgteilVersions(): array + { + // get public status from config + $public_status = array_keys(array_filter( $GLOBALS['MVV_STGTEILVERSION']['STATUS']['values'], function ($v) { return $v['public']; } )); - $stmt = DBManager::get()->prepare($query); - $stmt->execute([ - ':keyword' => '%' . $keyword . '%', - ':stat' => $stat, - ':sem_start' => $this->selected_semester->beginn, - ':sem_end' => $this->selected_semester->ende, - ':version_ids' => $version_ids - ]); - $res = ['results' => []]; - foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $version_id) { - $version = StgteilVersion::find($version_id); - $res['results'][] = [ - 'id' => $version->id, - 'text' => $version->getDisplayName() - ]; - } - return $res; + return StgteilVersion::findBySQL( + "JOIN `mvv_stgteil` USING(`stgteil_id`) + JOIN `fach` USING(`fach_id`) + JOIN `semester_data` AS `start_sem` + ON `mvv_stgteilversion`.`start_sem` = `start_sem`.`semester_id` + LEFT JOIN `semester_data` AS `end_sem` + ON `mvv_stgteilversion`.`end_sem` = `end_sem`.`semester_id` + WHERE (`start_sem`.`beginn` <= :sem_end) + AND (`end_sem`.`ende` >= :sem_start OR ISNULL(`end_sem`.`ende`)) + AND `mvv_stgteilversion`.`stat` IN (:status) + ORDER BY `fach`.`name`, `mvv_stgteil`.`kp`", + [ + ':sem_start' => $this->selected_semester->beginn, + ':sem_end' => $this->selected_semester->ende, + ':status' => $public_status + ] + ); } } diff --git a/app/controllers/search/studiengaenge.php b/app/controllers/search/studiengaenge.php index e17eb2a..0dd5c7d 100644 --- a/app/controllers/search/studiengaenge.php +++ b/app/controllers/search/studiengaenge.php @@ -364,7 +364,9 @@ class Search_StudiengaengeController extends MVVController Sidebar::get()->addWidget($widget, 'mhb_export'); } - $this->breadcrumb->append($this->studiengang, 'studiengang'); + if ($this->breadcrumb) { + $this->breadcrumb->append($this->studiengang, 'studiengang'); + } $this->render_template('search/studiengaenge/verlauf', $this->layout); } diff --git a/app/views/admin/overlapping/admin_info.php b/app/views/admin/overlapping/admin_info.php index 1eeac77..7dbd179 100644 --- a/app/views/admin/overlapping/admin_info.php +++ b/app/views/admin/overlapping/admin_info.php @@ -32,3 +32,4 @@ +render_partial('admin/overlapping/buttons') ?> diff --git a/app/views/admin/overlapping/buttons.php b/app/views/admin/overlapping/buttons.php new file mode 100644 index 0000000..64c3082 --- /dev/null +++ b/app/views/admin/overlapping/buttons.php @@ -0,0 +1,46 @@ + +
+ + version_infoURL($conflict->id), + ['data-dialog' => 'size=auto;reload-on-close'] + ) ?> + + + excludeURL($conflict->id), + ['data-dialog' => 'size=auto;reload-on-close'] + ) ?> + + course_infoURL($conflict->id), + ['data-dialog' => 'size=auto;reload-on-close'] + ) ?> + + + admin_infoURL($conflict->id), + ['data-dialog' => 'size=auto;reload-on-close'] + ) ?> + + + course_conflictURL($conflict->id), + ['data-dialog' => 'size=auto;reload-on-close'] + ) ?> + + +
diff --git a/app/views/admin/overlapping/conflicts.php b/app/views/admin/overlapping/conflicts.php index 6d22c1d..f1cd914 100644 --- a/app/views/admin/overlapping/conflicts.php +++ b/app/views/admin/overlapping/conflicts.php @@ -12,95 +12,102 @@
  • - - - getDisplayName()); ?> - + abschnitte->findBy('abschnitt_id', $conflicts->findBy('base_metadate_id', $cycle->id)->pluck('comp_abschnitt_id'))->orderBy('position', SORT_NUMERIC) ?> +
  • + + + - + diff --git a/app/views/admin/overlapping/course_conflict.php b/app/views/admin/overlapping/course_conflict.php new file mode 100644 index 0000000..345d4ba --- /dev/null +++ b/app/views/admin/overlapping/course_conflict.php @@ -0,0 +1,2 @@ +render_partial('admin/overlapping/overlapping') ?> +render_partial('admin/overlapping/buttons') ?> diff --git a/app/views/admin/overlapping/courses.php b/app/views/admin/overlapping/courses.php index aa2e83f..5cb0a7d 100644 --- a/app/views/admin/overlapping/courses.php +++ b/app/views/admin/overlapping/courses.php @@ -15,23 +15,24 @@ return ($selected_semester->beginn <= $c->date && $selected_semester->ende >= $c->date); }); ?>
  • -
    !
    abschnitt_id . $modulteil->id . $course['seminar_id']) ?> - - VeranstaltungsNummer) ?> - - 'text-bottom', - 'title' => _('Veranstaltungsdetails') - ]) ?> - - getFullName('type-name')) ?> - admission_turnout) : ?> - admission_turnout)) ?> - - 'text-bottom']) ?> - toString('short'), count($dates)); ?> +
    + + VeranstaltungsNummer) ?> + + 'text-bottom', + 'title' => _('Veranstaltungsdetails') + ]) ?> + + getFullName('type-name')) ?> + admission_turnout) : ?> + admission_turnout)) ?> + + asImg(['class' => 'text-bottom']) ?> + toString('short'), count($dates)); ?> +
    diff --git a/app/views/admin/overlapping/index.php b/app/views/admin/overlapping/index.php index 892a198..7736186 100644 --- a/app/views/admin/overlapping/index.php +++ b/app/views/admin/overlapping/index.php @@ -3,12 +3,11 @@ * @var SimpleORMapCollection $conflicts * @var array $semtypes * @var array $fachsems + * @var array $stgteil_versions + * @var string $fullcalendar */ ?> render_partial('admin/overlapping/selection', ['fachsems' => $fachsems, 'semtypes' => $semtypes]) ?> render_partial('admin/overlapping/overlapping') ?> - diff --git a/app/views/admin/overlapping/info_dialog.php b/app/views/admin/overlapping/info_dialog.php new file mode 100644 index 0000000..63f6c25 --- /dev/null +++ b/app/views/admin/overlapping/info_dialog.php @@ -0,0 +1,7 @@ + + +render_partial('admin/overlapping/buttons') ?> diff --git a/app/views/admin/overlapping/modul.php b/app/views/admin/overlapping/modul.php index fe9d483..aaf31d2 100644 --- a/app/views/admin/overlapping/modul.php +++ b/app/views/admin/overlapping/modul.php @@ -5,10 +5,13 @@ * @var StgteilAbschnitt $abschnitt */ ?> - getDisplayName()); ?> +
    + + getDisplayName()); ?> +