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.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 class for requesting user data.
  19   *
  20   * @package    core_grading
  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 core_grading\privacy;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  use \core_privacy\local\metadata\collection;
  30  use \core_privacy\local\request\approved_contextlist;
  31  use \core_privacy\local\request\contextlist;
  32  use \core_privacy\local\request\transform;
  33  use \core_privacy\local\request\writer;
  34  use \core_privacy\manager;
  35  
  36  /**
  37   * Privacy class for requesting user data.
  38   *
  39   * @copyright  2018 Sara Arjona <sara@moodle.com>
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class provider implements
  43      \core_privacy\local\metadata\provider,
  44      \core_privacy\local\request\plugin\provider,
  45      \core_privacy\local\request\core_userlist_provider,
  46      \core_privacy\local\request\subsystem\provider {
  47  
  48      /**
  49       * Returns meta data about this system.
  50       *
  51       * @param   collection     $collection The initialised collection to add items to.
  52       * @return  collection     A listing of user data stored through this system.
  53       */
  54      public static function get_metadata(collection $collection) : collection {
  55          $collection->add_database_table('grading_definitions', [
  56                  'method' => 'privacy:metadata:grading_definitions:method',
  57                  'areaid' => 'privacy:metadata:grading_definitions:areaid',
  58                  'name' => 'privacy:metadata:grading_definitions:name',
  59                  'description' => 'privacy:metadata:grading_definitions:description',
  60                  'status' => 'privacy:metadata:grading_definitions:status',
  61                  'copiedfromid' => 'privacy:metadata:grading_definitions:copiedfromid',
  62                  'timecopied' => 'privacy:metadata:grading_definitions:timecopied',
  63                  'timecreated' => 'privacy:metadata:grading_definitions:timecreated',
  64                  'usercreated' => 'privacy:metadata:grading_definitions:usercreated',
  65                  'timemodified' => 'privacy:metadata:grading_definitions:timemodified',
  66                  'usermodified' => 'privacy:metadata:grading_definitions:usermodified',
  67                  'options' => 'privacy:metadata:grading_definitions:options',
  68              ], 'privacy:metadata:grading_definitions');
  69  
  70          $collection->add_database_table('grading_instances', [
  71                  'raterid' => 'privacy:metadata:grading_instances:raterid',
  72                  'rawgrade' => 'privacy:metadata:grading_instances:rawgrade',
  73                  'status' => 'privacy:metadata:grading_instances:status',
  74                  'feedback' => 'privacy:metadata:grading_instances:feedback',
  75                  'feedbackformat' => 'privacy:metadata:grading_instances:feedbackformat',
  76                  'timemodified' => 'privacy:metadata:grading_instances:timemodified',
  77              ], 'privacy:metadata:grading_instances');
  78  
  79          // Link to subplugin.
  80          $collection->add_plugintype_link('gradingform', [], 'privacy:metadata:gradingformpluginsummary');
  81  
  82          return $collection;
  83      }
  84  
  85      /**
  86       * Get the list of contexts that contain user information for the specified user.
  87       *
  88       * @param int $userid The user to search.
  89       * @return contextlist $contextlist The contextlist containing the list of contexts used in this plugin.
  90       */
  91      public static function get_contexts_for_userid(int $userid) : contextlist {
  92          $contextlist = new contextlist();
  93  
  94          $sql = "SELECT c.id
  95                    FROM {context} c
  96                    JOIN {grading_areas} a ON a.contextid = c.id
  97                    JOIN {grading_definitions} d ON d.areaid = a.id
  98               LEFT JOIN {grading_instances} i ON i.definitionid = d.id AND i.raterid = :raterid
  99                   WHERE c.contextlevel = :contextlevel
 100                     AND (d.usercreated = :usercreated OR d.usermodified = :usermodified OR i.id IS NOT NULL)";
 101          $params = [
 102              'usercreated' => $userid,
 103              'usermodified' => $userid,
 104              'raterid' => $userid,
 105              'contextlevel' => CONTEXT_MODULE
 106          ];
 107          $contextlist->add_from_sql($sql, $params);
 108  
 109          return $contextlist;
 110      }
 111  
 112      /**
 113       * Get the list of users who have data within a context.
 114       *
 115       * @param   userlist    $userlist   The userlist containing the list of users who have data in this context/plugin combination.
 116       */
 117      public static function get_users_in_context(\core_privacy\local\request\userlist $userlist) {
 118          $context = $userlist->get_context();
 119          if ($context->contextlevel != CONTEXT_MODULE) {
 120              return;
 121          }
 122  
 123          $params = ['contextid' => $context->id];
 124  
 125          $sql = "SELECT d.usercreated, d.usermodified
 126                    FROM {grading_definitions} d
 127                    JOIN {grading_areas} a ON a.id = d.areaid
 128                    WHERE a.contextid = :contextid";
 129          $userlist->add_from_sql('usercreated', $sql, $params);
 130          $userlist->add_from_sql('usermodified', $sql, $params);
 131  
 132          $sql = "SELECT i.raterid
 133                    FROM {grading_definitions} d
 134                    JOIN {grading_areas} a ON a.id = d.areaid
 135                    JOIN {grading_instances} i ON i.definitionid = d.id
 136                    WHERE a.contextid = :contextid";
 137          $userlist->add_from_sql('raterid', $sql, $params);
 138      }
 139  
 140      /**
 141       * Export all user data for the specified user, in the specified contexts.
 142       *
 143       * @param approved_contextlist $contextlist The approved contexts to export information for.
 144       */
 145      public static function export_user_data(approved_contextlist $contextlist) {
 146          // Remove contexts different from MODULE.
 147          $contexts = array_reduce($contextlist->get_contexts(), function($carry, $context) {
 148              if ($context->contextlevel == CONTEXT_MODULE) {
 149                  $carry[] = $context;
 150              }
 151              return $carry;
 152          }, []);
 153  
 154          if (empty($contexts)) {
 155              return;
 156          }
 157  
 158          $userid = $contextlist->get_user()->id;
 159          $subcontext = [get_string('gradingmethod', 'grading')];
 160          foreach ($contexts as $context) {
 161              // Export grading definitions created or modified on this context.
 162              self::export_definitions($context, $subcontext, $userid);
 163          }
 164      }
 165  
 166      /**
 167       * Export all user data related to a context and itemid.
 168       *
 169       * @param  \context $context    Context to export on.
 170       * @param  int      $itemid     Item ID to export on.
 171       * @param  array    $subcontext Directory location to export to.
 172       */
 173      public static function export_item_data(\context $context, int $itemid, array $subcontext) {
 174          global $DB;
 175  
 176          $sql = "SELECT gi.id AS instanceid, gd.id AS definitionid, gd.method
 177                    FROM {grading_areas} ga
 178                    JOIN {grading_definitions} gd ON gd.areaid = ga.id
 179                    JOIN {grading_instances} gi ON gi.definitionid = gd.id AND gi.itemid = :itemid
 180                   WHERE ga.contextid = :contextid";
 181          $params = [
 182              'itemid' => $itemid,
 183              'contextid' => $context->id,
 184          ];
 185          $records = $DB->get_recordset_sql($sql, $params);
 186          foreach ($records as $record) {
 187              $instancedata = manager::component_class_callback(
 188                  "gradingform_{$record->method}",
 189                  gradingform_provider_v2::class,
 190                  'export_gradingform_instance_data',
 191                  [$context, $record->instanceid, $subcontext]
 192              );
 193          }
 194          $records->close();
 195      }
 196  
 197      /**
 198       * Deletes all user data related to a context and possibly an itemid.
 199       *
 200       * @param  \context $context The context to delete on.
 201       * @param  int|null $itemid  An optional item ID to refine the deletion.
 202       */
 203      public static function delete_instance_data(\context $context, int $itemid = null) {
 204          if (is_null($itemid)) {
 205              self::delete_data_for_instances($context);
 206          } else {
 207              self::delete_data_for_instances($context, [$itemid]);
 208          }
 209      }
 210  
 211      /**
 212       * Deletes all user data related to a context and possibly itemids.
 213       *
 214       * @param  \context $context The context to delete on.
 215       * @param  array $itemids  An optional list of item IDs to refine the deletion.
 216       */
 217      public static function delete_data_for_instances(\context $context, array $itemids = []) {
 218          global $DB;
 219          $itemsql = '';
 220          $params = ['contextid' => $context->id];
 221          if (!empty($itemids)) {
 222              list($itemsql, $itemparams) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED);
 223              $params = array_merge($params, $itemparams);
 224              $itemsql = "AND itemid $itemsql";
 225          }
 226          $sql = "SELECT gi.id AS instanceid, gd.id, gd.method
 227                    FROM {grading_definitions} gd
 228                    JOIN {grading_instances} gi ON gi.definitionid = gd.id
 229                    JOIN {grading_areas} ga ON ga.id = gd.areaid
 230                   WHERE ga.contextid = :contextid $itemsql";
 231          $records = $DB->get_records_sql($sql, $params);
 232          if ($records) {
 233              $firstrecord = current($records);
 234              $method = $firstrecord->method;
 235              $instanceids = array_map(function($record) {
 236                  return $record->instanceid;
 237              }, $records);
 238              manager::component_class_callback(
 239                  "gradingform_{$method}",
 240                  gradingform_provider_v2::class,
 241                  'delete_gradingform_for_instances',
 242                  [$instanceids]);
 243              // Delete grading_instances rows.
 244              $DB->delete_records_list('grading_instances', 'id', $instanceids);
 245          }
 246      }
 247  
 248      /**
 249       * Exports the data related to grading definitions within the specified context/subcontext.
 250       *
 251       * @param  \context         $context Context owner of the data.
 252       * @param  array            $subcontext Subcontext owner of the data.
 253       * @param  int              $userid The user whose information is to be exported.
 254       */
 255      protected static function export_definitions(\context $context, array $subcontext, int $userid = 0) {
 256          global $DB;
 257  
 258          $join = "JOIN {grading_areas} a ON a.id = d.areaid
 259                   JOIN {context} c ON a.contextid = c.id AND c.contextlevel = :contextlevel";
 260          $select = 'a.contextid = :contextid';
 261          $params = [
 262              'contextlevel' => CONTEXT_MODULE,
 263              'contextid'    => $context->id
 264          ];
 265  
 266          if (!empty($userid)) {
 267              $join .= ' LEFT JOIN {grading_instances} i ON i.definitionid = d.id AND i.raterid = :raterid';
 268              $select .= ' AND (usercreated = :usercreated
 269                  OR usermodified = :usermodified OR i.id IS NOT NULL)';
 270              $params['usercreated'] = $userid;
 271              $params['usermodified'] = $userid;
 272              $params['raterid'] = $userid;
 273          }
 274  
 275          $sql = "SELECT gd.id,
 276                         gd.method,
 277                         gd.name,
 278                         gd.description,
 279                         gd.timecopied,
 280                         gd.timecreated,
 281                         gd.usercreated,
 282                         gd.timemodified,
 283                         gd.usermodified
 284                    FROM (
 285                          SELECT DISTINCT d.id
 286                                     FROM {grading_definitions} d
 287                                    $join
 288                                    WHERE $select
 289                    ) ids
 290                    JOIN {grading_definitions} gd ON gd.id = ids.id";
 291          $definitions = $DB->get_recordset_sql($sql, $params);
 292          $defdata = [];
 293          foreach ($definitions as $definition) {
 294              $tmpdata = [
 295                  'method' => $definition->method,
 296                  'name' => $definition->name,
 297                  'description' => $definition->description,
 298                  'timecreated' => transform::datetime($definition->timecreated),
 299                  'usercreated' => transform::user($definition->usercreated),
 300                  'timemodified' => transform::datetime($definition->timemodified),
 301                  'usermodified' => transform::user($definition->usermodified),
 302              ];
 303              if (!empty($definition->timecopied)) {
 304                  $tmpdata['timecopied'] = transform::datetime($definition->timecopied);
 305              }
 306  
 307              // MDL-63167 - This section is to be removed with the final deprecation of the gradingform_provider interface.
 308              // Export gradingform information (if needed).
 309              $instancedata = manager::component_class_callback(
 310                  "gradingform_{$definition->method}",
 311                  gradingform_provider::class,
 312                  'get_gradingform_export_data',
 313                  [$context, $definition, $userid]
 314              );
 315              if (null !== $instancedata) {
 316                  $tmpdata = array_merge($tmpdata, $instancedata);
 317              }
 318              // End of section to be removed with deprecation.
 319  
 320              $defdata[] = (object) $tmpdata;
 321  
 322              // Export grading_instances information.
 323              self::export_grading_instances($context, $subcontext, $definition->id, $userid);
 324          }
 325          $definitions->close();
 326  
 327          if (!empty($defdata)) {
 328              $data = (object) [
 329                  'definitions' => $defdata,
 330              ];
 331  
 332              writer::with_context($context)->export_data($subcontext, $data);
 333          }
 334      }
 335  
 336      /**
 337       * Exports the data related to grading instances within the specified definition.
 338       *
 339       * @param  \context         $context Context owner of the data.
 340       * @param  array            $subcontext Subcontext owner of the data.
 341       * @param  int              $definitionid The definition ID whose grading instance information is to be exported.
 342       * @param  int              $userid The user whose information is to be exported.
 343       */
 344      protected static function export_grading_instances(\context $context, array $subcontext, int $definitionid, int $userid = 0) {
 345          global $DB;
 346  
 347          $params = ['definitionid' => $definitionid];
 348          if (!empty($userid)) {
 349              $params['raterid'] = $userid;
 350          }
 351          $instances = $DB->get_recordset('grading_instances', $params);
 352          $instancedata = [];
 353          foreach ($instances as $instance) {
 354              // TODO: Get the status name (instead of the ID).
 355              $tmpdata = [
 356                  'rawgrade' => $instance->rawgrade,
 357                  'status' => $instance->status,
 358                  'feedback' => $instance->feedback,
 359                  'feedbackformat' => $instance->feedbackformat,
 360                  'timemodified' => transform::datetime($instance->timemodified),
 361              ];
 362              $instancedata[] = (object) $tmpdata;
 363          }
 364          $instances->close();
 365  
 366          if (!empty($instancedata)) {
 367              $data = (object) [
 368                  'instances' => $instancedata,
 369              ];
 370  
 371              writer::with_context($context)->export_related_data($subcontext, 'gradinginstances', $data);
 372          }
 373      }
 374  
 375      /**
 376       * No deletion of the advanced grading is done.
 377       *
 378       * @param \context $context the context to delete in.
 379       */
 380      public static function delete_data_for_all_users_in_context(\context $context) {
 381          // MDL-63167 - This section is to be removed with the final deprecation of the gradingform_provider interface.
 382          manager::plugintype_class_callback(
 383              'gradingform',
 384              gradingform_provider::class,
 385              'delete_gradingform_for_context',
 386              [$context]
 387          );
 388          // End of section to be removed for final deprecation.
 389      }
 390  
 391      /**
 392       * Deletion of data in this provider is only related to grades and so can not be
 393       * deleted for the creator of the advanced grade criteria.
 394       *
 395       * @param approved_contextlist $contextlist a list of contexts approved for deletion.
 396       */
 397      public static function delete_data_for_user(approved_contextlist $contextlist) {
 398          // MDL-63167 - This section is to be removed with the final deprecation of the gradingform_provider interface.
 399          manager::plugintype_class_callback(
 400              'gradingform',
 401              gradingform_provider::class,
 402              'delete_gradingform_for_userid',
 403              [$contextlist]
 404          );
 405          // End of section to be removed for final deprecation.
 406      }
 407  
 408      /**
 409       * Delete multiple users within a single context.
 410       *
 411       * @param approved_userlist $userlist The approved context and user information to delete information for.
 412       */
 413      public static function delete_data_for_users(\core_privacy\local\request\approved_userlist $userlist) {
 414          // The only information left to be deleted here is the grading definitions. Currently we are not deleting these.
 415      }
 416  }