Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 401 and 402] [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   * Statement base object for xAPI structure checking and validation.
  19   *
  20   * @package    core_xapi
  21   * @copyright  2020 Ferran Recio
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_xapi\local;
  26  
  27  use core_xapi\local\statement\item;
  28  use core_xapi\local\statement\item_actor;
  29  use core_xapi\local\statement\item_object;
  30  use core_xapi\local\statement\item_verb;
  31  use core_xapi\local\statement\item_result;
  32  use core_xapi\local\statement\item_attachment;
  33  use core_xapi\local\statement\item_context;
  34  use core_xapi\xapi_exception;
  35  use JsonSerializable;
  36  use stdClass;
  37  
  38  defined('MOODLE_INTERNAL') || die();
  39  
  40  /**
  41   * Privacy Subsystem for core_xapi implementing null_provider.
  42   *
  43   * @copyright  2020 Ferran Recio
  44   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  45   */
  46  class statement implements JsonSerializable {
  47  
  48      /** @var item_actor The statement actor. */
  49      protected $actor = null;
  50  
  51      /** @var item_verb The statement verb. */
  52      protected $verb = null;
  53  
  54      /** @var item_object The statement object. */
  55      protected $object = null;
  56  
  57      /** @var item_result The statement result. */
  58      protected $result = null;
  59  
  60      /** @var item_context The statement context. */
  61      protected $context = null;
  62  
  63      /** @var string The statement timestamp. */
  64      protected $timestamp = null;
  65  
  66      /** @var string The statement stored. */
  67      protected $stored = null;
  68  
  69      /** @var authority The statement authority. */
  70      protected $authority = null;
  71  
  72      /** @var string The statement version. */
  73      protected $version = null;
  74  
  75      /** @var item_attachment[] The statement attachments. */
  76      protected $attachments = null;
  77  
  78      /** @var additionalfields list of additional fields. */
  79      private static $additionalsfields = [
  80          'timestamp', 'stored', 'version'
  81      ];
  82  
  83      /**
  84       * Function to create a full statement from xAPI statement data.
  85       *
  86       * @param stdClass $data the original xAPI statement
  87       * @return statement statement object
  88       */
  89      public static function create_from_data(stdClass $data): self {
  90  
  91          $result  = new self();
  92  
  93          $requiredfields = ['actor', 'verb', 'object'];
  94          foreach ($requiredfields as $required) {
  95              if (!isset($data->$required)) {
  96                  throw new xapi_exception("Missing '{$required}'");
  97              }
  98          }
  99          $result->set_actor(item_actor::create_from_data($data->actor));
 100          $result->set_verb(item_verb::create_from_data($data->verb));
 101          $result->set_object(item_object::create_from_data($data->object));
 102  
 103          if (isset($data->result)) {
 104              $result->set_result(item_result::create_from_data($data->result));
 105          }
 106  
 107          if (!empty($data->attachments)) {
 108              if (!is_array($data->attachments)) {
 109                  throw new xapi_exception("Attachments must be an array");
 110              }
 111              foreach ($data->attachments as $attachment) {
 112                  $result->add_attachment(item_attachment::create_from_data($attachment));
 113              }
 114          }
 115  
 116          if (isset($data->context)) {
 117              $result->set_context(item_context::create_from_data($data->context));
 118          }
 119  
 120          if (isset($data->authority)) {
 121              $result->set_authority(item_actor::create_from_data($data->authority));
 122          }
 123  
 124          // Store other generic xAPI statement fields.
 125          foreach (self::$additionalsfields as $additional) {
 126              if (isset($data->$additional)) {
 127                  $method = 'set_'.$additional;
 128                  $result->$method($data->$additional);
 129              }
 130          }
 131          return $result;
 132      }
 133  
 134      /**
 135       * Return the data to serialize in case JSON statement is needed.
 136       *
 137       * @return stdClass the statement data structure
 138       */
 139      public function jsonSerialize(): stdClass {
 140          $result = (object) [
 141              'actor' => $this->actor,
 142              'verb' => $this->verb,
 143              'object' => $this->object,
 144          ];
 145          if (!empty($this->result)) {
 146              $result->result = $this->result;
 147          }
 148          if (!empty($this->context)) {
 149              $result->context = $this->context;
 150          }
 151          if (!empty($this->authority)) {
 152              $result->authority = $this->authority;
 153          }
 154          if (!empty($this->attachments)) {
 155              $result->attachments = $this->attachments;
 156          }
 157          foreach (self::$additionalsfields as $additional) {
 158              if (!empty($this->$additional)) {
 159                  $result->$additional = $this->$additional;
 160              }
 161          }
 162          return $result;
 163      }
 164  
 165      /**
 166       * Returns a minified version of a given statement.
 167       *
 168       * The returned structure is suitable to store in the "other" field
 169       * of logstore. xAPI standard specifies a list of attributes that can be calculated
 170       * instead of stored literally. This function get rid of these attributes.
 171       *
 172       * Note: it also converts stdClass to assoc array to make it compatible
 173       * with "other" field in the logstore
 174       *
 175       * @return array the minimal statement needed to be stored a part from logstore data
 176       */
 177      public function minify(): ?array {
 178          $result = [];
 179          $fields = ['verb', 'object',  'context', 'result', 'authority', 'attachments'];
 180          foreach ($fields as $field) {
 181              if (!empty($this->$field)) {
 182                  $result[$field] = $this->$field;
 183              }
 184          }
 185          return json_decode(json_encode($result), true);
 186      }
 187  
 188      /**
 189       * Set the statement actor.
 190       *
 191       * @param item_actor $actor actor item
 192       */
 193      public function set_actor(item_actor $actor): void {
 194          $this->actor = $actor;
 195      }
 196  
 197      /**
 198       * Set the statement verb.
 199       *
 200       * @param item_verb $verb verb element
 201       */
 202      public function set_verb(item_verb $verb): void {
 203          $this->verb = $verb;
 204      }
 205  
 206      /**
 207       * Set the statement object.
 208       *
 209       * @param item_object $object compatible object item
 210       */
 211      public function set_object(item_object $object): void {
 212          $this->object = $object;
 213      }
 214  
 215      /**
 216       * Set the statement context.
 217       *
 218       * @param item_context $context context item element
 219       */
 220      public function set_context(item_context $context): void {
 221          $this->context = $context;
 222      }
 223  
 224      /**
 225       * Set the statement result.
 226       *
 227       * @param item_result $result result item element
 228       */
 229      public function set_result(item_result $result): void {
 230          $this->result = $result;
 231      }
 232  
 233      /**
 234       * Set the statement timestamp.
 235       *
 236       * @param string $timestamp timestamp element
 237       */
 238      public function set_timestamp(string $timestamp): void {
 239          $this->timestamp = $timestamp;
 240      }
 241  
 242      /**
 243       * Set the statement stored.
 244       *
 245       * @param string $stored stored element
 246       */
 247      public function set_stored(string $stored): void {
 248          $this->stored = $stored;
 249      }
 250  
 251      /**
 252       * Set the statement authority.
 253       *
 254       * @param item $authority authority item element
 255       */
 256      public function set_authority(item_actor $authority): void {
 257          $this->authority = $authority;
 258      }
 259  
 260      /**
 261       * Set the statement version.
 262       *
 263       * @param string $version version element
 264       */
 265      public function set_version(string $version): void {
 266          $this->version = $version;
 267      }
 268  
 269      /**
 270       * Adds and attachment to the statement.
 271       *
 272       * @param item $attachments attachments item element
 273       */
 274      public function add_attachment(item_attachment $attachment): void {
 275          if ($this->attachments === null) {
 276              $this->attachments = [];
 277          }
 278          $this->attachments[] = $attachment;
 279      }
 280  
 281      /**
 282       * Returns the moodle user represented by this statement actor.
 283       *
 284       * @throws xapi_exception if it's a group statement
 285       * @return stdClass user record
 286       */
 287      public function get_user(): stdClass {
 288          if (!$this->actor) {
 289              throw new xapi_exception("No actor defined");
 290          }
 291          return $this->actor->get_user();
 292      }
 293  
 294      /**
 295       * Return all moodle users represented by this statement actor.
 296       *
 297       * @return array user records
 298       */
 299      public function get_all_users(): array {
 300          if (!$this->actor) {
 301              throw new xapi_exception("No actor defined");
 302          }
 303          return $this->actor->get_all_users();
 304      }
 305  
 306      /**
 307       * Return the moodle group represented by this statement actor.
 308       *
 309       * @throws xapi_exception if it is not a group statement
 310       * @return stdClass a group record
 311       */
 312      public function get_group(): stdClass {
 313          if (!$this->actor) {
 314              throw new xapi_exception("No actor defined");
 315          }
 316          if (method_exists($this->actor, 'get_group')) {
 317              return $this->actor->get_group();
 318          }
 319          throw new xapi_exception("Method not valid on this actor");
 320      }
 321  
 322      /**
 323       * Returns the statement verb ID.
 324       *
 325       * @throws xapi_exception in case the item is no yet defined
 326       * @return string verb ID
 327       */
 328      public function get_verb_id(): string {
 329          if (!$this->verb) {
 330              throw new xapi_exception("No verb defined");
 331          }
 332          return $this->verb->get_id();
 333      }
 334  
 335      /**
 336       * Returns the statement activity ID.
 337       *
 338       * @throws xapi_exception in case the item is no yet defined
 339       * @return string activity ID
 340       */
 341      public function get_activity_id(): string {
 342          if (!$this->object) {
 343              throw new xapi_exception("No object defined");
 344          }
 345          if (method_exists($this->object, 'get_id')) {
 346              return $this->object->get_id();
 347          }
 348          throw new xapi_exception("Method not valid on this object");
 349      }
 350  
 351      /**
 352       * Return the statement actor if it is defined.
 353       *
 354       * @return item_actor|null
 355       */
 356      public function get_actor(): ?item_actor {
 357          return $this->actor;
 358      }
 359  
 360      /**
 361       * Return the statement verb if it is defined.
 362       *
 363       * @return item_verb|null
 364       */
 365      public function get_verb(): ?item_verb {
 366          return $this->verb;
 367      }
 368  
 369      /**
 370       * Return the statement object if it is defined.
 371       *
 372       * @return item_object|null
 373       */
 374      public function get_object(): ?item_object {
 375          return $this->object;
 376      }
 377  
 378      /**
 379       * Return the statement context if it is defined.
 380       *
 381       * @return item|null
 382       */
 383      public function get_context(): ?item_context {
 384          return $this->context;
 385      }
 386  
 387      /**
 388       * Return the statement result if it is defined.
 389       *
 390       * @return item|null
 391       */
 392      public function get_result(): ?item_result {
 393          return $this->result;
 394      }
 395  
 396      /**
 397       * Return the statement timestamp if it is defined.
 398       *
 399       * @return string|null
 400       */
 401      public function get_timestamp(): ?string {
 402          return $this->timestamp;
 403      }
 404  
 405      /**
 406       * Return the statement stored if it is defined.
 407       *
 408       * @return string|null
 409       */
 410      public function get_stored(): ?string {
 411          return $this->stored;
 412      }
 413  
 414      /**
 415       * Return the statement authority if it is defined.
 416       *
 417       * @return item|null
 418       */
 419      public function get_authority(): ?item_actor {
 420          return $this->authority;
 421      }
 422  
 423      /**
 424       * Return the statement version if it is defined.
 425       *
 426       * @return string|null
 427       */
 428      public function get_version(): ?string {
 429          return $this->version;
 430      }
 431  
 432      /**
 433       * Return the statement attachments if it is defined.
 434       *
 435       * @return item_attachment[]|null
 436       */
 437      public function get_attachments(): ?array {
 438          return $this->attachments;
 439      }
 440  }