MDL-62564 privacy: Improve bulk deletion
authorMihail Geshoski <mihail@moodle.com>
Thu, 8 Nov 2018 01:28:36 +0000 (09:28 +0800)
committerMihail Geshoski <mihail@moodle.com>
Thu, 8 Nov 2018 01:28:36 +0000 (09:28 +0800)
13 files changed:
admin/tool/dataprivacy/classes/api.php
admin/tool/dataprivacy/classes/data_request.php
admin/tool/dataprivacy/classes/event/user_deleted_observer.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/expired_user_contexts.php [new file with mode: 0644]
admin/tool/dataprivacy/classes/local/helper.php
admin/tool/dataprivacy/classes/output/data_requests_table.php
admin/tool/dataprivacy/classes/privacy/provider.php
admin/tool/dataprivacy/datarequests.php
admin/tool/dataprivacy/db/events.php [new file with mode: 0644]
admin/tool/dataprivacy/db/upgrade.php
admin/tool/dataprivacy/lang/en/tool_dataprivacy.php
admin/tool/dataprivacy/mydatarequests.php
admin/tool/dataprivacy/settings.php

index 10889a9..06f20b5 100644 (file)
@@ -41,6 +41,7 @@ use tool_dataprivacy\external\data_request_exporter;
 use tool_dataprivacy\local\helper;
 use tool_dataprivacy\task\initiate_data_request_task;
 use tool_dataprivacy\task\process_data_request_task;
+use tool_dataprivacy\data_request;
 
 defined('MOODLE_INTERNAL') || die();
 
@@ -253,6 +254,8 @@ class api {
         $datarequest->set('type', $type);
         // Set request comments.
         $datarequest->set('comments', $comments);
+        // Set the creation method.
+        $datarequest->set('creationmethod', $creationmethod);
 
         // Store subject access request.
         $datarequest->create();
@@ -275,6 +278,7 @@ class api {
      * @param int $userid The User ID.
      * @param int[] $statuses The status filters.
      * @param int[] $types The request type filters.
+     * @param int[] $creationmethods The request creation method filters.
      * @param string $sort The order by clause.
      * @param int $offset Amount of records to skip.
      * @param int $limit Amount of records to fetch.
@@ -282,7 +286,8 @@ class api {
      * @throws coding_exception
      * @throws dml_exception
      */
-    public static function get_data_requests($userid = 0, $statuses = [], $types = [], $sort = '', $offset = 0, $limit = 0) {
+    public static function get_data_requests($userid = 0, $statuses = [], $types = [], $creationmethods = [],
+                                             $sort = '', $offset = 0, $limit = 0) {
         global $DB, $USER;
         $results = [];
         $sqlparams = [];
@@ -306,6 +311,13 @@ class api {
             $sqlparams = array_merge($sqlparams, $typeparams);
         }
 
+        // Set request creation method filter.
+        if (!empty($creationmethods)) {
+            list($typeinsql, $typeparams) = $DB->get_in_or_equal($creationmethods, SQL_PARAMS_NAMED);
+            $sqlconditions[] = "creationmethod $typeinsql";
+            $sqlparams = array_merge($sqlparams, $typeparams);
+        }
+
         if ($userid) {
             // Get the data requests for the user or data requests made by the user.
             $sqlconditions[] = "(userid = :userid OR requestedby = :requestedby)";
@@ -348,7 +360,7 @@ class api {
 
             if (!empty($expiredrequests)) {
                 data_request::expire($expiredrequests);
-                $results = self::get_data_requests($userid, $statuses, $types, $sort, $offset, $limit);
+                $results = self::get_data_requests($userid, $statuses, $types, $creationmethods, $sort, $offset, $limit);
             }
         }
 
@@ -361,11 +373,12 @@ class api {
      * @param int $userid The User ID.
      * @param int[] $statuses The status filters.
      * @param int[] $types The request type filters.
+     * @param int[] $creationmethods The request creation method filters.
      * @return int
      * @throws coding_exception
      * @throws dml_exception
      */
-    public static function get_data_requests_count($userid = 0, $statuses = [], $types = []) {
+    public static function get_data_requests_count($userid = 0, $statuses = [], $types = [], $creationmethods = []) {
         global $DB, $USER;
         $count = 0;
         $sqlparams = [];
@@ -379,6 +392,11 @@ class api {
             $sqlconditions[] = "type $typeinsql";
             $sqlparams = array_merge($sqlparams, $typeparams);
         }
+        if (!empty($creationmethods)) {
+            list($typeinsql, $typeparams) = $DB->get_in_or_equal($creationmethods, SQL_PARAMS_NAMED);
+            $sqlconditions[] = "creationmethod $typeinsql";
+            $sqlparams = array_merge($sqlparams, $typeparams);
+        }
         if ($userid) {
             // Get the data requests for the user or data requests made by the user.
             $sqlconditions[] = "(userid = :userid OR requestedby = :requestedby)";
@@ -965,6 +983,34 @@ class api {
         return data_registry::get_effective_contextlevel_value($contextlevel, 'purpose', $forcedvalue);
     }
 
+    /**
+     * Creates an expired context record for the provided context id.
+     *
+     * @param int $contextid
+     * @return \tool_dataprivacy\expired_context
+     */
+    public static function create_expired_context($contextid) {
+        $record = (object)[
+            'contextid' => $contextid,
+            'status' => expired_context::STATUS_EXPIRED,
+        ];
+        $expiredctx = new expired_context(0, $record);
+        $expiredctx->save();
+
+        return $expiredctx;
+    }
+
+    /**
+     * Deletes an expired context record.
+     *
+     * @param int $id The tool_dataprivacy_ctxexpire id.
+     * @return bool True on success.
+     */
+    public static function delete_expired_context($id) {
+        $expiredcontext = new expired_context($id);
+        return $expiredcontext->delete();
+    }
+
     /**
      * Updates the status of an expired context.
      *
index 9976422..091921e 100644 (file)
@@ -21,7 +21,9 @@
  * @copyright  2018 Jun Pataleta
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
+
 namespace tool_dataprivacy;
+
 defined('MOODLE_INTERNAL') || die();
 
 use core\persistent;
diff --git a/admin/tool/dataprivacy/classes/event/user_deleted_observer.php b/admin/tool/dataprivacy/classes/event/user_deleted_observer.php
new file mode 100644 (file)
index 0000000..380236b
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Event observers supported by this module.
+ *
+ * @package    tool_dataprivacy
+ * @copyright   2018 Mihail Geshoski <mihail@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+namespace tool_dataprivacy\event;
+
+use \tool_dataprivacy\api;
+use \tool_dataprivacy\data_request;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Event observers supported by this module.
+ *
+ * @package    tool_dataprivacy
+ * @copyright   2018 Mihail Geshoski <mihail@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class user_deleted_observer {
+
+    /**
+     * Create user data deletion request when the user is deleted.
+     *
+     * @param \core\event\user_deleted $event
+     */
+    public static function create_delete_data_request(\core\event\user_deleted $event) {
+        // Automatic creation of deletion requests must be enabled.
+        if (get_config('tool_dataprivacy', 'automaticdeletionrequests')) {
+            $requesttypes = [api::DATAREQUEST_TYPE_DELETE];
+            $requeststatuses = [api::DATAREQUEST_STATUS_COMPLETE, api::DATAREQUEST_STATUS_DELETED];
+
+            $hasongoingdeleterequests = api::has_ongoing_request($event->objectid, $requesttypes[0]);
+            $hascompleteddeleterequest = (api::get_data_requests_count($event->objectid, $requeststatuses,
+                    $requesttypes) > 0) ? true : false;
+
+            if (!$hasongoingdeleterequests && !$hascompleteddeleterequest) {
+                api::create_data_request($event->objectid, $requesttypes[0],
+                        get_string('datarequestcreatedupondelete', 'tool_dataprivacy'),
+                        data_request::DATAREQUEST_CREATION_AUTO);
+            }
+        }
+    }
+}
diff --git a/admin/tool/dataprivacy/classes/expired_user_contexts.php b/admin/tool/dataprivacy/classes/expired_user_contexts.php
new file mode 100644 (file)
index 0000000..009e71d
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Expired contexts manager for CONTEXT_USER.
+ *
+ * @package    tool_dataprivacy
+ * @copyright  2018 David Monllao
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+namespace tool_dataprivacy;
+
+use core_privacy\manager;
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Expired contexts manager for CONTEXT_USER.
+ *
+ * @copyright  2018 David Monllao
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class expired_user_contexts extends \tool_dataprivacy\expired_contexts_manager {
+
+    /**
+     * Only user level.
+     *
+     * @return int[]
+     */
+    protected function get_context_levels() {
+        return [CONTEXT_USER];
+    }
+
+    /**
+     * Returns the user context instances that are expired.
+     *
+     * @return \stdClass[]
+     */
+    protected function get_expired_contexts() {
+        global $DB;
+
+        // Including context info + last login timestamp.
+        $fields = 'ctx.id AS id, ' . \context_helper::get_preload_record_columns_sql('ctx');
+
+        $purpose = api::get_effective_contextlevel_purpose(CONTEXT_USER);
+
+        // Calculate what is considered expired according to the context level effective purpose (= now + retention period).
+        $expiredtime = new \DateTime();
+        $retention = new \DateInterval($purpose->get('retentionperiod'));
+        $expiredtime->sub($retention);
+
+        $sql = "SELECT $fields FROM {context} ctx
+                  JOIN {user} u ON ctx.contextlevel = ? AND ctx.instanceid = u.id
+                  LEFT JOIN {tool_dataprivacy_ctxexpired} expiredctx ON ctx.id = expiredctx.contextid
+                 WHERE u.lastaccess <= ? AND u.lastaccess > 0 AND expiredctx.id IS NULL
+                ORDER BY ctx.path, ctx.contextlevel ASC";
+        $possiblyexpired = $DB->get_recordset_sql($sql, [CONTEXT_USER, $expiredtime->getTimestamp()]);
+
+        $expiredcontexts = [];
+        foreach ($possiblyexpired as $record) {
+
+            \context_helper::preload_from_record($record);
+
+            // No strict checking as the context may already be deleted (e.g. we just deleted a course,
+            // module contexts below it will not exist).
+            $context = \context::instance_by_id($record->id, false);
+            if (!$context) {
+                continue;
+            }
+
+            if (is_siteadmin($context->instanceid)) {
+                continue;
+            }
+
+            $courses = enrol_get_users_courses($context->instanceid, false, ['enddate']);
+            foreach ($courses as $course) {
+                if (!$course->enddate) {
+                    // We can not know it what is going on here, so we prefer to be conservative.
+                    continue 2;
+                }
+
+                if ($course->enddate >= time()) {
+                    // Future or ongoing course.
+                    continue 2;
+                }
+            }
+
+            $expiredcontexts[$context->id] = $context;
+        }
+
+        return $expiredcontexts;
+    }
+
+    /**
+     * Deletes user data from the provided context.
+     *
+     * Overwritten to delete the user.
+     *
+     * @param manager $privacymanager
+     * @param expired_context $expiredctx
+     * @return \context|false
+     */
+    protected function delete_expired_context(manager $privacymanager, expired_context $expiredctx) {
+        $context = \context::instance_by_id($expiredctx->get('contextid'), IGNORE_MISSING);
+        if (!$context) {
+            return false;
+        }
+
+        if (!PHPUNIT_TEST) {
+            mtrace('Deleting context ' . $context->id . ' - ' .
+                shorten_text($context->get_context_name(true, true)));
+        }
+
+        // To ensure that all user data is deleted, instead of deleting by context, we run through and collect any stray
+        // contexts for the user that may still exist and call delete_data_for_user().
+        $user = \core_user::get_user($context->instanceid, '*', MUST_EXIST);
+        $approvedlistcollection = new \core_privacy\local\request\contextlist_collection($user->id);
+        $contextlistcollection = $privacymanager->get_contexts_for_userid($user->id);
+
+        foreach ($contextlistcollection as $contextlist) {
+            $approvedlistcollection->add_contextlist(new \core_privacy\local\request\approved_contextlist(
+                $user,
+                $contextlist->get_component(),
+                $contextlist->get_contextids()
+            ));
+        }
+
+        $privacymanager->delete_data_for_user($approvedlistcollection);
+        api::set_expired_context_status($expiredctx, expired_context::STATUS_CLEANED);
+
+        // Delete the user.
+        delete_user($user);
+
+        return $context;
+    }
+}
index 2609ef1..25f5bf8 100644 (file)
@@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die();
 use coding_exception;
 use moodle_exception;
 use tool_dataprivacy\api;
+use tool_dataprivacy\data_request;
 
 /**
  * Class containing helper functions for the data privacy tool.
@@ -44,6 +45,9 @@ class helper {
     /** Filter constant associated with the request status filter. */
     const FILTER_STATUS = 2;
 
+    /** Filter constant associated with the request creation filter. */
+    const FILTER_CREATION = 3;
+
     /** The request filters preference key. */
     const PREF_REQUEST_FILTERS = 'tool_dataprivacy_request-filters';
 
@@ -145,6 +149,34 @@ class helper {
         ];
     }
 
+    /**
+     * Retrieves the human-readable value of a data request creation method.
+     *
+     * @param int $creation The request creation method.
+     * @return string
+     * @throws moodle_exception
+     */
+    public static function get_request_creation_method_string($creation) {
+        $creationmethods = self::get_request_creation_methods();
+        if (!isset($creationmethods[$creation])) {
+            throw new moodle_exception('errorinvalidrequestcreationmethod', 'tool_dataprivacy');
+        }
+
+        return $creationmethods[$creation];
+    }
+
+    /**
+     * Returns the key value-pairs of request creation method code and string value.
+     *
+     * @return array
+     */
+    public static function get_request_creation_methods() {
+        return [
+            data_request::DATAREQUEST_CREATION_MANUAL => get_string('creationmanual', 'tool_dataprivacy'),
+            data_request::DATAREQUEST_CREATION_AUTO => get_string('creationauto', 'tool_dataprivacy'),
+        ];
+    }
+
     /**
      * Get the users that a user can make data request for.
      *
@@ -199,6 +231,10 @@ class helper {
                 'name' => get_string('requeststatus', 'tool_dataprivacy'),
                 'options' => self::get_request_statuses()
             ],
+            self::FILTER_CREATION => (object)[
+                'name' => get_string('requestcreation', 'tool_dataprivacy'),
+                'options' => self::get_request_creation_methods()
+            ],
         ];
         $options = [];
         foreach ($filters as $category => $filtercategory) {
index b6fa957..01a4d45 100644 (file)
@@ -74,15 +74,17 @@ class data_requests_table extends table_sql {
      * @param int $userid The user ID
      * @param int[] $statuses
      * @param int[] $types
+     * @param int[] $creationmethods
      * @param bool $manage
      * @throws coding_exception
      */
-    public function __construct($userid = 0, $statuses = [], $types = [], $manage = false) {
+    public function __construct($userid = 0, $statuses = [], $types = [], $creationmethods = [], $manage = false) {
         parent::__construct('data-requests-table');
 
         $this->userid = $userid;
         $this->statuses = $statuses;
         $this->types = $types;
+        $this->creationmethods = $creationmethods;
         $this->manage = $manage;
 
         $checkboxattrs = [
@@ -273,11 +275,12 @@ class data_requests_table extends table_sql {
         $sort = $this->get_sql_sort();
 
         // Get data requests from the given conditions.
-        $datarequests = api::get_data_requests($this->userid, $this->statuses, $this->types, $sort,
-                $this->get_page_start(), $this->get_page_size());
+        $datarequests = api::get_data_requests($this->userid, $this->statuses, $this->types,
+                $this->creationmethods, $sort, $this->get_page_start(), $this->get_page_size());
 
         // Count data requests from the given conditions.
-        $total = api::get_data_requests_count($this->userid, $this->statuses, $this->types);
+        $total = api::get_data_requests_count($this->userid, $this->statuses, $this->types,
+                $this->creationmethods);
         $this->pagesize($pagesize, $total);
 
         $this->rawdata = [];
index 193bd70..d415002 100644 (file)
@@ -167,6 +167,8 @@ class provider implements
             $data->type = tool_helper::get_shortened_request_type_string($record->type);
             // Status.
             $data->status = tool_helper::get_request_status_string($record->status);
+            // Creation method.
+            $data->creationmethod = tool_helper::get_request_creation_method_string($record->creationmethod);
             // Comments.
             $data->comments = $record->comments;
             // The DPO's comment about this request.
@@ -234,6 +236,10 @@ class provider implements
                         $option->category = get_string('requeststatus', 'tool_dataprivacy');
                         $option->name = tool_helper::get_request_status_string($value);
                         break;
+                    case tool_helper::FILTER_CREATION:
+                        $option->category = get_string('requestcreation', 'tool_dataprivacy');
+                        $option->name = tool_helper::get_request_creation_method_string($value);
+                        break;
                 }
                 $descriptions[] = get_string('filteroption', 'tool_dataprivacy', $option);
             }
index 8b2b16f..ec6b9c2 100644 (file)
@@ -55,6 +55,7 @@ if (\tool_dataprivacy\api::is_site_dpo($USER->id)) {
 
     $types = [];
     $statuses = [];
+    $creationmethods = [];
     foreach ($filtersapplied as $filter) {
         list($category, $value) = explode(':', $filter);
         switch($category) {
@@ -64,10 +65,13 @@ if (\tool_dataprivacy\api::is_site_dpo($USER->id)) {
             case \tool_dataprivacy\local\helper::FILTER_STATUS:
                 $statuses[] = $value;
                 break;
+            case \tool_dataprivacy\local\helper::FILTER_CREATION:
+                $creationmethods[] = $value;
+                break;
         }
     }
 
-    $table = new \tool_dataprivacy\output\data_requests_table(0, $statuses, $types, true);
+    $table = new \tool_dataprivacy\output\data_requests_table(0, $statuses, $types, $creationmethods, true);
     if (!empty($perpage)) {
         set_user_preference(\tool_dataprivacy\local\helper::PREF_REQUEST_PERPAGE, $perpage);
     } else {
diff --git a/admin/tool/dataprivacy/db/events.php b/admin/tool/dataprivacy/db/events.php
new file mode 100644 (file)
index 0000000..2278b66
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file defines observers needed by the plugin.
+ *
+ * @package    tool_dataprivacy
+ * @copyright   2018 Mihail Geshoski <mihail@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$observers = [
+    [
+        'eventname'   => '\core\event\user_deleted',
+        'callback'    => '\tool_dataprivacy\event\user_deleted_observer::create_delete_data_request',
+    ],
+];
index 5449638..306337d 100644 (file)
@@ -252,10 +252,12 @@ function xmldb_tool_dataprivacy_upgrade($oldversion) {
         // Define field sensitivedatareasons to be added to tool_dataprivacy_purpose.
         $table = new xmldb_table('tool_dataprivacy_request');
         $field = new xmldb_field('creationmethod', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, null, 0, 'timemodified');
+
         // Conditionally launch add field sensitivedatareasons.
         if (!$dbman->field_exists($table, $field)) {
             $dbman->add_field($table, $field);
         }
+
         // Dataprivacy savepoint reached.
         upgrade_plugin_savepoint(true, 2018100406, 'tool', 'dataprivacy');
     }
index 84ca013..72c20c4 100644 (file)
@@ -32,6 +32,8 @@ $string['addnewdefaults'] = 'Add a new module default';
 $string['addpurpose'] = 'Add purpose';
 $string['approve'] = 'Approve';
 $string['approverequest'] = 'Approve request';
+$string['automaticdeletionrequests'] = 'Create automatic data deletion requests';
+$string['automaticdeletionrequests_desc'] = 'If enabled, automatic delete data request will be created upon user deletion or for each existing deleted user which data was not fully deleted.';
 $string['bulkapproverequests'] = 'Approve requests';
 $string['bulkdenyrequests'] = 'Deny requests';
 $string['cachedef_purpose'] = 'Data purposes';
@@ -68,6 +70,8 @@ $string['contactdpoviaprivacypolicy'] = 'Please contact the privacy officer as d
 $string['createcategory'] = 'Create data category';
 $string['createnewdatarequest'] = 'Create a new data request';
 $string['createpurpose'] = 'Create data purpose';
+$string['creationauto'] = 'Automatically';
+$string['creationmanual'] = 'Manually';
 $string['datadeletion'] = 'Data deletion';
 $string['datadeletionpagehelp'] = 'Data for which the retention period has expired are listed here. Please review and confirm data deletion, which will then be executed by the "Delete expired contexts" scheduled task.';
 $string['dataprivacy:makedatarequestsforchildren'] = 'Make data requests for minors';
@@ -82,6 +86,7 @@ $string['dataretentionsummary'] = 'Data retention summary';
 $string['datarequestcreatedforuser'] = 'Data request created for {$a}';
 $string['datarequestcreatedfromscheduledtask'] = 'Automatically created from a scheduled task (pre-existing deleted user).';
 $string['datarequestemailsubject'] = 'Data request: {$a}';
+$string['datarequestcreatedupondelete'] = 'Automatically created upon user deletion.';
 $string['datarequests'] = 'Data requests';
 $string['datecomment'] = '[{$a->date}]: ' . PHP_EOL . ' {$a->comment}';
 $string['daterequested'] = 'Date requested';
@@ -117,6 +122,7 @@ $string['editpurposes'] = 'Edit purposes';
 $string['effectiveretentionperiodcourse'] = '{$a} (after the course end date)';
 $string['effectiveretentionperioduser'] = '{$a} (since the last time the user accessed the site)';
 $string['emailsalutation'] = 'Dear {$a},';
+$string['errorinvalidrequestcreationmethod'] = 'Invalid request creation method!';
 $string['errorinvalidrequeststatus'] = 'Invalid request status!';
 $string['errorinvalidrequesttype'] = 'Invalid request type!';
 $string['errornocapabilitytorequestforothers'] = 'User {$a->requestedby} doesn\'t have the capability to make a data request on behalf of user {$a->userid}';
@@ -235,6 +241,7 @@ $string['requestby'] = 'Requested by';
 $string['requestbydetail'] = 'Requested by:';
 $string['requestcomments'] = 'Comments';
 $string['requestcomments_help'] = 'This box enables you to enter any further details about your data request.';
+$string['requestcreation'] = 'Creation';
 $string['requestdenied'] = 'The request has been denied';
 $string['requestemailintro'] = 'You have received a data request:';
 $string['requestfor'] = 'User';
index f435d00..0a4070a 100644 (file)
@@ -55,7 +55,7 @@ $PAGE->set_title($title);
 echo $OUTPUT->header();
 echo $OUTPUT->heading($title);
 
-$requests = tool_dataprivacy\api::get_data_requests($USER->id, [], [], 'timecreated DESC');
+$requests = tool_dataprivacy\api::get_data_requests($USER->id, [], [], [], 'timecreated DESC');
 $requestlist = new tool_dataprivacy\output\my_data_requests_page($requests);
 $requestlistoutput = $PAGE->get_renderer('tool_dataprivacy');
 echo $requestlistoutput->render($requestlist);
index a434384..de3d715 100644 (file)
@@ -34,6 +34,14 @@ if ($hassiteconfig) {
                 new lang_string('contactdataprotectionofficer_desc', 'tool_dataprivacy'), 0)
         );
 
+        // Automatically create delete data request for users upon user deletion.
+        // Automatically create delete data request for pre-existing deleted users.
+        // Enabled by default.
+        $privacysettings->add(new admin_setting_configcheckbox('tool_dataprivacy/automaticdeletionrequests',
+                new lang_string('automaticdeletionrequests', 'tool_dataprivacy'),
+                new lang_string('automaticdeletionrequests_desc', 'tool_dataprivacy'), 1)
+        );
+
         // Set days approved data requests will be accessible. 1 week default.
         $privacysettings->add(new admin_setting_configduration('tool_dataprivacy/privacyrequestexpiry',
                 new lang_string('privacyrequestexpiry', 'tool_dataprivacy'),