Skip to content

Commit 1b946c5

Browse files
committed
[docs] Migrate Privacy Utils
1 parent 7a27f4a commit 1b946c5

File tree

3 files changed

+291
-2
lines changed

3 files changed

+291
-2
lines changed

data/migratedPages.yml

+3
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,9 @@ Plugin_types:
14011401
Privacy_API:
14021402
- filePath: "/docs/apis/subsystems/privacy/index.md"
14031403
slug: "/docs/apis/subsystems/privacy/"
1404+
Privacy_API/Utilities:
1405+
- filePath: "/docs/apis/subsystems/privacy/utils.md"
1406+
slug: "/docs/apis/subsystems/privacy/utils"
14041407
Process:
14051408
- filePath: "/general/development/process.md"
14061409
slug: "/general/development/process"

docs/apis/subsystems/privacy/index.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -974,11 +974,11 @@ class provider implements core_userlist_provider {
974974

975975
## Tips for development
976976

977-
- While implementing the privacy API into your plugin, there are CLI scripts that can help you to test things on the fly. Just don't forget these are not supposed to replace proper unit tests. See [Privacy API/Utilities](https://docs.moodle.org/dev/Privacy_API/Utilities) for details.
977+
- While implementing the privacy API into your plugin, there are CLI scripts that can help you to test things on the fly. Just don't forget these are not supposed to replace proper unit tests. See [Privacy API/Utilities](./utils.md) for details.
978978
- Inherit Unit tests from the `core_privacy\tests\provider_testcase</syntaxhighlight>, not <syntaxhighlight lang="php">advanced_testcase`. Advanced test case doesn't reset the Privacy content_writer between tests!
979979

980980
## See also
981981

982982
- [Subject Access Request FAQ](./faq.md)
983983
- [GDPR](https://docs.moodle.org/en/GDPR) in the user documentation
984-
- [Privacy API/Utilities](https://docs.moodle.org/dev/Privacy_API/Utilities) provides CLI scripts that are helpful during development
984+
- [Privacy API/Utilities](./utils.md) provides CLI scripts that are helpful during development

docs/apis/subsystems/privacy/utils.md

+286
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
---
2+
title: Utilities
3+
tags:
4+
- Privacy
5+
- GDPR
6+
---
7+
While implementing the privacy API into your plugin, there are CLI scripts that can help you to test things on the fly.
8+
9+
:::danger Important
10+
11+
These scripts are to assist during development. They are not intended to replace proper unit tests.
12+
13+
:::
14+
15+
Put these scripts into the root of your Moodle development installation and run them via command line. See [Privacy API](./index.md) for the full guide on implementing the API in your plugin.
16+
17+
## Test of privacy API compliance
18+
19+
```php
20+
<?php
21+
22+
// Set this if you want to run the script for one component only. Otherwise leave empty.
23+
$CHECK_COMPONENT = '';
24+
25+
define('CLI_SCRIPT', true);
26+
27+
require_once('config.php');
28+
29+
$user = \core_user::get_user(2);
30+
31+
\core\session\manager::init_empty_session();
32+
\core\session\manager::set_user($user);
33+
34+
$rc = new \ReflectionClass(\core_privacy\manager::class);
35+
$rcm = $rc->getMethod('get_component_list');
36+
$rcm->setAccessible(true);
37+
38+
$manager = new \core_privacy\manager();
39+
$components = $rcm->invoke($manager);
40+
41+
$list = (object) [
42+
'good' => [],
43+
'bad' => [],
44+
];
45+
46+
foreach ($components as $component) {
47+
if ($CHECK_COMPONENT && $component !== $CHECK_COMPONENT) {
48+
continue;
49+
}
50+
$compliant = $manager->component_is_compliant($component);
51+
if ($compliant) {
52+
$list->good[] = $component;
53+
} else {
54+
$list->bad[] = $component;
55+
}
56+
}
57+
58+
echo "The following plugins are not compliant:\n";
59+
echo "=> " . implode("\n=> ", array_values($list->bad)) . "\n";
60+
61+
echo "\n";
62+
echo "Testing the compliant plugins:\n";
63+
foreach ($list->good as $component) {
64+
$classname = \core_privacy\manager::get_provider_classname_for_component($component);
65+
echo "== {$component} ($classname) ==\n";
66+
if (check_implements($component, \core_privacy\local\metadata\null_provider::class)) {
67+
echo " Claims not to store any data with reason:\n";
68+
echo " '" . get_string($classname::get_reason(), $component) . "'\n";
69+
}
70+
else if (check_implements($component, \core_privacy\local\metadata\provider::class)) {
71+
$collection = new \core_privacy\local\metadata\collection($component);
72+
$classname::get_metadata($collection);
73+
$count = count($collection->get_collection());
74+
echo " Found {$count} items of metadata\n";
75+
if (empty($count)) {
76+
echo "!!! No metadata found!!! This an error.\n";
77+
}
78+
79+
if (check_implements($component, \core_privacy\local\request\user_preference_provider::class)) {
80+
$userprefdescribed = false;
81+
foreach ($collection->get_collection() as $item) {
82+
if ($item instanceof \core_privacy\local\metadata\types\user_preference) {
83+
$userprefdescribed = true;
84+
echo " ".$item->get_name()." : ".get_string($item->get_summary(), $component) . "\n";
85+
}
86+
}
87+
if (!$userprefdescribed) {
88+
echo "!!! User preference found, but was not described in metadata\n";
89+
}
90+
}
91+
92+
if (check_implements($component, \core_privacy\local\request\core_user_data_provider::class)) {
93+
// No need to check the return type - it's enforced by the interface.
94+
$contextlist = $classname::get_contexts_for_userid($user->id);
95+
$approvedcontextlist = new \core_privacy\local\request\approved_contextlist($user, $contextlist->get_component(), $contextlist->get_contextids());
96+
if (count($approvedcontextlist)) {
97+
$classname::export_user_data($approvedcontextlist);
98+
echo " Successfully ran a test export\n";
99+
} else {
100+
echo " Nothing to export.\n";
101+
}
102+
}
103+
if (check_implements($component, \core_privacy\local\request\shared_data_provider::class)) {
104+
echo " This is a shared data provider\n";
105+
}
106+
}
107+
}
108+
109+
echo "\n\n== Done ==\n";
110+
111+
function check_implements($component, $interface) {
112+
$manager = new \core_privacy\manager();
113+
$rc = new \ReflectionClass(\core_privacy\manager::class);
114+
$rcm = $rc->getMethod('component_implements');
115+
$rcm->setAccessible(true);
116+
117+
return $rcm->invoke($manager, $component, $interface);
118+
}
119+
```
120+
121+
## Test of exporting user data
122+
123+
```php
124+
<?php
125+
// This file is part of Moodle - http://moodle.org/
126+
//
127+
// Moodle is free software: you can redistribute it and/or modify
128+
// it under the terms of the GNU General Public License as published by
129+
// the Free Software Foundation, either version 3 of the License, or
130+
// (at your option) any later version.
131+
//
132+
// Moodle is distributed in the hope that it will be useful,
133+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
134+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
135+
// GNU General Public License for more details.
136+
//
137+
// You should have received a copy of the GNU General Public License
138+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
139+
140+
/**
141+
* Helper utility to perform a test export.
142+
*
143+
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
144+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
145+
*/
146+
147+
define('CLI_SCRIPT', true);
148+
require_once('config.php');
149+
require_once("$CFG->libdir/clilib.php");
150+
151+
list($options, $unrecognized) = cli_get_params(
152+
[
153+
'username' => '',
154+
'userid' => '',
155+
],
156+
[]
157+
);
158+
159+
$user = null;
160+
$username = $options['username'];
161+
$userid = $options['userid'];
162+
163+
if (!empty($options['username'])) {
164+
$user = \core_user::get_user_by_username($options['username']);
165+
} else if (!empty($options['userid'])) {
166+
$user = \core_user::get_user($options['userid']);
167+
}
168+
169+
while (empty($user)) {
170+
if (!empty($username)) {
171+
echo "Unable to find a user with username '{$username}'.\n";
172+
echo "Try again.\n";
173+
} else if (!empty($userid)) {
174+
echo "Unable to find a user with userid '{$userid}'.\n";
175+
echo "Try again.\n";
176+
}
177+
$username = readline("Username: ");
178+
$user = \core_user::get_user_by_username($username);
179+
}
180+
181+
echo "Processing export for " . fullname($user) . "\n";
182+
183+
\core\session\manager::init_empty_session();
184+
\core\session\manager::set_user($user);
185+
186+
$PAGE = new moodle_page();
187+
$OUTPUT = new core_renderer($PAGE, RENDERER_TARGET_GENERAL);
188+
189+
$manager = new \core_privacy\manager();
190+
191+
$approvedlist = new \core_privacy\local\request\contextlist_collection($user->id);
192+
193+
$contextlists = $manager->get_contexts_for_userid($user->id);
194+
foreach ($contextlists as $contextlist) {
195+
$approvedlist->add_contextlist(new \core_privacy\local\request\approved_contextlist(
196+
$user,
197+
$contextlist->get_component(),
198+
$contextlist->get_contextids()
199+
));
200+
}
201+
202+
$exportedcontent = $manager->export_user_data($approvedlist);
203+
$basedir = make_temp_directory('privacy');
204+
$exportpath = make_unique_writable_directory($basedir, true);
205+
$fp = get_file_packer();
206+
$fp->extract_to_pathname($exportedcontent, $exportpath);
207+
208+
echo "\n";
209+
echo "== File export was uncompressed to {$exportpath}\n";
210+
echo "============================================================================\n";
211+
```
212+
213+
## Test of deleting user data
214+
215+
```php
216+
<?php
217+
// This file is part of Moodle - http://moodle.org/
218+
//
219+
// Moodle is free software: you can redistribute it and/or modify
220+
// it under the terms of the GNU General Public License as published by
221+
// the Free Software Foundation, either version 3 of the License, or
222+
// (at your option) any later version.
223+
//
224+
// Moodle is distributed in the hope that it will be useful,
225+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
226+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
227+
// GNU General Public License for more details.
228+
//
229+
// You should have received a copy of the GNU General Public License
230+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
231+
232+
define('CLI_SCRIPT', true);
233+
require_once('config.php');
234+
require_once("$CFG->libdir/clilib.php");
235+
236+
list($options, $unrecognized) = cli_get_params(
237+
[
238+
'username' => '',
239+
'userid' => '',
240+
],
241+
[]
242+
);
243+
244+
$user = null;
245+
$username = $options['username'];
246+
$userid = $options['userid'];
247+
248+
if (!empty($options['username'])) {
249+
$user = \core_user::get_user_by_username($options['username']);
250+
} else if (!empty($options['userid'])) {
251+
$user = \core_user::get_user($options['userid']);
252+
}
253+
254+
while (empty($user)) {
255+
if (!empty($username)) {
256+
echo "Unable to find a user with username '{$username}'.\n";
257+
echo "Try again.\n";
258+
} else if (!empty($userid)) {
259+
echo "Unable to find a user with userid '{$userid}'.\n";
260+
echo "Try again.\n";
261+
}
262+
$username = readline("Username: ");
263+
$user = \core_user::get_user_by_username($username);
264+
}
265+
266+
echo "Processing delete for " . fullname($user) . "\n";
267+
268+
\core\session\manager::init_empty_session();
269+
\core\session\manager::set_user($user);
270+
271+
$manager = new \core_privacy\manager();
272+
273+
$approvedlist = new \core_privacy\local\request\contextlist_collection($user->id);
274+
275+
$trace = new text_progress_trace();
276+
$contextlists = $manager->get_contexts_for_userid($user->id, $trace);
277+
foreach ($contextlists as $contextlist) {
278+
$approvedlist->add_contextlist(new \core_privacy\local\request\approved_contextlist(
279+
$user,
280+
$contextlist->get_component(),
281+
$contextlist->get_contextids()
282+
));
283+
}
284+
285+
$manager->delete_data_for_user($approvedlist, $trace);
286+
```

0 commit comments

Comments
 (0)