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 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 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 scale 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 scale.
  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_scale extends grade_object {
  41      /**
  42       * DB Table (used by grade_object).
  43       * @var string $table
  44       */
  45      public $table = 'scale';
  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', 'userid', 'name', 'scale', 'description', 'descriptionformat', 'timemodified');
  52  
  53      /**
  54       * The course this scale belongs to.
  55       * @var int $courseid
  56       */
  57      public $courseid;
  58  
  59      /**
  60       * The ID of the user who created the scale
  61       * @var int $userid
  62       */
  63      public $userid;
  64  
  65      /**
  66       * The name of the scale.
  67       * @var string $name
  68       */
  69      public $name;
  70  
  71      /**
  72       * The items in this scale.
  73       * @var array $scale_items
  74       */
  75      public $scale_items = array();
  76  
  77      /**
  78       * A string representation of the scale items (a comma-separated list).
  79       * @var string $scale
  80       */
  81      public $scale;
  82  
  83      /**
  84       * A description for this scale.
  85       * @var string $description
  86       */
  87      public $description;
  88  
  89      /**
  90       * Finds and returns a grade_scale instance based on params.
  91       *
  92       * @static
  93       * @param array $params associative arrays varname=>value
  94       * @return object grade_scale instance or false if none found.
  95       */
  96      public static function fetch($params) {
  97          return grade_object::fetch_helper('scale', 'grade_scale', $params);
  98      }
  99  
 100      /**
 101       * Finds and returns all grade_scale instances based on params.
 102       *
 103       * @static
 104       * @param array $params associative arrays varname=>value
 105       * @return array array of grade_scale instances or false if none found.
 106       */
 107      public static function fetch_all($params) {
 108          return grade_object::fetch_all_helper('scale', 'grade_scale', $params);
 109      }
 110  
 111      /**
 112       * Records this object in the Database, sets its id to the returned value, and returns that value.
 113       * If successful this function also fetches the new object data from database and stores it
 114       * in object properties.
 115       *
 116       * @param string $source from where was the object inserted (mod/forum, manual, etc.)
 117       * @return int PK ID if successful, false otherwise
 118       */
 119      public function insert($source=null) {
 120          $this->timecreated = time();
 121          $this->timemodified = time();
 122  
 123          $result = parent::insert($source);
 124          if ($result) {
 125              // Trigger the scale created event.
 126              if (!empty($this->standard)) {
 127                  $eventcontext = context_system::instance();
 128              } else {
 129                  if (!empty($this->courseid)) {
 130                      $eventcontext = context_course::instance($this->courseid);
 131                  } else {
 132                      $eventcontext = context_system::instance();
 133                  }
 134              }
 135              $event = \core\event\scale_created::create(array(
 136                  'objectid' => $result,
 137                  'context' => $eventcontext
 138              ));
 139              $event->trigger();
 140          }
 141          return $result;
 142      }
 143  
 144      /**
 145       * In addition to update() it also updates grade_outcomes_courses if needed
 146       *
 147       * @param string $source from where was the object inserted
 148       * @return bool success
 149       */
 150      public function update($source=null) {
 151          $this->timemodified = time();
 152  
 153          $result = parent::update($source);
 154          if ($result) {
 155              // Trigger the scale updated event.
 156              if (!empty($this->standard)) {
 157                  $eventcontext = context_system::instance();
 158              } else {
 159                  if (!empty($this->courseid)) {
 160                      $eventcontext = context_course::instance($this->courseid);
 161                  } else {
 162                      $eventcontext = context_system::instance();
 163                  }
 164              }
 165              $event = \core\event\scale_updated::create(array(
 166                  'objectid' => $this->id,
 167                  'context' => $eventcontext
 168              ));
 169              $event->trigger();
 170          }
 171          return $result;
 172      }
 173  
 174      /**
 175       * Deletes this scale from the database.
 176       *
 177       * @param string $source from where was the object deleted (mod/forum, manual, etc.)
 178       * @return bool success
 179       */
 180      public function delete($source=null) {
 181          global $DB;
 182  
 183          // Trigger the scale deleted event.
 184          if (!empty($this->standard)) {
 185              $eventcontext = context_system::instance();
 186          } else {
 187              if (!empty($this->courseid)) {
 188                  $eventcontext = context_course::instance($this->courseid);
 189              } else {
 190                  $eventcontext = context_system::instance();
 191              }
 192          }
 193          $event = \core\event\scale_deleted::create(array(
 194              'objectid' => $this->id,
 195              'context' => $eventcontext
 196          ));
 197          $event->trigger();
 198          if (parent::delete($source)) {
 199              $context = context_system::instance();
 200              $fs = get_file_storage();
 201              $files = $fs->get_area_files($context->id, 'grade', 'scale', $this->id);
 202              foreach ($files as $file) {
 203                  $file->delete();
 204              }
 205              return true;
 206          }
 207          return false;
 208      }
 209  
 210      /**
 211       * Returns the most descriptive field for this object. This is a standard method used
 212       * when we do not know the exact type of an object.
 213       *
 214       * @return string name
 215       */
 216      public function get_name() {
 217          // Grade scales can be created at site or course context, so set the filter context appropriately.
 218          $context = empty($this->courseid) ? context_system::instance() : context_course::instance($this->courseid);
 219          return format_string($this->name, false, ['context' => $context]);
 220      }
 221  
 222      /**
 223       * Loads the scale's items into the $scale_items array.
 224       * There are three ways to achieve this:
 225       * 1. No argument given: The $scale string is already loaded and exploded to an array of items.
 226       * 2. A string is given: A comma-separated list of items is exploded into an array of items.
 227       * 3. An array of items is given and saved directly as the array of items for this scale.
 228       *
 229       * @param mixed $items Could be null, a string or an array. The method behaves differently for each case.
 230       * @return array The resulting array of scale items or null if the method failed to produce one.
 231       */
 232      public function load_items($items=NULL) {
 233          if (empty($items)) {
 234              $this->scale_items = explode(',', $this->scale);
 235          } elseif (is_array($items)) {
 236              $this->scale_items = $items;
 237          } else {
 238              $this->scale_items = explode(',', $items);
 239          }
 240  
 241          // Trim whitespace around each value
 242          foreach ($this->scale_items as $key => $val) {
 243              $this->scale_items[$key] = trim($val);
 244          }
 245  
 246          return $this->scale_items;
 247      }
 248  
 249      /**
 250       * Compacts (implodes) the array of items in $scale_items into a comma-separated string, $scale.
 251       * There are three ways to achieve this:
 252       * 1. No argument given: The $scale_items array is already loaded and imploded to a string of items.
 253       * 2. An array is given and is imploded into a string of items.
 254       * 3. A string of items is given and saved directly as the $scale variable.
 255       * NOTE: This method is the exact reverse of load_items, and their input/output should be interchangeable. However,
 256       * because load_items() trims the whitespace around the items, when the string is reconstructed these whitespaces will
 257       * be missing. This is not an issue, but should be kept in mind when comparing the two strings.
 258       *
 259       * @param mixed $items Could be null, a string or an array. The method behaves differently for each case.
 260       * @return array The resulting string of scale items or null if the method failed to produce one.
 261       */
 262      public function compact_items($items=NULL) {
 263          if (empty($items)) {
 264              $this->scale = implode(',', $this->scale_items);
 265          } elseif (is_array($items)) {
 266              $this->scale = implode(',', $items);
 267          } else {
 268              $this->scale = $items;
 269          }
 270  
 271          return $this->scale;
 272      }
 273  
 274      /**
 275       * When called on a loaded scale object (with a valid id) and given a float grade between
 276       * the grademin and grademax, this method returns the scale item that falls closest to the
 277       * float given (which is usually an average of several grades on a scale). If the float falls
 278       * below 1 but above 0, it will be rounded up to 1.
 279       *
 280       * @param float $grade
 281       * @return string
 282       */
 283      public function get_nearest_item($grade) {
 284          global $DB;
 285          // Obtain nearest scale item from average
 286          $scales_array = $DB->get_records('scale', array('id' => $this->id));
 287          $scale = $scales_array[$this->id];
 288          $scales = explode(",", $scale->scale);
 289  
 290          // this could be a 0 when summed and rounded, e.g, 1, no grade, no grade, no grade
 291          if ($grade < 1) {
 292              $grade = 1;
 293          }
 294  
 295          return $scales[$grade-1];
 296      }
 297  
 298      /**
 299       * Static function returning all global scales
 300       *
 301       * @return object
 302       */
 303      public static function fetch_all_global() {
 304          return grade_scale::fetch_all(array('courseid'=>0));
 305      }
 306  
 307      /**
 308       * Static function returning all local course scales
 309       *
 310       * @param int $courseid The course ID
 311       * @return array Returns an array of grade_scale instances
 312       */
 313      public static function fetch_all_local($courseid) {
 314          return grade_scale::fetch_all(array('courseid'=>$courseid));
 315      }
 316  
 317      /**
 318       * Checks if this is the last scale on the site.
 319       *
 320       * @return bool
 321       */
 322      public function is_last_global_scale() {
 323          return ($this->courseid == 0) && (count(self::fetch_all_global()) == 1);
 324      }
 325  
 326      /**
 327       * Checks if scale can be deleted.
 328       *
 329       * @return bool
 330       */
 331      public function can_delete() {
 332          return !$this->is_used() && !$this->is_last_global_scale();
 333      }
 334  
 335      /**
 336       * Returns if scale used anywhere - activities, grade items, outcomes, etc.
 337       *
 338       * @return bool
 339       */
 340      public function is_used() {
 341          global $DB;
 342          global $CFG;
 343  
 344          // count grade items excluding the
 345          $params = array($this->id);
 346          $sql = "SELECT COUNT(id) FROM {grade_items} WHERE scaleid = ? AND outcomeid IS NULL";
 347          if ($DB->count_records_sql($sql, $params)) {
 348              return true;
 349          }
 350  
 351          // count outcomes
 352          $sql = "SELECT COUNT(id) FROM {grade_outcomes} WHERE scaleid = ?";
 353          if ($DB->count_records_sql($sql, $params)) {
 354              return true;
 355          }
 356  
 357          // Ask the competency subsystem.
 358          if (\core_competency\api::is_scale_used_anywhere($this->id)) {
 359              return true;
 360          }
 361  
 362          // Ask all plugins if the scale is used anywhere.
 363          $pluginsfunction = get_plugins_with_function('scale_used_anywhere');
 364          foreach ($pluginsfunction as $plugintype => $plugins) {
 365              foreach ($plugins as $pluginfunction) {
 366                  if ($pluginfunction($this->id)) {
 367                      return true;
 368                  }
 369              }
 370          }
 371  
 372          return false;
 373      }
 374  
 375      /**
 376       * Returns the formatted grade description with URLs converted
 377       *
 378       * @return string
 379       */
 380      public function get_description() {
 381          global $CFG;
 382          require_once($CFG->libdir . '/filelib.php');
 383  
 384          $systemcontext = context_system::instance();
 385          $options = new stdClass;
 386          $options->noclean = true;
 387          $description = file_rewrite_pluginfile_urls($this->description, 'pluginfile.php', $systemcontext->id, 'grade', 'scale', $this->id);
 388          return format_text($description, $this->descriptionformat, $options);
 389      }
 390  }