Search moodle.org's
Developer Documentation

See Release Notes

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

Differences Between: [Versions 402 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  namespace core_xapi;
  18  
  19  use core_xapi\local\state;
  20  
  21  /**
  22   * The state store manager.
  23   *
  24   * @package    core_xapi
  25   * @since      Moodle 4.2
  26   * @copyright  2022 Ferran Recio <ferran@moodle.com>
  27   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  28   */
  29  class state_store {
  30  
  31      /** @var string component name in frankenstyle. */
  32      protected $component;
  33  
  34      /**
  35       * Constructor for a xAPI handler base class.
  36       *
  37       * @param string $component the component name
  38       */
  39      public function __construct(string $component) {
  40          $this->component = $component;
  41      }
  42  
  43      /**
  44       * Delete any extra state data stored in the database.
  45       *
  46       * This method will be called only if the state is accepted by validate_state.
  47       *
  48       * Plugins may override this method add extra clean up tasks to the deletion.
  49       *
  50       * @param state $state
  51       * @return bool if the state is removed
  52       */
  53      public function delete(state $state): bool {
  54          global $DB;
  55          $data = [
  56              'component' => $this->component,
  57              'userid' => $state->get_user()->id,
  58              'itemid' => $state->get_activity_id(),
  59              'stateid' => $state->get_state_id(),
  60              'registration' => $state->get_registration(),
  61          ];
  62          return $DB->delete_records('xapi_states', $data);
  63      }
  64  
  65      /**
  66       * Get a state object from the database.
  67       *
  68       * This method will be called only if the state is accepted by validate_state.
  69       *
  70       * Plugins may override this method if they store some data in different tables.
  71       *
  72       * @param state $state
  73       * @return state|null the state
  74       */
  75      public function get(state $state): ?state {
  76          global $DB;
  77          $data = [
  78              'component' => $this->component,
  79              'userid' => $state->get_user()->id,
  80              'itemid' => $state->get_activity_id(),
  81              'stateid' => $state->get_state_id(),
  82              'registration' => $state->get_registration(),
  83          ];
  84          $record = $DB->get_record('xapi_states', $data);
  85          if ($record) {
  86              $statedata = null;
  87              if ($record->statedata !== null) {
  88                  $statedata = json_decode($record->statedata, null, 512, JSON_THROW_ON_ERROR);
  89              }
  90              $state->set_state_data($statedata);
  91              return $state;
  92          }
  93  
  94          return null;
  95      }
  96  
  97      /**
  98       * Inserts an state object into the database.
  99       *
 100       * This method will be called only if the state is accepted by validate_state.
 101       *
 102       * Plugins may override this method if they store some data in different tables.
 103       *
 104       * @param state $state
 105       * @return bool if the state is inserted/updated
 106       */
 107      public function put(state $state): bool {
 108          global $DB;
 109          $data = [
 110              'component' => $this->component,
 111              'userid' => $state->get_user()->id,
 112              'itemid' => $state->get_activity_id(),
 113              'stateid' => $state->get_state_id(),
 114              'registration' => $state->get_registration(),
 115          ];
 116          $record = $DB->get_record('xapi_states', $data) ?: (object) $data;
 117          if (isset($record->id)) {
 118              $record->statedata = json_encode($state->jsonSerialize());
 119              $record->timemodified = time();
 120              $result = $DB->update_record('xapi_states', $record);
 121          } else {
 122              $data['statedata'] = json_encode($state->jsonSerialize());
 123              $data['timecreated'] = time();
 124              $data['timemodified'] = $data['timecreated'];
 125              $result = $DB->insert_record('xapi_states', $data);
 126          }
 127          return $result ? true : false;
 128      }
 129  
 130      /**
 131       * Reset all states from the component.
 132       * The given parameters are filters to decide the states to reset. If no parameters are defined, the only filter applied
 133       * will be the component.
 134       *
 135       * Plugins may override this method if they store some data in different tables.
 136       *
 137       * @param string|null $itemid
 138       * @param int|null $userid
 139       * @param string|null $stateid
 140       * @param string|null $registration
 141       */
 142      public function reset(
 143          ?string $itemid = null,
 144          ?int $userid = null,
 145          ?string $stateid = null,
 146          ?string $registration = null
 147      ): void {
 148          global $DB;
 149  
 150          $data = [
 151              'component' => $this->component,
 152          ];
 153          if ($itemid) {
 154              $data['itemid'] = $itemid;
 155          }
 156          if ($userid) {
 157              $data['userid'] = $userid;
 158          }
 159          if ($stateid) {
 160              $data['stateid'] = $stateid;
 161          }
 162          if ($registration) {
 163              $data['registration'] = $registration;
 164          }
 165          $DB->set_field('xapi_states', 'statedata', null, $data);
 166      }
 167  
 168      /**
 169       * Remove all states from the component
 170       * The given parameters are filters to decide the states to wipe. If no parameters are defined, the only filter applied
 171       * will be the component.
 172       *
 173       * Plugins may override this method if they store some data in different tables.
 174       *
 175       * @param string|null $itemid
 176       * @param int|null $userid
 177       * @param string|null $stateid
 178       * @param string|null $registration
 179       */
 180      public function wipe(
 181          ?string $itemid = null,
 182          ?int $userid = null,
 183          ?string $stateid = null,
 184          ?string $registration = null
 185      ): void {
 186          global $DB;
 187          $data = [
 188              'component' => $this->component,
 189          ];
 190          if ($itemid) {
 191              $data['itemid'] = $itemid;
 192          }
 193          if ($userid) {
 194              $data['userid'] = $userid;
 195          }
 196          if ($stateid) {
 197              $data['stateid'] = $stateid;
 198          }
 199          if ($registration) {
 200              $data['registration'] = $registration;
 201          }
 202          $DB->delete_records('xapi_states', $data);
 203      }
 204  
 205      /**
 206       * Get all state ids from a specific activity and agent.
 207       *
 208       * Plugins may override this method if they store some data in different tables.
 209       *
 210       * @param string|null $itemid
 211       * @param int|null $userid
 212       * @param string|null $registration
 213       * @param int|null $since filter ids updated since a specific timestamp
 214       * @return string[] the state ids values
 215       */
 216      public function get_state_ids(
 217          ?string $itemid = null,
 218          ?int $userid = null,
 219          ?string $registration = null,
 220          ?int $since = null,
 221      ): array {
 222          global $DB;
 223          $select = 'component = :component';
 224          $params = [
 225              'component' => $this->component,
 226          ];
 227          if ($itemid) {
 228              $select .= ' AND itemid = :itemid';
 229              $params['itemid'] = $itemid;
 230          }
 231          if ($userid) {
 232              $select .= ' AND userid = :userid';
 233              $params['userid'] = $userid;
 234          }
 235          if ($registration) {
 236              $select .= ' AND registration = :registration';
 237              $params['registration'] = $registration;
 238          }
 239          if ($since) {
 240              $select .= ' AND timemodified > :since';
 241              $params['since'] = $since;
 242          }
 243          return $DB->get_fieldset_select('xapi_states', 'stateid', $select, $params, '');
 244      }
 245  
 246      /**
 247       * Execute a state store clean up.
 248       *
 249       * Plugins can override this methos to provide an alternative clean up logic.
 250       */
 251      public function cleanup(): void {
 252          global $DB;
 253          $xapicleanupperiod = get_config('core', 'xapicleanupperiod');
 254          if (empty($xapicleanupperiod)) {
 255              return;
 256          }
 257          $todelete = time() - $xapicleanupperiod;
 258          $DB->delete_records_select(
 259              'xapi_states',
 260              'component = :component AND timemodified < :todelete',
 261              ['component' => $this->component, 'todelete' => $todelete]
 262          );
 263      }
 264  }