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.
   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 core_notes.
  19   *
  20   * @package    core_notes
  21   * @copyright  2018 Zig Tan <zig@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_notes\privacy;
  26  
  27  use core_privacy\local\metadata\collection;
  28  use core_privacy\local\request\approved_contextlist;
  29  use core_privacy\local\request\contextlist;
  30  use core_privacy\local\request\transform;
  31  use core_privacy\local\request\writer;
  32  use core_privacy\local\request\userlist;
  33  use \core_privacy\local\request\approved_userlist;
  34  
  35  defined('MOODLE_INTERNAL') || die();
  36  
  37  global $CFG;
  38  require_once($CFG->dirroot . '/notes/lib.php');
  39  
  40  /**
  41   * Implementation of the privacy subsystem plugin provider for core_notes.
  42   *
  43   * @copyright  2018 Zig Tan <zig@moodle.com>
  44   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  45   */
  46  class provider implements
  47          \core_privacy\local\metadata\provider,
  48          \core_privacy\local\request\core_userlist_provider,
  49          \core_privacy\local\request\plugin\provider {
  50  
  51      /**
  52       * Return the fields which contain personal data.
  53       *
  54       * @param collection $items a reference to the collection to use to store the metadata.
  55       * @return collection the updated collection of metadata items.
  56       */
  57      public static function get_metadata(collection $items) : collection {
  58          // The core_notes components utilises the shared mdl_post table.
  59          $items->add_database_table(
  60              'post',
  61              [
  62                  'content' => 'privacy:metadata:core_notes:content',
  63                  'courseid' => 'privacy:metadata:core_notes:courseid',
  64                  'created' => 'privacy:metadata:core_notes:created',
  65                  'lastmodified' => 'privacy:metadata:core_notes:lastmodified',
  66                  'publishstate' => 'privacy:metadata:core_notes:publishstate',
  67                  'userid' => 'privacy:metadata:core_notes:userid'
  68              ],
  69              'privacy:metadata:core_notes'
  70          );
  71  
  72          return $items;
  73      }
  74  
  75      /**
  76       * Get the list of contexts that contain user information for the specified user.
  77       *
  78       * @param int $userid the userid.
  79       * @return contextlist the list of contexts containing user info for the user.
  80       */
  81      public static function get_contexts_for_userid(int $userid) : contextlist {
  82          global $DB;
  83  
  84          $contextlist = new contextlist();
  85  
  86          $publishstates = [
  87              NOTES_STATE_PUBLIC,
  88              NOTES_STATE_SITE
  89          ];
  90          list($publishstatesql, $publishstateparams) = $DB->get_in_or_equal($publishstates, SQL_PARAMS_NAMED);
  91  
  92          // Retrieve all the Course contexts associated with notes written by the user, and also written about the user.
  93          // Only notes written about the user that are public or site wide will be exported.
  94          $sql = "SELECT c.id
  95                    FROM {context} c
  96              INNER JOIN {post} p ON p.courseid = c.instanceid AND c.contextlevel = :contextcoursewrittenby
  97                   WHERE p.module = 'notes'
  98                     AND p.usermodified = :usermodified
  99                   UNION
 100                  SELECT c.id
 101                    FROM {context} c
 102              INNER JOIN {post} p ON p.courseid = c.instanceid AND c.contextlevel = :contextcoursewrittenfor
 103                   WHERE p.module = 'notes'
 104                     AND p.userid = :userid
 105                     AND p.publishstate {$publishstatesql}";
 106  
 107          $params = [
 108              'contextcoursewrittenby'  => CONTEXT_COURSE,
 109              'usermodified'            => $userid,
 110              'contextcoursewrittenfor' => CONTEXT_COURSE,
 111              'userid'                  => $userid
 112          ];
 113          $params += $publishstateparams;
 114  
 115          $contextlist->add_from_sql($sql, $params);
 116  
 117          return $contextlist;
 118      }
 119  
 120      /**
 121       * Get the list of users who have data within a context.
 122       *
 123       * @param userlist $userlist The userlist containing the list of users who have data in this context/plugin combination.
 124       */
 125      public static function get_users_in_context(userlist $userlist) {
 126          global $DB;
 127  
 128          $context = $userlist->get_context();
 129  
 130          if (!$context instanceof \context_course) {
 131              return;
 132          }
 133  
 134          $params = [
 135              'instanceid' => $context->instanceid
 136          ];
 137  
 138          $sql = "SELECT usermodified as userid
 139                    FROM {post}
 140                   WHERE module = 'notes'
 141                         AND courseid = :instanceid";
 142  
 143          $userlist->add_from_sql('userid', $sql, $params);
 144  
 145          $publishstates = [
 146              NOTES_STATE_PUBLIC,
 147              NOTES_STATE_SITE
 148          ];
 149  
 150          list($publishstatesql, $publishstateparams) = $DB->get_in_or_equal($publishstates, SQL_PARAMS_NAMED);
 151          $params += $publishstateparams;
 152  
 153          $sql = "SELECT userid
 154                    FROM {post}
 155                   WHERE module = 'notes'
 156                         AND courseid = :instanceid
 157                         AND publishstate {$publishstatesql}";
 158  
 159          $userlist->add_from_sql('userid', $sql, $params);
 160      }
 161  
 162      /**
 163       * Export personal data for the given approved_contextlist.
 164       * User and context information is contained within the contextlist.
 165       *
 166       * @param approved_contextlist $contextlist a list of contexts approved for export.
 167       */
 168      public static function export_user_data(approved_contextlist $contextlist) {
 169          global $DB;
 170  
 171          if (empty($contextlist->count())) {
 172              return;
 173          }
 174  
 175          $userid = $contextlist->get_user()->id;
 176  
 177          list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
 178  
 179          // Export all notes written by and written about the user, and organize it by the associated Course context(s).
 180          $sql = "SELECT p.courseid as courseid,
 181                         p.content as content,
 182                         p.publishstate as publishstate,
 183                         p.userid as userid,
 184                         p.usermodified as usermodified,
 185                         p.created as datecreated,
 186                         p.lastmodified as datemodified
 187                    FROM {context} c
 188              INNER JOIN {post} p ON p.courseid = c.instanceid AND c.contextlevel = :contextcourse
 189                   WHERE p.module = 'notes'
 190                     AND (p.usermodified = :usermodified OR p.userid = :userid)
 191                     AND c.id {$contextsql}";
 192  
 193          $params = [
 194              'contextcourse' => CONTEXT_COURSE,
 195              'usermodified'  => $userid,
 196              'userid'        => $userid
 197          ];
 198          $params += $contextparams;
 199  
 200          $notes = $DB->get_recordset_sql($sql, $params);
 201          foreach ($notes as $note) {
 202              $contextcourse = \context_course::instance($note->courseid);
 203  
 204              // The exported notes will be organized in {Course Context}/Notes/{publishstate}/usernote-{userid}.json.
 205              $subcontext = [
 206                  get_string('notes', 'notes'),
 207                  $note->publishstate
 208              ];
 209  
 210              $name = 'usernote-' . transform::user($note->userid);
 211  
 212              $notecontent = (object) [
 213                 'content' => $note->content,
 214                 'publishstate' => $note->publishstate,
 215                 'userid' => transform::user($note->userid),
 216                 'usermodified' => transform::user($note->usermodified),
 217                 'datecreated' => transform::datetime($note->datecreated),
 218                 'datemodified' => transform::datetime($note->datemodified)
 219              ];
 220  
 221              writer::with_context($contextcourse)->export_related_data($subcontext, $name, $notecontent);
 222          }
 223          $notes->close();
 224      }
 225  
 226      /**
 227       * Delete all data for all users in the specified context.
 228       *
 229       * @param \context $context the context to delete in.
 230       */
 231      public static function delete_data_for_all_users_in_context(\context $context) {
 232          global $DB;
 233  
 234          if ($context->contextlevel != CONTEXT_COURSE) {
 235              return;
 236          }
 237  
 238          $DB->delete_records('post', ['module' => 'notes', 'courseid' => $context->instanceid]);
 239      }
 240  
 241      /**
 242       * Delete multiple users within a single context.
 243       *
 244       * @param approved_userlist $userlist The approved context and user information to delete information for.
 245       */
 246      public static function delete_data_for_users(approved_userlist $userlist) {
 247          global $DB;
 248  
 249          $context = $userlist->get_context();
 250          if ($context->contextlevel != CONTEXT_COURSE) {
 251              return;
 252          }
 253  
 254          $userids = $userlist->get_userids();
 255          if (empty($userids)) {
 256              return;
 257          }
 258  
 259          list($usersql, $userparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
 260          $select = "module = :module AND courseid = :courseid AND usermodified {$usersql}";
 261          $params = ['module' => 'notes', 'courseid' => $context->instanceid] + $userparams;
 262  
 263          $DB->delete_records_select('post', $select, $params);
 264      }
 265  
 266      /**
 267       * Delete all user data for the specified user, in the specified contexts.
 268       *
 269       * @param approved_contextlist $contextlist a list of contexts approved for deletion.
 270       */
 271      public static function delete_data_for_user(approved_contextlist $contextlist) {
 272          global $DB;
 273  
 274          if (empty($contextlist->count())) {
 275              return;
 276          }
 277  
 278          $userid = $contextlist->get_user()->id;
 279  
 280          foreach ($contextlist->get_contexts() as $context) {
 281              $conditions = [
 282                  'module'        => 'notes',
 283                  'courseid'      => $context->instanceid,
 284                  'usermodified'  => $userid
 285              ];
 286  
 287              $DB->delete_records('post', $conditions);
 288          }
 289      }
 290  }