Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.
   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   * Privacy Subsystem implementation for enrol_lti.
  18   *
  19   * @package    enrol_lti
  20   * @category   privacy
  21   * @copyright  2018 Mark Nelson <markn@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace enrol_lti\privacy;
  26  
  27  use core_privacy\local\metadata\collection;
  28  use core_privacy\local\request\approved_contextlist;
  29  use core_privacy\local\request\approved_userlist;
  30  use core_privacy\local\request\contextlist;
  31  use core_privacy\local\request\transform;
  32  use core_privacy\local\request\userlist;
  33  use core_privacy\local\request\writer;
  34  
  35  defined('MOODLE_INTERNAL') || die();
  36  
  37  /**
  38   * Privacy Subsystem for enrol_lti.
  39   *
  40   * @copyright  2018 Mark Nelson <markn@moodle.com>
  41   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  42   */
  43  class provider implements
  44      \core_privacy\local\metadata\provider,
  45      \core_privacy\local\request\plugin\provider,
  46      \core_privacy\local\request\core_userlist_provider {
  47  
  48      /**
  49       * Return the fields which contain personal data.
  50       *
  51       * @param collection $items a reference to the collection to use to store the metadata.
  52       * @return collection the updated collection of metadata items.
  53       */
  54      public static function get_metadata(collection $items) : collection {
  55          $items->add_database_table(
  56              'enrol_lti_users',
  57              [
  58                  'userid' => 'privacy:metadata:enrol_lti_users:userid',
  59                  'lastgrade' => 'privacy:metadata:enrol_lti_users:lastgrade',
  60                  'lastaccess' => 'privacy:metadata:enrol_lti_users:lastaccess',
  61                  'timecreated' => 'privacy:metadata:enrol_lti_users:timecreated'
  62              ],
  63              'privacy:metadata:enrol_lti_users'
  64          );
  65  
  66          return $items;
  67      }
  68  
  69      /**
  70       * Get the list of contexts that contain user information for the specified user.
  71       *
  72       * @param int $userid The user to search.
  73       * @return contextlist The contextlist containing the list of contexts used in this plugin.
  74       */
  75      public static function get_contexts_for_userid(int $userid) : contextlist {
  76          $contextlist = new contextlist();
  77  
  78          $sql = "SELECT DISTINCT ctx.id
  79                    FROM {enrol_lti_users} ltiusers
  80                    JOIN {enrol_lti_tools} ltitools
  81                      ON ltiusers.toolid = ltitools.id
  82                    JOIN {context} ctx
  83                      ON ctx.id = ltitools.contextid
  84                   WHERE ltiusers.userid = :userid";
  85          $params = ['userid' => $userid];
  86          $contextlist->add_from_sql($sql, $params);
  87  
  88          return $contextlist;
  89      }
  90  
  91      /**
  92       * Get the list of users who have data within a context.
  93       *
  94       * @param   userlist    $userlist   The userlist containing the list of users who have data in this context/plugin combination.
  95       */
  96      public static function get_users_in_context(userlist $userlist) {
  97          $context = $userlist->get_context();
  98  
  99          if (!($context instanceof \context_course || $context instanceof \context_module)) {
 100              return;
 101          }
 102  
 103          $sql = "SELECT ltiusers.userid
 104                    FROM {enrol_lti_users} ltiusers
 105                    JOIN {enrol_lti_tools} ltitools ON ltiusers.toolid = ltitools.id
 106                   WHERE ltitools.contextid = :contextid";
 107          $params = ['contextid' => $context->id];
 108          $userlist->add_from_sql('userid', $sql, $params);
 109      }
 110  
 111      /**
 112       * Export all user data for the specified user, in the specified contexts.
 113       *
 114       * @param approved_contextlist $contextlist The approved contexts to export information for.
 115       */
 116      public static function export_user_data(approved_contextlist $contextlist) {
 117          global $DB;
 118  
 119          if (empty($contextlist->count())) {
 120              return;
 121          }
 122  
 123          $user = $contextlist->get_user();
 124  
 125          list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
 126  
 127          $sql = "SELECT ltiusers.lastgrade, ltiusers.lastaccess, ltiusers.timecreated, ltitools.contextid
 128                    FROM {enrol_lti_users} ltiusers
 129                    JOIN {enrol_lti_tools} ltitools
 130                      ON ltiusers.toolid = ltitools.id
 131                    JOIN {context} ctx
 132                      ON ctx.id = ltitools.contextid
 133                   WHERE ctx.id {$contextsql}
 134                     AND ltiusers.userid = :userid";
 135          $params = $contextparams + ['userid' => $user->id];
 136          $ltiusers = $DB->get_recordset_sql($sql, $params);
 137          self::recordset_loop_and_export($ltiusers, 'contextid', [], function($carry, $record) {
 138              $carry[] = [
 139                  'lastgrade' => $record->lastgrade,
 140                  'timecreated' => transform::datetime($record->lastaccess),
 141                  'timemodified' => transform::datetime($record->timecreated)
 142              ];
 143              return $carry;
 144          }, function($contextid, $data) {
 145              $context = \context::instance_by_id($contextid);
 146              $finaldata = (object) $data;
 147              writer::with_context($context)->export_data(['enrol_lti_users'], $finaldata);
 148          });
 149      }
 150  
 151      /**
 152       * Delete all user data which matches the specified context.
 153       *
 154       * @param \context $context A user context.
 155       */
 156      public static function delete_data_for_all_users_in_context(\context $context) {
 157          global $DB;
 158  
 159          if (!($context instanceof \context_course || $context instanceof \context_module)) {
 160              return;
 161          }
 162  
 163          $enrolltitools = $DB->get_fieldset_select('enrol_lti_tools', 'id', 'contextid = :contextid',
 164              ['contextid' => $context->id]);
 165          if (!empty($enrolltitools)) {
 166              list($sql, $params) = $DB->get_in_or_equal($enrolltitools, SQL_PARAMS_NAMED);
 167              $DB->delete_records_select('enrol_lti_users', 'toolid ' . $sql, $params);
 168          }
 169      }
 170  
 171      /**
 172       * Delete all user data for the specified user, in the specified contexts.
 173       *
 174       * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
 175       */
 176      public static function delete_data_for_user(approved_contextlist $contextlist) {
 177          global $DB;
 178  
 179          $userid = $contextlist->get_user()->id;
 180  
 181          foreach ($contextlist->get_contexts() as $context) {
 182              if (!($context instanceof \context_course || $context instanceof \context_module)) {
 183                  continue;
 184              }
 185  
 186              $enrolltitools = $DB->get_fieldset_select('enrol_lti_tools', 'id', 'contextid = :contextid',
 187                  ['contextid' => $context->id]);
 188              if (!empty($enrolltitools)) {
 189                  list($sql, $params) = $DB->get_in_or_equal($enrolltitools, SQL_PARAMS_NAMED);
 190                  $params = array_merge($params, ['userid' => $userid]);
 191                  $DB->delete_records_select('enrol_lti_users', "toolid $sql AND userid = :userid", $params);
 192              }
 193          }
 194      }
 195  
 196      /**
 197       * Delete multiple users within a single context.
 198       *
 199       * @param   approved_userlist   $userlist   The approved context and user information to delete information for.
 200       */
 201      public static function delete_data_for_users(approved_userlist $userlist) {
 202          global $DB;
 203  
 204          $context = $userlist->get_context();
 205  
 206          if (!($context instanceof \context_course || $context instanceof \context_module)) {
 207              return;
 208          }
 209  
 210          $enrolltitools = $DB->get_fieldset_select('enrol_lti_tools', 'id', 'contextid = :contextid',
 211                  ['contextid' => $context->id]);
 212          if (!empty($enrolltitools)) {
 213              list($toolsql, $toolparams) = $DB->get_in_or_equal($enrolltitools, SQL_PARAMS_NAMED);
 214              $userids = $userlist->get_userids();
 215              list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
 216              $params = $toolparams + $userparams;
 217              $DB->delete_records_select('enrol_lti_users', "toolid $toolsql AND userid $usersql", $params);
 218          }
 219      }
 220  
 221      /**
 222       * Loop and export from a recordset.
 223       *
 224       * @param \moodle_recordset $recordset The recordset.
 225       * @param string $splitkey The record key to determine when to export.
 226       * @param mixed $initial The initial data to reduce from.
 227       * @param callable $reducer The function to return the dataset, receives current dataset, and the current record.
 228       * @param callable $export The function to export the dataset, receives the last value from $splitkey and the dataset.
 229       * @return void
 230       */
 231      protected static function recordset_loop_and_export(\moodle_recordset $recordset, $splitkey, $initial,
 232              callable $reducer, callable $export) {
 233          $data = $initial;
 234          $lastid = null;
 235  
 236          foreach ($recordset as $record) {
 237              if ($lastid && $record->{$splitkey} != $lastid) {
 238                  $export($lastid, $data);
 239                  $data = $initial;
 240              }
 241              $data = $reducer($data, $record);
 242              $lastid = $record->{$splitkey};
 243          }
 244          $recordset->close();
 245  
 246          if (!empty($lastid)) {
 247              $export($lastid, $data);
 248          }
 249      }
 250  }