From afbc8b2586644c1bff6e53c29f352314103fffbe Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Sat, 30 Oct 2021 00:34:34 +0800 Subject: [PATCH] MDL-72873 core_grades: Add tertiary navigation in grade outcomes --- .../output/course_outcomes_action_bar.php | 65 ++++++++++++ .../output/manage_outcomes_action_bar.php | 95 +++++++++++++++++ grade/edit/outcome/course.php | 6 +- grade/edit/outcome/course_form.html | 11 -- grade/edit/outcome/edit.php | 9 +- grade/edit/outcome/import.php | 48 +++++---- grade/edit/outcome/import_outcomes_form.php | 7 +- grade/edit/outcome/index.php | 29 +++-- .../course_outcomes_action_bar.mustache | 74 +++++++++++++ .../manage_outcomes_action_bar.mustache | 100 ++++++++++++++++++ lang/en/grades.php | 2 + 11 files changed, 388 insertions(+), 58 deletions(-) create mode 100644 grade/classes/output/course_outcomes_action_bar.php create mode 100644 grade/classes/output/manage_outcomes_action_bar.php create mode 100644 grade/templates/course_outcomes_action_bar.mustache create mode 100644 grade/templates/manage_outcomes_action_bar.mustache diff --git a/grade/classes/output/course_outcomes_action_bar.php b/grade/classes/output/course_outcomes_action_bar.php new file mode 100644 index 00000000000..8c90ff2e6c2 --- /dev/null +++ b/grade/classes/output/course_outcomes_action_bar.php @@ -0,0 +1,65 @@ +. + +namespace core_grades\output; + +use moodle_url; + +/** + * Renderable class for the action bar elements in the gradebook course outcomes page. + * + * @package core_grades + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class course_outcomes_action_bar extends action_bar { + + /** + * Returns the template for the action bar. + * + * @return string + */ + public function get_template(): string { + return 'core_grades/course_outcomes_action_bar'; + } + + /** + * 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 { + if ($this->context->contextlevel !== CONTEXT_COURSE) { + return []; + } + $courseid = $this->context->instanceid; + // Get the data used to output the general navigation selector. + $generalnavselector = new general_action_bar($this->context, + new moodle_url('/grade/edit/outcome/course.php', ['id' => $courseid]), 'outcome', 'course'); + $data = $generalnavselector->export_for_template($output); + + if (has_capability('moodle/grade:manageoutcomes', $this->context)) { + // Add a button to the action bar with a link to the 'manage outcomes' page. + $manageoutcomeslink = new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid]); + $manageoutcomesbutton = new \single_button($manageoutcomeslink, get_string('manageoutcomes', 'grades'), + 'get', true); + $data['manageoutcomesbutton'] = $manageoutcomesbutton->export_for_template($output); + } + + return $data; + } +} diff --git a/grade/classes/output/manage_outcomes_action_bar.php b/grade/classes/output/manage_outcomes_action_bar.php new file mode 100644 index 00000000000..e279285f4c0 --- /dev/null +++ b/grade/classes/output/manage_outcomes_action_bar.php @@ -0,0 +1,95 @@ +. + +namespace core_grades\output; + +use moodle_url; + +/** + * Renderable class for the action bar elements in the manage outcomes page. + * + * @package core_grades + * @copyright 2021 Mihail Geshoski + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class manage_outcomes_action_bar extends action_bar { + + /** @var bool $hasoutcomes Whether there are existing outcomes. */ + protected $hasoutcomes; + + /** + * The class constructor. + * + * @param \context $context The context object. + * @param bool $hasoutcomes Whether there are existing outcomes. + */ + public function __construct(\context $context, bool $hasoutcomes) { + parent::__construct($context); + $this->hasoutcomes = $hasoutcomes; + } + + /** + * Returns the template for the action bar. + * + * @return string + */ + public function get_template(): string { + return 'core_grades/manage_outcomes_action_bar'; + } + + /** + * 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 { + $data = []; + $courseid = 0; + // Display the following buttons only if the user is in course gradebook. + if ($this->context->contextlevel === CONTEXT_COURSE) { + $courseid = $this->context->instanceid; + // Add a button to the action bar with a link to the 'course outcomes' page. + $backlink = new moodle_url('/grade/edit/outcome/course.php', ['id' => $courseid]); + $backbutton = new \single_button($backlink, get_string('back'), 'get'); + $data['backbutton'] = $backbutton->export_for_template($output); + + // Add a button to the action bar with a link to the 'import outcomes' page. The import outcomes + // functionality is currently only available in the course context. + $importoutcomeslink = new moodle_url('/grade/edit/outcome/import.php', ['courseid' => $courseid]); + $importoutcomesbutton = new \single_button($importoutcomeslink, get_string('importoutcomes', 'grades'), + 'get'); + $data['importoutcomesbutton'] = $importoutcomesbutton->export_for_template($output); + } + + // Add a button to the action bar with a link to the 'add new outcome' page. + $addoutcomelink = new moodle_url('/grade/edit/outcome/edit.php', ['courseid' => $courseid]); + $addoutcomebutton = new \single_button($addoutcomelink, get_string('outcomecreate', 'grades'), + 'get', true); + $data['addoutcomebutton'] = $addoutcomebutton->export_for_template($output); + + if ($this->hasoutcomes) { + // Add a button to the action bar which enables export of all existing outcomes. + $exportoutcomeslink = new moodle_url('/grade/edit/outcome/export.php', + ['id' => $courseid, 'sesskey' => sesskey()]); + $exportoutcomesbutton = new \single_button($exportoutcomeslink, get_string('exportalloutcomes', 'grades'), + 'get'); + $data['exportoutcomesbutton'] = $exportoutcomesbutton->export_for_template($output); + } + + return $data; + } +} diff --git a/grade/edit/outcome/course.php b/grade/edit/outcome/course.php index b23dc998d9c..6353c82b725 100644 --- a/grade/edit/outcome/course.php +++ b/grade/edit/outcome/course.php @@ -132,8 +132,10 @@ if ($data = data_submitted() and confirm_sesskey()) { redirect('course.php?id='.$courseid); // we must redirect to get fresh data } -/// Print header -print_grade_page_head($COURSE->id, 'outcome', 'course'); +$actionbar = new \core_grades\output\course_outcomes_action_bar($context); +// Print header. +print_grade_page_head($COURSE->id, 'outcome', 'course', false, false, false, + true, null, null, null, $actionbar); require('course_form.html'); diff --git a/grade/edit/outcome/course_form.html b/grade/edit/outcome/course_form.html index 312eef2e050..b9594f699c5 100644 --- a/grade/edit/outcome/course_form.html +++ b/grade/edit/outcome/course_form.html @@ -61,17 +61,6 @@ - - -

- -

- - diff --git a/grade/edit/outcome/edit.php b/grade/edit/outcome/edit.php index f9132d08f44..e6db82db70c 100644 --- a/grade/edit/outcome/edit.php +++ b/grade/edit/outcome/edit.php @@ -41,7 +41,7 @@ $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); $systemcontext = context_system::instance(); -$heading = null; +$heading = get_string('addoutcome', 'grades'); // a bit complex access control :-O if ($id) { @@ -74,7 +74,6 @@ if ($id) { } } else if ($courseid){ - $heading = get_string('addoutcome', 'grades'); /// adding new outcome from course $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); require_login($course); @@ -159,11 +158,7 @@ if ($mform->is_cancelled()) { redirect($returnurl); } -if ($courseid) { - print_grade_page_head($courseid, 'outcome', 'edit', $heading); -} else { - echo $OUTPUT->header(); -} +print_grade_page_head($courseid ?: SITEID, 'outcome', 'edit', $heading, false, false, false); if (!grade_scale::fetch_all_local($courseid) && !grade_scale::fetch_all_global()) { echo $OUTPUT->confirm(get_string('noscales', 'grades'), $CFG->wwwroot.'/grade/edit/scale/edit.php?courseid='.$courseid, $returnurl); diff --git a/grade/edit/outcome/import.php b/grade/edit/outcome/import.php index ec5117ee55c..379183c93a4 100644 --- a/grade/edit/outcome/import.php +++ b/grade/edit/outcome/import.php @@ -59,14 +59,19 @@ $navigation = grade_build_nav(__FILE__, get_string('outcomes', 'grades'), $cours $upload_form = new import_outcomes_form(); -// display import form -if (!$upload_form->get_data()) { - print_grade_page_head($courseid, 'outcome', 'import', get_string('importoutcomes', 'grades')); +if ($upload_form->is_cancelled()) { + redirect(new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid])); + die; +} + +print_grade_page_head($courseid, 'outcome', 'import', get_string('importoutcomes', 'grades'), + false, false, false); + +if (!$upload_form->get_data()) { // Display the import form. $upload_form->display(); echo $OUTPUT->footer(); die; } -print_grade_page_head($courseid, 'outcome', 'import', get_string('importoutcomes', 'grades')); $imported_file = $CFG->tempdir . '/outcomeimport/importedfile_'.time().'.csv'; make_temp_directory('outcomeimport'); @@ -99,6 +104,7 @@ if ($handle = fopen($imported_file, 'r')) { $imported_headers = array(); // will later be initialized with the values found in the file $fatal_error = false; + $errormessage = ''; // data should be separated by a ';'. *NOT* by a comma! TODO: version 2.0 // or whenever we can depend on PHP5, set the second parameter (8192) to 0 (unlimited line length) : the database can store over 128k per line. @@ -124,11 +130,8 @@ if ($handle = fopen($imported_file, 'r')) { } } if ($error) { - echo $OUTPUT->box_start('generalbox importoutcomenofile buttons'); - echo get_string('importoutcomenofile', 'grades', $line); - echo $OUTPUT->single_button(new moodle_url('/grade/edit/outcome/import.php', array('courseid'=> $courseid)), get_string('back'), 'get'); - echo $OUTPUT->box_end(); $fatal_error = true; + $errormessage = get_string('importoutcomenofile', 'grades', $line); break; } @@ -143,23 +146,16 @@ if ($handle = fopen($imported_file, 'r')) { // sanity check #2: every line must have the same number of columns as there are // headers. If not, processing stops. if ( count($csv_data) != count($file_headers) ) { - echo $OUTPUT->box_start('generalbox importoutcomenofile'); - echo get_string('importoutcomenofile', 'grades', $line); - echo $OUTPUT->single_button(new moodle_url('/grade/edit/outcome/import.php', array('courseid'=> $courseid)), get_string('back'), 'get'); - echo $OUTPUT->box_end(); $fatal_error = true; - //echo $OUTPUT->box(var_export($csv_data, true) ."
". var_export($header, true)); + $errormessage = get_string('importoutcomenofile', 'grades', $line); break; } // sanity check #3: all required fields must be present on the current line. foreach ($headers as $header => $position) { if ($csv_data[$imported_headers[$header]] == '') { - echo $OUTPUT->box_start('generalbox importoutcomenofile'); - echo get_string('importoutcomenofile', 'grades', $line); - echo $OUTPUT->single_button(new moodle_url('/grade/edit/outcome/import.php', array('courseid'=> $courseid)), get_string('back'), 'get'); - echo $OUTPUT->box_end(); $fatal_error = true; + $errormessage = get_string('importoutcomenofile', 'grades', $line); break; } } @@ -182,7 +178,8 @@ if ($handle = fopen($imported_file, 'r')) { if ($outcome) { // already exists, print a message and skip. - echo $OUTPUT->box(get_string('importskippedoutcome', 'grades', $csv_data[$imported_headers['outcome_shortname']])); + echo $OUTPUT->notification(get_string('importskippedoutcome', 'grades', + $csv_data[$imported_headers['outcome_shortname']]), 'info', false); continue; } @@ -196,7 +193,8 @@ if ($handle = fopen($imported_file, 'r')) { $scale_id = key($scale); } else { if (!has_capability('moodle/course:managescales', $context)) { - echo $OUTPUT->box(get_string('importskippednomanagescale', 'grades', $csv_data[$imported_headers['outcome_shortname']])); + echo $OUTPUT->notification(get_string('importskippedoutcome', 'grades', + $csv_data[$imported_headers['outcome_shortname']]), 'warning', false); continue; } else { // scale doesn't exists : create it. @@ -233,7 +231,17 @@ if ($handle = fopen($imported_file, 'r')) { $outcome_success_strings = new StdClass(); $outcome_success_strings->name = $outcome_data['fullname']; $outcome_success_strings->id = $outcome_id; - echo $OUTPUT->box(get_string('importoutcomesuccess', 'grades', $outcome_success_strings)); + echo $OUTPUT->notification(get_string('importoutcomesuccess', 'grades', $outcome_success_strings), + 'success', false); + } + + if ($fatal_error) { + echo $OUTPUT->notification($errormessage, 'error', false); + echo $OUTPUT->single_button(new moodle_url('/grade/edit/outcome/import.php', ['courseid' => $courseid]), + get_string('back'), 'get'); + } else { + echo $OUTPUT->single_button(new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid]), + get_string('continue'), 'get'); } } else { echo $OUTPUT->box(get_string('importoutcomenofile', 'grades', 0)); diff --git a/grade/edit/outcome/import_outcomes_form.php b/grade/edit/outcome/import_outcomes_form.php index 46ec51822c4..8058d8a3ec2 100644 --- a/grade/edit/outcome/import_outcomes_form.php +++ b/grade/edit/outcome/import_outcomes_form.php @@ -51,8 +51,11 @@ class import_outcomes_form extends moodleform { $mform->addRule('userfile', get_string('required'), 'required', null, 'server'); $mform->addHelpButton('userfile', 'importoutcomes', 'grades'); - $mform->addElement('submit', 'save', get_string('uploadthisfile')); - + $buttonarray = [ + $mform->createElement('submit', 'save', get_string('uploadthisfile')), + $mform->createElement('cancel') + ]; + $mform->addGroup($buttonarray, 'buttonar', '', ' ', false); } } diff --git a/grade/edit/outcome/index.php b/grade/edit/outcome/index.php index 236049c0857..3d134191d9d 100644 --- a/grade/edit/outcome/index.php +++ b/grade/edit/outcome/index.php @@ -50,6 +50,7 @@ if ($courseid) { } require_once $CFG->libdir.'/adminlib.php'; admin_externalpage_setup('outcomes'); + $context = context_system::instance(); } /// return tracking object @@ -113,7 +114,6 @@ if ($courseid) { $caneditcoursescales = has_capability('moodle/course:managescales', $context); } else { - echo $OUTPUT->header(); $caneditcoursescales = $caneditsystemscales; } @@ -122,7 +122,7 @@ $outcomes_tables = array(); $heading = get_string('outcomes', 'grades'); if ($courseid and $outcomes = grade_outcome::fetch_all_local($courseid)) { - $return = $OUTPUT->heading($strcustomoutcomes, 3, 'main'); + $return = $OUTPUT->heading($strcustomoutcomes, 3, 'main mt-3'); $data = array(); foreach($outcomes as $outcome) { $line = array(); @@ -172,7 +172,7 @@ if ($courseid and $outcomes = grade_outcome::fetch_all_local($courseid)) { if ($outcomes = grade_outcome::fetch_all_global()) { - $return = $OUTPUT->heading($strstandardoutcome, 3, 'main'); + $return = $OUTPUT->heading($strstandardoutcome, 3, 'main mt-3'); $data = array(); foreach($outcomes as $outcome) { $line = array(); @@ -223,22 +223,19 @@ if ($outcomes = grade_outcome::fetch_all_global()) { $outcomes_tables[] = $return; } -if ($courseid) { - /// Print header - print_grade_page_head($courseid, 'outcome', 'edit', $heading); -} +$actionbar = new \core_grades\output\manage_outcomes_action_bar($context, !empty($outcomes_tables)); +print_grade_page_head($courseid ?: SITEID, 'outcome', 'edit', $heading, false, false, + true, null, null, null, $actionbar); -foreach($outcomes_tables as $table) { - echo $table; +// If there are existing outcomes, output the outcome tables. +if (!empty($outcomes_tables)) { + foreach ($outcomes_tables as $table) { + echo $table; + } +} else { + echo $OUTPUT->notification(get_string('noexistingoutcomes', 'grades'), 'info', false); } -echo $OUTPUT->container_start('buttons'); -echo $OUTPUT->single_button(new moodle_url('edit.php', array('courseid'=>$courseid)), $strcreatenewoutcome); -if ( !empty($outcomes_tables) ) { - echo $OUTPUT->single_button(new moodle_url('export.php', array('id'=>$courseid, 'sesskey'=>sesskey())), get_string('exportalloutcomes', 'grades')); -} -echo $OUTPUT->container_end(); - echo $OUTPUT->footer(); /** diff --git a/grade/templates/course_outcomes_action_bar.mustache b/grade/templates/course_outcomes_action_bar.mustache new file mode 100644 index 00000000000..ed99c160c78 --- /dev/null +++ b/grade/templates/course_outcomes_action_bar.mustache @@ -0,0 +1,74 @@ +{{! + 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/course_outcomes_action_bar + + Actions bar for the gradebook course outcomes page. + + Context variables required for this template: + * generalnavselector - The data object containing the required properties to render the general navigation selector. + * manageoutcomesbutton - The data object containing the required properties to render the 'manage outcomes' button. + + 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 + }, + "manageoutcomesbutton": { + "id": "single_button12345", + "method" : "get", + "classes": "singlebutton", + "formid": null, + "url" : "#", + "primary" : true, + "tooltip" : null, + "label" : "Manage outcomes", + "attributes": [] + } + } +}} +
+
+
+
+ {{#generalnavselector}} + {{>core/url_select}} + {{/generalnavselector}} +
+ {{#manageoutcomesbutton}} +
+ {{>core/single_button}} +
+ {{/manageoutcomesbutton}} +
+
+
diff --git a/grade/templates/manage_outcomes_action_bar.mustache b/grade/templates/manage_outcomes_action_bar.mustache new file mode 100644 index 00000000000..87e6579f86a --- /dev/null +++ b/grade/templates/manage_outcomes_action_bar.mustache @@ -0,0 +1,100 @@ +{{! + 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/manage_outcomes_action_bar + + Actions bar for the manage outcomes page. + + Context variables required for this template: + * backbutton - The data object containing the required properties to render the 'back' button. + * addoutcomebutton - The data object containing the required properties to render the 'add a new outcome' button. + * importoutcomesbutton - The data object containing the required properties to render the 'import outcomes' button. + * exportoutcomesbutton - The data object containing the required properties to render the 'export outcomes' button. + + Example context (json): + { + "backbutton": { + "id": "single_button12345", + "method" : "get", + "classes": "singlebutton", + "formid": null, + "url" : "#", + "primary" : false, + "tooltip" : null, + "label" : "Back", + "attributes": [] + }, + "addoutcomebutton": { + "id": "single_button13245", + "method" : "get", + "classes": "singlebutton", + "formid": null, + "url" : "#", + "primary" : true, + "tooltip" : null, + "label" : "Add a new outcome", + "attributes": [] + }, + "importoutcomesbutton": { + "id": "single_button14325", + "method" : "get", + "classes": "singlebutton", + "formid": null, + "url" : "#", + "primary" : false, + "tooltip" : null, + "label" : "Import outcomes", + "attributes": [] + }, + "exportoutcomesbutton": { + "id": "single_button15245", + "method" : "get", + "classes": "singlebutton", + "formid": null, + "url" : "#", + "primary" : false, + "tooltip" : null, + "label" : "Export all outcomes", + "attributes": [] + } + } +}} +
+
+
+ {{#backbutton}} +
+ {{>core/single_button}} +
+ {{/backbutton}} + {{#addoutcomebutton}} +
+ {{>core/single_button}} +
+ {{/addoutcomebutton}} +
+
+ {{#importoutcomesbutton}} +
+ {{>core/single_button}} +
+ {{/importoutcomesbutton}} + {{#exportoutcomesbutton}} +
+ {{>core/single_button}} +
+ {{/exportoutcomesbutton}} +
+
+
diff --git a/lang/en/grades.php b/lang/en/grades.php index 1bb83626c96..f95c14e82b3 100644 --- a/lang/en/grades.php +++ b/lang/en/grades.php @@ -477,6 +477,7 @@ $string['locktimedate'] = 'Locked after: {$a}'; $string['lockverbose'] = 'Lock {$a->category} {$a->itemmodule} {$a->itemname}'; $string['lowest'] = 'Lowest'; $string['lowgradeletter'] = 'Low'; +$string['manageoutcomes'] = 'Manage outcomes'; $string['manualitem'] = 'Manual item'; $string['mapfrom'] = 'Map from'; $string['mapfrom_help'] = 'Select the column in the spreadsheet containing data for identifying the user, such as username, user ID or email address.'; @@ -554,6 +555,7 @@ $string['nocategories'] = 'Grade categories could not be added or found for this $string['nocategoryname'] = 'No category name was given.'; $string['nocategoryview'] = 'No category to view by'; $string['nocourses'] = 'There are no courses yet'; +$string['noexistingoutcomes'] = 'There are no existing outcomes.'; $string['noforce'] = 'Do not force'; $string['nogradeletters'] = 'No grade letters set'; $string['nogradesreturned'] = 'No grades returned';