<?php /** * @file * EU cookie compliance primary module file. * * 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. * * Author: Marcin Pajdzik */ /** * Implements hook_menu(). */ function eu_cookie_compliance_menu() { $items['admin/config/system/eu-cookie-compliance'] = array( 'title' => 'EU Cookie Compliance', 'description' => 'Make your web site compliant with the EU Directive on Privacy and Electronic Communications.', 'page callback' => 'drupal_get_form', 'page arguments' => array('eu_cookie_compliance_admin_form'), 'access arguments' => array('administer EU Cookie Compliance popup'), 'file' => 'eu_cookie_compliance.admin.inc', ); $items['admin/config/system/eu-cookie-compliance/settings'] = array( 'title' => 'Settings', 'description' => 'Configure the cookie consent popup.', 'weight' => 0, 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/config/system/eu-cookie-compliance/categories'] = array( 'title' => 'Categories', 'description' => 'Administer cookie categories.', 'page callback' => 'drupal_get_form', 'page arguments' => array('eu_cookie_compliance_admin_categories_form'), 'access arguments' => array('administer EU Cookie Compliance categories'), 'file' => 'eu_cookie_compliance.admin.categories.inc', 'weight' => 1, 'type' => MENU_LOCAL_TASK, ); $items['admin/config/system/eu-cookie-compliance/categories/add'] = array( 'title' => 'Add new category', 'page callback' => 'drupal_get_form', 'page arguments' => array('eu_cookie_compliance_admin_category_form'), 'access arguments' => array('administer EU Cookie Compliance categories'), 'file' => 'eu_cookie_compliance.admin.categories.inc', ); $items['admin/config/system/eu-cookie-compliance/categories/%/edit'] = array( 'title' => 'Edit', 'page callback' => 'drupal_get_form', 'page arguments' => array('eu_cookie_compliance_admin_category_form', 5), 'access arguments' => array('administer EU Cookie Compliance categories'), 'file' => 'eu_cookie_compliance.admin.categories.inc', ); $items['admin/config/system/eu-cookie-compliance/categories/%/delete'] = array( 'title' => 'Delete', 'page callback' => 'drupal_get_form', 'page arguments' => array( 'eu_cookie_compliance_admin_category_delete_form', 5, ), 'access arguments' => array('administer EU Cookie Compliance categories'), 'file' => 'eu_cookie_compliance.admin.categories.inc', ); $items['eu-cookie-compliance/store_consent/%'] = array( 'title' => 'EU Cookie Compliance store consent', 'page callback' => 'eu_cookie_compliance_store_consent', 'page arguments' => array(2), 'access arguments' => array('display EU Cookie Compliance popup'), 'type' => MENU_CALLBACK, ); if (module_exists('geoip') || module_exists('smart_ip') || extension_loaded('geoip')) { $items['eu-cookie-compliance-check'] = array( 'title' => 'Check if visit is from EU', 'page callback' => 'eu_cookie_compliance_json', 'type' => MENU_CALLBACK, 'access arguments' => array('display EU Cookie Compliance popup'), ); } if (eu_cookie_compliance_get_settings('withdraw_enabled')) { // Cookie settings menu item suggestion which can be found in the // 'navigation' menu. // Toggles the withdraw banner using JavaScript. Because Drupal 7 doesn't // allow menu items without link and callback, we use a dummy redirect to // the privacy policy instead as fragment fallback. $items['#editCookieSettings'] = array( 'title' => 'Cookie settings', 'description' => 'Toggle the cookie consent settings tray.', 'page callback' => 'eu_cookie_compliance_privacy_policy_redirect', 'type' => MENU_SUGGESTED_ITEM, 'access arguments' => array('display EU Cookie Compliance popup'), 'menu_name' => 'navigation', 'options' => array( 'external' => TRUE, // Pseudo. 'fragment' => 'editCookieSettings', 'attributes' => array( 'class' => 'eu-cookie-compliance-toggle-withdraw-banner', 'rel' => 'nofollow', ), ), ); } return $items; } /** * Implements hook_action_info(). */ function eu_cookie_compliance_admin_access() { return user_access('administer EU Cookie Compliance categories') || user_access('administer EU Cookie Compliance popup'); } /** * Implements hook_ctools_plugin_directory(). */ function eu_cookie_compliance_ctools_plugin_directory($module, $plugin) { if ($module === 'eu_cookie_compliance' && !empty($plugin)) { return 'plugins/' . $plugin; } } /** * Implements hook_ctools_plugin_type(). */ function eu_cookie_compliance_ctools_plugin_type() { $plugins = array(); $plugins['consent_storage'] = array(); return $plugins; } /** * Implements hook_page_alter(). */ function eu_cookie_compliance_page_alter(&$page) { if (isset($page['#theme']) && $page['#theme'] === 'panels_everywhere_page') { eu_cookie_compliance_page_build($page); } } /** * Implements hook_page_build(). */ function eu_cookie_compliance_page_build(&$page) { $popup_settings = eu_cookie_compliance_get_settings(); global $theme, $user; // Check Add/Remove domains. $domain_allow = TRUE; $domain_option = isset($popup_settings['domains_option']) ? $popup_settings['domains_option'] : 1; if (!empty($popup_settings['domains_list'])) { global $base_url; $domains_list = str_replace(array("\r\n", "\r"), "\n", $popup_settings['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; } if (!$domain_option && !$domain_match) { $domain_allow = FALSE; } } // Check exclude paths. $path_match = FALSE; if (isset($popup_settings['exclude_paths'])) { $path = drupal_strtolower(drupal_get_path_alias($_GET['q'])); $path_match = drupal_match_path($path, $popup_settings['exclude_paths']); if ($path != $_GET['q']) { $path_match = $path_match || drupal_match_path($_GET['q'], $popup_settings['exclude_paths']); } drupal_alter('eu_cookie_compliance_path_match', $path_match, $path, $popup_settings['exclude_paths']); } // Check exclude admin pages. $admin_page_match = FALSE; if (!empty($popup_settings['exclude_admin_pages']) && path_is_admin(current_path())) { $admin_page_match = TRUE; } $geoip_match = array('in_eu' => TRUE); if (isset($popup_settings['eu_only']) && $popup_settings['eu_only']) { $geoip_match = eu_cookie_compliance_user_in_eu(); } // Allow other modules to alter the geo IP matching logic. drupal_alter('eu_cookie_compliance_geoip_match', $geoip_match); $uid1_match = TRUE; if ($user->uid == 1 && isset($popup_settings['exclude_uid_1']) && $popup_settings['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_alter('eu_cookie_compliance_show_popup', $modules_allow_popup); if (!empty($popup_settings['popup_enabled']) && user_access('display EU Cookie Compliance popup') && $geoip_match['in_eu'] && $domain_allow && !$path_match && !$admin_page_match && $uid1_match && $modules_allow_popup) { global $language; $method = !empty($popup_settings['method']) ? $popup_settings['method'] : 'opt_in'; // Set default template (based on earlier version of module). if (!isset($popup_settings['info_template'])) { $popup_settings['info_template'] = $method === 'default' ? 'legacy' : 'new'; } // Array storage for caching full client data. if (module_exists('domain')) { global $_domain; $cid = 'eu_cookie_compliance_client_settings:' . $language->language . ':' . $theme . ':' . $_domain['domain_id']; } else { $cid = 'eu_cookie_compliance_client_settings:' . $language->language . ':' . $theme; } $data = array(); if ($cache = cache_get($cid, 'cache')) { $data = $cache->data; } else { // Initialize some needed popup settings messages. $popup_settings_messages = array( 'popup_agree_button_message' => t('Accept'), 'popup_disagree_button_message' => t('More info'), 'popup_hide_button_message' => t('Hide'), 'popup_find_more_button_message' => t('More info'), ); foreach ($popup_settings_messages as $key => $value) { if (!isset($popup_settings[$key])) { $popup_settings[$key] = $value; } } // Color overrides. $data['css'] = ''; $position = $popup_settings['popup_position'] ? 'top' : 'bottom'; if (empty($popup_settings['use_bare_css'])) { if ($popup_settings['popup_bg_hex'] != '') { $data['css'] = '#sliding-popup.sliding-popup-' . $position . ', #sliding-popup.sliding-popup-' . $position . ' .eu-cookie-withdraw-banner, .eu-cookie-withdraw-tab {background:#' . check_plain($popup_settings['popup_bg_hex']) . ';} #sliding-popup.sliding-popup-' . $position . '.eu-cookie-withdraw-wrapper {background: transparent}'; } if ($popup_settings['popup_text_hex'] != '') { $data['css'] .= '#sliding-popup .popup-content #popup-text h1, #sliding-popup .popup-content #popup-text h2, #sliding-popup .popup-content #popup-text h3, #sliding-popup .popup-content #popup-text p, #sliding-popup label, #sliding-popup div, .eu-cookie-compliance-secondary-button, .eu-cookie-withdraw-tab {color: #' . check_plain($popup_settings['popup_text_hex']) . ' !important;} .eu-cookie-withdraw-tab { border-color: #' . check_plain($popup_settings['popup_text_hex']) . ';}'; } if ($popup_settings['info_template'] === 'new') { $data['css'] .= '.eu-cookie-compliance-more-button {color: #' . check_plain($popup_settings['popup_text_hex']) . ' !important;}'; } } if (!empty($popup_settings['popup_position']) && $popup_settings['popup_position'] && !empty($popup_settings['fixed_top_position']) && $popup_settings['fixed_top_position']) { $data['css'] .= '#sliding-popup.sliding-popup-top { position: fixed; }'; } $show_disagree_buttons = TRUE; if (array_key_exists('show_disagree_button', $popup_settings) && $popup_settings['show_disagree_button'] === 0) { $show_disagree_buttons = FALSE; } if ($method === 'auto') { $dnt = isset($_SERVER['HTTP_DNT']) ? $_SERVER['HTTP_DNT'] : NULL; if ((int) $dnt === 0 && $dnt !== NULL) { $method = 'default'; } else { $method = 'opt_in'; } } $withdraw_button_on_info_popup = $popup_settings['withdraw_enabled']; $save_preferences_button_label = ''; $info_templates = array( 'legacy' => 'eu_cookie_compliance_popup_info_consent_default', 'new' => 'eu_cookie_compliance_popup_info', ); $tertiary_button_label = ''; $tertiary_button_class = ''; $close_button_enabled = FALSE; switch ($method) { case 'default': $click_confirmation = isset($popup_settings['popup_clicking_confirmation']) ? $popup_settings['popup_clicking_confirmation'] : TRUE; $scroll_confirmation = isset($popup_settings['popup_scrolling_confirmation']) ? $popup_settings['popup_scrolling_confirmation'] : FALSE; $primary_button_label = !empty($popup_settings['popup_agree_button_message']) ? filter_xss($popup_settings['popup_agree_button_message']) : 'Accept'; $primary_button_class = 'agree-button eu-cookie-compliance-default-button'; $secondary_button_label = ''; $secondary_button_class = ''; $privacy_settings_tab_label = FALSE; $withdraw_button_on_info_popup = FALSE; $popup_info_template = $info_templates[$popup_settings['info_template']]; break; case 'opt_in': $click_confirmation = FALSE; $scroll_confirmation = FALSE; $primary_button_label = !empty($popup_settings['popup_agree_button_message']) ? filter_xss($popup_settings['popup_agree_button_message']) : 'Accept'; $primary_button_class = 'agree-button eu-cookie-compliance-secondary-button'; $secondary_button_label = !empty($popup_settings['disagree_button_label']) ? filter_xss($popup_settings['disagree_button_label']) : 'Decline'; $secondary_button_class = 'decline-button eu-cookie-compliance-default-button'; $popup_info_template = 'eu_cookie_compliance_popup_info'; $privacy_settings_tab_label = $withdraw_button_on_info_popup ? $popup_settings['withdraw_tab_button_label'] : FALSE; break; case 'categories': $click_confirmation = FALSE; $scroll_confirmation = FALSE; if ($popup_settings['enable_save_preferences_button']) { $save_preferences_button_label = $popup_settings['save_preferences_button_label']; $primary_button_label = $popup_settings['accept_all_categories_button_label']; } else { $save_preferences_button_label = ''; $primary_button_label = $popup_settings['popup_agree_button_message']; } $primary_button_class = 'agree-button eu-cookie-compliance-default-button'; $secondary_button_label = $popup_settings['withdraw_action_button_label']; $secondary_button_class = 'eu-cookie-withdraw-button eu-cookie-compliance-hidden'; $tertiary_button_label = $popup_settings['reject_button_label']; $tertiary_button_class = 'eu-cookie-compliance-default-button eu-cookie-compliance-reject-button'; $close_button_enabled = !empty($popup_settings['close_button_enabled']) ? filter_xss($popup_settings['close_button_enabled']) : FALSE; $privacy_settings_tab_label = $withdraw_button_on_info_popup ? $popup_settings['withdraw_tab_button_label'] : FALSE; $popup_info_template = $info_templates[$popup_settings['info_template']]; break; case 'opt_out': $click_confirmation = FALSE; $scroll_confirmation = FALSE; $primary_button_label = !empty($popup_settings['disagree_button_label']) ? filter_xss($popup_settings['disagree_button_label']) : 'Decline'; $primary_button_class = 'decline-button eu-cookie-compliance-secondary-button'; $secondary_button_label = !empty($popup_settings['popup_agree_button_message']) ? filter_xss($popup_settings['popup_agree_button_message']) : 'Accept'; $secondary_button_class = 'agree-button eu-cookie-compliance-default-button'; $privacy_settings_tab_label = $withdraw_button_on_info_popup ? $popup_settings['withdraw_tab_button_label'] : FALSE; $popup_info_template = 'eu_cookie_compliance_popup_info'; break; } if (module_exists('translation') && isset($popup_settings['popup_link_translate']) && $popup_settings['popup_link_translate']) { $node_parts = explode('/', $popup_settings['popup_link']); if (isset($node_parts[1])) { $node = node_load($node_parts[1]); if ($node) { $translations = translation_node_get_translations($node->tnid); if (isset($translations[$language->language])) { $popup_settings['popup_link'] = $node_parts[0] . '/' . $translations[$language->language]->nid; } } } } $popup_text_info = str_replace(array("\r", "\n"), '', filter_xss_admin($popup_settings['popup_info']['value'])); $popup_text_agreed = str_replace(array("\r", "\n"), '', filter_xss_admin($popup_settings['popup_agreed']['value'])); $mobile_popup_text_info = str_replace(array("\r", "\n"), '', !empty($popup_settings['mobile_popup_info']['value']) ? filter_xss_admin($popup_settings['mobile_popup_info']['value']) : ''); $withdraw_markup = str_replace(array("\r", "\n"), '', !empty($popup_settings['withdraw_message']['value']) ? filter_xss_admin($popup_settings['withdraw_message']['value']) : ''); $cookie_categories = variable_get('eu_cookie_compliance_categories', array()); $cookie_categories = $method === 'categories' ? $cookie_categories : FALSE; $html_info = theme($popup_info_template, array( 'message' => check_markup($popup_text_info, $popup_settings['popup_info']['format'], FALSE), 'agree_button' => $primary_button_label, 'disagree_button' => $show_disagree_buttons ? filter_xss($popup_settings['popup_disagree_button_message']) : FALSE, 'secondary_button_label' => $secondary_button_label, 'primary_button_class' => $primary_button_class, 'secondary_button_class' => $secondary_button_class, 'tertiary_button_label' => $tertiary_button_label, 'tertiary_button_class' => $tertiary_button_class, 'close_button_enabled' => $close_button_enabled, 'cookie_categories' => $cookie_categories, 'save_preferences_button_label' => $save_preferences_button_label, 'privacy_settings_tab_label' => $privacy_settings_tab_label, 'withdraw_button_on_info_popup' => $withdraw_button_on_info_popup, 'method' => $method, )); $mobile_html_info = theme($popup_info_template, array( 'message' => check_markup($mobile_popup_text_info, $popup_settings['popup_info']['format'], FALSE), 'agree_button' => $primary_button_label, 'disagree_button' => $show_disagree_buttons ? filter_xss($popup_settings['popup_disagree_button_message']) : FALSE, 'secondary_button_label' => $secondary_button_label, 'primary_button_class' => $primary_button_class, 'secondary_button_class' => $secondary_button_class, 'tertiary_button_label' => $tertiary_button_label, 'tertiary_button_class' => $tertiary_button_class, 'close_button_enabled' => $close_button_enabled, 'cookie_categories' => $cookie_categories, 'save_preferences_button_label' => $save_preferences_button_label, 'privacy_settings_tab_label' => $privacy_settings_tab_label, 'withdraw_button_on_info_popup' => $withdraw_button_on_info_popup, 'method' => $method, )); $html_agreed = theme('eu_cookie_compliance_popup_agreed', array( 'message' => check_markup($popup_text_agreed, $popup_settings['popup_agreed']['format'], FALSE), 'hide_button' => filter_xss($popup_settings['popup_hide_button_message']), 'find_more_button' => $show_disagree_buttons ? filter_xss($popup_settings['popup_find_more_button_message']) : FALSE, )); $withdraw_markup = theme('eu_cookie_compliance_withdraw', array( 'message' => check_markup($withdraw_markup, $popup_settings['withdraw_message']['format'], FALSE), 'withdraw_tab_button_label' => !empty($popup_settings['withdraw_tab_button_label']) ? filter_xss($popup_settings['withdraw_tab_button_label']) : '', 'withdraw_action_button_label' => !empty($popup_settings['withdraw_action_button_label']) ? filter_xss($popup_settings['withdraw_action_button_label']) : '', )); // Check if theme_debug is enabled. if (variable_get('theme_debug') == 1) { // Remove unwanted HTML comments. $html_info = preg_replace('/<!--(.|\s)*?-->/', '', $html_info); $mobile_html_info = preg_replace('/<!--(.|\s)*?-->/', '', $mobile_html_info); $html_agreed = preg_replace('/<!--(.|\s)*?-->/', '', $html_agreed); $withdraw_markup = preg_replace('/<!--(.|\s)*?-->/', '', $withdraw_markup); } $cookie_categories = variable_get('eu_cookie_compliance_categories', array()); $data['variables'] = array( 'cookie_policy_version' => isset($popup_settings['cookie_policy_version']) ? $popup_settings['cookie_policy_version'] : '1.0.0', 'popup_enabled' => $popup_settings['popup_enabled'], 'popup_agreed_enabled' => $popup_settings['popup_agreed_enabled'], 'popup_hide_agreed' => isset($popup_settings['popup_hide_agreed']) ? $popup_settings['popup_hide_agreed'] : FALSE, 'popup_clicking_confirmation' => $click_confirmation, 'popup_scrolling_confirmation' => $scroll_confirmation, 'popup_html_info' => empty($html_info) ? FALSE : trim($html_info), 'use_mobile_message' => !empty($popup_settings['use_mobile_message']) ? $popup_settings['use_mobile_message'] : FALSE, 'mobile_popup_html_info' => $popup_settings['popup_enabled'] ? $mobile_html_info : FALSE, 'mobile_breakpoint' => !empty($popup_settings['mobile_breakpoint']) ? $popup_settings['mobile_breakpoint'] : '768', 'popup_html_agreed' => empty($html_agreed) ? FALSE : trim($html_agreed), 'popup_use_bare_css' => empty($popup_settings['use_bare_css']) ? FALSE : $popup_settings['use_bare_css'], 'popup_height' => ($popup_settings['popup_height'] !== '') ? (int) $popup_settings['popup_height'] : 'auto', 'popup_width' => (drupal_substr($popup_settings['popup_width'], -1) === '%') ? $popup_settings['popup_width'] : (int) $popup_settings['popup_width'], 'popup_delay' => (int) ($popup_settings['popup_delay']), 'popup_link' => url(token_replace($popup_settings['popup_link'])), 'popup_link_new_window' => isset($popup_settings['popup_link_new_window']) ? $popup_settings['popup_link_new_window'] : 1, 'popup_position' => empty($popup_settings['popup_position']) ? NULL : $popup_settings['popup_position'], 'fixed_top_position' => empty($popup_settings['fixed_top_position']) ? FALSE : $popup_settings['fixed_top_position'], 'popup_language' => $language->language, 'store_consent' => $popup_settings['consent_storage_method'] !== 'do_not_store', 'better_support_for_screen_readers' => isset($popup_settings['better_support_for_screen_readers']) ? $popup_settings['better_support_for_screen_readers'] : 0, 'reload_page' => isset($popup_settings['reload_page']) ? $popup_settings['reload_page'] : 0, 'domain' => variable_get('eu_cookie_compliance_domain', ''), 'domain_all_sites' => variable_get('eu_cookie_compliance_domain_all_sites'), 'popup_eu_only_js' => isset($popup_settings['eu_only_js']) ? $popup_settings['eu_only_js'] : 0, 'cookie_lifetime' => variable_get('eu_cookie_compliance_cookie_lifetime', 100), 'cookie_session' => empty($popup_settings['cookie_session']) ? FALSE : $popup_settings['cookie_session'], 'disagree_do_not_show_popup' => isset($popup_settings['disagree_do_not_show_popup']) ? $popup_settings['disagree_do_not_show_popup'] : 0, 'method' => $method, 'allowed_cookies' => !empty($popup_settings['allowed_cookies']) ? $popup_settings['allowed_cookies'] : '', 'withdraw_markup' => $withdraw_markup, 'withdraw_enabled' => !empty($popup_settings['withdraw_enabled']) ? $popup_settings['withdraw_enabled'] : FALSE, 'withdraw_button_on_info_popup' => $popup_settings['withdraw_button_on_info_popup'], 'cookie_categories' => is_array($cookie_categories) ? array_keys($cookie_categories) : FALSE, 'cookie_categories_details' => $cookie_categories, 'enable_save_preferences_button' => $popup_settings['enable_save_preferences_button'], 'cookie_name' => !empty($popup_settings['cookie_name']) ? $popup_settings['cookie_name'] : '', 'cookie_value_disagreed' => !empty($popup_settings['cookie_value_disagreed']) ? $popup_settings['cookie_value_disagreed'] : '0', 'cookie_value_agreed_show_thank_you' => !empty($popup_settings['cookie_value_agreed_show_thank_you']) ? $popup_settings['cookie_value_agreed_show_thank_you'] : '1', 'cookie_value_agreed' => !empty($popup_settings['cookie_value_agreed']) ? $popup_settings['cookie_value_agreed'] : '2', 'containing_element' => !empty($popup_settings['containing_element']) ? $popup_settings['containing_element'] : 'body', 'automatic_cookies_removal' => !empty($popup_settings['automatic_cookies_removal']) ? $popup_settings['automatic_cookies_removal'] : '', 'close_button_action' => !empty($popup_settings['close_button_action']) ? $popup_settings['close_button_action'] : '', ); // For some reason, we're getting the wrong language when editing the // localized form, so we shouldn't cache. if (empty($_GET['variable_realm_key_language'])) { cache_set($cid, $data, 'cache', CACHE_PERMANENT); } } if ($data['css']) { $cid = md5($data['css']); ctools_include('css'); $filename = ctools_css_retrieve($cid); if (empty($filename)) { $filename = ctools_css_store($cid, $data['css'], FALSE); } drupal_add_css($filename, array( 'weight' => 1000, )); } $script_scope = isset($popup_settings['script_scope']) ? $popup_settings['script_scope'] : 'footer'; // Add inline javascript. $disabled_javascripts = isset($popup_settings['disabled_javascripts']) ? str_replace('&', '&', filter_xss($popup_settings['disabled_javascripts'])) : ''; $load_disabled_scripts = ''; if ($disabled_javascripts != '') { $load_disabled_scripts = ''; $disabled_javascripts = _eu_cookie_compliance_explode_multiple_lines($disabled_javascripts, FALSE); $disabled_javascripts = array_filter($disabled_javascripts, 'strlen'); foreach ($disabled_javascripts as $script) { $parts = explode(':', $script); $category = NULL; if (count($parts) > 1 && $popup_settings['method'] === 'categories') { $category = array_shift($parts); } $script = implode(':', $parts); // Split the string if a | is present. // The second parameter (after the |) will be used to trigger a script // attach. $attach_name = ''; if (strpos($script, '|') !== FALSE) { // Swallow a notice in case there is no behavior name. @list($script, $attach_name) = explode('|', $script); } _eu_cookie_compliance_convert_relative_uri($script); if (substr($script, 0, 4) !== 'http' && substr($script, 0, 2) !== '//') { $script = '/' . $script; } if ($category !== NULL) { $load_disabled_scripts .= 'if (category === "' . $category . '") {'; } $load_disabled_scripts .= 'var scriptTag = document.createElement("script");'; $load_disabled_scripts .= 'scriptTag.src = ' . drupal_json_encode($script) . ';'; $load_disabled_scripts .= 'document.body.appendChild(scriptTag);'; // The script will not immediately load, so we need to trigger the // attach in an interval function. if ($attach_name) { $load_disabled_scripts .= 'var EUCookieInterval' . $attach_name . '= setInterval(function() { if (Drupal.behaviors.' . $attach_name . ' !== undefined) { Drupal.behaviors.' . $attach_name . '.attach(document, Drupal.settings);clearInterval(EUCookieInterval' . $attach_name . ')};}, 100);'; } if ($category !== NULL) { $load_disabled_scripts .= '}'; } } } if ($load_disabled_scripts) { drupal_add_js('window.euCookieComplianceLoadScripts = function(category) {' . $load_disabled_scripts . '}', array( 'type' => 'inline', 'scope' => $script_scope, )); } // Add the cookie name inline, since Drupal.settings will not be available // if the script is loaded in the header. drupal_add_js('window.eu_cookie_compliance_cookie_name = ' . drupal_json_encode(!empty($popup_settings['cookie_name']) ? $popup_settings['cookie_name'] : '') . ';', array( 'type' => 'inline', 'scope' => $script_scope, )); drupal_add_js(array('eu_cookie_compliance' => $data['variables']), array( 'type' => 'setting', 'scope' => $script_scope, )); if (!isset($popup_settings['use_bare_css']) || $popup_settings['use_bare_css'] == 0) { drupal_add_css(drupal_get_path('module', 'eu_cookie_compliance') . '/css/eu_cookie_compliance.css'); } else { drupal_add_css(drupal_get_path('module', 'eu_cookie_compliance') . '/css/eu_cookie_compliance.bare.css'); } drupal_add_library('system', 'jquery.cookie'); drupal_add_js(drupal_get_path('module', 'eu_cookie_compliance') . '/js/eu_cookie_compliance.min.js', array( 'type' => 'file', 'scope' => $script_scope, 'group' => JS_DEFAULT, 'weight' => 0, 'defer' => TRUE, )); } } /** * Implements hook_permission(). */ function eu_cookie_compliance_permission() { return array( 'administer EU Cookie Compliance popup' => array( 'title' => 'Administer EU Cookie Compliance banner', ), 'administer EU Cookie Compliance categories' => array( 'title' => 'Administer EU Cookie Compliance categories', ), 'display EU Cookie Compliance popup' => array( 'title' => 'Display EU Cookie Compliance banner', ), ); } /** * Implements hook_theme(). */ function eu_cookie_compliance_theme() { $path = drupal_get_path('module', 'eu_cookie_compliance') . '/theme'; return array( 'eu_cookie_compliance_popup_info_consent_default' => array( 'template' => 'eu-cookie-compliance-popup-info-consent-default', 'variables' => array( 'message' => NULL, 'agree_button' => NULL, 'disagree_button' => NULL, 'secondary_button_label' => NULL, 'primary_button_class' => NULL, 'secondary_button_class' => NULL, 'save_preferences_button_label' => NULL, 'privacy_settings_tab_label' => NULL, 'withdraw_button_on_info_popup' => FALSE, 'method' => 'default', ), 'path' => $path, ), 'eu_cookie_compliance_popup_info' => array( 'template' => 'eu-cookie-compliance-popup-info', 'variables' => array( 'message' => NULL, 'agree_button' => NULL, 'disagree_button' => NULL, 'secondary_button_label' => NULL, 'primary_button_class' => NULL, 'secondary_button_class' => NULL, 'privacy_settings_tab_label' => NULL, 'tertiary_button_label' => NULL, 'tertiary_button_class' => NULL, 'close_button_enabled' => NULL, 'method' => 'default', ), 'path' => $path, ), 'eu_cookie_compliance_popup_agreed' => array( 'template' => 'eu-cookie-compliance-popup-agreed', 'variables' => array( 'message' => NULL, 'hide_button' => NULL, 'find_more_button' => NULL, ), 'path' => $path, ), 'eu_cookie_compliance_withdraw' => array( 'template' => 'eu-cookie-compliance-withdraw', 'variables' => array( 'withdraw_tab_button_label' => NULL, 'message' => NULL, 'withdraw_action_button_label' => NULL, ), 'path' => $path, ), 'eu_cookie_compliance_admin_categories_form' => array( 'render element' => 'form', ), ); } /** * Converts a multiline, list of key|label|description strings to an array. * * @param string $string * A multiline string containing key, label and optionally a description * separated by a pipe symbol. * * @return array * An array of categories the form * [key => ['label' => label, 'description' => description]] */ function _eu_cookie_compliance_extract_category_key_label_description($string) { $categories = array(); $list = explode("\n", $string); $list = array_map('trim', $list); $list = array_filter($list, 'strlen'); foreach ($list as $text) { $parts = explode('|', $text); $num_parts = count($parts); if ($num_parts >= 3) { $key = trim($parts[0]); $label = trim($parts[1]); $description = trim($parts[2]); } elseif ($num_parts === 2) { $key = trim($parts[0]); $label = trim($parts[1]); $description = ''; } else { $key = $label = trim($parts[0]); $description = ''; } $categories[$key] = array('label' => $label, 'description' => $description); } return $categories; } /** * Retrieves settings from the database for a current language. * * @param string $setting * Setting to retrieve. * * @return string|null|array * The requested setting or an array of all settings. */ function eu_cookie_compliance_get_settings($setting = 'all') { $popup_settings = variable_get('eu_cookie_compliance', array()); if (!is_array($popup_settings)) { $popup_settings = array(); } // Ensure a default value exists for each setting. $popup_settings_dafaults = array( 'consent_storage_method' => 'do_not_store', 'enable_save_preferences_button' => NULL, 'show_disagree_button' => TRUE, 'withdraw_button_on_info_popup' => NULL, ); $popup_settings = array_merge($popup_settings_dafaults, $popup_settings); if ($setting === 'all') { return $popup_settings; } return isset($popup_settings[$setting]) ? $popup_settings[$setting] : NULL; } /** * Menu callback for return JSON EU visitor status. */ function eu_cookie_compliance_json() { $data = eu_cookie_compliance_user_in_eu(); // Allow other modules to alter the geo IP matching logic. drupal_alter('eu_cookie_compliance_geoip_match', $data); drupal_add_http_header('Cache-Control', 'private'); drupal_json_output($data); drupal_exit(); } /** * Check if the user is in the EU. */ function eu_cookie_compliance_user_in_eu() { $geoip_match = FALSE; $eu_countries_default = array( NULL, 'AT', 'AX', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'EL', 'ES', 'EU', 'FI', 'FR', 'GB', 'GF', 'GI', 'GP', 'GR', 'HR', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'ME', 'MF', 'MQ', 'MT', 'NL', 'NO', 'PL', 'PT', 'RE', 'RO', 'SE', 'SI', 'SK', 'YT', 'UK', ); // Allow custom array of countries to be loaded from settings.php, defaulting // to the array above. $eu_countries = variable_get('eu_cookie_compliance_eu_countries', $eu_countries_default); $country_code = extension_loaded('geoip') ? geoip_country_code_by_name(ip_address()) : ''; if (module_exists('geoip')) { $country_code = geoip_country_code(); } elseif (module_exists('smart_ip')) { $smart_ip_session = smart_ip_get_location(ip_address()); $country_code = isset($smart_ip_session['country_code']) ? $smart_ip_session['country_code'] : NULL; } // If the CloudFlare provided country header is available, use it as a // fallback. See: // https://support.cloudflare.com/hc/en-us/articles/200168236-What-does-Cloudflare-IP-Geolocation-do- if (empty($country_code) && (isset($_SERVER['HTTP_CF_IPCOUNTRY']))) { $country_code = $_SERVER['HTTP_CF_IPCOUNTRY']; } if (in_array($country_code, $eu_countries)) { $geoip_match = TRUE; } if ($country_code == '' || $country_code == '-') { $geoip_match = TRUE; } return array( 'country' => $country_code, 'in_eu' => $geoip_match, ); } /** * Clear cache for the eu_cookie_compliance settings. */ function eu_cookie_compliance_clear_caches() { $use_domain = FALSE; if (module_exists('domain')) { $domains = domain_domains(); $use_domain = TRUE; } // Clear cache for all enabled themes. $themes = system_rebuild_theme_data(); foreach ($themes as $theme) { if ($theme->status == 1) { if (module_exists('locale')) { $languages = locale_language_list(); } else { global $language; $current_language = !empty($language->language) ? $language->language : 'en'; $languages = array($current_language => $current_language); } foreach ($languages as $key => $current_language) { if ($use_domain) { foreach ($domains as $domain) { cache_clear_all('eu_cookie_compliance_client_settings:' . $key . ':' . $theme->name . ':' . $domain['domain_id'], 'cache', TRUE); } } else { cache_clear_all('eu_cookie_compliance_client_settings:' . $key . ':' . $theme->name, 'cache', TRUE); } } } } } /** * Implements hook_js_alter(). */ function eu_cookie_compliance_js_alter(&$javascript) { global $user; // Determine which array key holds the EUCC disabled script loader. $js_disabled_script_position = _eu_cookie_compliance_get_script_key($javascript); $popup_settings = eu_cookie_compliance_get_settings(); if (!empty($popup_settings['disabled_javascripts'])) { $disabled_javascripts = $popup_settings['disabled_javascripts']; $disabled_javascripts = _eu_cookie_compliance_explode_multiple_lines($disabled_javascripts, FALSE); $exclude_paths = explode(PHP_EOL, $popup_settings['exclude_paths']); $current_path = drupal_get_path_alias(); $path_is_excluded = FALSE; foreach ($exclude_paths as $path) { $path_clean = trim($path); if (drupal_match_path($current_path, $path_clean)) { $path_is_excluded = TRUE; } } if (!empty($popup_settings['exclude_admin_pages']) && path_is_admin($current_path)) { $path_is_excluded = TRUE; } if ((int) $user->uid === 1 && isset($popup_settings['exclude_uid_1']) && $popup_settings['exclude_uid_1']) { $path_is_excluded = TRUE; } if ($path_is_excluded === FALSE) { foreach ($disabled_javascripts as $script) { // Remove 'category:' if present. $parts = explode(':', $script, 2); // If the string after the colon starts with two slashes, it is not a // category but the schema of an absolute link. if (!empty($parts[1]) && strpos($parts[1], '//') !== 0) { $script = $parts[1]; } // Parse the string and drop the parameter that is a behavior name. if (strpos($script, '|') !== FALSE) { @list($script) = explode('|', $script); } // https://www.drupal.org/project/eu_cookie_compliance/issues/3205742 // Only add the script if it's actually present in the page scripts. if (array_key_exists($script, $javascript)) { unset($javascript[$script]); } elseif ($js_disabled_script_position !== FALSE) { _eu_cookie_compliance_remove_script_from_loader($javascript, $script, $js_disabled_script_position); } } } } } /** * Find the array key that holds the disabled script loader. * * @param array $javascript * The javascripts. * * @return int|bool * The array key where the script loader lives, FALSE if not found. */ function _eu_cookie_compliance_get_script_key(array $javascript) { foreach ($javascript as $key => $script) { if (!empty($script['data']) && !is_array($script['data']) && strpos($script['data'], 'window.euCookieComplianceLoadScripts') !== FALSE) { return $key; } } return FALSE; } /** * Modifies the script loader to remove a script. * * @param array $javascript * The javascript array. * @param string $script * The script to remove. * @param int $js_disabled_script_position * The position of our script loader. */ function _eu_cookie_compliance_remove_script_from_loader(array &$javascript, $script, $js_disabled_script_position) { $string_to_find = 'var scriptTag = document.createElement("script");scriptTag.src = "' . str_replace('/', '\\/', $GLOBALS['base_url']) . '\/' . str_replace('/', '\\/', $script) . '";document.body.appendChild(scriptTag);'; $javascript[$js_disabled_script_position]['data'] = str_replace($string_to_find, '', $javascript[$js_disabled_script_position]['data']); } /** * Implements hook_library_alter(). */ function eu_cookie_compliance_library_alter(&$libraries, $module) { if ($module !== 'system') { return; } $libraries['jquery.cookie']['js']['misc/jquery.cookie.js']['data'] = drupal_get_path('module', 'eu_cookie_compliance') . '/js/jquery.cookie-1.4.1.min.js'; $libraries['jquery.cookie']['version'] = '1.4.1'; } /** * Implements hook_block_info(). */ function eu_cookie_compliance_block_info() { // Cookie Settings button block: $blocks['cookie_settings_button'] = array( 'info' => t('EU Cookie Compliance: "Cookie settings" Button'), 'cache' => DRUPAL_CACHE_GLOBAL, ); return $blocks; } /** * Implements hook_block_view(). */ function eu_cookie_compliance_block_view($delta = '') { $block = array(); switch ($delta) { // Cookie Settings button block: case 'cookie_settings_button': $block['subject'] = t('Cookie settings'); if (eu_cookie_compliance_get_settings('withdraw_enabled')) { $block['content'] = array( '#type' => 'link', '#title' => t('Cookie settings'), '#href' => '#', '#attributes' => array( 'class' => 'button eu-cookie-compliance-toggle-withdraw-banner', 'onclick' => 'Drupal.eu_cookie_compliance.toggleWithdrawBanner(); return false;', 'rel' => 'nofollow', ), ); } else { $block['content'] = t('This block requires the "@withdraw_enabled_setting_name" to be enabled in !eu_cookie_compliance_settings.', array( '@withdraw_enabled_setting_name' => t('Enable floating privacy settings tab and withdraw consent banner'), '!eu_cookie_compliance_settings' => l(t('EU Cookie Compliance settings'), 'admin/config/system/eu-cookie-compliance'), )); } break; } return $block; } /** * Splits a return delimited text string into an array. * * @param string $text * Text to split. * @param bool $convert * Whether to convert the strings to relative URLs. * * @return array * Text split into an array. */ function _eu_cookie_compliance_explode_multiple_lines($text, $convert = TRUE) { $text = explode("\r\n", $text); if (count($text) == 1) { $text = explode("\r", $text[0]); } if (count($text) == 1) { $text = explode("\n", $text[0]); } if ($convert) { array_walk($text, '_eu_cookie_compliance_convert_relative_uri'); } return $text; } /** * Callback function for SQLite regular expression query. * * @param string $pattern * Search pattern. * @param string $data * Data coming from SQLite. * * @return bool * Returns whether the pattern can be found in the data. */ function _eu_cookie_compliance_sqlite_regex_query_builder($pattern, $data) { return (preg_match(sprintf('%1$s%2$s%1$s%3$s', '~', $pattern, 'isuS'), $data) > 0); } /** * Lookup the latest revision of the privacy policy node (if present). * * @return int|bool * The node revision id or FALSE. */ function _eu_cookie_compliance_get_current_policy_node_revision() { $cookie_policy_path = eu_cookie_compliance_get_settings('popup_link'); $drupal_path = drupal_get_normal_path($cookie_policy_path); if (substr($drupal_path, 0, 5) === 'node/') { $drupal_path = explode('/', $drupal_path); $cookie_policy_node_id = $drupal_path[1]; $cookie_policy_node = node_load($cookie_policy_node_id); if (!empty($cookie_policy_node)) { return $cookie_policy_node->vid; } } return FALSE; } /** * Callback for the consent storage JSON call. * * @param string $type * The type of consent. 'banner' or form ID. */ function eu_cookie_compliance_store_consent($type) { ctools_include('plugins'); $type = check_plain($type); $consent_storage_method = eu_cookie_compliance_get_settings('consent_storage_method'); if ($consent_storage_method === 'do_not_store' || $consent_storage_method === '') { drupal_json_output(NULL); drupal_exit(); } // Get plugin. $consent_storage_plugin = ctools_get_plugins('eu_cookie_compliance', 'consent_storage', $consent_storage_method); if (!empty($consent_storage_plugin['consent_storage_callback'])) { $consent_storage_function = $consent_storage_plugin['consent_storage_callback']; if ($consent_storage_function !== '') { $result = $consent_storage_function($type); drupal_json_output($result); drupal_exit(); } } drupal_json_output(NULL); drupal_exit(); } /** * Menu callback for the 'Cookie settings' menu item suggestion. * * Because that menu item is a JavaScript function which toggles * Drupal.eu_cookie_compliance.toggleWithdrawBanner(); but Drupal 7 menu items * require a callback, this is mainly a dummy placeholder. * * To give it useful functionality, we redirect to the privacy policy page if * the placeholder link is accessed. */ function eu_cookie_compliance_privacy_policy_redirect() { $popup_link = url(token_replace(eu_cookie_compliance_get_settings('popup_link'))); if (!empty($popup_link)) { return drupal_goto($popup_link); } return drupal_goto('<front>'); } /** * Convert uri to relative path. * * Example public://file.js to /sites/default/files/file.js. * * @param string $element * Url to transform. */ function _eu_cookie_compliance_convert_relative_uri(&$element) { $element = preg_replace('/^\//', '', file_create_url($element)); } /** * Implements hook_views_api(). */ function eu_cookie_compliance_views_api() { return array( 'api' => 3, 'path' => drupal_get_path('module', 'eu_cookie_compliance') . '/includes/views', ); } /** * Implements hook_variable_settings_form_alter(). */ function eu_cookie_compliance_variable_settings_form_alter(&$form, &$form_state, $form_id) { // If both these submit callback are present, make sure // variable_realm_variable_settings_form_submit is right before // variable_settings_form_submit, so the order of execution // is as expected. If our custom form handler comes after // variable_settings_form_submit that stays, if it comes before // it will also come before variable_realm_variable_settings_form_submit. if (($index_realm = array_search('variable_realm_variable_settings_form_submit', $form['#submit'])) !== FALSE) { if (($index_var = array_search('variable_settings_form_submit', $form['#submit'])) !== FALSE) { $element = 'variable_realm_variable_settings_form_submit'; unset($form['#submit'][$index_realm]); $form['#submit'] = array_values($form['#submit']); $index = ($index_var - 1) < 0 ? 0 : $index_var - 1; array_splice($form['#submit'], $index, 0, $element); } } } /** * Implements hook_module_implements_alter(). */ function eu_cookie_compliance_module_implements_alter(&$implementations, $hook) { if ($hook === 'variable_settings_form_alter') { // Make sure we run *after* variable_realm_variable_settings_form_alter. $group = $implementations['eu_cookie_compliance']; unset($implementations['eu_cookie_compliance']); $implementations['eu_cookie_compliance'] = $group; } } /** * Returns an associative array of keys and labels for use in #options. * * @return array * The options list for cookie default checkbox states. */ function _eu_cookie_compliance_get_category_checkbox_default_state_options_list() { return array( 'unchecked' => t('Unchecked by default'), 'checked' => t('Checked by default'), 'required' => t('Checked and disabled (user cannot clear the checkbox)'), ); }