Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / eu_cookie_compliance / eu_cookie_compliance.module
1 <?php
2
3 /**
4  * @file
5  * The main file for the EU Cookie Compliance module.
6  *
7  * This module intends to deal with the EU Directive on Privacy and Electronic
8  * Communications that comes into effect in the UK on 26th May 2012.
9  */
10
11 use Drupal\Core\Form\FormStateInterface;
12 use Drupal\Core\Cache\Cache;
13 use Drupal\Core\Url;
14 use Drupal\Component\Utility\UrlHelper;
15 use Drupal\Component\Utility\Html;
16 use Drupal\Core\Routing\RouteMatchInterface;
17 use Drupal\smart_ip\SmartIp;
18 use Drupal\Core\Database\Database;
19 use Drupal\Core\Asset\AttachedAssetsInterface;
20 use Drupal\Component\Serialization\Json;
21
22 /**
23  * Implements hook_help().
24  */
25 function eu_cookie_compliance_help($route_name, RouteMatchInterface $route_match) {
26   switch ($route_name) {
27     case 'help.page.eu_cookie_compliance':
28       $output = '';
29       $output .= '<h3>' . t('About') . '</h3>';
30       $output .= '<p>' . t('This module intends to deal with the EU Directive on Privacy and Electronic Communications that comes into effect on 26th May 2012. From that date, if you are not compliant or visibly working towards compliance, you run the risk of enforcement action, which can include a fine of up to half a million pounds for a serious breach.') . '</p>';
31       $output .= '<h3>' . t('How it works') . '</h3>';
32       $output .= '<p>' . t('The module displays a banner at the bottom or the top of website to make users aware of the fact that cookies are being set. The user may then give his/her consent or move to a page that provides more details. Consent is given by user pressing the agree buttons or by continuing browsing the website. Once consent is given another banner appears with a "Thank you" message.') . '</p>';
33       $output .= '<p>' . t('The module provides a settings page where the banner can be customized. There are also template files for the banners that can be overridden by your theme.') . '</p>';
34       $output .= '<h3>' . t('Installation') . '</h3>';
35       $output .= '<ol><p><li>' . t('Unzip the files to the "sites/all/modules" OR "modules" directory and enable the module.') . '</li></p>';
36       $output .= '<p><li>' . t('If desired, give the administer EU Cookie Compliance banner permissions that allow users of certain roles access the administration page. You can do so on the admin/user/permissions page.') . '</li></p>';
37       // @codingStandardsIgnoreLine
38       $output .= "<p><label>-&nbsp;</label>" . t("there is also a 'display eu cookie compliance banner' permission that helps you show the banner to the roles you desire.") . "</p>";
39       $output .= '<p><li>' . t('You may want to create a page that would explain how your site uses cookies. Alternatively, if you have a privacy policy, you can link the banner to that page (see next step).') . '</li></p>';
40       $output .= '<p><li>' . t('Go to the admin/config/system/eu-cookie-compliance page to configure and enable the banner.') . '</li></p>';
41       $output .= '<p><li>' . t('If you want to customize the banner background and text color, either type in the hex values or simply install http://drupal.org/project/jquery_colorpicker.') . '</li></p>';
42       $output .= '<p><li>' . t('If you want to theme your banner override the themes in the template file.') . '</li></p>';
43       $output .= '<p><li>' . t('If you want to show the message in EU countries only, install the Smart IP module: http://drupal.org/project/smart_ip and enable the option on the admin page.') . '</li></p></ol>';
44       $output .= '<p><b>' . t('NOTICE: The module does not audit your cookies nor does it prevent cookies from being set.') . '</b></p>';
45       $output .= '<h3>' . t('For developers') . '</h3>';
46       $output .= '<p>' . t('If you want to conditionally set cookies in your module, there is a javascript function provided that returns TRUE if the current user has given his consent:') . '</p>';
47       $output .= '<p><code>Drupal.eu_cookie_compliance.hasAgreed()</code></p>';
48
49       return ['#markup' => $output];
50   }
51 }
52
53 /**
54  * Implements hook_page_attachments().
55  */
56 function eu_cookie_compliance_page_attachments(&$attachments) {
57   $config = Drupal::config('eu_cookie_compliance.settings');
58
59   // Check Add/Remove domains.
60   $domain_allow = TRUE;
61   $domain_option = $config->get('domains_option');
62
63   if (!empty($config->get('domains_list'))) {
64     global $base_url;
65
66     $domains_list = str_replace(["\r\n", "\r"], "\n", $config->get('domains_list'));
67     $domains_list = explode("\n", $domains_list);
68     $domains_list = preg_replace('{/$}', '', $domains_list);
69     $domain_match = in_array($base_url, $domains_list);
70
71     if ($domain_option && $domain_match) {
72       $domain_allow = FALSE;
73     }
74
75     if (!$domain_option && !$domain_match) {
76       $domain_allow = FALSE;
77     }
78   }
79
80   // Check exclude paths.
81   $path_match = FALSE;
82
83   if (!empty($config->get('exclude_paths'))) {
84     // Check both the URL path and the URL alias against the list to exclude.
85     $path = Drupal::service('path.current')->getPath();
86     $url_alias_path = \Drupal::service('path.alias_manager')->getAliasByPath($path);
87     $path_match = Drupal::service('path.matcher')->matchPath($path, $config->get('exclude_paths'));
88     $path_match_url_alias = Drupal::service('path.matcher')->matchPath($url_alias_path, $config->get('exclude_paths'));
89     $path_match = $path_match || $path_match_url_alias;
90     $exclude_paths = $config->get('exclude_paths');
91     Drupal::moduleHandler()->alter('eu_cookie_compliance_path_match', $path_match, $path, $exclude_paths);
92   }
93
94   // Check hide cookie compliance on admin theme.
95   $admin_theme_match = FALSE;
96
97   if ($config->get('exclude_admin_theme')) {
98     // Determines whether the active route is an admin one.
99     $is_route_admin = Drupal::service('router.admin_context')->isAdminRoute();
100     if ($is_route_admin) {
101       $admin_theme_match = TRUE;
102     }
103   }
104
105   $geoip_match = ['in_eu' => TRUE];
106   if (!empty($config->get('eu_only')) && $config->get('eu_only')) {
107     $geoip_match = eu_cookie_compliance_user_in_eu();
108   }
109
110   // Allow other modules to alter the geo IP matching logic.
111   Drupal::moduleHandler()->alter('eu_cookie_compliance_geoip_match', $geoip_match);
112
113   $uid1_match = TRUE;
114   if (Drupal::currentUser()->id() == 1 && !empty($config->get('exclude_uid_1')) && $config->get('exclude_uid_1')) {
115     $uid1_match = FALSE;
116   }
117
118   // Allow other modules to alter if the banner needs to be shown or not.
119   $modules_allow_popup = TRUE;
120   Drupal::moduleHandler()->alter('eu_cookie_compliance_show_popup', $modules_allow_popup);
121
122   if ($config->get('popup_enabled') && Drupal::currentUser()->hasPermission('display eu cookie compliance popup') && $geoip_match['in_eu'] && $domain_allow && !$path_match && !$admin_theme_match && $uid1_match && $modules_allow_popup) {
123     $language = Drupal::languageManager()->getCurrentLanguage();
124
125     $data['css'] = '';
126     // Color overrides.
127     if ($config->get('popup_bg_hex') !== '' && $config->get('popup_text_hex') !== '') {
128       $data['css'] = 'div#sliding-popup, div#sliding-popup .eu-cookie-withdraw-banner, .eu-cookie-withdraw-tab {background: #' . Html::escape($config->get('popup_bg_hex')) . '} div#sliding-popup.eu-cookie-withdraw-wrapper { background: transparent; } #sliding-popup h1, #sliding-popup h2, #sliding-popup h3, #sliding-popup p, .eu-cookie-compliance-more-button, .eu-cookie-compliance-secondary-button, .eu-cookie-withdraw-tab { color: #' . Html::escape($config->get('popup_text_hex')) . ';} .eu-cookie-withdraw-tab { border-color: #' . Html::escape($config->get('popup_text_hex')) . ';}';
129     }
130     if (!empty($config->get('popup_position')) && $config->get('popup_position') && !empty($config->get('fixed_top_position')) && $config->get('fixed_top_position')) {
131       $data['css'] .= '#sliding-popup.sliding-popup-top { position: fixed; }';
132     }
133
134     $method = $config->get('method');
135
136     if ($method == 'auto') {
137       $dnt = isset($_SERVER['HTTP_DNT']) ? $_SERVER['HTTP_DNT'] : NULL;
138       if ((int) $dnt === 0 && $dnt !== NULL) {
139         $method = 'default';
140       }
141       else {
142         $method = 'opt_in';
143       }
144     }
145
146     switch ($method) {
147       case 'default':
148         $click_confirmation = $config->get('popup_clicking_confirmation');
149         $scroll_confirmation = $config->get('popup_scrolling_confirmation');
150         $primary_button_label = $config->get('popup_agree_button_message');
151         $primary_button_class = 'agree-button eu-cookie-compliance-default-button';
152         $secondary_button_label = '';
153         $secondary_button_class = '';
154         break;
155
156       case 'opt_in':
157         $click_confirmation = FALSE;
158         $scroll_confirmation = FALSE;
159         $primary_button_label = $config->get('popup_agree_button_message');
160         $primary_button_class = 'agree-button eu-cookie-compliance-secondary-button';
161         $secondary_button_label = $config->get('disagree_button_label');
162         $secondary_button_class = 'decline-button eu-cookie-compliance-default-button';
163         break;
164
165       case 'opt_out':
166         $click_confirmation = FALSE;
167         $scroll_confirmation = FALSE;
168         $primary_button_label = $config->get('disagree_button_label');
169         $primary_button_class = 'decline-button eu-cookie-compliance-secondary-button';
170         $secondary_button_label = $config->get('popup_agree_button_message');
171         $secondary_button_class = 'agree-button eu-cookie-compliance-default-button';
172         break;
173     }
174
175     $popup_text_info = str_replace(["\r", "\n"], '', $config->get('popup_info.value'));
176     $popup_text_agreed = str_replace(["\r", "\n"], '', $config->get('popup_agreed.value'));
177     $withdraw_markup = str_replace(["\r", "\n"], '', $config->get('withdraw_message.value'));
178     $html_info = [
179       '#theme' => 'eu_cookie_compliance_popup_info',
180       '#message' => check_markup($popup_text_info, $config->get('popup_info.format'), FALSE),
181       '#agree_button' => $primary_button_label,
182       '#disagree_button' => ($config->get('show_disagree_button') == TRUE) ? $config->get('popup_disagree_button_message') : FALSE,
183       '#secondary_button_label' => $secondary_button_label,
184       '#primary_button_class' => $primary_button_class,
185       '#secondary_button_class' => $secondary_button_class,
186     ];
187     $mobile_popup_text_info = str_replace(["\r", "\n"], '', $config->get('mobile_popup_info.value'));
188     $mobile_html_info = [
189       '#theme' => 'eu_cookie_compliance_popup_info',
190       '#message' => check_markup($mobile_popup_text_info, $config->get('popup_info.format'), FALSE),
191       '#agree_button' => $primary_button_label,
192       '#disagree_button' => ($config->get('show_disagree_button') == TRUE) ? $config->get('popup_disagree_button_message') : FALSE,
193       '#secondary_button_label' => $secondary_button_label,
194       '#primary_button_class' => $primary_button_class,
195       '#secondary_button_class' => $secondary_button_class,
196     ];
197     $html_agreed = [
198       '#theme' => 'eu_cookie_compliance_popup_agreed',
199       '#message' => check_markup($popup_text_agreed, $config->get('popup_agreed.format'), FALSE),
200       '#hide_button' => $config->get('popup_hide_button_message'),
201       '#find_more_button' => ($config->get('show_disagree_button') == TRUE) ? $config->get('popup_find_more_button_message') : FALSE,
202     ];
203     $withdraw_markup = [
204       '#theme' => 'eu_cookie_compliance_withdraw',
205       '#message' => check_markup($withdraw_markup, $config->get('withdraw_message.format'), FALSE),
206       '#withdraw_tab_button_label' => $config->get('withdraw_tab_button_label'),
207       '#withdraw_action_button_label' => $config->get('withdraw_action_button_label'),
208     ];
209
210     $was_debugging = FALSE;
211
212     /*
213      * @var $twig_service Twig_Environment
214      */
215     $twig_service = Drupal::service('twig');
216
217     if ($twig_service->isDebug()) {
218       $was_debugging = TRUE;
219       $twig_service->disableDebug();
220     }
221
222     $html_info = trim(Drupal::service('renderer')->renderRoot($html_info)->__toString());
223     $mobile_html_info = trim(Drupal::service('renderer')->renderRoot($mobile_html_info)->__toString());
224     $html_agreed = trim(Drupal::service('renderer')->renderRoot($html_agreed)->__toString());
225     $withdraw_markup = trim(Drupal::service('renderer')->renderRoot($withdraw_markup)->__toString());
226
227     if ($was_debugging) {
228       $twig_service->enableDebug();
229     }
230
231     $popup_link = $config->get('popup_link');
232     if (UrlHelper::isExternal($popup_link)) {
233       $popup_link = Url::fromUri($popup_link);
234     }
235     else {
236       // Guard against translations being entered without leading slash.
237       if (substr( $popup_link, 0, 1) != '/' && substr( $popup_link, 0, 1) != '?' && substr( $popup_link, 0, 1) != '#') {
238         $popup_link = '/' . $popup_link;
239       }
240       $popup_link = $popup_link === '<front>' ? '/' : $popup_link;
241       $popup_link = Url::fromUserInput($popup_link);
242     }
243     $popup_link = $popup_link->toString();
244
245     $data['variables'] = [
246       'popup_enabled'        => $config->get('popup_enabled'),
247       'popup_agreed_enabled' => $config->get('popup_agreed_enabled'),
248       'popup_hide_agreed'    => $config->get('popup_hide_agreed'),
249       'popup_clicking_confirmation' => $click_confirmation,
250       'popup_scrolling_confirmation' => $scroll_confirmation,
251       'popup_html_info'      => $config->get('popup_enabled') ? $html_info : FALSE,
252       'use_mobile_message'   => !empty($config->get('use_mobile_message')) ? $config->get('use_mobile_message') : FALSE,
253       'mobile_popup_html_info' => $config->get('popup_enabled') ? $mobile_html_info : FALSE,
254       'mobile_breakpoint'    => !empty($config->get('mobile_breakpoint')) ? $config->get('mobile_breakpoint') : '768',
255       'popup_html_agreed'    => $config->get('popup_agreed_enabled') ? $html_agreed : FALSE,
256       'popup_use_bare_css'   => !empty($config->get('popup_use_bare_css')) ? $config->get('popup_use_bare_css') : FALSE,
257       'popup_height'         => !empty($config->get('popup_height')) ? $config->get('popup_height') : 'auto',
258       'popup_width'          => !empty($config->get('popup_width')) ? $config->get('popup_width') : '100%',
259       'popup_delay'          => (int) ($config->get('popup_delay')),
260       'popup_link'           => $popup_link,
261       'popup_link_new_window' => $config->get('popup_link_new_window'),
262       'popup_position'       => $config->get('popup_position'),
263       'popup_language'       => $language->getId(),
264       'store_consent'        => $config->get('consent_storage_method') != 'do_not_store',
265       'better_support_for_screen_readers'         => !empty($config->get('better_support_for_screen_readers')) ? $config->get('better_support_for_screen_readers') : FALSE,
266       'cookie_name'          => !empty($config->get('cookie_name')) ? $config->get('cookie_name') : '',
267       'reload_page'          => !empty($config->get('reload_page')) ? $config->get('reload_page') : FALSE,
268       'domain'               => $config->get('domain'),
269       'popup_eu_only_js'     => !empty($config->get('eu_only_js')) ? $config->get('eu_only_js') : FALSE,
270       'cookie_lifetime'      => $config->get('cookie_lifetime'),
271       'cookie_session'       => $config->get('cookie_session'),
272       'disagree_do_not_show_popup' => !empty($config->get('disagree_do_not_show_popup')) ? $config->get('disagree_do_not_show_popup') : FALSE,
273       'method'               => $method,
274       'whitelisted_cookies'  => !empty($config->get('whitelisted_cookies')) ? $config->get('whitelisted_cookies') : '',
275       'withdraw_markup'      => $withdraw_markup,
276       'withdraw_enabled'     => $config->get('withdraw_enabled'),
277     ];
278
279     $attachments['#attached']['drupalSettings']['eu_cookie_compliance'] = $data['variables'];
280     if ($config->get('use_bare_css')) {
281       $attachments['#attached']['library'][] = 'eu_cookie_compliance/eu_cookie_compliance_bare';
282     }
283     else {
284       $attachments['#attached']['library'][] = 'eu_cookie_compliance/eu_cookie_compliance';
285     }
286     // Add inline javascript.
287     $disabled_javascripts = $config->get('disabled_javascripts');
288     $load_disabled_scripts = '';
289     if ($disabled_javascripts != '') {
290       $load_disabled_scripts = '';
291       $disabled_javascripts = _eu_cookie_compliance_explode_multiple_lines($disabled_javascripts);
292       foreach ($disabled_javascripts as $script) {
293         if (substr($script, 0, 4) !== 'http' && substr($script, 0, 2) !== '//') {
294           $script = '/' . $script;
295         }
296         $load_disabled_scripts .= 'var scriptTag = document.createElement("script");';
297         $load_disabled_scripts .= 'scriptTag.src = ' . Json::encode($script) . ';';
298         $load_disabled_scripts .= 'document.body.appendChild(scriptTag);';
299       }
300     }
301
302     $attachments['#attached']['html_head'][] = [
303       [
304         '#type' => 'html_tag',
305         '#tag' => 'script',
306         '#value' => 'function euCookieComplianceLoadScripts() {' . $load_disabled_scripts . '}',
307       ],
308       'eu-cookie-compliance-js',
309     ];
310
311     // Add inline css.
312     $attachments['#attached']['html_head'][] = [
313       [
314         '#tag' => 'style',
315         '#value' => $data['css'],
316       ],
317       'eu-cookie-compliance-css',
318     ];
319     $cache_tags = isset($attachments['#cache']['tags']) ? $attachments['#cache']['tags'] : [];
320     $attachments['#cache']['tags'] = Cache::mergeTags($cache_tags, $config->getCacheTags());
321
322     // Check if disabled scripts are in page html_head.
323     $disabled_javascripts = $config->get('disabled_javascripts');
324     $disabled_javascripts = _eu_cookie_compliance_explode_multiple_lines($disabled_javascripts);
325     $disabled_javascripts = array_filter($disabled_javascripts);
326
327     if (!empty($disabled_javascripts)) {
328       foreach ($attachments['#attached']['html_head'] as $index => $asset) {
329         $is_script = !empty($asset[0]['#type']) && $asset[0]['#type'] == 'html_tag' && $asset[0]['#tag'] == 'script';
330         $is_src = !empty($asset[0]['#attributes']['src']);
331         if (!$is_script || !$is_src) {
332           continue;
333         }
334         $src = $asset[0]['#attributes']['src'];
335         $src = preg_replace('/\.js\?[^"]+/', '.js', $src);
336         $src = preg_replace('/^\//', '', $src);
337         if (in_array($src, $disabled_javascripts)) {
338           $attachments['#attached']['html_head'][$index][0]['#access'] = FALSE;
339         }
340       }
341     }
342   }
343 }
344
345 /**
346  * Implements hook_theme().
347  */
348 function eu_cookie_compliance_theme($existing, $type, $theme, $path) {
349   return [
350     'eu_cookie_compliance_popup_info' => [
351       'template' => 'eu_cookie_compliance_popup_info',
352       'variables' => [
353         'message' => NULL,
354         'agree_button' => NULL,
355         'disagree_button' => NULL,
356         'secondary_button_label' => NULL,
357         'primary_button_class' => NULL,
358         'secondary_button_class' => NULL,
359       ],
360     ],
361     'eu_cookie_compliance_popup_agreed' => [
362       'template' => 'eu_cookie_compliance_popup_agreed',
363       'variables' => [
364         'message' => NULL,
365         'hide_button' => NULL,
366         'find_more_button' => NULL,
367       ],
368     ],
369     'eu_cookie_compliance_withdraw' => [
370       'template' => 'eu_cookie_compliance_withdraw',
371       'variables' => [
372         'withdraw_tab_button_label' => NULL,
373         'message' => NULL,
374         'withdraw_action_button_label' => NULL,
375       ],
376     ],
377   ];
378 }
379
380 /**
381  * Validate field for a HEX value if a value is set.
382  *
383  * @param array $element
384  *   Element.
385  * @param \Drupal\Core\Form\FormStateInterface $form_state
386  *   Form State Interface.
387  */
388 function eu_cookie_compliance_validate_hex(array $element, FormStateInterface &$form_state) {
389   if (!empty($element['#value']) && !preg_match('/^[0-9a-fA-F]{3,6}$/', $element['#value'])) {
390     $form_state->setError($element, t('%name must be a HEX value (without leading #) or empty.', ['%name' => $element['#title']]));
391   }
392 }
393
394 /**
395  * Check if the user is in the EU.
396  */
397 function eu_cookie_compliance_user_in_eu() {
398   $geoip_match = FALSE;
399   $eu_countries_default = [
400     NULL, 'BE', 'BG', 'CZ', 'DK', 'DE', 'EE', 'IE', 'EL', 'ES', 'FR', 'HR',
401     'IT', 'CY', 'LV', 'LT', 'LU', 'HU', 'MT', 'NL', 'AT', 'PL', 'PT', 'RO',
402     'SI', 'SK', 'FI', 'SE', 'UK', 'GB', 'NO',
403   ];
404   // Allow custom array of countries to be loaded from settings.php, defaulting
405   // to the array above.
406   $config = Drupal::config('eu_cookie_compliance.settings');
407   $eu_countries = !empty($config->get('eu_countries')) ? $config->get('eu_countries') : $eu_countries_default;
408
409   $ip_address = Drupal::request()->getClientIp();
410
411   $country_code = extension_loaded('geoip') ? geoip_country_code_by_name($ip_address) : '';
412   if (Drupal::moduleHandler()->moduleExists('smart_ip')) {
413     $smart_ip_session = SmartIp::query($ip_address);
414     $country_code = isset($smart_ip_session['countryCode']) ? $smart_ip_session['countryCode'] : NULL;
415   }
416   if (in_array($country_code, $eu_countries) || $country_code == '' || $country_code == '-') {
417     $geoip_match = TRUE;
418   }
419
420   return [
421     'country' => $country_code,
422     'in_eu' => $geoip_match,
423   ];
424 }
425
426 /**
427  * Implements hook_js_alter().
428  */
429 function eu_cookie_compliance_js_alter(&$javascript, AttachedAssetsInterface $assets) {
430   $config = Drupal::config('eu_cookie_compliance.settings');
431   $disabled_javascripts = $config->get('disabled_javascripts');
432   $disabled_javascripts = _eu_cookie_compliance_explode_multiple_lines($disabled_javascripts);
433
434   foreach ($disabled_javascripts as $script) {
435     unset($javascript[$script]);
436   }
437 }
438
439 /**
440  * Splits a return delimited text string into an array.
441  *
442  * @param string $text
443  *   Text to split.
444  *
445  * @return array
446  *   Text split into an array.
447  */
448 function _eu_cookie_compliance_explode_multiple_lines($text) {
449   $text = explode("\r\n", $text);
450   if (count($text) == 1) {
451     $text = explode("\r", $text[0]);
452   }
453   if (count($text) == 1) {
454     $text = explode("\n", $text[0]);
455   }
456   return $text;
457 }
458
459 /**
460  * Attempt to find the cookie/privacy policy by searching for common titles.
461  *
462  * @return bool|string
463  *   URL to the node if found, otherwise FALSE.
464  */
465 function _eu_cookie_compliance_find_privacy_policy() {
466   if (!\Drupal::entityTypeManager()->hasDefinition('node')) {
467     return FALSE;
468   }
469
470   $pattern = 'privacy|privacy +policy|cookie +policy|terms +of +use|terms +of +service|terms +and +conditions';
471
472   $connection = Database::getConnection();
473   // Select operator based on the database type.
474   switch ($connection->databaseType()) {
475     case 'pgsql':
476       $op = '~*';
477       break;
478
479     case 'sqlite':
480       $op = 'REGEXP';
481       break;
482
483     default:
484       $op = 'RLIKE';
485   }
486
487   $query = \Drupal::entityQuery('node')
488     ->condition('title', $pattern, $op);
489
490   $result = $query->execute();
491   if (!empty($result)) {
492     return ('/node/' . array_shift($result));
493   }
494   return FALSE;
495 }
496
497 /**
498  * Helper function to set module weight.
499  */
500 function eu_cookie_compliance_module_set_weight() {
501   $weight = 1;
502   $exclude_modules = [
503     'eu_cookie_compliance',
504   ];
505
506   $extension_config = \Drupal::configFactory()->get('core.extension');
507   // Loop through all installed modules to find the highest weight.
508   foreach ($extension_config->get('module') as $module_name => $module_weight) {
509     if ($module_weight > $weight && !in_array($module_name, $exclude_modules)) {
510       $weight = $module_weight + 1;
511     }
512   }
513   module_set_weight('eu_cookie_compliance', $weight);
514 }