first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+583
View File
@@ -0,0 +1,583 @@
<?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/>.
/**
* Compontent definition of a gradeitem.
*
* @package core_grades
* @copyright Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace core_grades;
use context;
use gradingform_controller;
use gradingform_instance;
use moodle_exception;
use stdClass;
use grade_item as core_gradeitem;
use grading_manager;
/**
* Compontent definition of a gradeitem.
*
* @package core_grades
* @copyright Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class component_gradeitem {
/** @var array The scale data for the current grade item */
protected $scale;
/** @var string The component */
protected $component;
/** @var context The context for this activity */
protected $context;
/** @var string The item name */
protected $itemname;
/** @var int The grade itemnumber */
protected $itemnumber;
/**
* component_gradeitem constructor.
*
* @param string $component
* @param context $context
* @param string $itemname
* @throws \coding_exception
*/
final protected function __construct(string $component, context $context, string $itemname) {
$this->component = $component;
$this->context = $context;
$this->itemname = $itemname;
$this->itemnumber = component_gradeitems::get_itemnumber_from_itemname($component, $itemname);
}
/**
* Fetch an instance of a specific component_gradeitem.
*
* @param string $component
* @param context $context
* @param string $itemname
* @return self
*/
public static function instance(string $component, context $context, string $itemname): self {
$itemnumber = component_gradeitems::get_itemnumber_from_itemname($component, $itemname);
$classname = "{$component}\\grades\\{$itemname}_gradeitem";
if (!class_exists($classname)) {
throw new \coding_exception("Unknown gradeitem {$itemname} for component {$classname}");
}
return $classname::load_from_context($context);
}
/**
* Load an instance of the current component_gradeitem based on context.
*
* @param context $context
* @return self
*/
abstract public static function load_from_context(context $context): self;
/**
* The table name used for grading.
*
* @return string
*/
abstract protected function get_table_name(): string;
/**
* Get the itemid for the current gradeitem.
*
* @return int
*/
public function get_grade_itemid(): int {
return component_gradeitems::get_itemnumber_from_itemname($this->component, $this->itemname);
}
/**
* Whether grading is enabled for this item.
*
* @return bool
*/
abstract public function is_grading_enabled(): bool;
/**
* Get the grade value for this instance.
* The itemname is translated to the relevant grade field for the activity.
*
* @return int
*/
abstract protected function get_gradeitem_value(): ?int;
/**
* Whether the grader can grade the gradee.
*
* @param stdClass $gradeduser The user being graded
* @param stdClass $grader The user who is grading
* @return bool
*/
abstract public function user_can_grade(stdClass $gradeduser, stdClass $grader): bool;
/**
* Require that the user can grade, throwing an exception if not.
*
* @param stdClass $gradeduser The user being graded
* @param stdClass $grader The user who is grading
* @throws \required_capability_exception
*/
abstract public function require_user_can_grade(stdClass $gradeduser, stdClass $grader): void;
/**
* Get the scale if a scale is being used.
*
* @return stdClass
*/
protected function get_scale(): ?stdClass {
global $DB;
$gradetype = $this->get_gradeitem_value();
if ($gradetype > 0) {
return null;
}
// This is a scale.
if (null === $this->scale) {
$this->scale = $DB->get_record('scale', ['id' => -1 * $gradetype]);
}
return $this->scale;
}
/**
* Check whether a scale is being used for this grade item.
*
* @return bool
*/
public function is_using_scale(): bool {
$gradetype = $this->get_gradeitem_value();
return $gradetype < 0;
}
/**
* Whether this grade item is configured to use direct grading.
*
* @return bool
*/
public function is_using_direct_grading(): bool {
if ($this->is_using_scale()) {
return false;
}
if ($this->get_advanced_grading_controller()) {
return false;
}
return true;
}
/**
* Whether this grade item is configured to use advanced grading.
*
* @return bool
*/
public function is_using_advanced_grading(): bool {
if ($this->is_using_scale()) {
return false;
}
if ($this->get_advanced_grading_controller()) {
return true;
}
return false;
}
/**
* Get the name of the advanced grading method.
*
* @return string
*/
public function get_advanced_grading_method(): ?string {
$gradingmanager = $this->get_grading_manager();
if (empty($gradingmanager)) {
return null;
}
return $gradingmanager->get_active_method();
}
/**
* Get the name of the component responsible for grading this gradeitem.
*
* @return string
*/
public function get_grading_component_name(): ?string {
if (!$this->is_grading_enabled()) {
return null;
}
if ($method = $this->get_advanced_grading_method()) {
return "gradingform_{$method}";
}
return 'core_grades';
}
/**
* Get the name of the component subtype responsible for grading this gradeitem.
*
* @return string
*/
public function get_grading_component_subtype(): ?string {
if (!$this->is_grading_enabled()) {
return null;
}
if ($method = $this->get_advanced_grading_method()) {
return null;
}
if ($this->is_using_scale()) {
return 'scale';
}
return 'point';
}
/**
* Whether decimals are allowed.
*
* @return bool
*/
protected function allow_decimals(): bool {
return $this->get_gradeitem_value() > 0;
}
/**
* Get the grading manager for this advanced grading definition.
*
* @return grading_manager
*/
protected function get_grading_manager(): ?grading_manager {
require_once(__DIR__ . '/../grading/lib.php');
return get_grading_manager($this->context, $this->component, $this->itemname);
}
/**
* Get the advanced grading controller if advanced grading is enabled.
*
* @return gradingform_controller
*/
protected function get_advanced_grading_controller(): ?gradingform_controller {
$gradingmanager = $this->get_grading_manager();
if (empty($gradingmanager)) {
return null;
}
if ($gradingmethod = $gradingmanager->get_active_method()) {
return $gradingmanager->get_controller($gradingmethod);
}
return null;
}
/**
* Get the list of available grade items.
*
* @return array
*/
public function get_grade_menu(): array {
return make_grades_menu($this->get_gradeitem_value());
}
/**
* Check whether the supplied grade is valid and throw an exception if not.
*
* @param float $grade The value being checked
* @throws moodle_exception
* @return bool
*/
public function check_grade_validity(?float $grade): bool {
$grade = grade_floatval(unformat_float($grade));
if ($grade) {
if ($this->is_using_scale()) {
// Fetch all options for this scale.
$scaleoptions = make_menu_from_list($this->get_scale()->scale);
if ($grade != -1 && !array_key_exists((int) $grade, $scaleoptions)) {
// The selected option did not exist.
throw new moodle_exception('error:notinrange', 'core_grading', '', (object) [
'maxgrade' => count($scaleoptions),
'grade' => $grade,
]);
}
} else if ($grade) {
$maxgrade = $this->get_gradeitem_value();
if ($grade > $maxgrade) {
// The grade is greater than the maximum possible value.
throw new moodle_exception('error:notinrange', 'core_grading', '', (object) [
'maxgrade' => $maxgrade,
'grade' => $grade,
]);
} else if ($grade < 0) {
// Negative grades are not supported.
throw new moodle_exception('error:notinrange', 'core_grading', '', (object) [
'maxgrade' => $maxgrade,
'grade' => $grade,
]);
}
}
}
return true;
}
/**
* Create an empty row in the grade for the specified user and grader.
*
* @param stdClass $gradeduser The user being graded
* @param stdClass $grader The user who is grading
* @return stdClass The newly created grade record
*/
abstract public function create_empty_grade(stdClass $gradeduser, stdClass $grader): stdClass;
/**
* Get the grade record for the specified grade id.
*
* @param int $gradeid
* @return stdClass
* @throws \dml_exception
*/
public function get_grade(int $gradeid): stdClass {
global $DB;
return $DB->get_record($this->get_table_name(), ['id' => $gradeid]);
}
/**
* Get the grade for the specified user.
*
* @param stdClass $gradeduser The user being graded
* @param stdClass $grader The user who is grading
* @return stdClass The grade value
*/
abstract public function get_grade_for_user(stdClass $gradeduser, stdClass $grader): ?stdClass;
/**
* Returns the grade that should be displayed to the user.
*
* The grade does not necessarily return a float value, this method takes grade settings into considering so
* the correct value be shown, eg. a float vs a letter.
*
* @param stdClass $gradeduser
* @param stdClass $grader
* @return stdClass|null
*/
public function get_formatted_grade_for_user(stdClass $gradeduser, stdClass $grader): ?stdClass {
global $DB;
if ($grade = $this->get_grade_for_user($gradeduser, $grader)) {
$gradeitem = $this->get_grade_item();
if (!$this->is_using_scale()) {
$grade->grade = !is_null($grade->grade) ? (float)$grade->grade : null; // Cast non-null values, keeping nulls.
$grade->usergrade = grade_format_gradevalue($grade->grade, $gradeitem);
$grade->maxgrade = format_float($gradeitem->grademax, $gradeitem->get_decimals());
// If displaying the raw grade, also display the total value.
if ($gradeitem->get_displaytype() == GRADE_DISPLAY_TYPE_REAL) {
$grade->usergrade .= ' / ' . $grade->maxgrade;
}
} else {
$grade->usergrade = '-';
if ($scale = $DB->get_record('scale', ['id' => $gradeitem->scaleid])) {
$options = make_menu_from_list($scale->scale);
$gradeint = (int) $grade->grade;
if (isset($options[$gradeint])) {
$grade->usergrade = $options[$gradeint];
}
}
$grade->maxgrade = format_float($gradeitem->grademax, $gradeitem->get_decimals());
}
return $grade;
}
return null;
}
/**
* Get the grade status for the specified user.
* If the user has a grade as defined by the implementor return true else return false.
*
* @param stdClass $gradeduser The user being graded
* @return bool The grade status
*/
abstract public function user_has_grade(stdClass $gradeduser): bool;
/**
* Get grades for all users for the specified gradeitem.
*
* @return stdClass[] The grades
*/
abstract public function get_all_grades(): array;
/**
* Get the grade item instance id.
*
* This is typically the cmid in the case of an activity, and relates to the iteminstance field in the grade_items
* table.
*
* @return int
*/
abstract public function get_grade_instance_id(): int;
/**
* Get the core grade item from the current component grade item.
* This is mainly used to access the max grade for a gradeitem
*
* @return \grade_item The grade item
*/
public function get_grade_item(): \grade_item {
global $CFG;
require_once("{$CFG->libdir}/gradelib.php");
[$itemtype, $itemmodule] = \core_component::normalize_component($this->component);
$gradeitem = \grade_item::fetch([
'itemtype' => $itemtype,
'itemmodule' => $itemmodule,
'itemnumber' => $this->itemnumber,
'iteminstance' => $this->get_grade_instance_id(),
]);
return $gradeitem;
}
/**
* Create or update the grade.
*
* @param stdClass $grade
* @return bool Success
*/
abstract protected function store_grade(stdClass $grade): bool;
/**
* Create or update the grade.
*
* @param stdClass $gradeduser The user being graded
* @param stdClass $grader The user who is grading
* @param stdClass $formdata The data submitted
* @return bool Success
*/
public function store_grade_from_formdata(stdClass $gradeduser, stdClass $grader, stdClass $formdata): bool {
// Require gradelib for grade_floatval.
require_once(__DIR__ . '/../../lib/gradelib.php');
$grade = $this->get_grade_for_user($gradeduser, $grader);
if ($this->is_using_advanced_grading()) {
$instanceid = $formdata->instanceid;
$gradinginstance = $this->get_advanced_grading_instance($grader, $grade, (int) $instanceid);
$grade->grade = $gradinginstance->submit_and_get_grade($formdata->advancedgrading, $grade->id);
if ($grade->grade == -1) {
// In advanced grading, a value of -1 means no data.
return false;
}
} else {
// Handle the case when grade is set to No Grade.
if (isset($formdata->grade)) {
$grade->grade = grade_floatval(unformat_float($formdata->grade));
}
}
return $this->store_grade($grade);
}
/**
* Get the advanced grading instance for the specified grade entry.
*
* @param stdClass $grader The user who is grading
* @param stdClass $grade The row from the grade table.
* @param int $instanceid The instanceid of the advanced grading form
* @return gradingform_instance
*/
public function get_advanced_grading_instance(stdClass $grader, stdClass $grade, int $instanceid = null): ?gradingform_instance {
$controller = $this->get_advanced_grading_controller($this->itemname);
if (empty($controller)) {
// Advanced grading not enabeld for this item.
return null;
}
if (!$controller->is_form_available()) {
// The form is not available for this item.
return null;
}
// Fetch the instance for the specified graderid/itemid.
$gradinginstance = $controller->fetch_instance(
(int) $grader->id,
(int) $grade->id,
$instanceid
);
// Set the allowed grade range.
$gradinginstance->get_controller()->set_grade_range(
$this->get_grade_menu(),
$this->allow_decimals()
);
return $gradinginstance;
}
/**
* Sends a notification about the item being graded for the student.
*
* @param stdClass $gradeduser The user being graded
* @param stdClass $grader The user who is grading
*/
public function send_student_notification(stdClass $gradeduser, stdClass $grader): void {
$contextname = $this->context->get_context_name();
$eventdata = new \core\message\message();
$eventdata->courseid = $this->context->get_course_context()->instanceid;
$eventdata->component = 'moodle';
$eventdata->name = 'gradenotifications';
$eventdata->userfrom = $grader;
$eventdata->userto = $gradeduser;
$eventdata->subject = get_string('gradenotificationsubject', 'grades');
$eventdata->fullmessage = get_string('gradenotificationmessage', 'grades', $contextname);
$eventdata->contexturl = $this->context->get_url();
$eventdata->contexturlname = $contextname;
$eventdata->fullmessageformat = FORMAT_HTML;
$eventdata->fullmessagehtml = '';
$eventdata->smallmessage = '';
$eventdata->notification = 1;
message_send($eventdata);
}
}
+226
View File
@@ -0,0 +1,226 @@
<?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/>.
/**
* Helper class to fetch information about component grade items.
*
* @package core_grades
* @copyright Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace core_grades;
use code_grades\local\gradeitem\itemnumber_mapping;
use code_grades\local\gradeitem\advancedgrading_mapping;
/**
* Helper class to fetch information about component grade items.
*
* @package core_grades
* @copyright Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class component_gradeitems {
/**
* Get the gradeitems classname for the specific component.
*
* @param string $component The component to fetch the classname for
* @return string The composed classname
*/
protected static function get_component_classname(string $component): string {
return "{$component}\\grades\gradeitems";
}
/**
* Get the grade itemnumber mapping for a component.
*
* @param string $component The component that the grade item belongs to
* @return array
*/
public static function get_itemname_mapping_for_component(string $component): array {
$classname = "{$component}\\grades\gradeitems";
if (!class_exists($classname)) {
return [
0 => '',
];
}
if (!is_subclass_of($classname, 'core_grades\local\gradeitem\itemnumber_mapping')) {
throw new \coding_exception("The {$classname} class does not implement " . itemnumber_mapping::class);
}
return $classname::get_itemname_mapping_for_component();
}
/**
* Whether the named grading item exists.
*
* @param string $component
* @param string $itemname
* @return bool
*/
public static function is_valid_itemname(string $component, string $itemname): bool {
$items = self::get_itemname_mapping_for_component($component);
return array_search($itemname, $items) !== false;
}
/**
* Check whether the component class defines the advanced grading items.
*
* @param string $component The component to check
* @return bool
*/
public static function defines_advancedgrading_itemnames_for_component(string $component): bool {
return is_subclass_of(self::get_component_classname($component), 'core_grades\local\gradeitem\advancedgrading_mapping');
}
/**
* Get the list of advanced grading item names for the named component.
*
* @param string $component
* @return array
*/
public static function get_advancedgrading_itemnames_for_component(string $component): array {
$classname = self::get_component_classname($component);
if (!self::defines_advancedgrading_itemnames_for_component($component)) {
throw new \coding_exception("The {$classname} class does not implement " . advancedgrading_mapping::class);
}
return $classname::get_advancedgrading_itemnames();
}
/**
* Whether the named grading item name supports advanced grading.
*
* @param string $component
* @param string $itemname
* @return bool
*/
public static function is_advancedgrading_itemname(string $component, string $itemname): bool {
$gradingareas = self::get_advancedgrading_itemnames_for_component($component);
return array_search($itemname, $gradingareas) !== false;
}
/**
* Get the suffixed field name for an activity field mapped from its itemnumber.
*
* For legacy reasons, the first itemnumber has no suffix on field names.
*
* @param string $component The component that the grade item belongs to
* @param int $itemnumber The grade itemnumber
* @param string $fieldname The name of the field to be rewritten
* @return string The translated field name
*/
public static function get_field_name_for_itemnumber(string $component, int $itemnumber, string $fieldname): string {
$classname = "{$component}\grades\gradeitems";
if (class_exists($classname) && is_subclass_of($classname, 'core_grades\local\gradeitem\fieldname_mapping')) {
$fieldname = $classname::get_field_name_for_itemnumber($component, $itemnumber, $fieldname);
} else {
$itemname = static::get_itemname_from_itemnumber($component, $itemnumber);
if ($itemname) {
$fieldname .= '_' . $itemname;
}
}
return $fieldname;
}
/**
* Get the suffixed field name for an activity field mapped from its itemnumber.
*
* For legacy reasons, the first itemnumber has no suffix on field names.
*
* @param string $component The component that the grade item belongs to
* @param string $itemname The grade itemname
* @param string $fieldname The name of the field to be rewritten
* @return string The translated field name
*/
public static function get_field_name_for_itemname(string $component, string $itemname, string $fieldname): string {
if (empty($itemname)) {
return $fieldname;
}
$itemnumber = static::get_itemnumber_from_itemname($component, $itemname);
if ($itemnumber > 0) {
return "{$fieldname}_{$itemname}";
}
return $fieldname;
}
/**
* Get the itemname for an itemnumber.
*
* For legacy compatability when the itemnumber is 0, the itemname will always be empty.
*
* @param string $component The component that the grade item belongs to
* @param int $itemnumber The grade itemnumber
* @return int The grade itemnumber of the itemname
*/
public static function get_itemname_from_itemnumber(string $component, int $itemnumber): string {
if ($itemnumber === 0) {
return '';
}
$mappings = self::get_itemname_mapping_for_component($component);
if (isset($mappings[$itemnumber])) {
return $mappings[$itemnumber];
}
if ($itemnumber >= 1000) {
// An itemnumber >= 1000 belongs to an outcome.
return '';
}
throw new \coding_exception("Unknown itemnumber mapping for {$itemnumber} in {$component}");
}
/**
* Get the itemnumber for a item name.
*
* For legacy compatability when the itemname is empty, the itemnumber will always be 0.
*
* @param string $component The component that the grade item belongs to
* @param string $itemname The grade itemname
* @return int The grade itemname of the itemnumber
*/
public static function get_itemnumber_from_itemname(string $component, string $itemname): int {
if (empty($itemname)) {
return 0;
}
$mappings = self::get_itemname_mapping_for_component($component);
$flipped = array_flip($mappings);
if (isset($flipped[$itemname])) {
return $flipped[$itemname];
}
throw new \coding_exception("Unknown itemnumber mapping for {$itemname} in {$component}");
}
}
+240
View File
@@ -0,0 +1,240 @@
<?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\external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
defined('MOODLE_INTERNAL') || die;
require_once("$CFG->libdir/gradelib.php");
require_once("$CFG->dirroot/grade/edit/tree/lib.php");
/**
* Create gradecategories webservice.
*
* @package core_grades
* @copyright 2021 Peter Burnett <peterburnett@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.11
*/
class create_gradecategories extends external_api {
/**
* Returns description of method parameters
*
* @return external_function_parameters
* @since Moodle 3.11
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters(
[
'courseid' => new external_value(PARAM_INT, 'id of course', VALUE_REQUIRED),
'categories' => new external_multiple_structure(
new external_single_structure([
'fullname' => new external_value(PARAM_TEXT, 'fullname of category', VALUE_REQUIRED),
'options' => new external_single_structure([
'aggregation' => new external_value(PARAM_INT, 'aggregation method', VALUE_OPTIONAL),
'aggregateonlygraded' => new external_value(PARAM_BOOL, 'exclude empty grades', VALUE_OPTIONAL),
'aggregateoutcomes' => new external_value(PARAM_BOOL, 'aggregate outcomes', VALUE_OPTIONAL),
'droplow' => new external_value(PARAM_INT, 'drop low grades', VALUE_OPTIONAL),
'itemname' => new external_value(PARAM_TEXT, 'the category total name', VALUE_OPTIONAL),
'iteminfo' => new external_value(PARAM_TEXT, 'the category iteminfo', VALUE_OPTIONAL),
'idnumber' => new external_value(PARAM_TEXT, 'the category idnumber', VALUE_OPTIONAL),
'gradetype' => new external_value(PARAM_INT, 'the grade type', VALUE_OPTIONAL),
'grademax' => new external_value(PARAM_INT, 'the grade max', VALUE_OPTIONAL),
'grademin' => new external_value(PARAM_INT, 'the grade min', VALUE_OPTIONAL),
'gradepass' => new external_value(PARAM_INT, 'the grade to pass', VALUE_OPTIONAL),
'display' => new external_value(PARAM_INT, 'the display type', VALUE_OPTIONAL),
'decimals' => new external_value(PARAM_INT, 'the decimal count', VALUE_OPTIONAL),
'hiddenuntil' => new external_value(PARAM_INT, 'grades hidden until', VALUE_OPTIONAL),
'locktime' => new external_value(PARAM_INT, 'lock grades after', VALUE_OPTIONAL),
'weightoverride' => new external_value(PARAM_BOOL, 'weight adjusted', VALUE_OPTIONAL),
'aggregationcoef2' => new external_value(PARAM_RAW, 'weight coefficient', VALUE_OPTIONAL),
'parentcategoryid' => new external_value(PARAM_INT, 'The parent category id', VALUE_OPTIONAL),
'parentcategoryidnumber' => new external_value(PARAM_TEXT,
'the parent category idnumber', VALUE_OPTIONAL),
], 'optional category data', VALUE_DEFAULT, []),
], 'Category to create', VALUE_REQUIRED)
, 'Categories to create', VALUE_REQUIRED)
]
);
}
/**
* Creates gradecategories inside of the specified course.
*
* @param int $courseid the courseid to create the gradecategory in.
* @param array $categories the categories to create.
* @return array array of created categoryids and warnings.
* @since Moodle 3.11
*/
public static function execute(int $courseid, array $categories): array {
$params = self::validate_parameters(self::execute_parameters(),
['courseid' => $courseid, 'categories' => $categories]);
// Now params are validated, update the references.
$courseid = $params['courseid'];
$categories = $params['categories'];
// Check that the context and permissions are OK.
$context = \context_course::instance($courseid);
self::validate_context($context);
require_capability('moodle/grade:manage', $context);
return self::create_gradecategories_from_data($courseid, $categories);
}
/**
* Returns description of method result value
*
* @return external_single_structure
* @since Moodle 3.11
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'categoryids' => new external_multiple_structure(
new external_value(PARAM_INT, 'created cateogry ID')
),
'warnings' => new external_warnings(),
]);
}
/**
* Takes an array of categories and creates the inside the category tree for the supplied courseid.
*
* @param int $courseid the courseid to create the categories inside of.
* @param array $categories the categories to create.
* @return array array of results and warnings.
*/
public static function create_gradecategories_from_data(int $courseid, array $categories): array {
global $CFG, $DB;
$defaultparentcat = \grade_category::fetch_course_category($courseid);
// Setup default data so WS call needs to contain only data to set.
// This is not done in the Parameters, so that the array of options can be optional.
$defaultdata = [
'aggregation' => grade_get_setting($courseid, 'aggregation', $CFG->grade_aggregation, true),
'aggregateonlygraded' => 1,
'aggregateoutcomes' => 0,
'droplow' => 0,
'grade_item_itemname' => '',
'grade_item_iteminfo' => '',
'grade_item_idnumber' => '',
'grade_item_gradetype' => GRADE_TYPE_VALUE,
'grade_item_grademax' => 100,
'grade_item_grademin' => 1,
'grade_item_gradepass' => 1,
'grade_item_display' => GRADE_DISPLAY_TYPE_DEFAULT,
// Hack. This must be -2 to use the default setting.
'grade_item_decimals' => -2,
'grade_item_hiddenuntil' => 0,
'grade_item_locktime' => 0,
'grade_item_weightoverride' => 0,
'grade_item_aggregationcoef2' => 0,
'parentcategory' => $defaultparentcat->id
];
// Most of the data items need boilerplate prepended. These are the exceptions.
$ignorekeys = [
'aggregation',
'aggregateonlygraded',
'aggregateoutcomes',
'droplow',
'parentcategoryid',
'parentcategoryidnumber'
];
$createdcats = [];
foreach ($categories as $category) {
// Setup default data so WS call needs to contain only data to set.
// This is not done in the Parameters, so that the array of options can be optional.
$data = $defaultdata;
$data['fullname'] = $category['fullname'];
foreach ($category['options'] as $key => $value) {
if (!in_array($key, $ignorekeys)) {
$fullkey = 'grade_item_' . $key;
$data[$fullkey] = $value;
} else {
$data[$key] = $value;
}
}
// Handle parent category special case.
// This figures the parent category id from the provided id OR idnumber.
if (array_key_exists('parentcategoryid', $category['options']) && $parentcat = $DB->get_record('grade_categories',
['id' => $category['options']['parentcategoryid'], 'courseid' => $courseid])) {
$data['parentcategory'] = $parentcat->id;
} else if (array_key_exists('parentcategoryidnumber', $category['options']) &&
$parentcatgradeitem = $DB->get_record('grade_items', [
'itemtype' => 'category',
'courseid' => $courseid,
'idnumber' => $category['options']['parentcategoryidnumber']
], '*', IGNORE_MULTIPLE)) {
if ($parentcat = $DB->get_record('grade_categories',
['courseid' => $courseid, 'id' => $parentcatgradeitem->iteminstance])) {
$data['parentcategory'] = $parentcat->id;
}
}
// Create new gradecategory item.
$gradecategory = new \grade_category(['courseid' => $courseid], false);
$gradecategory->apply_default_settings();
$gradecategory->apply_forced_settings();
// Data Validation.
if (array_key_exists('grade_item_gradetype', $data) and $data['grade_item_gradetype'] == GRADE_TYPE_SCALE) {
if (empty($data['grade_item_scaleid'])) {
$warnings[] = ['item' => 'scaleid', 'warningcode' => 'invalidscale',
'message' => get_string('missingscale', 'grades')];
}
}
if (array_key_exists('grade_item_grademin', $data) and array_key_exists('grade_item_grademax', $data)) {
if (($data['grade_item_grademax'] != 0 OR $data['grade_item_grademin'] != 0) AND
($data['grade_item_grademax'] == $data['grade_item_grademin'] OR
$data['grade_item_grademax'] < $data['grade_item_grademin'])) {
$warnings[] = ['item' => 'grademax', 'warningcode' => 'invalidgrade',
'message' => get_string('incorrectminmax', 'grades')];
}
}
if (!empty($warnings)) {
return ['categoryids' => [], 'warnings' => $warnings];
}
// Now call the update function with data. Transactioned so the gradebook isn't broken on bad data.
// This is done per-category so that children can correctly discover the parent categories.
try {
$transaction = $DB->start_delegated_transaction();
\grade_edit_tree::update_gradecategory($gradecategory, (object) $data);
$transaction->allow_commit();
$createdcats[] = $gradecategory->id;
} catch (\Exception $e) {
// If the submitted data was broken for any reason.
$warnings['database'] = $e->getMessage();
$transaction->rollback($e);
return ['warnings' => $warnings];
}
}
return['categoryids' => $createdcats, 'warnings' => []];
}
}
@@ -0,0 +1,182 @@
<?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\external;
use core_external\external_api;
use core_external\external_description;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use core_external\restricted_context_exception;
use moodle_url;
use core_user;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot.'/grade/lib.php');
/**
* Get the enrolled users within and map some fields to the returned array of user objects.
*
* @package core_grades
* @copyright 2022 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.1
* @deprecated
*/
class get_enrolled_users_for_search_widget extends external_api {
/**
* Returns description of method parameters.
*
* @return external_function_parameters
* @deprecated since 4.2
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters (
[
'courseid' => new external_value(PARAM_INT, 'Course Id', VALUE_REQUIRED),
'actionbaseurl' => new external_value(PARAM_URL, 'The base URL for the user option', VALUE_REQUIRED),
'groupid' => new external_value(PARAM_INT, 'Group Id', VALUE_DEFAULT, 0)
]
);
}
/**
* Given a course ID find the enrolled users within and map some fields to the returned array of user objects.
*
* @param int $courseid
* @param string $actionbaseurl The base URL for the user option.
* @param int|null $groupid
* @return array Users and warnings to pass back to the calling widget.
* @throws coding_exception
* @throws invalid_parameter_exception
* @throws moodle_exception
* @throws restricted_context_exception
* @deprecated since 4.2
*/
public static function execute(int $courseid, string $actionbaseurl, ?int $groupid = 0): array {
global $DB, $PAGE;
$params = self::validate_parameters(
self::execute_parameters(),
[
'courseid' => $courseid,
'actionbaseurl' => $actionbaseurl,
'groupid' => $groupid
]
);
$warnings = [];
$coursecontext = \context_course::instance($params['courseid']);
parent::validate_context($coursecontext);
require_capability('moodle/course:viewparticipants', $coursecontext);
$course = $DB->get_record('course', ['id' => $params['courseid']]);
// Create a graded_users_iterator because it will properly check the groups etc.
$defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol);
$showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol);
$showonlyactiveenrol = $showonlyactiveenrol || !has_capability('moodle/course:viewsuspendedusers', $coursecontext);
$gui = new \graded_users_iterator($course, null, $params['groupid']);
$gui->require_active_enrolment($showonlyactiveenrol);
$gui->init();
$users = [];
$userfieldsapi = \core_user\fields::for_identity($coursecontext, false)->with_userpic();
$extrauserfields = $userfieldsapi->get_required_fields([\core_user\fields::PURPOSE_IDENTITY]);
while ($userdata = $gui->next_user()) {
$guiuser = $userdata->user;
$user = new \stdClass();
$user->fullname = fullname($guiuser);
foreach (\core_user\fields::get_name_fields() as $field) {
$user->$field = $guiuser->$field ?? null;
}
$user->id = $guiuser->id;
$user->url = (new moodle_url($actionbaseurl, ['id' => $courseid, 'userid' => $guiuser->id]))->out(false);
$userpicture = new \user_picture($guiuser);
$userpicture->size = 1;
$user->profileimage = $userpicture->get_url($PAGE)->out(false);
foreach ($extrauserfields as $field) {
$user->$field = $userdata->user->$field ?? null;
}
$user->active = false;
$users[] = $user;
}
$gui->close();
return [
'users' => $users,
'warnings' => $warnings,
];
}
/**
* Returns description of method result value.
*
* @return external_single_structure
* @deprecated since 4.2
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'users' => new external_multiple_structure(self::user_description()),
'warnings' => new external_warnings(),
]);
}
/**
* Create user return value description.
*
* @return external_description
*/
public static function user_description(): external_description {
$userfields = [
'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'),
'profileimage' => new external_value(
PARAM_URL,
'The location of the users larger image',
VALUE_OPTIONAL
),
'url' => new external_value(
PARAM_URL,
'The link to the user report',
VALUE_OPTIONAL
),
'fullname' => new external_value(PARAM_TEXT, 'The full name of the user', VALUE_OPTIONAL),
'email' => new external_value(
core_user::get_property_type('email'),
'An email address - allow email as root@localhost',
VALUE_OPTIONAL),
'active' => new external_value(PARAM_BOOL, 'Are we currently on this item?', VALUE_REQUIRED)
];
return new external_single_structure($userfields);
}
/**
* Mark the function as deprecated.
* @return bool
*/
public static function execute_is_deprecated() {
return true;
}
}
@@ -0,0 +1,138 @@
<?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\external;
use core_user_external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use core_external\restricted_context_exception;
use user_picture;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot.'/grade/lib.php');
require_once($CFG->dirroot .'/user/externallib.php');
/**
* Get the enrolled users within and map some fields to the returned array of user objects.
*
* @package core_grades
* @copyright 2022 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 4.2
*/
class get_enrolled_users_for_selector extends external_api {
/**
* Returns description of method parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters (
[
'courseid' => new external_value(PARAM_INT, 'Course Id', VALUE_REQUIRED),
'groupid' => new external_value(PARAM_INT, 'Group Id', VALUE_DEFAULT, 0)
]
);
}
/**
* Given a course ID find the enrolled users within and map some fields to the returned array of user objects.
*
* @param int $courseid
* @param int|null $groupid
* @return array Users and warnings to pass back to the calling widget.
* @throws coding_exception
* @throws invalid_parameter_exception
* @throws moodle_exception
* @throws restricted_context_exception
*/
public static function execute(int $courseid, ?int $groupid = 0): array {
global $DB, $PAGE;
$params = self::validate_parameters(
self::execute_parameters(),
[
'courseid' => $courseid,
'groupid' => $groupid
]
);
$warnings = [];
$coursecontext = \context_course::instance($params['courseid']);
parent::validate_context($coursecontext);
require_capability('moodle/course:viewparticipants', $coursecontext);
$course = $DB->get_record('course', ['id' => $params['courseid']]);
// Create a graded_users_iterator because it will properly check the groups etc.
$defaultgradeshowactiveenrol = !empty($CFG->grade_report_showonlyactiveenrol);
$showonlyactiveenrol = get_user_preferences('grade_report_showonlyactiveenrol', $defaultgradeshowactiveenrol);
$showonlyactiveenrol = $showonlyactiveenrol || !has_capability('moodle/course:viewsuspendedusers', $coursecontext);
$gui = new \graded_users_iterator($course, null, $params['groupid']);
$gui->require_active_enrolment($showonlyactiveenrol);
$gui->init();
$users = [];
$userfieldsapi = \core_user\fields::for_identity($coursecontext, false)->with_userpic();
$extrauserfields = $userfieldsapi->get_required_fields([\core_user\fields::PURPOSE_IDENTITY]);
while ($userdata = $gui->next_user()) {
$userforselector = new \stdClass();
$userforselector->id = $userdata->user->id;
$userforselector->fullname = fullname($userdata->user);
foreach (\core_user\fields::get_name_fields() as $field) {
$userforselector->$field = $userdata->user->$field ?? null;
}
$userpicture = new user_picture($userdata->user);
$userpicture->size = 1;
$userforselector->profileimageurl = $userpicture->get_url($PAGE)->out(false);
$userpicture->size = 0; // Size f2.
$userforselector->profileimageurlsmall = $userpicture->get_url($PAGE)->out(false);
foreach ($extrauserfields as $field) {
$userforselector->$field = $userdata->user->$field ?? null;
}
$users[] = $userforselector;
}
$gui->close();
return [
'users' => $users,
'warnings' => $warnings,
];
}
/**
* Returns description of method result value.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'users' => new external_multiple_structure(core_user_external::user_description()),
'warnings' => new external_warnings(),
]);
}
}
+114
View File
@@ -0,0 +1,114 @@
<?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\external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use invalid_parameter_exception;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot.'/grade/lib.php');
/**
* Web service to fetch students feedback for a grade item.
*
* @package core_grades
* @copyright 2023 Kevin Percy <kevin.percy@moodle.com>
* @category external
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class get_feedback extends external_api {
/**
* Returns description of method parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters (
[
'courseid' => new external_value(PARAM_INT, 'Course ID', VALUE_REQUIRED),
'userid' => new external_value(PARAM_INT, 'User ID', VALUE_REQUIRED),
'itemid' => new external_value(PARAM_INT, 'Grade Item ID', VALUE_REQUIRED)
]
);
}
/**
* Given a user ID and grade item ID, return feedback and user details.
*
* @param int $courseid The course ID.
* @param int $userid
* @param int $itemid
* @return array Feedback and user details
*/
public static function execute(int $courseid, int $userid, int $itemid): array {
global $OUTPUT, $CFG;
$params = self::validate_parameters(
self::execute_parameters(),
[
'courseid' => $courseid,
'userid' => $userid,
'itemid' => $itemid
]
);
$context = \context_course::instance($courseid);
parent::validate_context($context);
require_capability('gradereport/grader:view', $context);
$gtree = new \grade_tree($params['courseid'], false, false, null, !$CFG->enableoutcomes);
$gradeitem = $gtree->get_item($params['itemid']);
// If Item ID is not part of Course ID, $gradeitem will be set to false.
if ($gradeitem === false) {
throw new invalid_parameter_exception('Course ID and item ID mismatch');
}
$grade = $gradeitem->get_grade($params['userid'], false);
$user = \core_user::get_user($params['userid']);
$extrafields = \core_user\fields::get_identity_fields($context);
return [
'feedbacktext' => $grade->feedback,
'title' => $gradeitem->get_name(true),
'fullname' => fullname($user),
'picture' => $OUTPUT->user_picture($user, ['size' => 50, 'link' => false]),
'additionalfield' => empty($extrafields) ? '' : $user->{$extrafields[0]},
];
}
/**
* Describes the return structure.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'feedbacktext' => new external_value(PARAM_RAW, 'The full feedback text'),
'title' => new external_value(PARAM_TEXT, 'Title of the grade item that the feedback is for'),
'fullname' => new external_value(PARAM_TEXT, 'Students name'),
'picture' => new external_value(PARAM_RAW, 'Students picture'),
'additionalfield' => new external_value(PARAM_RAW, 'Additional field for the user (email or ID number, for example)'),
]);
}
}
+122
View File
@@ -0,0 +1,122 @@
<?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\external;
use coding_exception;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use core_external\restricted_context_exception;
use core_user_external;
use invalid_parameter_exception;
use moodle_exception;
use user_picture;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot . '/grade/lib.php');
require_once($CFG->dirroot . '/user/externallib.php');
/**
* Get the gradable users in a course.
*
* @package core_grades
* @copyright 2023 Ilya Tregubov <ilya.a.tregubov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class get_gradable_users extends external_api {
/**
* Returns description of method parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters (
[
'courseid' => new external_value(PARAM_INT, 'Course Id', VALUE_REQUIRED),
'groupid' => new external_value(PARAM_INT, 'Group Id', VALUE_DEFAULT, 0),
'onlyactive' => new external_value(PARAM_BOOL, 'Only active enrolment', VALUE_DEFAULT, false),
]
);
}
/**
* Given a course ID find the gradable users within a group.
*
* @param int $courseid Course ID
* @param int|null $groupid Group ID
* @param bool $onlyactive Whether we should only return active enrolments.
* @return array Users and warnings.
* @throws coding_exception
* @throws invalid_parameter_exception
* @throws moodle_exception
* @throws restricted_context_exception
*/
public static function execute(int $courseid, ?int $groupid = 0, bool $onlyactive = false): array {
global $DB, $PAGE;
$params = self::validate_parameters(
self::execute_parameters(),
[
'courseid' => $courseid,
'groupid' => $groupid,
'onlyactive' => $onlyactive,
]
);
$warnings = [];
$coursecontext = \context_course::instance($params['courseid']);
parent::validate_context($coursecontext);
require_capability('moodle/course:viewparticipants', $coursecontext);
$course = $DB->get_record('course', ['id' => $params['courseid']]);
$onlyactive = $onlyactive || !has_capability('moodle/course:viewsuspendedusers', $coursecontext);
$users = get_gradable_users($course->id, $params['groupid'], $onlyactive);
$users = array_map(function ($user) use ($PAGE) {
$user->fullname = fullname($user);
$userpicture = new user_picture($user);
$userpicture->size = 1;
$user->profileimageurlsmall = $userpicture->get_url($PAGE)->out(false);
$user->profileimageurl = $userpicture->get_url($PAGE)->out(false);
return $user;
}, $users);
sort($users);
return [
'users' => $users,
'warnings' => $warnings,
];
}
/**
* Returns description of method result value.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'users' => new external_multiple_structure(core_user_external::user_description()),
'warnings' => new external_warnings(),
]);
}
}
+121
View File
@@ -0,0 +1,121 @@
<?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\external;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_value;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->dirroot.'/grade/lib.php');
/**
* Web service to return the grade tree structure for a given course.
*
* @package core_grades
* @copyright 2023 Mihail Geshoski <mihail@moodle.com>
* @category external
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class get_grade_tree extends external_api {
/**
* Returns description of method parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters (
[
'courseid' => new external_value(PARAM_INT, 'Course ID', VALUE_REQUIRED)
]
);
}
/**
* Given a course ID, return the grade tree structure for that course.
*
* @param int $courseid The course ID.
* @return string JSON encoded data representing the course grade tree structure.
*/
public static function execute(int $courseid): string {
$params = self::validate_parameters(
self::execute_parameters(),
[
'courseid' => $courseid
]
);
$context = \context_course::instance($params['courseid']);
parent::validate_context($context);
// Make sure that the user has the capability to view the full grade tree in the course.
require_capability('moodle/grade:viewall', $context);
// Get the course grade category.
$coursegradecategory = \grade_category::fetch_course_category($params['courseid']);
$coursegradetree = self::generate_course_grade_tree($coursegradecategory);
return json_encode($coursegradetree);
}
/**
* Describes the return structure.
*
* @return external_value
*/
public static function execute_returns(): external_value {
return new external_value(PARAM_RAW, 'JSON encoded data representing the course grade tree structure.');
}
/**
* Recursively generates the course grade tree structure.
*
* @param \grade_category $gradecategory The course grade category.
* @return array The course grade tree structure.
*/
private static function generate_course_grade_tree(\grade_category $gradecategory): array {
$gradecategorydata = [
'id' => $gradecategory->id,
'name' => $gradecategory->get_name(),
'iscategory' => true,
'haschildcategories' => false
];
// Get the children of the grade category.
if ($gradecategorychildren = $gradecategory->get_children()) {
foreach ($gradecategorychildren as $child) {
// If the child is a grade category, recursively generate the grade tree structure for that category.
if ($child['object'] instanceof \grade_category) {
$gradecategorydata['haschildcategories'] = true;
$gradecategorydata['children'][] = self::generate_course_grade_tree($child['object']);
} else { // Otherwise, add the grade item to the grade tree structure.
$gradecategorydata['children'][] = [
'id' => $child['object']->id,
'name' => $child['object']->get_name(),
'iscategory' => false,
'children' => null
];
}
}
} else { // If the grade category has no children, set the children property to null.
$gradecategorydata['children'] = null;
}
return $gradecategorydata;
}
}
+106
View File
@@ -0,0 +1,106 @@
<?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\external;
defined('MOODLE_INTERNAL') || die;
use context_course;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use core_external\restricted_context_exception;
use grade_item;
require_once($CFG->libdir . '/gradelib.php');
/**
* External grade get gradeitems API implementation
*
* @package core_grades
* @copyright 2023 Mathew May <mathew.solutions>
* @category external
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class get_gradeitems extends external_api {
/**
* Returns description of method parameters.
*
* @return external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters (
[
'courseid' => new external_value(PARAM_INT, 'Course ID', VALUE_REQUIRED)
]
);
}
/**
* Given a course ID find the grading objects and return their names & IDs.
*
* @param int $courseid
* @return array
* @throws restricted_context_exception
* @throws \invalid_parameter_exception
*/
public static function execute(int $courseid): array {
$params = self::validate_parameters(
self::execute_parameters(),
[
'courseid' => $courseid
]
);
$warnings = [];
$context = context_course::instance($params['courseid']);
parent::validate_context($context);
$allgradeitems = grade_item::fetch_all(['courseid' => $params['courseid']]);
$gradeitems = array_filter($allgradeitems, function($item) {
$item->itemname = $item->get_name();
$item->category = $item->get_parent_category()->get_name();
return $item->gradetype != GRADE_TYPE_NONE && !$item->is_category_item() && !$item->is_course_item();
});
return [
'gradeItems' => $gradeitems,
'warnings' => $warnings,
];
}
/**
* Returns description of what gradeitems fetch should return.
*
* @return external_single_structure
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'gradeItems' => new external_multiple_structure(
new external_single_structure([
'id' => new external_value(PARAM_ALPHANUM, 'An ID for the grade item', VALUE_REQUIRED),
'itemname' => new external_value(PARAM_RAW, 'The full name of the grade item', VALUE_REQUIRED),
'category' => new external_value(PARAM_TEXT, 'The grade category of the grade item', VALUE_OPTIONAL),
])
),
'warnings' => new external_warnings(),
]);
}
}
+159
View File
@@ -0,0 +1,159 @@
<?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\external;
use context_course;
use core_external\external_api;
use core_external\external_description;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
/**
* External group report API implementation
*
* @package core_grades
* @copyright 2022 Mathew May <mathew.solutions>
* @category external
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @deprecated
*/
class get_groups_for_search_widget extends external_api {
/**
* Returns description of method parameters.
*
* @return external_function_parameters
* @deprecated since 4.2
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters (
[
'courseid' => new external_value(PARAM_INT, 'Course Id', VALUE_REQUIRED),
'actionbaseurl' => new external_value(PARAM_URL, 'The base URL for the group action', VALUE_REQUIRED)
]
);
}
/**
* Given a course ID find the existing user groups and map some fields to the returned array of group objects.
*
* @param int $courseid
* @param string $actionbaseurl The base URL for the group action.
* @return array Groups and warnings to pass back to the calling widget.
* @deprecated since 4.2
*/
public static function execute(int $courseid, string $actionbaseurl): array {
global $DB, $USER, $COURSE;
$params = self::validate_parameters(
self::execute_parameters(),
[
'courseid' => $courseid,
'actionbaseurl' => $actionbaseurl
]
);
$warnings = [];
$context = context_course::instance($params['courseid']);
parent::validate_context($context);
$mappedgroups = [];
$course = $DB->get_record('course', ['id' => $params['courseid']]);
// Initialise the grade tracking object.
if ($groupmode = $course->groupmode) {
$aag = has_capability('moodle/site:accessallgroups', $context);
$usergroups = [];
$groupuserid = 0;
if ($groupmode == VISIBLEGROUPS || $aag) {
// Get user's own groups and put to the top.
$usergroups = groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid);
} else {
$groupuserid = $USER->id;
}
$allowedgroups = groups_get_all_groups($course->id, $groupuserid, $course->defaultgroupingid);
$allgroups = array_merge($allowedgroups, $usergroups);
// Filter out any duplicate groups.
$groupsmenu = array_intersect_key($allgroups, array_unique(array_column($allgroups, 'name')));
if (!$allowedgroups || $groupmode == VISIBLEGROUPS || $aag) {
array_unshift($groupsmenu, (object) [
'id' => 0,
'name' => get_string('allparticipants'),
]);
}
$mappedgroups = array_map(function($group) use ($COURSE, $actionbaseurl, $context) {
$url = new \moodle_url($actionbaseurl, [
'id' => $COURSE->id,
'group' => $group->id
]);
return (object) [
'id' => $group->id,
'name' => format_string($group->name, true, ['context' => $context]),
'url' => $url->out(false),
'active' => false
];
}, $groupsmenu);
}
return [
'groups' => $mappedgroups,
'warnings' => $warnings,
];
}
/**
* Returns description of what the group search for the widget should return.
*
* @return external_single_structure
* @deprecated since 4.2
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'groups' => new external_multiple_structure(self::group_description()),
'warnings' => new external_warnings(),
]);
}
/**
* Create group return value description.
*
* @return external_description
*/
public static function group_description(): external_description {
$groupfields = [
'id' => new external_value(PARAM_ALPHANUM, 'An ID for the group', VALUE_REQUIRED),
'url' => new external_value(PARAM_URL, 'The link that applies the group action', VALUE_REQUIRED),
'name' => new external_value(PARAM_TEXT, 'The full name of the group', VALUE_REQUIRED),
'active' => new external_value(PARAM_BOOL, 'Are we currently on this item?', VALUE_REQUIRED)
];
return new external_single_structure($groupfields);
}
/**
* Mark the function as deprecated.
* @return bool
*/
public static function execute_is_deprecated() {
return true;
}
}
+667
View File
@@ -0,0 +1,667 @@
<?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\form;
use context;
use context_course;
use core_form\dynamic_form;
use grade_category;
use grade_edit_tree;
use grade_helper;
use grade_item;
use grade_plugin_return;
use grade_scale;
use moodle_url;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/grade/lib.php');
require_once($CFG->dirroot . '/grade/edit/tree/lib.php');
/**
* Prints the add category gradebook form
*
* @copyright 2023 Ilya Tregubov <ilya@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package core_grades
*/
class add_category extends dynamic_form {
/** Grade plugin return tracking object.
* @var object $gpr
*/
public $gpr;
/** Available aggregations.
* @var array|null $aggregation_options
*/
private ?array $aggregation_options;
/**
* Helper function to grab the current grade category based on information within the form.
*
* @return array
* @throws \moodle_exception
*/
private function get_gradecategory(): array {
$courseid = $this->optional_param('courseid', null, PARAM_INT);
$id = $this->optional_param('category', null, PARAM_INT);
if ($gradecategory = grade_category::fetch(['id' => $id, 'courseid' => $courseid])) {
$gradecategory->apply_forced_settings();
$category = $gradecategory->get_record_data();
// Set parent.
$category->parentcategory = $gradecategory->parent;
$gradeitem = $gradecategory->load_grade_item();
// Normalize coef values if needed.
$parentcategory = $gradecategory->get_parent_category();
foreach ($gradeitem->get_record_data() as $key => $value) {
$category->{"grade_item_$key"} = $value;
}
$decimalpoints = $gradeitem->get_decimals();
$category->grade_item_grademax = format_float($category->grade_item_grademax, $decimalpoints);
$category->grade_item_grademin = format_float($category->grade_item_grademin, $decimalpoints);
$category->grade_item_gradepass = format_float($category->grade_item_gradepass, $decimalpoints);
$category->grade_item_multfactor = format_float($category->grade_item_multfactor, 4);
$category->grade_item_plusfactor = format_float($category->grade_item_plusfactor, 4);
$category->grade_item_aggregationcoef2 = format_float($category->grade_item_aggregationcoef2 * 100.0, 4);
if (isset($parentcategory)) {
if ($parentcategory->aggregation == GRADE_AGGREGATE_SUM ||
$parentcategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) {
$category->grade_item_aggregationcoef = $category->grade_item_aggregationcoef == 0 ? 0 : 1;
} else {
$category->grade_item_aggregationcoef = format_float($category->grade_item_aggregationcoef, 4);
}
}
// Check if the gradebook is frozen. This allows grades not be altered at all until a user verifies that they
// wish to update the grades.
$gradebookcalculationsfreeze = get_config('core', 'gradebook_calculations_freeze_' . $courseid);
// Stick with the original code if the grade book is frozen.
if ($gradebookcalculationsfreeze && (int)$gradebookcalculationsfreeze <= 20150627) {
if ($category->aggregation == GRADE_AGGREGATE_SUM) {
// Input fields for grademin and grademax are disabled for the "Natural" category,
// this means they will be ignored if user does not change aggregation method.
// But if user does change aggregation method the default values should be used.
$category->grademax = 100;
$category->grade_item_grademax = 100;
$category->grademin = 0;
$category->grade_item_grademin = 0;
}
} else {
if ($category->aggregation == GRADE_AGGREGATE_SUM && !$gradeitem->is_calculated()) {
// Input fields for grademin and grademax are disabled for the "Natural" category,
// this means they will be ignored if user does not change aggregation method.
// But if user does change aggregation method the default values should be used.
// This does not apply to calculated category totals.
$category->grademax = 100;
$category->grade_item_grademax = 100;
$category->grademin = 0;
$category->grade_item_grademin = 0;
}
}
} else {
$gradecategory = new grade_category(['courseid' => $courseid], false);
$gradecategory->apply_default_settings();
$gradecategory->apply_forced_settings();
$category = $gradecategory->get_record_data();
$gradeitem = new grade_item(['courseid' => $courseid, 'itemtype' => 'manual'], false);
foreach ($gradeitem->get_record_data() as $key => $value) {
$category->{"grade_item_$key"} = $value;
}
}
return [
'gradecategory' => $gradecategory,
'categoryitem' => $category,
'gradeitem' => $gradeitem
];
}
/**
* Form definition
*
* @return void
* @throws \coding_exception
* @throws \dml_exception
* @throws \moodle_exception
*/
protected function definition(): void {
global $CFG, $OUTPUT, $COURSE;
$courseid = $this->optional_param('courseid', null, PARAM_INT);
$id = $this->optional_param('category', 0, PARAM_INT);
$gprplugin = $this->optional_param('gpr_plugin', '', PARAM_TEXT);
if ($gprplugin && ($gprplugin !== 'tree')) {
$this->gpr = new grade_plugin_return(['type' => 'report', 'plugin' => $gprplugin, 'courseid' => $courseid]);
} else {
$this->gpr = new grade_plugin_return(['type' => 'edit', 'plugin' => 'tree', 'courseid' => $courseid]);
}
$mform = $this->_form;
$this->aggregation_options = grade_helper::get_aggregation_strings();
$local = $this->get_gradecategory();
$category = $local['categoryitem'];
// Hidden elements.
$mform->addElement('hidden', 'id', 0);
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'courseid', $courseid);
$mform->setType('courseid', PARAM_INT);
$mform->addElement('hidden', 'category', $id);
$mform->setType('category', PARAM_INT);
// Visible elements.
$mform->addElement('text', 'fullname', get_string('categoryname', 'grades'));
$mform->setType('fullname', PARAM_TEXT);
$mform->addRule('fullname', null, 'required', null, 'client');
$mform->addElement('select', 'aggregation', get_string('aggregation', 'grades'), $this->aggregation_options);
$mform->addHelpButton('aggregation', 'aggregation', 'grades');
$mform->addElement('checkbox', 'aggregateonlygraded', get_string('aggregateonlygraded', 'grades'));
$mform->addHelpButton('aggregateonlygraded', 'aggregateonlygraded', 'grades');
if (empty($CFG->enableoutcomes)) {
$mform->addElement('hidden', 'aggregateoutcomes');
$mform->setType('aggregateoutcomes', PARAM_INT);
} else {
$mform->addElement('checkbox', 'aggregateoutcomes', get_string('aggregateoutcomes', 'grades'));
$mform->addHelpButton('aggregateoutcomes', 'aggregateoutcomes', 'grades');
}
$mform->addElement('text', 'keephigh', get_string('keephigh', 'grades'), 'size="3"');
$mform->setType('keephigh', PARAM_INT);
$mform->addHelpButton('keephigh', 'keephigh', 'grades');
$mform->addElement('text', 'droplow', get_string('droplow', 'grades'), 'size="3"');
$mform->setType('droplow', PARAM_INT);
$mform->addHelpButton('droplow', 'droplow', 'grades');
$mform->hideIf('droplow', 'keephigh', 'noteq', 0);
$mform->hideIf('keephigh', 'droplow', 'noteq', 0);
$mform->hideIf('droplow', 'keephigh', 'noteq', 0);
if (!empty($category->id)) {
$gradeitem = $local['gradeitem'];
// If grades exist set a message so the user knows why they can not alter the grade type or scale.
// We could never change the grade type for external items, so only need to show this for manual grade items.
if ($gradeitem->has_overridden_grades()) {
// Set a message so the user knows why the can not alter the grade type or scale.
if ($gradeitem->gradetype == GRADE_TYPE_SCALE) {
$gradesexistmsg = get_string('modgradecategorycantchangegradetyporscalemsg', 'grades');
} else {
$gradesexistmsg = get_string('modgradecategorycantchangegradetypemsg', 'grades');
}
$notification = new \core\output\notification($gradesexistmsg, \core\output\notification::NOTIFY_INFO);
$notification->set_show_closebutton(false);
$mform->addElement('static', 'gradesexistmsg', '', $OUTPUT->render($notification));
}
}
$options = [
GRADE_TYPE_NONE => get_string('typenone', 'grades'),
GRADE_TYPE_VALUE => get_string('typevalue', 'grades'),
GRADE_TYPE_SCALE => get_string('typescale', 'grades'),
GRADE_TYPE_TEXT => get_string('typetext', 'grades')
];
$mform->addElement('select', 'grade_item_gradetype', get_string('gradetype', 'grades'), $options);
$mform->addHelpButton('grade_item_gradetype', 'gradetype', 'grades');
$mform->setDefault('grade_item_gradetype', GRADE_TYPE_VALUE);
$mform->hideIf('grade_item_gradetype', 'aggregation', 'eq', GRADE_AGGREGATE_SUM);
$options = [0 => get_string('usenoscale', 'grades')];
if ($scales = grade_scale::fetch_all_local($COURSE->id)) {
foreach ($scales as $scale) {
$options[$scale->id] = $scale->get_name();
}
}
if ($scales = grade_scale::fetch_all_global()) {
foreach ($scales as $scale) {
$options[$scale->id] = $scale->get_name();
}
}
// Ugly BC hack - it was possible to use custom scale from other courses.
if (!empty($category->grade_item_scaleid) && !isset($options[$category->grade_item_scaleid])) {
if ($scale = grade_scale::fetch(['id' => $category->grade_item_scaleid])) {
$options[$scale->id] = $scale->get_name().' '.get_string('incorrectcustomscale', 'grades');
}
}
$mform->addElement('select', 'grade_item_scaleid', get_string('scale'), $options);
$mform->addHelpButton('grade_item_scaleid', 'typescale', 'grades');
$mform->hideIf('grade_item_scaleid', 'grade_item_gradetype', 'noteq', GRADE_TYPE_SCALE);
$mform->hideIf('grade_item_scaleid', 'aggregation', 'eq', GRADE_AGGREGATE_SUM);
$choices = [];
$choices[''] = get_string('choose');
$choices['no'] = get_string('no');
$choices['yes'] = get_string('yes');
$mform->addElement('select', 'grade_item_rescalegrades', get_string('modgradecategoryrescalegrades', 'grades'), $choices);
$mform->addHelpButton('grade_item_rescalegrades', 'modgradecategoryrescalegrades', 'grades');
$mform->hideIf('grade_item_rescalegrades', 'grade_item_gradetype', 'noteq', GRADE_TYPE_VALUE);
$mform->addElement('float', 'grade_item_grademax', get_string('grademax', 'grades'));
$mform->addHelpButton('grade_item_grademax', 'grademax', 'grades');
$mform->hideIf('grade_item_grademax', 'grade_item_gradetype', 'noteq', GRADE_TYPE_VALUE);
$mform->hideIf('grade_item_grademax', 'aggregation', 'eq', GRADE_AGGREGATE_SUM);
if ((bool) get_config('moodle', 'grade_report_showmin')) {
$mform->addElement('float', 'grade_item_grademin', get_string('grademin', 'grades'));
$mform->addHelpButton('grade_item_grademin', 'grademin', 'grades');
$mform->hideIf('grade_item_grademin', 'grade_item_gradetype', 'noteq', GRADE_TYPE_VALUE);
$mform->hideIf('grade_item_grademin', 'aggregation', 'eq', GRADE_AGGREGATE_SUM);
}
// Hiding.
// advcheckbox is not compatible with disabledIf!
$mform->addElement('checkbox', 'grade_item_hidden', get_string('hidden', 'grades'));
$mform->addHelpButton('grade_item_hidden', 'hidden', 'grades');
// Locking.
$mform->addElement('checkbox', 'grade_item_locked', get_string('locked', 'grades'));
$mform->addHelpButton('grade_item_locked', 'locked', 'grades');
$mform->addElement('advcheckbox', 'grade_item_weightoverride', get_string('adjustedweight', 'grades'));
$mform->addHelpButton('grade_item_weightoverride', 'weightoverride', 'grades');
$mform->addElement('float', 'grade_item_aggregationcoef2', get_string('weight', 'grades'));
$mform->addHelpButton('grade_item_aggregationcoef2', 'weight', 'grades');
$mform->hideIf('grade_item_aggregationcoef2', 'grade_item_weightoverride');
$options = [];
$default = -1;
$categories = grade_category::fetch_all(['courseid' => $courseid]);
foreach ($categories as $cat) {
$cat->apply_forced_settings();
$options[$cat->id] = $cat->get_name();
if ($cat->is_course_category()) {
$default = $cat->id;
}
}
if (count($categories) > 1) {
$mform->addElement('select', 'parentcategory', get_string('parentcategory', 'grades'), $options);
$mform->setDefault('parentcategory', $default);
}
$params = ['courseid' => $courseid];
if ($id > 0) {
$params['id'] = $id;
}
$url = new moodle_url('/grade/edit/tree/category.php', $params);
$url = $this->gpr->add_url_params($url);
$url = '<a class="showadvancedform" href="' . $url . '">' . get_string('showmore', 'form') .'</a>';
$mform->addElement('static', 'advancedform', $url);
// Add return tracking info.
$this->gpr->add_mform_elements($mform);
$this->set_data($category);
}
/**
* This method implements changes to the form that need to be made once the form data is set.
*/
public function definition_after_data(): void {
global $CFG;
$mform =& $this->_form;
$categoryobject = new grade_category();
foreach ($categoryobject->forceable as $property) {
if ((int)$CFG->{"grade_{$property}_flag"} & 1) {
if ($mform->elementExists($property)) {
if (empty($CFG->grade_hideforcedsettings)) {
$mform->hardFreeze($property);
} else {
if ($mform->elementExists($property)) {
$mform->removeElement($property);
}
}
}
}
}
if ($CFG->grade_droplow > 0) {
if ($mform->elementExists('keephigh')) {
$mform->removeElement('keephigh');
}
} else if ($CFG->grade_keephigh > 0) {
if ($mform->elementExists('droplow')) {
$mform->removeElement('droplow');
}
}
if ($id = $mform->getElementValue('id')) {
$gradecategory = grade_category::fetch(['id' => $id]);
$gradeitem = $gradecategory->load_grade_item();
// Remove agg coef if not used.
if ($gradecategory->is_course_category()) {
if ($mform->elementExists('parentcategory')) {
$mform->removeElement('parentcategory');
}
} else {
// If we wanted to change parent of existing category
// we would have to verify there are no circular references in parents!!!
if ($mform->elementExists('parentcategory')) {
$mform->hardFreeze('parentcategory');
}
}
// Prevent the user from using drop lowest/keep highest when the aggregation method cannot handle it.
if (!$gradecategory->can_apply_limit_rules()) {
if ($mform->elementExists('keephigh')) {
$mform->setConstant('keephigh', 0);
$mform->hardFreeze('keephigh');
}
if ($mform->elementExists('droplow')) {
$mform->setConstant('droplow', 0);
$mform->hardFreeze('droplow');
}
}
if ($gradeitem->is_calculated()) {
$gradesexistmsg = get_string('calculationwarning', 'grades');
$gradesexisthtml = '<div class=\'alert alert-warning\'>' . $gradesexistmsg . '</div>';
$mform->addElement('static', 'gradesexistmsg', '', $gradesexisthtml);
// Following elements are ignored when calculation formula used.
if ($mform->elementExists('aggregation')) {
$mform->removeElement('aggregation');
}
if ($mform->elementExists('keephigh')) {
$mform->removeElement('keephigh');
}
if ($mform->elementExists('droplow')) {
$mform->removeElement('droplow');
}
if ($mform->elementExists('aggregateonlygraded')) {
$mform->removeElement('aggregateonlygraded');
}
if ($mform->elementExists('aggregateoutcomes')) {
$mform->removeElement('aggregateoutcomes');
}
}
// If it is a course category, remove the "required" rule from the "fullname" element.
if ($gradecategory->is_course_category()) {
unset($mform->_rules['fullname']);
$key = array_search('fullname', $mform->_required);
unset($mform->_required[$key]);
}
// If it is a course category and its fullname is ?, show an empty field.
if ($gradecategory->is_course_category() && $mform->getElementValue('fullname') == '?') {
$mform->setDefault('fullname', '');
}
// Remove unwanted aggregation options.
if ($mform->elementExists('aggregation')) {
$allaggoptions = array_keys($this->aggregation_options);
$aggel =& $mform->getElement('aggregation');
$visible = explode(',', $CFG->grade_aggregations_visible);
if (!is_null($gradecategory->aggregation)) {
// Current type is always visible.
$visible[] = $gradecategory->aggregation;
}
foreach ($allaggoptions as $type) {
if (!in_array($type, $visible)) {
$aggel->removeOption($type);
}
}
}
} else {
// Adding new category
// Remove unwanted aggregation options.
if ($mform->elementExists('aggregation')) {
$allaggoptions = array_keys($this->aggregation_options);
$aggel =& $mform->getElement('aggregation');
$visible = explode(',', $CFG->grade_aggregations_visible);
foreach ($allaggoptions as $type) {
if (!in_array($type, $visible)) {
$aggel->removeOption($type);
}
}
}
$mform->removeElement('grade_item_rescalegrades');
}
// Grade item.
if ($id = $mform->getElementValue('id')) {
$gradecategory = grade_category::fetch(['id' => $id]);
$gradeitem = $gradecategory->load_grade_item();
// Load appropriate "hidden"/"hidden until" defaults.
if (!$gradeitem->is_hiddenuntil()) {
$mform->setDefault('grade_item_hidden', $gradeitem->get_hidden());
}
if ($gradeitem->has_overridden_grades()) {
// Can't change the grade type or the scale if there are grades.
$mform->hardFreeze('grade_item_gradetype, grade_item_scaleid');
// If we are using scales then remove the unnecessary rescale and grade fields.
if ($gradeitem->gradetype == GRADE_TYPE_SCALE) {
$mform->removeElement('grade_item_rescalegrades');
$mform->removeElement('grade_item_grademax');
if ($mform->elementExists('grade_item_grademin')) {
$mform->removeElement('grade_item_grademin');
}
} else {
// Not using scale, so remove it.
$mform->removeElement('grade_item_scaleid');
$mform->hideIf('grade_item_grademax', 'grade_item_rescalegrades', 'eq', '');
$mform->hideIf('grade_item_grademin', 'grade_item_rescalegrades', 'eq', '');
}
} else { // Remove the rescale element if there are no grades.
$mform->removeElement('grade_item_rescalegrades');
}
// Remove the aggregation coef element if not needed.
if ($gradeitem->is_course_item()) {
if ($mform->elementExists('grade_item_aggregationcoef')) {
$mform->removeElement('grade_item_aggregationcoef');
}
if ($mform->elementExists('grade_item_weightoverride')) {
$mform->removeElement('grade_item_weightoverride');
}
if ($mform->elementExists('grade_item_aggregationcoef2')) {
$mform->removeElement('grade_item_aggregationcoef2');
}
} else {
if ($gradeitem->is_category_item()) {
$category = $gradeitem->get_item_category();
$parentcategory = $category->get_parent_category();
} else {
$parentcategory = $gradeitem->get_parent_category();
}
$parentcategory->apply_forced_settings();
if (!$parentcategory->is_aggregationcoef_used()) {
if ($mform->elementExists('grade_item_aggregationcoef')) {
$mform->removeElement('grade_item_aggregationcoef');
}
} else {
$coefstring = $gradeitem->get_coefstring();
if ($coefstring == 'aggregationcoefextrasum' || $coefstring == 'aggregationcoefextraweightsum') {
// Advcheckbox is not compatible with disabledIf!
$coefstring = 'aggregationcoefextrasum';
$element =& $mform->createElement('checkbox', 'grade_item_aggregationcoef',
get_string($coefstring, 'grades'));
} else {
$element =& $mform->createElement('text', 'grade_item_aggregationcoef',
get_string($coefstring, 'grades'));
$mform->setType('grade_item_aggregationcoef', PARAM_FLOAT);
}
$mform->insertElementBefore($element, 'parentcategory');
$mform->addHelpButton('grade_item_aggregationcoef', $coefstring, 'grades');
}
// Remove fields used by natural weighting if the parent category is not using natural weighting.
// Or if the item is a scale and scales are not used in aggregation.
if ($parentcategory->aggregation != GRADE_AGGREGATE_SUM
|| (empty($CFG->grade_includescalesinaggregation) && $gradeitem->gradetype == GRADE_TYPE_SCALE)) {
if ($mform->elementExists('grade_item_weightoverride')) {
$mform->removeElement('grade_item_weightoverride');
}
if ($mform->elementExists('grade_item_aggregationcoef2')) {
$mform->removeElement('grade_item_aggregationcoef2');
}
}
}
}
}
/**
* Return form context
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
$courseid = $this->optional_param('courseid', null, PARAM_INT);
return context_course::instance($courseid);
}
/**
* Check if current user has access to this form, otherwise throw exception
*
* @return void
* @throws \required_capability_exception
*/
protected function check_access_for_dynamic_submission(): void {
$courseid = $this->optional_param('courseid', null, PARAM_INT);
require_capability('moodle/grade:manage', context_course::instance($courseid));
}
/**
* Load in existing data as form defaults
*
* @return void
*/
public function set_data_for_dynamic_submission(): void {
$this->set_data((object)[
'courseid' => $this->optional_param('courseid', null, PARAM_INT),
'category' => $this->optional_param('category', null, PARAM_INT)
]);
}
/**
* Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
*
* @return moodle_url
* @throws \moodle_exception
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$params = [
'id' => $this->optional_param('courseid', null, PARAM_INT),
'category' => $this->optional_param('category', null, PARAM_INT),
];
return new moodle_url('/grade/edit/tree/index.php', $params);
}
/**
* Process the form submission, used if form was submitted via AJAX
*
* @return array
* @throws \moodle_exception
*/
public function process_dynamic_submission(): array {
$data = $this->get_data();
$url = $this->gpr->get_return_url('index.php?id=' . $data->courseid);
$local = $this->get_gradecategory();
$gradecategory = $local['gradecategory'];
grade_edit_tree::update_gradecategory($gradecategory, $data);
return [
'result' => true,
'url' => $url,
'errors' => [],
];
}
/**
* Form validation.
*
* @param array $data array of ("fieldname"=>value) of submitted data
* @param array $files array of uploaded files "element_name"=>tmp_file_path
* @return array of "element_name"=>"error_description" if there are errors,
* or an empty array if everything is OK (true allowed for backwards compatibility too).
*/
public function validation($data, $files): array {
$gradeitem = false;
if ($data['id']) {
$gradecategory = grade_category::fetch(['id' => $data['id']]);
$gradeitem = $gradecategory->load_grade_item();
}
$errors = parent::validation($data, $files);
if (array_key_exists('grade_item_gradetype', $data) && $data['grade_item_gradetype'] == GRADE_TYPE_SCALE) {
if (empty($data['grade_item_scaleid'])) {
$errors['grade_item_scaleid'] = get_string('missingscale', 'grades');
}
}
// We need to make all the validations related with grademax and grademin
// with them being correct floats, keeping the originals unmodified for
// later validations / showing the form back...
// TODO: Note that once MDL-73994 is fixed we'll have to re-visit this and
// adapt the code below to the new values arriving here, without forgetting
// the special case of empties and nulls.
$grademax = isset($data['grade_item_grademax']) ? unformat_float($data['grade_item_grademax']) : null;
$grademin = isset($data['grade_item_grademin']) ? unformat_float($data['grade_item_grademin']) : null;
if (!is_null($grademin) && !is_null($grademax)) {
if (($grademax != 0 || $grademin != 0) && ($grademax == $grademin || $grademax < $grademin)) {
$errors['grade_item_grademin'] = get_string('incorrectminmax', 'grades');
$errors['grade_item_grademax'] = get_string('incorrectminmax', 'grades');
}
}
if ($data['id'] && $gradeitem->has_overridden_grades()) {
if ($gradeitem->gradetype == GRADE_TYPE_VALUE) {
if (grade_floats_different($grademin, $gradeitem->grademin) ||
grade_floats_different($grademax, $gradeitem->grademax)) {
if (empty($data['grade_item_rescalegrades'])) {
$errors['grade_item_rescalegrades'] = get_string('mustchooserescaleyesorno', 'grades');
}
}
}
}
return $errors;
}
}
+571
View File
@@ -0,0 +1,571 @@
<?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\form;
defined('MOODLE_INTERNAL') || die;
use context;
use context_course;
use core_form\dynamic_form;
use grade_category;
use grade_item;
use grade_plugin_return;
use grade_scale;
use moodle_url;
require_once($CFG->dirroot.'/grade/lib.php');
/**
* Prints the add item gradebook form
*
* @copyright 2023 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package core_grades
*/
class add_item extends dynamic_form {
/** Grade plugin return tracking object.
* @var object $gpr
*/
public $gpr;
/**
* Helper function to grab the current grade item based on information within the form.
*
* @return array
* @throws \moodle_exception
*/
private function get_gradeitem(): array {
$courseid = $this->optional_param('courseid', null, PARAM_INT);
$id = $this->optional_param('itemid', null, PARAM_INT);
if ($gradeitem = grade_item::fetch(['id' => $id, 'courseid' => $courseid])) {
$item = $gradeitem->get_record_data();
$parentcategory = $gradeitem->get_parent_category();
} else {
$gradeitem = new grade_item(['courseid' => $courseid, 'itemtype' => 'manual'], false);
$item = $gradeitem->get_record_data();
$parentcategory = grade_category::fetch_course_category($courseid);
}
$item->parentcategory = $parentcategory->id;
$decimalpoints = $gradeitem->get_decimals();
if ($item->hidden > 1) {
$item->hiddenuntil = $item->hidden;
$item->hidden = 0;
} else {
$item->hiddenuntil = 0;
}
$item->locked = !empty($item->locked);
$item->grademax = format_float($item->grademax, $decimalpoints);
$item->grademin = format_float($item->grademin, $decimalpoints);
if ($parentcategory->aggregation == GRADE_AGGREGATE_SUM || $parentcategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) {
$item->aggregationcoef = $item->aggregationcoef == 0 ? 0 : 1;
} else {
$item->aggregationcoef = format_float($item->aggregationcoef, 4);
}
if ($parentcategory->aggregation == GRADE_AGGREGATE_SUM) {
$item->aggregationcoef2 = format_float($item->aggregationcoef2 * 100.0);
}
$item->cancontrolvisibility = $gradeitem->can_control_visibility();
return [
'gradeitem' => $gradeitem,
'item' => $item
];
}
/**
* Form definition
*
* @return void
* @throws \coding_exception
* @throws \dml_exception
* @throws \moodle_exception
*/
protected function definition() {
global $CFG;
$courseid = $this->optional_param('courseid', null, PARAM_INT);
$id = $this->optional_param('itemid', 0, PARAM_INT);
$gprplugin = $this->optional_param('gpr_plugin', '', PARAM_TEXT);
if ($gprplugin && ($gprplugin !== 'tree')) {
$this->gpr = new grade_plugin_return(['type' => 'report', 'plugin' => $gprplugin, 'courseid' => $courseid]);
} else {
$this->gpr = new grade_plugin_return(['type' => 'edit', 'plugin' => 'tree', 'courseid' => $courseid]);
}
$mform =& $this->_form;
$local = $this->get_gradeitem();
$gradeitem = $local['gradeitem'];
$item = $local['item'];
// Hidden elements.
$mform->addElement('hidden', 'id', 0);
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'courseid', $courseid);
$mform->setType('courseid', PARAM_INT);
$mform->addElement('hidden', 'itemid', $id);
$mform->setType('itemid', PARAM_INT);
$mform->addElement('hidden', 'itemtype', 'manual'); // All new items are manual only.
$mform->setType('itemtype', PARAM_ALPHA);
// Visible elements.
$mform->addElement('text', 'itemname', get_string('itemname', 'grades'));
$mform->setType('itemname', PARAM_TEXT);
if (!empty($item->id)) {
// If grades exist set a message so the user knows why they can not alter the grade type or scale.
// We could never change the grade type for external items, so only need to show this for manual grade items.
if ($gradeitem->has_grades() && !$gradeitem->is_external_item()) {
// Set a message so the user knows why they can not alter the grade type or scale.
if ($gradeitem->gradetype == GRADE_TYPE_SCALE) {
$gradesexistmsg = get_string('modgradecantchangegradetyporscalemsg', 'grades');
} else {
$gradesexistmsg = get_string('modgradecantchangegradetypemsg', 'grades');
}
$gradesexisthtml = '<div class=\'alert\'>' . $gradesexistmsg . '</div>';
$mform->addElement('static', 'gradesexistmsg', '', $gradesexisthtml);
}
}
// Manual grade items cannot have grade type GRADE_TYPE_NONE.
$mform->addElement('select', 'gradetype', get_string('gradetype', 'grades'), [
GRADE_TYPE_VALUE => get_string('typevalue', 'grades'),
GRADE_TYPE_SCALE => get_string('typescale', 'grades'),
GRADE_TYPE_TEXT => get_string('typetext', 'grades')
]);
$mform->addHelpButton('gradetype', 'gradetype', 'grades');
$mform->setDefault('gradetype', GRADE_TYPE_VALUE);
$options = [0 => get_string('usenoscale', 'grades')];
if ($scales = grade_scale::fetch_all_local($courseid)) {
foreach ($scales as $scale) {
$options[$scale->id] = $scale->get_name();
}
}
if ($scales = grade_scale::fetch_all_global()) {
foreach ($scales as $scale) {
$options[$scale->id] = $scale->get_name();
}
}
$mform->addElement('select', 'scaleid', get_string('scale'), $options);
$mform->addHelpButton('scaleid', 'typescale', 'grades');
$mform->hideIf('scaleid', 'gradetype', 'noteq', GRADE_TYPE_SCALE);
$mform->addElement('select', 'rescalegrades', get_string('modgraderescalegrades', 'grades'), [
'' => get_string('choose'),
'no' => get_string('no'),
'yes' => get_string('yes')
]);
$mform->addHelpButton('rescalegrades', 'modgraderescalegrades', 'grades');
$mform->hideIf('rescalegrades', 'gradetype', 'noteq', GRADE_TYPE_VALUE);
$mform->addElement('float', 'grademax', get_string('grademax', 'grades'));
$mform->addHelpButton('grademax', 'grademax', 'grades');
$mform->hideIf('grademax', 'gradetype', 'noteq', GRADE_TYPE_VALUE);
if (get_config('moodle', 'grade_report_showmin')) {
$mform->addElement('float', 'grademin', get_string('grademin', 'grades'));
$mform->addHelpButton('grademin', 'grademin', 'grades');
$mform->hideIf('grademin', 'gradetype', 'noteq', GRADE_TYPE_VALUE);
}
// Hiding.
if ($item->cancontrolvisibility) {
$mform->addElement('advcheckbox', 'hidden', get_string('hidden', 'grades'), '', [], [0, 1]);
$mform->hideIf('hidden', 'hiddenuntil[enabled]', 'checked');
} else {
$mform->addElement('static', 'hidden', get_string('hidden', 'grades'),
get_string('componentcontrolsvisibility', 'grades'));
// Unset hidden to avoid data override.
unset($item->hidden);
}
$mform->addHelpButton('hidden', 'hidden', 'grades');
// Locking.
$mform->addElement('advcheckbox', 'locked', get_string('locked', 'grades'));
$mform->addHelpButton('locked', 'locked', 'grades');
// Weight overrides.
$mform->addElement('advcheckbox', 'weightoverride', get_string('adjustedweight', 'grades'));
$mform->addHelpButton('weightoverride', 'weightoverride', 'grades');
$mform->hideIf('weightoverride', 'gradetype', 'eq', GRADE_TYPE_NONE);
$mform->hideIf('weightoverride', 'gradetype', 'eq', GRADE_TYPE_TEXT);
// Parent category related settings.
$mform->addElement('float', 'aggregationcoef2', get_string('weight', 'grades'));
$mform->addHelpButton('aggregationcoef2', 'weight', 'grades');
$mform->hideIf('aggregationcoef2', 'weightoverride');
$mform->hideIf('aggregationcoef2', 'gradetype', 'eq', GRADE_TYPE_NONE);
$mform->hideIf('aggregationcoef2', 'gradetype', 'eq', GRADE_TYPE_TEXT);
$options = [];
$categories = grade_category::fetch_all(['courseid' => $courseid]);
foreach ($categories as $cat) {
$cat->apply_forced_settings();
$options[$cat->id] = $cat->get_name();
}
if (count($categories) > 1) {
$mform->addElement('select', 'parentcategory', get_string('gradecategory', 'grades'), $options);
}
$parentcategory = $gradeitem->get_parent_category();
if (!$parentcategory) {
// If we do not have an id, we are creating a new grade item.
// Assign the course category to this grade item.
$parentcategory = grade_category::fetch_course_category($courseid);
$gradeitem->parent_category = $parentcategory;
}
if ($gradeitem->is_external_item()) {
// Following items are set up from modules and should not be overrided by user.
if ($mform->elementExists('grademin')) {
// The site setting grade_report_showmin may have prevented grademin being added to the form.
$mform->hardFreeze('grademin');
}
$mform->hardFreeze('itemname,gradetype,grademax,scaleid');
// For external items we can not change the grade type, even if no grades exist, so if it is set to
// scale, then remove the grademax and grademin fields from the form - no point displaying them.
if ($gradeitem->gradetype == GRADE_TYPE_SCALE) {
$mform->removeElement('grademax');
if ($mform->elementExists('grademin')) {
$mform->removeElement('grademin');
}
} else { // Not using scale, so remove it.
$mform->removeElement('scaleid');
}
// Always remove the rescale grades element if it's an external item.
$mform->removeElement('rescalegrades');
} else if ($gradeitem->has_grades()) {
// Can't change the grade type or the scale if there are grades.
$mform->hardFreeze('gradetype, scaleid');
// If we are using scales then remove the unnecessary rescale and grade fields.
if ($gradeitem->gradetype == GRADE_TYPE_SCALE) {
$mform->removeElement('rescalegrades');
$mform->removeElement('grademax');
if ($mform->elementExists('grademin')) {
$mform->removeElement('grademin');
}
} else { // Remove the scale field.
$mform->removeElement('scaleid');
// Set the maximum grade to disabled unless a grade is chosen.
$mform->hideIf('grademax', 'rescalegrades', 'eq', '');
}
} else {
// Remove rescale element if there are no grades.
$mform->removeElement('rescalegrades');
}
// If we wanted to change parent of existing item - we would have to verify there are no circular references in parents!!!
if ($id > -1 && $mform->elementExists('parentcategory')) {
$mform->hardFreeze('parentcategory');
}
$parentcategory->apply_forced_settings();
if (!$parentcategory->is_aggregationcoef_used()) {
if ($mform->elementExists('aggregationcoef')) {
$mform->removeElement('aggregationcoef');
}
} else {
$coefstring = $gradeitem->get_coefstring();
if ($coefstring !== '') {
if ($coefstring == 'aggregationcoefextrasum' || $coefstring == 'aggregationcoefextraweightsum') {
// The advcheckbox is not compatible with disabledIf!
$coefstring = 'aggregationcoefextrasum';
$element =& $mform->createElement('checkbox', 'aggregationcoef', get_string($coefstring, 'grades'));
} else {
$element =& $mform->createElement('text', 'aggregationcoef', get_string($coefstring, 'grades'));
$mform->setType('aggregationcoef', PARAM_FLOAT);
}
if ($mform->elementExists('parentcategory')) {
$mform->insertElementBefore($element, 'parentcategory');
} else {
$mform->insertElementBefore($element, 'aggregationcoef2');
}
$mform->addHelpButton('aggregationcoef', $coefstring, 'grades');
}
$mform->hideIf('aggregationcoef', 'gradetype', 'eq', GRADE_TYPE_NONE);
$mform->hideIf('aggregationcoef', 'gradetype', 'eq', GRADE_TYPE_TEXT);
$mform->hideIf('aggregationcoef', 'parentcategory', 'eq', $parentcategory->id);
}
// Remove fields used by natural weighting if the parent category is not using natural weighting.
// Or if the item is a scale and scales are not used in aggregation.
if ($parentcategory->aggregation != GRADE_AGGREGATE_SUM
|| (empty($CFG->grade_includescalesinaggregation) && $gradeitem->gradetype == GRADE_TYPE_SCALE)) {
if ($mform->elementExists('weightoverride')) {
$mform->removeElement('weightoverride');
}
if ($mform->elementExists('aggregationcoef2')) {
$mform->removeElement('aggregationcoef2');
}
}
if ($category = $gradeitem->get_item_category()) {
if ($category->aggregation == GRADE_AGGREGATE_SUM) {
if ($mform->elementExists('gradetype')) {
$mform->hardFreeze('gradetype');
}
if ($mform->elementExists('grademin')) {
$mform->hardFreeze('grademin');
}
if ($mform->elementExists('grademax')) {
$mform->hardFreeze('grademax');
}
if ($mform->elementExists('scaleid')) {
$mform->removeElement('scaleid');
}
}
}
$url = new moodle_url('/grade/edit/tree/item.php', ['id' => $id, 'courseid' => $courseid]);
$url = $this->gpr->add_url_params($url);
$url = '<a class="showadvancedform" href="' . $url . '">' . get_string('showmore', 'form') .'</a>';
$mform->addElement('static', 'advancedform', $url);
// Add return tracking info.
$this->gpr->add_mform_elements($mform);
$this->set_data($item);
}
/**
* Return form context
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
$courseid = $this->optional_param('courseid', null, PARAM_INT);
return context_course::instance($courseid);
}
/**
* Check if current user has access to this form, otherwise throw exception
*
* @return void
* @throws \required_capability_exception
*/
protected function check_access_for_dynamic_submission(): void {
$courseid = $this->optional_param('courseid', null, PARAM_INT);
require_capability('moodle/grade:manage', context_course::instance($courseid));
}
/**
* Load in existing data as form defaults
*
* @return void
*/
public function set_data_for_dynamic_submission(): void {
$this->set_data((object)[
'courseid' => $this->optional_param('courseid', null, PARAM_INT),
'itemid' => $this->optional_param('itemid', null, PARAM_INT)
]);
}
/**
* Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
*
* @return moodle_url
* @throws \moodle_exception
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$params = [
'id' => $this->optional_param('courseid', null, PARAM_INT),
'itemid' => $this->optional_param('itemid', null, PARAM_INT),
];
return new moodle_url('/grade/edit/tree/index.php', $params);
}
/**
* Process the form submission, used if form was submitted via AJAX
*
* @return array
* @throws \moodle_exception
*/
public function process_dynamic_submission() {
$data = $this->get_data();
$url = $this->gpr->get_return_url('index.php?id=' . $data->courseid);
$local = $this->get_gradeitem();
$gradeitem = $local['gradeitem'];
$item = $local['item'];
$parentcategory = grade_category::fetch_course_category($data->courseid);
// Form submission handling.
// This is a new item, and the category chosen is different than the default category.
if (empty($gradeitem->id) && isset($data->parentcategory) && $parentcategory->id != $data->parentcategory) {
$parentcategory = grade_category::fetch(['id' => $data->parentcategory]);
}
// If unset, give the aggregation values a default based on parent aggregation method.
$defaults = grade_category::get_default_aggregation_coefficient_values($parentcategory->aggregation);
if (!isset($data->aggregationcoef) || $data->aggregationcoef == '') {
$data->aggregationcoef = $defaults['aggregationcoef'];
}
if (!isset($data->weightoverride)) {
$data->weightoverride = $defaults['weightoverride'];
}
if (!isset($data->gradepass) || $data->gradepass == '') {
$data->gradepass = 0;
}
if (!isset($data->grademin) || $data->grademin == '') {
$data->grademin = 0;
}
$hide = empty($data->hiddenuntil) ? 0 : $data->hiddenuntil;
if (!$hide) {
$hide = empty($data->hidden) ? 0 : $data->hidden;
}
$locked = empty($data->locked) ? 0 : $data->locked;
$locktime = empty($data->locktime) ? 0 : $data->locktime;
$convert = ['grademax', 'grademin', 'aggregationcoef', 'aggregationcoef2'];
foreach ($convert as $param) {
if (property_exists($data, $param)) {
$data->$param = unformat_float($data->$param);
}
}
if (isset($data->aggregationcoef2) && $parentcategory->aggregation == GRADE_AGGREGATE_SUM) {
$data->aggregationcoef2 = $data->aggregationcoef2 / 100.0;
} else {
$data->aggregationcoef2 = $defaults['aggregationcoef2'];
}
$oldmin = $gradeitem->grademin;
$oldmax = $gradeitem->grademax;
grade_item::set_properties($gradeitem, $data);
$gradeitem->outcomeid = null;
// Handle null decimals value.
if (!property_exists($data, 'decimals') || $data->decimals < 0) {
$gradeitem->decimals = null;
}
if (empty($gradeitem->id)) {
$gradeitem->itemtype = 'manual'; // All new items to be manual only.
$gradeitem->insert();
// Set parent if needed.
if (isset($data->parentcategory)) {
$gradeitem->set_parent($data->parentcategory, false);
}
} else {
$gradeitem->update();
if (!empty($data->rescalegrades) && $data->rescalegrades == 'yes') {
$newmin = $gradeitem->grademin;
$newmax = $gradeitem->grademax;
$gradeitem->rescale_grades_keep_percentage($oldmin, $oldmax, $newmin, $newmax, 'gradebook');
}
}
if ($item->cancontrolvisibility) {
// Update hiding flag.
$gradeitem->set_hidden($hide, true);
}
$gradeitem->set_locktime($locktime); // Locktime first - it might be removed when unlocking.
$gradeitem->set_locked($locked);
return [
'result' => true,
'url' => $url,
'errors' => [],
];
}
/**
* Form validation.
*
* @param array $data array of ("fieldname"=>value) of submitted data
* @param array $files array of uploaded files "element_name"=>tmp_file_path
* @return array of "element_name"=>"error_description" if there are errors,
* or an empty array if everything is OK (true allowed for backwards compatibility too).
*/
public function validation($data, $files): array {
$errors = [];
$local = $this->get_gradeitem();
$gradeitem = $local['gradeitem'];
if (isset($data['gradetype']) && $data['gradetype'] == GRADE_TYPE_SCALE) {
if (empty($data['scaleid'])) {
$errors['scaleid'] = get_string('missingscale', 'grades');
}
}
// We need to make all the validations related with grademax and grademin
// with them being correct floats, keeping the originals unmodified for
// later validations / showing the form back...
// TODO: Note that once MDL-73994 is fixed we'll have to re-visit this and
// adapt the code below to the new values arriving here, without forgetting
// the special case of empties and nulls.
$grademax = isset($data['grademax']) ? unformat_float($data['grademax']) : null;
$grademin = isset($data['grademin']) ? unformat_float($data['grademin']) : null;
if (!is_null($grademin) && !is_null($grademax)) {
if ($grademax == $grademin || $grademax < $grademin) {
$errors['grademin'] = get_string('incorrectminmax', 'grades');
$errors['grademax'] = get_string('incorrectminmax', 'grades');
}
}
// We do not want the user to be able to change the grade type or scale for this item if grades exist.
if ($gradeitem && $gradeitem->has_grades()) {
// Check that grade type is set - should never not be set unless form has been modified.
if (!isset($data['gradetype'])) {
$errors['gradetype'] = get_string('modgradecantchangegradetype', 'grades');
} else if ($data['gradetype'] !== $gradeitem->gradetype) { // Check if we are changing the grade type.
$errors['gradetype'] = get_string('modgradecantchangegradetype', 'grades');
} else if ($data['gradetype'] == GRADE_TYPE_SCALE) {
// Check if we are changing the scale - can't do this when grades exist.
if (isset($data['scaleid']) && ($data['scaleid'] !== $gradeitem->scaleid)) {
$errors['scaleid'] = get_string('modgradecantchangescale', 'grades');
}
}
}
if ($gradeitem) {
if ($gradeitem->gradetype == GRADE_TYPE_VALUE) {
if ((((bool) get_config('moodle', 'grade_report_showmin')) &&
grade_floats_different($grademin, $gradeitem->grademin)) ||
grade_floats_different($grademax, $gradeitem->grademax)) {
if ($gradeitem->has_grades() && empty($data['rescalegrades'])) {
$errors['rescalegrades'] = get_string('mustchooserescaleyesorno', 'grades');
}
}
}
}
return $errors;
}
}
+494
View File
@@ -0,0 +1,494 @@
<?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\form;
defined('MOODLE_INTERNAL') || die;
use context;
use context_course;
use core_form\dynamic_form;
use grade_category;
use grade_item;
use grade_outcome;
use grade_plugin_return;
use moodle_url;
require_once($CFG->dirroot.'/grade/lib.php');
/**
* Prints the add outcome gradebook form.
*
* @copyright 2023 Mathew May <mathew.solutions>
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package core_grades
*/
class add_outcome extends dynamic_form {
/** Grade plugin return tracking object.
* @var object $gpr
*/
public $gpr;
/**
* Helper function to grab the current grade outcome item based on information within the form.
*
* @return array
* @throws \moodle_exception
*/
private function get_gradeitem(): array {
$courseid = $this->optional_param('courseid', null, PARAM_INT);
$id = $this->optional_param('itemid', null, PARAM_INT);
if ($gradeitem = grade_item::fetch(['id' => $id, 'courseid' => $courseid])) {
// Redirect if outcomeid not present.
if (empty($gradeitem->outcomeid)) {
$url = new moodle_url('/grade/edit/tree/item.php', ['id' => $id, 'courseid' => $courseid]);
redirect($this->gpr->add_url_params($url));
}
$item = $gradeitem->get_record_data();
$parentcategory = $gradeitem->get_parent_category();
if ($item->itemtype == 'mod') {
$cm = get_coursemodule_from_instance($item->itemmodule, $item->iteminstance, $item->courseid);
$item->cmid = $cm->id;
} else {
$item->cmid = 0;
}
} else {
$gradeitem = new grade_item(['courseid' => $courseid, 'itemtype' => 'manual'], false);
$item = $gradeitem->get_record_data();
$parentcategory = grade_category::fetch_course_category($courseid);
}
$item->parentcategory = $parentcategory->id;
if ($item->hidden > 1) {
$item->hiddenuntil = $item->hidden;
$item->hidden = 0;
} else {
$item->hiddenuntil = 0;
}
$item->locked = !empty($item->locked);
if ($parentcategory->aggregation == GRADE_AGGREGATE_SUM || $parentcategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) {
$item->aggregationcoef = $item->aggregationcoef == 0 ? 0 : 1;
} else {
$item->aggregationcoef = format_float($item->aggregationcoef, 4);
}
if ($parentcategory->aggregation == GRADE_AGGREGATE_SUM) {
$item->aggregationcoef2 = format_float($item->aggregationcoef2 * 100.0);
}
$item->cancontrolvisibility = $gradeitem->can_control_visibility();
return [
'gradeitem' => $gradeitem,
'item' => $item
];
}
/**
* Form definition
*
* @return void
* @throws \coding_exception
* @throws \dml_exception
* @throws \moodle_exception
*/
protected function definition() {
$courseid = $this->optional_param('courseid', null, PARAM_INT);
$id = $this->optional_param('itemid', 0, PARAM_INT);
$gprplugin = $this->optional_param('gpr_plugin', '', PARAM_TEXT);
if ($gprplugin && ($gprplugin !== 'tree')) {
$this->gpr = new grade_plugin_return(['type' => 'report', 'plugin' => $gprplugin, 'courseid' => $courseid]);
} else {
$this->gpr = new grade_plugin_return(['type' => 'edit', 'plugin' => 'tree', 'courseid' => $courseid]);
}
$mform =& $this->_form;
$local = $this->get_gradeitem();
$gradeitem = $local['gradeitem'];
$item = $local['item'];
// Hidden elements.
$mform->addElement('hidden', 'id', 0);
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'courseid', $courseid);
$mform->setType('courseid', PARAM_INT);
$mform->addElement('hidden', 'itemid', $id);
$mform->setType('itemid', PARAM_INT);
// Allow setting of outcomes on module items too.
$outcomeoptions = [];
if ($outcomes = grade_outcome::fetch_all_available($courseid)) {
foreach ($outcomes as $outcome) {
$outcomeoptions[$outcome->id] = $outcome->get_name();
}
}
// Visible elements.
$mform->addElement('text', 'itemname', get_string('itemname', 'grades'));
$mform->addRule('itemname', get_string('required'), 'required', null, 'client');
$mform->setType('itemname', PARAM_TEXT);
$mform->addElement('selectwithlink', 'outcomeid', get_string('outcome', 'grades'), $outcomeoptions);
$mform->addHelpButton('outcomeid', 'outcome', 'grades');
$mform->addRule('outcomeid', get_string('required'), 'required');
$options = [0 => get_string('none')];
if ($coursemods = get_course_mods($courseid)) {
foreach ($coursemods as $coursemod) {
if ($mod = get_coursemodule_from_id($coursemod->modname, $coursemod->id)) {
$options[$coursemod->id] = format_string($mod->name);
}
}
}
$mform->addElement('select', 'cmid', get_string('linkedactivity', 'grades'), $options);
$mform->addHelpButton('cmid', 'linkedactivity', 'grades');
$mform->setDefault('cmid', 0);
// Hiding.
$mform->addElement('checkbox', 'hidden', get_string('hidden', 'grades'));
$mform->addHelpButton('hidden', 'hidden', 'grades');
// Locking.
$mform->addElement('advcheckbox', 'locked', get_string('locked', 'grades'));
$mform->addHelpButton('locked', 'locked', 'grades');
// Parent category related settings.
$mform->addElement('advcheckbox', 'weightoverride', get_string('adjustedweight', 'grades'));
$mform->addHelpButton('weightoverride', 'weightoverride', 'grades');
$mform->addElement('text', 'aggregationcoef2', get_string('weight', 'grades'));
$mform->addHelpButton('aggregationcoef2', 'weight', 'grades');
$mform->setType('aggregationcoef2', PARAM_RAW);
$mform->hideIf('aggregationcoef2', 'weightoverride');
$options = [];
$coefstring = '';
$categories = grade_category::fetch_all(['courseid' => $courseid]);
foreach ($categories as $cat) {
$cat->apply_forced_settings();
$options[$cat->id] = $cat->get_name();
if ($cat->is_aggregationcoef_used()) {
if ($cat->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
$coefstring = ($coefstring == '' || $coefstring == 'aggregationcoefweight') ?
'aggregationcoefweight' : 'aggregationcoef';
} else if ($cat->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) {
$coefstring = ($coefstring == '' || $coefstring == 'aggregationcoefextrasum') ?
'aggregationcoefextrasum' : 'aggregationcoef';
} else if ($cat->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) {
$coefstring = ($coefstring == '' || $coefstring == 'aggregationcoefextraweight') ?
'aggregationcoefextraweight' : 'aggregationcoef';
} else if ($cat->aggregation == GRADE_AGGREGATE_SUM) {
$coefstring = ($coefstring == '' || $coefstring == 'aggregationcoefextrasum') ?
'aggregationcoefextrasum' : 'aggregationcoef';
} else {
$coefstring = 'aggregationcoef';
}
} else {
$mform->disabledIf('aggregationcoef', 'parentcategory', 'eq', $cat->id);
}
}
if (count($categories) > 1) {
$mform->addElement('select', 'parentcategory', get_string('gradecategory', 'grades'), $options);
$mform->disabledIf('parentcategory', 'cmid', 'noteq', 0);
}
if ($coefstring !== '') {
if ($coefstring == 'aggregationcoefextrasum' || $coefstring == 'aggregationcoefextraweightsum') {
$coefstring = 'aggregationcoefextrasum';
$mform->addElement('checkbox', 'aggregationcoef', get_string($coefstring, 'grades'));
} else {
$mform->addElement('text', 'aggregationcoef', get_string($coefstring, 'grades'));
}
$mform->addHelpButton('aggregationcoef', $coefstring, 'grades');
}
// Remove the aggregation coef element if not needed.
if ($gradeitem->is_course_item()) {
if ($mform->elementExists('parentcategory')) {
$mform->removeElement('parentcategory');
}
if ($mform->elementExists('aggregationcoef')) {
$mform->removeElement('aggregationcoef');
}
} else {
// If we wanted to change parent of existing item - we would have to verify there are no circular references in parents.
if ($id > -1 && $mform->elementExists('parentcategory')) {
$mform->hardFreeze('parentcategory');
}
$parentcategory = $gradeitem->get_parent_category();
if (!$parentcategory) {
// If we do not have an id, we are creating a new grade item.
// Assign the course category to this grade item.
$parentcategory = grade_category::fetch_course_category($courseid);
$gradeitem->parent_category = $parentcategory;
}
$parentcategory->apply_forced_settings();
if (!$parentcategory->is_aggregationcoef_used() || !$parentcategory->aggregateoutcomes) {
if ($mform->elementExists('aggregationcoef')) {
$mform->removeElement('aggregationcoef');
}
} else {
// Fix label if needed.
$agg_el =& $mform->getElement('aggregationcoef');
$aggcoef = '';
if ($parentcategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) {
$aggcoef = 'aggregationcoefweight';
} else if ($parentcategory->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) {
$aggcoef = 'aggregationcoefextrasum';
} else if ($parentcategory->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) {
$aggcoef = 'aggregationcoefextraweight';
} else if ($parentcategory->aggregation == GRADE_AGGREGATE_SUM) {
$aggcoef = 'aggregationcoefextrasum';
}
if ($aggcoef !== '') {
$agg_el->setLabel(get_string($aggcoef, 'grades'));
$mform->addHelpButton('aggregationcoef', $aggcoef, 'grades');
}
}
// Remove the natural weighting fields for other aggregations,
// or when the category does not aggregate outcomes.
if ($parentcategory->aggregation != GRADE_AGGREGATE_SUM ||
!$parentcategory->aggregateoutcomes) {
if ($mform->elementExists('weightoverride')) {
$mform->removeElement('weightoverride');
}
if ($mform->elementExists('aggregationcoef2')) {
$mform->removeElement('aggregationcoef2');
}
}
}
$url = new moodle_url('/grade/edit/tree/outcomeitem.php', ['id' => $id, 'courseid' => $courseid]);
$url = $this->gpr->add_url_params($url);
$url = '<a class="showadvancedform" href="' . $url . '">' . get_string('showmore', 'form') .'</a>';
$mform->addElement('static', 'advancedform', $url);
// Add return tracking info.
$this->gpr->add_mform_elements($mform);
$this->set_data($item);
}
/**
* Return form context
*
* @return context
*/
protected function get_context_for_dynamic_submission(): context {
$courseid = $this->optional_param('courseid', null, PARAM_INT);
return context_course::instance($courseid);
}
/**
* Check if current user has access to this form, otherwise throw exception
*
* @return void
* @throws \required_capability_exception
*/
protected function check_access_for_dynamic_submission(): void {
$courseid = $this->optional_param('courseid', null, PARAM_INT);
require_capability('moodle/grade:manage', context_course::instance($courseid));
}
/**
* Load in existing data as form defaults
*
* @return void
*/
public function set_data_for_dynamic_submission(): void {
$this->set_data((object)[
'courseid' => $this->optional_param('courseid', null, PARAM_INT),
'itemid' => $this->optional_param('itemid', null, PARAM_INT)
]);
}
/**
* Returns url to set in $PAGE->set_url() when form is being rendered or submitted via AJAX
*
* @return moodle_url
* @throws \moodle_exception
*/
protected function get_page_url_for_dynamic_submission(): moodle_url {
$params = [
'id' => $this->optional_param('courseid', null, PARAM_INT),
'itemid' => $this->optional_param('itemid', null, PARAM_INT),
];
return new moodle_url('/grade/edit/tree/index.php', $params);
}
/**
* Process the form submission, used if form was submitted via AJAX
*
* @return array
* @throws \moodle_exception
*/
public function process_dynamic_submission() {
global $DB;
$data = $this->get_data();
$url = $this->gpr->get_return_url('index.php?id=' . $data->courseid);
$local = $this->get_gradeitem();
$gradeitem = $local['gradeitem'];
$item = $local['item'];
$parentcategory = grade_category::fetch_course_category($data->courseid);
// Form submission handling.
// If unset, give the aggregation values a default based on parent aggregation method.
$defaults = grade_category::get_default_aggregation_coefficient_values($parentcategory->aggregation);
if (!isset($data->aggregationcoef) || $data->aggregationcoef == '') {
$data->aggregationcoef = $defaults['aggregationcoef'];
}
if (!isset($data->weightoverride)) {
$data->weightoverride = $defaults['weightoverride'];
}
if (property_exists($data, 'calculation')) {
$data->calculation = grade_item::normalize_formula($data->calculation, $data->courseid);
}
$hide = empty($data->hiddenuntil) ? 0 : $data->hiddenuntil;
if (!$hide) {
$hide = empty($data->hidden) ? 0 : $data->hidden;
}
$locked = empty($data->locked) ? 0 : $data->locked;
$locktime = empty($data->locktime) ? 0 : $data->locktime;
$convert = ['gradepass', 'aggregationcoef', 'aggregationcoef2'];
foreach ($convert as $param) {
if (property_exists($data, $param)) {
$data->$param = unformat_float($data->$param);
}
}
if (isset($data->aggregationcoef2) && $parentcategory->aggregation == GRADE_AGGREGATE_SUM) {
$data->aggregationcoef2 = $data->aggregationcoef2 / 100.0;
} else {
$data->aggregationcoef2 = $defaults['aggregationcoef2'];
}
grade_item::set_properties($gradeitem, $data);
// Link this outcome item to the user specified linked activity.
if (empty($data->cmid) || $data->cmid == 0) {
// Manual item.
$gradeitem->itemtype = 'manual';
$gradeitem->itemmodule = null;
$gradeitem->iteminstance = null;
$gradeitem->itemnumber = 0;
} else {
$params = [$data->cmid];
$module = $DB->get_record_sql("SELECT cm.*, m.name as modname
FROM {modules} m, {course_modules} cm
WHERE cm.id = ? AND cm.module = m.id ", $params);
$gradeitem->itemtype = 'mod';
$gradeitem->itemmodule = $module->modname;
$gradeitem->iteminstance = $module->instance;
if ($items = grade_item::fetch_all(['itemtype' => 'mod', 'itemmodule' => $gradeitem->itemmodule,
'iteminstance' => $gradeitem->iteminstance, 'courseid' => $data->courseid])) {
if (!empty($gradeitem->id) && in_array($gradeitem, $items)) {
// No change needed.
} else {
$max = 999;
foreach ($items as $item) {
if (empty($item->outcomeid)) {
continue;
}
if ($item->itemnumber > $max) {
$max = $item->itemnumber;
}
}
$gradeitem->itemnumber = $max + 1;
}
} else {
$gradeitem->itemnumber = 1000;
}
}
// Fix scale used.
$outcome = grade_outcome::fetch(['id' => $data->outcomeid]);
$gradeitem->gradetype = GRADE_TYPE_SCALE;
$gradeitem->scaleid = $outcome->scaleid; // TODO: we might recalculate existing outcome grades when changing scale.
if (empty($gradeitem->id)) {
$gradeitem->insert();
// Move next to activity if adding linked outcome.
if ($gradeitem->itemtype == 'mod') {
if ($linkeditem = grade_item::fetch(['itemtype' => 'mod', 'itemmodule' => $gradeitem->itemmodule,
'iteminstance' => $gradeitem->iteminstance, 'itemnumber' => 0, 'courseid' => $data->courseid])) {
$gradeitem->set_parent($linkeditem->categoryid);
$gradeitem->move_after_sortorder($linkeditem->sortorder);
}
} else {
// Set parent if needed.
if (isset($data->parentcategory)) {
$gradeitem->set_parent($data->parentcategory, false);
}
}
} else {
$gradeitem->update();
}
if ($item->cancontrolvisibility) {
// Update hiding flag.
$gradeitem->set_hidden($hide, true);
}
$gradeitem->set_locktime($locktime); // Locktime first - it might be removed when unlocking.
$gradeitem->set_locked($locked, false, true);
return [
'result' => true,
'url' => $url,
'errors' => [],
];
}
/**
* Form validation.
*
* @param array $data array of ("fieldname"=>value) of submitted data
* @param array $files array of uploaded files "element_name"=>tmp_file_path
* @return array of "element_name"=>"error_description" if there are errors,
* or an empty array if everything is OK (true allowed for backwards compatibility too).
*/
public function validation($data, $files): array {
$errors = [];
$local = $this->get_gradeitem();
$gradeitem = $local['gradeitem'];
$item = $local['item'];
if (!grade_verify_idnumber($gradeitem->id, $item->courseid, $gradeitem)) {
$errors['idnumber'] = get_string('idnumbertaken');
}
return $errors;
}
}
@@ -0,0 +1,207 @@
<?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/>.
/**
* Web service functions relating to point grades and grading.
*
* @package core_grades
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace core_grades\grades\grader\gradingpanel\point\external;
use coding_exception;
use context;
use core_grades\component_gradeitem as gradeitem;
use core_grades\component_gradeitems;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use moodle_exception;
use stdClass;
/**
* External grading panel point API
*
* @package core_grades
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fetch extends external_api {
/**
* Describes the parameters for fetching the grading panel for a simple grade.
*
* @return external_function_parameters
* @since Moodle 3.8
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters ([
'component' => new external_value(
PARAM_ALPHANUMEXT,
'The name of the component',
VALUE_REQUIRED
),
'contextid' => new external_value(
PARAM_INT,
'The ID of the context being graded',
VALUE_REQUIRED
),
'itemname' => new external_value(
PARAM_ALPHANUM,
'The grade item itemname being graded',
VALUE_REQUIRED
),
'gradeduserid' => new external_value(
PARAM_INT,
'The ID of the user show',
VALUE_REQUIRED
),
]);
}
/**
* Fetch the data required to build a grading panel for a simple grade.
*
* @param string $component
* @param int $contextid
* @param string $itemname
* @param int $gradeduserid
* @return array
* @throws \dml_exception
* @throws \invalid_parameter_exception
* @throws \restricted_context_exception
* @throws coding_exception
* @throws moodle_exception
* @since Moodle 3.8
*/
public static function execute(string $component, int $contextid, string $itemname, int $gradeduserid): array {
global $USER, $CFG;
require_once("{$CFG->libdir}/gradelib.php");
[
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
] = self::validate_parameters(self::execute_parameters(), [
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
]);
// Validate the context.
$context = context::instance_by_id($contextid);
self::validate_context($context);
// Validate that the supplied itemname is a gradable item.
if (!component_gradeitems::is_valid_itemname($component, $itemname)) {
throw new coding_exception("The '{$itemname}' item is not valid for the '{$component}' component");
}
// Fetch the gradeitem instance.
$gradeitem = gradeitem::instance($component, $context, $itemname);
if (!$gradeitem->is_using_direct_grading()) {
throw new moodle_exception("The {$itemname} item in {$component}/{$contextid} is not configured for direct grading");
}
// Fetch the actual data.
$gradeduser = \core_user::get_user($gradeduserid, '*', MUST_EXIST);
// One can access its own grades. Others just if they're graders.
if ($gradeduserid != $USER->id) {
$gradeitem->require_user_can_grade($gradeduser, $USER);
}
$hasgrade = $gradeitem->user_has_grade($gradeduser);
$grade = $gradeitem->get_formatted_grade_for_user($gradeduser, $USER);
$isgrading = $gradeitem->user_can_grade($gradeduser, $USER);
// Set up some items we need to return on other interfaces.
$gradegrade = \grade_grade::fetch(['itemid' => $gradeitem->get_grade_item()->id, 'userid' => $gradeduser->id]);
$gradername = $gradegrade ? fullname(\core_user::get_user($gradegrade->usermodified)) : null;
return self::get_fetch_data($grade, $hasgrade, $gradeitem, $gradername, $isgrading);
}
/**
* Get the data to be fetched.
*
* @param stdClass $grade
* @param bool $hasgrade
* @param gradeitem $gradeitem
* @param string|null $gradername
* @param bool $isgrading
* @return array
*/
public static function get_fetch_data(
stdClass $grade,
bool $hasgrade,
gradeitem $gradeitem,
?string $gradername,
bool $isgrading = false
): array {
$templatename = 'core_grades/grades/grader/gradingpanel/point';
// We do not want to display anything if we are showing the grade as a letter. For example the 'Grade' might
// read 'B-'. We do not want to show the user the actual point they were given. See MDL-71439.
if (($gradeitem->get_grade_item()->get_displaytype() == GRADE_DISPLAY_TYPE_LETTER) && !$isgrading) {
$templatename = 'core_grades/grades/grader/gradingpanel/point_blank';
}
return [
'templatename' => $templatename,
'hasgrade' => $hasgrade,
'grade' => [
'grade' => $grade->grade,
'usergrade' => $grade->usergrade,
'maxgrade' => (int) $grade->maxgrade,
'gradedby' => $gradername,
'timecreated' => $grade->timecreated,
'timemodified' => $grade->timemodified,
],
'warnings' => [],
];
}
/**
* Describes the data returned from the external function.
*
* @return external_single_structure
* @since Moodle 3.8
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'templatename' => new external_value(PARAM_SAFEPATH, 'The template to use when rendering this data'),
'hasgrade' => new external_value(PARAM_BOOL, 'Does the user have a grade?'),
'grade' => new external_single_structure([
'grade' => new external_value(PARAM_FLOAT, 'The numeric grade'),
'usergrade' => new external_value(PARAM_RAW, 'Current user grade'),
'maxgrade' => new external_value(PARAM_RAW, 'Max possible grade'),
'gradedby' => new external_value(PARAM_RAW, 'The assumed grader of this grading instance'),
'timecreated' => new external_value(PARAM_INT, 'The time that the grade was created'),
'timemodified' => new external_value(PARAM_INT, 'The time that the grade was last updated'),
]),
'warnings' => new external_warnings(),
]);
}
}
@@ -0,0 +1,186 @@
<?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/>.
/**
* Web service functions relating to point grades and grading.
*
* @package core_grades
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace core_grades\grades\grader\gradingpanel\point\external;
use coding_exception;
use context;
use core_grades\component_gradeitem as gradeitem;
use core_grades\component_gradeitems;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use moodle_exception;
/**
* External grading panel point API
*
* @package core_grades
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class store extends external_api {
/**
* Describes the parameters for fetching the grading panel for a simple grade.
*
* @return external_function_parameters
* @since Moodle 3.8
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters ([
'component' => new external_value(
PARAM_ALPHANUMEXT,
'The name of the component',
VALUE_REQUIRED
),
'contextid' => new external_value(
PARAM_INT,
'The ID of the context being graded',
VALUE_REQUIRED
),
'itemname' => new external_value(
PARAM_ALPHANUM,
'The grade item itemname being graded',
VALUE_REQUIRED
),
'gradeduserid' => new external_value(
PARAM_INT,
'The ID of the user show',
VALUE_REQUIRED
),
'notifyuser' => new external_value(
PARAM_BOOL,
'Wheteher to notify the user or not',
VALUE_DEFAULT,
false
),
'formdata' => new external_value(
PARAM_RAW,
'The serialised form data representing the grade',
VALUE_REQUIRED
),
]);
}
/**
* Fetch the data required to build a grading panel for a simple grade.
*
* @param string $component
* @param int $contextid
* @param string $itemname
* @param int $gradeduserid
* @param bool $notifyuser
* @param string $formdata
* @return array
* @throws \dml_exception
* @throws \invalid_parameter_exception
* @throws \restricted_context_exception
* @throws coding_exception
* @throws moodle_exception
* @since Moodle 3.8
*/
public static function execute(string $component, int $contextid, string $itemname, int $gradeduserid,
bool $notifyuser, string $formdata): array {
global $USER, $CFG;
require_once("{$CFG->libdir}/gradelib.php");
[
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
'notifyuser' => $notifyuser,
'formdata' => $formdata,
] = self::validate_parameters(self::execute_parameters(), [
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
'notifyuser' => $notifyuser,
'formdata' => $formdata,
]);
// Validate the context.
$context = context::instance_by_id($contextid);
self::validate_context($context);
// Validate that the supplied itemname is a gradable item.
if (!component_gradeitems::is_valid_itemname($component, $itemname)) {
throw new coding_exception("The '{$itemname}' item is not valid for the '{$component}' component");
}
// Fetch the gradeitem instance.
$gradeitem = gradeitem::instance($component, $context, $itemname);
// Validate that this gradeitem is actually enabled.
if (!$gradeitem->is_grading_enabled()) {
throw new moodle_exception("Grading is not enabled for {$itemname} in this context");
}
// Fetch the record for the graded user.
$gradeduser = \core_user::get_user($gradeduserid);
// Require that this user can save grades.
$gradeitem->require_user_can_grade($gradeduser, $USER);
if (!$gradeitem->is_using_direct_grading()) {
throw new moodle_exception("The {$itemname} item in {$component}/{$contextid} is not configured for direct grading");
}
// Parse the serialised string into an object.
$data = [];
parse_str($formdata, $data);
// Grade.
$gradeitem->store_grade_from_formdata($gradeduser, $USER, (object) $data);
$hasgrade = $gradeitem->user_has_grade($gradeduser);
// Notify.
if ($notifyuser) {
// Send notification.
$gradeitem->send_student_notification($gradeduser, $USER);
}
// Fetch the updated grade back out.
$grade = $gradeitem->get_formatted_grade_for_user($gradeduser, $USER);
$gradegrade = \grade_grade::fetch(['itemid' => $gradeitem->get_grade_item()->id, 'userid' => $gradeduser->id]);
$gradername = $gradegrade ? fullname(\core_user::get_user($gradegrade->usermodified)) : null;
return fetch::get_fetch_data($grade, $hasgrade, $gradeitem, $gradername);
}
/**
* Describes the data returned from the external function.
*
* @return external_single_structure
* @since Moodle 3.8
*/
public static function execute_returns(): external_single_structure {
return fetch::execute_returns();
}
}
@@ -0,0 +1,211 @@
<?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/>.
/**
* Web service functions relating to scale grades and grading.
*
* @package core_grades
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace core_grades\grades\grader\gradingpanel\scale\external;
use coding_exception;
use context;
use core_grades\component_gradeitem as gradeitem;
use core_grades\component_gradeitems;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_multiple_structure;
use core_external\external_single_structure;
use core_external\external_value;
use core_external\external_warnings;
use moodle_exception;
use stdClass;
/**
* External grading panel scale API
*
* @package core_grades
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fetch extends external_api {
/**
* Describes the parameters for fetching the grading panel for a simple grade.
*
* @return external_function_parameters
* @since Moodle 3.8
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters ([
'component' => new external_value(
PARAM_ALPHANUMEXT,
'The name of the component',
VALUE_REQUIRED
),
'contextid' => new external_value(
PARAM_INT,
'The ID of the context being graded',
VALUE_REQUIRED
),
'itemname' => new external_value(
PARAM_ALPHANUM,
'The grade item itemname being graded',
VALUE_REQUIRED
),
'gradeduserid' => new external_value(
PARAM_INT,
'The ID of the user show',
VALUE_REQUIRED
),
]);
}
/**
* Fetch the data required to build a grading panel for a simple grade.
*
* @param string $component
* @param int $contextid
* @param string $itemname
* @param int $gradeduserid
* @return array
* @throws \dml_exception
* @throws \invalid_parameter_exception
* @throws \restricted_context_exception
* @throws coding_exception
* @throws moodle_exception
* @since Moodle 3.8
*/
public static function execute(string $component, int $contextid, string $itemname, int $gradeduserid): array {
global $USER, $CFG;
require_once("{$CFG->libdir}/gradelib.php");
[
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
] = self::validate_parameters(self::execute_parameters(), [
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
]);
// Validate the context.
$context = context::instance_by_id($contextid);
self::validate_context($context);
// Validate that the supplied itemname is a gradable item.
if (!component_gradeitems::is_valid_itemname($component, $itemname)) {
throw new coding_exception("The '{$itemname}' item is not valid for the '{$component}' component");
}
// Fetch the gradeitem instance.
$gradeitem = gradeitem::instance($component, $context, $itemname);
if (!$gradeitem->is_using_scale()) {
throw new moodle_exception("The {$itemname} item in {$component}/{$contextid} is not configured for grading with scales");
}
$gradeduser = \core_user::get_user($gradeduserid, '*', MUST_EXIST);
// One can access its own grades. Others just if they're graders.
if ($gradeduserid != $USER->id) {
$gradeitem->require_user_can_grade($gradeduser, $USER);
}
// Set up some items we need to return on other interfaces.
$gradegrade = \grade_grade::fetch(['itemid' => $gradeitem->get_grade_item()->id, 'userid' => $gradeduser->id]);
$gradername = $gradegrade ? fullname(\core_user::get_user($gradegrade->usermodified)) : null;
$maxgrade = (int) $gradeitem->get_grade_item()->grademax;
return self::get_fetch_data($gradeitem, $gradeduser, $maxgrade, $gradername);
}
/**
* Get the data to be fetched.
*
* @param gradeitem $gradeitem
* @param stdClass $gradeduser
* @param int $maxgrade
* @param string|null $gradername
* @return array
*/
public static function get_fetch_data(gradeitem $gradeitem, stdClass $gradeduser, int $maxgrade, ?string $gradername): array {
global $USER;
$hasgrade = $gradeitem->user_has_grade($gradeduser);
$grade = $gradeitem->get_formatted_grade_for_user($gradeduser, $USER);
$currentgrade = (int) unformat_float($grade->grade);
$menu = $gradeitem->get_grade_menu();
$values = array_map(function($description, $value) use ($currentgrade) {
return [
'value' => $value,
'title' => $description,
'selected' => ($value == $currentgrade),
];
}, $menu, array_keys($menu));
return [
'templatename' => 'core_grades/grades/grader/gradingpanel/scale',
'hasgrade' => $hasgrade,
'grade' => [
'options' => $values,
'usergrade' => $grade->usergrade,
'maxgrade' => $maxgrade,
'gradedby' => $gradername,
'timecreated' => $grade->timecreated,
'timemodified' => $grade->timemodified,
],
'warnings' => [],
];
}
/**
* Describes the data returned from the external function.
*
* @return external_single_structure
* @since Moodle 3.8
*/
public static function execute_returns(): external_single_structure {
return new external_single_structure([
'templatename' => new external_value(PARAM_SAFEPATH, 'The template to use when rendering this data'),
'hasgrade' => new external_value(PARAM_BOOL, 'Does the user have a grade?'),
'grade' => new external_single_structure([
'options' => new external_multiple_structure(
new external_single_structure([
'value' => new external_value(PARAM_FLOAT, 'The grade value'),
'title' => new external_value(PARAM_RAW, 'The description fo the option'),
'selected' => new external_value(PARAM_BOOL, 'Whether this item is currently selected'),
]),
'The description of the grade option'
),
'usergrade' => new external_value(PARAM_RAW, 'Current user grade'),
'maxgrade' => new external_value(PARAM_RAW, 'Max possible grade'),
'gradedby' => new external_value(PARAM_RAW, 'The assumed grader of this grading instance'),
'timecreated' => new external_value(PARAM_INT, 'The time that the grade was created'),
'timemodified' => new external_value(PARAM_INT, 'The time that the grade was last updated'),
]),
'warnings' => new external_warnings(),
]);
}
}
@@ -0,0 +1,180 @@
<?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/>.
/**
* Web service functions relating to scale grades and grading.
*
* @package core_grades
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace core_grades\grades\grader\gradingpanel\scale\external;
use coding_exception;
use context;
use core_grades\component_gradeitem as gradeitem;
use core_grades\component_gradeitems;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_single_structure;
use core_external\external_value;
use moodle_exception;
/**
* External grading panel scale API
*
* @package core_grades
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class store extends external_api {
/**
* Describes the parameters for fetching the grading panel for a simple grade.
*
* @return external_function_parameters
* @since Moodle 3.8
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters ([
'component' => new external_value(
PARAM_ALPHANUMEXT,
'The name of the component',
VALUE_REQUIRED
),
'contextid' => new external_value(
PARAM_INT,
'The ID of the context being graded',
VALUE_REQUIRED
),
'itemname' => new external_value(
PARAM_ALPHANUM,
'The grade item itemname being graded',
VALUE_REQUIRED
),
'gradeduserid' => new external_value(
PARAM_INT,
'The ID of the user show',
VALUE_REQUIRED
),
'notifyuser' => new external_value(
PARAM_BOOL,
'Wheteher to notify the user or not',
VALUE_DEFAULT,
false
),
'formdata' => new external_value(
PARAM_RAW,
'The serialised form data representing the grade',
VALUE_REQUIRED
),
]);
}
/**
* Fetch the data required to build a grading panel for a simple grade.
*
* @param string $component
* @param int $contextid
* @param string $itemname
* @param int $gradeduserid
* @param string $formdata
* @param bool $notifyuser
* @return array
* @throws coding_exception
* @throws moodle_exception
* @since Moodle 3.8
*/
public static function execute(string $component, int $contextid, string $itemname, int $gradeduserid,
bool $notifyuser, string $formdata): array {
global $USER, $CFG;
require_once("{$CFG->libdir}/gradelib.php");
[
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
'notifyuser' => $notifyuser,
'formdata' => $formdata,
] = self::validate_parameters(self::execute_parameters(), [
'component' => $component,
'contextid' => $contextid,
'itemname' => $itemname,
'gradeduserid' => $gradeduserid,
'notifyuser' => $notifyuser,
'formdata' => $formdata,
]);
// Validate the context.
$context = context::instance_by_id($contextid);
self::validate_context($context);
// Validate that the supplied itemname is a gradable item.
if (!component_gradeitems::is_valid_itemname($component, $itemname)) {
throw new coding_exception("The '{$itemname}' item is not valid for the '{$component}' component");
}
// Fetch the gradeitem instance.
$gradeitem = gradeitem::instance($component, $context, $itemname);
// Validate that this gradeitem is actually enabled.
if (!$gradeitem->is_grading_enabled()) {
throw new moodle_exception("Grading is not enabled for {$itemname} in this context");
}
// Fetch the record for the graded user.
$gradeduser = \core_user::get_user($gradeduserid);
// Require that this user can save grades.
$gradeitem->require_user_can_grade($gradeduser, $USER);
if (!$gradeitem->is_using_scale()) {
throw new moodle_exception("The {$itemname} item in {$component}/{$contextid} is not configured for grading with scales");
}
// Parse the serialised string into an object.
$data = [];
parse_str($formdata, $data);
// Grade.
$gradeitem->store_grade_from_formdata($gradeduser, $USER, (object) $data);
// Notify.
if ($notifyuser) {
// Send notification.
$gradeitem->send_student_notification($gradeduser, $USER);
}
$gradegrade = \grade_grade::fetch(['itemid' => $gradeitem->get_grade_item()->id, 'userid' => $gradeduser->id]);
$gradername = $gradegrade ? fullname(\core_user::get_user($gradegrade->usermodified)) : null;
$maxgrade = (int) $gradeitem->get_grade_item()->grademax;
return fetch::get_fetch_data($gradeitem, $gradeduser, $maxgrade, $gradername);
}
/**
* Describes the data returned from the external function.
*
* @return external_single_structure
* @since Moodle 3.8
*/
public static function execute_returns(): external_single_structure {
return fetch::execute_returns();
}
}
@@ -0,0 +1,43 @@
<?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/>.
/**
* Grade item, itemnumber mapping.
*
* @package core_grades
* @copyright Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace core_grades\local\gradeitem;
/**
* Grade item, itemnumber mapping.
*
* @package core_grades
* @copyright Andrew Nicols <andrew@nicols.co.uk>
*/
interface advancedgrading_mapping {
/**
* Get the list of advanced grading item names for this component.
*
* @return array
*/
public static function get_advancedgrading_itemnames(): array;
}
@@ -0,0 +1,48 @@
<?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/>.
/**
* Grade item, fieldname mapping.
*
* @package core_grades
* @copyright Ilya Tregubov <ilya.a.tregubov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace core_grades\local\gradeitem;
/**
* Grade item, fieldname mapping.
*
* @package core_grades
* @copyright Ilya Tregubov <ilya.a.tregubov@gmail.com>
*/
interface fieldname_mapping {
/**
* Get the suffixed field name for an activity field mapped from its itemnumber.
*
* For legacy reasons, the first itemnumber has no suffix on field names.
*
* @param string $component The component that the grade item belongs to
* @param int $itemnumber The grade itemnumber
* @param string $fieldname The name of the field to be rewritten
* @return string The translated field name
*/
public static function get_field_name_for_itemnumber(string $component, int $itemnumber, string $fieldname): string;
}
@@ -0,0 +1,43 @@
<?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/>.
/**
* Grade item, itemnumber mapping.
*
* @package core_grades
* @copyright Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types = 1);
namespace core_grades\local\gradeitem;
/**
* Grade item, itemnumber mapping.
*
* @package core_grades
* @copyright Andrew Nicols <andrew@nicols.co.uk>
*/
interface itemnumber_mapping {
/**
* Get the grade item mapping of item number to item name.
*
* @return array
*/
public static function get_itemname_mapping_for_component(): array;
}
+49
View File
@@ -0,0 +1,49 @@
<?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 templatable;
use renderable;
/**
* The base class for the action bar in the gradebook pages.
*
* @package core_grades
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @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;
}
@@ -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', \single_button::BUTTON_PRIMARY);
$data['manageoutcomesbutton'] = $manageoutcomesbutton->export_for_template($output);
}
return $data;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?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 export pages.
*
* @package core_grades
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class export_action_bar extends action_bar {
/** @var string $activeplugin The plugin of the current export grades page (xml, ods, ...). */
protected $activeplugin;
/**
* The class constructor.
*
* @param \context $context The context object.
* @param null $unused This parameter has been deprecated since 4.1 and should not be used anymore.
* @param string $activeplugin The plugin of the current export grades page (xml, ods, ...).
*/
public function __construct(\context $context, $unused, string $activeplugin) {
if ($unused !== null) {
debugging('Deprecated argument passed to ' . __FUNCTION__, DEBUG_DEVELOPER);
}
parent::__construct($context);
$this->activeplugin = $activeplugin;
}
/**
* Returns the template for the action bar.
*
* @return string
*/
public function get_template(): string {
return 'core_grades/export_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/export/index.php', ['id' => $courseid]), 'export', $this->activeplugin);
$data = $generalnavselector->export_for_template($output);
// Get all grades export plugins. If there isn't any available export plugins there is no need to create and
// display the exports navigation selector menu. Therefore, return only the current data.
if (!$exports = \grade_helper::get_plugins_export($courseid)) {
return $data;
}
// If exports key management is enabled, always display this item at the end of the list.
if (array_key_exists('keymanager', $exports)) {
$keymanager = $exports['keymanager'];
unset($exports['keymanager']);
$exports['keymanager'] = $keymanager;
}
$exportsmenu = [];
$exportactiveurl = null;
// Generate the data for the exports navigation selector menu.
foreach ($exports as $export) {
$exportsmenu[$export->link->out()] = $export->string;
if ($export->id == $this->activeplugin) {
$exportactiveurl = $export->link->out();
}
}
// This navigation selector menu will contain the links to all available grade export plugin pages.
$exportsurlselect = new \core\output\select_menu('exportas', $exportsmenu, $exportactiveurl);
$exportsurlselect->set_label(get_string('exportas', 'grades'));
$data['exportselector'] = $exportsurlselect->export_for_template($output);
return $data;
}
}
@@ -0,0 +1,62 @@
<?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 exports key manager 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 export_key_manager_action_bar extends action_bar {
/**
* Returns the template for the action bar.
*
* @return string
*/
public function get_template(): string {
return 'core_grades/export_key_manager_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 and exports navigation selector.
$exportnavselectors = new export_action_bar($this->context, null, 'keymanager');
$data = $exportnavselectors->export_for_template($output);
// Add a button to the action bar with a link to the 'add user key' page.
$adduserkeylink = new moodle_url('/grade/export/key.php', ['courseid' => $courseid]);
$adduserkeybutton = new \single_button($adduserkeylink, get_string('adduserkey', 'userkey'),
'get', \single_button::BUTTON_PRIMARY);
$data['adduserkeybutton'] = $adduserkeybutton->export_for_template($output);
return $data;
}
}
@@ -0,0 +1,73 @@
<?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 publish export 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 export_publish_action_bar extends action_bar {
/** @var string $activeplugin The plugin of the current export grades page (xml, ods, ...). */
protected $activeplugin;
/**
* The class constructor.
*
* @param \context $context The context object.
* @param string $activeplugin The plugin of the current export grades page (xml, ods, ...).
*/
public function __construct(\context $context, string $activeplugin) {
parent::__construct($context);
$this->activeplugin = $activeplugin;
}
/**
* Returns the template for the action bar.
*
* @return string
*/
public function get_template(): string {
return 'core_grades/export_publish_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;
// Add a back button to the action bar.
$backlink = new moodle_url("/grade/export/{$this->activeplugin}/index.php", ['id' => $courseid]);
$backbutton = new \single_button($backlink, get_string('back'), 'get');
return [
'backbutton' => $backbutton->export_for_template($output)
];
}
}
+195
View File
@@ -0,0 +1,195 @@
<?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;
use core\output\select_menu;
/**
* 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 <mihail@moodle.com>
* @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 {
$selectmenu = $this->get_action_selector();
if (is_null($selectmenu)) {
return [];
}
return [
'generalnavselector' => $selectmenu->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 \select_menu|null The URL select object.
*/
private function get_action_selector(): ?select_menu {
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;
}
$selectmenu = new select_menu('gradesactionselect', $menu, $this->activeurl->out(false));
$selectmenu->set_label(get_string('gradebooknavigationmenu', 'grades'), ['class' => 'sr-only']);
return $selectmenu;
}
}
@@ -0,0 +1,61 @@
<?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 grade letters 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 grade_letters_action_bar extends action_bar {
/**
* Returns the template for the action bar.
*
* @return string
*/
public function get_template(): string {
return 'core_grades/grade_letters_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 = [];
// If in the course context, we should display the general navigation selector in gradebook.
if ($this->context->contextlevel === CONTEXT_COURSE) {
// Get the data used to output the general navigation selector.
$generalnavselector = new general_action_bar($this->context, new moodle_url('/grade/edit/letter/index.php',
['id' => $this->context->id]), 'letter', 'view');
$data = $generalnavselector->export_for_template($output);
}
// Add a button to the action bar with a link to the 'edit grade letters' page.
$editbuttonlink = new moodle_url('/grade/edit/letter/index.php', ['id' => $this->context->id, 'edit' => 1]);
$editbutton = new \single_button($editbuttonlink, get_string('edit'), 'get', \single_button::BUTTON_PRIMARY);
$data['editbutton'] = $editbutton->export_for_template($output);
return $data;
}
}
@@ -0,0 +1,108 @@
<?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;
use html_writer;
/**
* Renderable class for the action bar elements in the gradebook setup pages.
*
* @package core_grades
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class gradebook_setup_action_bar extends action_bar {
/**
* Returns the template for the action bar.
*
* @return string
*/
public function get_template(): string {
return 'core_grades/gradebook_setup_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 {
global $CFG;
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/tree/index.php', ['id' => $courseid]), 'settings', 'setup');
$data = $generalnavselector->export_for_template($output);
$actions = [];
$additemurl = new moodle_url('#');
// Add a button to the action bar dropdown with a link to the 'add grade item' modal.
$actions[] = new \action_menu_link_secondary(
$additemurl,
null,
get_string('additem', 'grades'),
[
'data-courseid' => $courseid,
'data-itemid' => -1,
'data-trigger' => 'add-item-form',
'data-gprplugin' => 'tree',
]
);
// If outcomes are enabled, add a button to the action bar dropdown with a link to the 'add outcome item' modal.
if (!empty($CFG->enableoutcomes) && count(\grade_outcome::fetch_all_available($courseid)) > 0) {
// Add a button to the action bar dropdown with a link to the 'add outcome item' modal.
$actions[] = new \action_menu_link_secondary(
$additemurl,
null,
get_string('addoutcomeitem', 'grades'),
[
'data-courseid' => $courseid,
'data-itemid' => -1,
'data-trigger' => 'add-outcome-form',
'data-gprplugin' => 'tree',
]
);
}
// Add a button to the action bar dropdown with a link to the 'add category' modal.
$actions[] = new \action_menu_link_secondary(
$additemurl,
null,
get_string('addcategory', 'grades'),
[
'data-courseid' => $courseid,
'data-category' => -1,
'data-trigger' => 'add-category-form',
'data-gprplugin' => 'tree',
]
);
$addmenu = new \action_menu($actions);
$addmenu->set_menu_trigger(get_string('add'), 'btn font-weight-bold');
$data['addmenu'] = $addmenu->export_for_template($output);
return $data;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?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 import pages.
*
* @package core_grades
* @copyright 2021 Mihail Geshoski <mihail@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class import_action_bar extends action_bar {
/** @var string $activeplugin The plugin of the current import grades page (xml, csv, ...). */
protected $activeplugin;
/**
* The class constructor.
*
* @param \context $context The context object.
* @param null $unused This parameter has been deprecated since 4.1 and should not be used anymore.
* @param string $activeplugin The plugin of the current import grades page (xml, csv, ...).
*/
public function __construct(\context $context, $unused, string $activeplugin) {
if ($unused !== null) {
debugging('Deprecated argument passed to ' . __FUNCTION__, DEBUG_DEVELOPER);
}
parent::__construct($context);
$this->activeplugin = $activeplugin;
}
/**
* Returns the template for the action bar.
*
* @return string
*/
public function get_template(): string {
return 'core_grades/import_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/import/index.php', ['id' => $courseid]), 'import', $this->activeplugin);
$data = $generalnavselector->export_for_template($output);
// Get all grades import plugins. If there isn't any available import plugins there is no need to create and
// display the imports navigation selector menu. Therefore, return only the current data.
if (!$imports = \grade_helper::get_plugins_import($courseid)) {
return $data;
}
// If imports key management is enabled, always display this item at the end of the list.
if (array_key_exists('keymanager', $imports)) {
$keymanager = $imports['keymanager'];
unset($imports['keymanager']);
$imports['keymanager'] = $keymanager;
}
$importsmenu = [];
$importactiveurl = null;
// Generate the data for the imports navigation selector menu.
foreach ($imports as $import) {
$importsmenu[$import->link->out()] = $import->string;
if ($import->id == $this->activeplugin) {
$importactiveurl = $import->link->out();
}
}
// This navigation selector menu will contain the links to all available grade export plugin pages.
$importsurlselect = new \core\output\select_menu('importas', $importsmenu, $importactiveurl);
$importsurlselect->set_label(get_string('importas', 'grades'));
$data['importselector'] = $importsurlselect->export_for_template($output);
return $data;
}
}
@@ -0,0 +1,62 @@
<?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 import key manager 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 import_key_manager_action_bar extends action_bar {
/**
* Returns the template for the action bar.
*
* @return string
*/
public function get_template(): string {
return 'core_grades/import_key_manager_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 and imports navigation selector.
$importnavselectors = new import_action_bar($this->context, null, 'keymanager');
$data = $importnavselectors->export_for_template($output);
// Add a button to the action bar with a link to the 'add user key' page.
$adduserkeylink = new moodle_url('/grade/import/key.php', ['courseid' => $courseid]);
$adduserkeybutton = new \single_button($adduserkeylink, get_string('adduserkey', 'userkey'),
'get', \single_button::BUTTON_PRIMARY);
$data['adduserkeybutton'] = $adduserkeybutton->export_for_template($output);
return $data;
}
}
@@ -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', \single_button::BUTTON_PRIMARY);
$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;
}
}
@@ -0,0 +1,64 @@
<?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 scales 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 scales_action_bar extends action_bar {
/**
* Returns the template for the action bar.
*
* @return string
*/
public function get_template(): string {
return 'core_grades/scales_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;
// If in the course context, we should display the general navigation selector in gradebook.
if ($this->context->contextlevel === CONTEXT_COURSE) {
$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/scale/index.php', ['id' => $courseid]), 'scale', 'scale');
$data = $generalnavselector->export_for_template($output);
}
// Add a button to the action bar with a link to the 'add new scale' page.
$addnewscalelink = new moodle_url('/grade/edit/scale/edit.php', ['courseid' => $courseid]);
$addnewscalebutton = new \single_button($addnewscalelink, get_string('scalescustomcreate'),
'get', \single_button::BUTTON_PRIMARY);
$data['addnewscalebutton'] = $addnewscalebutton->export_for_template($output);
return $data;
}
}
@@ -0,0 +1,40 @@
<?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\privacy;
use grade_grade;
/**
* A grade_item which has a reference to its historical content.
*
* @package core_grades
* @category grade
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class grade_grade_with_history extends grade_grade {
public int $historyid;
public function __construct(\stdClass $params = null, $fetch = true) {
// The grade history is not a real grade_grade so we remove the ID.
$this->historyid = $params->id;
unset($params->id);
parent::__construct($params, $fetch);
}
}
File diff suppressed because it is too large Load Diff