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.
   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   * Evidence persistent file.
  19   *
  20   * @package    core_competency
  21   * @copyright  2015 Frédéric Massart - FMCorz.net
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_competency;
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  use coding_exception;
  29  use context;
  30  use context_user;
  31  use lang_string;
  32  use moodle_exception;
  33  use stdClass;
  34  
  35  /**
  36   * Evidence persistent class.
  37   *
  38   * @package    core_competency
  39   * @copyright  2015 Frédéric Massart - FMCorz.net
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class evidence extends persistent {
  43  
  44      const TABLE = 'competency_evidence';
  45  
  46      /** Action logging. */
  47      const ACTION_LOG = 0;
  48      /** Action rating a competency when no rating is set. */
  49      const ACTION_COMPLETE = 2;
  50      /** Action rating a competency. */
  51      const ACTION_OVERRIDE = 3;
  52  
  53      /**
  54       * Return the definition of the properties of this model.
  55       *
  56       * @return array
  57       */
  58      protected static function define_properties() {
  59          return array(
  60              'usercompetencyid' => array(
  61                  'type' => PARAM_INT
  62              ),
  63              'contextid' => array(
  64                  'type' => PARAM_INT
  65              ),
  66              'action' => array(
  67                  'type' => PARAM_INT,
  68                  'choices' => array(self::ACTION_LOG, self::ACTION_COMPLETE, self::ACTION_OVERRIDE)
  69              ),
  70              'actionuserid' => array(
  71                  'type' => PARAM_INT,
  72                  'default' => null,
  73                  'null' => NULL_ALLOWED
  74              ),
  75              'descidentifier' => array(
  76                  'type' => PARAM_STRINGID
  77              ),
  78              'desccomponent' => array(
  79                  'type' => PARAM_COMPONENT
  80              ),
  81              'desca' => array(
  82                  'type' => PARAM_RAW,
  83                  'default' => null,
  84                  'null' => NULL_ALLOWED
  85              ),
  86              'url' => array(
  87                  'type' => PARAM_URL,
  88                  'default' => null,
  89                  'null' => NULL_ALLOWED
  90              ),
  91              'grade' => array(
  92                  'type' => PARAM_INT,
  93                  'default' => null,
  94                  'null' => NULL_ALLOWED
  95              ),
  96              'note' => array(
  97                  'type' => PARAM_NOTAGS,
  98                  'default' => null,
  99                  'null' => NULL_ALLOWED
 100              )
 101          );
 102      }
 103  
 104      /**
 105       * Return the competency linked to this.
 106       *
 107       * @return competency
 108       */
 109      public function get_competency() {
 110          return user_competency::get_competency_by_usercompetencyid($this->get('usercompetencyid'));
 111      }
 112  
 113      /**
 114       * Return the evidence's context.
 115       *
 116       * @return context
 117       */
 118      public function get_context() {
 119          return context::instance_by_id($this->get('contextid'));
 120      }
 121  
 122      /**
 123       * Convenience method to get the description $a.
 124       *
 125       * @return mixed
 126       */
 127      protected function get_desca() {
 128          $value = $this->raw_get('desca');
 129          if ($value !== null) {
 130              $value = json_decode($value);
 131          }
 132          return $value;
 133      }
 134  
 135      /**
 136       * Convenience method to get the description.
 137       *
 138       * @return lang_string
 139       */
 140      public function get_description() {
 141          return new lang_string($this->get('descidentifier'), $this->get('desccomponent'), $this->get_desca());
 142      }
 143  
 144      /**
 145       * Convenience method to set the description $a.
 146       *
 147       * @param mixed $value
 148       * @return mixed
 149       */
 150      protected function set_desca($value) {
 151          if ($value !== null) {
 152              if (!is_scalar($value) && !is_array($value) && !($value instanceof stdClass)) {
 153                  throw new coding_exception('$a format not supported.');
 154              }
 155              $value = json_encode($value);
 156          }
 157          $this->raw_set('desca', $value);
 158      }
 159  
 160      /**
 161       * Convenience method handling moodle_urls.
 162       *
 163       * @param null|string|moodle_url $url The URL.
 164       */
 165      protected function set_url($url) {
 166          if ($url instanceof \moodle_url) {
 167              $url = $url->out(false);
 168          }
 169          $this->raw_set('url', $url);
 170      }
 171  
 172      /**
 173       * Validate the action user ID.
 174       *
 175       * @param  int $value A user ID.
 176       * @return true|lang_string
 177       */
 178      protected function validate_actionuserid($value) {
 179          if ($value !== null && !\core_user::is_real_user($value)) {
 180              return new lang_string('invaliddata', 'error');
 181          }
 182          return true;
 183      }
 184  
 185      /**
 186       * Validate the context ID.
 187       *
 188       * @param  int $value
 189       * @return true|lang_string
 190       */
 191      protected function validate_contextid($value) {
 192          try {
 193              context::instance_by_id($value);
 194          } catch (moodle_exception $e) {
 195              // That does not look good...
 196              return new lang_string('invaliddata', 'error');
 197          }
 198          return true;
 199      }
 200  
 201      /**
 202       * Validate the description $a.
 203       *
 204       * @param string $value
 205       * @return true|lang_string
 206       */
 207      protected function validate_desca($value) {
 208          if ($value === null) {
 209              return true;
 210          }
 211  
 212          $desc = json_decode($value);
 213          if ($desc === null && json_last_error() !== JSON_ERROR_NONE) {
 214              return new lang_string('invaliddata', 'error');
 215          }
 216  
 217          return true;
 218      }
 219  
 220      /**
 221       * Validate the description identifier.
 222       *
 223       * Only validate string existence during create. If the string is removed later on we should
 224       * not prevent this model from being updated. Alternatively we could check if the string has
 225       * changed before performing the check but this overhead is not required for now.
 226       * An evidence should usually never be updated anyway.
 227       *
 228       * @param  string $value
 229       * @return true|lang_string
 230       */
 231      protected function validate_descidentifier($value) {
 232          if (!$this->get('id') && !get_string_manager()->string_exists($value, $this->get('desccomponent'))) {
 233              return new lang_string('invalidevidencedesc', 'core_competency');
 234          }
 235  
 236          return true;
 237      }
 238  
 239      /**
 240       * Validate the grade.
 241       *
 242       * For performance reason we do not validate that the grade is a valid item of the
 243       * scale associated with the competency or framework.
 244       *
 245       * @param int $value The value.
 246       * @return true|lang_string
 247       */
 248      protected function validate_grade($value) {
 249          if ($value !== null && $value <= 0) {
 250              return new lang_string('invalidgrade', 'core_competency');
 251          }
 252  
 253          $action = $this->get('action');
 254          if ($value === null && $action == self::ACTION_COMPLETE) {
 255              return new lang_string('invalidgrade', 'core_competency');
 256  
 257          } else if ($value !== null && $action == self::ACTION_LOG) {
 258              return new lang_string('invalidgrade', 'core_competency');
 259          }
 260  
 261          if ($value !== null) {
 262              // TODO MDL-52243 Use a core method to validate the grade_scale item.
 263              // Check if grade exist in the scale item values.
 264              $competency = $this->get_competency();
 265              if (!array_key_exists($value - 1, $competency->get_scale()->scale_items)) {
 266                  return new lang_string('invalidgrade', 'core_competency');
 267              }
 268          }
 269  
 270          return true;
 271      }
 272  
 273      /**
 274       * Validate the user competency.
 275       *
 276       * @param  int $value
 277       * @return true|lang_string
 278       */
 279      protected function validate_usercompetencyid($value) {
 280          if (!user_competency::record_exists($value)) {
 281              return new lang_string('invaliddata', 'error');
 282          }
 283          return true;
 284      }
 285  
 286      /**
 287       * Whether the current user can delete an evidence in the context of a user.
 288       *
 289       * @param int $userid The user ID the evidence belongs to.
 290       * @return bool
 291       */
 292      public static function can_delete_user($userid) {
 293          return has_capability('moodle/competency:evidencedelete', context_user::instance($userid));
 294      }
 295  
 296      /**
 297       * Load a list of records in a context for a user competency.
 298       *
 299       * @param int $usercompetencyid The id of the user competency.
 300       * @param context $context Context to filter the evidence list.
 301       * @param string $sort The field from the evidence table to sort on.
 302       * @param string $order The sort direction
 303       * @param int $skip Limitstart.
 304       * @param int $limit Number of rows to return.
 305       *
 306       * @return \core_competency\persistent[]
 307       */
 308      public static function get_records_for_usercompetency($usercompetencyid,
 309                                                            \context $context,
 310                                                            $sort = '',
 311                                                            $order = 'ASC',
 312                                                            $skip = 0,
 313                                                            $limit = 0) {
 314          global $DB;
 315  
 316          $params = array(
 317              'usercompid' => $usercompetencyid,
 318              'path' => $context->path . '/%',
 319              'contextid' => $context->id
 320          );
 321  
 322          if (!empty($sort)) {
 323              $sort = ' ORDER BY e.' . $sort . ' ' . $order . ', e.id ASC';
 324          } else {
 325              $sort = ' ORDER BY e.id ASC';
 326          }
 327  
 328          $sql = 'SELECT e.*
 329                    FROM {' . static::TABLE . '} e
 330                    JOIN {context} c ON c.id = e.contextid
 331                   WHERE (c.path LIKE :path OR c.id = :contextid)
 332                     AND e.usercompetencyid = :usercompid
 333                   ' . $sort;
 334          $records = $DB->get_records_sql($sql, $params, $skip, $limit);
 335          $instances = array();
 336  
 337          foreach ($records as $record) {
 338              $newrecord = new static(0, $record);
 339              array_push($instances, $newrecord);
 340          }
 341          return $instances;
 342      }
 343  
 344  }