Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?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);}}