aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJan-Hendrik Willms <tleilax+studip@gmail.com>2025-08-07 10:13:09 +0200
committerJan-Hendrik Willms <tleilax+studip@gmail.com>2025-08-07 10:13:09 +0200
commitb1bc66f361a4dda92aba22fdd42843f619797a6c (patch)
treeecf6a08bddd1230edf7b49fc756073b4071ba169 /lib
parentca3ade956dd9c9041c07cef048136fa91f5faca2 (diff)
implement performance optimizations for my courses, fixes #4693
Closes #4693 Merge request studip/studip!3724
Diffstat (limited to 'lib')
-rw-r--r--lib/classes/ForumEntry.php0
-rw-r--r--lib/classes/ForumVisit.php0
-rw-r--r--lib/classes/MyRealmModel.php115
-rw-r--r--lib/models/Forum/Posting.php38
-rw-r--r--lib/modules/Blubber.php131
-rw-r--r--lib/modules/ConsultationModule.php15
-rw-r--r--lib/modules/CoreAdmin.php14
-rw-r--r--lib/modules/CoreCalendar.php21
-rw-r--r--lib/modules/CoreDocuments.php71
-rw-r--r--lib/modules/CoreForum.php55
-rw-r--r--lib/modules/CoreOverview.php60
-rw-r--r--lib/modules/CoreParticipants.php177
-rw-r--r--lib/modules/CorePersonal.php8
-rw-r--r--lib/modules/CoreSchedule.php95
-rw-r--r--lib/modules/CoreScm.php102
-rw-r--r--lib/modules/CoreStudygroupAdmin.php20
-rw-r--r--lib/modules/CoreStudygroupParticipants.php40
-rw-r--r--lib/modules/CoreWiki.php130
-rw-r--r--lib/modules/CoursewareModule.php78
-rw-r--r--lib/modules/FeedbackModule.php11
-rw-r--r--lib/modules/GradebookModule.php81
-rw-r--r--lib/modules/IconNavigationTrait.php19
-rw-r--r--lib/modules/IliasInterfaceModule.php116
-rw-r--r--lib/modules/StudipModuleExtended.php27
24 files changed, 851 insertions, 573 deletions
diff --git a/lib/classes/ForumEntry.php b/lib/classes/ForumEntry.php
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/classes/ForumEntry.php
diff --git a/lib/classes/ForumVisit.php b/lib/classes/ForumVisit.php
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lib/classes/ForumVisit.php
diff --git a/lib/classes/MyRealmModel.php b/lib/classes/MyRealmModel.php
index 283ff75..138451e 100644
--- a/lib/classes/MyRealmModel.php
+++ b/lib/classes/MyRealmModel.php
@@ -297,8 +297,6 @@ class MyRealmModel
$_course['sem_class'] = $course->getSemClass();
$_course['obj_type'] = 'sem';
- $visits = get_objects_visits([$course->id], 0, null, null, $course->tools->pluck('plugin_id'));
-
if ($group_field === 'sem_tree_id') {
$_course['sem_tree'] = $course->study_areas->toArray();
}
@@ -320,8 +318,6 @@ class MyRealmModel
});
}
- $_course['last_visitdate'] = $visits[$course->id][0]['last_visitdate'];
- $_course['visitdate'] = $visits[$course->id][0]['visitdate'];
$_course['user_status'] = $user_status;
$_course['gruppe'] = !$is_deputy ? $member_ships[$course->id]['gruppe'] ?? null : ($deputy ? $deputy->gruppe : null);
$_course['sem_number_end'] = $course->isOpenEnded() ? $max_sem_key : Semester::getIndexById($course->end_semester->id);
@@ -336,15 +332,8 @@ class MyRealmModel
}
$_course['parent_course'] = $course->parent_course ?? null;
$_course['is_group'] = $course->getSemClass()->isGroup();
- $_course['navigation'] = self::getAdditionalNavigations(
- $_course['seminar_id'],
- $_course,
- $_course['sem_class'],
- $GLOBALS['user']->id,
- $visits[$course->id]
- );
- // add the the course to the correct semester
+ // add the course to the correct semester
if (empty($_course['parent_course']) && !$course->isStudygroup()) {
if ($course->isOpenEnded()) {
@@ -380,6 +369,18 @@ class MyRealmModel
$sem_courses[$semester_assign[$parent]][$parent]['children'] = $kids;
}
+ $navs = self::getManyAdditionalNavigations(
+ $sem_courses,
+ User::findCurrent()->id
+ );
+ foreach ($sem_courses as &$courses) {
+ foreach ($courses as $c_id => &$course) {
+ if (!empty($navs[$c_id])) {
+ $course = array_merge($course, $navs[$c_id]);
+ }
+ }
+ }
+
if (!empty($params['main_navigation'])) {
return $sem_courses;
}
@@ -449,10 +450,6 @@ class MyRealmModel
continue;
}
- if (!Config::get()->VOTE_ENABLE && $plugin_id === 'vote') {
- continue;
- }
-
if ($plugin === 'vote') {
$navigation[$plugin_id] = self::checkVote($my_obj_values, $user_id, $object_id);
} else if ($tool = $my_obj_values['tools']->findOneBy('plugin_id', $plugin_id)) {
@@ -480,6 +477,88 @@ class MyRealmModel
return $navigation;
}
+ private static function getManyAdditionalNavigations(array $sem_courses, $user_id): array
+ {
+ // -- 0. Extract all courses: semester is irrelevant and flatten for children
+ $all_courses = [];
+ foreach ($sem_courses as $courses) {
+ foreach ($courses as $c_id => $course) {
+ $all_courses[$c_id] = $course;
+ if (!empty($course['children'])) {
+ foreach ($course['children'] as $child_course) {
+ $all_courses[$child_course['seminar_id']] = $child_course;
+ }
+ }
+ }
+ }
+
+ // -- 1. Calculate all the relevant courses for each StudipModule
+ $navigation = [];
+ $activated_tools = [];
+ $default_modules = self::getDefaultModules();
+ foreach ($all_courses as $course_id => $course) {
+ // add every default module with null, so there will be blank spaces in the nav
+ $navigation[$course_id] = array_fill_keys(
+ array_keys($default_modules),
+ null
+ );
+
+ foreach ($course['tools'] as $tool) {
+ $studip_module = $tool->getStudipModule();
+ if (
+ !$studip_module
+ || $studip_module instanceof CoreAdmin
+ || $studip_module instanceof CoreStudygroupAdmin
+ ) {
+ continue;
+ }
+ if (Seminar_Perm::get()->have_studip_perm($tool->getVisibilityPermission(), $course_id, $user_id)) {
+ $activated_tools[$tool['plugin_id']]['studip_module'] = $studip_module;
+ $activated_tools[$tool['plugin_id']]['courses'][$course_id] = $course_id;
+ }
+ }
+ }
+ // -- 2. Fetch the Navigation per StudipModule
+ $all_course_ids = array_keys($all_courses);
+ $visits = get_objects_visits($all_course_ids, 0, null, null, array_keys($activated_tools));
+ foreach ($activated_tools as $plugin_id => $plugin_data) {
+ $c_ids = $plugin_data['courses'];
+ if ($c_ids) {
+ if ($plugin_id === -1) {
+ foreach ($c_ids as $c_id) {
+ // TODO testing vote need to be done
+ $navigation[$c_id][$plugin_id] = self::checkVote($all_courses[$c_id], $user_id, $c_id);
+ }
+ } elseif ($plugin_data['studip_module'] instanceof StudipModuleExtended) {
+ $fetched_navs = $plugin_data['studip_module']->getManyIconNavigation($c_ids, $user_id);
+ foreach ($fetched_navs as $fetched_c_id => $fetched_nav) {
+ $navigation[$fetched_c_id][$plugin_id] = $fetched_nav;
+ }
+ } else {
+ foreach ($c_ids as $c_id) {
+ $navigation[$c_id][$plugin_id] = $plugin_data['studip_module']->getIconNavigation(
+ $c_id,
+ $visits[$c_id][$plugin_id]['visitdate'],
+ $user_id
+ );
+ }
+ }
+ }
+ }
+
+ // -- 3. Set each nav and visitdate by course
+ $result = [];
+ foreach ($navigation as $cid => $nav) {
+ $result[$cid] = [
+ 'navigation' => $nav,
+ 'visitdate' => $visits[$cid][0]['visitdate'] ?? null,
+ 'last_visitdate' => $visits[$cid][0]['last_visitdate'] ?? null,
+ ];
+ }
+ return $result;
+ }
+
+
/**
* This function reset all visits on every available modules
* @param $object
@@ -910,7 +989,9 @@ class MyRealmModel
}
$default_modules[$id] = $plugin;
}
- $default_modules[-1] = 'vote';
+ if (Config::get()->VOTE_ENABLE) {
+ $default_modules[-1] = 'vote';
+ }
return $default_modules;
}
}
diff --git a/lib/models/Forum/Posting.php b/lib/models/Forum/Posting.php
index b0ae0e8..f8fb579 100644
--- a/lib/models/Forum/Posting.php
+++ b/lib/models/Forum/Posting.php
@@ -73,32 +73,38 @@ class Posting extends SimpleORMap
return null;
}
- public static function getRecentPosts($range_id, int $last_visit = 0): array
+ public static function getRecentPosts(array|string $range_ids): array
{
- $query = [
+ $single = is_string($range_ids);
+ if ($single) {
+ $range_ids = [$range_ids];
+ }
+ $query =
"SELECT
+ forum_topics.range_id,
forum_discussions.*,
COUNT(DISTINCT forum_postings.posting_id) AS 'posts'
FROM forum_topics
JOIN forum_discussions USING(topic_id)
JOIN forum_postings USING(discussion_id)
- WHERE forum_topics.range_id = :range_id AND forum_postings.user_id != :user_id
- ",
- [
- 'range_id' => $range_id,
- 'user_id' => User::findCurrent()->user_id
- ]
+ LEFT JOIN forum_posting_reads AS fp_reads
+ ON fp_reads.discussion_id = forum_discussions.discussion_id
+ AND fp_reads.user_id = :user_id
+ WHERE forum_topics.range_id IN (:range_ids)
+ AND forum_postings.user_id != :user_id
+ AND forum_postings.mkdate > IFNULL(fp_reads.read_index, 0)
+ GROUP BY forum_topics.range_id, forum_discussions.discussion_id";
+ $params = [
+ ':range_ids' => $range_ids,
+ ':user_id' => User::findCurrent()->id,
];
- if ($last_visit) {
- $query[0] .= " AND forum_postings.mkdate > :last_visit";
- $query[1]["last_visit"] = $last_visit;
+ $res = \DBManager::get()->fetchAll($query, $params);
+ $by_course = [];
+ foreach ($res as $row) {
+ $by_course[$row['range_id']][] = $row;
}
-
- return \DBManager::get()->fetchAll(
- $query[0]." GROUP BY discussion_id ORDER BY forum_postings.mkdate DESC",
- $query[1]
- );
+ return $single ? (array_pop($by_course) ?? []) : $by_course;
}
public function getOpenGraphURLs(): array
diff --git a/lib/modules/Blubber.php b/lib/modules/Blubber.php
index c40c090..ecddda5 100644
--- a/lib/modules/Blubber.php
+++ b/lib/modules/Blubber.php
@@ -12,8 +12,10 @@
* Class Blubber - the Blubber-plugin
* This is only used to manage blubber within a course.
*/
-class Blubber extends CorePlugin implements StudipModule
+class Blubber extends CorePlugin implements StudipModuleExtended
{
+ use IconNavigationTrait;
+
/**
* Returns a navigation for the tab displayed in the course.
* @param string $course_id of the course
@@ -29,76 +31,93 @@ class Blubber extends CorePlugin implements StudipModule
return ['blubber' => $tab];
}
- /**
- * Returns a navigation-object with the grey/red icon for displaying in the
- * my_courses.php page.
- * @param string $course_id
- * @param int $last_visit
- * @param string|null $user_id
- * @return \Navigation
- */
- public function getIconNavigation($course_id, $last_visit, $user_id = null)
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
- $user_id || $user_id = $GLOBALS['user']->id;
- $icon = new Navigation(
- _('Blubber'),
- 'dispatch.php/course/messenger/course'
- );
- $icon->setImage(Icon::create('blubber'));
- $icon->setLinkAttributes(['title' => _('Blubber-Messenger')]);
+ $user_id = $user_id ?? User::findCurrent()->id;
+ $threshold = object_get_visit_threshold();
- $condition = "INNER JOIN blubber_threads USING (thread_id)
+ // check if there are comments newer than the last visit of blubber
+ $condition = "JOIN blubber_threads USING (thread_id)
+ LEFT JOIN object_user_visits AS ouv
+ ON ouv.object_id = blubber_threads.context_id
+ AND ouv.user_id = :me
+ AND ouv.plugin_id = :plugin_id
WHERE blubber_threads.context_type = 'course'
- AND blubber_threads.context_id = :course_id
- AND blubber_comments.mkdate >= :last_visit
+ AND blubber_threads.context_id IN (:course_ids)
+ AND blubber_comments.mkdate >= IF(ouv.visitdate > :threshold, ouv.visitdate, :threshold)
AND blubber_comments.user_id != :me
- AND blubber_threads.visible_in_stream = 1
- ";
- $comments = BlubberComment::findBySQL($condition, [
- 'course_id' => $course_id,
- 'last_visit' => $last_visit,
- 'me' => $user_id,
- ]);
- foreach ($comments as $comment) {
- if (
- $comment->thread->isVisibleInStream()
- && $comment->thread->isReadable()
- && $comment->thread->getLatestActivity() > $comment->thread->getLastVisit()
- ) {
- $icon->setImage(Icon::create('blubber', Icon::ROLE_NEW, ['title' => _('Es gibt neue Blubber')]));
- $icon->setTitle(_('Es gibt neue Blubber'));
- $icon->setBadgeNumber(count($comments));
- $icon->setURL('dispatch.php/course/messenger/course', ['thread' => 'new']);
- break;
+ AND blubber_threads.visible_in_stream = 1";
+ $params = [
+ ':course_ids' => $course_ids,
+ ':threshold' => $threshold,
+ ':me' => $user_id,
+ ':plugin_id' => 0, // module doesnt write directly into ouv
+ ];
+ $threads = [];
+ BlubberComment::findEachBySQL(
+ function ($comment) use (&$threads) {
+ $threads[$comment->thread_id][] = $comment;
+ },
+ $condition,
+ $params
+ );
+
+ $navs = [];
+ foreach ($threads as $comments) {
+ $thread = $comments[0]->thread;
+ if (isset($navs[$thread->context_id])) {
+ continue;
+ }
+ // check if there are comments that are newer thant the last visit of the blubber thread(!)
+ if ($thread->isReadable() && $thread->getLatestActivity() > $thread->getLastVisit()) {
+ $nav = new Navigation(_('Blubber'), 'dispatch.php/course/messenger/course', ['thread' => 'new']);
+ $nav->setImage(Icon::create('blubber', Icon::ROLE_ATTENTION));
+ $nav->setLinkAttributes(['title' => _('Es gibt neue Blubber')]);
+ $nav->setBadgeNumber(count($comments));
+ $navs[$thread->context_id] = $nav;
}
}
- $condition = "context_type = 'course'
- AND context_id = :course_id
- AND mkdate >= :last_visit
- AND user_id != :me
- AND visible_in_stream = 1
+ // Check for the remaining Courses, if new threads were created
+ $remaining_courses = array_diff($course_ids, array_keys($navs));
+ $condition = "LEFT JOIN object_user_visits AS ouv
+ ON ouv.object_id = blubber_threads.context_id
+ AND ouv.user_id = :me
+ AND ouv.plugin_id = :plugin_id
+ WHERE blubber_threads.context_type = 'course'
+ AND blubber_threads.context_id IN (:course_ids)
+ AND blubber_threads.mkdate >= IF(ouv.visitdate > :threshold, ouv.visitdate, :threshold)
+ AND blubber_threads.user_id != :me
+ AND blubber_threads.visible_in_stream = 1
AND (
blubber_threads.display_class IS NOT NULL
- OR blubber_threads.`content` IS NOT NULL
+ OR blubber_threads.content IS NOT NULL
)";
$threads = BlubberThread::findBySQL($condition, [
- 'course_id' => $course_id,
- 'last_visit' => $last_visit,
- 'me' => $GLOBALS['user']->id,
+ ':course_ids' => $remaining_courses,
+ ':threshold' => $threshold,
+ ':me' => $user_id,
+ ':plugin_id' => 0, // module doesnt write directly into ouv
]);
foreach ($threads as $thread) {
- if (
- $thread->isVisibleInStream()
- && $thread->isReadable()
- && $thread->mkdate > $thread->getLastVisit()
- ) {
- $icon->setImage(Icon::create('blubber', Icon::ROLE_ATTENTION, ['title' => _('Es gibt neue Blubber')]));
- $icon->setTitle(_('Es gibt neue Blubber'));
- break;
+ if ($thread->isReadable()) {
+ $nav = new Navigation(_('Blubber'), 'dispatch.php/course/messenger/course');
+ $nav->setImage(Icon::create('blubber', Icon::ROLE_ATTENTION));
+ $nav->setLinkAttributes(['title' => _('Es gibt neue Blubber')]);
+ $nav->setTitle(_('Es gibt neue Blubber'));
+ $navs[$thread->context_id] = $nav;
+ }
+ }
+
+ $default_navigation = new Navigation(_('Blubber'), 'dispatch.php/course/messenger/course');
+ $default_navigation->setImage(Icon::create('blubber'));
+ $default_navigation->setLinkAttributes(['title' => _('Blubber-Messenger')]);
+ foreach ($course_ids as $course_id) {
+ if (!isset($navs[$course_id])) {
+ $navs[$course_id] = $default_navigation;
}
}
- return $icon;
+ return $navs;
}
/**
diff --git a/lib/modules/ConsultationModule.php b/lib/modules/ConsultationModule.php
index 1de4fa3..03ee93f 100644
--- a/lib/modules/ConsultationModule.php
+++ b/lib/modules/ConsultationModule.php
@@ -3,8 +3,14 @@
* @author Jan-Hendrik Willms <tleilax+studip@gmail.com>
* @license GPL2 or any later version
*/
-class ConsultationModule extends CorePlugin implements StudipModule, SystemPlugin, PrivacyPlugin, HomepagePlugin
+class ConsultationModule extends CorePlugin implements
+ StudipModuleExtended,
+ SystemPlugin,
+ PrivacyPlugin,
+ HomepagePlugin
{
+ use IconNavigationTrait;
+
public function __construct()
{
parent::__construct();
@@ -88,14 +94,11 @@ class ConsultationModule extends CorePlugin implements StudipModule, SystemPlugi
return true;
}
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
// TODO
- return null;
+ return [];
}
/**
diff --git a/lib/modules/CoreAdmin.php b/lib/modules/CoreAdmin.php
index aded3ae..bddf732 100644
--- a/lib/modules/CoreAdmin.php
+++ b/lib/modules/CoreAdmin.php
@@ -7,22 +7,18 @@
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*/
-class CoreAdmin extends CorePlugin implements StudipModule
+class CoreAdmin extends CorePlugin implements StudipModuleExtended
{
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ use IconNavigationTrait;
+
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
$navigation = new Navigation(_('Verwaltung'), 'dispatch.php/course/management');
$navigation->setImage(Icon::create('admin'));
$navigation->setLinkAttributes(['title' => _('Verwaltung')]);
- return $navigation;
+ return array_fill_keys($course_ids, $navigation);
}
- /**
- * {@inheritdoc}
- */
public function getTabNavigation($course_id)
{
$range = RangeFactory::find($course_id);
diff --git a/lib/modules/CoreCalendar.php b/lib/modules/CoreCalendar.php
index bc20bdb..fbfc36e 100644
--- a/lib/modules/CoreCalendar.php
+++ b/lib/modules/CoreCalendar.php
@@ -9,20 +9,23 @@
* the License, or (at your option) any later version.
*/
-class CoreCalendar extends CorePlugin implements StudipModule
+class CoreCalendar extends CorePlugin implements StudipModuleExtended
{
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ use IconNavigationTrait;
+
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
if (!Config::get()->CALENDAR_GROUP_ENABLE) {
- return null;
+ return [];
}
- $navigation = new Navigation(_('Kalender'), URLHelper::getURL('dispatch.php/calendar/calendar/course/' . $course_id));
- $navigation->setImage(Icon::create('schedule'));
- return $navigation;
+ $navs = [];
+ foreach ($course_ids as $course_id) {
+ $navigation = new Navigation(_('Kalender'), URLHelper::getURL('dispatch.php/calendar/calendar/course/' . $course_id));
+ $navigation->setImage(Icon::create('schedule'));
+ $navs[$course_id] = $navigation;
+ }
+ return $navs;
}
/**
diff --git a/lib/modules/CoreDocuments.php b/lib/modules/CoreDocuments.php
index 5eb352c..6d2dea2 100644
--- a/lib/modules/CoreDocuments.php
+++ b/lib/modules/CoreDocuments.php
@@ -9,8 +9,9 @@
* the License, or (at your option) any later version.
*/
-class CoreDocuments extends CorePlugin implements StudipModule, OERModule
+class CoreDocuments extends CorePlugin implements StudipModuleExtended, OERModule
{
+ use IconNavigationTrait;
/**
* {@inheritdoc}
@@ -102,43 +103,51 @@ class CoreDocuments extends CorePlugin implements StudipModule, OERModule
}
}
-
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
- $range_type = get_object_type($course_id, ['sem', 'inst']) === 'sem' ? 'course' : 'institute';
- $navigation = new Navigation(
- _('Dateibereich'),
- "dispatch.php/{$range_type}/files"
- );
- $navigation->setImage(Icon::create('files'));
- $navigation->setLinkAttributes(['title' => _('Dateien')]);
-
- $condition = "INNER JOIN folders ON (folders.id = file_refs.folder_id)
+ // Assume that either courses or institutes will be fetched, but not a mix of them
+ $c_ids_copy = array_reverse($course_ids);
+ $range_type = get_object_type(array_pop($c_ids_copy), ['sem', 'inst']) === 'sem' ? 'course' : 'institute';
+ $condition = "SELECT folders.range_id, file_refs.id
+ FROM file_refs
+ JOIN folders ON (folders.id = file_refs.folder_id)
+ LEFT JOIN object_user_visits AS ouv
+ ON ouv.object_id = folders.range_id
+ AND ouv.user_id = :me
+ AND ouv.plugin_id = :plugin_id
WHERE folders.range_type = :range_type
- AND folders.range_id = :context_id
- AND GREATEST(file_refs.mkdate, file_refs.chdate) >= :last_visit
+ AND folders.range_id IN (:context_ids)
+ AND file_refs.chdate >= IF(ouv.visitdate > :threshold, ouv.visitdate, :threshold)
AND file_refs.user_id != :me";
- $file_refs = FileRef::findBySQL($condition, [
- 'me' => $user_id,
- 'last_visit' => $last_visit,
- 'context_id' => $course_id,
- 'range_type' => $range_type
+ $file_refs_by_range = DBManager::get()->fetchGroupedPairs($condition, [
+ ':me' => $user_id,
+ ':plugin_id' => $this->getPluginId(),
+ ':threshold' => object_get_visit_threshold(),
+ ':context_ids' => $course_ids,
+ ':range_type' => $range_type
]);
- foreach ($file_refs as $fileref) {
- $foldertype = $fileref->folder->getTypedFolder();
- if ($foldertype->isFileDownloadable($fileref->getId(), $user_id)) {
- $navigation->setImage(Icon::create('files', Icon::ROLE_ATTENTION), [
- 'title' => _('Es gibt neue Dateien.'),
- ]);
- $navigation->setURL("dispatch.php/{$range_type}/files/flat", ['select' => 'new']);
- break;
+
+ $navs = [];
+ foreach ($file_refs_by_range as $range_id => $file_refs) {
+ $file_ref_objs = FileRef::findMany($file_refs);
+ foreach ($file_ref_objs as $file_ref_obj) {
+ $foldertype = $file_ref_obj->folder->getTypedFolder();
+ $nav = new Navigation(_('Dateibereich'), "dispatch.php/{$range_type}/files");
+ if ($foldertype->isFileDownloadable($file_ref_obj->getId(), $user_id)) {
+ $nav->setImage(Icon::create('files', Icon::ROLE_ATTENTION));
+ $nav->setLinkAttributes(['title' => _('Es gibt neue Dateien.')]);
+ $nav->setURL("dispatch.php/{$range_type}/files/flat", ['select' => 'new']);
+ $navs[$range_id] = $nav;
+ break;
+ }
}
}
- return $navigation;
+ $default_navigation = new Navigation(_('Dateibereich'), "dispatch.php/{$range_type}/files");
+ $default_navigation->setImage(Icon::create('files'));
+ $default_navigation->setLinkAttributes(['title' => _('Dateien')]);
+ $remaining_courses = array_diff($course_ids, array_keys($navs));
+ return array_merge($navs, array_fill_keys($remaining_courses, $default_navigation));
}
/**
diff --git a/lib/modules/CoreForum.php b/lib/modules/CoreForum.php
index 838362b..a5b293a 100644
--- a/lib/modules/CoreForum.php
+++ b/lib/modules/CoreForum.php
@@ -10,8 +10,10 @@
use Forum\Posting;
-class CoreForum extends CorePlugin implements StudipModule
+class CoreForum extends CorePlugin implements StudipModuleExtended
{
+ use IconNavigationTrait;
+
public function getTabNavigation($course_id)
{
$navigation = new Navigation(_('Forum'), 'dispatch.php/course/forum/topics');
@@ -43,36 +45,39 @@ class CoreForum extends CorePlugin implements StudipModule
return ['forum' => $navigation];
}
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
- $recent_posts_count = 0;
- $navigation_title = _('Forum');
-
- if ($GLOBALS['perm']->have_studip_perm('user', $course_id)) {
- $recent_posts = Posting::getRecentPosts($course_id, $last_visit);
- $recent_posts_count = array_sum(array_column($recent_posts, 'posts'));
+ $navs = [];
+ $posts = Posting::getRecentPosts($course_ids);
+ foreach ($course_ids as $course_id) {
+ $recent_posts_count = 0;
+ $navigation_title = _('Forum');
+
+ if ($GLOBALS['perm']->have_studip_perm('user', $course_id)) {
+ $recent_posts_count = !empty($posts[$course_id])
+ ? array_sum(array_column($posts[$course_id], 'posts'))
+ : 0;
+
+ if ($recent_posts_count > 0) {
+ $navigation_title = sprintf(_('%s neue Beiträge seit Ihrem letzten Besuch.'), $recent_posts_count);
+ } else {
+ $navigation_title = _('Keine neuen Beiträge seit Ihrem letzten Besuch.');
+ }
+ }
+ $navigation = new Navigation(_('Forum'));
+ $navigation->setBadgeNumber($recent_posts_count);
+ $navigation->setLinkAttributes(['title' => $navigation_title]);
if ($recent_posts_count > 0) {
- $navigation_title = sprintf(_('%s neue Beiträge seit Ihrem letzten Besuch.'), $recent_posts_count);
+ $navigation->setImage(Icon::create('forum', Icon::ROLE_ATTENTION));
+ $navigation->setURL('dispatch.php/course/forum/recent');
} else {
- $navigation_title = _('Keine neuen Beiträge seit Ihrem letzten Besuch.');
+ $navigation->setImage(Icon::create('forum'));
+ $navigation->setURL('dispatch.php/course/forum/topics');
}
+ $navs[$course_id] = $navigation;
}
-
- $navigation = new Navigation(_("Forum"));
- $navigation->setBadgeNumber($recent_posts_count);
-
- $navigation->setLinkAttributes(['title' => $navigation_title]);
-
- if ($recent_posts_count > 0) {
- $navigation->setImage(Icon::create('forum', Icon::ROLE_ATTENTION));
- $navigation->setURL('dispatch.php/course/forum/recent', ['last_visit' => $last_visit]);
- } else {
- $navigation->setImage(Icon::create('forum'));
- $navigation->setURL('dispatch.php/course/forum/topics');
- }
-
- return $navigation;
+ return $navs;
}
public function getInfoTemplate($course_id)
diff --git a/lib/modules/CoreOverview.php b/lib/modules/CoreOverview.php
index 7a73be7..5550ed3 100644
--- a/lib/modules/CoreOverview.php
+++ b/lib/modules/CoreOverview.php
@@ -9,7 +9,7 @@
* the License, or (at your option) any later version.
*/
-class CoreOverview extends CorePlugin implements StudipModule
+class CoreOverview extends CorePlugin implements StudipModuleExtended
{
/**
* {@inheritdoc}
@@ -72,6 +72,64 @@ class CoreOverview extends CorePlugin implements StudipModule
return $nav;
}
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
+ {
+ $sql = "SELECT news_r.range_id,
+ COUNT(news.news_id) AS count,
+ COUNT(IF((news.chdate > IFNULL(b.visitdate, :threshold) AND news.user_id !=:user_id), news.news_id, NULL)) AS neue
+ FROM news_range AS news_r
+ JOIN news
+ ON news_r.news_id = news.news_id
+ AND UNIX_TIMESTAMP() BETWEEN date AND date + expire
+ LEFT JOIN object_user_visits AS b
+ ON b.object_id = news_r.news_id
+ AND b.user_id = :user_id
+ AND b.plugin_id = :plugin_id
+ WHERE news_r.range_id IN (:course_ids)
+ GROUP BY news_r.range_id";
+ $results = DBManager::get()->fetchAll($sql, [
+ ':user_id' => $user_id,
+ ':course_ids' => $course_ids,
+ ':threshold' => object_get_visit_threshold(),
+ ':plugin_id' => $this->getPluginId(),
+ ]);
+
+ $navs = [];
+ foreach ($results as $result) {
+ $nav = new Navigation(_('Ankündigungen'), '');
+ if ($result['neue']) {
+ $nav->setURL('?new_news=true');
+ $nav->setImage(Icon::create('news', Icon::ROLE_ATTENTION));
+ $nav->setLinkAttributes([
+ 'title' => sprintf(
+ ngettext(
+ '%1$d Ankündigung, %2$d neue',
+ '%1$d Ankündigungen, %2$d neue',
+ $result['count']
+ ),
+ $result['count'],
+ $result['neue']
+ )
+ ]);
+ $nav->setBadgeNumber($result['neue']);
+ } elseif ($result['count']) {
+ $nav->setImage(Icon::create('news'));
+ $nav->setLinkAttributes([
+ 'title' => sprintf(
+ ngettext(
+ '%d Ankündigung',
+ '%d Ankündigungen',
+ $result['count']
+ ),
+ $result['count']
+ )
+ ]);
+ }
+ $navs[$result['range_id']] = $nav;
+ }
+ return $navs;
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/lib/modules/CoreParticipants.php b/lib/modules/CoreParticipants.php
index d74d604..a208850 100644
--- a/lib/modules/CoreParticipants.php
+++ b/lib/modules/CoreParticipants.php
@@ -9,103 +9,126 @@
* the License, or (at your option) any later version.
*/
-class CoreParticipants extends CorePlugin implements StudipModule
+class CoreParticipants extends CorePlugin implements StudipModuleExtended
{
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ use IconNavigationTrait;
+
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
+ $navs = array_fill_keys($course_ids, null);
if ($user_id === 'nobody') {
- return null;
+ return $navs;
}
- $auto_insert_perm = Config::get()->AUTO_INSERT_SEM_PARTICIPANTS_VIEW_PERM;
- // show the participants-icon only if the course is not an auto-insert-sem
- if (
- AutoInsert::checkSeminar($course_id)
- && (
- ($GLOBALS['perm']->have_perm('admin', $user_id) && !$GLOBALS['perm']->have_perm($auto_insert_perm, $user_id))
- || !$GLOBALS['perm']->have_studip_perm($auto_insert_perm, $course_id, $user_id)
- )
- ) {
- return null;
+ // Filter courses that are auto-insert-seminars, to not show any icon
+ $course_ids = array_filter($course_ids, function ($course_id) use ($user_id) {
+ $auto_insert_perm = Config::get()->AUTO_INSERT_SEM_PARTICIPANTS_VIEW_PERM;
+ $is_auto_insert =
+ AutoInsert::checkSeminar($course_id)
+ && (
+ ($GLOBALS['perm']->have_perm('admin', $user_id) && !$GLOBALS['perm']->have_perm($auto_insert_perm, $user_id))
+ || !$GLOBALS['perm']->have_studip_perm($auto_insert_perm, $course_id, $user_id)
+ );
+ return !$is_auto_insert;
+ });
+
+ $courses = Course::findMany($course_ids);
+ $urls = [];
+ foreach ($courses as $course) {
+ $is_student = !$GLOBALS['perm']->have_studip_perm('tutor', $course->seminar_id, $user_id);
+
+ // Determine url to redirect to
+ if (!$course->getSemClass()->isGroup()) {
+ $urls[$course->seminar_id] = 'dispatch.php/course/members/index';
+ } elseif ($is_student) {
+ $navs[$course->seminar_id] = 0;
+ continue;
+ } else {
+ $urls[$course->seminar_id] = 'dispatch.php/course/grouping/members';
+ }
+
+ $navigation = new Navigation(_('Teilnehmende'), $urls[$course->seminar_id]);
+ $navigation->setImage(Icon::create('persons', Icon::ROLE_CLICKABLE));
+
+ // Check permission, show no indicator if not at least tutor
+ if ($is_student) {
+ $navs[$course->seminar_id] = $navigation;
+ }
}
- $course = Course::find($course_id);
-
- // Determine url to redirect to
- if (!$course->getSemClass()->isGroup()) {
- $url = 'dispatch.php/course/members/index';
- } elseif (!$GLOBALS['perm']->have_studip_perm('tutor', $course_id, $user_id)) {
- return null;
- } else {
- $url = 'dispatch.php/course/grouping/members';
- }
+ // For the remaining courses, show if there are new users
+ $remaining_course_ids = array_filter(
+ $course_ids,
+ fn($c_id) => $navs[$c_id] === null
+ );
- $navigation = new Navigation(_('Teilnehmende'), $url);
- $navigation->setImage(Icon::create('persons2'));
-
- // Check permission, show no indicator if not at least tutor
- if (!$GLOBALS['perm']->have_studip_perm('tutor', $course_id, $user_id)) {
- return $navigation;
- }
-
- $query = "SELECT COUNT(tmp.user_id) as count,
- COUNT(IF((tmp.mkdate > IFNULL(b.visitdate, :threshold) AND tmp.user_id != :user_id), tmp.user_id, NULL)) AS neue
+ $query = "SELECT seminar_users.seminar_id as seminar_id,
+ COUNT(seminar_users.user_id) as count,
+ COUNT(IF((seminar_users.mkdate > IFNULL(b.visitdate, :threshold) AND seminar_users.user_id != :user_id), seminar_users.user_id, NULL)) AS neue
FROM (
- SELECT user_id, mkdate
+ SELECT user_id, seminar_id, mkdate
FROM admission_seminar_user
- WHERE seminar_id = :course_id
+ WHERE seminar_id IN (:course_ids)
UNION ALL
- SELECT user_id, mkdate
+ SELECT user_id, seminar_id, mkdate
FROM seminar_user
- WHERE seminar_id = :course_id
- ) AS tmp
+ WHERE seminar_id IN (:course_ids)
+ ) AS seminar_users
LEFT JOIN object_user_visits AS b
- ON b.object_id = :course_id
+ ON b.object_id = seminar_users.seminar_id
AND b.user_id = :user_id
- AND b.plugin_id = :plugin_id";
- $statement = DBManager::get()->prepare($query);
- $statement->bindValue(':user_id', $user_id);
- $statement->bindValue(':course_id', $course_id);
- $statement->bindValue(':threshold', $last_visit);
- $statement->bindValue(':plugin_id', $this->getPluginId());
-
- $statement->execute();
- $result = $statement->fetch(PDO::FETCH_ASSOC);
-
- if ($result['neue']) {
- $navigation->setImage(Icon::create('persons', Icon::ROLE_ATTENTION, [
- 'title' => sprintf(
- ngettext(
- '%1$d Teilnehmende/r, %2$d neue/r',
- '%1$d Teilnehmende, %2$d neue',
- $result['count']
- ),
- $result['count'],
- $result['neue']
- )
- ]));
- $navigation->setBadgeNumber($result['neue']);
- } elseif ($result['count']) {
- $navigation->setLinkAttributes([
- 'title' => sprintf(
- ngettext(
- '%d Teilnehmende/r',
- '%d Teilnehmende',
+ AND b.plugin_id = :plugin_id
+ GROUP BY seminar_users.seminar_id";
+ $users_per_course = DBManager::get()->fetchAll($query, [
+ ':course_ids' => $remaining_course_ids,
+ ':user_id' => $user_id,
+ ':threshold' => object_get_visit_threshold(),
+ ':plugin_id' => $this->getPluginId(),
+ ]);
+
+ foreach ($users_per_course as $result) {
+ $navigation = new Navigation(_('Teilnehmende'), $urls[$result['seminar_id']]);
+
+ if ($result['neue']) {
+ $navigation->setImage(Icon::create('persons', Icon::ROLE_ATTENTION));
+ $navigation->setLinkAttributes([
+ 'title' => sprintf(
+ ngettext(
+ '%1$d Teilnehmende/r, %2$d neue/r',
+ '%1$d Teilnehmende, %2$d neue',
+ $result['count']
+ ),
+ $result['count'],
+ $result['neue']
+ )
+ ]);
+ $navigation->setBadgeNumber($result['neue']);
+ } elseif ($result['count']) {
+ $navigation->setImage(Icon::create('persons'));
+ $navigation->setLinkAttributes([
+ 'title' => sprintf(
+ ngettext(
+ '%d Teilnehmende/r',
+ '%d Teilnehmende',
+ $result['count']
+ ),
$result['count']
- ),
- $result['count']
- )
- ]);
- }
+ )
+ ]);
+ }
- return $navigation;
+ $navs[$result['seminar_id']] = $navigation;
+ }
+ // map the zeros to null;
+ return array_map(
+ fn ($nav) => $nav ?: null,
+ $navs
+ );
}
+
/**
* {@inheritdoc}
*/
diff --git a/lib/modules/CorePersonal.php b/lib/modules/CorePersonal.php
index 48aa5b2..9972d6f 100644
--- a/lib/modules/CorePersonal.php
+++ b/lib/modules/CorePersonal.php
@@ -8,11 +8,13 @@
* the License, or (at your option) any later version.
*/
-class CorePersonal extends CorePlugin implements StudipModule
+class CorePersonal extends CorePlugin implements StudipModuleExtended
{
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ use IconNavigationTrait;
+
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
- return null;
+ return [];
}
public function getTabNavigation($course_id)
diff --git a/lib/modules/CoreSchedule.php b/lib/modules/CoreSchedule.php
index 39b3e1f..d5a86af 100644
--- a/lib/modules/CoreSchedule.php
+++ b/lib/modules/CoreSchedule.php
@@ -9,61 +9,64 @@
* the License, or (at your option) any later version.
*/
-class CoreSchedule extends CorePlugin implements StudipModule
+class CoreSchedule extends CorePlugin implements StudipModuleExtended
{
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ use IconNavigationTrait;
+
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
- $query = "SELECT COUNT(termin_id) AS count,
- COUNT(IF((chdate > IFNULL(ouv.visitdate, :threshold) AND autor_id != :user_id), termin_id, NULL)) AS neue
+ $query = "SELECT termine.range_id,
+ COUNT(termin_id) AS count,
+ COUNT(IF((chdate > IFNULL(ouv.visitdate, :threshold) AND autor_id != :user_id), termin_id, NULL)) AS neue
FROM termine
LEFT JOIN object_user_visits AS ouv
- ON ouv.object_id = range_id
+ ON ouv.object_id = termine.range_id
AND ouv.user_id = :user_id
AND ouv.plugin_id = :plugin_id
- WHERE range_id = :course_id";
- $statement = DBManager::get()->prepare($query);
- $statement->bindValue(':user_id', $user_id);
- $statement->bindValue(':course_id', $course_id);
- $statement->bindValue(':threshold', $last_visit);
- $statement->bindValue(':plugin_id', $this->getPluginId());
- $statement->execute();
- $result = $statement->fetch(PDO::FETCH_ASSOC);
+ WHERE termine.range_id IN (:course_ids)
+ GROUP BY termine.range_id ";
+ $results = DBManager::get()->fetchAll($query, [
+ ':user_id' => $user_id,
+ ':course_ids' => $course_ids,
+ ':threshold' => object_get_visit_threshold(),
+ ':plugin_id' => $this->getPluginId(),
+ ],PDO::FETCH_ASSOC);
- if (!$result || (!$result['neue'] && !$result['count'])) {
- return null;
- }
-
- $nav = new Navigation(_('Ablaufplan'), 'dispatch.php/course/dates');
- if ($result['neue']) {
- $nav->setImage(Icon::create('schedule', Icon::ROLE_ATTENTION, [
- 'title' => sprintf(
- ngettext(
- '%1$d Termin, %2$d neuer',
- '%1$d Termine, %2$d neue',
- $result['count']
- ),
- $result['count'],
- $result['neue']
- )
- ]));
- $nav->setBadgeNumber($result['neue']);
- } else {
- $nav->setImage(Icon::create('schedule'));
- $nav->setLinkAttributes([
- 'title' => sprintf(
- ngettext(
- '%d Termin',
- '%d Termine',
+ $navs = array_fill_keys($course_ids, null);
+ foreach ($results as $result) {
+ $nav = new Navigation(_('Ablaufplan'), 'dispatch.php/course/dates');
+ if ($result['neue']) {
+ $nav->setBadgeNumber($result['neue']);
+ $nav->setLinkAttributes([
+ 'title' => sprintf(
+ ngettext(
+ '%1$d Termin, %2$d neuer',
+ '%1$d Termine, %2$d neue',
+ $result['count']
+ ),
+ $result['count'],
+ $result['neue']
+ )
+ ]);
+ $nav->setImage(Icon::create('schedule', Icon::ROLE_ATTENTION));
+ } else {
+ $nav->setLinkAttributes([
+ 'title' => sprintf(
+ ngettext(
+ '%d Termin',
+ '%d Termine',
+ $result['count']
+ ),
$result['count']
- ),
- $result['count']
- )
- ]);
+ )
+ ]);
+ $nav->setImage(Icon::create('schedule'));
+ }
+
+ $navs[$result['range_id']] = $nav;
}
- return $nav;
+
+ return $navs;
}
/**
diff --git a/lib/modules/CoreScm.php b/lib/modules/CoreScm.php
index 66f3853..3f2c6ed 100644
--- a/lib/modules/CoreScm.php
+++ b/lib/modules/CoreScm.php
@@ -9,62 +9,52 @@
* the License, or (at your option) any later version.
*/
-class CoreScm extends CorePlugin implements StudipModule
+class CoreScm extends CorePlugin implements StudipModuleExtended
{
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ use IconNavigationTrait;
+
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
if (!Config::get()->SCM_ENABLE) {
- return null;
+ return [];
}
-
- $sql = "SELECT scm_id,
- SUM(IF(content != '', 1, 0)) AS count,
- SUM(IF((chdate > IFNULL(ouv.visitdate, :threshold) AND scm.user_id !=:user_id), IF(content != '', 1, 0), NULL)) AS neue
- FROM scm
- LEFT JOIN object_user_visits AS ouv
- ON ouv.object_id = scm.range_id
- AND ouv.user_id = :user_id
- AND ouv.plugin_id = :plugin_id
- WHERE scm.range_id = :course_id
- GROUP BY scm.range_id";
-
- $statement = DBManager::get()->prepare($sql);
- $statement->bindValue(':user_id', $user_id);
- $statement->bindValue(':course_id', $course_id);
- $statement->bindValue(':threshold', $last_visit);
- $statement->bindValue(':plugin_id', $this->getPluginId());
- $statement->execute();
- $result = $statement->fetch(PDO::FETCH_ASSOC);
-
- if (!$result) {
- return null;
- }
-
- $scm = StudipScmEntry::find($result['scm_id']);
-
- $nav = new Navigation((string) $scm->tab_name, 'dispatch.php/course/scm');
-
- if ($result['count']) {
- if ($result['neue']) {
- $nav->setImage(Icon::create('infopage', Icon::ROLE_NEW));
- $nav->setBadgeNumber($result['neue']);
- if ($result['count'] == 1) {
- $title = $scm->tab_name . _(' (geändert)');
- } else {
- $title = sprintf(
- _('%1$d Einträge insgesamt, %2$d neue'),
- $result['count'],
- $result['neue']
- );
- }
- } else {
- $nav->setImage(Icon::create('infopage'));
- if ($result['count'] == 1) {
- $title = $scm->tab_name;
- } else {
+ $navs = array_fill_keys($course_ids, null);
+ $sql = "SELECT range_id, tab_name,
+ SUM(IF(content != '', 1, 0)) AS count,
+ SUM(IF((chdate > IFNULL(ouv.visitdate, :threshold) AND scm.user_id !=:user_id), IF(content != '', 1, 0), NULL)) AS neue
+ FROM scm
+ LEFT JOIN object_user_visits AS ouv
+ ON ouv.object_id = scm.range_id
+ AND ouv.user_id = :user_id
+ AND ouv.plugin_id = :plugin_id
+ WHERE scm.range_id IN (:course_ids)
+ GROUP BY scm.range_id";
+ $results = DBManager::get()->fetchAll($sql, [
+ ':user_id' => $user_id,
+ ':course_ids' => $course_ids,
+ ':threshold' => object_get_visit_threshold(),
+ ':plugin_id' => $this->getPluginId(),
+ ]);
+
+ foreach ($results as $result) {
+ $title = $result['tab_name'];
+ $image = Icon::create('info');
+ $badge = 0;
+
+ if ($result['count']) {
+ if ($result['neue']) {
+ $badge = $result['neue'];
+ if ($result['count'] == 1) {
+ $title .= _(' (geändert)');
+ } else {
+ $title = sprintf(
+ _('%1$d Einträge insgesamt, %2$d neue'),
+ $result['count'],
+ $result['neue']
+ );
+ }
+ $image = Icon::create('info', Icon::ROLE_ATTENTION);
+ } else if ($result['count'] > 1) {
$title = sprintf(
ngettext(
'%d Eintrag',
@@ -73,11 +63,17 @@ class CoreScm extends CorePlugin implements StudipModule
),
$result['count']
);
+ $image = Icon::create('info');
}
}
+ $nav = new Navigation($title, 'dispatch.php/course/scm');
+ $nav->setBadgeNumber($badge);
+ $nav->setImage($image);
$nav->setLinkAttributes(['title' => $title]);
+ $navs[$result['range_id']] = $nav;
}
- return $nav;
+
+ return $navs;
}
/**
diff --git a/lib/modules/CoreStudygroupAdmin.php b/lib/modules/CoreStudygroupAdmin.php
index 5d0f947..869b0cc 100644
--- a/lib/modules/CoreStudygroupAdmin.php
+++ b/lib/modules/CoreStudygroupAdmin.php
@@ -9,18 +9,20 @@
* the License, or (at your option) any later version.
*/
-class CoreStudygroupAdmin extends CorePlugin implements StudipModule
+class CoreStudygroupAdmin extends CorePlugin implements StudipModuleExtended
{
+ use IconNavigationTrait;
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
- $navigation = new Navigation(_('Verwaltung'), "dispatch.php/course/studygroup/edit/?cid={$course_id}");
- $navigation->setImage(Icon::create('admin'));
- $navigation->setLinkAttributes(['title' => _('Verwaltung')]);
- return $navigation;
+ $navs = [];
+ foreach ($course_ids as $course_id) {
+ $navigation = new Navigation(_('Verwaltung'), "dispatch.php/course/studygroup/edit/?cid={$course_id}");
+ $navigation->setImage(Icon::create('admin'));
+ $navigation->setLinkAttributes(['title' => _('Verwaltung')]);
+ $navs[$course_id] = $navigation;
+ }
+ return $navs;
}
/**
diff --git a/lib/modules/CoreStudygroupParticipants.php b/lib/modules/CoreStudygroupParticipants.php
index 5f8ac87..e77721f 100644
--- a/lib/modules/CoreStudygroupParticipants.php
+++ b/lib/modules/CoreStudygroupParticipants.php
@@ -9,19 +9,39 @@
* the License, or (at your option) any later version.
*/
-class CoreStudygroupParticipants extends CorePlugin implements StudipModule
+class CoreStudygroupParticipants extends CorePlugin implements StudipModuleExtended
{
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ use IconNavigationTrait;
+
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
- $navigation = new Navigation(_('Teilnehmende'), "dispatch.php/course/studygroup/members/{$course_id}");
- $navigation->setImage(Icon::create('persons'));
- if ($last_visit && CourseMember::countBySQL("seminar_id = :course_id AND mkdate >= :last_visit", ['last_visit' => $last_visit, 'course_id' => $course_id]) > 0) {
- $navigation->setImage(Icon::create('persons', Icon::ROLE_ATTENTION));
+ $results = DBManager::get()->fetchAll(
+ "SELECT seminar_user.Seminar_id, COUNT(seminar_user.user_id) as neue
+ FROM seminar_user
+ LEFT JOIN object_user_visits AS ouv
+ ON ouv.object_id = seminar_user.Seminar_id
+ AND ouv.user_id = :user_id
+ AND ouv.plugin_id = :plugin_id
+ WHERE seminar_user.Seminar_id IN (:course_ids)
+ AND seminar_user.mkdate > IFNULL(ouv.visitdate, :threshold)
+ GROUP BY seminar_user.Seminar_id",
+ [
+ ':course_ids' => $course_ids,
+ ':user_id' => $user_id,
+ ':plugin_id' => $this->getPluginId(),
+ 'threshold' => object_get_visit_threshold()
+ ],
+ );
+ $navs = [];
+ foreach ($course_ids as $course_id) {
+ $navigation = new Navigation(_('Teilnehmende'), "dispatch.php/course/studygroup/members/{$course_id}");
+ $navigation->setImage(Icon::create('persons'));
+ if (isset($results[$course_id]) && !empty($results[$course_id]['neue'])) {
+ $navigation->setImage(Icon::create('persons', Icon::ROLE_ATTENTION));
+ }
+ $navs[$course_id] = $navigation;
}
- return $navigation;
+ return $navs;
}
/**
diff --git a/lib/modules/CoreWiki.php b/lib/modules/CoreWiki.php
index 76a1f8f..9fd03ab 100644
--- a/lib/modules/CoreWiki.php
+++ b/lib/modules/CoreWiki.php
@@ -9,98 +9,79 @@
* the License, or (at your option) any later version.
*/
-class CoreWiki extends CorePlugin implements StudipModule
+class CoreWiki extends CorePlugin implements StudipModuleExtended
{
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($range_id, $last_visit, $user_id)
+ use IconNavigationTrait;
+
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
if (!Config::get()->WIKI_ENABLE) {
- return null;
+ return [];
}
$perm = $GLOBALS['perm']->get_perm($user_id);
if (in_array($perm, ['admin', 'root'])) {
$perm = 'dozent';
}
- $statement = DBManager::get()->prepare("
- SELECT `wiki_pages`.`page_id`
- FROM `wiki_pages`
- LEFT JOIN `statusgruppe_user` ON (`statusgruppe_user`.`statusgruppe_id` = `wiki_pages`.`read_permission`)
- WHERE `wiki_pages`.`range_id` = :range_id
- AND (
- `wiki_pages`.`read_permission` = 'all'
- OR `statusgruppe_user`.`user_id` = :user_id
- OR `wiki_pages`.`read_permission` = :perm
- OR (`wiki_pages`.`read_permission` = 'tutor' AND :perm = 'dozent')
- )
- ");
-
- $statement->execute([
- 'range_id' => $range_id,
- 'user_id' => $user_id,
- 'perm' => $perm
- ]);
- $wiki_page_ids = $statement->fetchAll(PDO::FETCH_COLUMN);
- if (count($wiki_page_ids) === 0) {
- return null;
- }
-
- $visit_date = object_get_visit($range_id, $this->getPluginId(), 'visitdate') ?? $last_visit;
-
- $statement = DBManager::get()->prepare("
- SELECT COUNT(*) AS `neue`
- FROM `wiki_pages`
- WHERE `wiki_pages`.`page_id` IN (:page_ids)
- AND `wiki_pages`.`chdate` > :threshold
- AND `wiki_pages`.`user_id` != :user_id
- ");
- $statement->execute([
- 'page_ids' => $wiki_page_ids,
- 'threshold' => $visit_date,
- 'user_id' => $user_id,
+ $query = "SELECT wiki_pages.range_id,
+ COUNT(page_id) AS count,
+ COUNT(IF((wiki_pages.chdate > IFNULL(ouv.visitdate, :threshold) AND wiki_pages.user_id != :user_id), page_id, NULL)) AS neue
+ FROM wiki_pages
+ LEFT JOIN statusgruppe_user ON (statusgruppe_user.statusgruppe_id = wiki_pages.read_permission)
+ LEFT JOIN object_user_visits AS ouv
+ ON ouv.object_id = wiki_pages.range_id
+ AND ouv.user_id = :user_id
+ AND ouv.plugin_id = :plugin_id
+ WHERE wiki_pages.range_id IN (:range_ids)
+ AND (
+ wiki_pages.read_permission = 'all'
+ OR statusgruppe_user.user_id = :user_id
+ OR wiki_pages.read_permission = :perm
+ OR (wiki_pages.read_permission = 'tutor' AND :perm = 'dozent')
+ )
+ GROUP BY wiki_pages.range_id;";
+ $results = DBManager::get()->fetchAll($query, [
+ ':range_ids' => $course_ids,
+ ':user_id' => $user_id,
+ ':perm' => $perm,
+ ':plugin_id' => $this->getPluginId(),
+ ':threshold' => object_get_visit_threshold(),
]);
- $new_pages = $statement->fetch(PDO::FETCH_COLUMN, 0);
- $nav = new Navigation(_('Wiki'));
- if ($new_pages > 0) {
- $nav->setURL('dispatch.php/course/wiki/newpages');
- $nav->setImage(Icon::create('wiki', Icon::ROLE_ATTENTION, [
+ $navs = array_fill_keys($course_ids, null);
+ foreach ($results as $result) {
+ $nav = new Navigation(_('Wiki'));
+ $params = [
'title' => sprintf(
ngettext(
'%d Wiki-Seite',
'%d Wiki-Seiten',
- count($wiki_page_ids)
+ $result['count']
),
- count($wiki_page_ids)
+ $result['count']
)
- . ', '
- . sprintf(
- ngettext(
- '%d Änderung',
- '%d Änderungen',
- $new_pages
- ),
- $new_pages
- )
- ]));
- $nav->setBadgeNumber($new_pages);
- } else {
- $nav->setURL('dispatch.php/course/wiki/page');
- $nav->setImage(Icon::create('wiki'));
- $nav->setLinkAttributes([
- 'title' => sprintf(
- ngettext(
- '%d Wiki-Seite',
- '%d Wiki-Seiten',
- count($wiki_page_ids)
- ),
- count($wiki_page_ids)
- )
- ]);
+ ];
+ if ($result['neue']) {
+ $nav->setURL('dispatch.php/course/wiki/newpages');
+ $nav->setImage(Icon::create('wiki', Icon::ROLE_ATTENTION));
+ $params['title'] .= ', ' . sprintf(
+ ngettext(
+ '%d Änderung',
+ '%d Änderungen',
+ $result['neue']
+ ),
+ $result['neue']
+ );
+ $nav->setBadgeNumber($result['neue']);
+ } else {
+ $nav->setURL('dispatch.php/course/wiki/page');
+ $nav->setImage(Icon::create('wiki'));
+
+ }
+ $nav->setLinkAttributes($params);
+ $navs[$result['range_id']] = $nav;
}
- return $nav;
+ return $navs;
}
/**
@@ -227,5 +208,4 @@ class CoreWiki extends CorePlugin implements StudipModule
{
return (bool) Config::get()->getValue('WIKI_ENABLE');
}
-
}
diff --git a/lib/modules/CoursewareModule.php b/lib/modules/CoursewareModule.php
index c081e6a..5b12cd6 100644
--- a/lib/modules/CoursewareModule.php
+++ b/lib/modules/CoursewareModule.php
@@ -3,8 +3,10 @@
use Courseware\Instance;
use Courseware\StructuralElement;
-class CoursewareModule extends CorePlugin implements SystemPlugin, StudipModule
+class CoursewareModule extends CorePlugin implements SystemPlugin, StudipModuleExtended
{
+ use IconNavigationTrait;
+
/**
* {@inheritdoc}
*/
@@ -79,54 +81,58 @@ class CoursewareModule extends CorePlugin implements SystemPlugin, StudipModule
return ['courseware' => $navigation];
}
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($courseId, $last_visit, $user_id)
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
if ($user_id === 'nobody') {
- return null;
+ return [];
}
- $statement = DBManager::get()->prepare("
- SELECT COUNT(DISTINCT elem.id)
+ $results = DBManager::get()->fetchGrouped(
+ "SELECT elem.range_id,
+ COUNT(IF((blocks.chdate > IFNULL(ouv.visitdate, :threshold) AND blocks.editor_id != :user_id), elem.id, NULL)) AS neue
FROM `cw_structural_elements` AS elem
INNER JOIN `cw_containers` as container ON (elem.id = container.structural_element_id)
INNER JOIN `cw_blocks` as blocks ON (container.id = blocks.container_id)
+ LEFT JOIN object_user_visits AS ouv
+ ON ouv.object_id = elem.range_id
+ AND ouv.user_id = :user_id
+ AND ouv.plugin_id = :plugin_id
WHERE elem.range_type = 'course'
- AND elem.range_id = :range_id
+ AND elem.range_id IN (:range_ids)
AND blocks.payload != ''
- AND blocks.chdate > :last_visit
AND blocks.editor_id != :user_id
- ");
-
- $statement->execute([
- 'range_id' => $courseId,
- 'last_visit' => $last_visit,
- 'user_id' => $user_id
- ]);
-
- $new = $statement->fetchColumn();
+ GROUP BY elem.range_id",
+ [
+ 'user_id' => $user_id,
+ 'range_ids' => $course_ids,
+ 'threshold' => object_get_visit_threshold(),
+ 'plugin_id' => $this->getPluginId(),
+ ]
+ );
- $nav = new Navigation(_('Courseware'), 'dispatch.php/course/courseware');
- $nav->setImage(Icon::create('courseware'));
- $nav->setLinkAttributes(['title' => _('Courseware')]);
+ $navs = [];
+ foreach ($course_ids as $course_id) {
+ $nav = new Navigation(_('Courseware'), 'dispatch.php/course/courseware');
+ $nav->setImage(Icon::create('courseware'));
+ $nav->setLinkAttributes(['title' => _('Courseware'),]);
+
+ if (!empty($results[$course_id]['neue'])) {
+ $text = sprintf(
+ ngettext(
+ '%u neue Seite',
+ '%u neue Seiten',
+ $results[$course_id]['neue']
+ ),
+ $results[$course_id]['neue']
+ );
+ $nav->setImage(Icon::create('courseware', Icon::ROLE_ATTENTION));
+ $nav->setLinkAttributes(['title' => $text]);
+ $nav->setBadgeNumber($results[$course_id]['neue']);
+ }
- if ($new > 0) {
- $text = sprintf(
- ngettext(
- '%u neue Seite',
- '%u neue Seiten',
- $new
- ),
- $new
- );
- $nav->setImage(Icon::create('courseware', Icon::ROLE_ATTENTION));
- $nav->setLinkAttributes(['title' => $text]);
- $nav->setBadgeNumber($new);
+ $navs[$course_id] = $nav;
}
-
- return $nav;
+ return $navs;
}
/**
diff --git a/lib/modules/FeedbackModule.php b/lib/modules/FeedbackModule.php
index 34a1ff1..f59ed4b 100644
--- a/lib/modules/FeedbackModule.php
+++ b/lib/modules/FeedbackModule.php
@@ -11,8 +11,10 @@
* @author Nils Gehrke <nils.gehrke@uni-goettingen.de>
* @license https://www.gnu.org/licenses/gpl-2.0.html GPL version 2
*/
-class FeedbackModule extends CorePlugin implements StudipModule, SystemPlugin
+class FeedbackModule extends CorePlugin implements StudipModuleExtended, SystemPlugin
{
+ use IconNavigationTrait;
+
/**
* {@inheritdoc}
*/
@@ -21,12 +23,9 @@ class FeedbackModule extends CorePlugin implements StudipModule, SystemPlugin
return null;
}
- /**
- * {@inheritdoc}
- */
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
- return null;
+ return [];
}
/**
diff --git a/lib/modules/GradebookModule.php b/lib/modules/GradebookModule.php
index 2a3be5c..34bf835 100644
--- a/lib/modules/GradebookModule.php
+++ b/lib/modules/GradebookModule.php
@@ -14,8 +14,10 @@ use Grading\Instance;
* @author <mlunzena@uos.de>
* @license http://www.gnu.org/licenses/gpl-2.0.html GPL version 2
*/
-class GradebookModule extends CorePlugin implements SystemPlugin, StudipModule
+class GradebookModule extends CorePlugin implements SystemPlugin, StudipModuleExtended
{
+ use IconNavigationTrait;
+
public function __construct()
{
parent::__construct();
@@ -38,41 +40,56 @@ class GradebookModule extends CorePlugin implements SystemPlugin, StudipModule
return null;
}
- /**
- * {@inheritdoc}
- *
- * @SuppressWarnings(PHPMD.Superglobals)
- */
- public function getIconNavigation($courseId, $lastVisit, $userId)
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
- if ($userId === 'nobody') {
- return null;
+ if ($user_id === 'nobody') {
+ return [];
}
+ // split courses in student-perms and tutor-perms
+ $tutor_c_ids = [];
+ foreach ($course_ids as $course_id) {
+ if ($GLOBALS['perm']->have_studip_perm('tutor', $course_id, $user_id)) {
+ $tutor_c_ids[$course_id] = $course_id;
+ }
+ }
+ $results = DBManager::get()->fetchGrouped(
+ "SELECT gd.course_id, gi.user_id
+ FROM grading_instances gi
+ JOIN grading_definitions gd ON(gd.id = definition_id)
+ LEFT JOIN object_user_visits AS ouv
+ ON ouv.object_id = gd.course_id
+ AND ouv.user_id = :user_id
+ AND ouv.plugin_id = :plugin_id
+ WHERE gd.course_id IN (:course_ids) AND gi.chdate > IFNULL(ouv.visitdate, :threshold)",
+ [
+ ':user_id' => $user_id,
+ ':plugin_id' => 0, // module doesnt write directly into ouv
+ ':course_ids' => $course_ids,
+ ':threshold' => object_get_visit_threshold(),
+ ]
+ );
$title = _('Gradebook');
- if ($GLOBALS['perm']->have_studip_perm('tutor', $courseId, $userId)) {
- $changed = Instance::countBySQL(
- 'INNER JOIN grading_definitions gd ON(gd.id = definition_id) '.
- 'WHERE gd.course_id = ? AND grading_instances.chdate > ?',
- [$courseId, $lastVisit]
- );
- } else {
- $changed = Instance::countBySQL(
- 'INNER JOIN grading_definitions gd ON(gd.id = definition_id) '.
- 'WHERE gd.course_id = ? AND grading_instances.chdate > ? AND user_id = ?',
- [$courseId, $lastVisit, $userId]
- );
+ $navs = [];
+ foreach ($course_ids as $course_id) {
+ if (empty($results[$course_id])) {
+ $changed = false;
+ } elseif (isset($tutor_c_ids[$course_id])) {
+ $changed = count($results[$course_id]);
+ } else {
+ $filtered_results = array_filter($results[$course_id], fn ($fetched_user_id) => $fetched_user_id === $user_id);
+ $changed = !empty($filtered_results) ? count($filtered_results) : 0;
+ }
+ $icon = $changed
+ ? Icon::create('assessment', Icon::ROLE_ATTENTION)
+ : Icon::create('assessment');
+ $navigation = new Navigation($title, 'dispatch.php/course/gradebook/overview');
+ $navigation->setImage($icon);
+ $navigation->setLinkAttributes(['title' => $title]);
+ $navs[$course_id] = $navigation;
}
- $icon = $changed
- ? Icon::create('gradebook', Icon::ROLE_NEW)
- : Icon::create('gradebook');
-
- $navigation = new Navigation($title, 'dispatch.php/course/gradebook/overview');
- $navigation->setImage($icon);
- $navigation->setLinkAttributes(['title' => $title]);
-
- return $navigation;
+ return $navs;
}
/**
@@ -157,8 +174,8 @@ class GradebookModule extends CorePlugin implements SystemPlugin, StudipModule
'description' => _('Dieses Modul ermöglicht die manuelle und automatische Erfassung von Noten und Leistungen.'),
'category' => _('Lehr- und Lernorganisation'),
'keywords' => _('automatische und manuelle Erfassung von gewichteten Leistungen;Export von Leistungen;persönliche Fortschrittskontrolle'),
- 'icon' => Icon::create('gradebook', Icon::ROLE_INFO),
- 'icon_clickable' => Icon::create('gradebook'),
+ 'icon' => Icon::create('assessment', Icon::ROLE_INFO),
+ 'icon_clickable' => Icon::create('assessment'),
'screenshots' => [
'path' => 'assets/images/plus/screenshots/Gradebook',
'pictures' => [
diff --git a/lib/modules/IconNavigationTrait.php b/lib/modules/IconNavigationTrait.php
new file mode 100644
index 0000000..739d645
--- /dev/null
+++ b/lib/modules/IconNavigationTrait.php
@@ -0,0 +1,19 @@
+<?php
+
+trait IconNavigationTrait
+{
+
+ public static array $nav_cache = [];
+
+ public function getIconNavigation($course_id, $last_visit, $user_id)
+ {
+ /** @var StudipModuleExtended $this */
+ if (!array_key_exists($course_id, self::$nav_cache)) {
+ $navs = $this->getManyIconNavigation([$course_id], $user_id);
+ self::$nav_cache[$course_id] = $navs[$course_id] ?? null;
+ }
+
+ return self::$nav_cache[$course_id];
+ }
+
+}
diff --git a/lib/modules/IliasInterfaceModule.php b/lib/modules/IliasInterfaceModule.php
index fdda3e3..6f9eca8 100644
--- a/lib/modules/IliasInterfaceModule.php
+++ b/lib/modules/IliasInterfaceModule.php
@@ -8,8 +8,10 @@
* @since 4.3
*/
-class IliasInterfaceModule extends CorePlugin implements StudipModule, SystemPlugin
+class IliasInterfaceModule extends CorePlugin implements StudipModuleExtended, SystemPlugin
{
+ use IconNavigationTrait;
+
public function __construct()
{
parent::__construct();
@@ -48,13 +50,16 @@ class IliasInterfaceModule extends CorePlugin implements StudipModule, SystemPlu
return null;
}
- public function getIconNavigation($course_id, $last_visit, $user_id)
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array
{
+ // TODO Test this function
if (!Config::get()->ILIAS_INTERFACE_ENABLE) {
- return;
+ return [];
}
- $sql = "SELECT COUNT(IF(a.module_type != 'crs', module_id, NULL)) AS count_modules,
+ $results = DBManager::get()->fetchAll(
+ "SELECT a.object_id,
+ COUNT(IF(a.module_type != 'crs', module_id, NULL)) AS count_modules,
COUNT(IF(a.module_type = 'crs', module_id, NULL)) AS count_courses,
COUNT(IF((chdate > IFNULL(b.visitdate, :threshold) AND a.module_type != 'crs'), module_id, NULL)) AS neue
FROM object_contentmodules AS a
@@ -62,62 +67,61 @@ class IliasInterfaceModule extends CorePlugin implements StudipModule, SystemPlu
ON b.object_id = a.object_id
AND b.user_id = :user_id
AND b.plugin_id = :plugin_id
- WHERE a.object_id = :course_id
- GROUP BY a.object_id";
-
- $statement = DBManager::get()->prepare($sql);
- $statement->bindValue(':user_id', $user_id);
- $statement->bindValue(':course_id', $course_id);
- $statement->bindValue(':threshold', $last_visit);
- $statement->bindValue(':plugin_id', $this->getPluginId());
- $statement->execute();
- $result = $statement->fetch(PDO::FETCH_ASSOC);
+ WHERE a.object_id IN (:course_ids)
+ GROUP BY a.object_id",
+ [
+ ':user_id' => $user_id,
+ ':course_ids' => $course_ids,
+ ':threshold' => object_get_visit_threshold(),
+ ':plugin_id' => $this->getPluginId(),
+ ]
+ );
- if (!$result) {
- return null;
- }
-
- $title = CourseConfig::get($course_id)->getValue('ILIAS_INTERFACE_MODULETITLE');
- $nav = new Navigation($title, 'dispatch.php/course/ilias_interface/index');
- if ($result['neue']) {
- $nav->setImage(Icon::create('learnmodule', Icon::ROLE_ATTENTION));
- $nav->setLinkAttributes([
- 'title' => sprintf(
- ngettext(
- '%1$d Lernobjekt, %2$d neues',
- '%1$d Lernobjekte, %2$d neue',
- $result['count_modules']
- ),
- $result['count_modules'],
- $result['neue']
- )
- ]);
- } elseif ($result['count_modules']) {
- $nav->setImage(Icon::create('learnmodule'));
- $nav->setLinkAttributes([
- 'title' => sprintf(
- ngettext(
- '%d Lernobjekt',
- '%d Lernobjekte',
+ $navs = [];
+ foreach ($results as $result) {
+ $title = CourseConfig::get($result['object_id'])->getValue('ILIAS_INTERFACE_MODULETITLE');
+ $nav = new Navigation($title, 'dispatch.php/course/ilias_interface/index');
+ if ($result['neue']) {
+ $nav->setImage(Icon::create('learnmodule', Icon::ROLE_ATTENTION));
+ $nav->setLinkAttributes([
+ 'title' => sprintf(
+ ngettext(
+ '%1$d Lernobjekt, %2$d neues',
+ '%1$d Lernobjekte, %2$d neue',
+ $result['count_modules']
+ ),
+ $result['count_modules'],
+ $result['neue']
+ )
+ ]);
+ } elseif ($result['count_modules']) {
+ $nav->setImage(Icon::create('learnmodule'));
+ $nav->setLinkAttributes([
+ 'title' => sprintf(
+ ngettext(
+ '%d Lernobjekt',
+ '%d Lernobjekte',
+ $result['count_modules']
+ ),
$result['count_modules']
- ),
- $result['count_modules']
- )
- ]);
- } elseif ($result['count_courses']) {
- $nav->setImage(Icon::create('learnmodule'));
- $nav->setLinkAttributes([
- 'title' => sprintf(
- ngettext(
- '%d ILIAS-Kurs',
- '%d ILIAS-Kurse',
+ )
+ ]);
+ } elseif ($result['count_courses']) {
+ $nav->setImage(Icon::create('learnmodule'));
+ $nav->setLinkAttributes([
+ 'title' => sprintf(
+ ngettext(
+ '%d ILIAS-Kurs',
+ '%d ILIAS-Kurse',
+ $result['count_courses']
+ ),
$result['count_courses']
- ),
- $result['count_courses']
- )
- ]);
+ )
+ ]);
+ }
+ $navs[$result['object_id']] = $nav;
}
- return $nav;
+ return $navs;
}
public function getTabNavigation($course_id)
diff --git a/lib/modules/StudipModuleExtended.php b/lib/modules/StudipModuleExtended.php
new file mode 100644
index 0000000..c80af15
--- /dev/null
+++ b/lib/modules/StudipModuleExtended.php
@@ -0,0 +1,27 @@
+<?php
+
+interface StudipModuleExtended extends StudipModule
+{
+ /**
+ * Returns navigation objects representing this plugin
+ * in the course overview table for every given course or institute.
+ * The navigation object's title will not be shown,
+ * only the image (and its associated attributes like 'title')
+ * and the URL are actually used.
+ *
+ * By convention, new or changed plugin content is indicated
+ * by a different icon and a corresponding tooltip.
+ *
+ * Returning null for a course will result in a blank space, while returning no entry will render nothing.
+ *
+ * @param array $course_ids array of course or institute range ids.
+ * Only ranges where the module is active should be given
+ * @param string|null $user_id the user to get the navigation for
+ *
+ * @return Navigation[] associative array per given course, containing a navigation or null,
+ * where the course_id is the key: ['course_id_1' => $nav1, 'course_id_2' => $nav2, 'course_id_3' => null, ...].
+ *
+ */
+ public function getManyIconNavigation(array $course_ids, ?string $user_id = null): array;
+
+}