-
Notifications
You must be signed in to change notification settings - Fork 7
/
gdpr.module
462 lines (416 loc) · 16.8 KB
/
gdpr.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
<?php
/**
* @file
* Module file.
*/
use Drupal\Core\Link;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\gdpr\Form\ContentLinksForm;
/**
* Implements hook_help().
*/
function gdpr_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
// Main module help for the GDPR module.
case 'help.page.gdpr':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Provides help for making your drupal site GDPR-compliant.') . '</p>';
return $output;
default:
}
}
/**
* Implements hook_checklistapi_checklist_info().
*/
function gdpr_checklistapi_checklist_info() {
$definitions = [];
$definitions['gdpr_checklist'] = [
'#title' => t('GDPR Checklist'),
'#path' => 'admin/config/gdpr/checklist',
'#description' => t('GDPR Checklist'),
'#help' => t('<p>Complete this checklist to make your site GDPR-compliant.</p>'),
'#callback' => 'gdpr_checklistapi_checklist_items',
];
return $definitions;
}
/**
* Callback for gdpr_checklistapi_checklist_info().
*/
function gdpr_checklistapi_checklist_items() {
$langCode = \Drupal::languageManager()->getCurrentLanguage()->getId();
$contentLinks = \Drupal::config(ContentLinksForm::GDPR_CONTENT_CONF_KEY)
->get('links');
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
if (!empty($contentLinks[$langCode])) {
$linkLabels = ContentLinksForm::requiredContentList();
$linkOptions = [
'absolute' => TRUE,
'attributes' => [
'target' => '_blank',
'rel' => 'noopener noreferrer',
],
];
$description = [
'#theme' => 'item_list',
'#cache' => [
'tags' => [
'config:' . ContentLinksForm::GDPR_CONTENT_CONF_KEY,
],
],
];
foreach ($contentLinks[$langCode] as $key => $link) {
if (empty($link)) {
$configStatus = [
'#prefix' => '<span class="admin-missing">',
'#suffix' => '</span>',
'#markup' => t('not configured'),
];
$title = $linkLabels[$key];
}
else {
$configStatus = [
'#prefix' => '<span class="admin-enabled">',
'#suffix' => '</span>',
'#markup' => t('configured'),
];
$linkAsUrl = Url::fromUri($link, $linkOptions);
$title = Link::fromTextAndUrl($linkLabels[$key], $linkAsUrl)
->toString();
}
$description['#items'][] = t('@title (@status)', [
'@title' => $title,
'@status' => $renderer->renderPlain($configStatus),
]);
}
}
$contentConfigFormLink = Link::createFromRoute('Content link configuration', 'gdpr.content_links_form')
->toString();
if (empty($description['#items'])) {
$contentDiscoveryDescription = t('No nodes with the following terms have been found: "Privacy Policy", "Terms of Use", "About us" or "Impressum". Configure them here: @config_link', [
'@config_link' => $contentConfigFormLink,
]);
$privacyPolicyDescription = t('Please verify manually that such content of similar titles exists and has been published.');
}
else {
$contentDiscoveryDescription = $renderer->renderPlain($description);
$contentDiscoveryDescription = t('You can configure content links here: @config_link', [
'@config_link' => $contentConfigFormLink,
]) . $contentDiscoveryDescription;
if (!empty($contentLinks[$langCode]['privacy_policy'])) {
$privacyPolicyDescription = t('This one looks good to go, but you need to verify that it has real and relevant content.');
}
else {
$privacyPolicyDescription = t('We could not find a <em>configured</em> “Privacy Policy” page.');
}
}
$items = [
'getting_started' => [
'#title' => t('Getting Started'),
'#description' => t("<p>To begin your self-assessment process, it's highly recommended to take the following steps:</p>"),
'responsibility_agreement' => [
'#title' => t("Yes, I agree that by using the <em>GDPR</em> module, I'm still accountable for personal data handling performed on the site."),
'#description' => t('Responsibility Agreement: before the site owner starts the checklist process, they should acknowledge that installing and using this module pack does not mean sharing responsibility. Neither the Drupal Community nor module maintainers can guarantee full compliance with the GDPR regulations in case of a potential control.'),
],
'recommended_resources' => [
'#title' => t('Yes, I have read at least one of the following recommended resources from the list below'),
'wiki_page' => [
'#text' => t('GDPR Wikipedia page'),
'#url' => Url::fromUri((string) t('https://en.wikipedia.org/wiki/General_Data_Protection_Regulation')),
],
'summary' => [
'#text' => t('Summary of the GDPR regulation'),
'#url' => Url::fromUri((string) t('http://eur-lex.europa.eu/legal-content/EN/LSU/?uri=CELEX:32016R0679#document1')),
],
'legislation' => [
'#text' => t('The GDPR legislation'),
'#url' => Url::fromUri((string) t('http://eur-lex.europa.eu/eli/reg/2016/679/oj')),
],
],
],
'policies' => [
'#title' => t('Policies and other measures'),
'#description' => t('<p>Modules or libraries that already implement some of the needed features.</p>'),
'data1' => [
'#title' => t('Cookie policy'),
'#description' => t('User needs to be informed when your site uses cookies to collect data.'),
'handbook_page' => [
'#text' => t('EU Cookie Compliance-module'),
'#url' => Url::fromUri('https://www.drupal.org/project/eu_cookie_compliance'),
],
],
],
'content_related_suggestions' => [
'#title' => t('Content related suggestions'),
'#description' => t('<p>Automated search performed on content of site.</p>') . $contentDiscoveryDescription,
'privacy_policy_page' => [
'#title' => t("I have checked through the site's content and verified that a page containing <em>Privacy Policy</em> exists and has been published to this site."),
'#description' => $privacyPolicyDescription,
],
'privacy_policy_published' => [
'#title' => t('I confirm the existing "Privacy Policy" or similar page has been published.'),
'#description' => t('This <em>GDPR</em> module does not automatically affect publishing status of site content. Therefore, even if such information has been uploaded (regarding the previous point), then you must make sure that this content is available to visitors.'),
],
'privacy_policy_in_menu' => [
'#title' => t('I confirm the published "Privacy Policy" or similar page is included in at least one menu of the site.'),
'#description' => t("To ensure best practice, it's strongly recommended to divulge personal data handling guidelines on the site by including a link to the guidelines page in a generally displayed menu in clear view on the site layout."),
],
],
'site_features' => [
'#title' => t('Site feature related suggestions'),
'#description' => t('This group of checkpoints addresses various actions to be taken regarding site features.'),
'external_traffic_measurement' => [
'#title' => t("Yes, I'm aware there are external traffic measurement and user tracking services integrated on this site"),
'#description' => t('As the owner and/or maintainer of the site I have, to the best of my knowledge, taken the necessary measures to set up these 3rd-party data collecting tools to ensure maximum compliance with GDPR regulations.') . _get_module_list('tracking'),
],
'social_media_connections' => [
'#title' => t("Yes, I'm aware there are connections established between this site and some social media platforms"),
'#description' => t('As many social media platforms collects identifiable personal data, I acknowledge that the owner of this site is responsible for setting up these connections to ensure maximum compliance with GDPR regulations.') . _get_module_list('social'),
],
'module_data_collection' => [
'#title' => t('I confirm that all Drupal core, community, and custom modules have been revised as to not gather any unnecessary personal data of site visitors'),
'#description' => t('Owner and/or maintainer of the site has to ensure that the modules listed below have been revised as to gather only necessary (i.e. not needed for provision of service) personal data of site visitors.') . _get_module_list('collect'),
],
'user_role_permissions' => [
'#title' => t('I have examined all aspects of displayed personal data, associated with this site, to ensure that all users can access only permitted types of information according to their role'),
'#description' => t('Consider every use case in which Drupal fetches information from its database and exposes it to external parties. Bear in mind all possible scenarios extending beyond webpages, eg. RSS feeds, metadata and outgoing e-mail templates sent by the site.'),
],
],
'configuration' => [
'#title' => t('Configuration'),
'#description' => t('<p>Things you can can do by just making changes in your site configuration</p>'),
'cancel_account' => [
'#title' => t('Allow users to cancel their own user account'),
'permission' => [
'#text' => t('Permissions for "Cancel own user account"'),
'#url' => Url::fromUserInput('/admin/people/permissions#module-user'),
],
],
'delete_data' => [
'#title' => t('Users need to be able to request for removal of all their personal data.'),
'account_config' => [
'#text' => t('Account configuration for "Delete the account and make its content belong to the Anonymous user"'),
'#url' => Url::fromRoute('entity.user.admin_form'),
],
],
],
'beyond_website_management' => [
'#title' => t('Beyond website management'),
'#description' => t('The scope of GDPR is not clearly defined, stretching far beyond IT and legal aspects.'),
'legal_adviser_consulted' => [
'#title' => t('I have found and consulted with a legal adviser'),
'#description' => t('It is strongly suggested to have a complete overview of the internal data handling workflows within the organisation represented by the website. An experienced legal adviser can guide your organisation through this transformation process.'),
],
'gdpr_modules' => [
'#title' => t('I have enabled all features of the GDPR module'),
'#description' => _get_gdpr_module_description(),
],
'notice_upon_breach' => [
'#title' => t('I declare that I am able to notify the supervisory authority within 72 hours in the case of a personal data breach'),
'#description' => t('Referring to Article 33 of the regulation, site owner/maintainer as a data controller must have a contingency plan in place in the event of any personal data breach incident.'),
],
'logging_responsibility' => [
'#title' => t('I confirm that I have understood that the responsibility of personal data logging is dependent on the size of my organisation as described below:'),
'#description' => _get_logging_responsibility_description(),
],
],
];
return $items;
}
/**
* Returns a HTML markup for logging_responsibility checkpoint description.
*
* @return string
* HTML markup string
*/
function _get_logging_responsibility_description() {
$logging_items = [
'#theme' => 'item_list',
'#items' => [
t('Has fewer than 250 employees the processing it carries out is likely no result in a risk to the rights and freedoms of data subjects, the processing is only occasional and the processing does not includes special categories of data (Article 30/5) and as such my organisation is not required to create records of processing activities'),
t('My organisation has 250 or more employees and records of processing activities have been prepared according to Article 30 of the regulation'),
],
];
return \Drupal::service('renderer')->renderPlain($logging_items);
}
/**
* Returns a HTML markup for gdpr_modules checkpoint description.
*
* @return string
* HTML markup string
*/
function _get_gdpr_module_description() {
$all_modules = _get_modules();
$renderer = \Drupal::service('renderer');
$gdpr_module_names = ['gdpr_dump'];
$gdpr_modules_list = [
'#theme' => 'item_list',
'#items' => [],
];
$module_statuses = _get_module_statuses();
$color_classes = [
-1 => 'admin-missing',
0 => 'admin-missing',
1 => 'admin-enabled',
];
foreach ($gdpr_module_names as $module_name) {
if (isset($all_modules[$module_name])) {
if (!$all_modules[$module_name]['status']) {
$status_class = $color_classes[$all_modules[$module_name]['status']];
$status = [
'#prefix' => '<span class="' . $status_class . '">',
'#suffix' => '</span>',
'#markup' => $module_statuses[$all_modules[$module_name]['status']],
];
$gdpr_modules_list['#items'][] = t('@title (@status)', [
'@title' => $all_modules[$module_name]['name'],
'@status' => $renderer->renderPlain($status),
]);
}
}
else {
$status_class = $color_classes[-1];
$status = [
'#prefix' => '<span class="' . $status_class . '">',
'#suffix' => '</span>',
'#markup' => $module_statuses[-1],
];
$gdpr_modules_list['#items'][] = t('@title (@status)', [
'@title' => $module_name,
'@status' => $renderer->renderPlain($status),
]);
}
}
$gdpr_modules = $renderer->renderPlain($gdpr_modules_list);
$options = ['absolute' => TRUE];
$link = Link::createFromRoute(t('Enable them on Extend admin page'), 'system.modules_list', [], $options);
if (!empty($gdpr_modules_list['#items'])) {
$text = t("In order to have a complete overview of the site functionalities, it's recommended to enable the following features as well:");
return $text . $gdpr_modules . $link->toString();
}
else {
return '';
}
}
/**
* Returns an array of module statuses.
*
* @return array
* Module statuses array
*/
function _get_module_statuses() {
return [
-1 => t('missing'),
0 => t('disabled'),
1 => t('enabled'),
];
}
/**
* Returns a HTML markup for data_collecting modules checkpoint description.
*
* @param string $type
* Type of modules to search for.
*
* @return string
* HTML markup string
*/
function _get_module_list($type) {
$c_modules = _get_module_list_by_type($type);
$renderer = \Drupal::service('renderer');
$modules_list = [
'#theme' => 'item_list',
'#items' => [],
];
$module_statuses = _get_module_statuses();
$all_modules = _get_modules();
$color_classes = [
-1 => 'admin-enabled',
0 => 'admin-enabled',
1 => 'admin-missing',
];
foreach ($c_modules as $c_module) {
if (isset($all_modules[$c_module])) {
$status_class = $color_classes[$all_modules[$c_module]['status']];
$status = [
'#prefix' => '<span class="' . $status_class . '">',
'#suffix' => '</span>',
'#markup' => $module_statuses[$all_modules[$c_module]['status']],
];
$modules_list['#items'][] = t('@title (@status)', [
'@title' => $all_modules[$c_module]['name'],
'@status' => $renderer->renderPlain($status),
]);
}
}
return $renderer->renderPlain($modules_list);
}
/**
* Returns array of module machine_names by type.
*
* @param string $type
* The type of modules to search for.
*
* @return array
* Array of module machine_names
*/
function _get_module_list_by_type($type) {
$list = [];
switch ($type) {
case 'collect':
$list = [
'contact',
'webform',
'geoip',
];
break;
case 'tracking':
$list = [
'google_analytics',
'google_tag',
'piwik',
'hotjar',
'recaptcha',
'site_verify',
'adsense',
'yandex_metrics',
];
break;
case 'social':
$list = [
'oauth',
'sharethis',
'addtoany',
'cas',
'fb_likebox',
'easy_social',
];
break;
}
return $list;
}
/**
* Returns cached module data from module info files.
*
* @return array
* Array of module data
*/
function _get_modules() {
$cache = \Drupal::cache()->get('modules.list');
if (!empty($cache->data)) {
return $cache->data;
}
else {
$modules_list_raw = system_rebuild_module_data();
$modules_list = [];
foreach ($modules_list_raw as $module) {
$modules_list[$module->getName()] = [
'status' => $module->status,
'name' => $module->info['name'],
];
}
\Drupal::cache()->set('modules.list', $modules_list);
return $modules_list;
}
}