Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are 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   * This file contains the core_privacy\local\request helper.
  19   *
  20   * @package core_privacy
  21   * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
  22   *
  23   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  namespace core_privacy\local\request;
  26  
  27  use \core_privacy\local\request\writer;
  28  
  29  defined('MOODLE_INTERNAL') || die();
  30  
  31  require_once($CFG->libdir . '/modinfolib.php');
  32  require_once($CFG->dirroot . '/course/modlib.php');
  33  
  34  /**
  35   * The core_privacy\local\request\helper class with useful shared functionality.
  36   *
  37   * @package core_privacy
  38   * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
  39   *
  40   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class helper {
  43  
  44      /**
  45       * Add core-controlled contexts which are related to a component but that component may know about.
  46       *
  47       * For example, most activities are not aware of activity completion, but the course implements it for them.
  48       * These should be included.
  49       *
  50       * @param   int             $userid The user being added for.
  51       * @param   contextlist     $contextlist The contextlist being appended to.
  52       * @return  contextlist     The final contextlist
  53       */
  54      public static function add_shared_contexts_to_contextlist_for(int $userid, contextlist $contextlist) : contextlist {
  55          if (strpos($contextlist->get_component(), 'mod_') === 0) {
  56              // Activity modules support data stored by core about them - for example, activity completion.
  57              $contextlist = static::add_shared_contexts_to_contextlist_for_course_module($userid, $contextlist);
  58          }
  59  
  60          return $contextlist;
  61      }
  62  
  63      /**
  64       * Add core-controlled contexts which are related to a component but that component may know about.
  65       *
  66       * For example, most activities are not aware of activity completion, but the course implements it for them.
  67       * These should be included.
  68       *
  69       * @param   \core_privacy\local\request\userlist    $userlist
  70       * @return  contextlist     The final contextlist
  71       */
  72      public static function add_shared_users_to_userlist(\core_privacy\local\request\userlist $userlist) {
  73      }
  74  
  75      /**
  76       * Handle export of standard data for a plugin which implements the null provider and does not normally store data
  77       * of its own.
  78       *
  79       * This is used in cases such as activities like mod_resource, which do not store their own data, but may still have
  80       * data on them (like Activity Completion).
  81       *
  82       * Any context provided in a contextlist should have base data exported as a minimum.
  83       *
  84       * @param   approved_contextlist    $contextlist    The approved contexts to export information for.
  85       */
  86      public static function export_data_for_null_provider(approved_contextlist $contextlist) {
  87          $user = $contextlist->get_user();
  88          foreach ($contextlist as $context) {
  89              $data = static::get_context_data($context, $user);
  90              static::export_context_files($context, $user);
  91  
  92              writer::with_context($context)->export_data([], $data);
  93          }
  94      }
  95  
  96      /**
  97       * Handle removal of 'standard' data for any plugin.
  98       *
  99       * This will handle deletion for things such as activity completion.
 100       *
 101       * @param   string          $component The component being deleted for.
 102       * @param   context         $context   The specific context to delete data for.
 103       */
 104      public static function delete_data_for_all_users_in_context(string $component, \context $context) {
 105          // Activity modules support data stored by core about them - for example, activity completion.
 106          static::delete_data_for_all_users_in_context_course_module($component, $context);
 107      }
 108  
 109      /**
 110       * Delete all 'standard' user data for the specified user, in the specified contexts.
 111       *
 112       * This will handle deletion for things such as activity completion.
 113       *
 114       * @param   approved_contextlist    $contextlist    The approved contexts and user information to delete information for.
 115       */
 116      public static function delete_data_for_user(approved_contextlist $contextlist) {
 117          $component = $contextlist->get_component();
 118  
 119          // Activity modules support data stored by core about them - for example, activity completion.
 120          static::delete_data_for_user_in_course_module($contextlist);
 121      }
 122  
 123      /**
 124       * Get all general data for this context.
 125       *
 126       * @param   \context        $context The context to retrieve data for.
 127       * @param   \stdClass       $user The user being written.
 128       * @return  \stdClass
 129       */
 130      public static function get_context_data(\context $context, \stdClass $user) : \stdClass {
 131          global $DB;
 132  
 133          $basedata = (object) [];
 134          if ($context instanceof \context_module) {
 135              return static::get_context_module_data($context, $user);
 136          }
 137          if ($context instanceof \context_block) {
 138              return static::get_context_block_data($context, $user);
 139          }
 140  
 141          return $basedata;
 142      }
 143  
 144      /**
 145       * Export all files for this context.
 146       *
 147       * @param   \context        $context The context to export files for.
 148       * @param   \stdClass       $user The user being written.
 149       * @return  \stdClass
 150       */
 151      public static function export_context_files(\context $context, \stdClass $user) {
 152          if ($context instanceof \context_module) {
 153              return static::export_context_module_files($context, $user);
 154          }
 155      }
 156  
 157      /**
 158       * Add core-controlled contexts which are related to a component but that component may know about.
 159       *
 160       * For example, most activities are not aware of activity completion, but the course implements it for them.
 161       * These should be included.
 162       *
 163       * @param   int             $userid The user being added for.
 164       * @param   contextlist     $contextlist The contextlist being appended to.
 165       * @return  contextlist     The final contextlist
 166       */
 167      protected static function add_shared_contexts_to_contextlist_for_course_module(int $userid, contextlist $contextlist) : contextlist {
 168          // Fetch all contexts where the user has activity completion enabled.
 169          $sql = "SELECT
 170                  c.id
 171                    FROM {course_modules_completion} cmp
 172              INNER JOIN {course_modules} cm ON cm.id = cmp.coursemoduleid
 173              INNER JOIN {modules} m ON m.id = cm.module
 174              INNER JOIN {context} c ON c.instanceid = cm.id AND c.contextlevel = :contextlevel
 175                   WHERE cmp.userid = :userid
 176                     AND m.name = :modname";
 177          $params = [
 178              'userid' => $userid,
 179              // Strip the mod_ from the name.
 180              'modname' => substr($contextlist->get_component(), 4),
 181              'contextlevel' => CONTEXT_MODULE,
 182          ];
 183  
 184          $contextlist->add_from_sql($sql, $params);
 185  
 186          return $contextlist;
 187      }
 188  
 189      /**
 190       * Get all general data for the activity module at this context.
 191       *
 192       * @param   \context_module $context The context to retrieve data for.
 193       * @param   \stdClass       $user The user being written.
 194       * @return  \stdClass
 195       */
 196      protected static function get_context_module_data(\context_module $context, \stdClass $user) : \stdClass {
 197          global $DB;
 198  
 199          $coursecontext = $context->get_course_context();
 200          $modinfo = get_fast_modinfo($coursecontext->instanceid);
 201          $cm = $modinfo->cms[$context->instanceid];
 202          $component = "mod_{$cm->modname}";
 203          $course = $cm->get_course();
 204          $moduledata = $DB->get_record($cm->modname, ['id' => $cm->instance]);
 205  
 206          $basedata = (object) [
 207              'name' => $cm->get_formatted_name(),
 208          ];
 209  
 210          if (plugin_supports('mod', $cm->modname, FEATURE_MOD_INTRO, true)) {
 211              $intro = $moduledata->intro;
 212  
 213              $intro = writer::with_context($context)
 214                  ->rewrite_pluginfile_urls([], $component, 'intro', 0, $intro);
 215  
 216              $options = [
 217                  'noclean' => true,
 218                  'para' => false,
 219                  'context' => $context,
 220                  'overflowdiv' => true,
 221              ];
 222              $basedata->intro = format_text($intro, $moduledata->introformat, $options);
 223          }
 224  
 225          // Completion tracking.
 226          $completiondata = \core_completion\privacy\provider::get_activity_completion_info($user, $course, $cm);
 227          if (isset($completiondata->completionstate)) {
 228              $basedata->completion = (object) [
 229                  'state' => $completiondata->completionstate,
 230              ];
 231          }
 232  
 233          return $basedata;
 234      }
 235  
 236      /**
 237       * Get all general data for the block at this context.
 238       *
 239       * @param   \context_block $context The context to retrieve data for.
 240       * @param   \stdClass $user The user being written.
 241       * @return  \stdClass General data about this block instance.
 242       */
 243      protected static function get_context_block_data(\context_block $context, \stdClass $user) : \stdClass {
 244          global $DB;
 245  
 246          $block = $DB->get_record('block_instances', ['id' => $context->instanceid]);
 247  
 248          $basedata = (object) [
 249              'blocktype' => get_string('pluginname', 'block_' . $block->blockname)
 250          ];
 251  
 252          return $basedata;
 253      }
 254  
 255      /**
 256       * Get all general data for the activity module at this context.
 257       *
 258       * @param   \context_module $context The context to retrieve data for.
 259       * @param   \stdClass       $user The user being written.
 260       * @return  \stdClass
 261       */
 262      protected static function export_context_module_files(\context_module $context, \stdClass $user) {
 263          $coursecontext = $context->get_course_context();
 264          $modinfo = get_fast_modinfo($coursecontext->instanceid);
 265          $cm = $modinfo->cms[$context->instanceid];
 266          $component = "mod_{$cm->modname}";
 267  
 268          writer::with_context($context)
 269              // Export the files for the intro.
 270              ->export_area_files([], $component, 'intro', 0);
 271      }
 272  
 273      /**
 274       * Handle removal of 'standard' data for course modules.
 275       *
 276       * This will handle deletion for things such as activity completion.
 277       *
 278       * @param   string              $component The component being deleted for.
 279       * @param   \context            $context The context to delete all data for.
 280       */
 281      public static function delete_data_for_all_users_in_context_course_module(string $component, \context $context) {
 282          global $DB;
 283  
 284          if ($context instanceof \context_module) {
 285              // Delete course completion data for this context.
 286              \core_completion\privacy\provider::delete_completion(null, null, $context->instanceid);
 287          }
 288      }
 289  
 290      /**
 291       * Delete all 'standard' user data for the specified user in course modules.
 292       *
 293       * This will handle deletion for things such as activity completion.
 294       *
 295       * @param   approved_contextlist    $contextlist    The approved contexts and user information to delete information for.
 296       */
 297      protected static function delete_data_for_user_in_course_module(approved_contextlist $contextlist) {
 298          global $DB;
 299  
 300          foreach ($contextlist as $context) {
 301              if ($context instanceof \context_module) {
 302                  // Delete course completion data for this context.
 303                  \core_completion\privacy\provider::delete_completion($contextlist->get_user(), null, $context->instanceid);
 304              }
 305          }
 306  
 307      }
 308  }