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.

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