Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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 mod_assignment.
19
 *
20
 * @package    mod_assignment
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 mod_assignment\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\helper;
33
use core_privacy\local\request\approved_userlist;
34
use core_privacy\local\request\userlist;
35
 
36
defined('MOODLE_INTERNAL') || die();
37
 
38
global $CFG;
39
require_once($CFG->dirroot . '/mod/assignment/lib.php');
40
 
41
/**
42
 * Implementation of the privacy subsystem plugin provider for mod_assignment.
43
 *
44
 * @copyright  2018 Zig Tan <zig@moodle.com>
45
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
46
 */
47
class provider implements
48
    \core_privacy\local\metadata\provider,
49
    \core_privacy\local\request\plugin\provider,
50
    \core_privacy\local\request\user_preference_provider,
51
    \core_privacy\local\request\core_userlist_provider {
52
 
53
    /**
54
     * Return the fields which contain personal data.
55
     *
56
     * @param collection $collection a reference to the collection to use to store the metadata.
57
     * @return collection the updated collection of metadata items.
58
     */
59
    public static function get_metadata(collection $collection) : collection {
60
        $collection->add_database_table(
61
            'assignment_submissions',
62
            [
63
                'userid' => 'privacy:metadata:assignment_submissions:userid',
64
                'timecreated' => 'privacy:metadata:assignment_submissions:timecreated',
65
                'timemodified' => 'privacy:metadata:assignment_submissions:timemodified',
66
                'numfiles' => 'privacy:metadata:assignment_submissions:numfiles',
67
                'data1' => 'privacy:metadata:assignment_submissions:data1',
68
                'data2' => 'privacy:metadata:assignment_submissions:data2',
69
                'grade' => 'privacy:metadata:assignment_submissions:grade',
70
                'submissioncomment' => 'privacy:metadata:assignment_submissions:submissioncomment',
71
                'teacher' => 'privacy:metadata:assignment_submissions:teacher',
72
                'timemarked' => 'privacy:metadata:assignment_submissions:timemarked',
73
                'mailed' => 'privacy:metadata:assignment_submissions:mailed'
74
            ],
75
            'privacy:metadata:assignment_submissions'
76
        );
77
 
78
        // Legacy mod_assignment preferences from Moodle 2.X.
79
        $collection->add_user_preference('assignment_filter', 'privacy:metadata:assignmentfilter');
80
        $collection->add_user_preference('assignment_mailinfo', 'privacy:metadata:assignmentmailinfo');
81
        $collection->add_user_preference('assignment_perpage', 'privacy:metadata:assignmentperpage');
82
        $collection->add_user_preference('assignment_quickgrade', 'privacy:metadata:assignmentquickgrade');
83
 
84
        return $collection;
85
    }
86
 
87
    /**
88
     * Get the list of contexts that contain user information for the specified user.
89
     *
90
     * @param int $userid the userid.
91
     * @return contextlist the list of contexts containing user info for the user.
92
     */
93
    public static function get_contexts_for_userid(int $userid) : contextlist {
94
        $contextlist = new contextlist();
95
 
96
        $sql = "SELECT DISTINCT
97
                       ctx.id
98
                  FROM {context} ctx
99
                  JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmodule
100
                  JOIN {modules} m ON cm.module = m.id AND m.name = :modulename
101
                  JOIN {assignment} a ON cm.instance = a.id
102
                  JOIN {assignment_submissions} s ON s.assignment = a.id
103
                 WHERE s.userid = :userid
104
                    OR s.teacher = :teacher";
105
 
106
        $params = [
107
            'contextmodule'  => CONTEXT_MODULE,
108
            'modulename'    => 'assignment',
109
            'userid'        => $userid,
110
            'teacher'       => $userid
111
        ];
112
 
113
        $contextlist->add_from_sql($sql, $params);
114
 
115
        return $contextlist;
116
    }
117
 
118
    /**
119
     * Get the list of users who have data within a context.
120
     *
121
     * @param   userlist    $userlist   The userlist containing the list of users who have data in this context/plugin combination.
122
     */
123
    public static function get_users_in_context(userlist $userlist) {
124
        $context = $userlist->get_context();
125
        if ($context->contextlevel != CONTEXT_MODULE) {
126
            return;
127
        }
128
 
129
        $params = [
130
            'modulename' => 'assignment',
131
            'contextlevel' => CONTEXT_MODULE,
132
            'contextid' => $context->id
133
        ];
134
        $sql = "SELECT s.userid
135
                  FROM {assignment_submissions} s
136
                  JOIN {assignment} a ON s.assignment = a.id
137
                  JOIN {modules} m ON m.name = :modulename
138
                  JOIN {course_modules} cm ON a.id = cm.instance AND cm.module = m.id
139
                  JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextlevel
140
                 WHERE ctx.id = :contextid
141
        ";
142
        $userlist->add_from_sql('userid', $sql, $params);
143
 
144
        $sql = "SELECT s.teacher
145
                  FROM {assignment_submissions} s
146
                  JOIN {assignment} a ON s.assignment = a.id
147
                  JOIN {modules} m ON m.name = :modulename
148
                  JOIN {course_modules} cm ON a.id = cm.instance AND cm.module = m.id
149
                  JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextlevel
150
                 WHERE ctx.id = :contextid
151
        ";
152
        $userlist->add_from_sql('teacher', $sql, $params);
153
    }
154
 
155
    /**
156
     * Export personal data for the given approved_contextlist.
157
     * User and context information is contained within the contextlist.
158
     *
159
     * @param approved_contextlist $contextlist a list of contexts approved for export.
160
     */
161
    public static function export_user_data(approved_contextlist $contextlist) {
162
        if (empty($contextlist->count())) {
163
            return;
164
        }
165
 
166
        $user = $contextlist->get_user();
167
 
168
        foreach ($contextlist->get_contexts() as $context) {
169
            if ($context->contextlevel != CONTEXT_MODULE) {
170
                continue;
171
            }
172
 
173
            // Cannot make use of helper::export_context_files(), need to manually export assignment details.
174
            $assignmentdata = self::get_assignment_by_context($context);
175
 
176
            // Get assignment details object for output.
177
            $assignment = self::get_assignment_output($assignmentdata);
178
            writer::with_context($context)->export_data([], $assignment);
179
 
180
            // Check if the user has marked any assignment's submissions to determine assignment submissions to export.
181
            $teacher = (self::has_marked_assignment_submissions($assignmentdata->id, $user->id) == true) ? true : false;
182
 
183
            // Get the assignment submissions submitted by & marked by the user for an assignment.
184
            $submissionsdata = self::get_assignment_submissions_by_assignment($assignmentdata->id, $user->id, $teacher);
185
 
186
            foreach ($submissionsdata as $submissiondata) {
187
                // Default subcontext path to export assignment submissions submitted by the user.
188
                $subcontexts = [
189
                    get_string('privacy:submissionpath', 'mod_assignment')
190
                ];
191
 
192
                if ($teacher == true) {
193
                    if ($submissiondata->teacher == $user->id) {
194
                        // Export assignment submissions that have been marked by the user.
195
                        $subcontexts = [
196
                            get_string('privacy:markedsubmissionspath', 'mod_assignment'),
197
                            transform::user($submissiondata->userid)
198
                        ];
199
                    }
200
                }
201
 
202
                // Get assignment submission details object for output.
203
                $submission = self::get_assignment_submission_output($submissiondata);
204
                $itemid = $submissiondata->id;
205
 
206
                writer::with_context($context)
207
                    ->export_data($subcontexts, $submission)
208
                    ->export_area_files($subcontexts, 'mod_assignment', 'submission', $itemid);
209
            }
210
        }
211
    }
212
 
213
    /**
214
     * Stores the user preferences related to mod_assign.
215
     *
216
     * @param  int $userid The user ID that we want the preferences for.
217
     */
218
    public static function export_user_preferences(int $userid) {
219
        $context = \context_system::instance();
220
        $assignmentpreferences = [
221
            'assignment_filter' => [
222
                'string' => get_string('privacy:metadata:assignmentfilter', 'mod_assignment'),
223
                'bool' => false
224
            ],
225
            'assignment_mailinfo' => [
226
                'string' => get_string('privacy:metadata:assignmentmailinfo', 'mod_assignment'),
227
                'bool' => false
228
            ],
229
            'assignment_perpage' => [
230
                'string' => get_string('privacy:metadata:assignmentperpage', 'mod_assignment'),
231
                'bool' => false
232
            ],
233
            'assignment_quickgrade' => [
234
                'string' => get_string('privacy:metadata:assignmentquickgrade', 'mod_assignment'),
235
                'bool' => false
236
            ],
237
        ];
238
        foreach ($assignmentpreferences as $key => $preference) {
239
            $value = get_user_preferences($key, null, $userid);
240
            if ($preference['bool']) {
241
                $value = transform::yesno($value);
242
            }
243
            if (isset($value)) {
244
                writer::with_context($context)
245
                    ->export_user_preference('mod_assignment', $key, $value, $preference['string']);
246
            }
247
        }
248
    }
249
 
250
    /**
251
     * Delete all data for all users in the specified context.
252
     *
253
     * @param \context $context the context to delete in.
254
     */
255
    public static function delete_data_for_all_users_in_context(\context $context) {
256
        global $DB;
257
 
258
        if ($context->contextlevel == CONTEXT_MODULE) {
259
            // Delete all assignment submissions for the assignment associated with the context module.
260
            $assignment = self::get_assignment_by_context($context);
261
            if ($assignment != null) {
262
                $DB->delete_records('assignment_submissions', ['assignment' => $assignment->id]);
263
 
264
                // Delete all file uploads associated with the assignment submission for the specified context.
265
                $fs = get_file_storage();
266
                $fs->delete_area_files($context->id, 'mod_assignment', 'submission');
267
            }
268
        }
269
    }
270
 
271
    /**
272
     * Delete all user data for the specified user, in the specified contexts.
273
     *
274
     * @param approved_contextlist $contextlist a list of contexts approved for deletion.
275
     */
276
    public static function delete_data_for_user(approved_contextlist $contextlist) {
277
        global $DB;
278
 
279
        if (empty($contextlist->count())) {
280
            return;
281
        }
282
 
283
        $userid = $contextlist->get_user()->id;
284
 
285
        // Only retrieve assignment submissions submitted by the user for deletion.
286
        $assignmentsubmissionids = array_keys(self::get_assignment_submissions_by_contextlist($contextlist, $userid));
287
        $DB->delete_records_list('assignment_submissions', 'id', $assignmentsubmissionids);
288
 
289
        // Delete all file uploads associated with the assignment submission for the user's specified list of contexts.
290
        $fs = get_file_storage();
291
        foreach ($contextlist->get_contextids() as $contextid) {
292
            foreach ($assignmentsubmissionids as $submissionid) {
293
                $fs->delete_area_files($contextid, 'mod_assignment', 'submission', $submissionid);
294
            }
295
        }
296
    }
297
 
298
    /**
299
     * Delete multiple users within a single context.
300
     *
301
     * @param   approved_userlist       $userlist The approved context and user information to delete information for.
302
     */
303
    public static function delete_data_for_users(approved_userlist $userlist) {
304
        global $DB;
305
 
306
        $context = $userlist->get_context();
307
        // If the context isn't for a module then return early.
308
        if ($context->contextlevel != CONTEXT_MODULE) {
309
            return;
310
        }
311
        // Fetch the assignment.
312
        $assignment = self::get_assignment_by_context($context);
313
        $userids = $userlist->get_userids();
314
 
315
        list($inorequalsql, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
316
        $params['assignmentid'] = $assignment->id;
317
 
318
        // Get submission ids.
319
        $sql = "
320
            SELECT s.id
321
            FROM {assignment_submissions} s
322
            JOIN {assignment} a ON s.assignment = a.id
323
            WHERE a.id = :assignmentid
324
            AND s.userid $inorequalsql
325
        ";
326
 
327
        $submissionids = $DB->get_records_sql($sql, $params);
328
        list($submissionidsql, $submissionparams) = $DB->get_in_or_equal(array_keys($submissionids), SQL_PARAMS_NAMED);
329
        $fs = get_file_storage();
330
        $fs->delete_area_files_select($context->id, 'mod_assignment', 'submission', $submissionidsql, $submissionparams);
331
        // Delete related tables.
332
        $DB->delete_records_list('assignment_submissions', 'id', array_keys($submissionids));
333
    }
334
 
335
    // Start of helper functions.
336
 
337
    /**
338
     * Helper function to check if a user has marked assignment submissions for a given assignment.
339
     *
340
     * @param int $assignmentid The assignment ID to check if user has marked associated submissions.
341
     * @param int $userid       The user ID to check if user has marked associated submissions.
342
     * @return bool             If user has marked associated submissions returns true, otherwise false.
343
     * @throws \dml_exception
344
     */
345
    protected static function has_marked_assignment_submissions($assignmentid, $userid) {
346
        global $DB;
347
 
348
        $params = [
349
            'assignment' => $assignmentid,
350
            'teacher'    => $userid
351
        ];
352
 
353
        $sql = "SELECT count(s.id) as nomarked
354
                  FROM {assignment_submissions} s
355
                 WHERE s.assignment = :assignment
356
                   AND s.teacher = :teacher";
357
 
358
        $results = $DB->get_record_sql($sql, $params);
359
 
360
        return ($results->nomarked > 0) ? true : false;
361
    }
362
 
363
    /**
364
     * Helper function to return assignment for a context module.
365
     *
366
     * @param object $context   The context module object to return the assignment record by.
367
     * @return mixed            The assignment details or null record associated with the context module.
368
     * @throws \dml_exception
369
     */
370
    protected static function get_assignment_by_context($context) {
371
        global $DB;
372
 
373
        $params = [
374
            'modulename' => 'assignment',
375
            'contextmodule' => CONTEXT_MODULE,
376
            'contextid' => $context->id
377
        ];
378
 
379
        $sql = "SELECT a.id,
380
                       a.name,
381
                       a.intro,
382
                       a.assignmenttype,
383
                       a.grade,
384
                       a.timedue,
385
                       a.timeavailable,
386
                       a.timemodified
387
                  FROM {assignment} a
388
                  JOIN {course_modules} cm ON a.id = cm.instance
389
                  JOIN {modules} m ON m.id = cm.module AND m.name = :modulename
390
                  JOIN {context} ctx ON ctx.instanceid = cm.id AND ctx.contextlevel = :contextmodule
391
                 WHERE ctx.id = :contextid";
392
 
393
        return $DB->get_record_sql($sql, $params);
394
    }
395
 
396
    /**
397
     * Helper function to return assignment submissions submitted by / marked by a user and their contextlist.
398
     *
399
     * @param object $contextlist   Object with the contexts related to a userid to retrieve assignment submissions by.
400
     * @param int $userid           The user ID to find assignment submissions that were submitted by.
401
     * @param bool $teacher         The teacher status to determine if marked assignment submissions should be returned.
402
     * @return array                Array of assignment submission details.
403
     * @throws \coding_exception
404
     * @throws \dml_exception
405
     */
406
    protected static function get_assignment_submissions_by_contextlist($contextlist, $userid, $teacher = false) {
407
        global $DB;
408
 
409
        list($contextsql, $contextparams) = $DB->get_in_or_equal($contextlist->get_contextids(), SQL_PARAMS_NAMED);
410
 
411
        $params = [
412
            'contextmodule' => CONTEXT_MODULE,
413
            'modulename' => 'assignment',
414
            'userid' => $userid
415
        ];
416
 
417
        $sql = "SELECT s.id as id,
418
                       s.assignment as assignment,
419
                       s.numfiles as numfiles,
420
                       s.data1 as data1,
421
                       s.data2 as data2,
422
                       s.grade as grade,
423
                       s.submissioncomment as submissioncomment,
424
                       s.teacher as teacher,
425
                       s.timemarked as timemarked,
426
                       s.timecreated as timecreated,
427
                       s.timemodified as timemodified
428
                  FROM {context} ctx
429
                  JOIN {course_modules} cm ON cm.id = ctx.instanceid AND ctx.contextlevel = :contextmodule
430
                  JOIN {modules} m ON cm.module = m.id AND m.name = :modulename
431
                  JOIN {assignment} a ON cm.instance = a.id
432
                  JOIN {assignment_submissions} s ON s.assignment = a.id
433
                 WHERE (s.userid = :userid";
434
 
435
        if ($teacher == true) {
436
            $sql .= " OR s.teacher = :teacher";
437
            $params['teacher'] = $userid;
438
        }
439
 
440
        $sql .= ")";
441
 
442
        $sql .= " AND ctx.id {$contextsql}";
443
        $params += $contextparams;
444
 
445
        return $DB->get_records_sql($sql, $params);
446
    }
447
 
448
    /**
449
     * Helper function to retrieve assignment submissions submitted by / marked by a user for a specific assignment.
450
     *
451
     * @param int $assignmentid     The assignment ID to retrieve assignment submissions by.
452
     * @param int $userid           The user ID to retrieve assignment submissions submitted / marked by.
453
     * @param bool $teacher         The teacher status to determine if marked assignment submissions should be returned.
454
     * @return array                Array of assignment submissions details.
455
     * @throws \dml_exception
456
     */
457
    protected static function get_assignment_submissions_by_assignment($assignmentid, $userid, $teacher = false) {
458
        global $DB;
459
 
460
        $params = [
461
            'assignment' => $assignmentid,
462
            'userid' => $userid
463
        ];
464
 
465
        $sql = "SELECT s.id as id,
466
                       s.assignment as assignment,
467
                       s.numfiles as numfiles,
468
                       s.data1 as data1,
469
                       s.data2 as data2,
470
                       s.grade as grade,
471
                       s.submissioncomment as submissioncomment,
472
                       s.teacher as teacher,
473
                       s.timemarked as timemarked,
474
                       s.timecreated as timecreated,
475
                       s.timemodified as timemodified,
476
                       s.userid as userid
477
                  FROM {assignment_submissions} s
478
                 WHERE s.assignment = :assignment
479
                   AND (s.userid = :userid";
480
 
481
        if ($teacher == true) {
482
            $sql .= " OR s.teacher = :teacher";
483
            $params['teacher'] = $userid;
484
        }
485
 
486
        $sql .= ")";
487
 
488
        return $DB->get_records_sql($sql, $params);
489
    }
490
 
491
    /**
492
     * Helper function generate assignment output object for exporting.
493
     *
494
     * @param object $assignmentdata    Object containing assignment data.
495
     * @return object                   Formatted assignment output object for exporting.
496
     */
497
    protected static function get_assignment_output($assignmentdata) {
498
        $assignment = (object) [
499
            'name' => $assignmentdata->name,
500
            'intro' => $assignmentdata->intro,
501
            'assignmenttype' => $assignmentdata->assignmenttype,
502
            'grade' => $assignmentdata->grade,
503
            'timemodified' => transform::datetime($assignmentdata->timemodified)
504
        ];
505
 
506
        if ($assignmentdata->timeavailable != 0) {
507
            $assignment->timeavailable = transform::datetime($assignmentdata->timeavailable);
508
        }
509
 
510
        if ($assignmentdata->timedue != 0) {
511
            $assignment->timedue = transform::datetime($assignmentdata->timedue);
512
        }
513
 
514
        return $assignment;
515
    }
516
 
517
    /**
518
     * Helper function generate assignment submission output object for exporting.
519
     *
520
     * @param object $submissiondata    Object containing assignment submission data.
521
     * @return object                   Formatted assignment submission output for exporting.
522
     */
523
    protected static function get_assignment_submission_output($submissiondata) {
524
        $submission = (object) [
525
            'assignment' => $submissiondata->assignment,
526
            'numfiles' => $submissiondata->numfiles,
527
            'data1' => $submissiondata->data1,
528
            'data2' => $submissiondata->data2,
529
            'grade' => $submissiondata->grade,
530
            'submissioncomment' => $submissiondata->submissioncomment,
531
            'teacher' => transform::user($submissiondata->teacher)
532
        ];
533
 
534
        if ($submissiondata->timecreated != 0) {
535
            $submission->timecreated = transform::datetime($submissiondata->timecreated);
536
        }
537
 
538
        if ($submissiondata->timemarked != 0) {
539
            $submission->timemarked = transform::datetime($submissiondata->timemarked);
540
        }
541
 
542
        if ($submissiondata->timemodified != 0) {
543
            $submission->timemodified = transform::datetime($submissiondata->timemodified);
544
        }
545
 
546
        return $submission;
547
    }
548
}