From d1a0e4a95cddf1ed039e8df3e7b9a92cfcd2a65c Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Wed, 27 Oct 2021 10:40:12 +0800 Subject: [PATCH] MDL-72873 core_grades: Add general tertiary navigation --- grade/classes/output/action_bar.php | 49 +++++ grade/classes/output/general_action_bar.php | 191 ++++++++++++++++++++ grade/lib.php | 114 ++++++------ grade/renderer.php | 40 ++++ grade/report/history/index.php | 6 +- grade/report/overview/index.php | 13 +- grade/report/singleview/index.php | 10 +- grade/templates/general_action_bar.mustache | 55 ++++++ lang/en/grades.php | 1 + 9 files changed, 413 insertions(+), 66 deletions(-) create mode 100644 grade/classes/output/action_bar.php create mode 100644 grade/classes/output/general_action_bar.php create mode 100644 grade/renderer.php create mode 100644 grade/templates/general_action_bar.mustache diff --git a/grade/classes/output/action_bar.php b/grade/classes/output/action_bar.php new file mode 100644 index 0000000000000..a2ca6f4da747d --- /dev/null +++ b/grade/classes/output/action_bar.php @@ -0,0 +1,49 @@ +. + +namespace core_grades\output; + +use templatable; +use renderable; + +/** + * The base class for the action bar in the gradebook pages. + * + * @package core_grades + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class action_bar implements templatable, renderable { + + /** @var \context $context The context object. */ + protected $context; + + /** + * The class constructor. + * + * @param \context $context The context object. + */ + public function __construct(\context $context) { + $this->context = $context; + } + + /** + * Returns the template for the actions bar. + * + * @return string + */ + abstract public function get_template(): string; +} diff --git a/grade/classes/output/general_action_bar.php b/grade/classes/output/general_action_bar.php new file mode 100644 index 0000000000000..3718d5cfb6ac8 --- /dev/null +++ b/grade/classes/output/general_action_bar.php @@ -0,0 +1,191 @@ +. + +namespace core_grades\output; + +use moodle_url; + +/** + * Renderable class for the general action bar in the gradebook pages. + * + * This class is responsible for rendering the general navigation select menu in the gradebook pages. + * + * @package core_grades + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class general_action_bar extends action_bar { + + /** @var moodle_url $activeurl The URL that should be set as active in the URL selector element. */ + protected $activeurl; + + /** + * The type of the current gradebook page (report, settings, import, export, scales, outcomes, letters). + * + * @var string $activetype + */ + protected $activetype; + + /** @var string $activeplugin The plugin of the current gradebook page (grader, fullview, ...). */ + protected $activeplugin; + + /** + * The class constructor. + * + * @param \context $context The context object. + * @param moodle_url $activeurl The URL that should be set as active in the URL selector element. + * @param string $activetype The type of the current gradebook page (report, settings, import, export, scales, + * outcomes, letters). + * @param string $activeplugin The plugin of the current gradebook page (grader, fullview, ...). + */ + public function __construct(\context $context, moodle_url $activeurl, string $activetype, string $activeplugin) { + parent::__construct($context); + $this->activeurl = $activeurl; + $this->activetype = $activetype; + $this->activeplugin = $activeplugin; + } + + /** + * Export the data for the mustache template. + * + * @param \renderer_base $output renderer to be used to render the action bar elements. + * @return array + */ + public function export_for_template(\renderer_base $output): array { + $urlselect = $this->get_action_selector(); + + if (is_null($urlselect)) { + return []; + } + + return [ + 'generalnavselector' => $urlselect->export_for_template($output), + ]; + } + + /** + * Returns the template for the action bar. + * + * @return string + */ + public function get_template(): string { + return 'core_grades/general_action_bar'; + } + + /** + * Returns the URL selector object. + * + * @return \url_select|null The URL select object. + */ + private function get_action_selector(): ?\url_select { + if ($this->context->contextlevel !== CONTEXT_COURSE) { + return null; + } + $courseid = $this->context->instanceid; + $plugininfo = grade_get_plugin_info($courseid, $this->activetype, $this->activeplugin); + $menu = []; + $viewgroup = []; + $setupgroup = []; + $moregroup = []; + + foreach ($plugininfo as $plugintype => $plugins) { + // Skip if the plugintype value is 'strings'. This particular item only returns an array of strings + // which we do not need. + if ($plugintype == 'strings') { + continue; + } + + // If $plugins is actually the definition of a child-less parent link. + if (!empty($plugins->id)) { + $string = $plugins->string; + if (!empty($plugininfo[$this->activetype]->parent)) { + $string = $plugininfo[$this->activetype]->parent->string; + } + $menu[$plugins->link->out(false)] = $string; + continue; + } + + foreach ($plugins as $key => $plugin) { + // Depending on the plugin type, include the plugin to the appropriate item group for the URL selector + // element. + switch ($plugintype) { + case 'report': + $viewgroup[$plugin->link->out(false)] = $plugin->string; + break; + case 'settings': + $setupgroup[$plugin->link->out(false)] = $plugin->string; + break; + case 'scale': + // We only need the link to the 'view scales' page, otherwise skip and continue to the next + // plugin. + if ($key !== 'view') { + continue 2; + } + $moregroup[$plugin->link->out(false)] = get_string('scales'); + break; + case 'outcome': + // We only need the link to the 'outcomes used in course' page, otherwise skip and continue to + // the next plugin. + if ($key !== 'course') { + continue 2; + } + $moregroup[$plugin->link->out(false)] = get_string('outcomes', 'grades'); + break; + case 'letter': + // We only need the link to the 'view grade letters' page, otherwise skip and continue to the + // next plugin. + if ($key !== 'view') { + continue 2; + } + $moregroup[$plugin->link->out(false)] = get_string('gradeletters', 'grades'); + break; + case 'import': + $link = new moodle_url('/grade/import/index.php', ['id' => $courseid]); + // If the link to the grade import options is already added to the group, skip and continue to + // the next plugin. + if (array_key_exists($link->out(false), $moregroup)) { + continue 2; + } + $moregroup[$link->out(false)] = get_string('import', 'grades'); + break; + case 'export': + $link = new moodle_url('/grade/export/index.php', ['id' => $courseid]); + // If the link to the grade export options is already added to the group, skip and continue to + // the next plugin. + if (array_key_exists($link->out(false), $moregroup)) { + continue 2; + } + $moregroup[$link->out(false)] = get_string('export', 'grades'); + break; + } + } + } + + if (!empty($viewgroup)) { + $menu[][get_string('view')] = $viewgroup; + } + + if (!empty($setupgroup)) { + $menu[][get_string('setup', 'grades')] = $setupgroup; + } + + if (!empty($moregroup)) { + $menu[][get_string('moremenu')] = $moregroup; + } + + return new \url_select($menu, $this->activeurl->out(false), null, 'gradesactionselect'); + } +} diff --git a/grade/lib.php b/grade/lib.php index d2dfff27b6458..754456e20d70e 100644 --- a/grade/lib.php +++ b/grade/lib.php @@ -25,6 +25,9 @@ require_once($CFG->libdir . '/gradelib.php'); require_once($CFG->dirroot . '/grade/export/lib.php'); +use \core_grades\output\action_bar; +use \core_grades\output\general_action_bar; + /** * This class iterates over all users that are graded in a course. * Returns detailed info about users and their grades. @@ -966,31 +969,29 @@ public function __construct($id, $link, $string, $parent=null) { } /** - * Prints the page headers, breadcrumb trail, page heading, (optional) dropdown navigation menu and - * (optional) navigation tabs for any gradebook page. All gradebook pages MUST use these functions - * in favour of the usual print_header(), print_header_simple(), print_heading() etc. - * !IMPORTANT! Use of tabs.php file in gradebook pages is forbidden unless tabs are switched off at - * the site level for the gradebook ($CFG->grade_navmethod = GRADE_NAVMETHOD_DROPDOWN). + * Prints the page headers, breadcrumb trail, page heading, (optional) navigation and for any gradebook page. + * All gradebook pages MUST use these functions in favour of the usual print_header(), print_header_simple(), + * print_heading() etc. * - * @param int $courseid Course id - * @param string $active_type The type of the current page (report, settings, - * import, export, scales, outcomes, letters) - * @param string $active_plugin The plugin of the current page (grader, fullview etc...) - * @param string $heading The heading of the page. Tries to guess if none is given + * @param int $courseid Course id + * @param string $active_type The type of the current page (report, settings, + * import, export, scales, outcomes, letters) + * @param string|null $active_plugin The plugin of the current page (grader, fullview etc...) + * @param string|bool $heading The heading of the page. Tries to guess if none is given * @param boolean $return Whether to return (true) or echo (false) the HTML generated by this function - * @param string $bodytags Additional attributes that will be added to the tag - * @param string $buttons Additional buttons to display on the page - * @param boolean $shownavigation should the gradebook navigation drop down (or tabs) be shown? - * @param string $headerhelpidentifier The help string identifier if required. - * @param string $headerhelpcomponent The component for the help string. - * @param stdClass $user The user object for use with the user context header. - * + * @param string|bool $buttons Additional buttons to display on the page + * @param boolean $shownavigation should the gradebook navigation be shown? + * @param string|null $headerhelpidentifier The help string identifier if required. + * @param string|null $headerhelpcomponent The component for the help string. + * @param stdClass|null $user The user object for use with the user context header. + * @param actionbar|null $actionbar The actions bar which will be displayed on the page if $shownavigation is set + * to true. If $actionbar is not explicitly defined, the general action bar + * (\core_grades\output\general_action_bar) will be used by default. * @return string HTML code or nothing if $return == false */ -function print_grade_page_head($courseid, $active_type, $active_plugin=null, - $heading = false, $return=false, - $buttons=false, $shownavigation=true, $headerhelpidentifier = null, $headerhelpcomponent = null, - $user = null) { +function print_grade_page_head(int $courseid, string $active_type, ?string $active_plugin = null, $heading = false, + bool $return = false, $buttons = false, bool $shownavigation = true, ?string $headerhelpidentifier = null, + ?string $headerhelpcomponent = null, ?stdClass $user = null, ?action_bar $actionbar = null) { global $CFG, $OUTPUT, $PAGE; // Put a warning on all gradebook pages if the course has modules currently scheduled for background deletion. @@ -1024,6 +1025,8 @@ function print_grade_page_head($courseid, $active_type, $active_plugin=null, } $PAGE->set_title(get_string('grades') . ': ' . $stractive_type); $PAGE->set_heading($title); + $PAGE->set_secondary_active_tab('grades'); + if ($buttons instanceof single_button) { $buttons = $OUTPUT->render($buttons); } @@ -1049,53 +1052,46 @@ function print_grade_page_head($courseid, $active_type, $active_plugin=null, } if ($shownavigation) { - $navselector = null; - if ($courseid != SITEID && - ($CFG->grade_navmethod == GRADE_NAVMETHOD_COMBO || $CFG->grade_navmethod == GRADE_NAVMETHOD_DROPDOWN)) { - // It's absolutely essential that this grade plugin selector is shown after the user header. Just ask Fred. - $navselector = print_grade_plugin_selector($plugin_info, $active_type, $active_plugin, true); - if ($return) { - $returnval .= $navselector; - } else if (!isset($user)) { - echo $navselector; - } - } - - $output = ''; - // Add a help dialogue box if provided. - if (isset($headerhelpidentifier)) { - $output = $OUTPUT->heading_with_help($heading, $headerhelpidentifier, $headerhelpcomponent); - } else { - if (isset($user)) { - $output = $OUTPUT->context_header( - array( - 'heading' => html_writer::link(new moodle_url('/user/view.php', array('id' => $user->id, - 'course' => $courseid)), fullname($user)), - 'user' => $user, - 'usercontext' => context_user::instance($user->id) - ), 2 - ) . $navselector; - } else { - $output = $OUTPUT->heading($heading); - } + $renderer = $PAGE->get_renderer('core_grades'); + // If the navigation action bar is not explicitly defined, use the general (default) action bar. + if (!$actionbar) { + $actionbar = new general_action_bar($PAGE->context, $PAGE->url, $active_type, $active_plugin); } if ($return) { - $returnval .= $output; + $returnval .= $renderer->render_action_bar($actionbar); } else { - echo $output; + echo $renderer->render_action_bar($actionbar); } + } - if ($courseid != SITEID && - ($CFG->grade_navmethod == GRADE_NAVMETHOD_COMBO || $CFG->grade_navmethod == GRADE_NAVMETHOD_TABS)) { - $returnval .= grade_print_tabs($active_type, $active_plugin, $plugin_info, $return); + $output = ''; + // Add a help dialogue box if provided. + if (isset($headerhelpidentifier)) { + $output = $OUTPUT->heading_with_help($heading, $headerhelpidentifier, $headerhelpcomponent); + } else { + if (isset($user)) { + $output = $OUTPUT->context_header( + array( + 'heading' => html_writer::link(new moodle_url('/user/view.php', array('id' => $user->id, + 'course' => $courseid)), fullname($user)), + 'user' => $user, + 'usercontext' => context_user::instance($user->id) + ), 2 + ); + } else { + $output = $OUTPUT->heading($heading); } } - $returnval .= print_natural_aggregation_upgrade_notice($courseid, - context_course::instance($courseid), - $PAGE->url, - $return); + if ($return) { + $returnval .= $output; + } else { + echo $output; + } + + $returnval .= print_natural_aggregation_upgrade_notice($courseid, context_course::instance($courseid), $PAGE->url, + $return); if ($return) { return $returnval; diff --git a/grade/renderer.php b/grade/renderer.php new file mode 100644 index 0000000000000..ceb5ab92fc4b4 --- /dev/null +++ b/grade/renderer.php @@ -0,0 +1,40 @@ +. + +defined('MOODLE_INTERNAL') || die; + +use \core_grades\output\action_bar; + +/** + * Renderer class for the grade pages. + * + * @package core_grades + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class core_grades_renderer extends plugin_renderer_base { + + /** + * Renders the action bar for a given page. + * + * @param action_bar $actionbar + * @return string The HTML output + */ + public function render_action_bar(action_bar $actionbar): string { + $data = $actionbar->export_for_template($this); + return $this->render_from_template($actionbar->get_template(), $data); + } +} diff --git a/grade/report/history/index.php b/grade/report/history/index.php index cb8fec8e77efb..469a2dec0503a 100644 --- a/grade/report/history/index.php +++ b/grade/report/history/index.php @@ -108,7 +108,11 @@ } // Print header. -print_grade_page_head($COURSE->id, 'report', 'history', get_string('pluginname', 'gradereport_history'), false, ''); +$actionbar = new \core_grades\output\general_action_bar($context, + new moodle_url('/grade/report/history/index.php', ['id' => $courseid]), 'report', 'history'); +print_grade_page_head($COURSE->id, 'report', 'history', get_string('pluginname', + 'gradereport_history'), false, false, true, null, null, + null, $actionbar); $mform->display(); if ($showreport) { diff --git a/grade/report/overview/index.php b/grade/report/overview/index.php index f181a583899d5..62c2cdcf6d866 100644 --- a/grade/report/overview/index.php +++ b/grade/report/overview/index.php @@ -90,6 +90,9 @@ // First make sure we have proper final grades. grade_regrade_final_grades_if_required($course); +$actionbar = new \core_grades\output\general_action_bar($context, + new moodle_url('/grade/report/overview/index.php', ['id' => $courseid]), 'report', 'overview'); + if (has_capability('moodle/grade:viewall', $context) && $courseid != SITEID) { // Please note this would be extremely slow if we wanted to implement this properly for all teachers. $groupmode = groups_get_course_groupmode($course); // Groups are being used @@ -110,8 +113,8 @@ } if (empty($userid)) { - // Add tabs - print_grade_page_head($courseid, 'report', 'overview'); + print_grade_page_head($courseid, 'report', 'overview', false, false, false, + true, null, null, null, $actionbar); groups_print_course_menu($course, $gpr->get_return_url('index.php?id='.$courseid, array('userid'=>0))); @@ -124,7 +127,8 @@ } else { // Only show one user's report $report = new grade_report_overview($userid, $gpr, $context); print_grade_page_head($courseid, 'report', 'overview', get_string('pluginname', 'gradereport_overview') . - ' - ' . fullname($report->user), false, false, true, null, null, $report->user); + ' - ' . fullname($report->user), false, false, true, null, null, + $report->user, $actionbar); groups_print_course_menu($course, $gpr->get_return_url('index.php?id='.$courseid, array('userid'=>0))); if ($user_selector) { @@ -170,7 +174,8 @@ } } else { // We have a course context. We must be navigating from the gradebook. print_grade_page_head($courseid, 'report', 'overview', get_string('pluginname', 'gradereport_overview') - . ' - ' . fullname($report->user)); + . ' - ' . fullname($report->user), false, false, true, null, null, + $report->user, $actionbar); if ($report->fill_table()) { echo '
' . $report->print_table(true); } diff --git a/grade/report/singleview/index.php b/grade/report/singleview/index.php index 9422fb12187a2..a0a83e355fdb9 100644 --- a/grade/report/singleview/index.php +++ b/grade/report/singleview/index.php @@ -130,10 +130,16 @@ } $PAGE->set_pagelayout('report'); + +$actionbar = new \core_grades\output\general_action_bar($context, + new moodle_url('/grade/report/singleview/index.php', ['id' => $courseid]), 'report', 'singleview'); + if ($itemtype == 'user') { - print_grade_page_head($course->id, 'report', 'singleview', $reportname, false, false, true, null, null, $report->screen->item); + print_grade_page_head($course->id, 'report', 'singleview', $reportname, false, false, + true, null, null, $report->screen->item, $actionbar); } else { - print_grade_page_head($course->id, 'report', 'singleview', $reportname); + print_grade_page_head($course->id, 'report', 'singleview', $reportname, false, false, + true, null, null, null, $actionbar); } $graderrightnav = $graderleftnav = null; diff --git a/grade/templates/general_action_bar.mustache b/grade/templates/general_action_bar.mustache new file mode 100644 index 0000000000000..fde58c7c55101 --- /dev/null +++ b/grade/templates/general_action_bar.mustache @@ -0,0 +1,55 @@ +{{! + 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_grades/general_action_bar + + The general actions bar for navigating through the gradebook pages. + + Context variables required for this template: + * generalnavselector - The data object containing the required properties to render the general navigation selector. + + Example context (json): + { + "generalnavselector": { + "id": "url_select12345", + "action": "https://example.com/get", + "classes": "urlselect", + "formid": "gradesactionselect", + "sesskey": "sesskey", + "label": "", + "helpicon": false, + "showbutton": null, + "options": [{ + "name": "View", "isgroup": true, "options": + [ + {"name": "Grader report", "isgroup": false, "value": "/grade/report/grader/index.php"} + ]}, + {"name": "Setup", "isgroup": true, "options": + [ + {"name": "Gradebook setup", "isgroup": false, "value": "/grade/edit/tree/index.php"} + ]}], + "disabled": false, + "title": null + } + } +}} +
+
+
+ {{#generalnavselector}} + {{>core/url_select}} + {{/generalnavselector}} +
+
+
diff --git a/lang/en/grades.php b/lang/en/grades.php index 42ebc37f8516c..1bb83626c96db 100644 --- a/lang/en/grades.php +++ b/lang/en/grades.php @@ -737,6 +737,7 @@ $string['setpreferences'] = 'Set preferences'; $string['setting'] = 'Setting'; $string['settings'] = 'Settings'; +$string['setup'] = 'Setup'; $string['setweights'] = 'Set weights'; $string['showanalysisicon'] = 'Show grade analysis icon'; $string['showanalysisicon_desc'] = 'Whether to show grade analysis icon by default. If the activity module supports it, the grade analysis icon links to a page with more detailed explanation of the grade and how it was obtained.';