Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / eu_cookie_compliance / src / Form / EuCookieComplianceConfigForm.php
1 <?php
2
3 namespace Drupal\eu_cookie_compliance\Form;
4
5 use Drupal\Core\Config\ConfigFactoryInterface;
6 use Drupal\Core\Form\FormStateInterface;
7 use Drupal\Core\Form\ConfigFormBase;
8 use Drupal\Core\Path\PathValidatorInterface;
9 use Drupal\Core\Url;
10 use Drupal\Component\Utility\UrlHelper;
11 use Drupal\Core\Routing\RequestContext;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
13 use Drupal\Core\Extension\ModuleHandlerInterface;
14 use Drupal\user\RoleStorageInterface;
15 use Drupal\filter\Entity\FilterFormat;
16 use Drupal\Core\Cache\Cache;
17
18 /**
19  * Provides settings for eu_cookie_compliance module.
20  */
21 class EuCookieComplianceConfigForm extends ConfigFormBase {
22
23   /**
24    * The path validator.
25    *
26    * @var \Drupal\Core\Path\PathValidatorInterface
27    */
28   protected $pathValidator;
29
30   /**
31    * The request context.
32    *
33    * @var \Drupal\Core\Routing\RequestContext
34    */
35   protected $requestContext;
36
37   /**
38    * The role storage.
39    *
40    * @var \Drupal\user\RoleStorageInterface
41    */
42   protected $roleStorage;
43
44   /**
45    * The module handler.
46    *
47    * @var \Drupal\Core\Extension\ModuleHandlerInterface
48    */
49   protected $moduleHandler;
50
51   /**
52    * Constructs an EuCookieComplianceConfigForm object.
53    *
54    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
55    *   The factory for configuration objects.
56    * @param \Drupal\Core\Path\PathValidatorInterface $path_validator
57    *   The path validator.
58    * @param \Drupal\Core\Routing\RequestContext $request_context
59    *   The request context.
60    * @param \Drupal\user\RoleStorageInterface $role_storage
61    *   The role storage.
62    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
63    *   The module handler.
64    */
65   public function __construct(ConfigFactoryInterface $config_factory, PathValidatorInterface $path_validator, RequestContext $request_context, RoleStorageInterface $role_storage, ModuleHandlerInterface $module_handler) {
66     parent::__construct($config_factory);
67
68     $this->pathValidator = $path_validator;
69     $this->requestContext = $request_context;
70     $this->roleStorage = $role_storage;
71     $this->moduleHandler = $module_handler;
72   }
73
74   /**
75    * {@inheritdoc}
76    */
77   public static function create(ContainerInterface $container) {
78     return new static(
79       $container->get('config.factory'),
80       $container->get('path.validator'),
81       $container->get('router.request_context'),
82       $container->get('entity.manager')->getStorage('user_role'),
83       $container->get('module_handler')
84     );
85   }
86
87   /**
88    * {@inheritdoc}
89    */
90   public function getFormId() {
91     return 'eu_cookie_compliance_config_form';
92   }
93
94   /**
95    * Gets the roles to display in this form.
96    *
97    * @return \Drupal\Core\Entity\EntityInterface[]
98    *   An array of role objects.
99    */
100   protected function getRoles() {
101     return $this->roleStorage->loadMultiple();
102   }
103
104   /**
105    * {@inheritdoc}
106    */
107   protected function getEditableConfigNames() {
108     return [
109       'eu_cookie_compliance.settings',
110     ];
111   }
112
113   /**
114    * {@inheritdoc}
115    */
116   public function buildForm(array $form, FormStateInterface $form_state) {
117
118     $config = $this->config('eu_cookie_compliance.settings');
119
120     $default_filter_format = filter_default_format();
121     $full_html_format = FilterFormat::load('full_html');
122     if ($default_filter_format == 'restricted_html' && !empty($full_html_format) && $full_html_format->get('status')) {
123       $default_filter_format = 'full_html';
124     }
125
126     $consent_storages = \Drupal::service('plugin.manager.eu_cookie_compliance.consent_storage');
127     $plugin_definitions = $consent_storages->getDefinitions();
128
129     $consent_storage_options = [];
130     $consent_storage_options['do_not_store'] = $this->t('Do not store');
131     foreach ($plugin_definitions as $plugin_name => $plugin_definition) {
132       /* @var \Drupal\Core\StringTranslation\TranslatableMarkup $plugin_definition_name */
133       $plugin_definition_name = $plugin_definition['name'];
134       $consent_storage_options[$plugin_name] = $plugin_definition_name->render();
135     }
136
137     $form['popup_enabled'] = [
138       '#type' => 'checkbox',
139       '#title' => $this->t('Enable banner'),
140       '#default_value' => $config->get('popup_enabled'),
141     ];
142
143     // List of checkbox values.
144     $role_names = [];
145     // Permissions per role.
146     $role_permissions = [];
147     // Which checkboxes should be ticked.
148     $role_values = [];
149
150     $perm = 'display eu cookie compliance popup';
151
152     foreach ($this->getRoles() as $role_name => $role) {
153       // Exclude Admin roles.
154       /* @var \Drupal\user\Entity\Role $role */
155       if (!$role->isAdmin()) {
156         $role_names[$role_name] = $role->label();
157         // Fetch permissions for the roles.
158         $role_permissions[$role_name] = $role->getPermissions();
159         // Indicate whether the checkbox should be ticked.
160         if (in_array($perm, $role_permissions[$role_name])) {
161           $role_values[] = $role_name;
162         }
163       }
164     }
165
166     $form['permissions'] = [
167       '#type' => 'details',
168       '#title' => $this->t('Permissions'),
169       '#open' => TRUE,
170     ];
171
172     $form['permissions']['see_the_banner'] = [
173       '#type' => 'checkboxes',
174       '#title' => $this->t('Display the banner for'),
175       '#options' => $role_names,
176       '#default_value' => $role_values,
177     ];
178
179     $form['consent_option'] = [
180       '#type' => 'details',
181       '#title' => $this->t('Consent for processing of personal information'),
182       '#open' => TRUE,
183     ];
184
185     $form['consent_option']['info'] = [
186       '#type' => 'markup',
187       '#markup' => $this->t("The EU General Data Protection Regulation (GDPR) (see <a href=\"https://www.eugdpr.org/\" target=\"_blank\">https://www.eugdpr.org/</a>) comes into enforcement from 25 May 2018 and introduces new requirements for web sites which handle information that can be used to identify individuals. The regulation underlines that consent must be <strong>unambiguous</strong> and involve a <strong>clear affirmative action</strong>. When evaluating how to best handle the requirements in the GDPR, remember that if you have a basic web site where the visitors don't log in, you always have the option to <strong>not process data that identifies individuals</strong>, in which case you may not need this module. Also note that GDPR applies to any electronic processing or storage of personal data that your organization may do, and simply installing a module may not be enough to become fully GDPR compliant."),
188     ];
189
190     $form['consent_option']['method'] = [
191       '#type' => 'radios',
192       '#title' => $this->t('Consent method'),
193       '#options' => [
194         'default' => $this->t("Consent by default. Don't provide any option to opt out."),
195         'opt_in' => $this->t("Opt-in. Don't track visitors unless they specifically give consent. (GDPR compliant)"),
196         'opt_out' => $this->t('Opt-out. Track visitors by default, unless they choose to opt out.'),
197         'auto' => $this->t('Automatic. Respect the DNT (Do not track) setting in the browser, if present. Uses opt-in when DNT is 1 or not set, and consent by default when DNT is 0.'),
198       ],
199       '#default_value' => $config->get('method'),
200     ];
201
202     $form['javascripts'] = [
203       '#type' => 'details',
204       '#title' => $this->t("Disable the following JavaScripts when consent isn't given"),
205       '#open' => TRUE,
206       '#states' => [
207         'visible' => [
208           "input[name='method']" => ['!value' => 'default'],
209         ],
210       ],
211     ];
212
213     $form['javascripts']['disabled_javascripts'] = [
214       '#type' => 'textarea',
215       '#title' => $this->t('Disable JavaScripts'),
216       '#default_value' => $config->get('disabled_javascripts'),
217       '#description' => $this->t("Include the full path of JavaScripts, each on a separate line. When using the opt-in or opt-out consent options, you can block certain JavaScript files from being loaded when consent isn't given. The on-site JavaScripts should be written as root relative paths <strong>without the leading slash</strong>, and off-site JavaScripts should be written as complete URLs <strong>with the leading http(s)://</strong>. Note that after the user gives consent, the scripts will be executed in the order you enter here."),
218     ];
219
220     $form['cookies'] = [
221       '#type' => 'details',
222       '#title' => $this->t('Cookie handling'),
223       '#open' => TRUE,
224       '#states' => [
225         'visible' => [
226           "input[name='method']" => ['!value' => 'default'],
227         ],
228       ],
229     ];
230
231     $form['cookies']['whitelisted_cookies'] = [
232       '#type' => 'textarea',
233       '#title' => $this->t('Whitelisted cookies'),
234       '#default_value' => $config->get('whitelisted_cookies'),
235       '#description' => $this->t("Include the name of cookies, each on a separate line. When using the opt-in or opt-out consent options, this module will <strong>prevent cookies that are not on the whitelist</strong> from being stored in the browser when consent isn't given. PHP session cookies and the cookie for this module are always whitelisted."),
236     ];
237
238     $form['consent_storage'] = [
239       '#type' => 'details',
240       '#title' => $this->t('Store record of consent'),
241       '#open' => TRUE,
242       '#states' => [
243         'visible' => [
244           "input[name='method']" => ['!value' => 'default'],
245         ],
246       ],
247     ];
248
249     $form['consent_storage']['info'] = [
250       '#type' => 'markup',
251       '#markup' => $this->t('Depending on your implementation of GDPR, you may have to store a record when the user consents. This module comes with a basic consent storage plugin that writes a record to the database. Note that if your site has significant traffic, the basic consent storage may become a bottleneck, as every consent action will require a write to the database. You can easily create your own module with a ConsentStorage Plugin that extends ConsentStorageBase, using BasicConsentStorage from this module as a template. If you create a highly performant consent storage plugin, please consider contributing it back to the Drupal community as a contrib module.'),
252     ];
253
254     $form['consent_storage']['consent_storage_method'] = [
255       '#type' => 'radios',
256       '#title' => $this->t('Consent storage method'),
257       '#default_value' => $config->get('consent_storage_method'),
258       '#options' => $consent_storage_options,
259     ];
260
261     $form['popup_message'] = [
262       '#type' => 'details',
263       '#title' => $this->t('Cookie information banner'),
264       '#open' => TRUE,
265     ];
266
267     $form['popup_message']['popup_clicking_confirmation'] = [
268       '#type' => 'checkbox',
269       '#title' => $this->t('Consent by clicking'),
270       '#default_value' => $config->get('popup_clicking_confirmation'),
271       '#description' => $this->t('By default by clicking any link or button on the website the visitor accepts the cookie policy. Uncheck this box if you don’t require this functionality. You may want to edit the banner message below accordingly.'),
272       '#states' => [
273         'visible' => [
274           'input[name="method"]' => ['value' => 'default'],
275         ],
276       ],
277     ];
278
279     $config_format = $config->get('popup_info.format');
280     if (!empty($config_format)) {
281       $filter_format = FilterFormat::load($config_format);
282       if (empty($filter_format) || !$filter_format->get('status')) {
283         $config_format = $default_filter_format;
284       }
285     }
286
287     $form['popup_message']['popup_info'] = [
288       '#type' => 'text_format',
289       '#title' => $this->t('Cookie information banner message'),
290       '#default_value' => $config->get('popup_info.value'),
291       '#required' => TRUE,
292       '#format' => $config_format,
293     ];
294
295     $form['popup_message']['use_mobile_message'] = [
296       '#type' => 'checkbox',
297       '#title' => $this->t('Use a different message for mobile phones.'),
298       '#default_value' => !empty($config->get('use_mobile_message')) ? $config->get('use_mobile_message') : FALSE,
299     ];
300
301     $form['popup_message']['container'] = [
302       '#type' => 'container',
303       '#states' => ['visible' => ['input[name="use_mobile_message"]' => ['checked' => TRUE]]],
304     ];
305
306     $config_format = $config->get('mobile_popup_info.format');
307     if (!empty($config_format)) {
308       $filter_format = FilterFormat::load($config_format);
309       if (empty($filter_format) || !$filter_format->get('status')) {
310         $config_format = $default_filter_format;
311       }
312     }
313
314     $form['popup_message']['container']['mobile_popup_info'] = [
315       '#type' => 'text_format',
316       '#title' => $this->t('Cookie information banner message - mobile'),
317       '#default_value' => $config->get('mobile_popup_info.value'),
318       '#required' => FALSE,
319       '#format' => $config_format,
320     ];
321
322     $form['popup_message']['mobile_breakpoint'] = [
323       '#type' => 'number',
324       '#title' => $this->t('Mobile breakpoint'),
325       '#default_value' => !empty($config->get('mobile_breakpoint')) ? $config->get('mobile_breakpoint') : '768',
326       '#field_suffix' => $this->t('px'),
327       '#size' => 4,
328       '#maxlength' => 4,
329       '#required' => FALSE,
330       '#description' => $this->t('The mobile message will be used when the window width is below or equal to the given value.'),
331       '#states' => [
332         'visible' => [
333           "input[name='use_mobile_message']" => ['checked' => TRUE],
334         ],
335       ],
336     ];
337
338     $form['popup_message']['popup_agree_button_message'] = [
339       '#type' => 'textfield',
340       '#title' => $this->t('Agree button label'),
341       '#default_value' => $config->get('popup_agree_button_message'),
342       '#size' => 30,
343       '#required' => TRUE,
344     ];
345
346     $form['popup_message']['disagree_button'] = [
347       '#type' => 'checkbox',
348       '#title' => $this->t('Show "Cookie Policy" and "More info" buttons'),
349       '#description' => $this->t('If this option is checked, the cookie policy button will be shown on the site. Disabling this option will hide both the "Cookie Policy" button on the information banner and the "More info" button on the "Thank you" banner.'),
350       '#default_value' => $config->get('show_disagree_button'),
351       '#states' => [
352         'visible' => [
353           "input[name='method']" => ['value' => 'default'],
354         ],
355       ],
356     ];
357
358     $form['popup_message']['popup_disagree_button_message'] = [
359       '#type' => 'textfield',
360       '#title' => $this->t('Cookie policy button label'),
361       '#default_value' => $config->get('popup_disagree_button_message'),
362       '#size' => 30,
363       '#states' => [
364         'visible' => [
365           ['input[name="disagree_button"]' => ['checked' => TRUE]],
366           ['input[name="method"]' => ['!value' => 'default']],
367         ],
368         'required' => [
369           ['input[name="disagree_button"]' => ['checked' => TRUE]],
370           ['input[name="method"]' => ['!value' => 'default']],
371         ],
372       ],
373     ];
374
375     $form['popup_message']['disagree_button_label'] = [
376       '#type' => 'textfield',
377       '#title' => $this->t('Disagree button label'),
378       '#default_value' => $config->get('disagree_button_label'),
379       '#size' => 30,
380       '#states' => [
381         'visible' => [
382           'input[name="method"]' => ['!value' => 'default'],
383         ],
384         'required' => [
385           'input[name="method"]' => ['!value' => 'default'],
386         ],
387       ],
388     ];
389
390     $form['withdraw_consent'] = [
391       '#type' => 'details',
392       '#title' => $this->t('Withdraw consent'),
393       '#open' => TRUE,
394       '#states' => [
395         'visible' => [
396           "input[name='method']" => ['!value' => 'default'],
397         ],
398       ],
399     ];
400
401     $form['withdraw_consent']['info'] = [
402       '#type' => 'markup',
403       '#markup' => t('GDPR requires that withdrawing consent for handling personal information should be as easy as giving consent. This module offers a tab button that when clicked brings up a message and a button that can be used to withdraw consent.'),
404     ];
405
406     $form['withdraw_consent']['withdraw_enabled'] = [
407       '#type' => 'checkbox',
408       '#title' => t('Enable floating privacy settings tab and withdraw consent banner'),
409       '#default_value' => $config->get('withdraw_enabled'),
410     ];
411
412     $config_format = $config->get('popup_info.format');
413     if (!empty($config_format)) {
414       $filter_format = FilterFormat::load($config_format);
415       if (empty($filter_format) || !$filter_format->get('status')) {
416         $config_format = $default_filter_format;
417       }
418     }
419
420     $form['withdraw_consent']['withdraw_message'] = [
421       '#type' => 'text_format',
422       '#title' => t('Withdraw consent banner message'),
423       '#default_value' => isset($config->get('withdraw_message')['value']) ? $config->get('withdraw_message')['value'] : '',
424       '#description' => t('Text that will be displayed in the banner that appears when the privacy settings tab is clicked.'),
425       '#format' => $config_format,
426     ];
427
428     $form['withdraw_consent']['withdraw_tab_button_label'] = [
429       '#type' => 'textfield',
430       '#title' => t('Privacy settings tab label'),
431       '#default_value' => $config->get('withdraw_tab_button_label'),
432       '#description' => t('Tab button that reveals/hides the withdraw message and action button when clicked.'),
433     ];
434
435     $form['withdraw_consent']['withdraw_action_button_label'] = [
436       '#type' => 'textfield',
437       '#title' => t('Withdraw consent action button label'),
438       '#default_value' => $config->get('withdraw_action_button_label'),
439       '#description' => t('This button will withdraw consent when clicked.'),
440     ];
441
442     $form['thank_you'] = [
443       '#type' => 'details',
444       '#open' => TRUE,
445       '#title' => $this->t('Thank you banner'),
446     ];
447
448     $form['thank_you']['popup_agreed_enabled'] = [
449       '#type' => 'checkbox',
450       '#title' => $this->t('Enable "Thank you" banner'),
451       '#default_value' => $config->get('popup_agreed_enabled'),
452     ];
453
454     $form['thank_you']['popup_hide_agreed'] = [
455       '#type' => 'checkbox',
456       '#title' => $this->t('Clicking hides "Thank you" banner.'),
457       '#default_value' => $config->get('popup_hide_agreed'),
458       '#description' => $this->t('Clicking a link or button hides the "Thank you" message automatically.'),
459     ];
460
461     $config_format = $config->get('popup_info.format');
462     if (!empty($config_format)) {
463       $filter_format = FilterFormat::load($config_format);
464       if (empty($filter_format) || !$filter_format->get('status')) {
465         $config_format = $default_filter_format;
466       }
467     }
468
469     $form['thank_you']['popup_agreed'] = [
470       '#type' => 'text_format',
471       '#title' => $this->t('"Thank you" banner message'),
472       '#default_value' => !empty($config->get('popup_agreed')['value']) ? $config->get('popup_agreed')['value'] : '',
473       '#required' => TRUE,
474       '#format' => $config_format,
475     ];
476
477     $form['thank_you']['popup_find_more_button_message'] = [
478       '#type' => 'textfield',
479       '#title' => $this->t('More info button label'),
480       '#default_value' => $config->get('popup_find_more_button_message'),
481       '#size' => 30,
482       '#states' => [
483         'visible' => [
484           ['input[name="disagree_button"]' => ['checked' => TRUE]],
485           ['input[name="method"]' => ['!value' => 'default']],
486         ],
487         'required' => [
488           ['input[name="disagree_button"]' => ['checked' => TRUE]],
489           ['input[name="method"]' => ['!value' => 'default']],
490         ],
491       ],
492     ];
493
494     $form['thank_you']['popup_hide_button_message'] = [
495       '#type' => 'textfield',
496       '#title' => $this->t('Hide button label'),
497       '#default_value' => $config->get('popup_hide_button_message'),
498       '#size' => 30,
499       '#required' => TRUE,
500     ];
501
502     $form['privacy'] = [
503       '#type' => 'details',
504       '#open' => TRUE,
505       '#title' => $this->t('Privacy policy'),
506     ];
507
508     $form['privacy']['popup_link'] = [
509       '#type' => 'textfield',
510       '#title' => $this->t('Privacy policy link'),
511       '#default_value' => $config->get('popup_link'),
512       '#maxlength' => 1024,
513       '#required' => TRUE,
514       '#description' => $this->t('Enter link to your privacy policy or other page that will explain cookies to your users, external links should start with http:// or https://.'),
515       '#element_validate' => [[$this, 'validatePopupLink']],
516     ];
517
518     $form['privacy']['popup_link_new_window'] = [
519       '#type' => 'checkbox',
520       '#title' => $this->t('Open privacy policy link in a new window.'),
521       '#default_value' => $config->get('popup_link_new_window'),
522     ];
523
524     $form['appearance'] = [
525       '#type' => 'details',
526       '#open' => TRUE,
527       '#title' => $this->t('Appearance'),
528     ];
529
530     $form_color_picker_type = 'textfield';
531
532     if ($this->moduleHandler->moduleExists('jquery_colorpicker')) {
533       $form_color_picker_type = 'jquery_colorpicker';
534     }
535
536     $popup_position_options = [
537       'bottom' => 'Bottom',
538       'top' => 'Top',
539     ];
540
541     $popup_position_value = ($config->get('popup_position') === TRUE ? 'top' : ($config->get('popup_position') === FALSE ? 'bottom' : $config->get('popup_position')));
542
543     $form['appearance']['popup_position'] = [
544       '#type' => 'radios',
545       '#title' => $this->t('Position'),
546       '#default_value' => $popup_position_value,
547       '#options' => $popup_position_options,
548     ];
549
550     $form['appearance']['use_bare_css'] = [
551       '#type' => 'checkbox',
552       '#title' => $this->t('Include minimal CSS, I want to style the banner in the theme CSS.'),
553       '#default_value' => !empty($config->get('use_bare_css')) ? $config->get('use_bare_css') : 0,
554       '#description' => $this->t('This may be useful if you want the banner to share the button style of your theme. Note that you will have to configure values like the banner width, text color and background color in your CSS file.'),
555     ];
556
557     $form['appearance']['popup_text_hex'] = [
558       '#type' => $form_color_picker_type,
559       '#title' => $this->t('Text color'),
560       '#default_value' => $config->get('popup_text_hex'),
561       '#description' => $this->t('Change the text color of the banner. Provide HEX value without the #.'),
562       '#element_validate' => ['eu_cookie_compliance_validate_hex'],
563       '#states' => [
564         'visible' => [
565           "input[name='use_bare_css']" => ['checked' => FALSE],
566         ],
567       ],
568     ];
569
570     $form['appearance']['popup_bg_hex'] = [
571       '#type' => $form_color_picker_type,
572       '#title' => $this->t('Background color'),
573       '#default_value' => $config->get('popup_bg_hex'),
574       '#description' => $this->t('Change the background color of the banner. Provide HEX value without the #.'),
575       '#element_validate' => ['eu_cookie_compliance_validate_hex'],
576       '#states' => [
577         'visible' => [
578           "input[name='use_bare_css']" => ['checked' => FALSE],
579         ],
580       ],
581     ];
582
583     $form['appearance']['popup_height'] = [
584       '#type' => 'number',
585       '#title' => $this->t('Banner height'),
586       '#default_value' => !empty($config->get('popup_height')) ? $config->get('popup_height') : '',
587       '#field_suffix' => $this->t('px'),
588       '#size' => 5,
589       '#maxlength' => 5,
590       '#required' => FALSE,
591       '#description' => $this->t('Enter an integer value for a desired height in pixels or leave empty for automatically adjusted height.'),
592       '#states' => [
593         'visible' => [
594           "input[name='use_bare_css']" => ['checked' => FALSE],
595         ],
596       ],
597     ];
598
599     $form['appearance']['popup_width'] = [
600       '#type' => 'textfield',
601       '#title' => $this->t('Banner width in pixels or a percentage value'),
602       '#default_value' => $config->get('popup_width'),
603       '#field_suffix' => $this->t('px or %'),
604       '#size' => 5,
605       '#maxlength' => 5,
606       '#description' => $this->t('Set the width of the banner. This can be either an integer value or percentage of the screen width. For example: 200 or 50%.'),
607       '#states' => [
608         'visible' => ["input[name='use_bare_css']" => ['checked' => FALSE]],
609         'required' => ["input[name='use_bare_css']" => ['checked' => FALSE]],
610       ],
611     ];
612
613     $form['eu_only'] = [
614       '#type' => 'details',
615       '#open' => TRUE,
616       '#title' => t('EU countries'),
617     ];
618
619     if ($this->moduleHandler->moduleExists('smart_ip') || extension_loaded('geoip')) {
620       $form['eu_only']['eu_only'] = [
621         '#type' => 'checkbox',
622         '#title' => $this->t('Only display banner in EU countries.'),
623         '#default_value' => !empty($config->get('eu_only')) ? $config->get('eu_only') : 0,
624         '#description' => $this->t('You can limit the number of countries for which the banner is displayed by checking this option. If you want to provide a list of countries other than current EU states, you may place an array in <code>$config[\'eu_cookie_compliance.settings\'][\'eu_countries\']</code> in your <code>settings.php</code> file. Using the <a href="http://drupal.org/project/smart_ip">smart_ip</a> module or the <a href="http://www.php.net/manual/en/function.geoip-country-code-by-name.php">geoip_country_code_by_name()</a> PHP function.'),
625       ];
626       $form['eu_only']['eu_only_js'] = [
627         '#type' => 'checkbox',
628         '#title' => $this->t('JavaScript-based (for Varnish): Only display banner in EU countries.'),
629         '#default_value' => !empty($config->get('eu_only_js')) ? $config->get('eu_only_js') : 0,
630         '#description' => $this->t('This option also works for visitors that bypass Varnish. You can limit the number of countries for which the banner is displayed by checking this option. If you want to provide a list of countries other than current EU states, you may place an array in <code>$config[\'eu_cookie_compliance.settings\'][\'eu_countries\']</code> in your <code>settings.php</code> file. Using the <a href="http://drupal.org/project/smart_ip">smart_ip</a> module or the <a href="http://www.php.net/manual/en/function.geoip-country-code-by-name.php">geoip_country_code_by_name()</a> PHP function.'),
631       ];
632     }
633     else {
634       $form['eu_only']['info'] = [
635         '#markup' => t('You can choose to show the banner only to visitors from EU countries. In order to achieve this, you need to install the <a href="http://drupal.org/project/smart_ip">smart_ip</a> module or enable the <a href="http://www.php.net/manual/en/function.geoip-country-code-by-name.php">geoip_country_code_by_name()</a> PHP function.'),
636       ];
637     }
638
639     $form['advanced'] = [
640       '#type' => 'details',
641       '#open' => FALSE,
642       '#title' => $this->t('Advanced'),
643     ];
644
645     $form['advanced']['fixed_top_position'] = [
646       '#type' => 'checkbox',
647       '#title' => $this->t("If the banner is at the top, don't scroll the banner with the page."),
648       '#default_value' => $config->get('fixed_top_position'),
649       '#description' => $this->t('Use position:fixed for the banner when displayed at the top.'),
650     ];
651
652     $form['advanced']['popup_delay'] = [
653       '#type' => 'number',
654       '#title' => $this->t('Banner sliding animation time'),
655       '#default_value' => $config->get('popup_delay'),
656       '#field_suffix' => $this->t('ms'),
657       '#size' => 5,
658       '#maxlength' => 5,
659       '#required' => TRUE,
660     ];
661
662     $form['advanced']['disagree_do_not_show_popup'] = [
663       '#type' => 'checkbox',
664       '#title' => $this->t('Do not show cookie policy when the user clicks the "Cookie Policy" button.'),
665       '#default_value' => !empty($config->get('disagree_do_not_show_popup')) ? $config->get('disagree_do_not_show_popup') : 0,
666       '#description' => $this->t('Enabling this will make it possible to record the fact that the user disagrees without the user having to see the privacy policy.'),
667     ];
668
669     $form['advanced']['reload_page'] = [
670       '#type' => 'checkbox',
671       '#title' => $this->t('Reload page after user clicks the "Agree" button.'),
672       '#default_value' => !empty($config->get('reload_page')) ? $config->get('reload_page') : 0,
673     ];
674
675     $form['advanced']['popup_scrolling_confirmation'] = [
676       '#type' => 'checkbox',
677       '#title' => $this->t('Consent by scrolling'),
678       '#default_value' => $config->get('popup_scrolling_confirmation'),
679       '#description' => $this->t('Scrolling makes the visitors to accept the cookie policy. In some countries, like Italy, it is permitted.'),
680       '#states' => [
681         'visible' => [
682           ['input[name="method"]' => ['value' => 'default']],
683         ],
684       ],
685     ];
686
687     $form['advanced']['cookie_name'] = [
688       '#type' => 'textfield',
689       '#title' => $this->t('Cookie name'),
690       '#default_value' => !empty($config->get('cookie_name')) ? $config->get('cookie_name') : '',
691       '#description' => $this->t('Sets the cookie name that is used to check whether the user has agreed or not. This option is useful when policies change and the user needs to agree again.'),
692     ];
693
694     // Adding option to add/remove banner on specified domains.
695     $exclude_domains_option_active = [
696       0 => $this->t('Add'),
697       1 => $this->t('Remove'),
698     ];
699
700     $form['advanced']['domains_option'] = [
701       '#type' => 'radios',
702       '#title' => $this->t('Add/remove banner on specified domains'),
703       '#default_value' => $config->get('domains_option'),
704       '#options' => $exclude_domains_option_active,
705       '#description' => $this->t('Specify if you want to add or remove banner on the listed below domains.'),
706     ];
707
708     $form['advanced']['domains_list'] = [
709       '#type' => 'textarea',
710       '#title' => $this->t('Domains list'),
711       '#default_value' => $config->get('domains_list'),
712       '#description' => $this->t('Specify domains with protocol (e.g., http or https). Enter one domain per line.'),
713     ];
714
715     $form['advanced']['exclude_paths'] = [
716       '#type' => 'textarea',
717       '#title' => $this->t('Exclude paths'),
718       '#default_value' => !empty($config->get('exclude_paths')) ? $config->get('exclude_paths') : '',
719       '#description' => $this->t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", [
720         '%blog' => '/blog',
721         '%blog-wildcard' => '/blog/*',
722         '%front' => '<front>',
723       ]),
724     ];
725
726     $form['advanced']['exclude_admin_theme'] = [
727       '#type' => 'checkbox',
728       '#title' => $this->t('Exclude admin pages.'),
729       '#default_value' => $config->get('exclude_admin_theme'),
730     ];
731
732     $form['advanced']['exclude_uid_1'] = [
733       '#type' => 'checkbox',
734       '#title' => $this->t("Don't show the banner for UID 1."),
735       '#default_value' => !empty($config->get('exclude_uid_1')) ? $config->get('exclude_uid_1') : 0,
736     ];
737
738     $form['advanced']['better_support_for_screen_readers'] = [
739       '#type' => 'checkbox',
740       '#title' => $this->t('Let screen readers see the banner before other links on the page.'),
741       '#default_value' => !empty($config->get('better_support_for_screen_readers')) ? $config->get('better_support_for_screen_readers') : 0,
742       '#description' => $this->t('Enable this if you want to place the banner as the first HTML element on the page. This will make it possible for screen readers to close the banner without tabbing through all links on the page.'),
743     ];
744
745     $form['advanced']['domain'] = [
746       '#type' => 'textfield',
747       '#title' => $this->t('Domain'),
748       '#default_value' => $config->get('domain'),
749       '#description' => $this->t('Sets the domain of the cookie to a specific url. Used when you need consistency across domains. This is language independent. Note: Make sure you actually enter a domain that the browser can make use of. For example if your site is accessible at both www.domain.com and domain.com, you will not be able to hide the banner at domain.com if your value for this field is www.domain.com.'),
750     ];
751
752     $form['advanced']['cookie_session'] = [
753       '#type' => 'checkbox',
754       '#title' => $this->t('Prompt for consent (from the same user) at every new browser session.'),
755       '#description' => $this->t("This sets cookie lifetime to 0, invalidating the cookie at the end of the browser session. To set a cookie lifetime greater than 0, uncheck this option. Note that some users will find this behavior highly annoying, and it's recommended to double-check with the legal advisor whether you really need this option enabled."),
756       '#default_value' => $config->get('cookie_session'),
757     ];
758
759     $form['advanced']['cookie_lifetime'] = [
760       '#type' => 'number',
761       '#title' => $this->t('Cookie lifetime'),
762       '#description' => $this->t("How many days the system remember the user's choice."),
763       '#default_value' => $config->get('cookie_lifetime'),
764       '#field_suffix' => $this->t('days'),
765       '#size' => 5,
766       '#maxlength' => 5,
767       '#required' => TRUE,
768       '#states' => [
769         'enabled' => [
770           "input[name='cookie_session']" => ['checked' => FALSE],
771         ],
772       ],
773     ];
774
775     return parent::buildForm($form, $form_state);
776   }
777
778   /**
779    * {@inheritdoc}
780    */
781   public function validateForm(array &$form, FormStateInterface $form_state) {
782     parent::validateForm($form, $form_state);
783
784     // Validate cookie name against valid characters.
785     if (preg_match('/[&\'\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5b-\x5d\x7b\x7d\x7f]/', $form_state->getValue('cookie_name'))) {
786       $form_state->setErrorByName('cookie_name', $this->t('Invalid cookie name, please remove any special characters and try again.'));
787     }
788
789   }
790
791   /**
792    * {@inheritdoc}
793    */
794   public function submitForm(array &$form, FormStateInterface $form_state) {
795     // Clear values if we are using minimal css.
796     if ($form_state->getValue('use_bare_css')) {
797       $form_state->setValue('popup_bg_hex', '');
798       $form_state->setValue('popup_text_hex', '');
799       $form_state->setValue('popup_height', '');
800       $form_state->setValue('popup_width', '');
801     }
802
803     // If there's no mobile message entered, disable the feature.
804     if (trim($form_state->getValue('mobile_popup_info')['value']) == '') {
805       $form_state->setValue('use_mobile_message', FALSE);
806     }
807
808     if ($form_state->getValue('popup_link') == '<front>' && $form_state->getValue('disagree_button')) {
809       drupal_set_message($this->t('Your privacy policy link is pointing at the front page. This is the default value after installation, and unless your privacy policy is actually posted at the front page, you will need to create a separate page for the privacy policy and link to that page.'), 'error');
810     }
811
812     // Save permissions.
813     $permission_name = 'display eu cookie compliance popup';
814
815     foreach ($this->getRoles() as $role_name => $role) {
816       /* @var \Drupal\user\Entity\Role $role */
817       if (!$role->isAdmin()) {
818         if (array_key_exists($role_name, $form_state->getValue('see_the_banner')) && $form_state->getValue('see_the_banner')[$role_name]) {
819           user_role_grant_permissions($role_name, [$permission_name]);
820         }
821         else {
822           user_role_revoke_permissions($role_name, [$permission_name]);
823         }
824       }
825     }
826
827     // Handle legacy settings for popup_position:
828     if ($form_state->getValue('popup_position') == 'top') {
829       $form_state->setValue('popup_position', TRUE);
830     }
831     elseif ($form_state->getValue('popup_position') == 'bottom') {
832       $form_state->setValue('popup_position', FALSE);
833     }
834
835     $method = $form_state->getValue('method');
836
837     if ($method != 'default') {
838       $form_state->setValue('disagree_button', TRUE);
839       $form_state->setValue('popup_clicking_confirmation', FALSE);
840       $form_state->setValue('popup_scrolling_confirmation', FALSE);
841
842     }
843     else {
844       $form_state->setValue('whitelisted_cookies', '');
845       $form_state->setValue('disabled_javascripts', '');
846     }
847
848     // Clear cached javascript.
849     Cache::invalidateTags(['library_info']);
850
851     eu_cookie_compliance_module_set_weight();
852
853     // Save settings.
854     $this->config('eu_cookie_compliance.settings')
855       ->set('domain', $form_state->getValue('domain'))
856       ->set('popup_enabled', $form_state->getValue('popup_enabled'))
857       ->set('popup_clicking_confirmation', $form_state->getValue('popup_clicking_confirmation'))
858       ->set('popup_scrolling_confirmation', $form_state->getValue('popup_scrolling_confirmation'))
859       ->set('popup_position', $form_state->getValue('popup_position'))
860       ->set('popup_agree_button_message', $form_state->getValue('popup_agree_button_message'))
861       ->set('show_disagree_button', $form_state->getValue('disagree_button'))
862       ->set('popup_disagree_button_message', $form_state->getValue('popup_disagree_button_message'))
863       ->set('popup_info', $form_state->getValue('popup_info'))
864       ->set('use_mobile_message', $form_state->getValue('use_mobile_message'))
865       ->set('mobile_popup_info', $form_state->getValue('use_mobile_message') ? $form_state->getValue('mobile_popup_info') : ['value' => '', 'format' => filter_default_format()])
866       ->set('mobile_breakpoint', $form_state->getValue('mobile_breakpoint'))
867       ->set('popup_agreed_enabled', $form_state->getValue('popup_agreed_enabled'))
868       ->set('popup_hide_agreed', $form_state->getValue('popup_hide_agreed'))
869       ->set('popup_find_more_button_message', $form_state->getValue('popup_find_more_button_message'))
870       ->set('popup_hide_button_message', $form_state->getValue('popup_hide_button_message'))
871       ->set('popup_agreed', $form_state->getValue('popup_agreed'))
872       ->set('popup_link', $form_state->getValue('popup_link'))
873       ->set('popup_link_new_window', $form_state->getValue('popup_link_new_window'))
874       ->set('popup_height', $form_state->getValue('popup_height'))
875       ->set('popup_width', $form_state->getValue('popup_width'))
876       ->set('popup_delay', $form_state->getValue('popup_delay'))
877       ->set('popup_bg_hex', $form_state->getValue('popup_bg_hex'))
878       ->set('popup_text_hex', $form_state->getValue('popup_text_hex'))
879       ->set('domains_option', $form_state->getValue('domains_option'))
880       ->set('domains_list', $form_state->getValue('domains_list'))
881       ->set('exclude_paths', $form_state->getValue('exclude_paths'))
882       ->set('exclude_admin_theme', $form_state->getValue('exclude_admin_theme'))
883       ->set('cookie_lifetime', $form_state->getValue('cookie_lifetime'))
884       ->set('cookie_session', $form_state->getValue('cookie_session'))
885       ->set('eu_only', $form_state->getValue('eu_only'))
886       ->set('eu_only_js', $form_state->getValue('eu_only_js'))
887       ->set('use_bare_css', $form_state->getValue('use_bare_css'))
888       ->set('disagree_do_not_show_popup', $form_state->getValue('disagree_do_not_show_popup'))
889       ->set('reload_page', $form_state->getValue('reload_page'))
890       ->set('cookie_name', $form_state->getValue('cookie_name'))
891       ->set('exclude_uid_1', $form_state->getValue('exclude_uid_1'))
892       ->set('better_support_for_screen_readers', $form_state->getValue('better_support_for_screen_readers'))
893       ->set('fixed_top_position', $form_state->getValue('fixed_top_position'))
894       ->set('method', $form_state->getValue('method'))
895       ->set('disagree_button_label', $form_state->getValue('disagree_button_label'))
896       ->set('whitelisted_cookies', $form_state->getValue('whitelisted_cookies'))
897       ->set('disabled_javascripts', $form_state->getValue('disabled_javascripts'))
898       ->set('consent_storage_method', $form_state->getValue('consent_storage_method'))
899       ->set('withdraw_message', $form_state->getValue('withdraw_message'))
900       ->set('withdraw_action_button_label', $form_state->getValue('withdraw_action_button_label'))
901       ->set('withdraw_tab_button_label', $form_state->getValue('withdraw_tab_button_label'))
902       ->set('withdraw_enabled', $form_state->getValue('withdraw_enabled'))
903       ->save();
904
905     parent::submitForm($form, $form_state);
906   }
907
908   /**
909    * Validates the banner link field.
910    *
911    * @param array $element
912    *   Element.
913    * @param \Drupal\Core\Form\FormStateInterface $form_state
914    *   Form state.
915    */
916   public function validatePopupLink(array $element, FormStateInterface &$form_state) {
917     if (empty($element['#value'])) {
918       return;
919     }
920
921     $input = $element['#value'];
922     if (UrlHelper::isExternal($input)) {
923       $allowed_protocols = ['http', 'https'];
924       if (!in_array(parse_url($input, PHP_URL_SCHEME), $allowed_protocols)) {
925         $form_state->setError($element, $this->t('Invalid protocol specified for the %name (valid protocols: %protocols).', [
926           '%name' => $element['#title'],
927           '%protocols' => implode(', ', $allowed_protocols),
928         ]));
929       }
930       else {
931         try {
932           Url::fromUri($input);
933         }
934         catch (\Exception $exc) {
935           $form_state->setError($element, $this->t('Invalid %name (:message).', ['%name' => $element['#title'], ':message' => $exc->getMessage()]));
936         }
937       }
938     }
939     else {
940       // Special case for '<front>'.
941       if ($input === '<front>') {
942         $input = '/';
943       }
944       try {
945         Url::fromUserInput($input);
946       }
947       catch (\Exception $exc) {
948         $form_state->setError($element, $this->t('Invalid URL in %name field (:message).', ['%name' => $element['#title'], ':message' => $exc->getMessage()]));
949       }
950     }
951   }
952
953 }