Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / modules / contrib / eu_cookie_compliance / eu_cookie_compliance.module
index 2c442676b01db191cba51d27f73afad962dad782..e2421377b79cbfaee6b2596310583519a5fe06a2 100644 (file)
@@ -1,29 +1,64 @@
 <?php
+
 /**
  * @file
+ * The main file for the EU Cookie Compliance module.
+ *
  * This module intends to deal with the EU Directive on Privacy and Electronic
  * Communications that comes into effect in the UK on 26th May 2012.
  */
 
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\Path\AliasManager;
-use Drupal\Core\Path\AliasStorage;
-use Drupal\Core\Path\AliasWhitelist;
-use Drupal\Core\Extension\ModuleHandler;
-use Drupal\Core\Database\Database;
 use Drupal\Core\Cache\Cache;
-use Drupal\Core\Cache\DatabaseBackend;
-use Drupal\Core\Language\LanguageManager;
 use Drupal\Core\Url;
-use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Component\Utility\Unicode;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Html;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\smart_ip\SmartIp;
+use Drupal\Core\Database\Database;
+
+/**
+ * Implements hook_help().
+ */
+function eu_cookie_compliance_help($route_name, RouteMatchInterface $route_match) {
+  switch ($route_name) {
+    case 'help.page.eu_cookie_compliance':
+      $output = '';
+      $output .= '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t('This module intends to deal with the EU Directive on Privacy and Electronic Communications that comes into effect on 26th May 2012.
+        From that date, if you are not compliant or visibly working towards compliance,
+        you run the risk of enforcement action, which can include a fine of up to
+        half a million pounds for a serious breach.') . '</p>';
+      $output .= '<h3>' . t('How it works') . '</h3>';
+      $output .= '<p>' . t('The module displays a banner at the bottom or the top of website to make users aware of the fact that cookies are being set. The user may then give
+        his/her consent or move to a page that provides more details. Consent is given
+        by user pressing the agree buttons or by continuing browsing the website. Once
+        consent is given another banner appears with a “Thank you” message.') . '</p>';
+      $output .= '<p>' . t('The module provides a settings page where the banner can be customized. There are also template files for the banners that can be overridden by your theme.') . '</p>';
+      $output .= '<h3>' . t('Installation') . '</h3>';
+      $output .= '<ol><p><li>' . t('Unzip the files to the "sites/all/modules" OR "modules" directory and enable the module.') . '</li></p>';
+      $output .= '<p><li>' . t('If desired, give the administer EU Cookie Compliance banner permissions that allow users of certain roles access the administration page. You can do so on the admin/user/permissions page.') . '</li></p>';
+      // @codingStandardsIgnoreLine
+      $output .= "<p><label>-&nbsp;</label>" . t("there is also a 'display eu cookie compliance banner' permission that helps you show the banner to the roles you desire.") . "</p>";
+      $output .= '<p><li>' . t('You may want to create a page that would explain how your site uses cookies. Alternatively, if you have a privacy policy, you can link the banner to that page (see next step).') . '</li></p>';
+      $output .= '<p><li>' . t('Go to the admin/config/system/eu-cookie-compliance page to configure and enable the banner.') . '</li></p>';
+      $output .= '<p><li>' . t('If you want to customize the banner background and text color, either type in the hex values or simply install http://drupal.org/project/jquery_colorpicker.') . '</li></p>';
+      $output .= '<p><li>' . t('If you want to theme your banner override the themes in the template file.') . '</li></p>';
+      $output .= '<p><li>' . t('If you want to show the message in EU countries only, install the Smart IP module: http://drupal.org/project/smart_ip and enable the option on the admin page.') . '</li></p></ol>';
+      $output .= '<p><b>' . t('NOTICE: The module does not audit your cookies nor does it prevent cookies from being set.') . '</b></p>';
+      $output .= '<h3>' . t('For developers') . '</h3>';
+      $output .= '<p>' . t('If you want to conditionally set cookies in your module, there is a javascript function provided that returns TRUE if the current user has given his consent:') . '</p>';
+      $output .= '<p><code>Drupal.eu_cookie_compliance.hasAgreed()</code></p>';
+
+      return ['#markup' => $output];
+  }
+}
 
 /**
  * Implements hook_page_attachments().
  */
 function eu_cookie_compliance_page_attachments(&$attachments) {
-  $config = \Drupal::config('eu_cookie_compliance.settings');
+  $config = Drupal::config('eu_cookie_compliance.settings');
 
   // Check Add/Remove domains.
   $domain_allow = TRUE;
@@ -34,13 +69,13 @@ function eu_cookie_compliance_page_attachments(&$attachments) {
 
     $domains_list = str_replace(array("\r\n", "\r"), "\n", $config->get('domains_list'));
     $domains_list = explode("\n", $domains_list);
+    $domains_list = preg_replace('{/$}', '', $domains_list);
     $domain_match = in_array($base_url, $domains_list);
 
     if ($domain_option && $domain_match) {
       $domain_allow = FALSE;
     }
 
-    // @TODO: Ask about this thing.
     if (!$domain_option && !$domain_match) {
       $domain_allow = FALSE;
     }
@@ -50,101 +85,147 @@ function eu_cookie_compliance_page_attachments(&$attachments) {
   $path_match = FALSE;
 
   if (!empty($config->get('exclude_paths'))) {
-    // @TODO: make this more robust.
-    $path = \Drupal::service('path.current')->getPath();
-    $path_match = \Drupal::service('path.matcher')->matchPath($path, $config->get('exclude_paths'));
+    $path = Drupal::service('path.current')->getPath();
+    $path_match = Drupal::service('path.matcher')->matchPath($path, $config->get('exclude_paths'));
+    $exclude_paths = $config->get('exclude_paths');
+    Drupal::moduleHandler()->alter('eu_cookie_compliance_path_match', $path_match, $path, $exclude_paths);
+  }
+
+  // Check hide cookie compliance on admin theme.
+  $admin_theme_match = FALSE;
+
+  if ($config->get('exclude_admin_theme')) {
+    // Determines whether the active route is an admin one.
+    $is_route_admin = Drupal::service('router.admin_context')->isAdminRoute();
+    if ($is_route_admin) {
+      $admin_theme_match = TRUE;
+    }
   }
 
-  // Unsupported GeoIP feature until those modules update their code.
   $geoip_match = TRUE;
+  if (!empty($config->get('eu_only')) && $config->get('eu_only')) {
+    $geoip_match = eu_cookie_compliance_user_in_eu();
+  }
 
-  if ($config->get('popup_enabled') && \Drupal::currentUser()->hasPermission('display eu cookie compliance popup') && $geoip_match && $domain_allow && !$path_match) {
+  // Allow other modules to alter the geo IP matching logic.
+  Drupal::moduleHandler()->alter('eu_cookie_compliance_geoip_match', $geoip_match);
+
+  $uid1_match = TRUE;
+  if (Drupal::currentUser()->id() == 1 && !empty($config->get('exclude_uid_1')) && $config->get('exclude_uid_1')) {
+    $uid1_match = FALSE;
+  }
+
+  // Allow other modules to alter if the banner needs to be shown or not.
+  $modules_allow_popup = TRUE;
+  Drupal::moduleHandler()->alter('eu_cookie_compliance_show_popup', $modules_allow_popup);
+
+  if ($config->get('popup_enabled') && Drupal::currentUser()->hasPermission('display eu cookie compliance popup') && $geoip_match && $domain_allow && !$path_match && !$admin_theme_match && $uid1_match && $modules_allow_popup) {
     $language = Drupal::languageManager()->getCurrentLanguage();
-    // Array storage for caching full client data.
 
-    $data = array();
-    if ($cache = \Drupal::cache()->get('eu_cookie_compliance_client_settings.' . $language->getId())) {
-      $data = $cache->data;
+    $data['css'] = '';
+    // Color overrides.
+    if ($config->get('popup_bg_hex') !== '' && $config->get('popup_text_hex') !== '') {
+      $data['css'] = 'div#sliding-popup {background:#' . Html::escape($config->get('popup_bg_hex')) . '} #sliding-popup h1, #sliding-popup h2, #sliding-popup h3, #sliding-popup p { color:#' . Html::escape($config->get('popup_text_hex')) . ';}';
+    }
+    if (!empty($config->get('popup_position')) && $config->get('popup_position') && !empty($config->get('fixed_top_position')) && $config->get('fixed_top_position')) {
+      $data['css'] .= '#sliding-popup.sliding-popup-top { position: fixed; }';
+    }
+
+    $popup_text_info = str_replace(array("\r", "\n"), '', $config->get('popup_info.value'));
+    $popup_text_agreed = str_replace(array("\r", "\n"), '', $config->get('popup_agreed.value'));
+    $html_info = array(
+      '#theme' => 'eu_cookie_compliance_popup_info',
+      '#message' => check_markup($popup_text_info, $config->get('popup_info.format'), FALSE),
+      '#agree_button' => $config->get('popup_agree_button_message'),
+      '#disagree_button' => ($config->get('show_disagree_button') == TRUE) ? $config->get('popup_disagree_button_message') : FALSE,
+    );
+    $mobile_popup_text_info = str_replace(array("\r", "\n"), '', $config->get('mobile_popup_info.value'));
+    $mobile_html_info = array(
+      '#theme' => 'eu_cookie_compliance_popup_info',
+      '#message' => check_markup($mobile_popup_text_info, $config->get('popup_info.format'), FALSE),
+      '#agree_button' => $config->get('popup_agree_button_message'),
+      '#disagree_button' => ($config->get('show_disagree_button') == TRUE) ? $config->get('popup_disagree_button_message') : FALSE,
+    );
+    $html_agreed = array(
+      '#theme' => 'eu_cookie_compliance_popup_agreed',
+      '#message' => check_markup($popup_text_agreed, $config->get('popup_agreed.format'), FALSE),
+      '#hide_button' => $config->get('popup_hide_button_message'),
+      '#find_more_button' => ($config->get('show_disagree_button') == TRUE) ? $config->get('popup_find_more_button_message') : FALSE,
+    );
+
+    $was_debugging = FALSE;
+
+    /*
+     * @var $twig_service Twig_Environment
+     */
+    $twig_service = Drupal::service('twig');
+
+    if ($twig_service->isDebug()) {
+      $was_debugging = TRUE;
+      $twig_service->disableDebug();
+    }
+
+    $html_info = trim(Drupal::service('renderer')->renderRoot($html_info)->__toString());
+    $mobile_html_info = trim(Drupal::service('renderer')->renderRoot($mobile_html_info)->__toString());
+    $html_agreed = trim(Drupal::service('renderer')->renderRoot($html_agreed)->__toString());
+
+    if ($was_debugging) {
+      $twig_service->enableDebug();
+    }
+
+    $popup_link = $config->get('popup_link');
+    if (UrlHelper::isExternal($popup_link)) {
+      $popup_link = Url::fromUri($popup_link);
     }
     else {
-      // Initialize some needed popup settings messages.
-      $popup_settings_messages = array(
-        'popup_agree_button_message',
-        'popup_disagree_button_message',
-        'popup_hide_button_message',
-        'popup_find_more_button_message'
-      );
-      foreach ($popup_settings_messages as $key) {
-        if (!isset($popup_settings[$key])) {
-          $popup_settings[$key] = '';
-        }
-      }
-      $data['css'] = '';
-      // Color overrides.
-      if ($config->get('popup_bg_hex') !== '' and $config->get('popup_text_hex') !== '') {
-        $position = $config->get('popup_position') ? 'top' : 'bottom';
-        $data['css'] = '#sliding-popup.sliding-popup-' . $position . ' {background:#' . Html::escape($config->get('popup_bg_hex')) . ';}'
-             . '#sliding-popup .popup-content #popup-text h2, #sliding-popup .popup-content #popup-text p {color:#' . Html::escape($config->get('popup_text_hex')) . ' !important;}';
-      }
-      $popup_text_info = str_replace(array("\r", "\n"), '', $config->get('popup_info.value'));
-      $popup_text_agreed = str_replace(array("\r", "\n"), '', $config->get('popup_agreed.value'));
-      $html_info = array(
-        '#theme' => 'eu_cookie_compliance_popup_info',
-        '#message' => check_markup($popup_text_info, $config->get('popup_info.format'), FALSE),
-        '#agree_button' => $config->get('popup_agree_button_message'),
-        '#disagree_button' => $config->get('popup_disagree_button_message'),
-      );
-      $html_agreed = array(
-        '#theme' => 'eu_cookie_compliance_popup_agreed',
-        '#message' => check_markup($popup_text_agreed, $config->get('popup_agreed.format'), FALSE),
-        '#hide_button' => $config->get('popup_hide_button_message'),
-        '#find_more_button' => $config->get('popup_find_more_button_message'),
-      );
-
-      $was_debugging = FALSE;
-
-      /**
-       * @var $twig_service Twig_Environment
-       */
-      $twig_service = \Drupal::service('twig');
-
-      if ($twig_service->isDebug()) {
-        $was_debugging = TRUE;
-        $twig_service->disableDebug();
-      }
-
-      $html_info = \Drupal::service('renderer')->renderRoot($html_info)->__toString();
-      $html_agreed = \Drupal::service('renderer')->renderRoot($html_agreed)->__toString();
-
-      if ($was_debugging) {
-        $twig_service->enableDebug();
-      }
-
-      $data['variables'] = array(
-        'popup_enabled'        => $config->get('popup_enabled'),
-        'popup_agreed_enabled' => $config->get('popup_agreed_enabled'),
-        'popup_hide_agreed'    => $config->get('popup_hide_agreed'),
-        'popup_clicking_confirmation' => $config->get('popup_clicking_confirmation'),
-        'popup_html_info'      => $config->get('popup_enabled') ? $html_info : FALSE,
-        'popup_html_agreed'    => $config->get('popup_agreed_enabled') ? $html_agreed : FALSE,
-        'popup_height'         => !empty($config->get('popup_height')) ? $config->get('popup_height') : 'auto',
-        'popup_width'          => !empty($config->get('popup_width')) ? $config->get('popup_width') : '100%',
-        'popup_delay'          => (int) ($config->get('popup_delay') * 1000),
-        'popup_link'           => $config->get('popup_link'),
-        'popup_link_new_window' => $config->get('popup_link_new_window'),
-        'popup_link'           => $config->get('popup_link'),
-        'popup_link_new_window' => !empty($config->get('popup_link_new_window')) ? $config->get('popup_link_new_window') : 1,
-        'popup_position'       => $config->get('popup_position'),
-        'popup_language'       => $language->getId(),
-        'popup_bg_hex'         => !empty($config->get('popup_bg_hex')) ? $config->get('popup_bg_hex') : FALSE,
-        'popup_text_hex'       => !empty($config->get('popup_text_hex')) ? $config->get('popup_text_hex') : FALSE,
-        'domain'               => $config->get('domain'),
-      );
-      \Drupal::cache()->set('eu_cookie_compliance_client_settings.' . $language->getId(), $data);
+      $popup_link = $popup_link === '<front>' ? '/' : $popup_link;
+      $popup_link = Url::fromUserInput($popup_link);
     }
+    $popup_link = $popup_link->toString();
+
+    $data['variables'] = array(
+      'popup_enabled'        => $config->get('popup_enabled'),
+      'popup_agreed_enabled' => $config->get('popup_agreed_enabled'),
+      'popup_hide_agreed'    => $config->get('popup_hide_agreed'),
+      'popup_clicking_confirmation' => $config->get('popup_clicking_confirmation'),
+      'popup_scrolling_confirmation' => $config->get('popup_scrolling_confirmation'),
+      'popup_html_info'      => $config->get('popup_enabled') ? $html_info : FALSE,
+      'use_mobile_message'   => !empty($config->get('use_mobile_message')) ? $config->get('use_mobile_message') : FALSE,
+      'mobile_popup_html_info' => $config->get('popup_enabled') ? $mobile_html_info : FALSE,
+      'mobile_breakpoint'    => !empty($config->get('mobile_breakpoint')) ? $config->get('mobile_breakpoint') : '768',
+      'popup_html_agreed'    => $config->get('popup_agreed_enabled') ? $html_agreed : FALSE,
+      'popup_use_bare_css'   => !empty($config->get('popup_use_bare_css')) ? $config->get('popup_use_bare_css') : FALSE,
+      'popup_height'         => !empty($config->get('popup_height')) ? $config->get('popup_height') : 'auto',
+      'popup_width'          => !empty($config->get('popup_width')) ? $config->get('popup_width') : '100%',
+      'popup_delay'          => (int) ($config->get('popup_delay')),
+      'popup_link'           => $popup_link,
+      'popup_link_new_window' => $config->get('popup_link_new_window'),
+      'popup_position'       => $config->get('popup_position'),
+      'popup_language'       => $language->getId(),
+      'better_support_for_screen_readers'         => !empty($config->get('better_support_for_screen_readers')) ? $config->get('better_support_for_screen_readers') : FALSE,
+      'cookie_name'         => !empty($config->get('cookie_name')) ? $config->get('cookie_name') : '',
+      'reload_page'         => !empty($config->get('reload_page')) ? $config->get('reload_page') : FALSE,
+      'domain'               => $config->get('domain'),
+      'popup_eu_only_js'     => !empty($config->get('eu_only_js')) ? $config->get('eu_only_js') : FALSE,
+      'cookie_lifetime'      => $config->get('cookie_lifetime'),
+      'disagree_do_not_show_popup'         => !empty($config->get('disagree_do_not_show_popup')) ? $config->get('disagree_do_not_show_popup') : FALSE,
+    );
 
     $attachments['#attached']['drupalSettings']['eu_cookie_compliance'] = $data['variables'];
-    $attachments['#attached']['library'][] = 'eu_cookie_compliance/eu_cookie_compliance';
+    if ($config->get('use_bare_css')) {
+      $attachments['#attached']['library'][] = 'eu_cookie_compliance/eu_cookie_compliance_bare';
+    }
+    else {
+      $attachments['#attached']['library'][] = 'eu_cookie_compliance/eu_cookie_compliance';
+    }
+    // Add inline css.
+    $attachments['#attached']['html_head'][] = [
+      [
+        '#tag' => 'style',
+        '#value' => $data['css'],
+      ],
+      'eu-cookie-compliance-css',
+    ];
     $cache_tags = isset($attachments['#cache']['tags']) ? $attachments['#cache']['tags'] : [];
     $attachments['#cache']['tags'] = Cache::mergeTags($cache_tags, $config->getCacheTags());
   }
@@ -160,7 +241,7 @@ function eu_cookie_compliance_theme($existing, $type, $theme, $path) {
       'variables' => array(
         'message' => NULL,
         'agree_button' => NULL,
-        'disagree_button' => NULL
+        'disagree_button' => NULL,
       ),
     ),
     'eu_cookie_compliance_popup_agreed' => array(
@@ -168,7 +249,7 @@ function eu_cookie_compliance_theme($existing, $type, $theme, $path) {
       'variables' => array(
         'message' => NULL,
         'hide_button' => NULL,
-        'find_more_button' => NULL
+        'find_more_button' => NULL,
       ),
     ),
   );
@@ -176,9 +257,80 @@ function eu_cookie_compliance_theme($existing, $type, $theme, $path) {
 
 /**
  * Validate field for a HEX value if a value is set.
+ *
+ * @param array $element
+ *   Element.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ *   Form State Interface.
  */
 function eu_cookie_compliance_validate_hex($element, FormStateInterface &$form_state) {
   if (!empty($element['#value']) && !preg_match('/^[0-9a-fA-F]{3,6}$/', $element['#value'])) {
     $form_state->setError($element, t('%name must be a HEX value (without leading #) or empty.', array('%name' => $element['#title'])));
   }
 }
+
+/**
+ * Check if the user is in the EU.
+ */
+function eu_cookie_compliance_user_in_eu() {
+  $geoip_match = FALSE;
+  $eu_countries_default = array(
+    NULL, 'BE', 'BG', 'CZ', 'DK', 'DE', 'EE', 'IE', 'EL', 'ES', 'FR', 'HR',
+    'IT', 'CY', 'LV', 'LT', 'LU', 'HU', 'MT', 'NL', 'AT', 'PL', 'PT', 'RO',
+    'SI', 'SK', 'FI', 'SE', 'UK', 'GB', 'NO',
+  );
+  // Allow custom array of countries to be loaded from settings.php, defaulting
+  // to the array above.
+  $config = Drupal::config('eu_cookie_compliance.settings');
+  $eu_countries = !empty($config->get('eu_countries')) ? $config->get('eu_countries') : $eu_countries_default;
+
+  $ip_address = Drupal::request()->getClientIp();
+
+  $country_code = extension_loaded('geoip') ? geoip_country_code_by_name($ip_address) : '';
+  if (Drupal::moduleHandler()->moduleExists('smart_ip')) {
+    $smart_ip_session = SmartIp::query($ip_address);
+    $country_code = isset($smart_ip_session['countryCode']) ? $smart_ip_session['countryCode'] : NULL;
+  }
+  if (in_array($country_code, $eu_countries) || $country_code == '' || $country_code == '-') {
+    $geoip_match = TRUE;
+  }
+
+  return array(
+    'country' => $country_code,
+    'in_eu' => $geoip_match,
+  );
+}
+
+/**
+ * Attempt to find the cookie/privacy policy by searching for common titles.
+ *
+ * @return bool|string
+ *   URL to the node if found, otherwise FALSE.
+ */
+function _eu_cookie_compliance_find_privacy_policy() {
+  $pattern = 'privacy|privacy +policy|cookie +policy|terms +of +use|terms +of +service|terms +and +conditions';
+
+  $connection = Database::getConnection();
+  // Select operator based on the database type.
+  switch ($connection->databaseType()) {
+    case 'pgsql':
+      $op = '~*';
+      break;
+
+    case 'sqlite':
+      $op = 'REGEXP';
+      break;
+
+    default:
+      $op = 'RLIKE';
+  }
+
+  $query = \Drupal::entityQuery('node')
+    ->condition('title', $pattern, $op);
+
+  $result = $query->execute();
+  if (!empty($result)) {
+    return ('/node/' . array_shift($result));
+  }
+  return FALSE;
+}