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.
   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 tool_policy.
  19   *
  20   * @package    tool_policy
  21   * @copyright  2018 Sara Arjona <sara@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace tool_policy\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\moodle_content_writer;
  32  use core_privacy\local\request\userlist;
  33  use core_privacy\local\request\transform;
  34  use core_privacy\local\request\writer;
  35  
  36  defined('MOODLE_INTERNAL') || die();
  37  
  38  /**
  39   * Implementation of the privacy subsystem plugin provider for the policy tool.
  40   *
  41   * @copyright  2018 Sara Arjona <sara@moodle.com>
  42   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  43   */
  44  class provider implements
  45          // This tool stores user data.
  46          \core_privacy\local\metadata\provider,
  47  
  48          // This plugin is capable of determining which users have data within it.
  49          \core_privacy\local\request\core_userlist_provider,
  50  
  51          // This tool may provide access to and deletion of user data.
  52          \core_privacy\local\request\plugin\provider {
  53  
  54      /**
  55       * Return the fields which contain personal data.
  56       *
  57       * @param   collection $collection The initialised collection to add items to.
  58       * @return  collection A listing of user data stored through this system.
  59       */
  60      public static function get_metadata(collection $collection) : collection {
  61          $collection->add_database_table(
  62              'tool_policy_acceptances',
  63              [
  64                  'policyversionid' => 'privacy:metadata:acceptances:policyversionid',
  65                  'userid' => 'privacy:metadata:acceptances:userid',
  66                  'status' => 'privacy:metadata:acceptances:status',
  67                  'lang' => 'privacy:metadata:acceptances:lang',
  68                  'usermodified' => 'privacy:metadata:acceptances:usermodified',
  69                  'timecreated' => 'privacy:metadata:acceptances:timecreated',
  70                  'timemodified' => 'privacy:metadata:acceptances:timemodified',
  71                  'note' => 'privacy:metadata:acceptances:note',
  72              ],
  73              'privacy:metadata:acceptances'
  74          );
  75  
  76          $collection->add_database_table(
  77              'tool_policy_versions',
  78              [
  79                  'name' => 'privacy:metadata:versions:name',
  80                  'type' => 'privacy:metadata:versions:type',
  81                  'audience' => 'privacy:metadata:versions:audience',
  82                  'archived' => 'privacy:metadata:versions:archived',
  83                  'usermodified' => 'privacy:metadata:versions:usermodified',
  84                  'timecreated' => 'privacy:metadata:versions:timecreated',
  85                  'timemodified' => 'privacy:metadata:versions:timemodified',
  86                  'policyid' => 'privacy:metadata:versions:policyid',
  87                  'revision' => 'privacy:metadata:versions:revision',
  88                  'summary' => 'privacy:metadata:versions:summary',
  89                  'summaryformat' => 'privacy:metadata:versions:summaryformat',
  90                  'content' => 'privacy:metadata:versions:content',
  91                  'contentformat' => 'privacy:metadata:versions:contentformat',
  92              ],
  93              'privacy:metadata:versions'
  94          );
  95  
  96          $collection->add_subsystem_link('core_files', [], 'privacy:metadata:subsystem:corefiles');
  97  
  98          return $collection;
  99      }
 100  
 101      /**
 102       * Get the list of contexts that contain user information for the specified user.
 103       *
 104       * @param int $userid The userid.
 105       * @return contextlist The list of contexts containing user info for the user.
 106       */
 107      public static function get_contexts_for_userid(int $userid) : contextlist {
 108          $contextlist = new contextlist();
 109  
 110          // Policies a user has modified.
 111          $sql = "SELECT c.id
 112                    FROM {context} c
 113                    JOIN {tool_policy_versions} v ON v.usermodified = :userid
 114                   WHERE c.contextlevel = :contextlevel";
 115          $params = [
 116              'contextlevel' => CONTEXT_SYSTEM,
 117              'userid' => $userid,
 118          ];
 119          $contextlist->add_from_sql($sql, $params);
 120  
 121          // Policies a user has accepted.
 122          $sql = "SELECT c.id
 123                    FROM {context} c
 124                    JOIN {tool_policy_acceptances} a ON c.instanceid = a.userid
 125                   WHERE
 126                      c.contextlevel = :contextlevel
 127                     AND (
 128                      a.userid = :userid OR a.usermodified = :usermodified
 129                     )";
 130          $params = [
 131              'contextlevel' => CONTEXT_USER,
 132              'userid' => $userid,
 133              'usermodified' => $userid,
 134          ];
 135          $contextlist->add_from_sql($sql, $params);
 136  
 137          return $contextlist;
 138      }
 139  
 140      /**
 141       * Get the list of users who have data within a context.
 142       *
 143       * @param   userlist    $userlist   The userlist containing the list of users who have data in this context/plugin combination.
 144       */
 145      public static function get_users_in_context(userlist $userlist) {
 146          $context = $userlist->get_context();
 147  
 148          // Users that have modified any policies, if fetching for system context.
 149          if (is_a($context, \context_system::class)) {
 150              $sql = "SELECT v.usermodified AS userid
 151                        FROM {tool_policy_versions} v";
 152              $userlist->add_from_sql('userid', $sql, []);
 153          }
 154  
 155          // Users that have accepted any policies, if fetching for user context.
 156          if (is_a($context, \context_user::class)) {
 157              $sql = "SELECT a.userid, a.usermodified
 158                        FROM {tool_policy_acceptances} a
 159                       WHERE a.userid = :instanceid";
 160              $params = ['instanceid' => $context->instanceid];
 161  
 162              $userlist->add_from_sql('userid', $sql, $params);
 163              $userlist->add_from_sql('usermodified', $sql, $params);
 164          }
 165      }
 166  
 167      /**
 168       * Export personal data for the given approved_contextlist. User and context information is contained within the contextlist.
 169       *
 170       * @param approved_contextlist $contextlist A list of contexts approved for export.
 171       */
 172      public static function export_user_data(approved_contextlist $contextlist) {
 173          global $DB;
 174  
 175          // Export user agreements.
 176          foreach ($contextlist->get_contexts() as $context) {
 177              if ($context->contextlevel == CONTEXT_USER) {
 178                  static::export_policy_agreements_for_context($context);
 179              } else if ($context->contextlevel == CONTEXT_SYSTEM) {
 180                  static::export_authored_policies($contextlist->get_user());
 181              }
 182          }
 183      }
 184  
 185      /**
 186       * Delete all data for all users in the specified context.
 187       *
 188       * We never delete user agreements to the policies because they are part of privacy data.
 189       * We never delete policy versions because they are part of privacy data.
 190       *
 191       * @param \context $context The context to delete in.
 192       */
 193      public static function delete_data_for_all_users_in_context(\context $context) {
 194      }
 195  
 196      /**
 197       * Delete all user data for the specified user, in the specified contexts.
 198       *
 199       * We never delete user agreements to the policies because they are part of privacy data.
 200       * We never delete policy versions because they are part of privacy data.
 201       *
 202       * @param approved_contextlist $contextlist A list of contexts approved for deletion.
 203       */
 204      public static function delete_data_for_user(approved_contextlist $contextlist) {
 205      }
 206  
 207      /**
 208       * Delete multiple users within a single context.
 209       *
 210       * We never delete user agreements to the policies because they are part of privacy data.
 211       * We never delete policy versions because they are part of privacy data.
 212       *
 213       * @param   approved_userlist       $userlist The approved context and user information to delete information for.
 214       */
 215      public static function delete_data_for_users(approved_userlist $userlist) {
 216      }
 217  
 218      /**
 219       * Export all policy agreements relating to the specified user context.
 220       *
 221       * @param \context_user $context The context to export
 222       */
 223      protected static function export_policy_agreements_for_context(\context_user $context) {
 224          global $DB;
 225  
 226          $sysctx = \context_system::instance();
 227          $fs = get_file_storage();
 228          $agreementsql = "
 229              SELECT
 230                  a.id AS agreementid, a.userid, a.timemodified, a.note, a.status,
 231                  a.policyversionid AS versionid, a.usermodified, a.timecreated,
 232                  v.id, v.archived, v.name, v.revision,
 233                  v.summary, v.summaryformat,
 234                  v.content, v.contentformat,
 235                  p.currentversionid
 236               FROM {tool_policy_acceptances} a
 237               JOIN {tool_policy_versions} v ON v.id = a.policyversionid
 238               JOIN {tool_policy} p ON v.policyid = p.id
 239              WHERE a.userid = :userid OR a.usermodified = :usermodified";
 240  
 241          // Fetch all agreements related to this user.
 242          $agreements = $DB->get_recordset_sql($agreementsql, [
 243              'userid' => $context->instanceid,
 244              'usermodified' => $context->instanceid,
 245          ]);
 246  
 247          $basecontext = [
 248              get_string('privacyandpolicies', 'admin'),
 249              get_string('useracceptances', 'tool_policy'),
 250          ];
 251  
 252          foreach ($agreements as $agreement) {
 253              $subcontext = array_merge($basecontext, [get_string('policynamedversion', 'tool_policy', $agreement)]);
 254  
 255              $summary = writer::with_context($context)->rewrite_pluginfile_urls(
 256                  $subcontext,
 257                  'tool_policy',
 258                  'policydocumentsummary',
 259                  $agreement->versionid,
 260                  $agreement->summary
 261              );
 262              $content = writer::with_context($context)->rewrite_pluginfile_urls(
 263                  $subcontext,
 264                  'tool_policy',
 265                  'policydocumentcontent',
 266                  $agreement->versionid,
 267                  $agreement->content
 268              );
 269              $agreementcontent = (object) [
 270                  'name' => $agreement->name,
 271                  'revision' => $agreement->revision,
 272                  'isactive' => transform::yesno($agreement->versionid == $agreement->currentversionid),
 273                  'isagreed' => transform::yesno($agreement->status),
 274                  'agreedby' => transform::user($agreement->usermodified),
 275                  'timecreated' => transform::datetime($agreement->timecreated),
 276                  'timemodified' => transform::datetime($agreement->timemodified),
 277                  'note' => $agreement->note,
 278                  'summary' => format_text($summary, $agreement->summaryformat),
 279                  'content' => format_text($content, $agreement->contentformat),
 280              ];
 281  
 282              writer::with_context($context)->export_data($subcontext, $agreementcontent);
 283              // Manually export the files as they reside in the system context so we can't use
 284              // the write's helper methods.
 285              foreach ($fs->get_area_files($sysctx->id, 'tool_policy', 'policydocumentsummary', $agreement->versionid) as $file) {
 286                  writer::with_context($context)->export_file($subcontext, $file);
 287              }
 288              foreach ($fs->get_area_files($sysctx->id, 'tool_policy', 'policydocumentcontent', $agreement->versionid) as $file) {
 289                  writer::with_context($context)->export_file($subcontext, $file);
 290              }
 291          }
 292          $agreements->close();
 293      }
 294  
 295      /**
 296       * Export all policy agreements that the user authored.
 297       *
 298       * @param stdClass $user The user who has created the policies to export.
 299       */
 300      protected static function export_authored_policies(\stdClass $user) {
 301          global $DB;
 302  
 303          // Authored policies are exported against the system.
 304          $context = \context_system::instance();
 305          $basecontext = [
 306              get_string('policydocuments', 'tool_policy'),
 307          ];
 308  
 309          $sql = "SELECT v.id,
 310                         v.name,
 311                         v.revision,
 312                         v.summary,
 313                         v.content,
 314                         v.archived,
 315                         v.usermodified,
 316                         v.timecreated,
 317                         v.timemodified,
 318                         p.currentversionid
 319                    FROM {tool_policy_versions} v
 320                    JOIN {tool_policy} p ON p.id = v.policyid
 321                   WHERE v.usermodified = :userid";
 322          $versions = $DB->get_recordset_sql($sql, ['userid' => $user->id]);
 323          foreach ($versions as $version) {
 324              $subcontext = array_merge($basecontext, [get_string('policynamedversion', 'tool_policy', $version)]);
 325  
 326              $versioncontent = (object) [
 327                  'name' => $version->name,
 328                  'revision' => $version->revision,
 329                  'summary' => writer::with_context($context)->rewrite_pluginfile_urls(
 330                      $subcontext,
 331                      'tool_policy',
 332                      'policydocumentsummary',
 333                      $version->id,
 334                      $version->summary
 335                  ),
 336                  'content' => writer::with_context($context)->rewrite_pluginfile_urls(
 337                      $subcontext,
 338                      'tool_policy',
 339                      'policydocumentcontent',
 340                      $version->id,
 341                      $version->content
 342                  ),
 343                  'isactive' => transform::yesno($version->id == $version->currentversionid),
 344                  'isarchived' => transform::yesno($version->archived),
 345                  'createdbyme' => transform::yesno($version->usermodified == $user->id),
 346                  'timecreated' => transform::datetime($version->timecreated),
 347                  'timemodified' => transform::datetime($version->timemodified),
 348              ];
 349              writer::with_context($context)
 350                  ->export_data($subcontext, $versioncontent)
 351                  ->export_area_files($subcontext, 'tool_policy', 'policydocumentsummary', $version->id)
 352                  ->export_area_files($subcontext, 'tool_policy', 'policydocumentcontent', $version->id);
 353          }
 354          $versions->close();
 355      }
 356  }