MDL-72873 core_grades: Add tertiary navigation in grade outcomes

This commit is contained in:
Mihail Geshoski 2021-10-30 00:34:34 +08:00
parent c326402e86
commit afbc8b2586
11 changed files with 388 additions and 58 deletions

View File

@ -0,0 +1,65 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 <mihail@moodle.com>
* @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;
}
}

View File

@ -0,0 +1,95 @@
<?php
// 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 <http://www.gnu.org/licenses/>.
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 <mihail@moodle.com>
* @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;
}
}

View File

@ -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');

View File

@ -61,17 +61,6 @@
</td>
</tr>
</table>
<?php
if (has_capability('moodle/grade:manageoutcomes', $context)) {
?>
<p class="mdl-align">
<a href="<?php echo $CFG->wwwroot ?>/grade/edit/outcome/index.php?id=<?php echo $courseid; ?>"><?php echo get_string('editoutcomes','grades'); ?></a>
</p>
<?php
}
?>
<input name="id" type="hidden" value="<?php echo $courseid?>"/>
<input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" />
</div>

View File

@ -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);

View File

@ -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) ."<br />". 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));

View File

@ -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);
}
}

View File

@ -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();
/**

View File

@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@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": []
}
}
}}
<div class="container-fluid mb-4">
<div class="row">
<div class="d-flex">
<div>
{{#generalnavselector}}
{{>core/url_select}}
{{/generalnavselector}}
</div>
{{#manageoutcomesbutton}}
<div class="ml-2">
{{>core/single_button}}
</div>
{{/manageoutcomesbutton}}
</div>
</div>
</div>

View File

@ -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 <http://www.gnu.org/licenses/>.
}}
{{!
@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": []
}
}
}}
<div class="container-fluid mb-4">
<div class="row">
<div class="d-flex">
{{#backbutton}}
<div class="mr-2">
{{>core/single_button}}
</div>
{{/backbutton}}
{{#addoutcomebutton}}
<div>
{{>core/single_button}}
</div>
{{/addoutcomebutton}}
</div>
<div class="ml-auto d-flex">
{{#importoutcomesbutton}}
<div class="ml-2">
{{>core/single_button}}
</div>
{{/importoutcomesbutton}}
{{#exportoutcomesbutton}}
<div class="ml-2">
{{>core/single_button}}
</div>
{{/exportoutcomesbutton}}
</div>
</div>
</div>

View File

@ -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';