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
 * This file contains the form add/update a data purpose.
19
 *
20
 * @package   tool_dataprivacy
21
 * @copyright 2018 David Monllao
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace tool_dataprivacy\form;
26
defined('MOODLE_INTERNAL') || die();
27
 
28
use core\form\persistent;
29
 
30
/**
31
 * Data purpose form.
32
 *
33
 * @package   tool_dataprivacy
34
 * @copyright 2018 David Monllao
35
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36
 */
37
class purpose extends persistent {
38
 
39
    /**
40
     * @var string The persistent class.
41
     */
42
    protected static $persistentclass = 'tool_dataprivacy\\purpose';
43
 
44
    /**
45
     * @var array The list of current overrides.
46
     */
47
    protected $existingoverrides = [];
48
 
49
    /**
50
     * Define the form - called by parent constructor
51
     */
52
    public function definition() {
53
        $mform = $this->_form;
54
 
55
        $mform->addElement('text', 'name', get_string('name'), 'maxlength="100"');
56
        $mform->setType('name', PARAM_TEXT);
57
        $mform->addRule('name', get_string('required'), 'required', null, 'server');
58
        $mform->addRule('name', get_string('maximumchars', '', 100), 'maxlength', 100, 'server');
59
 
60
        $mform->addElement('editor', 'description', get_string('description'), null, ['autosave' => false]);
61
        $mform->setType('description', PARAM_CLEANHTML);
62
 
63
        // Field for selecting lawful bases (from GDPR Article 6.1).
64
        $this->add_field($this->get_lawful_base_field());
65
        $mform->addRule('lawfulbases', get_string('required'), 'required', null, 'server');
66
 
67
        // Optional field for selecting reasons for collecting sensitive personal data (from GDPR Article 9.2).
68
        $this->add_field($this->get_sensitive_base_field());
69
 
70
        $this->add_field($this->get_retention_period_fields());
71
        $this->add_field($this->get_protected_field());
72
 
73
        $this->add_override_fields();
74
 
75
        if (!empty($this->_customdata['showbuttons'])) {
76
            if (!$this->get_persistent()->get('id')) {
77
                $savetext = get_string('add');
78
            } else {
79
                $savetext = get_string('savechanges');
80
            }
81
            $this->add_action_buttons(true, $savetext);
82
        }
83
    }
84
 
85
    /**
86
     * Add a fieldset to the current form.
87
     *
88
     * @param   \stdClass   $data
89
     */
90
    protected function add_field(\stdClass $data) {
91
        foreach ($data->fields as $field) {
92
            $this->_form->addElement($field);
93
        }
94
 
95
        if (!empty($data->helps)) {
96
            foreach ($data->helps as $fieldname => $helpdata) {
97
                $help = array_merge([$fieldname], $helpdata);
98
                call_user_func_array([$this->_form, 'addHelpButton'], $help);
99
            }
100
        }
101
 
102
        if (!empty($data->types)) {
103
            foreach ($data->types as $fieldname => $type) {
104
                $this->_form->setType($fieldname, $type);
105
            }
106
        }
107
 
108
        if (!empty($data->rules)) {
109
            foreach ($data->rules as $fieldname => $ruledata) {
110
                $rule = array_merge([$fieldname], $ruledata);
111
                call_user_func_array([$this->_form, 'addRule'], $rule);
112
            }
113
        }
114
 
115
        if (!empty($data->defaults)) {
116
            foreach ($data->defaults as $fieldname => $default) {
117
                $this->_form($fieldname, $default);
118
            }
119
        }
120
    }
121
 
122
    /**
123
     * Handle addition of relevant repeated element fields for role overrides.
124
     */
125
    protected function add_override_fields() {
126
        $purpose = $this->get_persistent();
127
 
128
        if (empty($purpose->get('id'))) {
129
            // It is not possible to use repeated elements in a modal form yet.
130
            return;
131
        }
132
 
133
        $fields = [
134
            $this->get_role_override_id('roleoverride_'),
135
            $this->get_role_field('roleoverride_'),
136
            $this->get_retention_period_fields('roleoverride_'),
137
            $this->get_protected_field('roleoverride_'),
138
            $this->get_lawful_base_field('roleoverride_'),
139
            $this->get_sensitive_base_field('roleoverride_'),
140
        ];
141
 
142
        $options = [
143
            'type' => [],
144
            'helpbutton' => [],
145
        ];
146
 
147
        // Start by adding the title.
148
        $overrideelements = [
149
            $this->_form->createElement('header', 'roleoverride', get_string('roleoverride', 'tool_dataprivacy')),
150
            $this->_form->createElement(
151
                'static',
152
                'roleoverrideoverview',
153
                '',
154
                get_string('roleoverrideoverview', 'tool_dataprivacy')
155
            ),
156
        ];
157
 
158
        foreach ($fields as $fielddata) {
159
            foreach ($fielddata->fields as $field) {
160
                $overrideelements[] = $field;
161
            }
162
 
163
            if (!empty($fielddata->helps)) {
164
                foreach ($fielddata->helps as $name => $help) {
165
                    if (!isset($options[$name])) {
166
                        $options[$name] = [];
167
                    }
168
                    $options[$name]['helpbutton'] = $help;
169
                }
170
            }
171
 
172
            if (!empty($fielddata->types)) {
173
                foreach ($fielddata->types as $name => $type) {
174
                    if (!isset($options[$name])) {
175
                        $options[$name] = [];
176
                    }
177
                    $options[$name]['type'] = $type;
178
                }
179
            }
180
 
181
            if (!empty($fielddata->rules)) {
182
                foreach ($fielddata->rules as $name => $rule) {
183
                    if (!isset($options[$name])) {
184
                        $options[$name] = [];
185
                    }
186
                    $options[$name]['rule'] = $rule;
187
                }
188
            }
189
 
190
            if (!empty($fielddata->defaults)) {
191
                foreach ($fielddata->defaults as $name => $default) {
192
                    if (!isset($options[$name])) {
193
                        $options[$name] = [];
194
                    }
195
                    $options[$name]['default'] = $default;
196
                }
197
            }
198
 
199
            if (!empty($fielddata->advanceds)) {
200
                foreach ($fielddata->advanceds as $name => $advanced) {
201
                    if (!isset($options[$name])) {
202
                        $options[$name] = [];
203
                    }
204
                    $options[$name]['advanced'] = $advanced;
205
                }
206
            }
207
        }
208
 
209
        $this->existingoverrides = $purpose->get_purpose_overrides();
210
        $existingoverridecount = count($this->existingoverrides);
211
 
212
        $this->repeat_elements(
213
                $overrideelements,
214
                $existingoverridecount,
215
                $options,
216
                'overrides',
217
                'addoverride',
218
                1,
219
                get_string('addroleoverride', 'tool_dataprivacy')
220
            );
221
    }
222
 
223
    /**
224
     * Converts fields.
225
     *
226
     * @param \stdClass $data
227
     * @return \stdClass
228
     */
229
    public function filter_data_for_persistent($data) {
230
        $data = parent::filter_data_for_persistent($data);
231
 
232
        $classname = static::$persistentclass;
233
        $properties = $classname::properties_definition();
234
 
235
        $data = (object) array_filter((array) $data, function($value, $key) use ($properties) {
236
            return isset($properties[$key]);
237
        }, ARRAY_FILTER_USE_BOTH);
238
 
239
        return $data;
240
    }
241
 
242
    /**
243
     * Get the field for the role name.
244
     *
245
     * @param   string  $prefix The prefix to apply to the field
246
     * @return  \stdClass
247
     */
248
    protected function get_role_override_id(string $prefix = ''): \stdClass {
249
        $fieldname = "{$prefix}id";
250
 
251
        $fielddata = (object) [
252
            'fields' => [],
253
        ];
254
 
255
        $fielddata->fields[] = $this->_form->createElement('hidden', $fieldname);
256
        $fielddata->types[$fieldname] = PARAM_INT;
257
 
258
        return $fielddata;
259
    }
260
 
261
    /**
262
     * Get the field for the role name.
263
     *
264
     * @param   string  $prefix The prefix to apply to the field
265
     * @return  \stdClass
266
     */
267
    protected function get_role_field(string $prefix = ''): \stdClass {
268
        $fieldname = "{$prefix}roleid";
269
 
270
        $fielddata = (object) [
271
            'fields' => [],
272
            'helps' => [],
273
        ];
274
 
275
        $roles = [
276
            '' => get_string('none'),
277
        ];
278
        foreach (role_get_names() as $roleid => $role) {
279
            $roles[$roleid] = $role->localname;
280
        }
281
 
282
        $fielddata->fields[] = $this->_form->createElement('select', $fieldname, get_string('role'),
283
            $roles,
284
            [
285
                'multiple' => false,
286
            ]
287
        );
288
        $fielddata->helps[$fieldname] = ['role', 'tool_dataprivacy'];
289
        $fielddata->defaults[$fieldname] = null;
290
 
291
        return $fielddata;
292
    }
293
 
294
    /**
295
     * Get the mform field for lawful bases.
296
     *
297
     * @param   string  $prefix The prefix to apply to the field
298
     * @return  \stdClass
299
     */
300
    protected function get_lawful_base_field(string $prefix = ''): \stdClass {
301
        $fieldname = "{$prefix}lawfulbases";
302
 
303
        $data = (object) [
304
            'fields' => [],
305
        ];
306
 
307
        $bases = [];
308
        foreach (\tool_dataprivacy\purpose::GDPR_ART_6_1_ITEMS as $article) {
309
            $key = 'gdpr_art_6_1_' . $article;
310
            $bases[$key] = get_string("{$key}_name", 'tool_dataprivacy');
311
        }
312
 
313
        $data->fields[] = $this->_form->createElement('autocomplete', $fieldname, get_string('lawfulbases', 'tool_dataprivacy'),
314
            $bases,
315
            [
316
                'multiple' => true,
317
            ]
318
        );
319
 
320
        $data->helps = [
321
            $fieldname => ['lawfulbases', 'tool_dataprivacy'],
322
        ];
323
 
324
        $data->advanceds = [
325
            $fieldname => true,
326
        ];
327
 
328
        return $data;
329
    }
330
 
331
    /**
332
     * Get the mform field for sensitive bases.
333
     *
334
     * @param   string  $prefix The prefix to apply to the field
335
     * @return  \stdClass
336
     */
337
    protected function get_sensitive_base_field(string $prefix = ''): \stdClass {
338
        $fieldname = "{$prefix}sensitivedatareasons";
339
 
340
        $data = (object) [
341
            'fields' => [],
342
        ];
343
 
344
        $bases = [];
345
        foreach (\tool_dataprivacy\purpose::GDPR_ART_9_2_ITEMS as $article) {
346
            $key = 'gdpr_art_9_2_' . $article;
347
            $bases[$key] = get_string("{$key}_name", 'tool_dataprivacy');
348
        }
349
 
350
        $data->fields[] = $this->_form->createElement(
351
            'autocomplete',
352
            $fieldname,
353
            get_string('sensitivedatareasons', 'tool_dataprivacy'),
354
            $bases,
355
            [
356
                'multiple' => true,
357
            ]
358
        );
359
        $data->helps = [
360
            $fieldname => ['sensitivedatareasons', 'tool_dataprivacy'],
361
        ];
362
 
363
        $data->advanceds = [
364
            $fieldname => true,
365
        ];
366
 
367
        return $data;
368
    }
369
 
370
    /**
371
     * Get the retention period fields.
372
     *
373
     * @param   string  $prefix The name of the main field, and prefix for the subfields.
374
     * @return  \stdClass
375
     */
376
    protected function get_retention_period_fields(string $prefix = ''): \stdClass {
377
        $prefix = "{$prefix}retentionperiod";
378
        $data = (object) [
379
            'fields' => [],
380
            'types' => [],
381
        ];
382
 
383
        $number = $this->_form->createElement('text', "{$prefix}number", null, ['size' => 8]);
384
        $data->types["{$prefix}number"] = PARAM_INT;
385
 
386
        $unitoptions = [
387
            'Y' => get_string('years'),
388
            'M' => strtolower(get_string('months')),
389
            'D' => strtolower(get_string('days'))
390
        ];
391
        $unit = $this->_form->createElement('select', "{$prefix}unit", '', $unitoptions);
392
 
393
        $data->fields[] = $this->_form->createElement(
394
                'group',
395
                $prefix,
396
                get_string('retentionperiod', 'tool_dataprivacy'),
397
                [
398
                    'number' => $number,
399
                    'unit' => $unit,
400
                ],
401
                null,
402
                false
403
            );
404
 
405
        return $data;
406
    }
407
 
408
    /**
409
     * Get the mform field for the protected flag.
410
     *
411
     * @param   string  $prefix The prefix to apply to the field
412
     * @return  \stdClass
413
     */
414
    protected function get_protected_field(string $prefix = ''): \stdClass {
415
        $fieldname = "{$prefix}protected";
416
 
417
        return (object) [
418
            'fields' => [
419
                $this->_form->createElement(
420
                        'advcheckbox',
421
                        $fieldname,
422
                        get_string('protected', 'tool_dataprivacy'),
423
                        get_string('protectedlabel', 'tool_dataprivacy')
424
                    ),
425
            ],
426
        ];
427
    }
428
 
429
    /**
430
     * Converts data to data suitable for storage.
431
     *
432
     * @param \stdClass $data
433
     * @return \stdClass
434
     */
435
    protected static function convert_fields(\stdClass $data) {
436
        $data = parent::convert_fields($data);
437
 
438
        if (!empty($data->lawfulbases) && is_array($data->lawfulbases)) {
439
            $data->lawfulbases = implode(',', $data->lawfulbases);
440
        }
441
        if (!empty($data->sensitivedatareasons) && is_array($data->sensitivedatareasons)) {
442
            $data->sensitivedatareasons = implode(',', $data->sensitivedatareasons);
443
        } else {
444
            // Nothing selected. Set default value of null.
445
            $data->sensitivedatareasons = null;
446
        }
447
 
448
        // A single value.
449
        $data->retentionperiod = 'P' . $data->retentionperiodnumber . $data->retentionperiodunit;
450
        unset($data->retentionperiodnumber);
451
        unset($data->retentionperiodunit);
452
 
453
        return $data;
454
    }
455
 
456
    /**
457
     * Get the default data.
458
     *
459
     * @return \stdClass
460
     */
461
    protected function get_default_data() {
462
        $data = parent::get_default_data();
463
 
464
        return $this->convert_existing_data_to_values($data);
465
    }
466
 
467
    /**
468
     * Normalise any values stored in existing data.
469
     *
470
     * @param   \stdClass $data
471
     * @return  \stdClass
472
     */
473
    protected function convert_existing_data_to_values(\stdClass $data): \stdClass {
474
        $data->lawfulbases = explode(',', $data->lawfulbases);
475
        if (!empty($data->sensitivedatareasons)) {
476
            $data->sensitivedatareasons = explode(',', $data->sensitivedatareasons);
477
        }
478
 
479
        // Convert the single properties into number and unit.
480
        $strlen = strlen($data->retentionperiod);
481
        $data->retentionperiodnumber = substr($data->retentionperiod, 1, $strlen - 2);
482
        $data->retentionperiodunit = substr($data->retentionperiod, $strlen - 1);
483
        unset($data->retentionperiod);
484
 
485
        return $data;
486
    }
487
 
488
    /**
489
     * Fetch the role override data from the list of submitted data.
490
     *
491
     * @param   \stdClass   $data The complete set of processed data
492
     * @return  \stdClass[] The list of overrides
493
     */
494
    public function get_role_overrides_from_data(\stdClass $data) {
495
        $overrides = [];
496
        if (!empty($data->overrides)) {
497
            $searchkey = 'roleoverride_';
498
 
499
            for ($i = 0; $i < $data->overrides; $i++) {
500
                $overridedata = (object) [];
501
                foreach ((array) $data as $fieldname => $value) {
502
                    if (strpos($fieldname, $searchkey) !== 0) {
503
                        continue;
504
                    }
505
 
506
                    $overridefieldname = substr($fieldname, strlen($searchkey));
507
                    $overridedata->$overridefieldname = $value[$i];
508
                }
509
 
510
                if (empty($overridedata->roleid) || empty($overridedata->retentionperiodnumber)) {
511
                    // Skip this one.
512
                    // There is no value and it will be delete.
513
                    continue;
514
                }
515
 
516
                $override = static::convert_fields($overridedata);
517
 
518
                $overrides[$i] = $override;
519
            }
520
        }
521
 
522
        return $overrides;
523
    }
524
 
525
    /**
526
     * Define extra validation mechanims.
527
     *
528
     * @param  stdClass $data Data to validate.
529
     * @param  array $files Array of files.
530
     * @param  array $errors Currently reported errors.
531
     * @return array of additional errors, or overridden errors.
532
     */
533
    protected function extra_validation($data, $files, array &$errors) {
534
        $overrides = $this->get_role_overrides_from_data($data);
535
 
536
        // Check role overrides to ensure that:
537
        // - roles are unique; and
538
        // - specifeid retention periods are numeric.
539
        $seenroleids = [];
540
        foreach ($overrides as $id => $override) {
541
            $override->purposeid = 0;
542
            $persistent = new \tool_dataprivacy\purpose_override($override->id, $override);
543
 
544
            if (isset($seenroleids[$persistent->get('roleid')])) {
545
                $errors["roleoverride_roleid[{$id}]"] = get_string('duplicaterole');
546
            }
547
            $seenroleids[$persistent->get('roleid')] = true;
548
 
549
            $errors = array_merge($errors, $persistent->get_errors());
550
        }
551
 
552
        return $errors;
553
    }
554
 
555
    /**
556
     * Load in existing data as form defaults. Usually new entry defaults are stored directly in
557
     * form definition (new entry form); this function is used to load in data where values
558
     * already exist and data is being edited (edit entry form).
559
     *
560
     * @param stdClass $data
561
     */
562
    public function set_data($data) {
563
        $purpose = $this->get_persistent();
564
 
565
        $count = 0;
566
        foreach ($this->existingoverrides as $override) {
567
            $overridedata = $this->convert_existing_data_to_values($override->to_record());
568
            foreach ($overridedata as $key => $value) {
569
                $keyname = "roleoverride_{$key}[{$count}]";
570
                $data->$keyname = $value;
571
            }
572
            $count++;
573
        }
574
 
575
        parent::set_data($data);
576
    }
577
}