diff --git a/CHANGES.md b/CHANGES.md index a9151170117..1fafe801d5e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,7 +7,7 @@ Changes ### Unreleased * 2024-03-13 - Bugfix: Custom course fields of type "Textarea" were not conditionally hidden in the smart menu configuration, resolves #576. -* 2024-03-01 - Improvement: implement new setting to show starred courses in a popover menu in navbar, solves #289. +* 2024-03-01 - Feature: Show starred courses popover in the navbar, resolves #289. ### v4.3-r8 diff --git a/README.md b/README.md index 9720ec2bf6d..7f6ca478e3e 100644 --- a/README.md +++ b/README.md @@ -321,7 +321,9 @@ With this setting, you can set an alternative link URL which will be used as lin With this setting, you can add a 'Set preferred language' setting to the language menu within the user menu. Understandably, this setting is only processed if the language menu is enabled at all. -###### Show starred courses in primary navigation bar +##### Navbar + +###### Show starred courses popover in the navbar With this setting, you can show a popover menu with links to starred courses next to the messages and notifications menus. @@ -779,6 +781,5 @@ Moodle an Hochschulen e.V. would like to thank these main contributors (in alpha * University of Graz, André Menrath: Code * University of Lübeck, Christian Wolters: Code, Peer Review, Ideating * Zurich University of Applied Sciences (ZHAW): Funding, Ideating -* Academic Moodle Cooperation (AMC): Code, Ideating Additionally, we thank all other contributors who contributed ideas, feedback and code snippets within the Github issues and pull requests as well as all contributors who contributed additional translations in AMOS, the Moodle translation tool. diff --git a/lang/en/theme_boost_union.php b/lang/en/theme_boost_union.php index 52d8759751a..0edbb9282a2 100644 --- a/lang/en/theme_boost_union.php +++ b/lang/en/theme_boost_union.php @@ -398,9 +398,13 @@ $string['addpreferredlangsetting'] = 'Add preferred language link to language menu'; $string['addpreferredlangsetting_desc'] = 'With this setting, you can add a \'Set preferred language\' setting to the language menu within the user menu. Understandably, this setting is only processed if the setting Display language menu is enabled, and if at least a second language pack is installed and offered for selection.'; $string['setpreferredlanglink'] = 'Set preferred language'; -// ... ... Settings: show a popover menu with starred courses next to the messages and notifications menus. -$string['shownavbarstarredcoursessetting'] = 'Show starred courses in primary navigation bar'; -$string['shownavbarstarredcoursessetting_desc'] = 'Show a popover menu with links to starred courses next to the messages and notifications menus.'; +// ... Section: Navbar heading. +$string['navbarheading'] = 'Navbar'; +// ... ... Setting: Show starred courses popover in the navbar. +$string['shownavbarstarredcoursessetting'] = 'Show starred courses popover in the navbar'; +$string['shownavbarstarredcoursessetting_desc'] = 'With this setting, you can show a popover menu with links to starred courses next to the messages and notifications menus.'; +$string['shownavbarstarredcourses_config'] = 'Set starred courses on the \'My courses\' page'; +$string['shownavbarstarredcourses_label'] = 'Starred courses'; // ... Section: Breadcrumbs. $string['breadcrumbsheading'] = 'Breadcrumbs'; // ... ... Setting: Course category breadcrumb. diff --git a/lib.php b/lib.php index b05b05af99b..65c8967a68a 100644 --- a/lib.php +++ b/lib.php @@ -609,6 +609,17 @@ function theme_boost_union_user_preferences(): array { * @return string */ function theme_boost_union_render_navbar_output() { - require_once(__DIR__ . '/locallib.php'); - return theme_boost_union_get_favourites_popover_menu(); + global $CFG; + + // Require local library. + require_once($CFG->dirroot . '/theme/boost_union/locallib.php'); + + // Initialize the navbar content. + $content = ''; + + // Setting: Show starred courses popover in the navbar. + $content .= theme_boost_union_get_navbar_starredcoursespopover(); + + // Return. + return $content; } diff --git a/locallib.php b/locallib.php index e03539bbf28..f6f8abd446b 100644 --- a/locallib.php +++ b/locallib.php @@ -1869,48 +1869,95 @@ function theme_boost_union_yesno_to_boolstring($var) { } /** - * Function that fetches all favorite courses, and renders them as a popover menu. + * Returns the HTML code for the starred courses popover. + * It fetches all favorite courses and renders them as a popover menu. * - * @return string HTML to display the main header. + * This function is copied and modified from block_starredcourses_external::get_starred_courses() + * + * @return string HTML to display in the navbar. */ -function theme_boost_union_get_favourites_popover_menu() { - global $USER, $DB, $OUTPUT; - // Menu is relevant only for logged in users. - if (isloggedin()) { - $settings = get_config('theme_boost_union'); - if (!isset($settings->shownavbarstarredcourses) || $settings->shownavbarstarredcourses == 'no') { - return ''; - } - // Get all favourite courses. - $ufservice = \core_favourites\service_factory::get_service_for_user_context(\context_user::instance($USER->id)); - $favourites = $ufservice->find_favourites_by_type('core_course', 'courses'); - if (!$favourites) { - return ''; - } - $favouritecourseids = array_map( - function($favourite) { - return $favourite->itemid; - }, $favourites); - $coursefields = 'id, shortname, fullname, visible'; - $courses = $DB->get_records_list('course', 'id', $favouritecourseids, 'visible DESC,sortorder ASC', $coursefields); - - // Sort courses by visibility and name. - usort($courses, function($a, $b) { - if ($a->visible != $b->visible) { - return $a->visible == 0 ? 1 : -1; - } - return strcasecmp(trim($a->fullname), trim($b->fullname)); - }); - $menu = []; - foreach ($courses as $course) { - $menu[] = [ +function theme_boost_union_get_navbar_starredcoursespopover() { + global $USER, $OUTPUT; + + // The popover is relevant only for logged-in users. If the user is not logged in, return directly. + if (!isloggedin()) { + return ''; + } + + // If the popover is disabled, return directly. + $setting = get_config('theme_boost_union', 'shownavbarstarredcourses'); + if (!isset($setting) || $setting != THEME_BOOST_UNION_SETTING_SELECT_YES) { + return ''; + } + + // Get the user context. + $usercontext = context_user::instance($USER->id); + + // Get the user favourites service, scoped to a single user (their favourites only). + $userservice = \core_favourites\service_factory::get_service_for_user_context($usercontext); + + // Get the favourites, by type, for the user. + $favourites = $userservice->find_favourites_by_type('core_course', 'courses'); + + // If there aren't any favourite courses, return directly. + if (!$favourites) { + return ''; + } + + // Pick the course IDs from the course objects. + $favouritecourseids = array_map( + function($favourite) { + return $favourite->itemid; + }, $favourites); + + // Get all courses that the current user is enrolled in, restricted down to favourites. + $filteredcourses = []; + if ($favouritecourseids) { + $courses = course_get_enrolled_courses_for_logged_in_user(0, 0, null, null, + COURSE_DB_QUERY_LIMIT, $favouritecourseids); + list($filteredcourses, $processedcount) = course_filter_courses_by_favourites( + $courses, + $favouritecourseids, + 0 + ); + } + // Grab the course ids. + $filteredcourseids = array_column($filteredcourses, 'id'); + + // Filter out any favourites that are not in the list of enroled courses. + $filteredfavourites = array_filter($favourites, function($favourite) use ($filteredcourseids) { + return in_array($favourite->itemid, $filteredcourseids); + }); + + // Compose the template context. + $coursesfortemplate = []; + foreach ($filteredfavourites as $favourite) { + $course = get_course($favourite->itemid); + $context = \context_course::instance($favourite->itemid); + $canviewhiddencourses = has_capability('moodle/course:viewhiddencourses', $context); + + if ($course->visible || $canviewhiddencourses) { + $coursesfortemplate[] = [ 'url' => new \moodle_url('/course/view.php', ['id' => $course->id]), 'fullname' => $course->fullname, 'visible' => $course->visible == 1, ]; } - $html = $OUTPUT->render_from_template('theme_boost_union/favourites-popover', ['favourites' => $menu]); - return $html; } - return ''; + + // Sort the favourites by name (if there is anything to be sorted). + if (count($coursesfortemplate) > 1) { + usort($coursesfortemplate, function($a, $b) { + if ($a['fullname'] == $b['fullname']) { + return 0; + } + + return strcasecmp(trim($a['fullname']), trim($b['fullname'])); + }); + } + + // Compose the popover menu. + $html = $OUTPUT->render_from_template('theme_boost_union/popover-favourites', ['favourites' => $coursesfortemplate]); + + return $html; } diff --git a/scss/boost_union/post.scss b/scss/boost_union/post.scss index c19b3ec1ca9..aa7b58e6821 100644 --- a/scss/boost_union/post.scss +++ b/scss/boost_union/post.scss @@ -473,42 +473,49 @@ body.hascourseindexcplsol.editing { } /*--------------------------------------- - * Setting: show a popover menu with starred courses next to the messages and notifications menus. + * Setting: Show starred courses popover in the navbar. --------------------------------------*/ -.navbar #nav-favourites-popover-container { +.navbar #nav-popover-favourites-container { .popover-region-container { + /* Make sure that the popover is only as high an necessary*/ height: auto; + /* Limit the width to some reasonable size to avoid that the popover menu is too wide. */ - max-width: 78vw; - border-radius: 0.5rem; - right: -5vw; + min-width: 250px; + max-width: 450px; width: auto; - @include media-breakpoint-down(lg) { - max-width: 73vw; - } - @include media-breakpoint-down(md) { - max-width: 68vw; - } - @include media-breakpoint-down(xs) { - right: -35vw; - top: 60px; - } - /* Hide overflowing course names. */ - .dropdown-item { - overflow-x: hidden; - text-overflow: ellipsis; - } } + .popover-region-header-container { - display: none; + /* Grant the popover header some more space.*/ + height: 35px; + padding-left: 10px; + padding-top: 5px; } + .popover-region-content-container { + /* Make sure that the popover is only as high an necessary. + (Full browser height minus navbar height minus popover header height minus some space at the bottom. */ height: auto; - min-width: 10rem; + max-height: calc(100vh - #{$navbar-height} - 35px - 1em); + + /* Improve the course list items. */ + .dropdown-item { + /* Fix the left padding of the dropdown item. */ + padding-left: 10px; + + /* Hide overflowing course names. */ + overflow-x: hidden; + text-overflow: ellipsis; + + /* Keep dimmed courses white when hovered. */ + &.dimmed:hover { + color: #FFFFFF; + } + } } } - /*--------------------------------------- * Setting: Back to top button. --------------------------------------*/ diff --git a/settings.php b/settings.php index cecfee49e1d..13790a7e5f1 100644 --- a/settings.php +++ b/settings.php @@ -1259,11 +1259,17 @@ $setting = new admin_setting_configselect($name, $title, $description, THEME_BOOST_UNION_SETTING_SELECT_NO, $yesnooption); $tab->add($setting); - // Setting: show a popover menu with starred courses next to the messages and notifications menus. + // Create navbar heading. + $name = 'theme_boost_union/navbarheading'; + $title = get_string('navbarheading', 'theme_boost_union', null, true); + $setting = new admin_setting_heading($name, $title, null); + $tab->add($setting); + + // Setting: Show starred courses popover in the navbar. $name = 'theme_boost_union/shownavbarstarredcourses'; $title = get_string('shownavbarstarredcoursessetting', 'theme_boost_union', null, true); $description = get_string('shownavbarstarredcoursessetting_desc', 'theme_boost_union', null, true); - $setting = new admin_setting_configselect($name, $title, $description, THEME_BOOST_UNION_SETTING_SELECT_YES, $yesnooption); + $setting = new admin_setting_configselect($name, $title, $description, THEME_BOOST_UNION_SETTING_SELECT_NO, $yesnooption); $setting->set_updatedcallback('theme_reset_all_caches'); $tab->add($setting); diff --git a/templates/favourites-popover.mustache b/templates/favourites-popover.mustache deleted file mode 100644 index 929f6a39e61..00000000000 --- a/templates/favourites-popover.mustache +++ /dev/null @@ -1,27 +0,0 @@ -{{< core/popover_region }} - {{$classes}}popover-region-favourites{{/classes}} - {{$attributes}}id="nav-favourites-popover-container" {{/attributes}} - - {{$togglelabel}}{{#str}} favourites {{/str}}{{/togglelabel}} - {{$togglecontent}} - - {{/togglecontent}} - - {{$containerlabel}}{{#str}} favourites {{/str}}{{/containerlabel}} - - - {{$content}} -
- {{#favourites}} - {{{fullname}}} - {{/favourites}} -
- {{/content}} -{{/ core/popover_region }} - -{{#js}} - require(['jquery', 'core/popover_region_controller'], function($, Controller) { - var container = $('#nav-favourites-popover-container'); - var controller = new Controller(container); - }); -{{/js}} \ No newline at end of file diff --git a/templates/popover-favourites.mustache b/templates/popover-favourites.mustache new file mode 100644 index 00000000000..6e13e83223d --- /dev/null +++ b/templates/popover-favourites.mustache @@ -0,0 +1,66 @@ +{{! + This file is part of Moodle - http://moodle.org/ + + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template theme_boost_union/popover-favourites + + Boost Union popover for starred courses template. + + Example context (json): + { + "favourites": [ + { + "url": "/course/view.php?id=42", + "fullname": "My nice little course", + "visible": true + } + ] + } +}} + +{{< core/popover_region }} + {{$classes}}popover-region-favourites{{/classes}} + {{$attributes}}id="nav-popover-favourites-container" {{/attributes}} + + {{$togglelabel}}{{#str}} shownavbarstarredcourses_label, theme_boost_union {{/str}}{{/togglelabel}} + {{$togglecontent}} + {{#pix}} i/star, core, {{#str}} shownavbarstarredcourses_label, theme_boost_union {{/str}} {{/pix}} + {{/togglecontent}} + + {{$containerlabel}}{{#str}} shownavbarstarredcourses_label, theme_boost_union {{/str}}{{/containerlabel}} + + {{$headertext}}{{#str}} shownavbarstarredcourses_label, theme_boost_union {{/str}}{{/headertext}} + {{$headeractions}} + + {{#pix}} i/settings, core {{/pix}} + {{/headeractions}} + + {{$content}} + {{#favourites}} + {{{fullname}}} + {{/favourites}} + {{/content}} +{{/ core/popover_region }} +{{#js}} +require(['jquery', 'core/popover_region_controller'], function($, Controller) { + var container = $('#nav-popover-favourites-container'); + var controller = new Controller(container); + controller.registerEventListeners(); + controller.registerListNavigationEventListeners(); +}); +{{/js}} diff --git a/tests/behat/theme_boost_union_feelsettings_navigation.feature b/tests/behat/theme_boost_union_feelsettings_navigation.feature index a8b7c7403af..e76b63e67de 100644 --- a/tests/behat/theme_boost_union_feelsettings_navigation.feature +++ b/tests/behat/theme_boost_union_feelsettings_navigation.feature @@ -83,53 +83,55 @@ Feature: Configuring the theme_boost_union plugin for the "Navigation" tab on th | no | should not | @javascript - Scenario Outline: Check whether a popover menu with starred courses is displayed in the navbar + Scenario Outline: Setting: Show starred courses popover in the navbar. Given the following config values are set as admin: - | config | value | plugin | - | shownavbarstarredcourses | | theme_boost_union | - When I log in as "admin" - And I navigate to "Development > Purge caches" in site administration - And I press "Purge all caches" - Then I should see "All caches were purged" - Then I log in as "student1" + | config | value | plugin | + | shownavbarstarredcourses | | theme_boost_union | + And the theme cache is purged and the theme is reloaded + When I log in as "student1" And I follow "My courses" - And I click on ".coursemenubtn" "css_element" in the "section.block_myoverview.block div[data-region=course-content] .menu" "css_element" - And I click on "Star this course" "link" in the "section.block_myoverview.block div[data-region=course-content] .menu" "css_element" - When I reload the page + And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 1')]" "xpath_element" + And I click on "Star this course" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 1')]" "xpath_element" + And I reload the page Then "nav.navbar #usernavigation .popover-region-favourites" "css_element" be visible + Examples: | setting | shouldornot | | yes | should | | no | should not | @javascript - Scenario: Check whether the correct courses are displayed in the "Starred courses" popover menu + Scenario: Setting: Show starred courses popover in the navbar (and make sure that I see the right courses there). Given the following config values are set as admin: - | config | value | plugin | - | shownavbarstarredcourses | yes | theme_boost_union | + | config | value | plugin | + | shownavbarstarredcourses | yes | theme_boost_union | And the following "courses" exist: | fullname | shortname | | Course 2 | C2 | | Course 3 | C3 | | Course 4 | C4 | And the following "course enrolments" exist: - | user | course | role | - | student1 | C2 | student | - | student1 | C3 | student | - | student1 | C4 | student | - When I log in as "admin" - And I navigate to "Development > Purge caches" in site administration - And I press "Purge all caches" - Then I should see "All caches were purged" - Then I log in as "student1" + | user | course | role | + | student1 | C2 | student | + | student1 | C3 | student | + | student1 | C4 | student | + And the theme cache is purged and the theme is reloaded + When I log in as "student1" And I follow "My courses" - And I click on ".coursemenubtn" "css_element" in the "section.block_myoverview.block div[data-region=course-content]:nth-child(1) .menu" "css_element" - And I click on "Star this course" "link" in the "section.block_myoverview.block div[data-region=course-content]:nth-child(1) .menu" "css_element" - And I click on ".coursemenubtn" "css_element" in the "section.block_myoverview.block div[data-region=course-content]:nth-child(2) .menu" "css_element" - And I click on "Star this course" "link" in the "section.block_myoverview.block div[data-region=course-content]:nth-child(2) .menu" "css_element" - When I reload the page - Then I click on "nav.navbar #usernavigation .popover-region-favourites .nav-link" "css_element" - And I should see "Course 1" in the ".popover-region-favourites .popover-region-content-container" "css_element" + And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element" + And I click on "Star this course" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 2')]" "xpath_element" + And I click on ".coursemenubtn" "css_element" in the "//div[@class='card dashboard-card' and contains(.,'Course 3')]" "xpath_element" + And I click on "Star this course" "link" in the "//div[@class='card dashboard-card' and contains(.,'Course 3')]" "xpath_element" + And I log out + And I log in as "admin" + And I am on "Course 3" course homepage + And I navigate to course participants + And I click on "Unenrol" "icon" in the "student1" "table_row" + And I click on "Unenrol" "button" in the "Unenrol" "dialogue" + And I log out + And I log in as "student1" + And I click on "nav.navbar #usernavigation .popover-region-favourites .nav-link" "css_element" + Then I should not see "Course 1" in the ".popover-region-favourites .popover-region-content-container" "css_element" And I should see "Course 2" in the ".popover-region-favourites .popover-region-content-container" "css_element" And I should not see "Course 3" in the ".popover-region-favourites .popover-region-content-container" "css_element" And I should not see "Course 4" in the ".popover-region-favourites .popover-region-content-container" "css_element"