diff --git a/admin/roles/assign.php b/admin/roles/assign.php index 10be913c2a2..7935781ee8a 100644 --- a/admin/roles/assign.php +++ b/admin/roles/assign.php @@ -183,7 +183,10 @@ switch ($context->contextlevel) { break; } -$PAGE->set_navigation_overflow_state(false); +// In a course category context we leverage overflow for the tertiary navigation +if ($context->contextlevel != CONTEXT_COURSECAT) { + $PAGE->set_navigation_overflow_state(false); +} // Within a course context we need to explicitly set active tab as there isn't a reference in the nav tree. if ($context->contextlevel == CONTEXT_COURSE) { diff --git a/admin/roles/check.php b/admin/roles/check.php index c47b8d411ac..15abd5dc455 100644 --- a/admin/roles/check.php +++ b/admin/roles/check.php @@ -126,7 +126,11 @@ if (!is_null($reportuser)) { $rolenames = role_get_names($context); } -$PAGE->set_navigation_overflow_state(false); +// In a course category context we leverage overflow for the tertiary navigation +if ($context->contextlevel != CONTEXT_COURSECAT) { + $PAGE->set_navigation_overflow_state(false); +} + echo $OUTPUT->header(); if ($context->contextlevel == CONTEXT_COURSE || $context->contextlevel == CONTEXT_MODULE) { echo $OUTPUT->render_participants_tertiary_nav($course); diff --git a/admin/roles/permissions.php b/admin/roles/permissions.php index ed9cc9bbec6..301171b545e 100644 --- a/admin/roles/permissions.php +++ b/admin/roles/permissions.php @@ -204,7 +204,11 @@ if ($capability && ($allowoverrides || ($allowsafeoverrides && is_safe_capabilit } } -$PAGE->set_navigation_overflow_state(false); +// When we are within a category context we leave it to the overflow state to display additional nav nodes. +if ($context->contextlevel != CONTEXT_COURSECAT) { + $PAGE->set_navigation_overflow_state(false); +} + echo $OUTPUT->header(); if ($context->contextlevel == CONTEXT_COURSE || $context->contextlevel == CONTEXT_MODULE) { echo $OUTPUT->render_participants_tertiary_nav($course); diff --git a/course/classes/category.php b/course/classes/category.php index 1733d520cf3..0e83c627000 100644 --- a/course/classes/category.php +++ b/course/classes/category.php @@ -2777,6 +2777,17 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr return has_capability('moodle/category:manage', $this->get_context()); } + /** + * Checks whether the category has access to content bank + * + * @return bool + */ + public function has_contentbank() { + $cb = new \core_contentbank\contentbank(); + return ($cb->is_context_allowed($this->get_context()) && + has_capability('moodle/contentbank:access', $this->get_context())); + } + /** * Returns true if the user has the manage capability on the parent category. * @return bool diff --git a/course/classes/management/helper.php b/course/classes/management/helper.php index 80ba0e4f2c9..d428acc12b9 100644 --- a/course/classes/management/helper.php +++ b/course/classes/management/helper.php @@ -173,6 +173,14 @@ class helper { $manageurl = new \moodle_url('/course/management.php', array('categoryid' => $category->id)); $baseurl = new \moodle_url($manageurl, array('sesskey' => \sesskey())); $actions = array(); + + // View link. + $actions['view'] = [ + 'url' => new \moodle_url('/course/index.php', ['categoryid' => $category->id]), + 'icon' => null, + 'string' => get_string('view') + ]; + // Edit. if ($category->can_edit()) { $actions['edit'] = array( @@ -252,36 +260,15 @@ class helper { ); } - // Assign roles. - if ($category->can_review_roles()) { - $actions['assignroles'] = array( - 'url' => new \moodle_url('/admin/roles/assign.php', array('contextid' => $category->get_context()->id, - 'returnurl' => $manageurl->out_as_local_url(false))), - 'icon' => new \pix_icon('t/assignroles', new \lang_string('assignroles', 'role')), - 'string' => new \lang_string('assignroles', 'role') - ); - } - // Permissions. if ($category->can_review_permissions()) { $actions['permissions'] = array( - 'url' => new \moodle_url('/admin/roles/permissions.php', array('contextid' => $category->get_context()->id, - 'returnurl' => $manageurl->out_as_local_url(false))), + 'url' => new \moodle_url('/admin/roles/permissions.php', ['contextid' => $category->get_context()->id]), 'icon' => new \pix_icon('i/permissions', new \lang_string('permissions', 'role')), 'string' => new \lang_string('permissions', 'role') ); } - // Check permissions. - if ($category->can_review_permissions()) { - $actions['checkroles'] = array( - 'url' => new \moodle_url('/admin/roles/check.php', array('contextid' => $category->get_context()->id, - 'returnurl' => $manageurl->out_as_local_url(false))), - 'icon' => new \pix_icon('i/checkpermissions', new \lang_string('checkpermissions', 'role')), - 'string' => new \lang_string('checkpermissions', 'role') - ); - } - // Context locking. if (!empty($CFG->contextlocking) && has_capability('moodle/site:managecontextlocks', $category->get_context())) { $parentcontext = $category->get_context()->get_parent_context(); @@ -351,6 +338,16 @@ class helper { } } + // Content bank. + if ($category->has_contentbank()) { + $url = new \moodle_url('/contentbank/index.php', ['contextid' => $category->get_context()->id]); + $actions['contentbank'] = [ + 'url' => $url, + 'icon' => new \pix_icon('i/contentbank', ''), + 'string' => get_string('contentbank') + ]; + } + return $actions; } diff --git a/course/classes/management_renderer.php b/course/classes/management_renderer.php index 08373bc9220..838e7fcdf2f 100644 --- a/course/classes/management_renderer.php +++ b/course/classes/management_renderer.php @@ -64,12 +64,16 @@ class core_course_management_renderer extends plugin_renderer_base { /** * Displays a heading for the management pages. * + * @deprecated since Moodle 4.0. This is now handled/replaced with the tertiary navigation + * @todo Final deprecation MDL-73975 * @param string $heading The heading to display * @param string|null $viewmode The current view mode if there are options. * @param int|null $categoryid The currently selected category if there is one. * @return string */ public function management_heading($heading, $viewmode = null, $categoryid = null) { + debugging('management_heading() is deprecated. Use the class manage_categories_action_bar instead.', DEBUG_DEVELOPER); + $html = html_writer::start_div('coursecat-management-header clearfix'); if (!empty($heading)) { $html .= $this->heading($heading); @@ -1294,11 +1298,13 @@ class core_course_management_renderer extends plugin_renderer_base { /** * Renders html to display a course search form * + * @deprecated since Moodle 4.0. This is now handled within manage_categories_action_bar + * @todo Final deprecation MDL-73975 * @param string $value default value to populate the search field * @return string */ public function course_search_form($value = '') { - + debugging('course_search_form() is deprecated. Use the class manage_categories_action_bar instead.', DEBUG_DEVELOPER); $data = [ 'action' => new moodle_url('/course/management.php'), 'btnclass' => 'btn-primary', @@ -1337,4 +1343,13 @@ class core_course_management_renderer extends plugin_renderer_base { return $html; } + /** + * Render the tertiary nav for the manage categories page. + * + * @param \core_course\output\manage_categories_action_bar $actionbar + * @return string The renderered template + */ + public function render_action_bar(\core_course\output\manage_categories_action_bar $actionbar): string { + return $this->render_from_template('core_course/manage_category_actionbar', $actionbar->export_for_template($this)); + } } diff --git a/course/classes/output/category_action_bar.php b/course/classes/output/category_action_bar.php new file mode 100644 index 00000000000..45cb61dd8e0 --- /dev/null +++ b/course/classes/output/category_action_bar.php @@ -0,0 +1,171 @@ +. + +namespace core_course\output; + +use context_coursecat; +use core_course_category; +use course_request; +use moodle_page; +use moodle_url; + +/** + * Class responsible for generating the action bar (tertiary nav) elements in an individual category page + * + * @package core + * @copyright 2021 Peter Dias + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class category_action_bar extends manage_categories_action_bar { + /** + * @var object The current category we are referring to. + */ + protected $category; + /** + * Constructor category_action_bar + * + * @param moodle_page $page The page object + * @param object $category + * @param object|null $course The course that we are generating the nav for + * @param string|null $searchvalue + */ + public function __construct(moodle_page $page, object $category, ?object $course = null, ?string $searchvalue = null) { + $this->category = $category; + parent::__construct($page, 'courses', $course, $searchvalue); + } + + /** + * Gets the url_select to be displayed in the participants page if available. + * + * @param \renderer_base $output + * @return object|null The content required to render the url_select + */ + protected function get_category_select(\renderer_base $output): ?object { + if (!$this->searchvalue && !core_course_category::is_simple_site()) { + $categories = core_course_category::make_categories_list(); + if (count($categories) > 1) { + foreach ($categories as $id => $cat) { + $url = new moodle_url($this->page->url, ['categoryid' => $id]); + $options[$url->out()] = $cat; + } + $currenturl = new moodle_url($this->page->url, ['categoryid' => $this->category->id]); + $select = new \url_select($options, $currenturl, null); + $select->set_label(get_string('categories'), ['class' => 'sr-only']); + $select->class .= ' text-truncate w-100'; + return $select->export_for_template($output); + } + } + + return null; + } + + /** + * Gets the additional options to be displayed within a 'More' dropdown in the tertiary navigation. + * The predefined order defined by UX is: + * - Add a course + * - Add a sub cat + * - Manage course + * - Request a course + * - Course pending approval + * + * @return array + */ + protected function get_additional_category_options(): array { + global $CFG, $DB; + if ($this->category->is_uservisible()) { + $context = get_category_or_system_context($this->category->id); + if (has_capability('moodle/course:create', $context)) { + $params = [ + 'category' => $this->category->id ?: $CFG->defaultrequestcategory, + 'returnto' => $this->category->id ? 'category' : 'topcat' + ]; + + $options[0] = [ + 'url' => new moodle_url('/course/edit.php', $params), + 'string' => get_string('addnewcourse') + ]; + } + + if (!empty($CFG->enablecourserequests)) { + // Display an option to request a new course. + if (course_request::can_request($context)) { + $params = []; + if ($context instanceof context_coursecat) { + $params['category'] = $context->instanceid; + } + + $options[3] = [ + 'url' => new moodle_url('/course/request.php', $params), + 'string' => get_string('requestcourse') + ]; + } + + // Display the manage pending requests option. + if (has_capability('moodle/site:approvecourse', $context)) { + $disabled = !$DB->record_exists('course_request', array()); + if (!$disabled) { + $options[4] = [ + 'url' => new moodle_url('/course/pending.php'), + 'string' => get_string('coursespending') + ]; + } + } + } + } + + if ($this->category->can_create_course() || $this->category->has_manage_capability()) { + // Add 'Manage' button if user has permissions to edit this category. + $options[2] = [ + 'url' => new moodle_url('/course/management.php', ['categoryid' => $this->category->id]), + 'string' => get_string('managecourses') + ]; + + if ($this->category->has_manage_capability()) { + $addsubcaturl = new moodle_url('/course/editcategory.php', array('parent' => $this->category->id)); + $options[1] = [ + 'url' => $addsubcaturl, + 'string' => get_string('addsubcategory') + ]; + } + } + + // We have stored the options in a predefined order. Sort it based on index and return. + if (isset($options)) { + sort($options); + return ['options' => $options]; + } + + return []; + } + + /** + * Export the content to be displayed on the category page. + * + * @param \renderer_base $output + * @return array Consists of the following: + * - categoryselect A list of available categories to be fed into a urlselect + * - search The course search form + * - additionaloptions Additional actions that can be performed in a category + */ + public function export_for_template(\renderer_base $output): array { + return [ + 'categoryselect' => $this->get_category_select($output), + 'search' => $this->get_search_form(), + 'additionaloptions' => $this->get_additional_category_options() + ]; + } + +} diff --git a/course/classes/output/manage_categories_action_bar.php b/course/classes/output/manage_categories_action_bar.php new file mode 100644 index 00000000000..70fee710d66 --- /dev/null +++ b/course/classes/output/manage_categories_action_bar.php @@ -0,0 +1,156 @@ +. + +namespace core_course\output; + +use moodle_page; +use moodle_url; + +/** + * Class responsible for generating the action bar (tertiary nav) elements in the category management page + * + * @package core + * @copyright 2021 Peter Dias + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class manage_categories_action_bar implements \renderable { + /** @var object $course The course we are dealing with. */ + protected $course; + /** @var moodle_page $page The current page. */ + protected $page; + /** @var string|null $viewmode The viewmode of the underlying page - Course and categories, categories or courses */ + protected $viewmode; + /** @var string|null $heading The heading to display */ + protected $heading; + /** @var string|null $searchvalue The search value if any */ + protected $searchvalue; + + /** + * Constructor for the manage_categories_action_bar + * + * @param moodle_page $page The page object + * @param string $viewmode The type of page we are viewing. + * @param object|null $course The course that we are generating the nav for + * @param string|null $searchvalue The search value if applicable + */ + public function __construct(moodle_page $page, string $viewmode, ?object $course, ?string $searchvalue) { + $this->course = $course; + $this->page = $page; + $this->viewmode = $viewmode; + $this->searchvalue = $searchvalue; + if ($searchvalue) { + $this->heading = get_string('searchresults'); + } + } + + /** + * Gets the url_select to be displayed in the participants page if available. + * + * @param \renderer_base $output + * @return object|null The content required to render the url_select + */ + protected function get_dropdown(\renderer_base $output): ?object { + // If a search is being performed then no need to display the dropdown. + if ($this->searchvalue) { + return null; + } + + $modes = \core_course\management\helper::get_management_viewmodes(); + $activeurl = null; + $content = []; + foreach ($modes as $mode => $description) { + $url = new moodle_url($this->page->url, ['view' => $mode]); + $content[$url->out()] = $description; + if ($this->viewmode == $mode) { + $activeurl = $url->out(); + $this->heading = get_string("manage$mode"); + } + } + + // Default to the first option if asking for default. This is combined. + if (!$activeurl && $this->viewmode === 'default') { + $activeurl = array_key_first($content); + $this->heading = get_string("managecombined"); + } + + if ($content) { + $urlselect = new \url_select($content, $activeurl, null); + $urlselect->set_label(get_string('viewing'), ['class' => 'sr-only']); + return $urlselect->export_for_template($output); + } + + return null; + } + + /** + * Gets the url_select to be displayed in the participants page if available. + * + * @param \renderer_base $output + * @return object|null The content required to render the url_select + */ + protected function get_category_select(\renderer_base $output): ?object { + if (!$this->searchvalue && $this->viewmode === 'courses') { + $categories = \core_course_category::make_categories_list(array('moodle/category:manage', 'moodle/course:create')); + $currentcat = $this->page->url->param('categoryid'); + foreach ($categories as $id => $cat) { + $url = new moodle_url($this->page->url, ['categoryid' => $id]); + if ($id == $currentcat) { + $currenturl = $url->out(); + } + $options[$url->out()] = $cat; + } + + $select = new \url_select($options, $currenturl, null); + $select->set_label(get_string('category'), ['class' => 'sr-only']); + $select->class .= ' text-truncate w-100'; + return $select->export_for_template($output); + } + + return null; + } + + /** + * Get the search box + * + * @return array + */ + protected function get_search_form(): array { + return [ + 'action' => new moodle_url('/course/management.php'), + 'btnclass' => 'btn-primary', + 'inputname' => 'search', + 'searchstring' => get_string('searchcourses'), + 'query' => $this->searchvalue + ]; + } + + /** + * Export the content to be displayed on the participants page. + * + * @param \renderer_base $output + * @return array Consists of the following: + * - urlselect A stdclass representing the standard navigation options to be fed into a urlselect + * - renderedcontent Rendered content to be displayed in line with the tertiary nav + */ + public function export_for_template(\renderer_base $output): array { + return [ + 'urlselect' => $this->get_dropdown($output), + 'categoryselect' => $this->get_category_select($output), + 'search' => $this->get_search_form(), + 'heading' => $this->heading, + ]; + } +} diff --git a/course/edit.php b/course/edit.php index 34710fc9003..e1050f626aa 100644 --- a/course/edit.php +++ b/course/edit.php @@ -112,6 +112,11 @@ if ($id) { $PAGE->set_context($catcontext); } +// We are adding a new course and have a category context. +if (isset($catcontext)) { + $PAGE->set_secondary_active_tab('categorymain'); +} + // Prepare course and the editor. $editoroptions = array('maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>false, 'noclean'=>true); $overviewfilesoptions = course_overviewfiles_options($course); diff --git a/course/lib.php b/course/lib.php index 374da482b4d..98ad0e61d5b 100644 --- a/course/lib.php +++ b/course/lib.php @@ -650,9 +650,13 @@ function get_category_or_system_context($categoryid) { * Print the buttons relating to course requests. * * @param context $context current page context. + * @deprecated since Moodle 4.0 + * @todo Final deprecation MDL-73976 */ function print_course_request_buttons($context) { global $CFG, $DB, $OUTPUT; + debugging("print_course_request_buttons() is deprecated. " . + "This is replaced with the category_action_bar tertiary navigation.", DEBUG_DEVELOPER); if (empty($CFG->enablecourserequests)) { return; } diff --git a/course/management.php b/course/management.php index 7bccc1cf7ec..9601ffd1771 100644 --- a/course/management.php +++ b/course/management.php @@ -108,7 +108,7 @@ $PAGE->set_pagelayout('admin'); $PAGE->set_title($strmanagement); $PAGE->set_heading($pageheading); $PAGE->requires->js_call_amd('core_course/copy_modal', 'init', array($context->id)); -$PAGE->set_secondary_active_tab('managecategory'); +$PAGE->set_secondary_active_tab('categorymain'); // This is a system level page that operates on other contexts. require_login(); @@ -473,12 +473,8 @@ $displaycourselisting = ($viewmode === 'default' || $viewmode === 'combined' || $displaycoursedetail = (isset($courseid)); echo $renderer->header(); - -if (!$issearching) { - echo $renderer->management_heading($strmanagement, $viewmode, $categoryid); -} else { - echo $renderer->management_heading(new lang_string('searchresults')); -} +$actionbar = new \core_course\output\manage_categories_action_bar($PAGE, $viewmode, $course, $search); +echo $renderer->render_action_bar($actionbar); if (count($notificationspass) > 0) { echo $renderer->notification(join('
', $notificationspass), 'notifysuccess'); @@ -489,8 +485,6 @@ if (count($notificationsfail) > 0) { // Start the management form. -echo $renderer->course_search_form($search); - echo $renderer->management_form_start(); echo $renderer->accessible_skipto_links($displaycategorylisting, $displaycourselisting, $displaycoursedetail); diff --git a/course/renderer.php b/course/renderer.php index 2c97561c3d9..e0db4c9534c 100644 --- a/course/renderer.php +++ b/course/renderer.php @@ -1667,14 +1667,8 @@ class core_course_renderer extends plugin_renderer_base { $coursecat = core_course_category::get(is_object($category) ? $category->id : $category); } $site = get_site(); - $output = ''; - - if ($coursecat->can_create_course() || $coursecat->has_manage_capability()) { - // Add 'Manage' button if user has permissions to edit this category. - $managebutton = $this->single_button(new moodle_url('/course/management.php', - array('categoryid' => $coursecat->id)), get_string('managecourses'), 'get'); - $this->page->set_button($managebutton); - } + $actionbar = new \core_course\output\category_action_bar($this->page, $coursecat); + $output = $this->render_from_template('core_course/category_actionbar', $actionbar->export_for_template($this)); if (core_course_category::is_simple_site()) { // There is only one category in the system, do not display link to it. @@ -1686,17 +1680,6 @@ class core_course_renderer extends plugin_renderer_base { } else { $strfulllistofcourses = get_string('fulllistofcourses'); $this->page->set_title("$site->shortname: $strfulllistofcourses"); - - // Print the category selector - $categorieslist = core_course_category::make_categories_list(); - if (count($categorieslist) > 1) { - $output .= html_writer::start_tag('div', array('class' => 'categorypicker')); - $select = new single_select(new moodle_url('/course/index.php'), 'categoryid', - core_course_category::make_categories_list(), $coursecat->id, null, 'switchcategory'); - $select->set_label(get_string('categories').':', ['class' => 'sr-only']); - $output .= $this->render($select); - $output .= html_writer::end_tag('div'); // .categorypicker - } } // Print current category description @@ -1741,33 +1724,10 @@ class core_course_renderer extends plugin_renderer_base { $catdisplayoptions['viewmoreurl'] = new moodle_url($baseurl, array('browse' => 'categories', 'page' => 1)); } $chelper->set_courses_display_options($coursedisplayoptions)->set_categories_display_options($catdisplayoptions); - // Add course search form. - $output .= $this->course_search_form(); // Display course category tree. $output .= $this->coursecat_tree($chelper, $coursecat); - // Add action buttons - $output .= $this->container_start('buttons mt-3'); - if ($coursecat->is_uservisible()) { - $context = get_category_or_system_context($coursecat->id); - if (has_capability('moodle/course:create', $context)) { - // Print link to create a new course, for the 1st available category. - if ($coursecat->id) { - $url = new moodle_url('/course/edit.php', array('category' => $coursecat->id, 'returnto' => 'category')); - } else { - $url = new moodle_url('/course/edit.php', - array('category' => $CFG->defaultrequestcategory, 'returnto' => 'topcat')); - } - $output .= $this->single_button($url, get_string('addnewcourse'), 'get'); - } - ob_start(); - print_course_request_buttons($context); - $output .= ob_get_contents(); - ob_end_clean(); - } - $output .= $this->container_end(); - return $output; } diff --git a/course/templates/category_actionbar.mustache b/course/templates/category_actionbar.mustache new file mode 100644 index 00000000000..f232dae39d3 --- /dev/null +++ b/course/templates/category_actionbar.mustache @@ -0,0 +1,117 @@ +{{! + 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 core_course/category_actionbar + + Displays the tertiary nav for the category page + + Context variables required for this template: + * urlselect Object - The content to be rendered within the url_select + * renderedcontent String - Any additional content/actions to be displayed inline with the select box + + Example context (json): + { + "categoryselect" : { + "formid": "url_select_f61834256d7754158", + "classes": "urlselect", + "label": "", + "disabled": false, + "title":null, + "id":"url_select61834256d7754159", + "sesskey":"LWILINyZMH", + "action":"http:\/\/localhost\/stable_master\/course\/jumpto.php", + "showbutton":null, + "options": [ + { + "name":"Category 1", + "value":"\/course\/index.php\/?categoryid=279" + }, + { + "name":"Category 2", + "value":"\/course\/index.php\/?categoryid=280" + }, + { + "name":"Category 3", + "value":"\/course\/index.php\/?categoryid=281" + } + ], + "labelattributes":[], + "helpicon":false, + "attributes":[] + }, + "search": { + "action": "https://moodle.local/admin/search.php", + "extraclasses": "my-2", + "inputname": "search", + "inform": false, + "searchstring": "Search settings", + "value": "policy", + "btnclass": "primary", + "query": "themedesigner", + "hiddenfields": [ + { + "name": "context", + "value": "11" + } + ] + }, + "additionaloptions": { + "options": [ + { + "url": "\/course\/index.php\/?categoryid=281", + "string": "Action 1" + "attributes": [ + { + "name": "data-id", + "value": "1" + }, + { + "name": "aria-role", + "value": "test" + } + ] + } + ] + } + } +}} +
+
+ {{#categoryselect}} + + {{/categoryselect}} + {{#search}} + + {{/search}} + {{#additionaloptions}} +
+ +
+ {{/additionaloptions}} +
+
diff --git a/course/templates/manage_category_actionbar.mustache b/course/templates/manage_category_actionbar.mustache new file mode 100644 index 00000000000..15bb73a5343 --- /dev/null +++ b/course/templates/manage_category_actionbar.mustache @@ -0,0 +1,119 @@ +{{! + 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 core_course/manage_category_actionbar + + Displays the tertiary nav for the category page + + Context variables required for this template: + * urlselect Object - The content to be rendered within the url_select + * renderedcontent String - Any additional content/actions to be displayed inline with the select box + + Example context (json): + { + "urlselect" : { + "formid": "url_select_f61834256d7754158", + "classes": "urlselect", + "label": "", + "disabled": false, + "title":null, + "id":"url_select61834256d7754159", + "sesskey":"LWILINyZMH", + "action":"http:\/\/localhost\/stable_master\/course\/jumpto.php", + "showbutton":null, + "options": [ + { + "name":"Course and categories", + "value":"\/course\/index.php\/?categoryid=279" + }, + { + "name":"Courses", + "value":"\/course\/index.php\/?categoryid=280" + }, + { + "name":"Categories", + "value":"\/course\/index.php\/?categoryid=281" + } + ], + "labelattributes":[], + "helpicon":false, + "attributes":[] + }, + "categoryselect" : { + "formid": "url_select_f61834256d7754158", + "classes": "urlselect", + "label": "", + "disabled": false, + "title":null, + "id":"url_select61834256d7754159", + "sesskey":"LWILINyZMH", + "action":"http:\/\/localhost\/stable_master\/course\/jumpto.php", + "showbutton":null, + "options": [ + { + "name":"Category 1", + "value":"\/course\/index.php\/?categoryid=279" + }, + { + "name":"Category 2", + "value":"\/course\/index.php\/?categoryid=280" + }, + { + "name":"Category 3", + "value":"\/course\/index.php\/?categoryid=281" + } + ], + "labelattributes":[], + "helpicon":false, + "attributes":[] + }, + "search": { + "action": "https://moodle.local/admin/search.php", + "extraclasses": "my-2", + "inputname": "search", + "inform": false, + "searchstring": "Search settings", + "value": "policy", + "btnclass": "primary", + "query": "themedesigner", + "hiddenfields": [ + { + "name": "context", + "value": "11" + } + ] + }, + "heading": "Heading of page" + } +}} +
+
+ {{#urlselect}} + + {{/urlselect}} + {{#categoryselect}} + + {{/categoryselect}} + {{#search}} + + {{/search}} +
+
+

{{heading}}

diff --git a/course/tests/management_helper_test.php b/course/tests/management_helper_test.php index ea3c2c25206..b9680138a40 100644 --- a/course/tests/management_helper_test.php +++ b/course/tests/management_helper_test.php @@ -1118,7 +1118,6 @@ class core_course_management_helper_test extends advanced_testcase { $this->assertArrayHasKey('moveup', $actions); $this->assertArrayHasKey('movedown', $actions); $this->assertArrayHasKey('delete', $actions); - $this->assertArrayHasKey('assignroles', $actions); $this->assertArrayHasKey('permissions', $actions); $this->assertArrayHasKey('cohorts', $actions); $this->assertArrayHasKey('filters', $actions); diff --git a/lang/en/moodle.php b/lang/en/moodle.php index fc09577e834..417f2296a8b 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1225,6 +1225,8 @@ $string['makeavailable'] = 'Make available'; $string['makeeditable'] = 'If you make \'{$a}\' editable by the web server process (eg apache) then you could edit this file directly from this page'; $string['makethismyhome'] = 'Make this my home page'; $string['makeunavailable'] = 'Make unavailable'; +$string['managecombined'] = 'Manage course categories and courses'; +$string['managecategories'] = 'Manage course categories'; $string['manageblocks'] = 'Blocks'; $string['managecategorythis'] = 'Manage this category'; $string['managecontentbanktypes'] = 'Manage content types'; @@ -1374,6 +1376,7 @@ $string['movefull'] = 'Move {$a} to this location'; $string['movehere'] = 'Move to here'; $string['moveleft'] = 'Move left'; $string['moremenu'] = 'More'; +$string['moreactions'] = 'More actions'; $string['moveright'] = 'Move right'; $string['movesection'] = 'Move section {$a}'; $string['moveselectedcategoriesto'] = 'Move selected categories to'; diff --git a/lib/classes/navigation/views/secondary.php b/lib/classes/navigation/views/secondary.php index 0d07339c100..16491831e1d 100644 --- a/lib/classes/navigation/views/secondary.php +++ b/lib/classes/navigation/views/secondary.php @@ -586,7 +586,9 @@ class secondary extends view { // that are redirected to, be in the course context or module context depending on which callback was used. // Third part plugins were checked to see if any existing plugins had settings in a system context and none were found. // The request of third party developers is to keep their settings within the specified context. - if ($this->page->context->contextlevel != CONTEXT_COURSE && $this->page->context->contextlevel != CONTEXT_MODULE) { + if ($this->page->context->contextlevel != CONTEXT_COURSE + && $this->page->context->contextlevel != CONTEXT_MODULE + && $this->page->context->contextlevel != CONTEXT_COURSECAT) { return null; } @@ -657,8 +659,12 @@ class secondary extends view { $settingsnav = $this->page->settingsnav; $mainnode = $settingsnav->find('categorysettings', self::TYPE_CONTAINER); $nodes = $this->get_default_category_mapping(); + $siteadmin = $settingsnav->find('root', self::TYPE_SITE_ADMIN); - if ($mainnode) { + // If an admin is viewing a category context then load the site admin menu. + if ($siteadmin) { + $this->load_admin_navigation(); + } else if ($mainnode) { $url = new \moodle_url('/course/index.php', ['categoryid' => $this->context->instanceid]); $this->add($this->context->get_context_name(), $url, self::TYPE_CONTAINER, null, 'categorymain'); diff --git a/lib/navigationlib.php b/lib/navigationlib.php index 531ed718b20..1fe6a79c2f0 100644 --- a/lib/navigationlib.php +++ b/lib/navigationlib.php @@ -5426,7 +5426,8 @@ class settings_navigation extends navigation_node { $categorynode->add(get_string('editcategorythis'), $editurl, self::TYPE_SETTING, null, 'edit', new pix_icon('i/edit', '')); $addsubcaturl = new moodle_url('/course/editcategory.php', array('parent' => $catcontext->instanceid)); - $categorynode->add(get_string('addsubcategory'), $addsubcaturl, self::TYPE_SETTING, null, 'addsubcat', new pix_icon('i/withsubcat', '')); + $categorynode->add(get_string('addsubcategory'), $addsubcaturl, self::TYPE_SETTING, null, + 'addsubcat', new pix_icon('i/withsubcat', ''))->set_show_in_secondary_navigation(false); } // Assign local roles diff --git a/course/tests/behat/category_role_assignment.feature b/theme/boost/tests/behat/category_role_assignment.feature similarity index 100% rename from course/tests/behat/category_role_assignment.feature rename to theme/boost/tests/behat/category_role_assignment.feature diff --git a/theme/classic/tests/behat/category_role_assignment.feature b/theme/classic/tests/behat/category_role_assignment.feature new file mode 100644 index 00000000000..865050c30ec --- /dev/null +++ b/theme/classic/tests/behat/category_role_assignment.feature @@ -0,0 +1,44 @@ +@core @core_course +Feature: Role assignments can be made at the category level + In order to grant a user different capabilities + As a user + I can assign roles in categories + + Background: + Given the following "users" exist: + | username | firstname | lastname | + | manager | Manager | Manager | + And the following "categories" exist: + | name | category | idnumber | + | Cat 1 | 0 | CAT1 | + And the following "role assigns" exist: + | user | role | contextlevel | reference | + | manager | manager | Category | CAT1 | + And I log in as "admin" + + @javascript + Scenario: A user with a category role can assign roles + Given I define the allowed role assignments for the "Manager" role as: + | Teacher | Assignable | + And I log out + And I log in as "manager" + And I am on course index + When I follow "Cat 1" + And I navigate to "Permissions" in current page administration + Then I should see "Assign roles" in the "jump" "select" + + @javascript + Scenario: A user with a category role cannot assign roles if there are no roles to assign + Given I define the allowed role assignments for the "Manager" role as: + | Manager | Not assignable | + | Course creator | Not assignable | + | Teacher | Not assignable | + | Non-editing teacher | Not assignable | + | Student | Not assignable | + And I change window size to "large" + And I log out + And I log in as "manager" + And I am on course index + When I follow "Cat 1" + And I navigate to "Permissions" in current page administration + Then I should not see "Assign roles" in the "jump" "select"