Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]

   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   * Definition of grade outcome class
  19   *
  20   * @package   core_grades
  21   * @category  grade
  22   * @copyright 2006 Nicolas Connault
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  require_once ('grade_object.php');
  29  
  30  /**
  31   * Class representing a grade outcome.
  32   *
  33   * It is responsible for handling its DB representation, modifying and returning its metadata.
  34   *
  35   * @package   core_grades
  36   * @category  grade
  37   * @copyright 2006 Nicolas Connault
  38   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class grade_outcome extends grade_object {
  41      /**
  42       * DB Table (used by grade_object).
  43       * @var string $table
  44       */
  45      public $table = 'grade_outcomes';
  46  
  47      /**
  48       * Array of required table fields, must start with 'id'.
  49       * @var array $required_fields
  50       */
  51      public $required_fields = array('id', 'courseid', 'shortname', 'fullname', 'scaleid','description',
  52                                   'descriptionformat', 'timecreated', 'timemodified', 'usermodified');
  53  
  54      /**
  55       * The course this outcome belongs to.
  56       * @var int $courseid
  57       */
  58      public $courseid;
  59  
  60      /**
  61       * The shortname of the outcome.
  62       * @var string $shortname
  63       */
  64      public $shortname;
  65  
  66      /**
  67       * The fullname of the outcome.
  68       * @var string $fullname
  69       */
  70      public $fullname;
  71  
  72      /**
  73       * A full grade_scale object referenced by $this->scaleid.
  74       * @var object $scale
  75       */
  76      public $scale;
  77  
  78      /**
  79       * The id of the scale referenced by this outcome.
  80       * @var int $scaleid
  81       */
  82      public $scaleid;
  83  
  84      /**
  85       * The description of this outcome - FORMAT_MOODLE.
  86       * @var string $description
  87       */
  88      public $description;
  89  
  90      /**
  91       * The userid of the person who last modified this outcome.
  92       *
  93       * @var int $usermodified
  94       */
  95      public $usermodified;
  96  
  97      /** @var int Identifier of the text format to be used. */
  98      public $descriptionformat = FORMAT_MOODLE;
  99  
 100      /**
 101       * Deletes this outcome from the database.
 102       *
 103       * @param string $source from where was the object deleted (mod/forum, manual, etc.)
 104       * @return bool success
 105       */
 106      public function delete($source=null) {
 107          global $DB;
 108          if (!empty($this->courseid)) {
 109              $DB->delete_records('grade_outcomes_courses', array('outcomeid' => $this->id, 'courseid' => $this->courseid));
 110          }
 111          if (parent::delete($source)) {
 112              $context = context_system::instance();
 113              $fs = get_file_storage();
 114              $files = $fs->get_area_files($context->id, 'grade', 'outcome', $this->id);
 115              foreach ($files as $file) {
 116                  $file->delete();
 117              }
 118              return true;
 119          }
 120          return false;
 121      }
 122  
 123      /**
 124       * Records this object in the Database, sets its id to the returned value, and returns that value.
 125       * If successful this function also fetches the new object data from database and stores it
 126       * in object properties.
 127       *
 128       * @param string $source from where was the object inserted (mod/forum, manual, etc.)
 129       * @param bool $isbulkupdate If bulk grade update is happening.
 130       * @return int PK ID if successful, false otherwise
 131       */
 132      public function insert($source = null, $isbulkupdate = false) {
 133          global $DB;
 134  
 135          $this->timecreated = $this->timemodified = time();
 136  
 137          if ($result = parent::insert($source)) {
 138              if (!empty($this->courseid)) {
 139                  $goc = new stdClass();
 140                  $goc->courseid = $this->courseid;
 141                  $goc->outcomeid = $this->id;
 142                  $DB->insert_record('grade_outcomes_courses', $goc);
 143              }
 144          }
 145          return $result;
 146      }
 147  
 148      /**
 149       * In addition to update() it also updates grade_outcomes_courses if needed
 150       *
 151       * @param string $source from where was the object inserted
 152       * @param bool $isbulkupdate If bulk grade update is happening.
 153       * @return bool success
 154       */
 155      public function update($source = null, $isbulkupdate = false) {
 156          $this->timemodified = time();
 157  
 158          if ($result = parent::update($source)) {
 159              if (!empty($this->courseid)) {
 160                  $this->use_in($this->courseid);
 161              }
 162          }
 163          return $result;
 164      }
 165  
 166      /**
 167       * Mark outcome as used in a course
 168       *
 169       * @param int $courseid
 170       * @return False if invalid courseid requested
 171       */
 172      public function use_in($courseid) {
 173          global $DB;
 174          if (!empty($this->courseid) and $courseid != $this->courseid) {
 175              return false;
 176          }
 177  
 178          if (!$DB->record_exists('grade_outcomes_courses', array('courseid' => $courseid, 'outcomeid' => $this->id))) {
 179              $goc = new stdClass();
 180              $goc->courseid  = $courseid;
 181              $goc->outcomeid = $this->id;
 182              $DB->insert_record('grade_outcomes_courses', $goc);
 183          }
 184          return true;
 185      }
 186  
 187      /**
 188       * Finds and returns a grade_outcome instance based on params.
 189       *
 190       * @static
 191       * @param array $params associative arrays varname=>value
 192       * @return object grade_outcome instance or false if none found.
 193       */
 194      public static function fetch($params) {
 195          return grade_object::fetch_helper('grade_outcomes', 'grade_outcome', $params);
 196      }
 197  
 198      /**
 199       * Finds and returns all grade_outcome instances based on params.
 200       *
 201       * @static
 202       * @param array $params associative arrays varname=>value
 203       * @return array array of grade_outcome insatnces or false if none found.
 204       */
 205      public static function fetch_all($params) {
 206          return grade_object::fetch_all_helper('grade_outcomes', 'grade_outcome', $params);
 207      }
 208  
 209      /**
 210       * Instantiates a grade_scale object whose data is retrieved from the database
 211       *
 212       * @return grade_scale
 213       */
 214      public function load_scale() {
 215          if (empty($this->scale->id) or $this->scale->id != $this->scaleid) {
 216              $this->scale = grade_scale::fetch(array('id'=>$this->scaleid));
 217              $this->scale->load_items();
 218          }
 219          return $this->scale;
 220      }
 221  
 222      /**
 223       * Static function returning all global outcomes
 224       *
 225       * @static
 226       * @return array
 227       */
 228      public static function fetch_all_global() {
 229          if (!$outcomes = grade_outcome::fetch_all(array('courseid'=>null))) {
 230              $outcomes = array();
 231          }
 232          return $outcomes;
 233      }
 234  
 235      /**
 236       * Static function returning all local course outcomes
 237       *
 238       * @static
 239       * @param int $courseid
 240       * @return array
 241       */
 242      public static function fetch_all_local($courseid) {
 243          if (!$outcomes =grade_outcome::fetch_all(array('courseid'=>$courseid))) {
 244              $outcomes = array();
 245          }
 246          return $outcomes;
 247      }
 248  
 249      /**
 250       * Static method that returns all outcomes available in course
 251       *
 252       * @static
 253       * @param int $courseid
 254       * @return array
 255       */
 256      public static function fetch_all_available($courseid) {
 257          global $CFG, $DB;
 258  
 259          $result = array();
 260          $params = array($courseid);
 261          $sql = "SELECT go.*
 262                    FROM {grade_outcomes} go, {grade_outcomes_courses} goc
 263                   WHERE go.id = goc.outcomeid AND goc.courseid = ?
 264                ORDER BY go.id ASC";
 265  
 266          if ($datas = $DB->get_records_sql($sql, $params)) {
 267              foreach($datas as $data) {
 268                  $instance = new grade_outcome();
 269                  grade_object::set_properties($instance, $data);
 270                  $result[$instance->id] = $instance;
 271              }
 272          }
 273          return $result;
 274      }
 275  
 276  
 277      /**
 278       * Returns the most descriptive field for this object. This is a standard method used
 279       * when we do not know the exact type of an object.
 280       *
 281       * @return string name
 282       */
 283      public function get_name() {
 284          // Grade outcomes can be created at site or course context, so set the filter context appropriately.
 285          $context = empty($this->courseid) ? context_system::instance() : context_course::instance($this->courseid);
 286          return format_string($this->fullname, false, ["context" => $context]);
 287      }
 288  
 289      /**
 290       * Returns unique outcome short name.
 291       *
 292       * @return string name
 293       */
 294      public function get_shortname() {
 295          return $this->shortname;
 296      }
 297  
 298      /**
 299       * Returns the formatted grade description with URLs converted
 300       *
 301       * @return string
 302       */
 303      public function get_description() {
 304          global $CFG;
 305          require_once($CFG->libdir . '/filelib.php');
 306  
 307          $options = new stdClass;
 308          $options->noclean = true;
 309          $systemcontext = context_system::instance();
 310          $description = file_rewrite_pluginfile_urls($this->description, 'pluginfile.php', $systemcontext->id, 'grade', 'outcome', $this->id);
 311          return format_text($description, $this->descriptionformat, $options);
 312      }
 313  
 314      /**
 315       * Checks if outcome can be deleted.
 316       *
 317       * @return bool
 318       */
 319      public function can_delete() {
 320          if ($this->get_item_uses_count()) {
 321              return false;
 322          }
 323          if (empty($this->courseid)) {
 324              if ($this->get_course_uses_count()) {
 325                  return false;
 326              }
 327          }
 328          return true;
 329      }
 330  
 331      /**
 332       * Returns the number of places where outcome is used.
 333       *
 334       * @return int
 335       */
 336      public function get_course_uses_count() {
 337          global $DB;
 338  
 339          if (!empty($this->courseid)) {
 340              return 1;
 341          }
 342  
 343          return $DB->count_records('grade_outcomes_courses', array('outcomeid' => $this->id));
 344      }
 345  
 346      /**
 347       * Returns the number of grade items that use this grade outcome
 348       *
 349       * @return int
 350       */
 351      public function get_item_uses_count() {
 352          global $DB;
 353          return $DB->count_records('grade_items', array('outcomeid' => $this->id));
 354      }
 355  
 356      /**
 357       * Computes then returns extra information about this outcome and other objects that are linked to it.
 358       * The average of all grades that use this outcome, for all courses (or 1 course if courseid is given) can
 359       * be requested, and is returned as a float if requested alone. If the list of items that use this outcome
 360       * is also requested, then a single array is returned, which contains the grade_items AND the average grade
 361       * if such is still requested (array('items' => array(...), 'avg' => 2.30)). This combining of two
 362       * methods into one is to save on DB queries, since both queries are similar and can be performed together.
 363       *
 364       * @param int $courseid An optional courseid to narrow down the average to 1 course only
 365       * @param bool $average Whether or not to return the average grade for this outcome
 366       * @param bool $items Whether or not to return the list of items using this outcome
 367       * @return float
 368       */
 369      public function get_grade_info($courseid=null, $average=true, $items=false) {
 370          global $CFG, $DB;
 371  
 372          if (!isset($this->id)) {
 373              debugging("You must setup the outcome's id before calling its get_grade_info() method!");
 374              return false; // id must be defined for this to work
 375          }
 376  
 377          if ($average === false && $items === false) {
 378              debugging('Either the 1st or 2nd param of grade_outcome::get_grade_info() must be true, or both, but not both false!');
 379              return false;
 380          }
 381  
 382          $params = array($this->id);
 383  
 384          $wheresql = '';
 385          if (!is_null($courseid)) {
 386              $wheresql = " AND {grade_items}.courseid = ? ";
 387              $params[] = $courseid;
 388          }
 389  
 390          $selectadd = '';
 391          if ($items !== false) {
 392              $selectadd = ", {grade_items}.* ";
 393          }
 394  
 395          $sql = "SELECT finalgrade $selectadd
 396                    FROM {grade_grades}, {grade_items}, {grade_outcomes}
 397                   WHERE {grade_outcomes}.id = {grade_items}.outcomeid
 398                     AND {grade_items}.id = {grade_grades}.itemid
 399                     AND {grade_outcomes}.id = ?
 400                     $wheresql";
 401  
 402          $grades = $DB->get_records_sql($sql, $params);
 403          $retval = array();
 404  
 405          if ($average !== false && count($grades) > 0) {
 406              $count = 0;
 407              $total = 0;
 408  
 409              foreach ($grades as $k => $grade) {
 410                  // Skip null finalgrades
 411                  if (!is_null($grade->finalgrade)) {
 412                      $total += $grade->finalgrade;
 413                      $count++;
 414                  }
 415                  unset($grades[$k]->finalgrade);
 416              }
 417  
 418              $retval['avg'] = $total / $count;
 419          }
 420  
 421          if ($items !== false) {
 422              foreach ($grades as $grade) {
 423                  $retval['items'][$grade->id] = new grade_item($grade);
 424              }
 425          }
 426  
 427          return $retval;
 428      }
 429  }