<?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('&amp;', '&', 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)'),
  );
}