aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+studip@gmail.com>2025-02-27 15:43:55 +0000
committerDavid Siegfried <david.siegfried@uni-vechta.de>2025-02-27 15:43:55 +0000
commit8ba78ce50c8cf61ad2df91ffaa19952bb5f4fff9 (patch)
tree6bc5d5674cc9905a9e190fab21b7270b564d80c0 /lib
parent257f38c62b0847f76fc477c6e014c5d2a0a5bf54 (diff)
replace old colour group selector and notifications with vue app that uses the same underlying data as my courses, relocate my courses vue components and remove now obsolete functions from meine_seminare_func, fixes #5165, fixes #5201
Closes #5165 and #5201 Merge request studip/studip!3892
Diffstat (limited to 'lib')
-rw-r--r--lib/classes/ModulesNotification.php4
-rw-r--r--lib/classes/MyCoursesHelper.php266
-rw-r--r--lib/classes/MyRealmModel.php1
-rw-r--r--lib/meine_seminare_func.inc.php273
4 files changed, 267 insertions, 277 deletions
diff --git a/lib/classes/ModulesNotification.php b/lib/classes/ModulesNotification.php
index 96aa660..22586fb 100644
--- a/lib/classes/ModulesNotification.php
+++ b/lib/classes/ModulesNotification.php
@@ -77,9 +77,7 @@ class ModulesNotification
$this->subject = _("Stud.IP Benachrichtigung");
}
-
-
- public function getAllNotifications ($user_id = null)
+ public function getAllNotifications($user_id = null)
{
if ($user_id === null) {
$user_id = $GLOBALS['user']->id;
diff --git a/lib/classes/MyCoursesHelper.php b/lib/classes/MyCoursesHelper.php
new file mode 100644
index 0000000..c4ef51b
--- /dev/null
+++ b/lib/classes/MyCoursesHelper.php
@@ -0,0 +1,266 @@
+<?php
+final class MyCoursesHelper
+{
+ public function createVueAppData(string $sem_key, string $group_field = 'sem_number'): array
+ {
+ return $this->getVueAppData(
+ $this->getCourses($sem_key, $group_field),
+ $group_field
+ );
+ }
+
+ public function getCourses(string $sem_key, string $group_field = 'sem_number'): array
+ {
+ return MyRealmModel::getPreparedCourses($sem_key, [
+ 'group_field' => $group_field,
+ 'order_by' => null,
+ 'order' => 'asc',
+ 'studygroups_enabled' => Config::get()->MY_COURSES_ENABLE_STUDYGROUPS,
+ 'deputies_enabled' => Config::get()->DEPUTIES_ENABLE,
+ ]);
+ }
+
+ /**
+ * Get the data array for presenting the course list in Vue.
+ *
+ * @param array|null $sem_courses
+ * @param string $group_field
+ * @return array{
+ * courses: array,
+ * groups: array,
+ * user_id: string,
+ * config: array{
+ * allow_dozent_visibility: bool,
+ * open_groups: array,
+ * sem_number: bool,
+ * display_type: string,
+ * responsive_type: string,
+ * navigation_show_only_new: bool,
+ * group_by: string
+ * }
+ * }
+ */
+ public function getVueAppData(?array $sem_courses, string $group_field = 'sem_number'): array
+ {
+ $sem_data = Semester::getAllAsArray();
+ $temp_courses = [];
+ $groups = [];
+
+ if (is_array($sem_courses)) {
+ foreach ($sem_courses as $_outer_index => $_outer) {
+ if ($group_field === 'sem_number') {
+ $_courses = [];
+
+ foreach ($_outer as $course) {
+ $_courses[$course['seminar_id']] = $course;
+ if (!empty($course['children']) && is_array($course['children'])) {
+ foreach ($course['children'] as $child) {
+ $_courses[$child['seminar_id']] = $child;
+ }
+ }
+ }
+
+ if ($_outer_index) {
+ $groups[] = [
+ 'id' => $_outer_index,
+ 'name' => (string)$sem_data[$_outer_index]['name'],
+ 'data' => [
+ [
+ 'id' => md5($_outer_index),
+ 'label' => false,
+ 'ids' => array_keys($_courses),
+ ],
+ ],
+ ];
+ }
+ $temp_courses = array_merge($temp_courses, $_courses);
+ } else {
+ $count = 1;
+ $_groups = [];
+ foreach ($_outer as $_inner_index => $_inner) {
+ $_courses = [];
+
+ foreach ($_inner as $course) {
+ $_courses[$course['seminar_id']] = $course;
+ if (isset($course['children']) && is_array($course['children'])) {
+ foreach ($course['children'] as $child) {
+ $_courses[$child['seminar_id']] = $child;
+ }
+ }
+ }
+
+ $label = $_inner_index;
+ if ($group_field === 'sem_tree_id' && !$label) {
+ $label = _('keine Zuordnung');
+ } elseif ($group_field === 'gruppe') {
+ $label = _('Gruppe') . ' ' . $count++;
+ }
+
+ $_groups[] = [
+ 'id' => md5($_outer_index . $_inner_index),
+ 'label' => $label,
+ 'ids' => array_keys($_courses),
+ ];
+
+ $temp_courses = array_merge($temp_courses, $_courses);
+ }
+
+ if ($_outer_index) {
+ $groups[] = [
+ 'id' => $_outer_index,
+ 'name' => (string)$sem_data[$_outer_index]['name'],
+ 'data' => $_groups,
+ ];
+ }
+ }
+ }
+ }
+
+ return [
+ 'setCourses' => $this->sanitizeNavigations(array_map([$this, 'convertCourse'], $temp_courses)),
+ 'setGroups' => $groups,
+ 'setUserId' => $GLOBALS['user']->id,
+ 'setConfig' => [
+ 'allow_dozent_visibility' => Config::get()->ALLOW_DOZENT_VISIBILITY,
+ 'open_groups' => array_values($GLOBALS['user']->cfg->MY_COURSES_OPEN_GROUPS),
+ 'sem_number' => Config::get()->IMPORTANT_SEMNUMBER,
+ 'view_settings' => $GLOBALS['user']->cfg->MY_COURSES_VIEW_SETTINGS,
+ 'group_by' => $group_field,
+ ],
+ ];
+ }
+
+ private function sanitizeNavigations(array $courses): array
+ {
+ // Count occurences of slots
+ $counters = [];
+ foreach ($courses as $course) {
+ foreach ($course['navigation'] as $key => $value) {
+ if (!isset($counters[$key])) {
+ $counters[$key] = 0;
+ }
+ if ($value) {
+ $counters[$key] += 1;
+ }
+ }
+ }
+
+ // Detect which slots are not set at all
+ $remove = array_keys(array_filter($counters, function ($counter) {
+ return !$counter;
+ }));
+
+ // Set positions by predefined positions without the always empty slots
+ $positions = array_diff(array_keys(MyRealmModel::getDefaultModules()), $remove);
+
+ // Get other positions based on count
+ arsort($counters);
+ foreach ($counters as $key => $count) {
+ if ($count && !in_array($key, $positions)) {
+ $positions[] = $key;
+ }
+ }
+
+ // Sort and filter course navigations
+ return array_map(
+ function ($course) use ($positions) {
+ $course['navigation'] = array_filter($course['navigation'], function ($key) use ($positions) {
+ return in_array($key, $positions);
+ }, ARRAY_FILTER_USE_KEY);
+ uksort($course['navigation'], function ($a, $b) use ($positions) {
+ return array_search($a, $positions) - array_search($b, $positions);
+ });
+ $course['navigation'] = array_values($course['navigation']);
+ return $course;
+ },
+ $courses
+ );
+ }
+
+ private function convertCourse($course)
+ {
+ $is_teacher = in_array($course['user_status'], ['tutor', 'dozent']);
+
+ $avatar = $course['sem_class']['studygroup_mode']
+ ? StudygroupAvatar::getAvatar($course['seminar_id'])
+ : CourseAvatar::getAvatar($course['seminar_id']);
+
+ $extra_navigation = false;
+ if ($is_teacher) {
+ $adminmodule = $course['sem_class']->getAdminModuleObject();
+ if ($adminmodule) {
+ $adminnavigation = $adminmodule->getIconNavigation($course['seminar_id'], 0, $GLOBALS['user']->id);
+ $extra_navigation = [
+ 'url' => URLHelper::getURL($adminnavigation->getURL(), ['cid' => $course['seminar_id']]),
+ 'icon' => $adminnavigation->getImage()->getShape(),
+ 'label' => $adminnavigation->getLinkAttributes()['title'] ?? _('Verwaltung'),
+ ];
+ }
+ }
+
+ return [
+ 'id' => (string) $course['seminar_id'],
+ 'name' => (string) $course['name'],
+ 'number' => (string) $course['veranstaltungsnummer'],
+ 'group' => (int) $course['gruppe'],
+ 'admission_binding' => (bool) $course['admission_binding'],
+ 'children' => array_column($course['children'] ?? [], 'seminar_id'),
+ 'parent' => $course['parent_course'] ?? null,
+
+ 'is_teacher' => in_array($course['user_status'], ['tutor', 'dozent']),
+ 'is_studygroup' => (bool) $course['sem_class']['studygroup_mode'],
+ 'is_hidden' => !$course['visible'],
+ 'is_deputy' => (bool) $course['is_deputy'],
+ 'is_group' => (bool) $course['is_group'],
+
+ 'avatar' => $avatar->getURL(Avatar::MEDIUM),
+
+ 'navigation' => $this->reduceNavigation($course['navigation']),
+ 'extra_navigation' => $extra_navigation,
+ ];
+ }
+
+ private function reduceNavigation($nav): array
+ {
+ if (!$nav) {
+ return [];
+ }
+
+ $result = [];
+ foreach (MyRealmModel::array_rtrim($nav) as $key => $n) {
+ if (!$n || !$n->isVisible(true)) {
+ $item = false;
+ } else {
+ $attr = $n->getLinkAttributes();
+ if (empty($attr['title']) && $n->getImage()) {
+ $attr['title'] = (string) ($n->getImage()->getAttributes()['title'] ?? '');
+ }
+ if (empty($attr['title'])) {
+ $attr['title'] = (string) $n->getTitle();
+ }
+ $attr['title'] = (string) $attr['title'];
+
+ $item = [
+ 'url' => $n->getURL(),
+ 'icon' => $this->convertIcon($n->getImage()),
+ 'attr' => $attr,
+ 'important' => $n->getImage()->signalsAttention(),
+ ];
+ }
+ $result[$key] = $item;
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return array{role: string, shape: string}
+ */
+ private function convertIcon(Icon $icon): array
+ {
+ return [
+ 'role' => $icon->getRole(),
+ 'shape' => $icon->getShape(),
+ ];
+ }
+}
diff --git a/lib/classes/MyRealmModel.php b/lib/classes/MyRealmModel.php
index f3ab44c..dea3cb5 100644
--- a/lib/classes/MyRealmModel.php
+++ b/lib/classes/MyRealmModel.php
@@ -23,7 +23,6 @@
* @since 3.1
*/
-require_once 'lib/meine_seminare_func.inc.php';
require_once 'lib/object.inc.php';
class MyRealmModel
diff --git a/lib/meine_seminare_func.inc.php b/lib/meine_seminare_func.inc.php
deleted file mode 100644
index 5313f8d..0000000
--- a/lib/meine_seminare_func.inc.php
+++ /dev/null
@@ -1,273 +0,0 @@
-<?php
-# Lifter002: TODO
-# Lifter007: TODO
-# Lifter003: TODO
-# Lifter010: TODO
-
-/**
- *
- * @param string $group_field
- * @param array $groups
- */
-function get_group_names(string $group_field, array $groups): array
-{
- $mapper = function (): string {
- return 'unknown';
- };
- if ($group_field === 'sem_number') {
- $all_semester = Semester::findAllVisible();
- $mapper = function ($key) use ($all_semester): string {
- return (string) ($all_semester[$key]['name'] ?? _('unbekanntes Semester'));
- };
- } elseif ($group_field === 'sem_tree_id') {
- $mapper = function ($key): string {
- return StudipStudyArea::getNode($key)->getPath(' > ');
- };
- } elseif ($group_field === 'sem_status') {
- $mapper = function ($key): string {
- $sem_type = $GLOBALS['SEM_TYPE'][$key];
- return "{$sem_type['name']} ({$GLOBALS['SEM_CLASS'][$sem_type['class']]['name']})";
- };
- } elseif ($group_field === 'no_grouped') {
- $mapper = function (): string {
- return _('keine Gruppierung');
- };
- } elseif ($group_field === 'gruppe') {
- $groupcount = 0;
- $mapper = function () use (&$groupcount): string {
- $groupcount += 1;
- return _('Gruppe') . " {$groupcount}";
- };
- } elseif ($group_field === 'dozent_id') {
- $mapper = function ($key): string {
- return get_fullname($key, 'no_title_short');
- };
- } elseif ($group_field === 'mvv') {
- $mapper = function ($key): string {
- $module = Modul::find($key);
- return $module ? (string) $module->getDisplayName() : _('Keinem Modul zugeordnet');
- };
- }
-
- $result = [];
- foreach (array_keys($groups) as $key) {
- $result[$key] = $mapper($key);
- }
- return $result;
-}
-
-/**
- *
- * @param string $group_field
- * @param array $groups
- */
-function sort_groups($group_field, &$groups)
-{
- switch ($group_field) {
- case 'sem_number':
- krsort($groups, SORT_NUMERIC);
- break;
-
- case 'gruppe':
- ksort($groups, SORT_NUMERIC);
- break;
-
- case 'sem_tree_id':
- uksort($groups, function ($a, $b) {
- $a_obj = StudipStudyArea::getNode($a);
- $b_obj = StudipStudyArea::getNode($b);
- return strcmp($a_obj->name, $b_obj->name);
- });
- break;
-
- case 'sem_status':
- uksort($groups, function ($a, $b) {
- global $SEM_CLASS,$SEM_TYPE;
- return strnatcasecmp(
- $SEM_TYPE[$a]['name'] . ' (' . $SEM_CLASS[$SEM_TYPE[$a]['class']]['name'] . ')',
- $SEM_TYPE[$b]['name'] . ' (' . $SEM_CLASS[$SEM_TYPE[$b]['class']]['name'] . ')'
- );
- });
- break;
-
- case 'dozent_id':
- uksort($groups, function ($a,$b) {
- $replacements = ['ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue'];
- return strnatcasecmp(
- str_replace(array_keys($replacements), array_values($replacements), mb_strtolower(get_fullname($a, 'no_title_short'))),
- str_replace(array_keys($replacements), array_values($replacements), mb_strtolower(get_fullname($b, 'no_title_short')))
- );
- });
- break;
-
- case 'mvv':
- uksort($groups, function ($a, $b): int {
- $module_a = Modul::find($a);
- $module_b = Modul::find($b);
-
- if (!$module_a) {
- return 1;
- }
- if (!$module_b) {
- return -1;
- }
- return strnatcasecmp($module_a->getDisplayName(), $module_b->getDisplayName());
- });
- break;
- }
-
- foreach ($groups as $key => &$value) {
- usort($value, function ($a, $b) {
- if ($a['gruppe'] != $b['gruppe']) {
- return (int)($a['gruppe'] - $b['gruppe']);
- } else {
- if (Config::get()->IMPORTANT_SEMNUMBER) {
- return strnatcasecmp($a['sem_nr'] ?? '', $b['sem_nr'] ?? '');
- } else {
- return strnatcmp($a['name'], $b['name']);
- }
- }
- });
- }
- return true;
-}
-
-/**
- *
- * @param array $groups
- * @param array $my_obj
- */
-function correct_group_sem_number(&$groups, &$my_obj): bool
-{
- if (!is_array($groups) || !is_array($my_obj)) {
- return false;
- }
-
- $current_semester = Semester::findCurrent();
-
- $my_sem = array_filter(
- $my_obj,
- fn($values) => $values['obj_type'] === 'sem'
- );
-
- Course::findEachMany(
- function (Course $course) use (&$groups, &$my_obj, $current_semester) {
- if (count($course->semesters) === 1) {
- return;
- }
-
- $obj_data = $my_obj[$course->id];
-
- if (
- $course->isOpenEnded()
- && $course->start_semester->beginn < $current_semester->beginn
- ) {
- unset($groups[$obj_data['sem_number']][$course->id]);
-
- fill_groups($groups, Semester::getIndexById($current_semester->id), [
- 'seminar_id' => $course->id,
- 'name' => $obj_data['name'],
- 'gruppe' => $obj_data['gruppe'],
- ]);
-
- if (count($groups[$obj_data['sem_number']]) === 0) {
- unset($groups[$obj_data['sem_number']]);
- }
- } else {
- $to_sem = $obj_data['sem_number_end'];
- for ($i = $obj_data['sem_number']; $i <= $to_sem; ++$i){
- fill_groups($groups, $i, [
- 'seminar_id' => $course->id,
- 'name' => $obj_data['name'],
- 'gruppe' => $obj_data['gruppe']
- ]);
- }
- }
-
- if (User::findCurrent()->getConfiguration()->getValue('SHOWSEM_ENABLE')) {
- $my_obj[$course->id]['name'] .= ' (' . $course->getTextualSemester() . ')';
- }
- },
- array_keys($my_sem)
- );
-
- return true;
-}
-
-/**
- *
- * @param mixed $my_obj
- */
-function add_sem_name(&$my_obj): bool
-{
- if ($GLOBALS['user']->cfg->getValue('SHOWSEM_ENABLE')) {
- $sem_data = Semester::findAllVisible();
- if (is_array($my_obj)) {
- foreach ($my_obj as $seminar_id => $values){
- if ($values['obj_type'] == 'sem' && $values['sem_number'] != $values['sem_number_end']){
- $sem_name = " (" . $sem_data[$values['sem_number']]['name'] . " - ";
- $sem_name .= (($values['sem_number_end'] == -1) ? _("unbegrenzt") : $sem_data[$values['sem_number_end']]['name']) . ")";
- $my_obj[$seminar_id]['name'] .= $sem_name;
- } else {
- $my_obj[$seminar_id]['name'] .= " (" . $sem_data[$values['sem_number']]['name'] . ") ";
- }
- }
- }
- }
- return true;
-}
-
-/**
- *
- * @param array $groups
- * @param string|null $group_key
- * @param array $group_entry
- *
- * @return bool
- */
-function fill_groups(array &$groups, ?string $group_key, array $group_entry): bool
-{
- if (is_null($group_key)){
- $group_key = 'not_grouped';
- }
-
- if (!isset($groups[$group_key]) || !is_array($groups[$group_key])) {
- $groups[$group_key] = [];
- }
-
- $group_entry['name'] = str_replace(
- ['ä', 'ö', 'ü'],
- ['ae', 'oe', 'ue'],
- mb_strtolower($group_entry['name'])
- );
- if (!in_array($group_entry, $groups[$group_key])) {
- $groups[$group_key][$group_entry['seminar_id']] = $group_entry;
- return true;
- }
-
- return false;
-}
-
-/**
- * This function returns all valid fields that may be used for course
- * grouping in "My Courses".
- *
- * @return array All fields that may be specified for course grouping
- */
-function getValidGroupingFields(): array
-{
- $valid = [
- 'not_grouped',
- 'sem_number',
- 'sem_tree_id',
- 'sem_status',
- 'gruppe',
- 'dozent_id',
- ];
-
- if (LvgruppeSeminar::countBySql('1') > 0) {
- $valid[] = 'mvv';
- }
-
- return $valid;
-}