Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 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]

   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              $defdata[] = (object) $tmpdata;
 308  
 309              // Export grading_instances information.
 310              self::export_grading_instances($context, $subcontext, $definition->id, $userid);
 311          }
 312          $definitions->close();
 313  
 314          if (!empty($defdata)) {
 315              $data = (object) [
 316                  'definitions' => $defdata,
 317              ];
 318  
 319              writer::with_context($context)->export_data($subcontext, $data);
 320          }
 321      }
 322  
 323      /**
 324       * Exports the data related to grading instances within the specified definition.
 325       *
 326       * @param  \context         $context Context owner of the data.
 327       * @param  array            $subcontext Subcontext owner of the data.
 328       * @param  int              $definitionid The definition ID whose grading instance information is to be exported.
 329       * @param  int              $userid The user whose information is to be exported.
 330       */
 331      protected static function export_grading_instances(\context $context, array $subcontext, int $definitionid, int $userid = 0) {
 332          global $DB;
 333  
 334          $params = ['definitionid' => $definitionid];
 335          if (!empty($userid)) {
 336              $params['raterid'] = $userid;
 337          }
 338          $instances = $DB->get_recordset('grading_instances', $params);
 339          $instancedata = [];
 340          foreach ($instances as $instance) {
 341              // TODO: Get the status name (instead of the ID).
 342              $tmpdata = [
 343                  'rawgrade' => $instance->rawgrade,
 344                  'status' => $instance->status,
 345                  'feedback' => $instance->feedback,
 346                  'feedbackformat' => $instance->feedbackformat,
 347                  'timemodified' => transform::datetime($instance->timemodified),
 348              ];
 349              $instancedata[] = (object) $tmpdata;
 350          }
 351          $instances->close();
 352  
 353          if (!empty($instancedata)) {
 354              $data = (object) [
 355                  'instances' => $instancedata,
 356              ];
 357  
 358              writer::with_context($context)->export_related_data($subcontext, 'gradinginstances', $data);
 359          }
 360      }
 361  
 362      /**
 363       * No deletion of the advanced grading is done.
 364       *
 365       * @param \context $context the context to delete in.
 366       */
 367      public static function delete_data_for_all_users_in_context(\context $context) {
 368          // The only information left to be deleted here is the grading definitions. Currently we are not deleting these.
 369      }
 370  
 371      /**
 372       * Deletion of data in this provider is only related to grades and so can not be
 373       * deleted for the creator of the advanced grade criteria.
 374       *
 375       * @param approved_contextlist $contextlist a list of contexts approved for deletion.
 376       */
 377      public static function delete_data_for_user(approved_contextlist $contextlist) {
 378          // The only information left to be deleted here is the grading definitions. Currently we are not deleting these.
 379      }
 380  
 381      /**
 382       * Delete multiple users within a single context.
 383       *
 384       * @param approved_userlist $userlist The approved context and user information to delete information for.
 385       */
 386      public static function delete_data_for_users(\core_privacy\local\request\approved_userlist $userlist) {
 387          // The only information left to be deleted here is the grading definitions. Currently we are not deleting these.
 388      }
 389  }