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  namespace tiny_autosave\privacy;
  18  
  19  use core_privacy\local\request\approved_contextlist;
  20  use core_privacy\local\request\writer;
  21  use core_privacy\local\metadata\collection;
  22  use core_privacy\local\request\userlist;
  23  use core_privacy\local\request\approved_userlist;
  24  use stdClass;
  25  
  26  /**
  27   * Privacy Subsystem implementation for tiny_autosave.
  28   *
  29   * @package    tiny_autosave
  30   * @copyright  2022 Andrew Nicols <andrew@nicols.co.uk>
  31   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  32   */
  33  class provider implements
  34      // The tiny editor stores user provided data.
  35      \core_privacy\local\metadata\provider,
  36  
  37      // The tiny editor provides data directly to core.
  38      \core_privacy\local\request\plugin\provider,
  39  
  40      // The tiny editor is capable of determining which users have data within it.
  41      \core_privacy\local\request\core_userlist_provider {
  42  
  43      /**
  44       * Returns information about how tiny_autosave stores its data.
  45       *
  46       * @param collection $collection The initialised collection to add items to.
  47       * @return collection A listing of user data stored through this system.
  48       */
  49      public static function get_metadata(collection $collection): collection {
  50          // There isn't much point giving details about the pageid, etc.
  51          $collection->add_database_table('tiny_autosave', [
  52              'userid' => 'privacy:metadata:database:tiny_autosave:userid',
  53              'drafttext' => 'privacy:metadata:database:tiny_autosave:drafttext',
  54              'timemodified' => 'privacy:metadata:database:tiny_autosave:timemodified',
  55          ], 'privacy:metadata:database:tiny_autosave');
  56  
  57          return $collection;
  58      }
  59  
  60      /**
  61       * Get the list of contexts that contain user information for the specified user.
  62       *
  63       * @param int $userid The user to search.
  64       * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
  65       */
  66      public static function get_contexts_for_userid(int $userid): \core_privacy\local\request\contextlist {
  67          $contextlist = new \core_privacy\local\request\contextlist();
  68  
  69          // Data may be saved in the user context.
  70          $sql = "SELECT
  71                      c.id
  72                    FROM {tiny_autosave} eas
  73                    JOIN {context} c ON c.id = eas.contextid
  74                   WHERE contextlevel = :contextuser AND c.instanceid = :userid";
  75          $contextlist->add_from_sql($sql, ['contextuser' => CONTEXT_USER, 'userid' => $userid]);
  76  
  77          // Data may be saved against the userid.
  78          $sql = "SELECT contextid FROM {tiny_autosave} WHERE userid = :userid";
  79          $contextlist->add_from_sql($sql, ['userid' => $userid]);
  80  
  81          return $contextlist;
  82      }
  83  
  84      /**
  85       * Get the list of users within a specific context.
  86       *
  87       * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
  88       */
  89      public static function get_users_in_context(userlist $userlist) {
  90          $context = $userlist->get_context();
  91  
  92          $params = [
  93              'contextid' => $context->id
  94          ];
  95  
  96          $sql = "SELECT userid
  97                    FROM {tiny_autosave}
  98                   WHERE contextid = :contextid";
  99  
 100          $userlist->add_from_sql('userid', $sql, $params);
 101      }
 102  
 103      /**
 104       * Export all user data for the specified user, in the specified contexts.
 105       *
 106       * @param   approved_contextlist    $contextlist    The approved contexts to export information for.
 107       */
 108      public static function export_user_data(approved_contextlist $contextlist) {
 109          global $DB;
 110  
 111          $user = $contextlist->get_user();
 112  
 113          // Firstly export all autosave records from all contexts in the list owned by the given user.
 114          list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
 115          $contextparams['userid'] = $user->id;
 116  
 117          $sql = "SELECT *
 118                    FROM {tiny_autosave}
 119                   WHERE userid = :userid AND contextid {$contextsql}";
 120  
 121          $autosaves = $DB->get_recordset_sql($sql, $contextparams);
 122          self::export_autosaves($user, $autosaves);
 123  
 124          // Additionally export all eventual records in the given user's context regardless the actual owner.
 125          // We still consider them to be the user's personal data even when edited by someone else.
 126          [$contextsql, $contextparams] = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
 127          $contextparams['userid'] = $user->id;
 128          $contextparams['contextuser'] = CONTEXT_USER;
 129  
 130          $sql = "SELECT eas.*
 131                    FROM {tiny_autosave} eas
 132                    JOIN {context} c ON c.id = eas.contextid
 133                   WHERE c.id {$contextsql} AND c.contextlevel = :contextuser AND c.instanceid = :userid";
 134  
 135          $autosaves = $DB->get_recordset_sql($sql, $contextparams);
 136          self::export_autosaves($user, $autosaves);
 137      }
 138  
 139      /**
 140       * Export all autosave records in the recordset, and close the recordset when finished.
 141       *
 142       * @param   stdClass   $user The user whose data is to be exported
 143       * @param   \moodle_recordset $autosaves The recordset containing the data to export
 144       */
 145      protected static function export_autosaves(stdClass $user, \moodle_recordset $autosaves) {
 146          foreach ($autosaves as $autosave) {
 147              $context = \context::instance_by_id($autosave->contextid);
 148              $subcontext = [
 149                  get_string('pluginname', 'tiny_autosave'),
 150                  $autosave->id,
 151              ];
 152  
 153              $html = writer::with_context($context)
 154                  ->rewrite_pluginfile_urls($subcontext, 'user', 'draft', $autosave->draftid, $autosave->drafttext);
 155  
 156              $data = (object) [
 157                  'drafttext' => format_text($html, FORMAT_HTML, static::get_filter_options()),
 158                  'timemodified' => \core_privacy\local\request\transform::datetime($autosave->timemodified),
 159              ];
 160  
 161              if ($autosave->userid != $user->id) {
 162                  $data->author = \core_privacy\local\request\transform::user($autosave->userid);
 163              }
 164  
 165              writer::with_context($context)
 166                  ->export_data($subcontext, $data)
 167                  ->export_area_files($subcontext, 'user', 'draft', $autosave->draftid);
 168          }
 169          $autosaves->close();
 170      }
 171  
 172      /**
 173       * Delete all data for all users in the specified context.
 174       *
 175       * @param context $context The specific context to delete data for.
 176       */
 177      public static function delete_data_for_all_users_in_context(\context $context) {
 178          global $DB;
 179  
 180          $DB->delete_records('tiny_autosave', [
 181              'contextid' => $context->id,
 182          ]);
 183      }
 184  
 185      /**
 186       * Delete multiple users within a single context.
 187       *
 188       * @param approved_userlist $userlist The approved context and user information to delete information for.
 189       */
 190      public static function delete_data_for_users(approved_userlist $userlist) {
 191          global $DB;
 192  
 193          $context = $userlist->get_context();
 194          $userids = $userlist->get_userids();
 195  
 196          [$useridsql, $useridsqlparams] = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
 197          $params = ['contextid' => $context->id] + $useridsqlparams;
 198  
 199          $DB->delete_records_select('tiny_autosave', "contextid = :contextid AND userid {$useridsql}",
 200              $params);
 201      }
 202  
 203      /**
 204       * Delete all user data for the specified user, in the specified contexts.
 205       *
 206       * @param approved_contextlist $contextlist The approved contexts and user information to delete information for.
 207       */
 208      public static function delete_data_for_user(approved_contextlist $contextlist) {
 209          global $DB;
 210  
 211          $user = $contextlist->get_user();
 212  
 213          [$contextsql, $contextparams] = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
 214          $contextparams['userid'] = $user->id;
 215  
 216          $sql = "SELECT * FROM {tiny_autosave} WHERE contextid {$contextsql}";
 217          $autosaves = $DB->delete_records_select(
 218              'tiny_autosave',
 219              "userid = :userid AND contextid {$contextsql}",
 220              $contextparams
 221          );
 222      }
 223  
 224      /**
 225       * Get the filter options.
 226       *
 227       * This is shared to allow unit testing too.
 228       *
 229       * @return stdClass
 230       */
 231      public static function get_filter_options() {
 232          return (object) [
 233              'overflowdiv' => true,
 234              'noclean' => true,
 235          ];
 236      }
 237  }