diff --git a/gdpr.info b/gdpr.info index 52f0108..d3adddf 100644 --- a/gdpr.info +++ b/gdpr.info @@ -2,6 +2,6 @@ name = General Data Protection Regulation (GDPR) description = Helps with making a site GDPR-compliant. core = 7.x dependencies[] = checklistapi -configure = admin/config/gdpr/checklist +configure = admin/gdpr/checklist package = General Data Protection Regulation files[] = gdpr.test diff --git a/gdpr.install b/gdpr.install index d5fc5e2..ed88117 100644 --- a/gdpr.install +++ b/gdpr.install @@ -9,7 +9,7 @@ * Implements hook_requirements(). */ function gdpr_requirements($phase) { - $requirements = []; + $requirements = array(); $t = get_t(); if ($phase === 'runtime' && $definitions = checklistapi_get_checklist_info()) { @@ -17,14 +17,14 @@ function gdpr_requirements($phase) { if (isset($definitions[$id]) && $checklist = checklistapi_checklist_load($id)) { $percent = round($checklist->getPercentComplete()); - $requirements['gdpr_status'] = [ + $requirements['gdpr_status'] = array( 'title' => $t('GDPR Preparation'), - 'value' => $t('Self assessment Checklist: @percent% done.', [ + 'value' => $t('Self assessment Checklist: @percent% done.', array( '@percent' => $percent, - '@url' => '/admin/config/gdpr/checklist', - ]), + '@url' => '/admin/gdpr/checklist', + )), 'severity' => REQUIREMENT_INFO, - ]; + ); } } diff --git a/gdpr.module b/gdpr.module index 10d0674..d6c6577 100644 --- a/gdpr.module +++ b/gdpr.module @@ -19,26 +19,23 @@ function gdpr_help($path, $arg) { * Implements hook_menu(). */ function gdpr_menu() { - // Administration pages. - $items['admin/config/gdpr'] = [ + $items['admin/gdpr'] = array( 'title' => 'GDPR', - 'position' => 'left', - 'page callback' => 'system_admin_menu_block_page', - 'access arguments' => [ - 'administer site configuration', - ], - 'file' => 'system.admin.inc', - 'file path' => drupal_get_path('module', 'system'), - ]; - - $items['user/%user/collected_data'] = [ + 'description' => 'Administer settings.', + 'page callback' => 'gdpr_dashboard_page', + 'access arguments' => array('administer gdpr settings'), + 'file' => 'includes/gdpr.admin.inc', + ); + + $items['user/%user/gdpr'] = array( 'title' => 'All your data', 'page callback' => 'gdpr_collected_user_data', - 'page arguments' => [1], + 'page arguments' => array(1), 'access callback' => 'gdpr_collected_user_data_access', - 'access arguments' => [1], + 'access arguments' => array(1), 'type' => MENU_LOCAL_TASK, - ]; + ); + return $items; } @@ -46,26 +43,40 @@ function gdpr_menu() { * Implements hook_theme(). */ function gdpr_theme() { - return [ - 'user_data_page' => [ + return array( + 'user_data_page' => array( 'template' => 'gdpr-user-data-page', 'path' => drupal_get_path('module', 'gdpr') . '/templates', - ], - ]; + ), + ); +} + +/** + * Implements hook_permission(). + */ +function gdpr_permission() { + $perms = array( + 'administer gdpr settings' => array( + 'title' => t('Administer GDPR settings'), + 'restrict access' => TRUE, + ), + ); + + return $perms; } /** * Implements hook_checklistapi_checklist_info(). */ function gdpr_checklistapi_checklist_info() { - $definitions = []; + $definitions = array(); $content = gdpr_search_content(); if (!empty($content)) { - $description = [ + $description = array( '#theme' => 'item_list', - ]; + ); foreach ($content as $node) { @@ -79,26 +90,26 @@ function gdpr_checklistapi_checklist_info() { } if ($node->status) { - $status = [ + $status = array( '#prefix' => '', '#suffix' => '', '#markup' => t('published'), - ]; + ); } else { - $status = [ + $status = array( '#prefix' => '', '#suffix' => '', '#markup' => t('not published'), - ]; + ); } - $options = [ + $options = array( 'absolute' => TRUE, - 'attributes' => [ + 'attributes' => array( 'target' => '_blank', - ], - ]; + ), + ); $mlids = db_select('menu_links', 'ml') ->fields('ml', ['mlid']) @@ -106,7 +117,7 @@ function gdpr_checklistapi_checklist_info() { ->execute() ->fetchCol(); - $menu_list = []; + $menu_list = array(); if (!empty($mlids)) { foreach ($mlids as $mlid) { $link = menu_link_load($mlid); @@ -114,13 +125,13 @@ function gdpr_checklistapi_checklist_info() { } } - $in_menu_text = !empty($menu_list) ? t('in menu: @menus', ['@menus' => implode(', ', $menu_list)]) : ''; + $in_menu_text = !empty($menu_list) ? t('in menu: @menus', array('@menus' => implode(', ', $menu_list))) : ''; - $description['#items'][] = t('!title (!status) @menu', [ + $description['#items'][] = t('!title (!status) @menu', array( '!title' => l($node->title, '/node/' . $node->nid, $options), '!status' => drupal_render($status), '@menu' => $in_menu_text, - ]); + )); } $content_discovery_description = drupal_render($description); @@ -129,128 +140,128 @@ function gdpr_checklistapi_checklist_info() { $content_discovery_description = t('No nodes with the following terms have been found: "Privacy Policy", "Terms of Use", "About us" or "Impressum".'); } - $definitions['gdpr_checklist'] = [ + $definitions['gdpr_checklist'] = array( '#title' => t('GDPR Checklist'), - '#path' => 'admin/config/gdpr/checklist', + '#path' => 'admin/gdpr/checklist', '#description' => t('GDPR Checklist'), '#help' => t('
Complete this checklist to make your site GDPR-compliant.
'), - 'getting_started' => [ + 'getting_started' => array( '#title' => t('Getting Started'), '#description' => t("To begin your self-assessment process, it's highly recommended to take the following steps:
"), - 'responsability_agreement' => [ + 'responsability_agreement' => array( '#title' => t("Yes, I agree that by using the GDPR 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' => [ + ), + 'recommended_resources' => array( '#title' => t('Yes, I have read at least one of the following recommended resources from the list below'), - 'wiki_page' => [ + 'wiki_page' => array( '#text' => t('GDPR Wikipedia page'), '#path' => t('https://en.wikipedia.org/wiki/General_Data_Protection_Regulation'), - ], - 'summary' => [ + ), + 'summary' => array( '#text' => t('Summary of the GDPR regulation'), '#path' => t('http://eur-lex.europa.eu/legal-content/EN/LSU/?uri=CELEX:32016R0679#document1'), - ], - 'legislation' => [ + ), + 'legislation' => array( '#text' => t('The GDPR legislation'), '#path' => t('http://eur-lex.europa.eu/eli/reg/2016/679/oj'), - ], - ], - ], - 'policies' => [ + ), + ), + ), + 'policies' => array( '#title' => t('Policies and other measures'), '#description' => t('Modules or libraries that already implement some of the needed features.
'), - 'data1' => [ + 'data1' => array( '#title' => t('Cookie policy'), '#description' => t('User needs to be informed when your site uses cookies to collect data.'), - 'handbook_page' => [ + 'handbook_page' => array( '#text' => t('EU Cookie Compliance-module'), '#path' => 'https://www.drupal.org/project/eu_cookie_compliance', - ], - ], - ], - 'content_related_suggestions' => [ + ), + ), + ), + 'content_related_suggestions' => array( '#title' => t('Content related suggestions'), '#description' => t('Automated search performed on uploaded content of site.
') . $content_discovery_description, - 'privacy_policy_page' => [ + 'privacy_policy_page' => array( '#title' => t('I have checked through the uploaded content and verified that a page containing Privacy Policy exists and has been published to this site.'), '#description' => t('No nodes with the following terms have been found: "Privacy Policy", "Terms of Use", "About us" or "Impressum". Please verify manually that such content of similar titles exists and has been published.'), - ], - 'privacy_policy_published' => [ + ), + 'privacy_policy_published' => array( '#title' => t('I confirm the existing "Privacy Policy" has been published.'), '#description' => t('This GDPR 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' => [ + ), + 'privacy_policy_in_menu' => array( '#title' => t('I confirm the published "Privacy Policy" 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' => [ + ), + ), + 'site_features' => array( '#title' => t('Site feature related suggestions'), '#description' => t('This group of checkpoints addresses various actions to be taken regarding site features.'), - 'external_traffic_measurement' => [ + 'external_traffic_measurement' => array( '#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' => [ + ), + 'social_media_connections' => array( '#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' => [ + ), + 'module_data_collection' => array( '#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' => [ + ), + 'user_role_permissions' => array( '#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' => [ + ), + ), + 'configuration' => array( '#title' => t('Configuration'), '#description' => t('Things you can can do by just making changes in your site configuration
'), - 'cancel_account' => [ + 'cancel_account' => array( '#title' => t('Allow users to cancel their own user account'), - 'permission' => [ + 'permission' => array( '#text' => t('Permissions for "Cancel own user account"'), '#path' => '/admin/people/permissions', - '#options' => [ + '#options' => array( 'fragment' => 'module-user', - ], - ], - ], - 'delete_data' => [ + ), + ), + ), + 'delete_data' => array( '#title' => t('Users need to be able to request for removal of all their personal data.'), - 'account_config' => [ + 'account_config' => array( '#text' => t('Account configuration for "Delete the account and make its content belong to the Anonymous user"'), '#path' => '/admin/config/people/accounts', - ], - ], - ], - 'beyond_website_management' => [ + ), + ), + ), + 'beyond_website_management' => array( '#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' => [ + 'legal_adviser_consulted' => array( '#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' => [ + ), + 'gdpr_modules' => array( '#title' => t('I have enabled all features of the GDPR module'), '#description' => _get_gdpr_module_description(), - ], - 'notice_upon_breach' => [ + ), + 'notice_upon_breach' => array( '#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_responsability' => [ + ), + 'logging_responsability' => array( '#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_responsability_description(), - ], - ], - ]; + ), + ), + ); return $definitions; } @@ -266,9 +277,9 @@ function gdpr_checklistapi_checklist_info() { function gdpr_collected_user_data(\stdClass $user) { drupal_set_title(t('Data stored about you')); $user_data = gdpr_get_user_data($user); - $output = theme('user_data_page', [ + $output = theme('user_data_page', array( 'user_data' => $user_data, - ]); + )); return $output; } @@ -340,13 +351,13 @@ function gdpr_star_value($value) { * HTML markup string */ function _get_logging_responsability_description() { - $logging_items = [ + $logging_items = array( '#theme' => 'item_list', - '#items' => [ + '#items' => array( 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 acitivities'), 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_render($logging_items); } @@ -360,55 +371,55 @@ function _get_logging_responsability_description() { function _get_gdpr_module_description() { $all_modules = _get_modules(); - $gdpr_module_names = ['gdpr_dump']; + $gdpr_module_names = array('gdpr_dump'); - $gdpr_modules_list = [ + $gdpr_modules_list = array( '#theme' => 'item_list', - '#items' => [], - ]; + '#items' => array(), + ); $module_statuses = _get_module_statuses(); - $color_classes = [ + $color_classes = array( -1 => 'admin-missing', 0 => 'admin-disabled', 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 = [ + $status = array( '#prefix' => '', '#suffix' => '', '#markup' => $module_statuses[$all_modules[$module_name]['status']], - ]; + ); - $gdpr_modules_list['#items'][] = t('@title (!status)', [ + $gdpr_modules_list['#items'][] = t('@title (!status)', array( '@title' => $all_modules[$module_name]['name'], '!status' => drupal_render($status), - ]); + )); } } else { $status_class = $color_classes[-1]; - $status = [ + $status = array( '#prefix' => '', '#suffix' => '', '#markup' => $module_statuses[-1], - ]; + ); - $gdpr_modules_list['#items'][] = t('@title (!status)', [ + $gdpr_modules_list['#items'][] = t('@title (!status)', array( '@title' => $module_name, '!status' => drupal_render($status), - ]); + )); } } $gdpr_modules = drupal_render($gdpr_modules_list); - $options = ['absolute' => TRUE]; + $options = array('absolute' => TRUE); $link = l(t('Enable them on Modules admin page'), '/admin/modules', $options); if (!empty($gdpr_modules_list['#items'])) { @@ -427,11 +438,11 @@ function _get_gdpr_module_description() { * Module statuses array */ function _get_module_statuses() { - return [ + return array( -1 => t('missing'), 0 => t('disabled'), 1 => t('enabled'), - ]; + ); } /** @@ -455,10 +466,12 @@ function gdpr_search_content() { if (!empty($results['node'])) { $nids = array_keys($results['node']); $nodes = node_load_multiple($nids); + return $nodes; + } + else { + return array(); } } - - return $nodes; } /** @@ -474,33 +487,33 @@ function _get_module_list($type) { $c_modules = _get_module_list_by_type($type); - $modules_list = [ + $modules_list = array( '#theme' => 'item_list', - '#items' => [], - ]; + '#items' => array(), + ); $module_statuses = _get_module_statuses(); $all_modules = _get_modules(); - $color_classes = [ + $color_classes = array( -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 = [ + $status = array( '#prefix' => '', '#suffix' => '', '#markup' => $module_statuses[$all_modules[$c_module]['status']], - ]; + ); - $modules_list['#items'][] = t('@title (!status)', [ + $modules_list['#items'][] = t('@title (!status)', array( '@title' => $all_modules[$c_module]['name'], '!status' => drupal_render($status), - ]); + )); } } @@ -517,18 +530,18 @@ function _get_module_list($type) { * Array of module machine_names */ function _get_module_list_by_type($type) { - $list = []; + $list = array(); switch ($type) { case 'collect': - $list = [ + $list = array( 'contact', 'webform', 'geoip', - ]; + ); break; case 'tracking': - $list = [ + $list = array( 'google_analytics', 'google_tag', 'piwik', @@ -537,18 +550,18 @@ function _get_module_list_by_type($type) { 'site_verify', 'adsense', 'yandex_metrics', - ]; + ); break; case 'social': - $list = [ + $list = array( 'oauth', 'sharethis', 'addtoany', 'cas', 'fb_likebox', 'easy_social', - ]; + ); break; } return $list; @@ -568,13 +581,13 @@ function _get_modules() { } else { $modules_list_raw = system_rebuild_module_data(); - $modules_list = []; + $modules_list = array(); foreach ($modules_list_raw as $module) { - $modules_list[$module->name] = [ + $modules_list[$module->name] = array( 'status' => $module->status, 'name' => $module->info['name'], - ]; + ); } cache_set('modules_list', $modules_list); return $modules_list; diff --git a/gdpr.test b/gdpr.test index 8a6e33b..609d590 100644 --- a/gdpr.test +++ b/gdpr.test @@ -48,7 +48,7 @@ class GdprUserCollectedDataPageTest extends DrupalWebTestCase { * Tests that anonymous users do not have access to these pages. */ public function testAnonymousUserShouldNotHaveAccess() { - $this->drupalGet(sprintf('user/%d/collected_data', $this->subject->uid)); + $this->drupalGet(sprintf('user/%d/gdpr', $this->subject->uid)); $this->assertResponse(403); } @@ -57,7 +57,7 @@ class GdprUserCollectedDataPageTest extends DrupalWebTestCase { */ public function testUsersShouldHaveAccessToOwnPage() { $this->drupalLogin($this->subject); - $this->drupalGet(sprintf('user/%d/collected_data', $this->subject->uid)); + $this->drupalGet(sprintf('user/%d/gdpr', $this->subject->uid)); $this->assertResponse(200); } @@ -66,7 +66,7 @@ class GdprUserCollectedDataPageTest extends DrupalWebTestCase { */ public function testUsersShouldNotHaveAccessToOtherPages() { $this->drupalLogin($this->observer); - $this->drupalGet(sprintf('user/%d/collected_data', $this->subject->uid)); + $this->drupalGet(sprintf('user/%d/gdpr', $this->subject->uid)); $this->assertResponse(403); } diff --git a/includes/gdpr.admin.inc b/includes/gdpr.admin.inc new file mode 100644 index 0000000..98b34a6 --- /dev/null +++ b/includes/gdpr.admin.inc @@ -0,0 +1,50 @@ +fetchAssoc()) { + $result = db_query(" + SELECT m.*, ml.* + FROM {menu_links} ml + INNER JOIN {menu_router} m ON ml.router_path = m.path + WHERE menu_name = :menu_name AND ml.mlid = :mlid AND hidden = 0", $admin, array('fetch' => PDO::FETCH_ASSOC)); + + foreach ($result as $item) { + _menu_link_translate($item); + if (!$item['access']) { + continue; + } + // The link description, either derived from 'description' in hook_menu() + // or customized via menu module is used as title attribute. + if (!empty($item['localized_options']['attributes']['title'])) { + $item['description'] = $item['localized_options']['attributes']['title']; + unset($item['localized_options']['attributes']['title']); + } + $block = $item; + $block['content'] = ''; + $block['content'] .= theme('admin_block_content', array('content' => system_admin_menu_block($item))); + if (!empty($block['content'])) { + $block['show'] = TRUE; + } + + // Prepare for sorting as in function _menu_tree_check_access(). + // The weight is offset so it is always positive, with a uniform 5-digits. + $blocks[(50000 + $item['weight']) . ' ' . $item['title'] . ' ' . $item['mlid']] = $block; + } + } + if ($blocks) { + ksort($blocks); + return theme('admin_page', array('blocks' => $blocks)); + } + else { + return t('You do not have any pages yet.'); + } +} diff --git a/modules/gdpr_consent/css/gdpr_consent.css b/modules/gdpr_consent/css/gdpr_consent.css new file mode 100644 index 0000000..0744974 --- /dev/null +++ b/modules/gdpr_consent/css/gdpr_consent.css @@ -0,0 +1,3 @@ +.gdpr_agreed_toggle { + padding-left: 5px; +} diff --git a/modules/gdpr_consent/gdpr_consent.info b/modules/gdpr_consent/gdpr_consent.info new file mode 100644 index 0000000..6f6edcb --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.info @@ -0,0 +1,5 @@ +name = General Data Protection Regulation (GDPR) - Consent Tracking +description = Allow tracking of GDPR Consent +core = 7.x +dependencies[] = entity +dependencies[] = gdpr diff --git a/modules/gdpr_consent/gdpr_consent.install b/modules/gdpr_consent/gdpr_consent.install new file mode 100644 index 0000000..dff7a84 --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.install @@ -0,0 +1,217 @@ + 'Base table for GDPR Consent Agreement entity.', + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'description' => 'Primary key of the GDPR Consent Agreement entity.', + ), + 'name' => array( + 'description' => 'The machine-readable name of this consent agreement.', + 'type' => 'varchar', + 'length' => 32, + 'not null' => TRUE, + ), + 'revision_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => 'The ID of consent agreement\'s default revision.', + ), + 'title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Title of the consent agreement.', + ), + 'created' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The Unix timestamp of the entity creation time.', + ), + 'changed' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'The Unix timestamp the entity was last edited.', + ), + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => "The {users}.uid of the associated user.", + ), + 'status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + 'description' => 'Entity status.', + ), + 'module' => array( + 'description' => 'The name of the providing module if the entity has been defined in code.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => FALSE, + ), + ), + 'foreign keys' => array( + 'uid' => array( + 'table' => 'users', + 'columns' => array('uid' => 'uid'), + ), + ), + 'primary key' => array('id'), + 'unique key' => array('name'), + 'indexes' => array( + 'name' => array('name'), + ), + ); + + $schema['gdpr_consent_agreement_revision'] = array( + 'description' => 'GDPR Consent Agreement entity revisions.', + 'fields' => array( + 'id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => 'The ID of the attached entity.', + ), + 'revision_id' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'description' => 'Primary Key: Unique revision ID.', + ), + 'title' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'Title of the consent agreement.', + ), + 'timestamp' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'description' => 'A Unix timestamp indicating when this version was created.', + ), + 'uid' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + 'description' => "The {users}.uid of the associated user.", + ), + 'description' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + 'description' => 'A description of the consent agreement.', + ), + 'long_description' => array( + 'type' => 'text', + 'size' => 'medium', + 'not null' => TRUE, + 'description' => 'A long description of the consent agreement.', + ), + 'agreement_type' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + 'description' => 'Consent agreement\'s type: implicit or explicit.', + ), + 'notes' => array( + 'type' => 'text', + 'size' => 'medium', + 'not null' => FALSE, + 'description' => 'Notes for staff to put their rationale for why they have done this for auditors.', + ), + 'log' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + 'description' => 'The log entry explaining the changes in this version.', + ), + ), + 'primary key' => array('revision_id'), + ); + + return $schema; +} + +/** + * Implements hook_field_schema(). + */ +function gdpr_consent_field_schema($field) { + $columns = array( + 'target_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'target_revision_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'agreed' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + 'size' => 'tiny', + ), + 'date' => array( + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'user_id' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + ), + 'user_id_accepted' => array( + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => NULL, + ), + 'notes' => array( + 'type' => 'text', + 'size' => 'medium', + 'not null' => FALSE, + ), + ); + $indexes = array( + 'target_id' => array('target_id'), + 'target_revision_id' => array('target_revision_id'), + ); + return array( + 'columns' => $columns, + 'indexes' => $indexes, + ); +} diff --git a/modules/gdpr_consent/gdpr_consent.module b/modules/gdpr_consent/gdpr_consent.module new file mode 100644 index 0000000..40f6e1a --- /dev/null +++ b/modules/gdpr_consent/gdpr_consent.module @@ -0,0 +1,611 @@ + 'Agreements', + 'description' => 'List Agreement Entities', + 'page callback' => 'gdpr_consent_collected_agreements', + 'page arguments' => array(1), + 'access arguments' => array('manage gdpr agreements'), + 'menu_name' => 'navigation', + 'file' => 'includes/gdpr_consent.agreements.inc', + ); + $items['admin/gdpr/agreements/%gdpr_consent_agreement'] = array( + 'title' => 'Consent Agreement', + 'page callback' => 'gdpr_consent_agreement_view_entity', + //'page callback' => 'entity_ui_entity_page_view', + 'page arguments' => array(3), + 'access callback' => TRUE, + 'file' => 'includes/gdpr_consent.agreements.inc', + ); + // 'View' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/view'] = array( + 'title' => 'View', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + // 'Edit' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/edit'] = array( + 'title' => 'Edit', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('gdpr_consent_agreement_form', 3), + 'access arguments' => array('manage gdpr agreements'), + 'type' => MENU_LOCAL_TASK, + 'weight' => -5, + 'file' => 'includes/gdpr_consent.admin.inc', + ); + // 'Revisions' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/revisions'] = array( + 'title' => 'Revisions', + 'page callback' => 'gdpr_consent_agreement_revision_overview', + 'page arguments' => array(3), + 'type' => MENU_LOCAL_TASK, + 'access arguments' => array('manage gdpr agreements'), + 'file' => 'includes/gdpr_consent.admin.inc', + 'weight' => -3, + ); + // An individual revision view page. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/revisions/%/view'] = array( + 'title' => 'Revision', + 'page callback' => 'gdpr_consent_agreement_view_revision', + 'page arguments' => array(5), + 'access arguments' => array('manage gdpr agreements'), + 'file' => 'includes/gdpr_consent.agreements.inc', + 'weight' => -3, + ); + // 'Delete' tab for an individual entity. + $items['admin/gdpr/agreements/%gdpr_consent_agreement/delete'] = array( + 'title' => 'Delete', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('gdpr_consent_agreement_form', 3), + 'access arguments' => array('manage gdpr agreements'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'includes/gdpr_consent.admin.inc', + 'weight' => 0, + ); + + return $items; +} + +/** + * Implements hook_permission(). + */ +function gdpr_consent_permission() { + $permissions = array( + 'manage gdpr agreements' => array( + 'title' => t('Manage GDPR Agreements'), + ), + 'grant any consent' => array( + 'title' => t('Grant Any Consent'), + ), + 'grant own consent' => array( + 'title' => t('Grant Own Consent'), + ), + ); + + return $permissions; +} + +/** + * Implements hook_entity_info(). + */ +function gdpr_consent_entity_info() { + $info = array(); + $info['gdpr_consent_agreement'] = array( + 'label' => t('GDPR Consent Agreement'), + 'base table' => 'gdpr_consent_agreement', + 'revision table' => 'gdpr_consent_agreement_revision', + 'entity class' => 'ConsentAgreement', + 'controller class' => 'ConsentAgreementController', + 'fieldable' => TRUE, + 'exportable' => TRUE, + 'bundles' => array( + 'gdpr_consent_agreement' => array( + 'label' => t('GDPR Consent Agreement'), + 'admin' => array( + 'path' => 'admin/gdpr/agreements', + 'access arguments' => array('administer site configuration'), + ), + ), + ), + 'entity keys' => array( + 'id' => 'id', + 'name' => 'name', + 'label' => 'title', + 'revision' => 'revision_id', + ), + 'uri callback' => 'entity_class_uri', + 'access callback' => 'gdpr_consent_access_callback', + 'admin ui' => array( + 'path' => 'admin/gdpr/agreements', + 'controller class' => 'ConsentAgreementEntityUIController', + 'menu wildcard' => '%gdpr_consent_agreement', + 'file' => 'includes/gdpr_consent.admin.inc', + ), + 'module' => 'gdpr_consent', + ); + + return $info; +} + +/** + * Implements hook_entity_property_info(). + */ +function gdpr_consent_entity_property_info() { + $info = array(); + $properties = &$info['gdpr_consent_agreement']['properties']; + + $properties['id'] = array( + 'label' => t('GDPR Consent Agreement ID'), + 'description' => t('The uniquie ID of the consent agreement entity.'), + 'type' => 'integer', + 'schema field' => 'id', + ); + + $properties['title'] = array( + 'label' => t('Title'), + 'description' => t('Title of the agreement'), + 'type' => 'text', + 'schema field' => 'title', + ); + + $properties['agreement_type'] = array( + 'label' => t('Agreement Type'), + 'description' => t('Whether consent is implicit or explicit. Set to "Explicit" if the user needs to explicitly agree, otherwise "Implicit'), + 'type' => 'boolean', + 'schema field' => 'agreement_type', + ); + + $properties['description'] = array( + 'label' => t('Description'), + 'description' => t('Text displayed to the user on the form'), + 'type' => 'text', + 'schema field' => 'description', + ); + + $properties['long_description'] = array( + 'label' => t('Long Description'), + 'description' => t('Text shown when the user clicks for more details'), + 'type' => 'text', + 'schema field' => 'long_description', + ); + + $properties['notes'] = array( + 'label' => t('Notes'), + 'description' => t('This should contain the rationale behind the agreement.'), + 'type' => 'text', + 'schema field' => 'notes', + ); + + $properties['created'] = array( + 'label' => t('Created date'), + 'description' => t('Date the consent agreement was created'), + 'type' => 'date', + 'schema field' => 'created', + ); + + $properties['changed'] = array( + 'label' => t('Updated date'), + 'description' => t('Date the consent agreement was last edited'), + 'type' => 'date', + 'schema field' => 'changed', + ); + + $properties['uid'] = array( + 'label' => t('Authored by'), + 'description' => t('The user ID of author of the Consent Agreement entity'), + 'type' => 'user', + 'schema field' => 'uid', + ); + + $properties['status'] = array( + 'label' => t('Publishing status'), + 'description' => t('A boolean indicating whether the Consent Agreement is published.'), + 'type' => 'boolean', + 'schema field' => 'status', + ); + + return $info; +} + +/** + * Access callback for agreements. + */ +function gdpr_consent_access_callback($op, $entity = NULL, $account = NULL) { + return user_access('manage gdpr agreements'); +} + +/** + * Menu autoloader for gdpr agreements. + */ +function gdpr_consent_agreement_load($id, $revision_id = NULL) { + if (is_numeric($revision_id)) { + $entity = entity_revision_load('gdpr_consent_agreement', $revision_id); + return $entity; + } + else { + $entity = entity_load_single('gdpr_consent_agreement', $id); + } + return $entity; +} + +/** + * Loads multiple agreements. + */ +function gdpr_consent_agreement_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) { + if (empty($ids)) { + $ids = FALSE; + } + + return entity_load('gdpr_consent_agreement', $ids, $conditions, $reset); +} + +/** + * Gets an array of all agreements, keyed by the machine name. + */ +function gdpr_consent_agreement_load_multiple_by_name($name = NULL) { + $signups = entity_load_multiple_by_name('gdpr_consent_agreement', isset($name) ? array($name) : FALSE); + return isset($name) ? reset($signups) : $signups; +} + +/** + * Implements hook_field_info(). + * + * Provides a user consent field type. + */ +function gdpr_consent_field_info() { + return array( + 'gdpr_user_consent' => array( + 'label' => t('GDPR Consent'), + 'description' => t('Stores user consent for a particular agreement'), + 'default_widget' => 'gdpr_consent_widget', + 'default_formatter' => 'gdpr_consent_formatter', + ), + ); +} + +/** + * Implements hook_field_validate(). + */ +function gdpr_consent_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { + $valid = TRUE; + + foreach ($items as $delta => $item) { + if (!empty($item['target_id'])) { + if (!$valid) { + $errors[$field['field_name']][$langcode][$delta][] = array( + 'error' => 'gdpr_user_consent_invalid', + 'message' => t('Referenced consent agreement entity is invalid.'), + ); + } + } + } +} + +/** + * Implements hook_field_insert(). + */ +function gdpr_consent_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { + $message = message_create('consent_agreement_accepted', array('uid' => $entity->uid)); + $wrapper = entity_metadata_wrapper('message', $message); + + $wrapper->user->set($field['user_id_accepted']); + $wrapper->agreement->set(array($field['target_id'], $field['target_revision_id'])); + $wrapper->notes->set($field['notes']); + $wrapper->agreed->set($field['agreed']); + + $wrapper->save(); +} + +/** + * Implements hook_field_is_empty(). + */ +function gdpr_consent_field_is_empty($item, $field) { + return empty($item['target_id']); +} + +/** + * Implements hook_field_formatter_info(). + */ +function gdpr_consent_field_formatter_info() { + return array( + 'gdpr_consent' => array( + 'label' => t('GDPR user consent formatter'), + 'field types' => array('gdpr_user_consent'), + ), + ); +} + +/** + * Implements hook_field_settings_form(). + */ +function gdpr_consent_field_settings_form($field, $instance, $has_data) { + $settings = $field['settings']; + + $agreements = array(); + + foreach (entity_load('gdpr_consent_agreement') as $agreement) { + $agreements[$agreement->id] = $agreement->title; + } + + $form['target_id'] = array( + '#type' => 'select', + '#title' => t('User consent agreement'), + '#default_value' => (isset($settings['target_id'])) ? $settings['target_id'] : '', + '#required' => FALSE, + '#options' => $agreements, + '#element_validate' => array( + 'element_validate_integer_positive', + ), + '#description' => t('The GDPR User Consent Agreement to display'), + ); + + return $form; +} + +/** + * Implements hook_field_formatter_view(). + */ +function gdpr_consent_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { + $element = array(); + + switch ($display['type']) { + case 'gdpr_consent': + foreach ($items as $delta => $item) { + $element[$delta] = array( + '#type' => 'html_tag', + '#tag' => 'p', + '#value' => t('User Consent ID: @entity', array('@entity' => $item['target_id'])), + ); + } + break; + } + + return $element; +} + +/** + * Implements hook_field_widget_info(). + * + * Field widget to show consent information. + */ +function gdpr_consent_field_widget_info() { + return array( + 'gdpr_consent_widget' => array( + 'label' => t('GDPR Consent'), + 'field types' => array('gdpr_user_consent'), + ), + ); +} + +/** + * Implements hook_field_widget_form(). + */ +function gdpr_consent_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { + global $user; + + $agreed = isset($items[$delta]['agreed']) ? $items[$delta]['agreed'] : ''; + $notes = isset($items[$delta]['notes']) ? $items[$delta]['notes'] : ''; + + $widget = $element; + $widget['#delta'] = $delta; + + // Get current revision of the referenced agreement entity. + $entity_type = 'gdpr_consent_agreement'; + $entity = entity_load_single('gdpr_consent_agreement', $field['settings']['target_id']); + + list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); + + switch ($instance['widget']['type']) { + + case 'gdpr_consent_widget': + $element['agreed'] = array( + '#type' => 'checkbox', + '#title' => $entity->title, + '#default_value' => $agreed, + '#weight' => 0, + '#attributes' => array( + 'class' => array('gdpr_consent_agreement'), + ), + '#attached' => array( + 'css' => array(drupal_get_path('module', 'gdpr_consent') . '/css/gdpr_consent.css'), + 'js' => array(drupal_get_path('module', 'gdpr_consent') . '/js/gdpr_consent.js'), + ), + '#description' => $entity->long_description, + ) + $widget; + $element['notes'] = array( + '#type' => 'textfield', + '#title' => 'Consent Notes', + '#default_value' => $notes, + '#weight' => 10, + '#description' => '', + '#access' => user_access('grant any consent', $user), + ) + $widget; + $element['target_id'] = array( + '#type' => 'hidden', + '#value' => $field['settings']['target_id'], + ) + $widget; + $element['target_revision_id'] = array( + '#type' => 'hidden', + '#value' => $vid, + ) + $widget; + $element['date'] = array( + '#type' => 'hidden', + '#value' => REQUEST_TIME, + ) + $widget; + $element['user_id_accepted'] = array( + '#type' => 'hidden', + '#value' => $user->uid, + ) + $widget; + + break; + } + + return $element; +} + +/** + * Implements hook_default_message_type(). + */ +function gdpr_consent_default_message_type() { + $items = array(); + + $items['consent_agreement_accepted'] = entity_import('message_type', '{ + "name" : "consent_agreement_accepted", + "description" : "GDPR Consent Agreement", + "argument_keys" : [], + "argument" : [], + "category" : "message_type", + "data" : { + "token options" : { "clear" : 0 }, + "purge" : { "override" : 1, "enabled" : 1, "quota" : "1000", "days" : "30" } + }, + "language" : "", + "arguments" : null, + "message_text" : { "und" : [ + { + "value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022[message:agreement:entity:url]\/revisions\/[message:agreement:target_revision_id]\/view\u0022\u003E[message:agreement:entity:title]\u003C\/a\u003E\u003Cbr \/\u003E\r\nAgreed: [message:agreed]\u003Cbr \/\u003E\r\nNotes: [message:notes]\u003C\/p\u003E", + "format" : "filtered_html", + "safe_value" : "\u003Cp\u003EAgreement: \u003Ca href=\u0022url]\/revisions\/[message:agreement:target_revision_id]\/view\u0022\u003E[message:agreement:entity:title]\u003C\/a\u003E\u003Cbr \/\u003E\nAgreed: [message:agreed]\u003Cbr \/\u003E\nNotes: [message:notes]\u003C\/p\u003E\n" + } + ] + }, + "rdf_mapping" : [] + }'); + + return $items; +} + +/** + * Our custom entity class. + */ +class ConsentAgreement extends Entity { + + /** + * {@inheritdoc} + */ + protected function defaultUri() { + return array('path' => 'admin/gdpr/agreements/' . $this->identifier()); + } + +} + +/** + * Custom controller for the gdpr_consent_agreement entity type. + */ +class ConsentAgreementController extends EntityAPIControllerExportable { + + /** + * {@inheritdoc} + */ + public function save($entity, DatabaseTransaction $transaction = NULL) { + if (isset($entity->is_new)) { + global $user; + + $entity->created = REQUEST_TIME; + $entity->uid = $user->uid; + } + + $entity->changed = REQUEST_TIME; + + // Always save new revisions. + $entity->is_new_revision = TRUE; + $entity->timestamp = REQUEST_TIME; + + return parent::save($entity, $transaction); + } + + /** + * {@inheritdoc} + */ + public function export($entity, $prefix = '') { + $vars = get_object_vars($entity); + unset($vars[$this->statusKey], $vars[$this->moduleKey], $vars['is_new']); + if ($this->nameKey != $this->idKey) { + unset($vars[$this->idKey]); + } + if ($this->revisionKey) { + unset($vars[$this->revisionKey]); + } + return entity_var_json_export($vars, $prefix); + } + +} + +/** + * Custom UI controller for the gdpr_consent_agreement entity type. + */ +class ConsentAgreementEntityUIController extends EntityDefaultUIController { + + /** + * {@inheritdoc} + */ + public function buildContent($entity, $view_mode = 'default', $langcode = NULL, $content = array()) { + $build = parent::buildContent($entity, $view_mode, $langcode, $content); + + $build['description'] = array( + '#type' => 'markup', + '#markup' => $entity->description, + ); + + $build['long_description'] = array( + '#type' => 'markup', + '#markup' => $entity->long_description, + ); + + $build['notes'] = array( + '#type' => 'markup', + '#markup' => $entity->notes, + ); + + $build['agreement_type'] = array( + '#type' => 'markup', + '#markup' => $entity->agreement_type, + ); + + return $build; + } + + /** + * {@inheritdoc} + */ + protected function overviewTableHeaders($conditions, $rows, $additional_header = array()) { + $additional_header = array( + t('Type'), + ); + return parent::overviewTableHeaders($conditions, $rows, $additional_header); + } + + /** + * {@inheritdoc} + */ + protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { + $additional_cols = array( + $entity->agreement_type ? 'Explicit' : 'Implicit', + ); + + $row = parent::overviewTableRow($conditions, $id, $entity, $additional_cols); + return $row; + } + +} diff --git a/modules/gdpr_consent/includes/gdpr_consent.admin.inc b/modules/gdpr_consent/includes/gdpr_consent.admin.inc new file mode 100644 index 0000000..d5195f9 --- /dev/null +++ b/modules/gdpr_consent/includes/gdpr_consent.admin.inc @@ -0,0 +1,145 @@ + t('Title'), + '#type' => 'textfield', + '#default_value' => isset($entity->title) ? $entity->title : '', + '#description' => t('Agreement Title'), + '#required' => TRUE, + '#weight' => -50, + ); + $form['agreement_type'] = array( + '#title' => t('Agreement Type'), + '#type' => 'select', + '#options' => array( + 0 => t('Implicit'), + 1 => t('Explicit'), + ), + '#default_value' => isset($entity->agreement_type) ? $entity->agreement_type : '', + '#description' => t('Whether consent is implicit or explicit. Set to "Explicit" if the user needs to explicitly agree, otherwise "Implicit'), + ); + $form['description'] = array( + '#title' => t('Description'), + '#type' => 'textfield', + '#default_value' => isset($entity->description) ? $entity->description : '', + '#description' => t('Text displayed to the user on the form'), + ); + $form['long_description'] = array( + '#title' => t('Long Description'), + '#type' => 'textarea', + '#default_value' => isset($entity->long_description) ? $entity->long_description : '', + '#description' => t('Text shown when the user clicks for more details'), + ); + $form['notes'] = array( + '#title' => t('Notes'), + '#type' => 'textarea', + '#default_value' => isset($entity->notes) ? $entity->notes : '', + '#description' => t('This should contain the rationale behind the agreement.'), + ); + $form['revision'] = array( + '#type' => 'checkbox', + '#title' => t('Create new revision'), + '#default_value' => 1, + ); + $form['log'] = array( + '#type' => 'textarea', + '#title' => t('Revision log message'), + '#rows' => 4, + '#default_value' => !empty($entity->log) ? $entity->log : '', + '#description' => t('Briefly describe the changes you have made.'), + ); + + field_attach_form('gdpr_consent_agreement', $entity, $form, $form_state); + + $form['actions'] = array( + '#type' => 'actions', + 'submit' => array( + '#type' => 'submit', + '#value' => isset($entity->id) ? t('Update consent agreement') : t('Save consent agreement'), + ), + ); + + return $form; +} + +/** + * Submit handler for consent agreement entity form. + */ +function gdpr_consent_agreement_form_submit($form, &$form_state) { + $entity = entity_ui_form_submit_build_entity($form, $form_state); + $entity->save(); + drupal_set_message(t('@title agreement has been saved.', array('@title' => $entity->title))); + $form_state['redirect'] = 'admin/gdpr/agreements'; +} + +/** + * Generates an overview table of older revisions of a Consent Agreement. + * + * @param $agreement + * A consent agreement entity. + * + * @return array + * An array as expected by drupal_render(). + */ +function gdpr_consent_agreement_revision_overview($agreement) { + drupal_set_title(t('Revisions for @title', array('@title' => $agreement->title))); + + $header = array(t('Revision'), array('data' => t('Operations'), 'colspan' => 2)); + + $revisions = gdpr_consent_revision_list($agreement); + + $rows = array(); + + foreach ($revisions as $revision) { + $row = array(); + $operations = array(); + + if ($revision->current_revision > 0) { + $row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "admin/gdpr/agreements/$agreement->id"), '!username' => theme('username', array('account' => $revision)))), + 'class' => array('revision-current')); + $operations[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current'), 'colspan' => 2); + } + else { + $row[] = t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/view"), '!username' => theme('username', array('account' => $revision)))); + $operations[] = l(t('revert'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/revert"); + $operations[] = l(t('delete'), "admin/gdpr/agreements/$agreement->id/revisions/$revision->revision_id/delete"); + } + $rows[] = array_merge($row, $operations); + } + + $build['gdpr_consent_revisions_table'] = array( + '#theme' => 'table', + '#rows' => $rows, + '#header' => $header, + ); + + return $build; +} + +/** + * Returns a list of all the existing revision numbers. + * + * @param $agreement + * The Consent Agreement entity. + * + * @return + * An associative array keyed by revision number. + */ +function gdpr_consent_revision_list($agreement) { + $revisions = array(); + $result = db_query('SELECT r.revision_id, a.title, r.uid, a.revision_id AS current_revision, r.timestamp, u.name FROM {gdpr_consent_agreement_revision} r LEFT JOIN {gdpr_consent_agreement} a ON a.revision_id = r.revision_id INNER JOIN {users} u ON u.uid = r.uid WHERE r.id = :id ORDER BY r.revision_id DESC', array(':id' => $agreement->id)); + foreach ($result as $revision) { + $revisions[$revision->revision_id] = $revision; + } + + return $revisions; +} diff --git a/modules/gdpr_consent/includes/gdpr_consent.agreements.inc b/modules/gdpr_consent/includes/gdpr_consent.agreements.inc new file mode 100644 index 0000000..ec6cd2e --- /dev/null +++ b/modules/gdpr_consent/includes/gdpr_consent.agreements.inc @@ -0,0 +1,93 @@ +entityCondition('entity_type', 'gdpr_consent_agreement'); + + $result = $query->execute(); + + if (isset($result['gdpr_consent_agreement'])) { + $consent_ids = array_keys($result['gdpr_consent_agreement']); + $consent_items = entity_load('gdpr_consent_agreement', $consent_ids); + } + + return MENU_ACCESS_DENIED; +} + +/** + * Callback for /admin/gdpr/agreements/{gdpr_consent_agreement ID} page. + * + * As we load the entity for display, we're responsible for invoking a number + * of hooks in their proper order. + * + * @see hook_entity_prepare_view() + * @see hook_entity_view() + * @see hook_entity_view_alter() + */ +function gdpr_consent_agreement_view_entity($entity, $view_mode = 'default') { + // Our entity type, for convenience. + $entity_type = 'gdpr_consent_agreement'; + // Start setting up the content. + $entity->content = array( + '#view_mode' => $view_mode, + ); + // Build fields content - this is where the Field API really comes in to play. + // The task has very little code here because it all gets taken care of by + // field module. + // field_attach_prepare_view() lets the fields load any data they need + // before viewing. + field_attach_prepare_view($entity_type, array($entity->id => $entity), + $view_mode); + // We call entity_prepare_view() so it can invoke hook_entity_prepare_view() + // for us. + entity_prepare_view($entity_type, array($entity->id => $entity)); + // Now field_attach_view() generates the content for the fields. + $entity->content += field_attach_view($entity_type, $entity, $view_mode); + + // OK, Field API done, now we can set up some of our own data. + $entity->content['created'] = array( + '#type' => 'item', + '#title' => t('Created date'), + '#markup' => format_date($entity->created), + ); + $entity->content['description'] = array( + '#type' => 'item', + '#title' => t('Description'), + '#markup' => $entity->description, + ); + + // Now to invoke some hooks. We need the language code for + // hook_entity_view(), so let's get that. + global $language; + $langcode = $language->language; + // And now invoke hook_entity_view(). + module_invoke_all('entity_view', $entity, $entity_type, $view_mode, + $langcode); + // Now invoke hook_entity_view_alter(). + drupal_alter(array('gdpr_consent_agreement_view_entity', 'entity_view'), + $entity->content, $entity_type); + + // And finally return the content. + return $entity->content; +} + +/** + * Callback for /admin/gdpr/agreements/{gdpr_consent_agreement ID}/{revision ID} page. + */ +function gdpr_consent_agreement_view_revision($revision_id) { + $agreement = entity_revision_load('gdpr_consent_agreement', $revision_id); + $output = gdpr_consent_agreement_view_entity($agreement); + drupal_set_title(t('@title revision @id', array('@title' => $agreement->title, '@id' => $agreement->revision_id))); + + return $output; +} diff --git a/modules/gdpr_consent/js/gdpr_consent.js b/modules/gdpr_consent/js/gdpr_consent.js new file mode 100644 index 0000000..339d617 --- /dev/null +++ b/modules/gdpr_consent/js/gdpr_consent.js @@ -0,0 +1,36 @@ +(function ($) { + $(function () { + // Hide the description for any GDPR checkboxes. + var container = $('.gdpr_consent_agreement').parent(); + var desc = container.find('.description'); + + if (!desc.length) { + container = container.parent(); + desc = container.find('.description'); + } + + desc.hide(); + + $('?') + .insertAfter(container.find('label')) + .click(function () { + var desc = $(this).find('.description'); + if (!desc.length) { + desc = $(this).parent().find('.description'); + } + + desc.slideToggle() + }); + + // Do the same for implicit + container = $('.gdpr_consent_implicit').parent(); + desc = container.next('.description'); + desc.hide(); + + $('?') + .appendTo(container) + .click(function () { + $(this).next('.description').slideToggle() + }); + }); +})(jQuery); diff --git a/modules/gdpr_dump/gdpr_dump.admin.inc b/modules/gdpr_dump/gdpr_dump.admin.inc new file mode 100644 index 0000000..76c3304 --- /dev/null +++ b/modules/gdpr_dump/gdpr_dump.admin.inc @@ -0,0 +1,6 @@ + 'Stores GDPR Sanitizers.', + 'export' => array( + 'key' => 'name', + 'key name' => 'Name', + 'admin_title' => 'label', + 'admin_description' => 'description', + 'primary key' => 'name', + 'identifier' => 'field', + 'default hook' => 'gdpr_dump_default_sanitizer', + 'object' => 'GDPRSanitizerDefault', + ), + 'fields' => array( + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'Machine name of sanitizer.', + ), + 'label' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'Label of sanitizer.', + ), + 'description' => array( + 'type' => 'varchar', + 'length' => 255, + 'description' => 'Description of sanitizer.', + ), + 'settings' => array( + 'type' => 'text', + 'size' => 'big', + 'description' => 'Additional settings.', + 'serialize' => TRUE, + ), + ), + 'primary key' => array('name'), + 'keys' => array( + 'enabled' => array('enabled'), + ), + ); + + return $schema; +} diff --git a/modules/gdpr_dump/gdpr_dump.module b/modules/gdpr_dump/gdpr_dump.module index b2ca09f..269b956 100644 --- a/modules/gdpr_dump/gdpr_dump.module +++ b/modules/gdpr_dump/gdpr_dump.module @@ -9,7 +9,7 @@ * Implements hook_menu(). */ function gdpr_dump_menu() { - $items['admin/config/gdpr/dump-settings'] = [ + $items['admin/gdpr/dump-settings'] = [ 'title' => 'SQL Dump settings', 'page callback' => 'drupal_get_form', 'page arguments' => ['gdpr_dump_settings_form'], @@ -36,7 +36,7 @@ function gdpr_dump_settings_form($form, &$form_state) { $plugins = []; // @todo: implement sanitize plugins - foreach (gdpr_dump_get_sanitizer_plugins() as $plugin_name => $plugin) { + foreach (gdpr_dump_get_gdpr_sanitizers() as $plugin_name => $plugin) { $plugins[$plugin_name] = $plugin['title']; } @@ -171,125 +171,93 @@ function gdpr_get_complete_schema() { } /** - * Implements hook_ctools_plugin_directory(). + * Helper function to load gdpr dump service. */ -function gdpr_dump_ctools_plugin_directory($module, $plugin) { - if ($module == 'gdpr_dump' && \array_key_exists($plugin, gdpr_dump_ctools_plugin_type())) { - return 'plugins/' . $plugin; +function gdpr_dump_service() { + $service = &drupal_static(__FUNCTION__); + + if (empty($service)) { + module_load_include('inc', 'gdpr_dump', 'inc/GdprDumpGdprSqlDump'); + $service = new GdprDumpGdprSqlDump(); } + + return $service; } /** * Implements hook_ctools_plugin_type(). */ function gdpr_dump_ctools_plugin_type() { - return [ - 'sanitizer' => [ - 'label' => 'sanitizer', - 'use hooks' => FALSE, - ], - ]; -} + $plugins['gdpr_sanitizer'] = array( + 'classes' => array('handler'), + 'child plugins' => TRUE, + 'use hooks' => TRUE, + ); -/** - * Helper function for listing gdpr sanitizer plugins. - */ -function gdpr_dump_get_sanitizer_plugins($id = NULL) { - return ctools_get_plugins('gdpr_dump', 'sanitizer', $id); + return $plugins; } /** - * Random word generator function. + * Implements hook_ctools_plugin_directory(). */ -function gdpr_random_word($length = 8) { - mt_srand((double) microtime() * 1000000); - $vowels = [ - "a", - "e", - "i", - "o", - "u", - ]; - $cons = [ - "b", - "c", - "d", - "g", - "h", - "j", - "k", - "l", - "m", - "n", - "p", - "r", - "s", - "t", - "u", - "v", - "w", - "tr", - "cr", - "br", - "fr", - "th", - "dr", - "ch", - "ph", - "wr", - "st", - "sp", - "sw", - "pr", - "sl", - "cl", - "sh", - ]; - $num_vowels = count($vowels); - $num_cons = count($cons); - $word = ''; - while (strlen($word) < $length) { - $word .= $cons[mt_rand(0, $num_cons - 1)] . $vowels[mt_rand(0, $num_vowels - 1)]; +function gdpr_dump_ctools_plugin_directory($owner, $plugin_type) { + if ($owner == 'gdpr_dump') { + return 'plugins/' . $plugin_type; + } + if ($owner == 'ctools' && $plugin_type == 'export_ui') { + return 'plugins/' . $plugin_type; } - return substr($word, 0, $length); } /** - * Random sentence generator. + * Fetch metadata for all context plugins. + * + * @return array + * An array of arrays with information about all available panel contexts. */ -function gdpr_random_sentence($word_count) { - $output_array = []; - - for ($i = 0; $i < $word_count; $i++) { - $output_array[] = gdpr_random_word(rand(1, 12)); - } +function gdpr_dump_get_gdpr_sanitizers() { + ctools_include('plugins'); - $output = implode(' ', $output_array); - - return ucfirst($output) . '.'; + return ctools_get_plugins('gdpr_dump', 'gdpr_sanitizer'); } /** - * Random paragraph generator. + * Fetch metadata for context plugins by id. */ -function gdpr_random_paragraphs($paragraph_count) { - $output = ''; - for ($i = 1; $i <= $paragraph_count; $i++) { - $output .= gdpr_random_sentence(mt_rand(20, 60)) . "\n\n"; - } - return $output; +function gdpr_dump_get_gdpr_sanitizer($plugin_id) { + ctools_include('plugins'); + + return ctools_get_plugins('gdpr_dump', 'gdpr_sanitizer', $plugin_id); } /** - * Helper function to load gdpr dump service. + * Implements hook_gdpr_dump_default_field_data(). + * + * Default hook for building field data plugins. */ -function gdpr_dump_service() { - $service = &drupal_static(__FUNCTION__); +function gdpr_dump_gdpr_dump_default_sanitizer() { + $export = array(); - if (empty($service)) { - module_load_include('inc', 'gdpr_dump', 'inc/GdprDumpGdprSqlDump'); - $service = new GdprDumpGdprSqlDump(); + $plugins = gdpr_dump_get_gdpr_sanitizers(); + + foreach ($plugins as $name => $plugin) { + $class = ctools_plugin_get_class($plugin, 'handler'); + $export[$name] = $class::create($plugin); } - return $service; + return $export; +} + +/** + * Implements hook_permission(). + */ +function gdpr_dump_permission() { + $perms = array( + 'administer gdpr sanitizers' => array( + 'title' => t('Administer GDPR sanitizer settings'), + 'restrict access' => TRUE, + ), + ); + + return $perms; } diff --git a/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php new file mode 100644 index 0000000..74d64d0 --- /dev/null +++ b/modules/gdpr_dump/plugins/export_ui/gdpr_sanitizer_ui.class.php @@ -0,0 +1,11 @@ + 'gdpr_dump_sanitizers', + 'access' => 'administer gdpr sanitizers', + + 'menu' => array( + 'menu prefix' => 'admin/gdpr', + 'menu item' => 'sanitizer-list', + 'menu title' => 'GDPR Sanitizers', + 'menu description' => 'Configure GDPR Sanitizers.', + ), + + 'title singular' => t('sanitizer'), + 'title singular proper' => t('Sanitizer'), + 'title plural' => t('sanitizers'), + 'title plural proper' => t('Sanitizers'), + + 'form' => array( + 'settings' => 'gdpr_dump_sanitizer_export_ui_form', + ), + 'handler' => 'gdpr_sanitizer_ui', + 'strings' => array( + 'confirmation' => array( + 'revert' => array( + 'information' => t('This action will permanently remove any customizations made to this view.'), + 'success' => t('The view has been reverted.'), + ), + 'delete' => array( + 'information' => t('This action will permanently remove the view from your database.'), + 'success' => t('The view has been deleted.'), + ), + ), + ), +); + +/** + * Define the preset add/edit form. + */ +function gdpr_dump_sanitizer_export_ui_form(&$form, &$form_state) { + /* @var GDPRSanitizerDefault $sanitizer */ + $sanitizer = $form_state['item']; + + $form['settings'] = array( + '#type' => 'fieldset', + '#title' => t('Settings'), + '#tree' => TRUE, + ); + + $form['settings']['notes'] = array( + '#type' => 'textarea', + '#title' => t('Notes'), + '#default_value' => $sanitizer->getSetting('notes', ''), + ); +} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php new file mode 100644 index 0000000..e88214f --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerDate.class.php @@ -0,0 +1,26 @@ +format('U'); + } + +} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerEmail.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerEmail.class.php new file mode 100644 index 0000000..9dce20a --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerEmail.class.php @@ -0,0 +1,37 @@ +word(self::EMAIL_LENGTH) . '@example.com'; + } + +} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php new file mode 100644 index 0000000..176722f --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerName.class.php @@ -0,0 +1,43 @@ + 'anon_' . $random->word(rand(self::MIN_LENGTH, self::MAX_LENGTH)), + 'family' => $random->word(rand(self::MIN_LENGTH, self::MAX_LENGTH)), + ); + } + +} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php new file mode 100644 index 0000000..3714cdb --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerPartyArchived.class.php @@ -0,0 +1,27 @@ +string(4); + // If the value is too long, tirm it. + if (isset($max_length) && strlen($value) > $max_length) { + $value = substr($value, 0, $max_length); + } + } + return $value; + } + +} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php new file mode 100644 index 0000000..55c5466 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/GDPRSanitizerUsername.class.php @@ -0,0 +1,35 @@ +name(self::NAME_LENGTH); + } + +} diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc new file mode 100644 index 0000000..05135ae --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_date.inc @@ -0,0 +1,16 @@ + array( + 'class' => 'GDPRSanitizerDate', + ), +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_email.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_email.inc new file mode 100644 index 0000000..c10c9fe --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_email.inc @@ -0,0 +1,15 @@ + array( + 'class' => 'GDPRSanitizerEmail', + ), +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_long_text.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_long_text.inc new file mode 100644 index 0000000..fb12df4 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_long_text.inc @@ -0,0 +1,17 @@ + array( + 'class' => 'GDPRSanitizerDefault', + ), + 'name' => 'gdpr_sanitizer_long_text', + 'label' => 'Long text sanitizer', +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_name.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_name.inc new file mode 100644 index 0000000..3c74f1c --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_name.inc @@ -0,0 +1,15 @@ + array( + 'class' => 'GDPRSanitizerName', + ), +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_party_archived.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_party_archived.inc new file mode 100644 index 0000000..996b408 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_party_archived.inc @@ -0,0 +1,17 @@ + array( + 'class' => 'GDPRSanitizerPartyArchived', + ), +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_password.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_password.inc new file mode 100644 index 0000000..f536494 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_password.inc @@ -0,0 +1,17 @@ + array( + 'class' => 'GDPRSanitizerDefault', + ), + 'name' => 'gdpr_sanitizer_password', + 'label' => 'Password sanitizer', +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_text.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_text.inc new file mode 100644 index 0000000..8715a87 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_text.inc @@ -0,0 +1,16 @@ + array( + 'class' => 'GDPRSanitizerText', + ), +); diff --git a/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_username.inc b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_username.inc new file mode 100644 index 0000000..6f8de09 --- /dev/null +++ b/modules/gdpr_dump/plugins/gdpr_sanitizer/gdpr_sanitizer_username.inc @@ -0,0 +1,15 @@ + array( + 'class' => 'GDPRSanitizerUsername', + ), +); diff --git a/modules/gdpr_dump/plugins/sanitizer/EmailSanitizer.inc b/modules/gdpr_dump/plugins/sanitizer/EmailSanitizer.inc deleted file mode 100644 index ec97cf4..0000000 --- a/modules/gdpr_dump/plugins/sanitizer/EmailSanitizer.inc +++ /dev/null @@ -1,22 +0,0 @@ - t('Email Sanitizer'), - 'sanitize callback' => 'gdpr_email_sanitize', -]; - -/** - * Email sanitize callback. - */ -function gdpr_email_sanitize($input) { - if (empty($input)) { - return $input; - } - - return gdpr_random_word(12) . '@example.com'; -} diff --git a/modules/gdpr_dump/plugins/sanitizer/LongTextSanitizer.inc b/modules/gdpr_dump/plugins/sanitizer/LongTextSanitizer.inc deleted file mode 100644 index 0e9cdb3..0000000 --- a/modules/gdpr_dump/plugins/sanitizer/LongTextSanitizer.inc +++ /dev/null @@ -1,61 +0,0 @@ - t('Long Text Sanitizer'), - 'sanitize callback' => 'gdpr_long_text_sanitize', -]; - -/** - * Long text sanitize callback. - */ -function gdpr_long_text_sanitize($input) { - if (empty($input)) { - return $input; - } - - $output = gdpr_remote_generator($input); - - if ($output === $input) { - $output = gdpr_local_generator($input); - } - - return $output; -} - -/** - * Remote generator for long text sanitizer. - */ -function gdpr_long_text_remote_generator($input) { - $result = NULL; - try { - $result = drupal_http_request('https://loripsum.net/api/3/medium/plaintext'); - } - catch (\Exception $e) { - // @todo: Log? - return $input; - } - - if (NULL !== $result && 200 == $result->code) { - $data = $result->data; - if (NULL === $data) { - return $input; - } - - return substr(trim($data), 0, strlen($input)); - } - - return $input; -} - -/** - * Local generator for long text sanitizer. - */ -function gdpr_long_text_local_generator($input) { - $paragraphCount = str_word_count($input) % 5; - return gdpr_random_paragraphs($paragraphCount); -} diff --git a/modules/gdpr_dump/plugins/sanitizer/PasswordSanitizer.inc b/modules/gdpr_dump/plugins/sanitizer/PasswordSanitizer.inc deleted file mode 100644 index 1b4db3d..0000000 --- a/modules/gdpr_dump/plugins/sanitizer/PasswordSanitizer.inc +++ /dev/null @@ -1,19 +0,0 @@ - t('Password Sanitizer'), - 'sanitize callback' => 'gdpr_password_sanitize', -]; - -/** - * Password sanitize callback. - */ -function gdpr_password_sanitize($input) { - require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); - return user_hash_password(user_password(8)); -} diff --git a/modules/gdpr_dump/plugins/sanitizer/TextSanitizer.inc b/modules/gdpr_dump/plugins/sanitizer/TextSanitizer.inc deleted file mode 100644 index 681425e..0000000 --- a/modules/gdpr_dump/plugins/sanitizer/TextSanitizer.inc +++ /dev/null @@ -1,60 +0,0 @@ - t('Text Sanitizer'), - 'sanitize callback' => 'gdpr_text_sanitize', -]; - -/** - * Text sanitize callback. - */ -function gdpr_text_sanitize($input) { - if (empty($input)) { - return $input; - } - - $output = gdpr_text_remote_generator($input); - - if ($output === $input) { - $output = gdpr_text_local_generator($input); - } - - return $output; -} - -/** - * Remote generator for text sanitize. - */ -function gdpr_text_remote_generator($input) { - $result = NULL; - try { - $result = drupal_http_request('https://loripsum.net/api/1/short/plaintext'); - } - catch (\Exception $e) { - // @todo: Log? - return $input; - } - - if (NULL !== $result && 200 == $result->code) { - $data = $result->data; - if (NULL === $data) { - return $input; - } - - return substr(trim($data), 0, strlen($input)); - } - - return $input; -} - -/** - * Local generator for text sanitize. - */ -function gdpr_text_local_generator($input) { - return gdpr_random_sentence(str_word_count($input)); -} diff --git a/modules/gdpr_dump/plugins/sanitizer/UsernameSanitizer.inc b/modules/gdpr_dump/plugins/sanitizer/UsernameSanitizer.inc deleted file mode 100644 index 124ce65..0000000 --- a/modules/gdpr_dump/plugins/sanitizer/UsernameSanitizer.inc +++ /dev/null @@ -1,59 +0,0 @@ - t('Username Sanitizer'), - 'sanitize callback' => 'gdpr_username_sanitize', -]; - -/** - * Username sanitize callback. - */ -function gdpr_username_sanitize($input) { - if (empty($input)) { - return $input; - } - - $output = gdpr_username_remote_generator($input); - - if ($output === $input) { - $output = gdpr_username_local_generator($input); - } - - return $output; -} - -/** - * Remote generator for username sanitizer. - */ -function gdpr_username_remote_generator($input) { - $result = NULL; - try { - $result = drupal_http_request('https://randomuser.me/api/?format=pretty&results=1&inc=name&noinfo&nat=us,gb'); - } - catch (\Exception $e) { - // @todo: Log? - return $input; - } - - if (NULL !== $result && 200 == $result->code) { - $data = $result->data; - $data = json_decode($data, TRUE); - - $name = reset($data['results'])['name']; - return $name['first'] . '.' . $name['last']; - } - - return $input; -} - -/** - * Local generator for username sanitizer. - */ -function gdpr_username_local_generator($input) { - return gdpr_random_word(strlen($input)); -} diff --git a/modules/gdpr_dump/src/GDPRUtilRandom.php b/modules/gdpr_dump/src/GDPRUtilRandom.php new file mode 100644 index 0000000..75a898c --- /dev/null +++ b/modules/gdpr_dump/src/GDPRUtilRandom.php @@ -0,0 +1,93 @@ +name = $plugin['name']; + } + + if (!empty($plugin['label'])) { + $sanitzer->label = $plugin['label']; + } + + return $sanitzer; + } + + /** + * Get a stored setting. + * + * @param string $setting + * The key of the setting to be fetched. + * @param mixed|null $default + * The default to be returned if not stored. + * + * @return mixed|null + * The field data setting. + */ + public function getSetting($setting, $default = NULL) { + if (isset($this->settings[$setting])) { + return $this->settings[$setting]; + } + + return $default; + } + + /** + * Get a stored setting. + * + * @param string $setting + * The key of the setting to be stored. + * @param mixed $value + * The value to be stored. + * + * @return $this + */ + public function setSetting($setting, $value) { + $this->settings[$setting] = $value; + return $this; + } + + /** + * Return the sanitized input. + * + * @var int|string $input + * The input. + * @var EntityMetadataWrapper|null $field + * The input. + * + * @return int|string + * The sanitized input. + */ + public function sanitize($input, $field = NULL) { + return $input; + } + +} diff --git a/templates/gdpr-user-data-page.tpl.php b/templates/gdpr-user-data-page.tpl.php index 6aac020..fdad013 100644 --- a/templates/gdpr-user-data-page.tpl.php +++ b/templates/gdpr-user-data-page.tpl.php @@ -5,14 +5,14 @@ * Renders a user's collected data. */ -$header = [t('Type'), t('Value')]; -$rows = []; +$header = array(t('Type'), t('Value')); +$rows = array(); foreach ($user_data as $field => $value) { - $rows[] = [ - 'data' => [$field, $value], - ]; + $rows[] = array( + 'data' => array($field, $value), + ); } -print theme('table', [ +print theme('table', array( 'header' => $header, 'rows' => $rows, -]); +));