Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 39 and 311]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Grade item storage for mod_forum.
  19   *
  20   * @package   mod_forum
  21   * @copyright Andrew Nicols <andrew@nicols.co.uk>
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  declare(strict_types = 1);
  26  
  27  namespace mod_forum\grades;
  28  
  29  use coding_exception;
  30  use context;
  31  use core_grades\component_gradeitem;
  32  use core_grades\local\gradeitem as gradeitem;
  33  use mod_forum\local\container as forum_container;
  34  use mod_forum\local\entities\forum as forum_entity;
  35  use required_capability_exception;
  36  use stdClass;
  37  
  38  /**
  39   * Grade item storage for mod_forum.
  40   *
  41   * @package   mod_forum
  42   * @copyright Andrew Nicols <andrew@nicols.co.uk>
  43   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  44   */
  45  class forum_gradeitem extends component_gradeitem {
  46      /** @var forum_entity The forum entity being graded */
  47      protected $forum;
  48  
  49      /**
  50       * Return an instance based on the context in which it is used.
  51       *
  52       * @param context $context
  53       */
  54      public static function load_from_context(context $context): parent {
  55          // Get all the factories that are required.
  56          $vaultfactory = forum_container::get_vault_factory();
  57          $forumvault = $vaultfactory->get_forum_vault();
  58  
  59          $forum = $forumvault->get_from_course_module_id((int) $context->instanceid);
  60  
  61          return static::load_from_forum_entity($forum);
  62      }
  63  
  64      /**
  65       * Return an instance using the forum_entity instance.
  66       *
  67       * @param forum_entity $forum
  68       *
  69       * @return forum_gradeitem
  70       */
  71      public static function load_from_forum_entity(forum_entity $forum): self {
  72          $instance = new static('mod_forum', $forum->get_context(), 'forum');
  73          $instance->forum = $forum;
  74  
  75          return $instance;
  76      }
  77  
  78      /**
  79       * The table name used for grading.
  80       *
  81       * @return string
  82       */
  83      protected function get_table_name(): string {
  84          return 'forum_grades';
  85      }
  86  
  87      /**
  88       * Whether grading is enabled for this item.
  89       *
  90       * @return bool
  91       */
  92      public function is_grading_enabled(): bool {
  93          return $this->forum->is_grading_enabled();
  94      }
  95  
  96      /**
  97       * Whether the grader can grade the gradee.
  98       *
  99       * @param stdClass $gradeduser The user being graded
 100       * @param stdClass $grader The user who is grading
 101       * @return bool
 102       */
 103      public function user_can_grade(stdClass $gradeduser, stdClass $grader): bool {
 104          // Validate the required capabilities.
 105          $managerfactory = forum_container::get_manager_factory();
 106          $capabilitymanager = $managerfactory->get_capability_manager($this->forum);
 107  
 108          return $capabilitymanager->can_grade($grader, $gradeduser);
 109      }
 110  
 111      /**
 112       * Require that the user can grade, throwing an exception if not.
 113       *
 114       * @param stdClass $gradeduser The user being graded
 115       * @param stdClass $grader The user who is grading
 116       * @throws required_capability_exception
 117       */
 118      public function require_user_can_grade(stdClass $gradeduser, stdClass $grader): void {
 119          if (!$this->user_can_grade($gradeduser, $grader)) {
 120              throw new required_capability_exception($this->forum->get_context(), 'mod/forum:grade', 'nopermissions', '');
 121          }
 122      }
 123  
 124      /**
 125       * Get the grade value for this instance.
 126       * The itemname is translated to the relevant grade field on the forum entity.
 127       *
 128       * @return int
 129       */
 130      protected function get_gradeitem_value(): int {
 131          $getter = "get_grade_for_{$this->itemname}";
 132  
 133          return $this->forum->{$getter}();
 134      }
 135  
 136      /**
 137       * Create an empty forum_grade for the specified user and grader.
 138       *
 139       * @param stdClass $gradeduser The user being graded
 140       * @param stdClass $grader The user who is grading
 141       * @return stdClass The newly created grade record
 142       * @throws \dml_exception
 143       */
 144      public function create_empty_grade(stdClass $gradeduser, stdClass $grader): stdClass {
 145          global $DB;
 146  
 147          $grade = (object) [
 148              'forum' => $this->forum->get_id(),
 149              'itemnumber' => $this->itemnumber,
 150              'userid' => $gradeduser->id,
 151              'timemodified' => time(),
 152          ];
 153          $grade->timecreated = $grade->timemodified;
 154  
 155          $gradeid = $DB->insert_record($this->get_table_name(), $grade);
 156  
 157          return $DB->get_record($this->get_table_name(), ['id' => $gradeid]);
 158      }
 159  
 160      /**
 161       * Get the grade for the specified user.
 162       *
 163       * @param stdClass $gradeduser The user being graded
 164       * @param stdClass $grader The user who is grading
 165       * @return stdClass The grade value
 166       * @throws \dml_exception
 167       */
 168      public function get_grade_for_user(stdClass $gradeduser, stdClass $grader = null): ?stdClass {
 169          global $DB;
 170  
 171          $params = [
 172              'forum' => $this->forum->get_id(),
 173              'itemnumber' => $this->itemnumber,
 174              'userid' => $gradeduser->id,
 175          ];
 176  
 177          $grade = $DB->get_record($this->get_table_name(), $params);
 178  
 179          if (empty($grade)) {
 180              $grade = $this->create_empty_grade($gradeduser, $grader);
 181          }
 182  
 183          return $grade ?: null;
 184      }
 185  
 186      /**
 187       * Get the grade status for the specified user.
 188       * Check if a grade obj exists & $grade->grade !== null.
 189       * If the user has a grade return true.
 190       *
 191       * @param stdClass $gradeduser The user being graded
 192       * @return bool The grade exists
 193       * @throws \dml_exception
 194       */
 195      public function user_has_grade(stdClass $gradeduser): bool {
 196          global $DB;
 197  
 198          $params = [
 199              'forum' => $this->forum->get_id(),
 200              'itemnumber' => $this->itemnumber,
 201              'userid' => $gradeduser->id,
 202          ];
 203  
 204          $grade = $DB->get_record($this->get_table_name(), $params);
 205  
 206          if (empty($grade) || $grade->grade === null) {
 207              return false;
 208          }
 209          return true;
 210      }
 211  
 212      /**
 213       * Get grades for all users for the specified gradeitem.
 214       *
 215       * @return stdClass[] The grades
 216       * @throws \dml_exception
 217       */
 218      public function get_all_grades(): array {
 219          global $DB;
 220  
 221          return $DB->get_records($this->get_table_name(), [
 222              'forum' => $this->forum->get_id(),
 223              'itemnumber' => $this->itemnumber,
 224          ]);
 225      }
 226  
 227      /**
 228       * Get the grade item instance id.
 229       *
 230       * This is typically the cmid in the case of an activity, and relates to the iteminstance field in the grade_items
 231       * table.
 232       *
 233       * @return int
 234       */
 235      public function get_grade_instance_id(): int {
 236          return (int) $this->forum->get_id();
 237      }
 238  
 239      /**
 240       * Defines whether only active users in the course should be gradeable.
 241       *
 242       * @return bool Whether only active users in the course should be gradeable.
 243       */
 244      public function should_grade_only_active_users(): bool {
 245          global $CFG;
 246  
 247          $showonlyactiveenrolconfig = !empty($CFG->grade_report_showonlyactiveenrol);
 248          // Grade only active users enrolled in the course either when the 'grade_report_showonlyactiveenrol' user
 249          // preference is set to true or the current user does not have the capability to view suspended users in the
 250          // course. In cases where the 'grade_report_showonlyactiveenrol' user preference is not set we are falling back
 251          // to the set value for the 'grade_report_showonlyactiveenrol' config.
 252          return get_user_preferences('grade_report_showonlyactiveenrol', $showonlyactiveenrolconfig) ||
 253              !has_capability('moodle/course:viewsuspendedusers', \context_course::instance($this->forum->get_course_id()));
 254      }
 255  
 256      /**
 257       * Create or update the grade.
 258       *
 259       * @param stdClass $grade
 260       * @return bool Success
 261       * @throws \dml_exception
 262       * @throws \moodle_exception
 263       * @throws coding_exception
 264       */
 265      protected function store_grade(stdClass $grade): bool {
 266          global $CFG, $DB;
 267          require_once("{$CFG->dirroot}/mod/forum/lib.php");
 268  
 269          if ($grade->forum != $this->forum->get_id()) {
 270              throw new coding_exception('Incorrect forum provided for this grade');
 271          }
 272  
 273          if ($grade->itemnumber != $this->itemnumber) {
 274              throw new coding_exception('Incorrect itemnumber provided for this grade');
 275          }
 276  
 277          // Ensure that the grade is valid.
 278          $this->check_grade_validity($grade->grade);
 279  
 280          $grade->forum = $this->forum->get_id();
 281          $grade->timemodified = time();
 282  
 283          $DB->update_record($this->get_table_name(), $grade);
 284  
 285          // Update in the gradebook (note that 'cmidnumber' is required in order to update grades).
 286          $mapper = forum_container::get_legacy_data_mapper_factory()->get_forum_data_mapper();
 287          $forumrecord = $mapper->to_legacy_object($this->forum);
 288          $forumrecord->cmidnumber = $this->forum->get_course_module_record()->idnumber;
 289  
 290          forum_update_grades($forumrecord, $grade->userid);
 291  
 292          return true;
 293      }
 294  }