MDL-66999 theme_boost: @extend remove extends for grades
[moodle.git] / grade / grading / classes / privacy / provider.php
CommitLineData
6416e87f
SA
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
25namespace core_grading\privacy;
26
27defined('MOODLE_INTERNAL') || die();
28
29use \core_privacy\local\metadata\collection;
30use \core_privacy\local\request\approved_contextlist;
31use \core_privacy\local\request\contextlist;
32use \core_privacy\local\request\transform;
33use \core_privacy\local\request\writer;
34use \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 */
42class provider implements
43 \core_privacy\local\metadata\provider,
98042502
AG
44 \core_privacy\local\request\plugin\provider,
45 \core_privacy\local\request\core_userlist_provider,
6416e87f
SA
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
b3252ecd 98 LEFT JOIN {grading_instances} i ON i.definitionid = d.id AND i.raterid = :raterid
6416e87f 99 WHERE c.contextlevel = :contextlevel
b3252ecd 100 AND (d.usercreated = :usercreated OR d.usermodified = :usermodified OR i.id IS NOT NULL)";
6416e87f
SA
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
98042502 112 /**
b16a3388 113 * Get the list of users who have data within a context.
98042502
AG
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
6416e87f
SA
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
f3a6d9bb
AG
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) {
98042502
AG
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 = []) {
f3a6d9bb
AG
218 global $DB;
219 $itemsql = '';
220 $params = ['contextid' => $context->id];
98042502
AG
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";
f3a6d9bb
AG
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
6416e87f
SA
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)) {
b3252ecd 267 $join .= ' LEFT JOIN {grading_instances} i ON i.definitionid = d.id AND i.raterid = :raterid';
6416e87f 268 $select .= ' AND (usercreated = :usercreated
b3252ecd 269 OR usermodified = :usermodified OR i.id IS NOT NULL)';
6416e87f
SA
270 $params['usercreated'] = $userid;
271 $params['usermodified'] = $userid;
272 $params['raterid'] = $userid;
273 }
274
5e08a28f
SA
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";
6416e87f
SA
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 }
f3a6d9bb
AG
306
307 // MDL-63167 - This section is to be removed with the final deprecation of the gradingform_provider interface.
6416e87f
SA
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 }
f3a6d9bb 318 // End of section to be removed with deprecation.
6416e87f
SA
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 /**
f3a6d9bb 376 * No deletion of the advanced grading is done.
6416e87f 377 *
f3a6d9bb 378 * @param \context $context the context to delete in.
6416e87f
SA
379 */
380 public static function delete_data_for_all_users_in_context(\context $context) {
f3a6d9bb 381 // MDL-63167 - This section is to be removed with the final deprecation of the gradingform_provider interface.
6416e87f
SA
382 manager::plugintype_class_callback(
383 'gradingform',
384 gradingform_provider::class,
385 'delete_gradingform_for_context',
386 [$context]
387 );
f3a6d9bb 388 // End of section to be removed for final deprecation.
6416e87f
SA
389 }
390
391 /**
f3a6d9bb
AG
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.
6416e87f 394 *
f3a6d9bb 395 * @param approved_contextlist $contextlist a list of contexts approved for deletion.
6416e87f
SA
396 */
397 public static function delete_data_for_user(approved_contextlist $contextlist) {
f3a6d9bb 398 // MDL-63167 - This section is to be removed with the final deprecation of the gradingform_provider interface.
6416e87f
SA
399 manager::plugintype_class_callback(
400 'gradingform',
401 gradingform_provider::class,
402 'delete_gradingform_for_userid',
403 [$contextlist]
404 );
f3a6d9bb 405 // End of section to be removed for final deprecation.
6416e87f 406 }
98042502
AG
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 }
6416e87f 416}