c30236a patch
diff --git a/eu_cookie_compliance.module b/eu_cookie_compliance.module
index 2c44267..e8da0bd 100644
--- a/eu_cookie_compliance.module
+++ b/eu_cookie_compliance.module
@@ -17,6 +17,7 @@ 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;
 
 /**
@@ -60,88 +61,88 @@ function eu_cookie_compliance_page_attachments(&$attachments) {
 
   if ($config->get('popup_enabled') && \Drupal::currentUser()->hasPermission('display eu cookie compliance popup') && $geoip_match && $domain_allow && !$path_match) {
     $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;
-    }
-    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();
+    // 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();
+    $html_info = \Drupal::service('renderer')->renderRoot($html_info)->__toString();
+    $html_agreed = \Drupal::service('renderer')->renderRoot($html_agreed)->__toString();
 
-      if ($was_debugging) {
-        $twig_service->enableDebug();
-      }
+    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 = $config->get('popup_link');
+    if (UrlHelper::isExternal($popup_link)) {
+      $popup_link = Url::fromUri($popup_link);
+    }
+    else {
+      $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_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'           => $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'),
+    );
 
     $attachments['#attached']['drupalSettings']['eu_cookie_compliance'] = $data['variables'];
     $attachments['#attached']['library'][] = 'eu_cookie_compliance/eu_cookie_compliance';
diff --git a/src/Form/EuCookieComplianceConfigForm.php b/src/Form/EuCookieComplianceConfigForm.php
index f342e80..298b253 100644
--- a/src/Form/EuCookieComplianceConfigForm.php
+++ b/src/Form/EuCookieComplianceConfigForm.php
@@ -6,6 +6,8 @@ use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Path\PathValidatorInterface;
+use Drupal\Core\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Routing\RequestContext;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -180,6 +182,7 @@ class EuCookieComplianceConfigForm extends ConfigFormBase {
       '#maxlength' => 1024,
       '#required' => TRUE,
       '#description' => $this->t('Enter link to your privacy policy or other page that will explain cookies to your users, internal/external links should start with http:// or https://.'),
+      '#element_validate' => array(array($this, 'validatePopupLink')),
     );
 
     $form['eu_cookie_compliance']['popup_link_new_window'] = array(
@@ -278,15 +281,6 @@ class EuCookieComplianceConfigForm extends ConfigFormBase {
   /**
    * {@inheritdoc}
    */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    parent::validateForm($form, $form_state);
-
-    // @TODO Validate other form elements settings.
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->config('eu_cookie_compliance.settings')
       ->set('domain', $form_state->getValue('domain'))
@@ -316,4 +310,41 @@ class EuCookieComplianceConfigForm extends ConfigFormBase {
     parent::submitForm($form, $form_state);
   }
 
+  /**
+   * Validates the popup link field.
+   */
+  public function validatePopupLink($element, FormStateInterface &$form_state) {
+    if (empty($element['#value'])) {
+      return;
+    }
+
+    $input = $element['#value'];
+    if (UrlHelper::isExternal($input)) {
+      $allowed_protocols = ['http', 'https'];
+      if (!in_array(parse_url($input, PHP_URL_SCHEME), $allowed_protocols)) {
+        $form_state->setError($element, t('Invalid protocol specified for the %name (valid protocols: %protocols).', array('%name' => $element['#title'], '%protocols' => implode(', ', $allowed_protocols))));
+      }
+      else {
+        try {
+          Url::fromUri($input);
+        }
+        catch (\Exception $exc) {
+          $form_state->setError($element, t('Invalid %name (:message).', array('%name' => $element['#title'], ':message' => $exc->getMessage())));
+        }
+      }
+    }
+    else {
+      // Special case for '<front>'.
+      if ($input === '<front>') {
+        $input = '/';
+      }
+      try {
+        Url::fromUserInput($input);
+      }
+      catch (\Exception $exc) {
+        $form_state->setError($element, t('Invalid URL in %name field (:message).', array('%name' => $element['#title'], ':message' => $exc->getMessage())));
+      }
+    }
+  }
+
 }
diff --git a/src/Tests/EuCookieComplianceConfigFormTest.php b/src/Tests/EuCookieComplianceConfigFormTest.php
index 8af4996..dea1537 100644
--- a/src/Tests/EuCookieComplianceConfigFormTest.php
+++ b/src/Tests/EuCookieComplianceConfigFormTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\eu_cookie_compliance\Tests;
 
+use Drupal\Core\Url;
+
 /**
  * Test functionality for EU Cookie Compliance Config form.
  *
@@ -23,7 +25,9 @@ class EuCookieComplianceConfigFormTest extends EuCookieComplianceTestBase {
    */
   public static $modules = [
     'eu_cookie_compliance',
-    'eu_cookie_compliance_test'
+    'eu_cookie_compliance_test',
+    'node',
+    'path',
   ];
 
   /**
@@ -32,14 +36,15 @@ class EuCookieComplianceConfigFormTest extends EuCookieComplianceTestBase {
   protected function setUp() {
     parent::setUp();
     // Create and log in admin user.
-    $this->adminUser = $this->drupalCreateUser(['display eu cookie compliance popup', 'administer eu cookie compliance popup']);
-    $this->drupalLogin($this->adminUser);
+    $this->adminUser = $this->drupalCreateUser(['display eu cookie compliance popup', 'administer eu cookie compliance popup', 'access content', 'administer url aliases']);
+    $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
   }
 
   /**
    * Tests the EuCookieComplianceConfigForm.
    */
   public function testEuCookieComplianceConfigForm() {
+    $this->drupalLogin($this->adminUser);
     $this->drupalGet('admin/config/system/eu-cookie-compliance');
     $this->assertNoFieldChecked('edit-popup-enabled');
     $edit = ['popup_enabled' => 1];
@@ -53,4 +58,68 @@ class EuCookieComplianceConfigFormTest extends EuCookieComplianceTestBase {
     $this->assertText('The configuration options have been saved.');
   }
 
+  /**
+   * Tests the popup link validation and configuration.
+   */
+  public function testPopupLinks() {
+    $this->drupalLogin($this->adminUser);
+
+    // Create 2 nodes.
+    $this->createNode();
+    $this->createNode();
+
+    // Set a path alias for the second node.
+    $this->drupalPostForm('/admin/config/search/path/add', ['source' => '/node/2', 'alias' => '/alias'], t('Save'));
+
+    $scenarios = [
+      // Format: User-entered value, value displayed in form, actual value for link.
+
+      // External URLs.
+      ['http://example.com/', 'http://example.com/', 'http://example.com/'],
+      ['https://drupal.org', 'https://drupal.org', 'https://drupal.org'],
+
+      // Internal URL without alias.
+      ['/node/1', '/node/1', '/node/1'],
+
+      // Internal URL with alias.
+      ['/node/2', '/node/2', '/alias'],
+
+      // Internal URL with alias with fragment.
+      ['/node/2#anchor', '/node/2#anchor', '/alias#anchor'],
+
+      // Special-case URLs.
+      ['<front>', '<front>', '/'],
+
+      // Spaces.
+      ['/lorem ipsum', '/lorem ipsum', '/lorem ipsum'],
+    ];
+
+    foreach ($scenarios as $scenario) {
+      // Configure EU cookie compliance.
+      $edit = ['popup_link' => $scenario[0], 'popup_enabled' => TRUE];
+      $this->drupalPostForm('/admin/config/system/eu-cookie-compliance', $edit, t('Save configuration'));
+
+      // Verify the popup link input field after submit.
+      $this->assertFieldByXpath('//input[@id="edit-popup-link"]', $scenario[1]);
+
+      // Load a page and verify the popup link in the page settings.
+      $this->drupalGet('/node/1');
+      $settings = $this->getDrupalSettings();
+      $expected_url = strpos($scenario[2], '/') === 0 ? Url::fromUserInput($scenario[2])->toString() : $scenario[2];
+      $this->assertEqual($settings['eu_cookie_compliance']['popup_link'], $expected_url);
+    }
+
+    // Test validation for the popup link setting.
+    $invalid_url_scenarios = [
+      'node/1' => "The user-entered string &#039;node/1&#039; must begin with a &#039;/&#039;, &#039;?&#039;, or &#039;#&#039;.",
+      'ftp://example.com' => "Invalid protocol specified for",
+    ];
+
+    foreach ($invalid_url_scenarios as $input => $message) {
+      $edit = ['popup_link' => $input];
+      $this->drupalPostForm('/admin/config/system/eu-cookie-compliance', $edit, t('Save configuration'));
+      $this->assertRaw($message);
+    }
+  }
+
 }
