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.
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

namespace core_xapi;

use core_xapi\local\state;

/**
 * The state store manager.
 *
 * @package    core_xapi
 * @since      Moodle 4.2
 * @copyright  2022 Ferran Recio <ferran@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
class state_store {

    /** @var string component name in frankenstyle. */
    protected $component;

    /**
     * Constructor for a xAPI handler base class.
     *
     * @param string $component the component name
     */
    public function __construct(string $component) {
        $this->component = $component;
    }

    /**
> * Convert the xAPI activity ID into an item ID integer. * Delete any extra state data stored in the database. > * * > * @throws xapi_exception if the activity id is not numeric. * This method will be called only if the state is accepted by validate_state. > * @param string $activityid the provided activity ID * > * @return int * Plugins may override this method add extra clean up tasks to the deletion. > */ * > protected function activity_id_to_item_id(string $activityid): int { * @param state $state > if (!is_numeric($activityid)) { * @return bool if the state is removed > throw new xapi_exception('The state store can only store numeric activity IDs.'); */ > } public function delete(state $state): bool { > return intval($activityid); global $DB; > } $data = [ > 'component' => $this->component, > /**
'userid' => $state->get_user()->id,
< 'itemid' => $state->get_activity_id(),
> 'itemid' => $this->activity_id_to_item_id($state->get_activity_id()),
'stateid' => $state->get_state_id(), 'registration' => $state->get_registration(), ]; return $DB->delete_records('xapi_states', $data); } /** * Get a state object from the database. * * This method will be called only if the state is accepted by validate_state. * * Plugins may override this method if they store some data in different tables. * * @param state $state * @return state|null the state */ public function get(state $state): ?state { global $DB; $data = [ 'component' => $this->component, 'userid' => $state->get_user()->id,
< 'itemid' => $state->get_activity_id(),
> 'itemid' => $this->activity_id_to_item_id($state->get_activity_id()),
'stateid' => $state->get_state_id(), 'registration' => $state->get_registration(), ]; $record = $DB->get_record('xapi_states', $data); if ($record) { $statedata = null; if ($record->statedata !== null) { $statedata = json_decode($record->statedata, null, 512, JSON_THROW_ON_ERROR); } $state->set_state_data($statedata); return $state; } return null; } /** * Inserts an state object into the database. * * This method will be called only if the state is accepted by validate_state. * * Plugins may override this method if they store some data in different tables. * * @param state $state * @return bool if the state is inserted/updated */ public function put(state $state): bool { global $DB; $data = [ 'component' => $this->component, 'userid' => $state->get_user()->id,
< 'itemid' => $state->get_activity_id(),
> 'itemid' => $this->activity_id_to_item_id($state->get_activity_id()),
'stateid' => $state->get_state_id(), 'registration' => $state->get_registration(), ]; $record = $DB->get_record('xapi_states', $data) ?: (object) $data; if (isset($record->id)) { $record->statedata = json_encode($state->jsonSerialize()); $record->timemodified = time(); $result = $DB->update_record('xapi_states', $record); } else { $data['statedata'] = json_encode($state->jsonSerialize()); $data['timecreated'] = time(); $data['timemodified'] = $data['timecreated']; $result = $DB->insert_record('xapi_states', $data); } return $result ? true : false; } /** * Reset all states from the component. * The given parameters are filters to decide the states to reset. If no parameters are defined, the only filter applied * will be the component. * * Plugins may override this method if they store some data in different tables. * * @param string|null $itemid * @param int|null $userid * @param string|null $stateid * @param string|null $registration */ public function reset( ?string $itemid = null, ?int $userid = null, ?string $stateid = null, ?string $registration = null ): void { global $DB; $data = [ 'component' => $this->component, ]; if ($itemid) {
< $data['itemid'] = $itemid;
> $data['itemid'] = $this->activity_id_to_item_id($itemid);
} if ($userid) { $data['userid'] = $userid; } if ($stateid) { $data['stateid'] = $stateid; } if ($registration) { $data['registration'] = $registration; } $DB->set_field('xapi_states', 'statedata', null, $data); } /** * Remove all states from the component * The given parameters are filters to decide the states to wipe. If no parameters are defined, the only filter applied * will be the component. * * Plugins may override this method if they store some data in different tables. * * @param string|null $itemid * @param int|null $userid * @param string|null $stateid * @param string|null $registration */ public function wipe( ?string $itemid = null, ?int $userid = null, ?string $stateid = null, ?string $registration = null ): void { global $DB; $data = [ 'component' => $this->component, ]; if ($itemid) {
< $data['itemid'] = $itemid;
> $data['itemid'] = $this->activity_id_to_item_id($itemid);
} if ($userid) { $data['userid'] = $userid; } if ($stateid) { $data['stateid'] = $stateid; } if ($registration) { $data['registration'] = $registration; } $DB->delete_records('xapi_states', $data); } /** * Get all state ids from a specific activity and agent. * * Plugins may override this method if they store some data in different tables. * * @param string|null $itemid * @param int|null $userid * @param string|null $registration * @param int|null $since filter ids updated since a specific timestamp * @return string[] the state ids values */ public function get_state_ids( ?string $itemid = null, ?int $userid = null, ?string $registration = null, ?int $since = null, ): array { global $DB; $select = 'component = :component'; $params = [ 'component' => $this->component, ]; if ($itemid) { $select .= ' AND itemid = :itemid';
< $params['itemid'] = $itemid;
> $params['itemid'] = $this->activity_id_to_item_id($itemid);
} if ($userid) { $select .= ' AND userid = :userid'; $params['userid'] = $userid; } if ($registration) { $select .= ' AND registration = :registration'; $params['registration'] = $registration; } if ($since) { $select .= ' AND timemodified > :since'; $params['since'] = $since; } return $DB->get_fieldset_select('xapi_states', 'stateid', $select, $params, ''); } /** * Execute a state store clean up. * * Plugins can override this methos to provide an alternative clean up logic. */ public function cleanup(): void { global $DB; $xapicleanupperiod = get_config('core', 'xapicleanupperiod'); if (empty($xapicleanupperiod)) { return; } $todelete = time() - $xapicleanupperiod; $DB->delete_records_select( 'xapi_states', 'component = :component AND timemodified < :todelete', ['component' => $this->component, 'todelete' => $todelete] ); } }