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.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [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  namespace core_xapi\privacy;
  18  
  19  use core_privacy\local\metadata\collection;
  20  use core_privacy\local\request\approved_contextlist;
  21  use core_privacy\local\request\transform;
  22  
  23  /**
  24   * Privacy implementation for core xAPI Library.
  25   *
  26   * @package    core_xapi
  27   * @category   privacy
  28   * @copyright  2020 Ferran Recio
  29   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   */
  31  class provider implements
  32      \core_privacy\local\metadata\provider,
  33      \core_privacy\local\request\subsystem\plugin_provider,
  34      \core_privacy\local\request\shared_userlist_provider {
  35  
  36      /**
  37       * Return the fields which contain personal data.
  38       *
  39       * @param   collection $collection The initialised collection to add items to.
  40       * @return  collection A listing of user data stored through this system.
  41       */
  42      public static function get_metadata(collection $collection) : collection {
  43          $collection->add_database_table('xapi_states', [
  44                  'component' => 'privacy:metadata:component',
  45                  'userid' => 'privacy:metadata:userid',
  46                  'itemid' => 'privacy:metadata:itemid',
  47                  'stateid' => 'privacy:metadata:stateid',
  48                  'statedata' => 'privacy:metadata:statedata',
  49                  'registration' => 'privacy:metadata:registration',
  50                  'timecreated' => 'privacy:metadata:timecreated',
  51                  'timemodified' => 'privacy:metadata:timemodified',
  52              ], 'privacy:metadata:xapi_states');
  53  
  54          return $collection;
  55      }
  56  
  57      /**
  58       * Provide a list of contexts which have xAPI for the user, in the respective area (component/itemtype combination).
  59       *
  60       * This method is to be called by consumers of the xAPI subsystem (plugins), in their get_contexts_for_userid() method,
  61       * to add the contexts for items which may have xAPI data, but would normally not be reported as having user data by the
  62       * plugin responsible for them.
  63       *
  64       * @param \core_privacy\local\request\contextlist $contextlist
  65       * @param int $userid The id of the user in scope.
  66       * @param string $component the frankenstyle component name.
  67       */
  68      public static function add_contexts_for_userid(
  69              \core_privacy\local\request\contextlist $contextlist,
  70              int $userid,
  71              string $component) {
  72          $sql = "SELECT ctx.id
  73                    FROM {xapi_states} xs
  74                    JOIN {context} ctx
  75                      ON ctx.id = xs.itemid
  76                   WHERE xs.userid = :userid
  77                     AND xs.component = :component";
  78  
  79          $params = ['userid' => $userid, 'component' => $component];
  80  
  81          $contextlist->add_from_sql($sql, $params);
  82      }
  83  
  84      /**
  85       * Add users to a userlist who have xAPI within the specified context.
  86       *
  87       * @param \core_privacy\local\request\userlist $userlist The userlist to add the users to.
  88       * @return void
  89       */
  90      public static function add_userids_for_context(\core_privacy\local\request\userlist $userlist) {
  91          if (empty($userlist)) {
  92              return;
  93          }
  94  
  95          $params = [
  96              'contextid' => $userlist->get_context()->id,
  97              'component' => $userlist->get_component()
  98          ];
  99  
 100          $sql = "SELECT xs.userid
 101                    FROM {xapi_states} xs
 102                    JOIN {context} ctx
 103                      ON ctx.id = xs.itemid
 104                   WHERE ctx.id = :contextid
 105                     AND xs.component = :component";
 106  
 107          $userlist->add_from_sql('userid', $sql, $params);
 108      }
 109  
 110      /**
 111       * Get xAPI states data for the specified user in the specified component and item ID.
 112       *
 113       * @param int $userid The id of the user in scope.
 114       * @param string $component The component name.
 115       * @param int $itemid The item ID.
 116       * @return array|null
 117       */
 118      public static function get_xapi_states_for_user(int $userid, string $component, int $itemid) {
 119          global $DB;
 120  
 121          $params = [
 122              'userid' => $userid,
 123              'component' => $component,
 124              'itemid' => $itemid,
 125          ];
 126  
 127          if (!$states = $DB->get_records('xapi_states', $params)) {
 128              return;
 129          }
 130  
 131          $result = [];
 132          foreach ($states as $state) {
 133              $result[] = [
 134                  'statedata' => $state->statedata,
 135                  'timecreated' => transform::datetime($state->timecreated),
 136                  'timemodified' => transform::datetime($state->timemodified)
 137              ];
 138          }
 139  
 140          return $result;
 141      }
 142  
 143      /**
 144       * Delete all xAPI states for all users in the specified contexts, and component area.
 145       *
 146       * @param \context $context The context to which deletion is scoped.
 147       * @param string $component The component name.
 148       * @throws \dml_exception if any errors are encountered during deletion.
 149       */
 150      public static function delete_states_for_all_users(\context $context, string $component) {
 151          global $DB;
 152  
 153          $params = [
 154              'component' => $component,
 155          ];
 156  
 157          $select = "component = :component";
 158  
 159          if (!empty($context)) {
 160              $select .= " AND itemid = :itemid";
 161              $params['itemid'] = $context->id;
 162          }
 163          $DB->delete_records_select('xapi_states', $select, $params);
 164      }
 165  
 166      /**
 167       * Delete all xAPI states for the specified users in the specified context, component area and item type.
 168       *
 169       * @param \core_privacy\local\request\approved_userlist $userlist The approved contexts and user information
 170       * to delete information for.
 171       * @param int $itemid Optional itemid associated with component.
 172       * @throws \dml_exception if any errors are encountered during deletion.
 173       */
 174      public static function delete_states_for_userlist(\core_privacy\local\request\approved_userlist $userlist, int $itemid = 0) {
 175          global $DB;
 176  
 177          $userids = $userlist->get_userids();
 178  
 179          if (empty($userids)) {
 180              return;
 181          }
 182  
 183          list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
 184  
 185          $params = [
 186              'component' => $userlist->get_component(),
 187          ];
 188  
 189          $params += $userparams;
 190          $select = "component = :component AND userid $usersql";
 191  
 192          if (!empty($itemid)) {
 193              $select .= " AND itemid = :itemid";
 194              $params['itemid'] = $itemid;
 195          }
 196  
 197          $DB->delete_records_select('xapi_states', $select, $params);
 198      }
 199  
 200      /**
 201       * Delete all xAPI states for the specified user, in the specified contexts.
 202       *
 203       * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
 204       * @param string $component The component name.
 205       * @param int $itemid Optional itemid associated with component.
 206       * @throws \coding_exception
 207       * @throws \dml_exception
 208       */
 209      public static function delete_states_for_user(approved_contextlist $contextlist, string $component, int $itemid = 0) {
 210          global $DB;
 211  
 212          $userid = $contextlist->get_user()->id;
 213  
 214          $params = [
 215              'userid' => $userid,
 216              'component' => $component,
 217          ];
 218  
 219          $select = "userid = :userid AND component = :component";
 220  
 221          if (!empty($itemid)) {
 222              $select .= " AND itemid = :itemid";
 223              $params['itemid'] = $itemid;
 224          }
 225  
 226          $DB->delete_records_select('xapi_states', $select, $params);
 227      }
 228  }