diff options
| author | Thomas Hackl <hackl@data-quest.de> | 2022-12-12 15:20:27 +0000 |
|---|---|---|
| committer | David Siegfried <david.siegfried@uni-vechta.de> | 2022-12-12 15:20:27 +0000 |
| commit | 936d1c00ea05bb0a5c56f441ff2eece6ccefada6 (patch) | |
| tree | 10c4b9a4a1d02780112f7ddc99f9a88b37a1ad20 /lib | |
| parent | 54dbf11a35cac0de6fed1c50114c9583d210a40e (diff) | |
Resolve "StEP00348: Responsive Navigation Stud.IP 5.x"
Closes #32
Merge request studip/studip!65
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/classes/ContentBar.php | 34 | ||||
| -rw-r--r-- | lib/classes/ResponsiveHelper.php | 173 | ||||
| -rw-r--r-- | lib/classes/SkipLinks.php | 24 | ||||
| -rw-r--r-- | lib/classes/WidgetContainer.php | 20 | ||||
| -rw-r--r-- | lib/classes/sidebar/Sidebar.php | 6 | ||||
| -rw-r--r-- | lib/include/html_head.inc.php | 6 | ||||
| -rw-r--r-- | lib/modules/ConsultationModule.class.php | 8 | ||||
| -rw-r--r-- | lib/modules/CoursewareModule.class.php | 1 | ||||
| -rw-r--r-- | lib/navigation/FooterNavigation.php | 6 | ||||
| -rw-r--r-- | lib/wiki.inc.php | 20 |
10 files changed, 242 insertions, 56 deletions
diff --git a/lib/classes/ContentBar.php b/lib/classes/ContentBar.php index 043fe18..53b3dfc 100644 --- a/lib/classes/ContentBar.php +++ b/lib/classes/ContentBar.php @@ -15,25 +15,41 @@ class ContentBar public $infoText = ''; public $icon = ''; public $toc = null; + public $actionMenu = null; + /** - * ContentBar constructor. - * - * Note: An icon for consumer mode is always shown, this would have to be changed via template. + * The singleton instance of the container + */ + protected static $instance = null; + + /** + * Returns the instance of this container to ensure there is only one + * instance. * * @param TOCItem $toc Table of contents object. * @param string $info Some information to show, like creation date, author etc. * @param Icon|null $icon An icon to show in content bar. * @param ActionMenu|null $actionMenu Optional action menu for page actions. + * @return ContentBar + * @static */ - public function __construct(TOCItem $toc, string $info = '', Icon $icon = null, ActionMenu $actionMenu = null) + public static function get(): ContentBar { - $this->infoText = $info; - $this->icon = $icon; - $this->toc = $toc; - $this->actionMenu = $actionMenu; + if (static::$instance === null) { + static::$instance = new static; + } + return static::$instance; } - public $actionMenu = null; + /** + * Private constructor to ensure that the singleton Get() method is always + * used. + * + * @see ContentBar::get + */ + protected function __construct() + { + } /** * Provide some info text. diff --git a/lib/classes/ResponsiveHelper.php b/lib/classes/ResponsiveHelper.php index eb7f41c..c2b201e 100644 --- a/lib/classes/ResponsiveHelper.php +++ b/lib/classes/ResponsiveHelper.php @@ -24,27 +24,54 @@ class ResponsiveHelper $link_params = array_fill_keys(array_keys(URLHelper::getLinkParams()), null); foreach (Navigation::getItem('/')->getSubNavigation() as $path => $nav) { - if (!$nav->isVisible(true)) { - continue; + $image = $nav->getImage(); + + $forceVisibility = false; + /* + * Special treatment for "browse" navigation which is normally hidden + * when we are inside a course. + */ + if ($path === 'browse' && !$image) { + $image = Icon::create('seminar'); + $forceVisibility = true; + } + /* + * Special treatment for "footer" navigation because + * the real footer is hidden in responsive view. + */ + if ($path === 'footer' && !$image) { + $image = Icon::create('info'); + $nav->setTitle(_('Impressum & Information')); + $forceVisibility = true; } - $image = $nav->getImage(); $image_src = $image ? $image->copyWithRole('info_alt')->asImagePath() : false; $item = [ - 'icon' => $image_src ? self::getAssetsURL($image_src) : false, - 'title' => (string) $nav->getTitle(), - 'url' => self::getURL($nav->getURL(), $link_params), + 'icon' => $image_src ? self::getAssetsURL($image_src) : false, + 'title' => (string) $nav->getTitle(), + 'url' => URLHelper::getURL($nav->getURL(), $link_params, true), + 'parent' => '/', + 'path' => $path, + 'visible' => $forceVisibility ? true : $nav->isVisible(true), + 'active' => $nav->isActive() ]; if ($nav->isActive()) { - $activated[] = $path; + // course navigation is integrated in course sub-navigation items + if ($path === 'course') { + $activated[] = 'browse/my_courses/' . (Context::get()->getId()); + } else { + $activated[] = $path; + } } - if ($nav->getSubnavigation() && $path != 'start') { + if ($nav->getSubnavigation() && $path != 'start') { $item['children'] = self::getChildren($nav, $path, $activated); } - $navigation[$path] = $item; + if ($path !== 'course') { + $navigation[$path] = $item; + } } return [$navigation, $activated]; @@ -54,35 +81,50 @@ class ResponsiveHelper * Recursively build a navigation array from the subnavigation/children * of a navigation object. * - * @param Navigation $navigation The navigation object - * @param String $path Current path segment - * @param array $activated Activated items + * @param Navigation $navigation The navigation object + * @param String $path Current path segment + * @param array $activated Activated items + * @param String|null $cid Optional context ID * @return Array containing the children (+ grandchildren...) */ - protected static function getChildren(Navigation $navigation, $path, &$activated = []) + protected static function getChildren(Navigation $navigation, $path, &$activated = [], string $cid = null) { $children = []; foreach ($navigation->getSubNavigation() as $subpath => $subnav) { - if (!$subnav->isVisible()) { + /*if (!$subnav->isVisible()) { continue; - } + }*/ + $originalSubpath = $subpath; $subpath = "{$path}/{$subpath}"; $item = [ - 'title' => (string) $subnav->getTitle(), - 'url' => self::getURL($subnav->getURL()), + 'title' => (string) $subnav->getTitle(), + 'url' => URLHelper::getURL($subnav->getURL(), $cid ? ['cid' => $cid] : []), + 'parent' => $path, + 'path' => $subpath, + 'visible' => $subnav->isVisible(), + 'active' => $subnav->isActive() ]; if ($subnav->isActive()) { - $activated[] = $subpath; + // course navigation is integrated in course sub-navigation items + if ($path === 'course') { + $activated[] = 'browse/my_courses/' . Context::get()->getId() . '/' . $originalSubpath; + } else { + $activated[] = $subpath; + } } if ($subnav->getSubNavigation()) { $item['children'] = self::getChildren($subnav, $subpath); } + if ($subpath === 'browse/my_courses') { + $item['children'] = array_merge($item['children'] ?? [], static::getMyCoursesNavigation($activated)); + } + $children[$subpath] = $item; } @@ -113,4 +155,99 @@ class ResponsiveHelper { return str_replace($GLOBALS['ASSETS_URL'], '', $url); } + + /** + * Specialty for responsive navigation: build navigation items + * for my courses in current semester. + * + * @return array + */ + protected static function getMyCoursesNavigation($activated): array + { + if (!$GLOBALS['perm']->have_perm('admin')) { + $sem_data = Semester::getAllAsArray(); + + $currentIndex = -1; + + foreach ($sem_data as $index => $semester) { + if ($semester['current']) { + $currentIndex = $index; + break; + } + } + + $params = [ + 'deputies_enabled' => Config::get()->DEPUTIES_ENABLE + ]; + + $courses = MyRealmModel::getCourses($currentIndex, $currentIndex, $params); + } else { + $courses = []; + } + + $items = []; + + $standardIcon = Icon::create('seminar', Icon::ROLE_INFO_ALT)->asImagePath(); + + // Add current course to list. + if (Context::get() && Context::isCourse()) { + $courses[] = Context::get(); + } + + foreach ($courses as $course) { + $avatar = CourseAvatar::getAvatar($course->id); + if ($avatar->is_customized()) { + $icon = $avatar->getURL(Avatar::SMALL); + } else { + $icon = $standardIcon; + } + + $cnav = [ + 'icon' => $icon, + 'title' => $course->getFullname(), + 'url' => URLHelper::getURL('dispatch.php/course/details', ['cid' => $course->id]), + 'parent' => 'browse/my_courses', + 'path' => 'browse/my_courses/' . $course->id, + 'visible' => true, + 'active' => Course::findCurrent() ? Course::findCurrent()->id === $course->id : false, + 'children' => [] + ]; + + foreach ($course->tools as $tool) { + if (Seminar_Perm::get()->have_studip_perm($tool->getVisibilityPermission(), $course->id)) { + + $path = 'browse/my_courses/' . $course->id; + + $studip_module = $tool->getStudipModule(); + if ($studip_module instanceof StudipModule) { + $tool_nav = $studip_module->getTabNavigation($course->id) ?: []; + foreach ($tool_nav as $nav_name => $navigation) { + if ($nav_name && is_a($navigation, 'Navigation')) { + $cnav['children'][$path . '/' . $nav_name] = [ + 'icon' => $navigation->getImage() ? $navigation->getImage()->asImagePath() : '', + 'title' => $tool->getDisplayname(), + 'url' => URLHelper::getURL($navigation->getURL(), ['cid' => $course->id]), + 'parent' => 'browse/my_courses/' . $course->id, + 'path' => 'browse/my_courses/' . $course->id . '/' . $nav_name, + 'visible' => true, + 'active' => $navigation->isActive(), + 'children' => static::getChildren( + $navigation, + 'browse/my_courses/' . $course->id . '/' . $nav_name, + $activated, + $course->id + ), + ]; + } + } + } + } + } + + $items['browse/my_courses/' . $course->id] = $cnav; + + } + + return $items; + } } diff --git a/lib/classes/SkipLinks.php b/lib/classes/SkipLinks.php index 684ae42..796206d 100644 --- a/lib/classes/SkipLinks.php +++ b/lib/classes/SkipLinks.php @@ -60,27 +60,29 @@ class SkipLinks * @param string $url the url of the links * @param integer $position the position of the link in the list */ - public static function addLink(string $name, string $url, $position = null) + public static function addLink(string $name, string $url, $position = null, bool $inFullscreen = true) { $position = (!$position || $position < 1) ? count(self::$links) + 100 : (int) $position; self::$links[$url] = [ - 'name' => $name, - 'url' => $url, - 'position' => $position, + 'name' => $name, + 'url' => $url, + 'position' => $position, + 'fullscreen' => $inFullscreen ]; } /** * Adds a link to an anker on the same page to the list of skip links. * - * @param string $name the displayed name of the links - * @param string $id the id of the anker - * @param integer $position the position of the link in the list + * @param string $name the displayed name of the links + * @param string $id the id of the anker + * @param integer $position the position of the link in the list + * @param bool $inFullscreen is this link relevant in fullscreen mode? */ - public static function addIndex($name, $id, $position = null) + public static function addIndex($name, $id, $position = null, bool $inFullscreen = true) { $url = '#' . $id; - self::addLink($name, $url, $position); + self::addLink($name, $url, $position, $inFullscreen); } /** @@ -99,12 +101,14 @@ class SkipLinks }); $navigation = new Navigation(''); + $fullscreen = []; foreach (array_values(self::$links) as $index => $link) { $navigation->addSubNavigation( "/skiplinks/link-{$index}", new Navigation($link['name'], $link['url']) ); + $fullscreen['/skiplinks/link-' . $index] = $link['fullscreen']; } - return $GLOBALS['template_factory']->render('skiplinks', compact('navigation')); + return $GLOBALS['template_factory']->render('skiplinks', compact('navigation', 'fullscreen')); } } diff --git a/lib/classes/WidgetContainer.php b/lib/classes/WidgetContainer.php index 108477a..c702d06 100644 --- a/lib/classes/WidgetContainer.php +++ b/lib/classes/WidgetContainer.php @@ -160,6 +160,24 @@ abstract class WidgetContainer } /** + * Returns the number of widgets in this container. Optionally constrained + * to widgets of a specific class. + * + * @param string|null $widget_class + * @return int + */ + public function countWidgets(string $widget_class = null): int + { + $widgets = $this->widgets; + if ($widget_class !== null) { + $widgets = array_filter($widgets, function (Widget $widget) use ($widget_class) { + return is_a($widget, $widget_class); + }); + } + return count($widgets); + } + + /** * Returns whether this container has any widget. * * @return bool True if the container has at least one widget, false @@ -167,7 +185,7 @@ abstract class WidgetContainer */ public function hasWidgets() { - return count($this->widgets) > 0; + return $this->countWidgets() > 0; } /** diff --git a/lib/classes/sidebar/Sidebar.php b/lib/classes/sidebar/Sidebar.php index 7536a7f..00c3b8f 100644 --- a/lib/classes/sidebar/Sidebar.php +++ b/lib/classes/sidebar/Sidebar.php @@ -224,7 +224,8 @@ class Sidebar extends WidgetContainer SkipLinks::addIndex( _('Dritte Navigationsebene'), $widget->getId(), - 20 + 20, + false ); $navigation_widget_added = true; @@ -237,7 +238,8 @@ class Sidebar extends WidgetContainer SkipLinks::addIndex( _('Aktionen'), $widget->getId(), - 21 + 21, + false ); $actions_widget_added = true; diff --git a/lib/include/html_head.inc.php b/lib/include/html_head.inc.php index 4ab1c0e..a7f2c21 100644 --- a/lib/include/html_head.inc.php +++ b/lib/include/html_head.inc.php @@ -32,12 +32,6 @@ $lang_attr = str_replace('_', '-', $_SESSION['_language']); String.locale = "<?= htmlReady(strtr($_SESSION['_language'], '_', '-')) ?>"; document.querySelector('html').className = 'js'; - setTimeout(() => { - // This needs to be put in a timeout since otherwise it will not match - if (window.matchMedia('(max-width: 767px)').matches) { - document.querySelector('html').classList.add('responsive-display'); - } - }, 0); window.STUDIP = { ABSOLUTE_URI_STUDIP: "<?= $GLOBALS['ABSOLUTE_URI_STUDIP'] ?>", diff --git a/lib/modules/ConsultationModule.class.php b/lib/modules/ConsultationModule.class.php index acc2cb4..5671776 100644 --- a/lib/modules/ConsultationModule.class.php +++ b/lib/modules/ConsultationModule.class.php @@ -103,11 +103,13 @@ class ConsultationModule extends CorePlugin implements StudipModule, SystemPlugi */ public function getTabNavigation($course_id) { - if ($GLOBALS['user']->id === 'nobody') { - return []; + if ($GLOBALS['user']->id !== 'nobody') { + $navigation = new ConsultationNavigation(RangeFactory::find($course_id)); + $navigation->setImage(Icon::create('consultation', Icon::ROLE_INFO_ALT)); + return ['consultation' => $navigation]; } - return ['consultation' => new ConsultationNavigation(RangeFactory::find($course_id))]; + return []; } /** diff --git a/lib/modules/CoursewareModule.class.php b/lib/modules/CoursewareModule.class.php index 0d8dfe8..e07d5d2 100644 --- a/lib/modules/CoursewareModule.class.php +++ b/lib/modules/CoursewareModule.class.php @@ -39,6 +39,7 @@ class CoursewareModule extends CorePlugin implements SystemPlugin, StudipModule, _('Courseware'), URLHelper::getURL('dispatch.php/course/courseware/?cid='.$courseId) ); + $navigation->setImage(Icon::create('courseware', Icon::ROLE_INFO_ALT)); $navigation->addSubNavigation( 'content', new Navigation(_('Inhalt'), 'dispatch.php/course/courseware/?cid='.$courseId) diff --git a/lib/navigation/FooterNavigation.php b/lib/navigation/FooterNavigation.php index d3f8b83..a24dc11 100644 --- a/lib/navigation/FooterNavigation.php +++ b/lib/navigation/FooterNavigation.php @@ -27,6 +27,9 @@ class FooterNavigation extends Navigation { parent::initSubNavigation(); + // imprint + $this->addSubNavigation('siteinfo', new Navigation(_('Impressum'), 'dispatch.php/siteinfo/show?cancel_login=1')); + // sitemap if (is_object($GLOBALS['user']) && $GLOBALS['user']->id !== 'nobody') { $this->addSubNavigation('sitemap', new Navigation(_('Sitemap'), 'dispatch.php/sitemap/')); @@ -35,9 +38,6 @@ class FooterNavigation extends Navigation //studip $this->addSubNavigation('studip', new Navigation(_('Stud.IP'), 'http://www.studip.de/')); - // imprint - $this->addSubNavigation('siteinfo', new Navigation(_('Impressum'), 'dispatch.php/siteinfo/show?cancel_login=1')); - // Datenschutzerklärung //Check if the privacy url is one of the Stud.IP pages: diff --git a/lib/wiki.inc.php b/lib/wiki.inc.php index 29e50e4..6d37e54 100644 --- a/lib/wiki.inc.php +++ b/lib/wiki.inc.php @@ -1031,8 +1031,14 @@ function wikiEdit($keyword, $wikiData, $user_id, $backpage=NULL, $ancestor=NULL) } // Create content bar. - $contentBar = new ContentBar($toc, - $page_string, Icon::create('wiki'), $actionMenu); + $contentBar = ContentBar::get() + ->setTOC(CoreWiki::getTOC(WikiPage::getStartPage(Context::getId()))) + ->setInfo($page_string) + ->setIcon(Icon::create('wiki')); + + if ($actionMenu) { + $contentBar->setActionMenu($actionMenu); + } $template = $GLOBALS['template_factory']->open('wiki/edit.php'); $template->keyword = $keyword; @@ -1590,8 +1596,14 @@ function showWikiPage($keyword, $version, $special="", $show_comments="icon", $h } // Create content bar. - $contentBar = new ContentBar($toc, - $page_string, Icon::create('wiki'), $actionMenu); + $contentBar = ContentBar::get() + ->setTOC(CoreWiki::getTOC(WikiPage::getStartPage(Context::getId()))) + ->setInfo($page_string) + ->setIcon(Icon::create('wiki')); + + if ($actionMenu) { + $contentBar->setActionMenu($actionMenu); + } if ($hilight) { // Highlighting must only take place outside HTML tags, so |
