diff --git a/frontend/drupal/sites/all/modules/ctools/bulk_export/bulk_export.info b/frontend/drupal/sites/all/modules/ctools/bulk_export/bulk_export.info index 9bb339445..310216207 100644 --- a/frontend/drupal/sites/all/modules/ctools/bulk_export/bulk_export.info +++ b/frontend/drupal/sites/all/modules/ctools/bulk_export/bulk_export.info @@ -4,8 +4,8 @@ core = 7.x dependencies[] = ctools package = Chaos tool suite -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/ctools.info b/frontend/drupal/sites/all/modules/ctools/ctools.info index 188b55dde..2a25ea4f4 100644 --- a/frontend/drupal/sites/all/modules/ctools/ctools.info +++ b/frontend/drupal/sites/all/modules/ctools/ctools.info @@ -19,8 +19,8 @@ files[] = tests/object_cache.test files[] = tests/object_cache_unit.test files[] = tests/page_tokens.test -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/ctools.module b/frontend/drupal/sites/all/modules/ctools/ctools.module index 62f291923..2ec0ab73f 100644 --- a/frontend/drupal/sites/all/modules/ctools/ctools.module +++ b/frontend/drupal/sites/all/modules/ctools/ctools.module @@ -479,7 +479,7 @@ function ctools_uuid_generate() { if (!module_exists('uuid')) { ctools_include('uuid'); - $callback = drupal_static(__FUNCTION__); + $callback = &drupal_static(__FUNCTION__); if (empty($callback)) { if (function_exists('uuid_create') && !function_exists('uuid_make')) { @@ -903,13 +903,14 @@ function ctools_process(&$variables, $hook) { */ function ctools_access_menu($access) { + $func_args = func_get_args(); // Short circuit everything if there are no access tests. if (empty($access['plugins'])) { return TRUE; } $contexts = array(); - foreach (func_get_args() as $arg) { + foreach ($func_args as $arg) { if (is_object($arg) && get_class($arg) == 'ctools_context') { $contexts[$arg->id] = $arg; } diff --git a/frontend/drupal/sites/all/modules/ctools/ctools_access_ruleset/ctools_access_ruleset.info b/frontend/drupal/sites/all/modules/ctools/ctools_access_ruleset/ctools_access_ruleset.info index 3fc355649..d2a693dcc 100644 --- a/frontend/drupal/sites/all/modules/ctools/ctools_access_ruleset/ctools_access_ruleset.info +++ b/frontend/drupal/sites/all/modules/ctools/ctools_access_ruleset/ctools_access_ruleset.info @@ -4,8 +4,8 @@ core = 7.x package = Chaos tool suite dependencies[] = ctools -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/ctools_ajax_sample/ctools_ajax_sample.info b/frontend/drupal/sites/all/modules/ctools/ctools_ajax_sample/ctools_ajax_sample.info index c2a52df40..a7012121a 100644 --- a/frontend/drupal/sites/all/modules/ctools/ctools_ajax_sample/ctools_ajax_sample.info +++ b/frontend/drupal/sites/all/modules/ctools/ctools_ajax_sample/ctools_ajax_sample.info @@ -4,8 +4,8 @@ package = Chaos tool suite dependencies[] = ctools core = 7.x -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/ctools_ajax_sample/ctools_ajax_sample.module b/frontend/drupal/sites/all/modules/ctools/ctools_ajax_sample/ctools_ajax_sample.module index 7cb1ff1c6..c022fdb61 100644 --- a/frontend/drupal/sites/all/modules/ctools/ctools_ajax_sample/ctools_ajax_sample.module +++ b/frontend/drupal/sites/all/modules/ctools/ctools_ajax_sample/ctools_ajax_sample.module @@ -187,7 +187,7 @@ function ctools_ajax_sample_page() { ); } - $output .= theme('table', array('header' => $header, 'rows' => $rows, array('class' => array('ajax-sample-table')))); + $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('class' => array('ajax-sample-table')))); // Show examples of ctools javascript widgets. $output .= '

' . t('CTools Javascript Widgets') . '

'; diff --git a/frontend/drupal/sites/all/modules/ctools/ctools_custom_content/ctools_custom_content.info b/frontend/drupal/sites/all/modules/ctools/ctools_custom_content/ctools_custom_content.info index 2bc73489c..9d71e8f19 100644 --- a/frontend/drupal/sites/all/modules/ctools/ctools_custom_content/ctools_custom_content.info +++ b/frontend/drupal/sites/all/modules/ctools/ctools_custom_content/ctools_custom_content.info @@ -4,8 +4,8 @@ core = 7.x package = Chaos tool suite dependencies[] = ctools -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/ctools_plugin_example/ctools_plugin_example.info b/frontend/drupal/sites/all/modules/ctools/ctools_plugin_example/ctools_plugin_example.info index 3f268dbc0..d57071832 100644 --- a/frontend/drupal/sites/all/modules/ctools/ctools_plugin_example/ctools_plugin_example.info +++ b/frontend/drupal/sites/all/modules/ctools/ctools_plugin_example/ctools_plugin_example.info @@ -7,8 +7,8 @@ dependencies[] = page_manager dependencies[] = advanced_help core = 7.x -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/includes/modal.inc b/frontend/drupal/sites/all/modules/ctools/includes/modal.inc index 18b216fc4..30eadbf40 100644 --- a/frontend/drupal/sites/all/modules/ctools/includes/modal.inc +++ b/frontend/drupal/sites/all/modules/ctools/includes/modal.inc @@ -74,7 +74,7 @@ function ctools_modal_add_js() { } /** - * @todo this is deprecated + * @todo Deprecated this. */ function ctools_modal_add_plugin_js($plugins) { $css = array(); @@ -85,7 +85,7 @@ function ctools_modal_add_plugin_js($plugins) { if (file_exists($file)) { $js[$file] = TRUE; } - elseif (file(exists($subtype['path'] . '/' . $file))) { + elseif (file_exists($subtype['path'] . '/' . $file)) { $js[$subtype['path'] . '/' . $file] = TRUE; } } @@ -95,7 +95,7 @@ function ctools_modal_add_plugin_js($plugins) { if (file_exists($file)) { $css[$file] = TRUE; } - elseif (file(exists($subtype['path'] . '/' . $file))) { + elseif (file_exists($subtype['path'] . '/' . $file)) { $css[$subtype['path'] . '/' . $file] = TRUE; } } diff --git a/frontend/drupal/sites/all/modules/ctools/includes/object-cache.inc b/frontend/drupal/sites/all/modules/ctools/includes/object-cache.inc index ef52f9ade..34c26623a 100644 --- a/frontend/drupal/sites/all/modules/ctools/includes/object-cache.inc +++ b/frontend/drupal/sites/all/modules/ctools/includes/object-cache.inc @@ -21,13 +21,12 @@ * @param $name * The name of the object being stored. * @param $skip_cache + * Deprecated in favor of drupal_static* * Skip the memory cache, meaning this must be read from the db again. * @param $sid * The session id, allowing someone to use Session API or their own solution; * defaults to session_id(). * - * @deprecated $skip_cache is deprecated in favor of drupal_static* - * * @return * The data that was cached. */ diff --git a/frontend/drupal/sites/all/modules/ctools/js/collapsible-div.js b/frontend/drupal/sites/all/modules/ctools/js/collapsible-div.js index 134151c3d..4e21a2259 100644 --- a/frontend/drupal/sites/all/modules/ctools/js/collapsible-div.js +++ b/frontend/drupal/sites/all/modules/ctools/js/collapsible-div.js @@ -123,7 +123,7 @@ var cookie = ''; // Get a list of IDs, saparated by comma - for (i in this.state) { + for (var i in this.state) { if (cookie != '') { cookie += ','; } @@ -190,7 +190,7 @@ var afterToggle = function () { if (Drupal.CTools.CollapsibleCallbacksAfterToggle) { - for (i in Drupal.CTools.CollapsibleCallbacksAfterToggle) { + for (var i in Drupal.CTools.CollapsibleCallbacksAfterToggle) { Drupal.CTools.CollapsibleCallbacksAfterToggle[i]($container, handle, content, toggle); } } @@ -198,7 +198,7 @@ var clickMe = function () { if (Drupal.CTools.CollapsibleCallbacks) { - for (i in Drupal.CTools.CollapsibleCallbacks) { + for (var i in Drupal.CTools.CollapsibleCallbacks) { Drupal.CTools.CollapsibleCallbacks[i]($container, handle, content, toggle); } } diff --git a/frontend/drupal/sites/all/modules/ctools/js/dependent.js b/frontend/drupal/sites/all/modules/ctools/js/dependent.js index a60fc1201..77777e38e 100644 --- a/frontend/drupal/sites/all/modules/ctools/js/dependent.js +++ b/frontend/drupal/sites/all/modules/ctools/js/dependent.js @@ -39,7 +39,7 @@ Drupal.CTools.dependent.autoAttach = function() { // Clear active bindings and triggers. - for (i in Drupal.CTools.dependent.activeTriggers) { + for (var i in Drupal.CTools.dependent.activeTriggers) { $(Drupal.CTools.dependent.activeTriggers[i]).unbind('change.ctools-dependent'); } Drupal.CTools.dependent.activeTriggers = []; @@ -51,7 +51,7 @@ } // Iterate through all relationships - for (id in Drupal.settings.CTools.dependent) { + for (var id in Drupal.settings.CTools.dependent) { // Test to make sure the id even exists; this helps clean up multiple // AJAX calls with multiple forms. @@ -59,7 +59,7 @@ // whether the binding is active or not. Defaults to no. Drupal.CTools.dependent.activeBindings[id] = 0; // Iterate through all possible values - for(bind_id in Drupal.settings.CTools.dependent[id].values) { + for (var bind_id in Drupal.settings.CTools.dependent[id].values) { // This creates a backward relationship. The bind_id is the ID // of the element which needs to change in order for the id to hide or become shown. // The id is the ID of the item which will be conditionally hidden or shown. @@ -87,7 +87,7 @@ } var getValue = function(item, trigger) { - if ($(trigger).size() == 0) { + if ($(trigger).length == 0) { return null; } @@ -129,7 +129,7 @@ return; } - for (i in Drupal.CTools.dependent.bindings[bind_id]) { + for (var i in Drupal.CTools.dependent.bindings[bind_id]) { var id = Drupal.CTools.dependent.bindings[bind_id][i]; // Fix numerous errors if (typeof id != 'string') { @@ -150,7 +150,7 @@ } var len = 0; - for (i in Drupal.CTools.dependent.activeBindings[id]) { + for (var i in Drupal.CTools.dependent.activeBindings[id]) { len++; } diff --git a/frontend/drupal/sites/all/modules/ctools/js/stylizer.js b/frontend/drupal/sites/all/modules/ctools/js/stylizer.js index 12ab72048..af53265d9 100644 --- a/frontend/drupal/sites/all/modules/ctools/js/stylizer.js +++ b/frontend/drupal/sites/all/modules/ctools/js/stylizer.js @@ -5,7 +5,7 @@ Drupal.CTools.Stylizer.addFarbtastic = function(context) { // This behavior attaches by ID, so is only valid once on a page. - if ($('#ctools_stylizer_color_scheme_form .color-form.Stylizer-processed').size()) { + if ($('#ctools_stylizer_color_scheme_form .color-form.Stylizer-processed').length)) { return; } @@ -21,7 +21,7 @@ // Decode reference colors to HSL /*var reference = Drupal.settings.Stylizer.reference.clone(); - for (i in reference) { + for (var i in reference) { reference[i] = farb.RGBToHSL(farb.unpack(reference[i])); } */ @@ -30,7 +30,7 @@ var colors = this.options[this.selectedIndex].value; if (colors != '') { colors = colors.split(','); - for (i in colors) { + for (var i in colors) { callback(inputs[i], colors[i], false, true); } } diff --git a/frontend/drupal/sites/all/modules/ctools/page_manager/page_manager.info b/frontend/drupal/sites/all/modules/ctools/page_manager/page_manager.info index a56663b8d..eb116ccbf 100644 --- a/frontend/drupal/sites/all/modules/ctools/page_manager/page_manager.info +++ b/frontend/drupal/sites/all/modules/ctools/page_manager/page_manager.info @@ -6,8 +6,8 @@ package = Chaos tool suite files[] = tests/head_links.test -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/page_manager/page_manager.module b/frontend/drupal/sites/all/modules/ctools/page_manager/page_manager.module index c614eff7d..ed2066b46 100644 --- a/frontend/drupal/sites/all/modules/ctools/page_manager/page_manager.module +++ b/frontend/drupal/sites/all/modules/ctools/page_manager/page_manager.module @@ -297,7 +297,7 @@ function page_manager_theme() { * Get the cached changes to a given task handler. */ function page_manager_get_page_cache($task_name) { - $caches = drupal_static(__FUNCTION__, array()); + $caches = &drupal_static(__FUNCTION__, array()); if (!isset($caches[$task_name])) { ctools_include('object-cache'); $cache = ctools_object_cache_get('page_manager_page', $task_name); diff --git a/frontend/drupal/sites/all/modules/ctools/page_manager/plugins/tasks/search.inc b/frontend/drupal/sites/all/modules/ctools/page_manager/plugins/tasks/search.inc index 172d963ab..e561c7179 100644 --- a/frontend/drupal/sites/all/modules/ctools/page_manager/plugins/tasks/search.inc +++ b/frontend/drupal/sites/all/modules/ctools/page_manager/plugins/tasks/search.inc @@ -97,9 +97,9 @@ function page_manager_search_menu_alter(&$items, $task) { * Entry point for our overridden search page. */ function page_manager_search_page($type) { - ctools_include('menu'); // Get the arguments and construct a keys string out of them. $args = func_get_args(); + ctools_include('menu'); // We have to remove the $type. array_shift($args); diff --git a/frontend/drupal/sites/all/modules/ctools/page_manager/plugins/tasks/term_view.inc b/frontend/drupal/sites/all/modules/ctools/page_manager/plugins/tasks/term_view.inc index 72fb7dbc9..101b67442 100644 --- a/frontend/drupal/sites/all/modules/ctools/page_manager/plugins/tasks/term_view.inc +++ b/frontend/drupal/sites/all/modules/ctools/page_manager/plugins/tasks/term_view.inc @@ -21,7 +21,7 @@ function page_manager_term_view_page_manager_tasks() { 'title' => t('Taxonomy term template'), 'admin title' => t('Taxonomy term template'), - 'admin description' => t('When enabled, this overrides the default Drupal behavior for displaying taxonomy terms at taxonomy/term/%term. If you add variants, you may use selection criteria such as vocabulary or user access to provide different displays of the taxonomy term and associated nodes. If no variant is selected, the default Drupal taxonomy term display will be used. This page only affects items actually displayed ad taxonomy/term/%term. Some taxonomy terms, such as forums, have their displays moved elsewhere. Also please note that if you are using pathauto, aliases may make a taxonomy terms appear somewhere else, but as far as Drupal is concerned, they are still at taxonomy/term/%term.'), + 'admin description' => t('When enabled, this overrides the default Drupal behavior for displaying taxonomy terms at taxonomy/term/%term. If you add variants, you may use selection criteria such as vocabulary or user access to provide different displays of the taxonomy term and associated nodes. If no variant is selected, the default Drupal taxonomy term display will be used. This page only affects items actually displayed at taxonomy/term/%term. Some taxonomy terms, such as forums, have their displays moved elsewhere. Also please note that if you are using pathauto, aliases may make a taxonomy terms appear somewhere else, but as far as Drupal is concerned, they are still at taxonomy/term/%term.'), 'admin path' => 'taxonomy/term/%taxonomy_term', 'admin summary' => 'page_manager_term_view_admin_summary', diff --git a/frontend/drupal/sites/all/modules/ctools/page_manager/tests/head_links.test b/frontend/drupal/sites/all/modules/ctools/page_manager/tests/head_links.test new file mode 100644 index 000000000..3a88e295b --- /dev/null +++ b/frontend/drupal/sites/all/modules/ctools/page_manager/tests/head_links.test @@ -0,0 +1,77 @@ + 'Head links test', + 'description' => 'Checks that the shortlink and canonical links are present on a node page overriden by Page manager', + 'group' => 'ctools', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp('page_manager'); + + // First add an override for "node/%node". + variable_set('page_manager_node_view_disabled', FALSE); + + $record = (object) array( + 'name' => 'node_view__http_response_707659df-062d-4252-8c2a-22a8e0289cd4', + 'task' => 'node_view', + 'subtask' => '', + 'handler' => 'http_response', + 'weight' => '1', + 'conf' => array( + 'title' => 'Test', + 'contexts' => array( + 0 => array( + 'identifier' => 'String', + 'keyword' => 'string', + 'name' => 'string', + 'string' => 'Test', + 'id' => 1, + ), + ), + 'relationships' => array(), + 'code' => '404', + 'destination' => '', + 'name' => '', + ), + ); + + page_manager_save_task_handler($record); + + menu_rebuild(); + } + + /** + * Test the presence of the head links. + */ + public function testHeadLinks() { + $node = $this->drupalCreateNode(); + $url = 'node/' . $node->nid; + $this->drupalGet($url); + + $shortlink = $this->xpath('//head//link[@rel="shortlink"]'); + $this->assertEqual(url($url), (string) $shortlink[0]['href'], 'shortlink url found'); + + $canonical = $this->xpath('//head//link[@rel="canonical"]'); + $this->assertEqual(url($url), (string) $canonical[0]['href'], 'canonical url found'); + } + +} diff --git a/frontend/drupal/sites/all/modules/ctools/plugins/access/term.inc b/frontend/drupal/sites/all/modules/ctools/plugins/access/term.inc index 50ec0d90f..224a00c6f 100644 --- a/frontend/drupal/sites/all/modules/ctools/plugins/access/term.inc +++ b/frontend/drupal/sites/all/modules/ctools/plugins/access/term.inc @@ -15,7 +15,7 @@ $plugin = array( 'callback' => 'ctools_term_ctools_access_check', 'default' => array('vids' => array()), 'settings form' => 'ctools_term_ctools_access_settings', - 'settings form validation' => 'ctools_term_ctools_access_settings_validate', + 'settings form validate' => 'ctools_term_ctools_access_settings_validate', 'settings form submit' => 'ctools_term_ctools_access_settings_submit', 'summary' => 'ctools_term_ctools_access_summary', 'required context' => new ctools_context_required(t('Term'), array('taxonomy_term', 'terms')), diff --git a/frontend/drupal/sites/all/modules/ctools/plugins/access/term_parent.inc b/frontend/drupal/sites/all/modules/ctools/plugins/access/term_parent.inc index 27375dfaf..0fddc020d 100644 --- a/frontend/drupal/sites/all/modules/ctools/plugins/access/term_parent.inc +++ b/frontend/drupal/sites/all/modules/ctools/plugins/access/term_parent.inc @@ -15,7 +15,7 @@ $plugin = array( 'callback' => 'ctools_term_parent_ctools_access_check', 'default' => array('vid' => array(), 'negate' => 0), 'settings form' => 'ctools_term_parent_ctools_access_settings', - 'settings form validation' => 'ctools_term_parent_ctools_access_settings_validate', + 'settings form validate' => 'ctools_term_parent_ctools_access_settings_validate', 'settings form submit' => 'ctools_term_parent_ctools_access_settings_submit', 'summary' => 'ctools_term_parent_ctools_access_summary', 'required context' => new ctools_context_required(t('Term'), array('taxonomy_term', 'terms')), diff --git a/frontend/drupal/sites/all/modules/ctools/plugins/content_types/block/block.inc b/frontend/drupal/sites/all/modules/ctools/plugins/content_types/block/block.inc index ff75c7504..ee5ce5764 100644 --- a/frontend/drupal/sites/all/modules/ctools/plugins/content_types/block/block.inc +++ b/frontend/drupal/sites/all/modules/ctools/plugins/content_types/block/block.inc @@ -352,7 +352,7 @@ function block_ctools_block_info($module, $delta, &$info) { // The title of custom blocks from the block module is stored in the // {block} table. Look for it in the default theme as a reasonable // default value for the title. - $block_info_cache = drupal_static(__FUNCTION__); + $block_info_cache = &drupal_static(__FUNCTION__); if (!isset($block_info_cache)) { $block_info_cache = db_select('block', 'b') ->fields('b') diff --git a/frontend/drupal/sites/all/modules/ctools/plugins/contexts/language.inc b/frontend/drupal/sites/all/modules/ctools/plugins/contexts/language.inc new file mode 100644 index 000000000..e66f02a16 --- /dev/null +++ b/frontend/drupal/sites/all/modules/ctools/plugins/contexts/language.inc @@ -0,0 +1,169 @@ + t('Language'), + 'description' => t('Language object.'), + 'context' => 'ctools_context_language_create', + 'context name' => 'language', + 'keyword' => 'language', + + // Provides a list of items which are exposed as keywords. + 'convert list' => 'ctools_language_context_convert_list', + // Convert keywords into data. + 'convert' => 'ctools_language_context_convert', + + 'placeholder form' => array( + '#type' => 'textfield', + '#description' => t('Enter a valid langcode.'), + '#value' => $GLOBALS['language']->language, + ), + + // Provide settings for the context. + 'edit form' => 'ctools_context_language_settings_form', + 'settings' => ctools_context_language_conf_defaults(), +); + +/** + * Ensures a full populated settings array with sane defaults. + * + * @param mixed $conf + * Array with the user defined settings, or a string identifying a language. + * + * @return array + * Array with all available settings. + */ +function ctools_context_language_conf_defaults($conf = array()) { + if (!is_array($conf)) { + $conf = array( + 'preset_langcode' => (string) $conf, + ); + } + + return $conf + array( + 'enable_cache_argument' => TRUE, + 'language_type' => 'language', + 'preset_langcode' => $GLOBALS['language']->language, + ); +} + +/** + * Create a context, either from manual configuration or the current language. + */ +function ctools_context_language_create($empty, $data = NULL, $conf = FALSE) { + $context = new ctools_context('language'); + $context->plugin = 'language'; + if ($empty) { + return $context; + } + $context->title = t('Language'); + + $settings = ctools_context_language_conf_defaults($data); + if ($settings['language_type'] != 'preset') { + $language_object = $GLOBALS[$settings['language_type']]; + } + else { + // Fetch the enabled language objects. + $languages = language_list('enabled'); + $languages = $languages[1]; + + // Set the custom language, but fallback to the interface language. + $language_object = $GLOBALS['language']; + if (isset($languages[$settings['preset_langcode']])) { + $language_object = $languages[$settings['preset_langcode']]; + } + } + // If enabled set the argument ot use in the cid. + if ($settings['enable_cache_argument']) { + $context->argument = $language_object->language; + } + $context->data = $language_object; + return $context; +} + +/** + * Provide a list of sub-keywords. + * + * This is used to provide keywords from the context for use in a content type, + * pane, etc. + */ +function ctools_language_context_convert_list() { + $context = new stdClass(); + $context->data = $GLOBALS['language']; + return array( + 'language' => t('Langcode. E.g. !example', array('!example' => ctools_language_context_convert($context, 'language'))), + 'name' => t('Name. E.g. !example', array('!example' => ctools_language_context_convert($context, 'name'))), + 'native' => t('Native name of the language. E.g. !example', array('!example' => ctools_language_context_convert($context, 'native'))), + 'direction' => t('Text direction 0=LRT, 1=RTL. E.g. !example', array('!example' => ctools_language_context_convert($context, 'direction'))), + 'enabled' => t('Status. E.g. !example', array('!example' => ctools_language_context_convert($context, 'enabled'))), + 'plurals' => t('Number of plural forms. E.g. !example', array('!example' => ctools_language_context_convert($context, 'plurals'))), + 'formula' => t('Plural formula. E.g. !example', array('!example' => ctools_language_context_convert($context, 'formula'))), + 'domain' => t('Domain prefix. E.g. !example', array('!example' => ctools_language_context_convert($context, 'domain'))), + 'prefix' => t('Url prefix . E.g. !example', array('!example' => ctools_language_context_convert($context, 'prefix'))), + 'weight' => t('The weight. E.g. !example', array('!example' => ctools_language_context_convert($context, 'weight'))), + 'javascript' => t('Key of the javascript file with the translations. E.g. !example', array('!example' => ctools_language_context_convert($context, 'javascript'))), + 'provider' => t('Negotiation method that defined the language. E.g. !example', array('!example' => ctools_language_context_convert($context, 'provider'))), + ); +} + +/** + * Convert a context property into a string to be used as a keyword. + */ +function ctools_language_context_convert($context, $type) { + if (isset($context->data->$type)) { + return $context->data->$type; + } +} + +/** + * Settings form. + */ +function ctools_context_language_settings_form($form, &$form_state) { + $conf = ctools_context_language_conf_defaults($form_state['conf']); + + $form['enable_cache_argument'] = array( + '#title' => t('Add language to cache id'), + '#description' => t('If enabled the langcode will be part of context aware caches.'), + '#type' => 'checkbox', + '#default_value' => $conf['enable_cache_argument'], + ); + + // Prepare language type options. + $language_type_options = drupal_map_assoc(language_types()); + $language_type_options['preset'] = t('Custom'); + + $form['language_type'] = array( + '#title' => t('The language type to use'), + '#type' => 'radios', + '#required' => TRUE, + '#options' => $language_type_options, + '#default_value' => $conf['language_type'], + ); + + ctools_include('language'); + $language_options = ctools_language_list(); + $form['preset_langcode'] = array( + '#title' => t('Language'), + '#type' => 'select', + '#options' => $language_options, + '#default_value' => $conf['preset_langcode'], + '#states' => array( + 'visible' => array( + ':input[name="language_type"]' => array('value' => 'preset'), + ), + ), + ); + + if (!empty($conf['preset_langcode']) && !isset($language_options[$conf['preset_langcode']])) { + drupal_set_message(t('The currently selected language %langcode is no longer available.', array('%langcode' => $conf['preset_langcode'])), 'error', FALSE); + } + return $form; +} diff --git a/frontend/drupal/sites/all/modules/ctools/stylizer/stylizer.info b/frontend/drupal/sites/all/modules/ctools/stylizer/stylizer.info index 90204a3de..81c643362 100644 --- a/frontend/drupal/sites/all/modules/ctools/stylizer/stylizer.info +++ b/frontend/drupal/sites/all/modules/ctools/stylizer/stylizer.info @@ -5,8 +5,8 @@ package = Chaos tool suite dependencies[] = ctools dependencies[] = color -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/term_depth/plugins/access/term_depth.inc b/frontend/drupal/sites/all/modules/ctools/term_depth/plugins/access/term_depth.inc index d63183f32..eb302b625 100644 --- a/frontend/drupal/sites/all/modules/ctools/term_depth/plugins/access/term_depth.inc +++ b/frontend/drupal/sites/all/modules/ctools/term_depth/plugins/access/term_depth.inc @@ -15,7 +15,7 @@ $plugin = array( 'callback' => 'term_depth_term_depth_ctools_access_check', 'default' => array('vid' => array(), 'depth' => 0), 'settings form' => 'term_depth_term_depth_ctools_access_settings', - 'settings form validation' => 'term_depth_term_depth_ctools_access_settings_validate', + 'settings form validate' => 'term_depth_term_depth_ctools_access_settings_validate', 'settings form submit' => 'term_depth_term_depth_ctools_access_settings_submit', 'summary' => 'term_depth_term_depth_ctools_access_summary', 'required context' => new ctools_context_required(t('Term'), array('taxonomy_term', 'terms')), diff --git a/frontend/drupal/sites/all/modules/ctools/term_depth/term_depth.info b/frontend/drupal/sites/all/modules/ctools/term_depth/term_depth.info index 4baee51d8..e9eef27be 100644 --- a/frontend/drupal/sites/all/modules/ctools/term_depth/term_depth.info +++ b/frontend/drupal/sites/all/modules/ctools/term_depth/term_depth.info @@ -4,8 +4,8 @@ core = 7.x dependencies[] = ctools package = Chaos tool suite -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/tests/ctools.drush.sh b/frontend/drupal/sites/all/modules/ctools/tests/ctools.drush.sh old mode 100644 new mode 100755 diff --git a/frontend/drupal/sites/all/modules/ctools/tests/ctools.test b/frontend/drupal/sites/all/modules/ctools/tests/ctools.test new file mode 100644 index 000000000..4d748c09e --- /dev/null +++ b/frontend/drupal/sites/all/modules/ctools/tests/ctools.test @@ -0,0 +1,239 @@ + 'Ctools module functions tests', + 'description' => 'Check functions in the ctools.module not otherwise tested.', + 'group' => 'ctools', + 'dependencies' => array('ctools'), + ); + } + + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); + } + + /** + * Test that the break phrase function behaves as expected. + */ + public function testBreakPhrase() { + $tests = array( + NULL => array('value' => array()), + '' => array('value' => array()), + '1' => array('operator' => 'and', 'value' => array(1)), + '99' => array('operator' => 'and', 'value' => array(99)), + '+1' => array('invalid_input' => TRUE, 'value' => array(-1)), + ' 1' => array('invalid_input' => TRUE, 'value' => array(-1)), + '1 ' => array('invalid_input' => TRUE, 'value' => array(-1)), + '-1' => array('invalid_input' => TRUE, 'value' => array(-1)), + '-99' => array('invalid_input' => TRUE, 'value' => array(-1)), + '1,2' => array('operator' => 'and', 'value' => array(1, 2)), + '1 2' => array('operator' => 'or', 'value' => array(1, 2)), + '1+2' => array('operator' => 'or', 'value' => array(1, 2)), + '1,2,3' => array('operator' => 'and', 'value' => array(1, 2, 3)), + '1 2 3' => array('operator' => 'or', 'value' => array(1, 2, 3)), + '1+2+3' => array('operator' => 'or', 'value' => array(1, 2, 3)), + '1 , 2 , 3' => array('invalid_input' => TRUE, 'value' => array(-1)), + '1 + 2 + 3' => array('invalid_input' => TRUE, 'value' => array(-1)), + '1,2,3,4,5,6,7,8,9' => array( + 'operator' => 'and', + 'value' => array(1, 2, 3, 4, 5, 6, 7, 8, 9), + ), + '1 2,3,4 5 6 7 8 9' => array('invalid_input' => TRUE, 'value' => array(-1)), + ); + + foreach ($tests as $string => $expected) { + $result = ctools_break_phrase($string); + $expected = (object) $expected; + $this->assertEqual($result, $expected, 'Break Phrase test patterns: ' . $string); + } + } + + /** + * Test that the (deprecated) getuserroles returns expected array. + */ + public function testGetUserRoles() { + $result = ctools_get_roles(); + $this->assertTrue(is_array($result), 'get_roles returns an array'); + + // A key-value array of integers. + foreach ($result as $k => $v) { + $this->assertTrue(is_numeric($k), 'Role key is numeric; ' . $k); + $this->assertTrue(is_string($v), 'Role id is string; ' . $v); + } + } + + /** + * Test the ctools_attach_js function returns the expected paths. + */ + public function testAttachJs() { + $taxonomy_path = drupal_get_path('module', 'taxonomy'); + $ctools_path = drupal_get_path('module', 'ctools'); + + // Func should probably do a different thing but this is current behaviour. + $path = ctools_attach_js(''); + $this->assertEqual($path, $ctools_path . '/js/.js', 'Attach an empty string'); + + $path = ctools_attach_js('foo'); + $this->assertEqual($path, $ctools_path . '/js/foo.js', 'Attach simple string'); + + $path = ctools_attach_js('foo', 'ctools', ''); + $this->assertEqual($path, $ctools_path . '//foo.js', 'Attach string with empty subdir'); + + $path = ctools_attach_js('foo', 'ctools', 'javascript'); + $this->assertEqual($path, $ctools_path . '/javascript/foo.js', 'Attach string with alternate subdir'); + + $path = ctools_attach_js('foo', 'taxonomy', 'javascript'); + $this->assertEqual($path, $taxonomy_path . '/javascript/foo.js', 'Attach string from different module'); + } + + /** + * Test the ctools_attach_css function returns the expected paths. + */ + public function testAttachCss() { + $taxonomy_path = drupal_get_path('module', 'taxonomy'); + $ctools_path = drupal_get_path('module', 'ctools'); + + // Func should probably do a different thing but this is current behaviour. + $path = ctools_attach_css(''); + $this->assertEqual($path, $ctools_path . '/css/.css', 'Attach empty string'); + + $path = ctools_attach_css('foo'); + $this->assertEqual($path, $ctools_path . '/css/foo.css', 'Attach simple string'); + + $path = ctools_attach_css('foo', 'ctools', ''); + $this->assertEqual($path, $ctools_path . '//foo.css', 'Attach string with empty subdir'); + + $path = ctools_attach_css('foo', 'ctools', 'theme'); + $this->assertEqual($path, $ctools_path . '/theme/foo.css', 'Attach string with alternate subdir'); + + $path = ctools_attach_css('foo', 'taxonomy', 'theme'); + $this->assertEqual($path, $taxonomy_path . '/theme/foo.css', 'Attach string from different module'); + } + + /** + * Test the ctools version compare function. + */ + public function testApiVersionCompare() { + // We're beyond version 1. + $ok = ctools_api_version('1.0'); + $this->assertTrue($ok, 'Check API version 1.0 is ok'); + + // We're beyond version 1.0.1 too. + $ok = ctools_api_version('1.0.1'); + $this->assertTrue($ok, 'Check API version 1.0.1 is ok'); + + // Not (yet) on api version 10. + $ok = ctools_api_version('10.0'); + $this->assertFalse($ok, 'Check API version 10.0 is not ok'); + + // We are (currently) between version 1.1 and version 4.0. + $ok = ctools_api_version('1.1', '4.0'); + $this->assertTrue($ok, 'Check API is between 1 and 4'); + } + + /** + * Test that the ctools_classs_add works. + */ + public function testClassesAdd() { + ctools_class_reset(); + + ctools_class_add('testclass'); + + $classes = ctools_get_classes(); + $this->assertEqual(is_array($classes), 1, 'Classes should be an array'); + $this->assertEqual(count($classes), 1, 'Classes array has one element'); + $this->assertEqual(count($classes['html']), 1, 'Classes array has element: html'); + $this->assertTrue(isset($classes['html']['add']), 'Classes array has element: html/add'); + $this->assertEqual($classes['html']['add'], array('testclass'), 'Classes array has expected value'); + + ctools_class_add('class2 class3'); + + $classes = ctools_get_classes(); + $this->assertEqual(is_array($classes), 1, 'Classes should be an array'); + $this->assertEqual(count($classes['html']), 1, 'Classes array has element: html'); + // TODO: An undesirable result: array('testclass', 'class2', 'class3') is better. + $this->assertEqual($classes['html']['add'], array( + 'testclass', + 'class2 class3', + ), 'Classes array has expected value'); + } + + /** + * Test that the ctools_classs_remove works. + */ + public function testClassesRemove() { + ctools_class_reset(); + + ctools_class_remove('testclass'); + + $classes = ctools_get_classes(); + $this->assertEqual(is_array($classes), 1, 'Classes should be an array'); + $this->assertEqual(count($classes), 1, 'Classes array has one element'); + $this->assertEqual(count($classes['html']), 1, 'Classes array has element: html'); + $this->assertTrue(isset($classes['html']['remove']), 'Classes array has element: html/remove'); + $this->assertEqual($classes['html']['remove'], array('testclass'), 'Classes array has expected value'); + + ctools_class_remove('class2 class3'); + + $classes = ctools_get_classes(); + $this->assertEqual(count($classes), 1, 'Classes array has one element'); + $this->assertEqual(count($classes['html']), 1, 'Classes array has element: html'); + // This is an undesirable result, is array('testclass', 'class2', 'class3') better. + $this->assertEqual($classes['html']['remove'], array( + 'testclass', + 'class2 class3', + ), 'Classes array has expected value'); + } + + /** + * Test that the ctools_classs_add and ctools_classs_remove interact well. + */ + public function testClassesAddRemove() { + ctools_class_reset(); + + ctools_class_add('testclass'); + ctools_class_remove('testclass'); + + $classes = ctools_get_classes(); + $this->assertTrue(isset($classes['html']['add']), 'Classes array has an add set'); + $this->assertEqual($classes['html']['add'], array('testclass'), 'testclass is in the add set'); + $this->assertTrue(isset($classes['html']['remove']), 'Classes array has a remove set'); + // TODO: Is it really good to let this happen? + $this->assertEqual($classes['html']['remove'], array('testclass'), 'testclass is in the remove set'); + } + + /** + * Test that the ctools_classs_add and ctools_classs_remove interact well .. 2. + */ + public function testClassesAddRemove2() { + ctools_class_reset(); + + ctools_class_add('class2 class3'); + ctools_class_remove('class3'); + + $classes = ctools_get_classes(); + $this->assertTrue(isset($classes['html']['add']), 'Classes array has an add set'); + $this->assertEqual($classes['html']['add'], array('class2 class3'), 'Added class2 class3 is in add set'); + $this->assertTrue(isset($classes['html']['remove']), 'Classes array has a remove set'); + // TODO: Is it really good to let this happen? + $this->assertEqual($classes['html']['remove'], array('class3'), 'class3 in remove set'); + } + +} diff --git a/frontend/drupal/sites/all/modules/ctools/tests/ctools_export_test/ctools_export_test.info b/frontend/drupal/sites/all/modules/ctools/tests/ctools_export_test/ctools_export_test.info index a2d0efbd9..3c5c38df9 100644 --- a/frontend/drupal/sites/all/modules/ctools/tests/ctools_export_test/ctools_export_test.info +++ b/frontend/drupal/sites/all/modules/ctools/tests/ctools_export_test/ctools_export_test.info @@ -7,8 +7,8 @@ hidden = TRUE files[] = ctools_export.test -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/tests/ctools_plugin_test.info b/frontend/drupal/sites/all/modules/ctools/tests/ctools_plugin_test.info index d51a52614..b4d0a5d24 100644 --- a/frontend/drupal/sites/all/modules/ctools/tests/ctools_plugin_test.info +++ b/frontend/drupal/sites/all/modules/ctools/tests/ctools_plugin_test.info @@ -5,8 +5,8 @@ core = 7.x dependencies[] = ctools hidden = TRUE -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/ctools/tests/object_cache_unit.test b/frontend/drupal/sites/all/modules/ctools/tests/object_cache_unit.test new file mode 100644 index 000000000..68b735031 --- /dev/null +++ b/frontend/drupal/sites/all/modules/ctools/tests/object_cache_unit.test @@ -0,0 +1,141 @@ + 'Object cache storage (unit tests)', + 'description' => 'Verify that objects are written, readable and lockable.', + 'group' => 'ctools', + 'dependencies' => array('ctools'), + ); + } + + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); + } + + /** + * Check that the supplied array looks like a ctools cache plugin. + * + * @param mixed $p + * The value to check. + * @param string $msg + * Prefix of the assertion message. + */ + public function assertValidCachePlugin($p, $msg) { + $this->assertTrue(is_array($p), $msg . ': plugin is an array'); + + $this->assertEqual($p['plugin type'], 'cache', $msg . ': type is cache'); + + $this->assertTrue(array_key_exists('title', $p), $msg . ': title element exists'); + $this->assertTrue(!empty($p['title']) && is_string($p['title']), $msg . ': title is a string'); + + $this->assertTrue(array_key_exists('cache get', $p), $msg . ': has a get function'); + $this->assertTrue(!empty($p['cache get']) && is_callable($p['cache get']), $msg . ': get is executable'); + + $this->assertTrue(array_key_exists('cache set', $p), $msg . ': has a set function'); + $this->assertTrue(!empty($p['cache set']) && is_callable($p['cache set']), $msg . ': set is executable'); + + // @todo Clear is required by the spec (cache.inc:40..48): but export_ui + // cache doesn't implement it. Enable the assertions when that problem is + // solved. + // $this->assertTrue(array_key_exists('cache clear', $p), $msg . ': has a clear function'); + // $this->assertTrue(is_callable($p['cache clear']), $msg . ': clear is executable'); + // @todo Break is optional acc'd to spec but does anything implement it? + $this->assertTrue(!array_key_exists('cache break', $p) || is_callable($p['cache break']), $msg . ': break is executable'); + + // @todo Finalize is optional so don't fail if not there?? + $this->assertTrue(!array_key_exists('cache finalize', $p) || is_callable($p['cache finalize']), $msg . ': finalize is executable'); + } + + /** + * Check the return value of the ctools_cache_find_plugin function. + * + * @param mixed $p + * The value to check. + * @param string $msg + * Prefix of the assertion message. + */ + public function assertPluginNotFound($p, $msg) { + $this->assertTrue(is_array($p), $msg . ': is an array'); + $plugin = array_shift($p); + $this->assertNull($plugin, $msg . ': no plugin info'); + $data = array_shift($p); + $this->assertTrue(empty($data) || is_string($data), $msg . ': data string-like'); + $this->assertTrue(empty($p), $msg . ': just two elements'); + } + + /** + * Check the return value of the ctools_cache_find_plugin function. + * + * @param mixed $p + * The value to check. + * @param string $msg + * Prefix of the assertion message. + */ + public function assertPluginFound($p, $msg) { + $this->assertTrue(is_array($p), $msg . ': is an array'); + $plugin = array_shift($p); + $this->assertTrue(is_array($plugin), $msg . ': has plugin data'); + $data = array_shift($p); + $this->assertTrue(empty($data) || is_string($data), $msg . ': data is string-like'); + $this->assertTrue(empty($p), $msg . ': just two elements'); + } + + /** + * Test to see that we can find the standard simple plugin. + */ + public function testFindSimpleCachePlugin() { + ctools_include('cache'); + + // The simple plugin. + $plugin = ctools_cache_find_plugin('simple'); + $this->assertPluginFound($plugin, 'The Simple Cache plugin is present'); + $this->assertValidCachePlugin($plugin[0], 'The Simple Cache plugin'); + + // The simple plugin, with ::. + $plugin = ctools_cache_find_plugin('simple::data'); + $this->assertPluginFound($plugin, 'The Simple Cache plugin is present, with data'); + } + + /** + * Test to see that we can find the standard export_ui plugin. + */ + public function testFindExportUICachePlugin() { + ctools_include('cache'); + + // The export plugin. + $plugin = ctools_cache_find_plugin('export_ui'); + $this->assertPluginFound($plugin, 'The Export UI Cache plugin is present'); + $this->assertValidCachePlugin($plugin[0], 'The Export Cache plugin'); + + // The export plugin, with ::. + $plugin = ctools_cache_find_plugin('export_ui::data'); + $this->assertTrue(is_array($plugin), 'The Export UI Cache plugin is present, with data'); + } + + /** + * Test to see that we don't find plugins that aren't there. + */ + public function testFindFoobarbazCachePlugin() { + ctools_include('cache'); + + // An imaginary foobarbaz plugin. + $plugin = ctools_cache_find_plugin('foobarbaz'); + $this->assertPluginNotFound($plugin, 'The Foobarbaz Cache plugin is absent'); + $plugin = ctools_cache_find_plugin('foobarbaz::data'); + $this->assertPluginNotFound($plugin, 'The Foobarbaz Cache plugin is absent, with data'); + } + +} diff --git a/frontend/drupal/sites/all/modules/ctools/tests/page_tokens.test b/frontend/drupal/sites/all/modules/ctools/tests/page_tokens.test new file mode 100644 index 000000000..7d95d9ae9 --- /dev/null +++ b/frontend/drupal/sites/all/modules/ctools/tests/page_tokens.test @@ -0,0 +1,136 @@ + 'Page token processing', + 'description' => 'Verify that page tokens can be set and read.', + 'group' => 'ctools', + 'dependencies' => array('ctools'), + ); + } + + /** + * {@inheritdoc} + */ + public function setUp(array $modules = array()) { + $modules[] = 'ctools'; + parent::setUp($modules); + + // Start each test anew. + ctools_reset_page_tokens(); + } + + /** + * Test that we can set page tokens. + */ + public function testSetPageToken() { + ctools_set_page_token('id', 'variable', 'test'); + + $tokens = ctools_set_page_token(); + $this->assertTrue(is_array($tokens) && count($tokens) === 1, 'Page tokens no longer empty.'); + $this->assertEqual($tokens['id'], array('variable', 'test'), 'Page token has correct value.'); + } + + /** + * Test that we can set page tokens. + */ + public function testSetVariableToken() { + $string = ctools_set_variable_token('title'); + $tokens = ctools_set_page_token(); + + $this->assertTrue(is_array($tokens) && count($tokens) === 1, 'Page tokens no longer empty'); + $this->assertEqual($tokens[$string], array('variable', 'title'), 'Page tokens no longer empty'); + } + + /** + * Test that we can set page tokens. + */ + public function testReplaceVariableToken() { + $string = ctools_set_variable_token('title'); + $this->assertEqual($string, '', 'Expected form of token was found'); + + $elements = array( + '#type' => 'container', + '#attributes' => array('class' => array('test')), + 'title' => '

Title

', + 'content' => array( + '#type' => 'markup', + '#markup' => t('This is my test markup'), + '#prefix' => $string, + ), + 'link' => array( + '#type' => 'link', + '#title' => t('My link'), + '#href' => 'node/1', + ), + ); + $markup = '
This is my test markupMy link
'; + + $new_markup = ctools_page_token_processing($markup, $elements); + + $this->assertTrue(is_string($new_markup) && strlen($new_markup) > 1, 'Should return string'); + + $this->assertTrue(strstr($new_markup, '

'), 'Variable Token Markup should contain h2 element'); + $this->assertFalse(strstr($new_markup, 'ctools-page-title'), 'Variable Token Markup should not contain comment element'); + } + + /** + * Test that we can set page tokens. + */ + public function testReplaceCallbackToken() { + $string = ctools_set_callback_token('title', 'test_ctools_page_callback_token'); + $this->assertEqual($string, '', 'Expected form of token was found'); + + $elements = array( + '#type' => 'container', + '#attributes' => array('class' => array('test')), + 'content' => array( + '#type' => 'markup', + '#markup' => t('This is my test markup'), + '#prefix' => $string, + ), + 'link' => array( + '#type' => 'link', + '#title' => t('My link'), + '#href' => 'node/1', + ), + ); + $markup = '
This is my test markupMy link
'; + + $new_markup = ctools_page_token_processing($markup, $elements); + + $this->assertTrue(is_string($new_markup) && strlen($new_markup) > 1, 'Should return a non-empty string'); + + $this->assertTrue(strstr($new_markup, '

'), 'Callback Token Markup should contain h2 element'); + $this->assertFalse(strstr($new_markup, 'ctools-page-title'), 'Callback Token Markup should not contain comment element'); + } + +} + +/** + * + * @param $elements + * + * @return string + */ +function test_ctools_page_callback_token($elements) { + // Check that 'elements' array looks good. + if (isset($elements['content'])) { + return '

Title

'; + } + else { + return ''; + } +} diff --git a/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views.inc b/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views.inc index 9ad7728f4..0754d4261 100644 --- a/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views.inc +++ b/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views.inc @@ -173,7 +173,7 @@ function views_content_views_content_type_render($subtype, $conf, $panel_args, $ if ($conf['use_pager'] && ($pager['type'] == 'none' || $pager['type'] == 'some')) { $pager['type'] = 'full'; } - elseif (!$conf['use_pager'] && $pager['type'] != 'none' && $pager['type'] != 'some') { + elseif (!$conf['use_pager']) { $pager['type'] = $view->get_items_per_page() ? 'some' : 'none'; } diff --git a/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views_panes.inc b/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views_panes.inc index 07e93f7a5..afc98136b 100644 --- a/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views_panes.inc +++ b/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views_panes.inc @@ -254,7 +254,7 @@ function views_content_views_panes_content_type_render($subtype, $conf, $panel_a if ($conf['use_pager'] && ($pager['type'] == 'none' || $pager['type'] == 'some')) { $pager['type'] = 'full'; } - elseif (!$conf['use_pager'] && $pager['type'] != 'none' && $pager['type'] != 'some') { + elseif (!$conf['use_pager']) { $pager['type'] = $view->get_items_per_page() || !empty($pager['options']['items_per_page']) ? 'some' : 'none'; } diff --git a/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views_row.inc b/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views_row.inc index 16b97dc73..6678f447d 100644 --- a/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views_row.inc +++ b/frontend/drupal/sites/all/modules/ctools/views_content/plugins/content_types/views_row.inc @@ -228,8 +228,9 @@ function views_content_views_row_content_type_admin_info($subtype, $conf, $conte function views_content_views_row_content_type_admin_title($subtype, $conf, $context) { $rows = array_filter($conf['rows']); + $row_count = count($rows); $rows = empty($rows) ? t('Show all') : implode(', ', $rows); - return format_plural(count($rows), + return format_plural($row_count, '"@context" row @rows', '"@context" rows @rows', array('@context' => $context->identifier, '@rows' => $rows) diff --git a/frontend/drupal/sites/all/modules/ctools/views_content/plugins/views/views_content_plugin_display_panel_pane.inc b/frontend/drupal/sites/all/modules/ctools/views_content/plugins/views/views_content_plugin_display_panel_pane.inc index 05d3478f0..801d3d61e 100644 --- a/frontend/drupal/sites/all/modules/ctools/views_content/plugins/views/views_content_plugin_display_panel_pane.inc +++ b/frontend/drupal/sites/all/modules/ctools/views_content/plugins/views/views_content_plugin_display_panel_pane.inc @@ -4,6 +4,7 @@ * The plugin that handles a panel_pane. */ class views_content_plugin_display_panel_pane extends views_plugin_display { + /** * If this variable is true, this display counts as a panel pane. We use * this variable so that other modules can create alternate pane displays. @@ -11,7 +12,9 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { public $panel_pane_display = TRUE; public $has_pane_conf = NULL; - + /** + * {@inheritdoc} + */ public function option_definition() { $options = parent::option_definition(); @@ -382,7 +385,31 @@ class views_content_plugin_display_panel_pane extends views_plugin_display { return (bool) $conf['more_link']; } + /** + * {@inheritdoc} + */ + public function has_path() { + return TRUE; + } + /** + * {@inheritdoc} + */ + public function validate() { + // To bypass the validation of the path from Views we temporarily + // override the path if one doesn't exist because it will be generated + // by panels though we want the rest of the validations to run. + $path = $this->get_path(); + if (!$path) { + $this->set_option('path', $_GET['q']); + } + + return parent::validate(); + } + + /** + * {@inheritdoc} + */ public function get_path() { if (empty($this->view->override_path)) { return parent::get_path(); diff --git a/frontend/drupal/sites/all/modules/ctools/views_content/views_content.info b/frontend/drupal/sites/all/modules/ctools/views_content/views_content.info index 68f4d8756..acfeeed99 100644 --- a/frontend/drupal/sites/all/modules/ctools/views_content/views_content.info +++ b/frontend/drupal/sites/all/modules/ctools/views_content/views_content.info @@ -9,8 +9,8 @@ files[] = plugins/views/views_content_plugin_display_ctools_context.inc files[] = plugins/views/views_content_plugin_display_panel_pane.inc files[] = plugins/views/views_content_plugin_style_ctools_context.inc -; Information added by Drupal.org packaging script on 2019-02-08 -version = "7.x-1.15" +; Information added by Drupal.org packaging script on 2020-10-23 +version = "7.x-1.16" core = "7.x" project = "ctools" -datestamp = "1549603691" +datestamp = "1603430414" diff --git a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.admin.categories.inc b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.admin.categories.inc new file mode 100644 index 000000000..18f9e133c --- /dev/null +++ b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.admin.categories.inc @@ -0,0 +1,402 @@ + TRUE, + 'categories' => array(), + ); + $categories = variable_get('eu_cookie_compliance_categories', array()); + $form['#submit'][] = 'eu_cookie_compliance_admin_categories_form_submit'; + $form['eu_cookie_compliance_categories'] = array('#type' => 'hidden'); + + $options_list = _eu_cookie_compliance_get_category_checkbox_default_state_options_list(); + + foreach ($categories as $machine_name => $category) { + $form['categories'][$machine_name]['label'] = array( + '#markup' => check_plain($category['label']), + ); + $form['categories'][$machine_name]['machine_name'] = array( + '#markup' => check_plain($machine_name), + ); + $form['categories'][$machine_name]['description'] = array( + '#markup' => check_plain($category['description']), + ); + $form['categories'][$machine_name]['checkbox_default_state'] = array( + '#markup' => isset($options_list[$category['checkbox_default_state']]) ? $options_list[$category['checkbox_default_state']] : $options_list['unchecked'], + ); + // This field is invisible, but contains sort info (weights). + $form['categories'][$machine_name]['weight'] = array( + '#type' => 'weight', + '#title' => t('Weight'), + '#title_display' => 'invisible', + '#default_value' => $category['weight'], + ); + $options = array(); + // Adapt links to follow realm language if present. + $query_parameters = drupal_get_query_parameters(); + if (isset($query_parameters['variable_realm_key_language'])) { + $language_list = language_list(); + $language = $language_list[$query_parameters['variable_realm_key_language']] ?: FALSE; + if ($language) { + $options['language'] = $language; + } + } + $form['categories'][$machine_name]['op'] = array( + '#markup' => + l(t('Edit'), '/admin/config/system/eu-cookie-compliance/categories/' . check_plain($machine_name) . '/edit', $options) . + ' | ' . + l(t('Delete'), '/admin/config/system/eu-cookie-compliance/categories/' . check_plain($machine_name) . '/delete', $options), + ); + } + + $options = array(); + // Adapt links to follow realm language if present. + $query_parameters = drupal_get_query_parameters(); + if (isset($query_parameters['variable_realm_key_language'])) { + $language_list = language_list(); + $language = $language_list[$query_parameters['variable_realm_key_language']] ?: FALSE; + if ($language) { + $options['language'] = $language; + } + } + + $form['add_new'] = array( + '#type' => 'markup', + '#markup' => t('', array( + '@path' => url('admin/config/system/eu-cookie-compliance/categories/add', $options), + )), + ); + + $form = system_settings_form($form); + unset($form['#theme']); + + return $form; +} + +/** + * Implements hook_theme(). + */ +function theme_eu_cookie_compliance_admin_categories_form($variables) { + $form = $variables['form']; + + $rows = array(); + + foreach (element_children($form['categories']) as $machine_name) { + $form['categories'][$machine_name]['weight']['#attributes']['class'] = array('categories-order-weight'); + $rows[] = array( + 'data' => array( + array('class' => array('slide-cross')), + drupal_render($form['categories'][$machine_name]['label']), + drupal_render($form['categories'][$machine_name]['machine_name']), + drupal_render($form['categories'][$machine_name]['description']), + drupal_render($form['categories'][$machine_name]['checkbox_default_state']), + drupal_render($form['categories'][$machine_name]['weight']), + drupal_render($form['categories'][$machine_name]['op']), + ), + 'class' => array('draggable'), + ); + } + + $header = array( + '', + t('Label'), + t('Machine name'), + t('Description'), + t('Checkbox default state'), + t('Weight'), + t('Operations'), + ); + $options = array(); + // Adapt links to follow realm language if present. + $query_parameters = drupal_get_query_parameters(); + if (isset($query_parameters['variable_realm_key_language'])) { + $language_list = language_list(); + $language = $language_list[$query_parameters['variable_realm_key_language']] ?: FALSE; + if ($language) { + $options['language'] = $language; + } + } + $output = drupal_render_children($form, array('add_new')); + $output .= theme('table', array( + 'header' => $header, + 'rows' => $rows, + 'empty' => t('No categories exist yet. Add new category.', array( + '@path' => url('admin/config/system/eu-cookie-compliance/categories/add', $options), + )), + 'attributes' => array( + 'id' => 'categories-order', + ), + ) + ); + + $output .= drupal_render_children($form); + + drupal_add_tabledrag('categories-order', 'order', 'sibling', 'categories-order-weight'); + + return $output; +} + +/** + * Submit handler for ordering cookie consent categories. + * + * @param array $form + * Form array. + * @param array $form_state + * Form state array. + */ +function eu_cookie_compliance_admin_categories_form_submit(array $form, array &$form_state) { + $categories = variable_get('eu_cookie_compliance_categories', array()); + + // Update categories with new weight values. + $weights = $form_state['values']['categories']; + foreach ($weights as $machine_name => $data) { + $categories[$machine_name]['weight'] = $data['weight']; + } + + // Order the categories by their weight. + uasort($categories, function ($a, $b) { + return $a['weight'] - $b['weight']; + }); + + $form_state['values'] = array(); + $form_state['values']['eu_cookie_compliance_categories'] = $categories; +} + +/** + * Provides form for adding / editing a cookie consent category. + * + * @param array $form + * Form array. + * @param array $form_state + * For state array. + * @param string $machine_name + * The machine name of the category. + * + * @return array + * Form render array. + */ +function eu_cookie_compliance_admin_category_form(array $form, array $form_state, $machine_name = NULL) { + $form = array(); + $categories = variable_get('eu_cookie_compliance_categories', array()); + // Add this so i18n can add what it needs. + $form['eu_cookie_compliance_categories'] = array('#type' => 'hidden'); + $form['#submit'][] = 'eu_cookie_compliance_admin_category_form_submit'; + + // We are in edit mode. + if ($machine_name !== NULL) { + $category = isset($categories[$machine_name]) ? $categories[$machine_name] : NULL; + if (!$category) { + drupal_set_message(t('You are tying to edit an invalid cookie category: %name.', array('%name' => $machine_name)), 'error'); + drupal_goto('admin/config/system/eu-cookie-compliance/categories'); + } + } + // Or we are in add mode. + else { + $category = array( + 'machine_name' => '', + 'label' => '', + 'description' => '', + 'checkbox_default_state' => 'unchecked', + 'weight' => 0, + ); + } + + $form['category'] = array( + '#type' => 'value', + '#value' => $category, + ); + + $form['label'] = array( + '#type' => 'textfield', + '#title' => t('Label'), + '#description' => t('The name that will be shown to the visitor.'), + '#required' => TRUE, + '#default_value' => check_plain($category['label']) ?: '', + ); + + $form['machine_name'] = array( + '#type' => 'machine_name', + '#default_value' => check_plain($category['machine_name']), + '#machine_name' => array( + 'exists' => 'eu_cookie_compliance_admin_category_exists', + 'source' => array('label'), + ), + '#disabled' => !empty($category['machine_name']), + ); + + $form['description'] = array( + '#type' => 'textarea', + '#title' => t('Description'), + '#description' => t('The description that will be shown to the visitor.'), + '#required' => FALSE, + '#default_value' => check_plain($category['description']) ?: '', + ); + + $form['checkbox_default_state'] = array( + '#type' => 'radios', + '#title' => t('Checkbox default state'), + '#description' => t("Determines the default state of this category's selection checkbox on the cookie consent popup."), + '#options' => _eu_cookie_compliance_get_category_checkbox_default_state_options_list(), + '#required' => TRUE, + '#default_value' => isset($category['checkbox_default_state']) ? $category['checkbox_default_state'] : 'unchecked', + ); + + $form = system_settings_form($form); + + $options = array(); + // Adapt links to follow realm language if present. + $query_parameters = drupal_get_query_parameters(); + if (isset($query_parameters['variable_realm_key_language'])) { + $language_list = language_list(); + $language = $language_list[$query_parameters['variable_realm_key_language']] ?: FALSE; + if ($language) { + $options['language'] = $language; + } + } + if ($category['machine_name']) { + $form['actions']['delete'] = array( + '#type' => 'markup', + '#markup' => l(t('Delete'), 'admin/config/system/eu-cookie-compliance/categories/' . check_plain($category['machine_name']) . '/delete', $options), + ); + } + + return $form; +} + +/** + * Submit handler for adding / editing a cookie consent category. + * + * All that happens here is massaging form values to match what + * system_settings_form expects. Bonus is that i18n variables can then also work + * its magic, with a little help from + * eu_cookie_compliance_variable_settings_form_alter. + * + * @param array $form + * Form array. + * @param array $form_state + * Form state array. + */ +function eu_cookie_compliance_admin_category_form_submit(array $form, array &$form_state) { + $categories = variable_get('eu_cookie_compliance_categories', array()); + + if (!isset($categories[$form_state['values']['machine_name']])) { + $categories[$form_state['values']['machine_name']] = array( + 'weight' => 0, + ); + } + + $categories[$form_state['values']['machine_name']] = array_merge( + $categories[$form_state['values']['machine_name']], + array( + 'machine_name' => $form_state['values']['machine_name'], + 'label' => $form_state['values']['label'], + 'description' => $form_state['values']['description'], + 'checkbox_default_state' => $form_state['values']['checkbox_default_state'], + ) + ); + + $label = $form_state['values']['label']; + $form_state['values'] = array(); + $form_state['values']['eu_cookie_compliance_categories'] = $categories; + + eu_cookie_compliance_clear_caches(); + drupal_set_message(t('The category %label has been saved.', array( + '%label' => $label, + ))); + $form_state['redirect'] = 'admin/config/system/eu-cookie-compliance/categories'; +} + +/** + * Confirm form for deleting an existing cookie category. + * + * @param array $form + * Form array. + * @param array $form_state + * Form state array. + * @param string $machine_name + * The machine name of the category. + * + * @return array + * Confirm form render array. + */ +function eu_cookie_compliance_admin_category_delete_form($form, &$form_state, $machine_name) { + $categories = variable_get('eu_cookie_compliance_categories', array()); + $form['eu_cookie_compliance_categories'] = array('#type' => 'hidden'); + $category = isset($categories[$machine_name]) ? $categories[$machine_name] : NULL; + if (!$category) { + drupal_set_message(t('You are tying to delete an invalid cookie category: %name.', array('%name' => $machine_name)), 'error'); + drupal_goto('admin/config/system/eu-cookie-compliance/categories'); + } + + $form['machine_name'] = array( + '#type' => 'value', + '#value' => $machine_name, + ); + $question = t('Are you sure you want to delete the %category category?', array( + '%category' => $category['label'], + )); + + $form['#submit'][] = 'eu_cookie_compliance_admin_category_delete_form_submit'; + $form['#submit'][] = 'system_settings_form_submit'; + + return confirm_form($form, $question, 'admin/config/system/eu-cookie-compliance/categories', t('This action cannot be undone.')); +} + +/** + * Submit handler for deleting an existing cookie category. + * + * @param array $form + * Form array. + * @param array $form_state + * Form state array. + */ +function eu_cookie_compliance_admin_category_delete_form_submit($form, &$form_state) { + $categories = variable_get('eu_cookie_compliance_categories', array()); + + $label = $categories[$form_state['values']['machine_name']]['label']; + unset($categories[$form_state['values']['machine_name']]); + + $form_state['values'] = array(); + $form_state['values']['eu_cookie_compliance_categories'] = $categories; + + eu_cookie_compliance_clear_caches(); + drupal_set_message(t('The category %label has been deleted.', array( + '%label' => $label, + ))); + $form_state['redirect'] = 'admin/config/system/eu-cookie-compliance/categories'; +} + +/** + * Callback for checking whether a submitted machine name value already exists. + * + * @param string $machine_name + * The machine name to search for. + * + * @return bool + * Whether the machine name already exists or not. + */ +function eu_cookie_compliance_admin_category_exists($machine_name) { + $categories = variable_get('eu_cookie_compliance_categories', array()); + + if (isset($categories[$machine_name])) { + return TRUE; + } + + return FALSE; +} diff --git a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.admin.inc b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.admin.inc index 4ea7e32c1..75b30119b 100644 --- a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.admin.inc +++ b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.admin.inc @@ -24,7 +24,7 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { $popup_settings = eu_cookie_compliance_get_settings(); $default_filter_format = filter_default_format(); - if ($default_filter_format == 'filtered_html' && filter_format_load('full_html')) { + if ($default_filter_format === 'filtered_html' && filter_format_load('full_html')) { $default_filter_format = 'full_html'; } @@ -129,12 +129,19 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { '#tree' => TRUE, ); + $options = array(); + // Adapt links to follow realm language if present. + $query_parameters = drupal_get_query_parameters(); + if (isset($query_parameters['variable_realm_key_language'])) { + $language_list = language_list(); + $language = $language_list[$query_parameters['variable_realm_key_language']] ?: FALSE; + if ($language) { + $options['language'] = $language; + } + } $form['consent_per_category']['eu_cookie_compliance']['cookie_categories'] = array( - '#type' => 'textarea', - '#title' => t('Cookie categories with separate consent'), - '#description' => t('List of cookie categories that require separate consent. E.g. Functional cookies, Advertisement cookies, …') . - '
' . t('Enter one value per line, in the following format: "key|label" or "key|label|description". Description is optional.'), - '#default_value' => isset($popup_settings['cookie_categories']) ? $popup_settings['cookie_categories'] : '', + '#markup' => t('See separate Categories tab for managing cookie categories themselves.', + array('@url' => url('admin/config/system/eu-cookie-compliance/categories', $options))), ); $form['consent_per_category']['eu_cookie_compliance']['enable_save_preferences_button'] = array( @@ -171,18 +178,6 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { ), ); - $form['consent_per_category']['eu_cookie_compliance']['fix_first_cookie_category'] = array( - '#type' => 'checkbox', - '#title' => t('Tick the first checkbox and mark it read only.'), - '#default_value' => isset($popup_settings['fix_first_cookie_category']) ? $popup_settings['fix_first_cookie_category'] : TRUE, - ); - - $form['consent_per_category']['eu_cookie_compliance']['select_all_categories_by_default'] = array( - '#type' => 'checkbox', - '#title' => t('Tick all category checkboxes by default.'), - '#default_value' => isset($popup_settings['select_all_categories_by_default']) ? $popup_settings['select_all_categories_by_default'] : FALSE, - ); - // The info_template element is initialized in function // eu_cookie_compliance_update_7009(). $default_value = (!empty($popup_settings['info_template'])) ? $popup_settings['info_template'] : 'new'; @@ -190,7 +185,7 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { '#type' => 'radios', '#title' => t('Info banner template'), '#options' => array( - 'legacy' => t('Cookie policy button in popup-buttons section and styled similarly to the Agree button, + 'legacy' => t('Cookie policy button in popup-buttons section and styled similarly to the Agree button, as in earlier versions of this module'), 'new' => t('Cookie policy button in popup-text section, styled differently than the Agree button.'), ), @@ -223,7 +218,7 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { '#type' => 'textarea', '#title' => t('Disable JavaScripts'), '#default_value' => isset($popup_settings['disabled_javascripts']) ? $popup_settings['disabled_javascripts'] : '', - '#description' => 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 without the leading slash, you can use public://path/to/file.js and private://path/to/file.js, and off-site JavaScripts should be written as complete URLs with the leading http(s)://. Note that after the user gives consent, the scripts will be executed in the order you enter here.

Libraries and scripts that attach to Drupal.behaviors are supported. To indicate a behavior that needs to be loaded on consent, append the behavior name after the script with a | (vertical bar). If you also want to conditionally load a library, place that as the third parameter, following another | (vertical bar). Example: modules/custom/custom_module/js/custom.js|customModule|custom_module/custom_module.
If your script file does not attach to Drupal.attributes, you may skip the second parameter. Example: modules/custom/custom_module/js/custom.js||custom_module/custom_module
Note that Drupal behavior name and library parameters are both optional, but may be required to achieve your objective.
") . + '#description' => 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 without the leading slash, you can use public://path/to/file.js and private://path/to/file.js, and off-site JavaScripts should be written as complete URLs with the leading http(s)://. Note that after the user gives consent, the scripts will be executed in the order you enter here.

Libraries and scripts that attach to Drupal.behaviors are supported. To indicate a behavior that needs to be loaded on consent, append the behavior name after the script with a | (vertical bar).
Note that Drupal behavior name parameter is optional, but may be required to achieve your objective.
") . '

' . t('When using the consent method "Opt-in with categories", you can link the script to a specific category by using the format: "category:path/to/the/script.js".'), ); @@ -244,12 +239,24 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { '#tree' => TRUE, ); - $form['cookies']['eu_cookie_compliance']['whitelisted_cookies'] = array( + $form['cookies']['eu_cookie_compliance']['automatic_cookies_removal'] = [ + '#type' => 'checkbox', + '#title' => t("Enable cookie(s) automatic-removal when consent isn't given."), + '#default_value' => isset($popup_settings['automatic_cookies_removal']) ? $popup_settings['automatic_cookies_removal'] : TRUE, + ]; + + $form['cookies']['eu_cookie_compliance']['allowed_cookies'] = array( '#type' => 'textarea', - '#title' => t('Whitelisted cookies'), - '#default_value' => isset($popup_settings['whitelisted_cookies']) ? $popup_settings['whitelisted_cookies'] : '', - '#description' => t("Include the name of cookies, each on a separate line. When using the opt-in or opt-out consent options, this module will delete cookies that are not on the whitelist every few seconds when consent isn't given. PHP session cookies and the cookie for this module are always whitelisted.") . - '

' . t('When using the consent method "Opt-in with categories", you can link the cookie to a specific consent category by using the format: "category:cookie_name". Only when consent is given for the given category, will the cookie be whitelisted.'), + '#title' => t('Allowed cookies'), + '#default_value' => isset($popup_settings['allowed_cookies']) ? $popup_settings['allowed_cookies'] : '', + '#description' => t("Include the name of cookies, each on a separate line. When using the opt-in or opt-out consent options, this module will delete cookies that are not allowed every few seconds when consent isn't given. PHP session cookies and the cookie for this module are always allowed.") . + '

' . t('When using the consent method "Opt-in with categories", you can link the cookie to a specific consent category by using the format: "category:cookie_name". Only when consent is given for the given category, will the cookie be allowed.') . + '
' . t('Cookie names can contain "*" characters which mean a series of any characters.'), + '#states' => array( + 'visible' => array( + 'input[name="eu_cookie_compliance[automatic_cookies_removal]"]' => array('checked' => TRUE), + ), + ), ); $form['consent_storage'] = array( @@ -447,6 +454,9 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { '#type' => 'checkbox', '#title' => t('Put the "Withdraw consent" button on the cookie information banner.'), '#default_value' => isset($popup_settings['withdraw_button_on_info_popup']) ? $popup_settings['withdraw_button_on_info_popup'] : FALSE, + '#states' => array( + 'visible' => array('input[name="eu_cookie_compliance[method]"]' => array('value' => 'categories')), + ), ); $form['withdraw_consent']['eu_cookie_compliance']['container'] = array( @@ -506,7 +516,6 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { '#type' => 'text_format', '#title' => t('Thank you banner message'), '#default_value' => isset($popup_settings['popup_agreed']['value']) ? $popup_settings['popup_agreed']['value'] : '', - '#required' => TRUE, '#format' => isset($popup_settings['popup_agreed']['format']) ? $popup_settings['popup_agreed']['format'] : $default_filter_format, ); @@ -515,16 +524,11 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { '#title' => t('More info button label'), '#default_value' => isset($popup_settings['popup_find_more_button_message']) ? $popup_settings['popup_find_more_button_message'] : t('More info'), '#size' => 30, - '#required' => TRUE, '#states' => array( 'visible' => array( array('input[name="eu_cookie_compliance[show_disagree_button]"]' => array('checked' => TRUE)), array('input[name="eu_cookie_compliance[method]"]' => array('!value' => 'default')), ), - 'required' => array( - array('input[name="eu_cookie_compliance[show_disagree_button]"]' => array('checked' => TRUE)), - array('input[name="eu_cookie_compliance[method]"]' => array('!value' => 'default')), - ), ), ); @@ -533,7 +537,6 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { '#title' => t('Hide button label'), '#default_value' => isset($popup_settings['popup_hide_button_message']) ? $popup_settings['popup_hide_button_message'] : t('Hide'), '#size' => 30, - '#required' => TRUE, ); $form['privacy'] = array( @@ -568,12 +571,42 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { ); } + if (module_exists('translation')) { + $form['privacy']['eu_cookie_compliance']['popup_link_translate'] = array( + '#type' => 'checkbox', + '#title' => t('Try to translate the privacy policy link.'), + '#default_value' => isset($popup_settings['popup_link_translate']) ? $popup_settings['popup_link_translate'] : 0, + '#description' => t('Link needs to be an internal link to a node, i. e. node/123. System tries to get translations for this node using Content Translation from Drupal Core Translation module.'), + ); + } + $form['privacy']['eu_cookie_compliance']['popup_link_new_window'] = array( '#type' => 'checkbox', '#title' => t('Open privacy policy link in a new window.'), '#default_value' => isset($popup_settings['popup_link_new_window']) ? $popup_settings['popup_link_new_window'] : 1, ); + $form['version_set'] = array( + '#type' => 'fieldset', + '#title' => t('Cookie Policy Version'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + + $form['version_set']['eu_cookie_compliance'] = array( + '#type' => 'item', + '#tree' => TRUE, + ); + + $form['version_set']['eu_cookie_compliance']['cookie_policy_version'] = array( + '#type' => 'textfield', + '#title' => t('Cookie Policy Version'), + '#title_display' => 'invisible', + '#description' => t('Change this value to make the cookie information banner reappear for all visitors. A typical usage of this field is to increase the value when the privacy policy has been changed.'), + '#default_value' => isset($popup_settings['cookie_policy_version']) ? $popup_settings['cookie_policy_version'] : '1.0.0', + '#required' => TRUE, + ); + $form['appearance'] = array( '#type' => 'fieldset', '#collapsible' => TRUE, @@ -592,6 +625,15 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { $form_color_picker_type = 'jquery_colorpicker'; } + $form['appearance']['eu_cookie_compliance']['containing_element'] = array( + '#type' => 'textfield', + '#title' => t('Containing element'), + '#default_value' => isset($popup_settings['containing_element']) ? $popup_settings['containing_element'] : 'body', + '#maxlength' => 255, + '#required' => TRUE, + '#description' => t('Serves as the parent element to append cookie banner html to.'), + ); + $popup_position_options = array( 'bottom' => 'Bottom', 'top' => 'Top', @@ -862,6 +904,8 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { $form = system_settings_form($form); $form['#submit'][] = 'eu_cookie_compliance_admin_form_submit'; + drupal_add_js(drupal_get_path('module', 'eu_cookie_compliance') . '/js/eu_cookie_compliance_admin.js'); + return $form; } @@ -874,6 +918,20 @@ function eu_cookie_compliance_admin_form(array $form, array $form_state) { * Form state array. */ function eu_cookie_compliance_admin_form_validate(array $form, array &$form_state) { + if ($form_state['values']['eu_cookie_compliance']['popup_agreed_enabled']) { + if (empty($form_state['values']['eu_cookie_compliance']['popup_agreed']['value'])) { + form_set_error('eu_cookie_compliance][popup_agreed', t('@name field is required', array('@name' => '"Thank you" banner message'))); + } + + if (empty($form_state['values']['eu_cookie_compliance']['popup_find_more_button_message'])) { + form_set_error('eu_cookie_compliance][popup_find_more_button_message', t('@name field is required', array('@name' => 'More info button label'))); + } + + if (empty($form_state['values']['eu_cookie_compliance']['popup_hide_button_message'])) { + form_set_error('eu_cookie_compliance][popup_hide_button_message', t('@name field is required', array('@name' => 'Hide button label'))); + } + } + if ((!preg_match('/^[1-9][0-9]{0,4}$/', $form_state['values']['eu_cookie_compliance']['popup_delay'])) && ($form_state['values']['eu_cookie_compliance']['popup_delay'] != '0')) { form_set_error('eu_cookie_compliance][popup_delay', t('Delay must be an integer value.')); } @@ -881,7 +939,7 @@ function eu_cookie_compliance_admin_form_validate(array $form, array &$form_stat if (!preg_match('/^[1-9][0-9]{0,4}$/', $form_state['values']['eu_cookie_compliance']['popup_height']) && !empty($form_state['values']['eu_cookie_compliance']['popup_height'])) { form_set_error('eu_cookie_compliance][popup_height', t('Height must be an integer value .')); } - if (!preg_match('/^[1-9][0-9]{1,4}\%?$/', $form_state['values']['eu_cookie_compliance']['popup_width'])) { + if (!preg_match('/^[1-9][0-9]{1,4}%?$/', $form_state['values']['eu_cookie_compliance']['popup_width'])) { form_set_error('eu_cookie_compliance][popup_width', t('Width must be an integer or a percentage value.')); } } @@ -900,28 +958,32 @@ function eu_cookie_compliance_admin_form_validate(array $form, array &$form_stat form_set_error('eu_cookie_compliance][popup_link', t('Looks like your privacy policy link contains a fragment #. You should make this an absolute url, e.g. @link', array('@link' => $popup_link))); } - if ($popup_link == '' && $form_state['values']['eu_cookie_compliance']['show_disagree_button']) { + if ($popup_link === '' && $form_state['values']['eu_cookie_compliance']['show_disagree_button']) { drupal_set_message(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'); } // Handle legacy settings for popup_position: - if ($form_state['values']['eu_cookie_compliance']['popup_position'] == 'top') { + if ($form_state['values']['eu_cookie_compliance']['popup_position'] === 'top') { $form_state['values']['eu_cookie_compliance']['popup_position'] = TRUE; } - elseif ($form_state['values']['eu_cookie_compliance']['popup_position'] == 'bottom') { + elseif ($form_state['values']['eu_cookie_compliance']['popup_position'] === 'bottom') { $form_state['values']['eu_cookie_compliance']['popup_position'] = FALSE; } $method = $form_state['values']['eu_cookie_compliance']['method']; - if ($method != 'default') { + // Fix possible urlencoded strings (#3171654). + $form_state['values']['eu_cookie_compliance']['disabled_javascripts'] = str_replace('%3A', ':', $form_state['values']['eu_cookie_compliance']['disabled_javascripts']); + + if ($method !== 'default') { $form_state['values']['eu_cookie_compliance']['show_disagree_button'] = TRUE; $form_state['values']['eu_cookie_compliance']['popup_clicking_confirmation'] = FALSE; $form_state['values']['eu_cookie_compliance']['popup_scrolling_confirmation'] = FALSE; $form_state['values']['eu_cookie_compliance']['info_template'] = 'new'; } else { - $form_state['values']['eu_cookie_compliance']['whitelisted_cookies'] = ''; + $form_state['values']['eu_cookie_compliance']['allowed_cookies'] = ''; + $form_state['values']['eu_cookie_compliance']['automatic_cookie_removal'] = FALSE; $form_state['values']['eu_cookie_compliance']['disabled_javascripts'] = ''; $form_state['values']['eu_cookie_compliance']['withdraw_enabled'] = FALSE; } diff --git a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.info b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.info index d08656c16..41a823dae 100644 --- a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.info +++ b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.info @@ -4,8 +4,8 @@ core = 7.x configure = admin/config/system/eu-cookie-compliance dependencies[] = ctools -; Information added by Drupal.org packaging script on 2020-05-06 -version = "7.x-1.32" +; Information added by Drupal.org packaging script on 2020-10-21 +version = "7.x-1.35" core = "7.x" project = "eu_cookie_compliance" -datestamp = "1588754554" +datestamp = "1603297775" diff --git a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.install b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.install index acfc21de5..f7735bef3 100644 --- a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.install +++ b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.install @@ -100,11 +100,11 @@ function eu_cookie_compliance_uninstall() { function eu_cookie_compliance_requirements($phase) { $requirements = array(); - if ($phase == 'runtime') { + if ($phase === 'runtime') { module_load_include('module', 'eu_cookie_compliance', 'eu_cookie_compliance'); $settings = eu_cookie_compliance_get_settings(); - if (!empty($settings['popup_link']) && $settings['popup_link'] == '' && !empty($settings['show_disagree_button']) && $settings['show_disagree_button']) { + if (!empty($settings['popup_link']) && $settings['popup_link'] === '' && !empty($settings['show_disagree_button']) && $settings['show_disagree_button']) { $requirements['eu_cookie_compliance'] = array( 'title' => t('EU Cookie Compliance'), 'severity' => REQUIREMENT_ERROR, @@ -144,17 +144,14 @@ function eu_cookie_compliance_module_set_weight() { */ function _eu_cookie_compliance_get_popup_default_setting() { $default_filter_format = filter_default_format(); - if ($default_filter_format == 'filtered_html' && filter_format_load('full_html')) { + if ($default_filter_format === 'filtered_html' && filter_format_load('full_html')) { $default_filter_format = 'full_html'; } module_load_include('module', 'eu_cookie_compliance', 'eu_cookie_compliance'); - $cookie_policy = _eu_cookie_compliance_find_privacy_policy(); - if (!$cookie_policy) { - $cookie_policy = ''; - } return array( + 'cookie_policy_version' => '1.0.0', 'info_template' => 'new', 'popup_info' => array( 'value' => '

We use cookies on this site to enhance your user experience

By clicking the Accept button, you agree to us doing so.

', @@ -172,7 +169,7 @@ function _eu_cookie_compliance_get_popup_default_setting() { 'popup_height' => '', 'popup_width' => '100%', 'popup_delay' => 1000, - 'popup_link' => $cookie_policy, + 'popup_link' => '', 'fixed_top_position' => TRUE, 'consent_storage_method' => 'do_not_store', 'disabled_javascripts' => '', @@ -217,12 +214,12 @@ function _eu_cookie_compliance_get_popup_default_setting() { 'reload_page' => 0, 'save_preferences_button_label' => 'Save preferences', 'script_scope' => 'footer', - 'select_all_categories_by_default' => 0, 'show_disagree_button' => 1, 'use_bare_css' => 0, 'use_mobile_message' => 0, - 'whitelisted_cookies' => '', + 'allowed_cookies' => '', 'withdraw_button_on_info_popup' => 0, + 'containing_element' => 'body', ); } @@ -420,7 +417,7 @@ function eu_cookie_compliance_update_7007() { */ function eu_cookie_compliance_update_7008() { $default_filter_format = filter_default_format(); - if ($default_filter_format == 'filtered_html' && filter_format_load('full_html')) { + if ($default_filter_format === 'filtered_html' && filter_format_load('full_html')) { $default_filter_format = 'full_html'; } $eu_cookie_settings = variable_get('eu_cookie_compliance', array()); @@ -445,7 +442,7 @@ function eu_cookie_compliance_update_7008() { function eu_cookie_compliance_update_7009() { $eu_cookie_settings = variable_get('eu_cookie_compliance', array()); - if (!empty($eu_cookie_settings['method']) && $eu_cookie_settings['method'] == 'default' && !empty($eu_cookie_settings['withdraw_enabled']) && $eu_cookie_settings['withdraw_enabled'] == 1) { + if (!empty($eu_cookie_settings['method']) && $eu_cookie_settings['method'] === 'default' && !empty($eu_cookie_settings['withdraw_enabled']) && $eu_cookie_settings['withdraw_enabled'] == 1) { $eu_cookie_settings['withdraw_enabled'] = 0; } variable_set('eu_cookie_compliance', $eu_cookie_settings); @@ -560,3 +557,188 @@ function eu_cookie_compliance_update_7013() { } } } + +/** + * Change configuration variable whitelisted_cookies to allowed_cookies. + */ +function eu_cookie_compliance_update_7014() { + $eu_cookie_settings = variable_get('eu_cookie_compliance', array()); + $eu_cookie_settings['allowed_cookies'] = $eu_cookie_settings['whitelisted_cookies']; + unset($eu_cookie_settings['whitelisted_cookies']); + variable_set('eu_cookie_compliance', $eu_cookie_settings); +} + +/** + * Add config variable for containing element. + */ +function eu_cookie_compliance_update_7015() { + $eu_cookie_settings = variable_get('eu_cookie_compliance', array()); + $new_settings = array( + 'containing_element' => 'body', + ); + variable_set('eu_cookie_compliance', $eu_cookie_settings + $new_settings); + + if (module_exists('i18n_variable')) { + $languages = language_list(); + foreach (array_keys($languages) as $langcode) { + $settings = i18n_variable_get('eu_cookie_compliance', $langcode); + if (!empty($settings)) { + $updated_setting = $settings + $new_settings; + variable_realm_set('language', $langcode, 'eu_cookie_compliance', $updated_setting, FALSE); + } + } + } + + // Set configuration variables for all domains. + if (module_exists('domain_settings') && module_exists('domain_conf')) { + $domains = domain_domains(); + foreach ($domains as $domain_data) { + $domain_id = $domain_data['domain_id']; + $settings = domain_conf_variable_get($domain_id, 'eu_cookie_compliance'); + $updated_settings = $settings + $new_settings; + domain_conf_variable_save($domain_id, 'eu_cookie_compliance', $updated_settings); + } + } +} + +/** + * Add config variable for cookie policy version. + */ +function eu_cookie_compliance_update_7016() { + $eu_cookie_settings = variable_get('eu_cookie_compliance', array()); + $new_settings = array( + 'cookie_policy_version' => '1.0.0', + ); + variable_set('eu_cookie_compliance', $eu_cookie_settings + $new_settings); + + if (module_exists('i18n_variable')) { + $languages = language_list(); + foreach (array_keys($languages) as $langcode) { + $settings = i18n_variable_get('eu_cookie_compliance', $langcode); + if (!empty($settings)) { + $updated_setting = $settings + $new_settings; + variable_realm_set('language', $langcode, 'eu_cookie_compliance', $updated_setting, FALSE); + } + } + } + + // Set configuration variables for all domains. + if (module_exists('domain_settings') && module_exists('domain_conf')) { + $domains = domain_domains(); + foreach ($domains as $domain_data) { + $domain_id = $domain_data['domain_id']; + $settings = domain_conf_variable_get($domain_id, 'eu_cookie_compliance'); + $updated_settings = $settings + $new_settings; + domain_conf_variable_save($domain_id, 'eu_cookie_compliance', $updated_settings); + } + } +} + +/** + * Migrate categories from string to array. + */ +function eu_cookie_compliance_update_7017() { + $settings = variable_get('eu_cookie_compliance', array()); + $categories = $settings['cookie_categories']; + + if (!is_array($categories)) { + // Split the string by new line and pipes and transform into array. + $categories = _eu_cookie_compliance_extract_category_key_label_description($categories); + $categories = _migrate_cookie_categories_to_separate_structure($categories, $settings); + } + + // Store categories in a new variable. + variable_set('eu_cookie_compliance_categories', $categories); + + // Remove legacy values from main variable. + unset($settings['cookie_categories']); + unset($settings['fix_first_cookie_category']); + variable_set('eu_cookie_compliance', $settings); + + if (module_exists('i18n_variable')) { + $languages = language_list(); + foreach (array_keys($languages) as $langcode) { + $settings = i18n_variable_get('eu_cookie_compliance', $langcode, array()); + $categories = $settings['cookie_categories']; + + if (!is_array($categories)) { + // Split the string by new line and pipes and transform into array. + $categories = _eu_cookie_compliance_extract_category_key_label_description($categories); + $categories = _migrate_cookie_categories_to_separate_structure($categories, $settings); + } + + // Store categories in a new variable. + i18n_variable_set('eu_cookie_compliance_categories', $categories, $langcode); + + // Remove legacy values from main variable. + unset($settings['cookie_categories']); + unset($settings['fix_first_cookie_category']); + i18n_variable_set('eu_cookie_compliance', $settings, $langcode); + } + } + + // Set configuration variables for all domains. + if (module_exists('domain_settings') && module_exists('domain_conf')) { + $domains = domain_domains(); + foreach ($domains as $domain_data) { + $domain_id = $domain_data['domain_id']; + $settings = domain_conf_variable_get($domain_id, 'eu_cookie_compliance'); + $categories = $settings['cookie_categories']; + + if (!is_array($categories)) { + // Split the string by new line and pipes and transform into array. + $categories = _eu_cookie_compliance_extract_category_key_label_description($categories); + $categories = _migrate_cookie_categories_to_separate_structure($categories, $settings); + } + + // Store categories in a new variable. + domain_conf_variable_save($domain_id, 'eu_cookie_compliance_categories', $categories); + + // Remove legacy values from main variable. + unset($settings['cookie_categories']); + unset($settings['fix_first_cookie_category']); + domain_conf_variable_save($domain_id, 'eu_cookie_compliance', $settings); + } + } +} + +/** + * Add variable for automatic cookies removal toggle. + */ +function eu_cookie_compliance_update_7018() { + $eu_cookie_settings = variable_get('eu_cookie_compliance', array()); + // No option to disable given before, so the default is TRUE. + $eu_cookie_settings['automatic_cookies_removal'] = TRUE; + variable_set('eu_cookie_compliance', $eu_cookie_settings); +} + +/** + * Transform string based categories into array based ones. + * + * @param $categories + * The array of categories to transform. + * @param $settings + * The full configuration settings. + * + * @return array + */ +function _migrate_cookie_categories_to_separate_structure($categories, $settings) { + if (count($categories)) { + $i = 0; + // Add default values for new attributes like weight. + foreach ($categories as $machine_name => &$category) { + $category['machine_name'] = $machine_name; + $category['checkbox_default_state'] = 'unchecked'; + // Preserve all categories being automatically checked + // and first category's special treatment, if any. + if (!empty($settings['select_all_categories_by_default'])) { + $category['checkbox_default_state'] = 'checked'; + } + if ($i === 0 && !empty($settings['fix_first_cookie_category'])) { + $category['checkbox_default_state'] = 'required'; + } + $category['weight'] = $i++; + } + } + return $categories; +} diff --git a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.module b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.module index b71d0119d..89e76a9b7 100644 --- a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.module +++ b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.module @@ -22,6 +22,43 @@ function eu_cookie_compliance_menu() { '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', @@ -41,11 +78,19 @@ function eu_cookie_compliance_menu() { 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)) { + if ($module === 'eu_cookie_compliance' && !empty($plugin)) { return 'plugins/' . $plugin; } } @@ -63,7 +108,7 @@ function eu_cookie_compliance_ctools_plugin_type() { * Implements hook_page_alter(). */ function eu_cookie_compliance_page_alter(&$page) { - if (isset($page['#theme']) && $page['#theme'] == 'panels_everywhere_page') { + if (isset($page['#theme']) && $page['#theme'] === 'panels_everywhere_page') { eu_cookie_compliance_page_build($page); } } @@ -133,7 +178,7 @@ function eu_cookie_compliance_page_build(&$page) { // Set default template (based on earlier version of module). if (!isset($popup_settings['info_template'])) { - $popup_settings['info_template'] = $method == 'default' ? 'legacy' : 'new'; + $popup_settings['info_template'] = $method === 'default' ? 'legacy' : 'new'; } // Array storage for caching full client data. if (module_exists('domain')) { @@ -173,7 +218,7 @@ function eu_cookie_compliance_page_build(&$page) { .eu-cookie-withdraw-tab { border-color: #' . check_plain($popup_settings['popup_text_hex']) . ';}'; } - if ($popup_settings['info_template'] == 'new') { + if ($popup_settings['info_template'] === 'new') { $data['css'] .= '.eu-cookie-compliance-more-button {color: #' . check_plain($popup_settings['popup_text_hex']) . ' !important;}'; } } @@ -186,7 +231,7 @@ function eu_cookie_compliance_page_build(&$page) { $show_disagree_buttons = FALSE; } - if ($method == 'auto') { + if ($method === 'auto') { $dnt = isset($_SERVER['HTTP_DNT']) ? $_SERVER['HTTP_DNT'] : NULL; if ((int) $dnt === 0 && $dnt !== NULL) { $method = 'default'; @@ -257,14 +302,26 @@ function eu_cookie_compliance_page_build(&$page) { break; } - $cookie_categories = $popup_settings['cookie_categories']; - $fix_first_cookie_category = $popup_settings['fix_first_cookie_category']; - $cookie_categories = $method === 'categories' ? _eu_cookie_compliance_extract_category_key_label_description($cookie_categories) : FALSE; + 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, @@ -274,7 +331,6 @@ function eu_cookie_compliance_page_build(&$page) { 'secondary_button_class' => $secondary_button_class, 'cookie_categories' => $cookie_categories, 'save_preferences_button_label' => $save_preferences_button_label, - 'fix_first_cookie_category' => $fix_first_cookie_category, 'privacy_settings_tab_label' => $privacy_settings_tab_label, 'withdraw_button_on_info_popup' => $withdraw_button_on_info_popup, 'method' => $method, @@ -288,7 +344,6 @@ function eu_cookie_compliance_page_build(&$page) { 'secondary_button_class' => $secondary_button_class, 'cookie_categories' => $cookie_categories, 'save_preferences_button_label' => $save_preferences_button_label, - 'fix_first_cookie_category' => $fix_first_cookie_category, 'privacy_settings_tab_label' => $privacy_settings_tab_label, 'withdraw_button_on_info_popup' => $withdraw_button_on_info_popup, 'method' => $method, @@ -311,10 +366,10 @@ function eu_cookie_compliance_page_build(&$page) { $html_agreed = preg_replace('//', '', $html_agreed); $withdraw_markup = preg_replace('//', '', $withdraw_markup); } - $cookie_categories = $popup_settings['cookie_categories']; - $cookie_categories = _eu_cookie_compliance_extract_category_key_label_description($cookie_categories); + $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, @@ -327,14 +382,14 @@ function eu_cookie_compliance_page_build(&$page) { '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_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', + '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', ''), @@ -344,15 +399,16 @@ function eu_cookie_compliance_page_build(&$page) { '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, - 'whitelisted_cookies' => !empty($popup_settings['whitelisted_cookies']) ? $popup_settings['whitelisted_cookies'] : '', + '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'], - 'fix_first_cookie_category' => $popup_settings['fix_first_cookie_category'], - 'select_all_categories_by_default' => $popup_settings['select_all_categories_by_default'], 'cookie_name' => !empty($popup_settings['cookie_name']) ? $popup_settings['cookie_name'] : '', + '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'] : '', ); // For some reason, we're getting the wrong language when editing the // localized form, so we shouldn't cache. @@ -361,8 +417,13 @@ function eu_cookie_compliance_page_build(&$page) { } } if ($data['css']) { - drupal_add_css($data['css'], array( - 'type' => 'inline', + $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, )); } @@ -378,20 +439,20 @@ function eu_cookie_compliance_page_build(&$page) { $disabled_javascripts = array_filter($disabled_javascripts, 'strlen'); foreach ($disabled_javascripts as $script) { - $parts = explode('%3A', $script); + $parts = explode(':', $script); $category = NULL; - if (count($parts) > 1) { + if (count($parts) > 1 && $popup_settings['method'] === 'categories') { $category = array_shift($parts); - $script = implode(':', $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, '%7C') !== FALSE) { + if (strpos($script, '|') !== FALSE) { // Swallow a notice in case there is no behavior name. - @list($script, $attach_name) = explode('%7C', $script); + @list($script, $attach_name) = explode('|', $script); } _eu_cookie_compliance_convert_relative_uri($script); @@ -417,12 +478,12 @@ function eu_cookie_compliance_page_build(&$page) { } if ($load_disabled_scripts) { - drupal_add_js('function euCookieComplianceLoadScripts(category) {' . $load_disabled_scripts . '}', array('type' => 'inline', 'scope' => $script_scope)); + 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('var 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('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) { @@ -437,6 +498,7 @@ function eu_cookie_compliance_page_build(&$page) { 'scope' => $script_scope, 'group' => JS_DEFAULT, 'weight' => 0, + 'defer' => TRUE, )); } } @@ -449,6 +511,9 @@ function eu_cookie_compliance_permission() { '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', ), @@ -470,9 +535,7 @@ function eu_cookie_compliance_theme() { 'secondary_button_label' => NULL, 'primary_button_class' => NULL, 'secondary_button_class' => NULL, - 'cookie_categories' => NULL, 'save_preferences_button_label' => NULL, - 'fix_first_cookie_category' => NULL, 'privacy_settings_tab_label' => NULL, 'withdraw_button_on_info_popup' => FALSE, 'method' => 'default', @@ -512,6 +575,9 @@ function eu_cookie_compliance_theme() { ), 'path' => $path, ), + 'eu_cookie_compliance_admin_categories_form' => array( + 'render element' => 'form', + ), ); } @@ -571,15 +637,12 @@ function eu_cookie_compliance_get_settings($setting = 'all') { // Ensure a default value exists for each setting. $popup_settings += array( 'consent_storage_method' => 'do_not_store', - 'cookie_categories' => NULL, 'enable_save_preferences_button' => NULL, - 'fix_first_cookie_category' => NULL, - 'select_all_categories_by_default' => NULL, 'show_disagree_button' => TRUE, 'withdraw_button_on_info_popup' => NULL, ); - if ($setting == 'all') { + if ($setting === 'all') { return $popup_settings; } @@ -691,10 +754,19 @@ function eu_cookie_compliance_js_alter(&$javascript) { foreach ($disabled_javascripts as $script) { // Remove 'category:' if present. $parts = explode(':', $script); - $script = end($parts); + // Deal with absolute links with category. + if (!empty($parts[2]) && strpos($parts[2], '//') === 0) { + array_shift($parts); + $script = implode(':', $parts); + } + // Deal with relative links. + else if (!empty($parts[1]) && strpos($parts[1], '//') !== 0) { + $script = end($parts); + } // Parse the string and drop the parameter that is a behavior name. - @list($script, $attach_name) = explode('|', $script); - _eu_cookie_compliance_convert_relative_uri($script); + if (strpos($script, '|') !== FALSE) { + @list($script, $attach_name) = explode('|', $script); + } unset($javascript[$script]); } @@ -705,7 +777,7 @@ function eu_cookie_compliance_js_alter(&$javascript) { * Implements hook_library_alter(). */ function eu_cookie_compliance_library_alter(&$libraries, $module) { - if ($module != 'system') { + 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'; @@ -738,69 +810,6 @@ function _eu_cookie_compliance_explode_multiple_lines($text, $convert = TRUE) { return $text; } -/** - * Attempt to find the cookie/privacy policy by searching for common titles. - * - * @return bool|string - * URL to the node if found, otherwise FALSE. - */ -function _eu_cookie_compliance_find_privacy_policy() { - $database_type = $GLOBALS['databases']['default']['default']['driver']; - - if ($database_type === 'sqlsrv') { - $query = db_query( - "select nid from {node} where - title LIKE 'Privacy%' OR - title LIKE 'Privacy policy%' OR - title LIKE 'Privacy Policy%' OR - title LIKE 'Cookie policy%' OR - title LIKE 'Cookie Policy%' OR - title LIKE 'Terms of use%' OR - title LIKE 'Terms of Use%' OR - title LIKE 'Terms of service%' OR - title LIKE 'Terms of Service%' OR - title LIKE 'Terms and conditions%' OR - title LIKE 'Terms and Conditions%' - " - ); - $result = $query->fetchAllAssoc('nid'); - - if (!empty($result)) { - return ('/node/' . reset(array_keys($result))); - } - } - else { - // Select operator based on the database type. - switch ($database_type) { - case 'pgsql': - $op = '~*'; - break; - - case 'sqlite': - $op = 'REGEXP'; - // The regexp function is not defined in SQLite for Drupal 7. - Database::getConnection('default')->sqliteCreateFunction('REGEXP', '_eu_cookie_compliance_sqlite_regex_query_builder', 2); - break; - - default: - $op = 'RLIKE'; - } - - $pattern = 'privacy|privacy +policy|cookie +policy|terms +of +use|terms +of +service|terms +and +conditions'; - - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', 'node') - ->propertyCondition('title', $pattern, $op); - - $result = $query->execute(); - if (isset($result['node'])) { - $nodes = array_keys($result['node']); - return ('node/' . array_shift($nodes)); - } - } - return FALSE; -} - /** * Callback function for SQLite regular expression query. * @@ -825,20 +834,15 @@ function _eu_cookie_compliance_sqlite_regex_query_builder($pattern, $data) { 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/') { + 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; } - else { - return FALSE; - } - } - else { - return FALSE; } + return FALSE; } /** @@ -852,17 +856,23 @@ function eu_cookie_compliance_store_consent($type) { $type = check_plain($type); $consent_storage_method = eu_cookie_compliance_get_settings('consent_storage_method'); - if ($consent_storage_method == 'do_not_store') { + 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); - $consent_storage_function = $consent_storage_plugin['consent_storage_callback']; - $result = $consent_storage_function($type); + 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($result); + drupal_json_output(NULL); drupal_exit(); } @@ -873,9 +883,6 @@ function eu_cookie_compliance_store_consent($type) { * * @param string $element * Url to transform. - * - * @return mixed - * Paths converted. */ function _eu_cookie_compliance_convert_relative_uri(&$element) { $element = preg_replace('/^\//', '', file_create_url($element)); @@ -890,3 +897,50 @@ function eu_cookie_compliance_views_api() { '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)'), + ); +} diff --git a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.variable.inc b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.variable.inc index ec85f47c5..671ac458a 100644 --- a/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.variable.inc +++ b/frontend/drupal/sites/all/modules/eu_cookie_compliance/eu_cookie_compliance.variable.inc @@ -17,6 +17,14 @@ function eu_cookie_compliance_variable_info($options) { 'localize' => TRUE, 'multidomain' => TRUE, ); + $variable['eu_cookie_compliance_categories'] = array( + 'title' => t('EU Cookie Compliance categories'), + 'description' => t('Settings for the EU Cookie Compliance categories.'), + 'type' => 'array', + 'access' => 'administer EU Cookie Compliance popup', + 'localize' => TRUE, + 'multidomain' => TRUE, + ); return $variable; } diff --git a/frontend/drupal/sites/all/modules/eu_cookie_compliance/includes/views/eu_cookie_compliance.views.inc b/frontend/drupal/sites/all/modules/eu_cookie_compliance/includes/views/eu_cookie_compliance.views.inc index 0dbc68a52..065f42aef 100644 --- a/frontend/drupal/sites/all/modules/eu_cookie_compliance/includes/views/eu_cookie_compliance.views.inc +++ b/frontend/drupal/sites/all/modules/eu_cookie_compliance/includes/views/eu_cookie_compliance.views.inc @@ -1,12 +1,12 @@ ').html(html); var $banner = $('.eu-cookie-withdraw-banner', $html); @@ -126,27 +159,29 @@ $html.hide(); var height = 0; if (Drupal.settings.eu_cookie_compliance.popup_position) { - $html.prependTo('body'); + $html.prependTo(Drupal.settings.eu_cookie_compliance.containing_element); height = $html.outerHeight(); $html.show() .addClass('sliding-popup-top') .addClass('clearfix') - .css({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top')) + height) : -1 * height }); + .css({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('padding-top')) + parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('margin-top')) + height) : -1 * height }); // For some reason, the tab outerHeight is -10 if we don't use a timeout // function to reveal the tab. setTimeout(function () { - $html.animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top')) + height) : -1 * height }, Drupal.settings.eu_cookie_compliance.popup_delay, null, function () { + $html.animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('padding-top')) + parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('margin-top')) + height) : -1 * height }, Drupal.settings.eu_cookie_compliance.popup_delay, null, function () { $html.trigger('eu_cookie_compliance_popup_open'); + $('body').addClass('eu-cookie-compliance-popup-open'); + Drupal.eu_cookie_compliance.positionTab(); }); }.bind($html, height), 0); } else { if (Drupal.settings.eu_cookie_compliance.better_support_for_screen_readers) { - $html.prependTo('body'); + $html.prependTo(Drupal.settings.eu_cookie_compliance.containing_element); } else { - $html.appendTo('body'); + $html.appendTo(Drupal.settings.eu_cookie_compliance.containing_element); } height = $html.outerHeight(); $html.show() @@ -157,6 +192,7 @@ setTimeout(function () { $html.animate({ bottom: -1 * height }, Drupal.settings.eu_cookie_compliance.popup_delay, null, function () { $html.trigger('eu_cookie_compliance_popup_open'); + $('body').addClass('eu-cookie-compliance-popup-open'); }); }.bind($html, height), 0); } @@ -164,28 +200,80 @@ Drupal.eu_cookie_compliance.toggleWithdrawBanner = function () { var $wrapper = $('#sliding-popup'); - var $tab = $('.eu-cookie-withdraw-tab'); - var topBottom = (Drupal.settings.eu_cookie_compliance.popup_position ? 'top' : 'bottom'); var height = $wrapper.outerHeight(); - var $bannerIsShowing = Drupal.settings.eu_cookie_compliance.popup_position ? parseInt($wrapper.css('top')) === (!Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top'))) : 0) : parseInt($wrapper.css('bottom')) === 0; + var $bannerIsShowing = ($wrapper.find('.eu-cookie-compliance-banner, .eu-cookie-withdraw-banner').is(':visible')); + if ($bannerIsShowing) { + $bannerIsShowing = Drupal.settings.eu_cookie_compliance.popup_position ? parseInt($wrapper.css('top')) === (!Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('padding-top')) + parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('margin-top'))) : 0) : parseInt($wrapper.css('bottom')) === 0; + } if (Drupal.settings.eu_cookie_compliance.popup_position) { if ($bannerIsShowing) { - $wrapper.animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top')) + height) : -1 * height}, Drupal.settings.eu_cookie_compliance.popup_delay); + $wrapper.animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('padding-top')) + parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('margin-top')) + height) : -1 * height }, Drupal.settings.eu_cookie_compliance.popup_delay).trigger('eu_cookie_compliance_popup_close'); + $('body').removeClass('eu-cookie-compliance-popup-open'); } else { - $wrapper.animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top'))) : 0}, Drupal.settings.eu_cookie_compliance.popup_delay); + // If "Do not show cookie policy when the user clicks the Cookie policy button." is + // selected, the inner banner may be hidden. + $wrapper.find('.eu-cookie-compliance-banner').show(); + $wrapper.animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('padding-top')) + parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('margin-top'))) : 0 }, Drupal.settings.eu_cookie_compliance.popup_delay).trigger('eu_cookie_compliance_popup_open'); + $('body').addClass('eu-cookie-compliance-popup-open'); } } else { if ($bannerIsShowing) { - $wrapper.animate({'bottom' : -1 * height}, Drupal.settings.eu_cookie_compliance.popup_delay); + $wrapper.animate({ 'bottom': -1 * height }, Drupal.settings.eu_cookie_compliance.popup_delay).trigger('eu_cookie_compliance_popup_close'); + $('body').removeClass('eu-cookie-compliance-popup-open'); } else { - $wrapper.animate({'bottom' : 0}, Drupal.settings.eu_cookie_compliance.popup_delay); + // If "Do not show cookie policy when the user clicks the Cookie policy button." is + // selected, the inner banner may be hidden. + $wrapper.find('.eu-cookie-compliance-banner').show(); + $wrapper.animate({ 'bottom': 0 }, Drupal.settings.eu_cookie_compliance.popup_delay).trigger('eu_cookie_compliance_popup_open'); + $('body').addClass('eu-cookie-compliance-popup-open'); } } }; + Drupal.eu_cookie_compliance.resizeListener = function () { + var $wrapper = $('#sliding-popup'); + + const debounce = function (func, wait ) { + var timeout; + + return function executedFunction() { + var later = function () { + clearTimeout(timeout); + func(); + }; + + clearTimeout(timeout); + timeout = setTimeout(later, wait); + }; + }; + + var checkIfPopupIsClosed = debounce(function () { + var wrapperHeight = $wrapper.outerHeight(); + if (Drupal.settings.eu_cookie_compliance.popup_position) { + var wrapperTopProperty = parseFloat($wrapper.css('bottom')); + if (wrapperTopProperty !== 0) { + $wrapper.css('top', wrapperHeight * -1); + } + } + else { + var wrapperBottomProperty = parseFloat($wrapper.css('bottom')); + if (wrapperBottomProperty !== 0) { + $wrapper.css('bottom', wrapperHeight * -1); + } + } + }, 50); + + setTimeout(function () { + checkIfPopupIsClosed(); + }); + + window.addEventListener('resize', checkIfPopupIsClosed); + + }; + Drupal.eu_cookie_compliance.createPopup = function (html, closed) { // This fixes a problem with jQuery 1.9. var $popup = $('
').html(html); @@ -198,23 +286,30 @@ $popup.hide(); var height = 0; if (Drupal.settings.eu_cookie_compliance.popup_position) { - $popup.prependTo('body'); + $popup.prependTo(Drupal.settings.eu_cookie_compliance.containing_element); height = $popup.outerHeight(); $popup.show() .attr({ 'class': 'sliding-popup-top clearfix' }) - .css({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top')) + height) : -1 * height }); + .css({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('padding-top')) + parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('margin-top')) + height) : -1 * height }); if (closed !== true) { - $popup.animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top'))) : 0 }, Drupal.settings.eu_cookie_compliance.popup_delay, null, function () { + $popup.animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('padding-top')) + parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('margin-top'))) : 0 }, Drupal.settings.eu_cookie_compliance.popup_delay, null, function () { $popup.trigger('eu_cookie_compliance_popup_open'); + $('body').addClass('eu-cookie-compliance-popup-open'); + Drupal.eu_cookie_compliance.positionTab(); }); } + else { + setTimeout(function () { + Drupal.eu_cookie_compliance.positionTab(); + }, 0); + } } else { if (Drupal.settings.eu_cookie_compliance.better_support_for_screen_readers) { - $popup.prependTo('body'); + $popup.prependTo(Drupal.settings.eu_cookie_compliance.containing_element); } else { - $popup.appendTo('body'); + $popup.appendTo(Drupal.settings.eu_cookie_compliance.containing_element); } height = $popup.outerHeight(); @@ -224,6 +319,7 @@ if (closed !== true) { $popup.animate({bottom: 0}, Drupal.settings.eu_cookie_compliance.popup_delay, null, function () { $popup.trigger('eu_cookie_compliance_popup_open'); + $('body').addClass('eu-cookie-compliance-popup-open'); }); } } @@ -311,15 +407,27 @@ if (Drupal.settings.eu_cookie_compliance.method === 'categories') { // Select Checked categories. - var categories = $("#eu-cookie-compliance-categories input:checkbox:checked").map(function(){ + var categories = $("#eu-cookie-compliance-categories input:checkbox:checked").map(function () { return $(this).val(); }).get(); Drupal.eu_cookie_compliance.setAcceptedCategories(categories); - // Load scripts for all categories. + // Load scripts for all categories. If no categories selected, none + // will be loaded. Drupal.eu_cookie_compliance.loadCategoryScripts(categories); + if (!categories.length) { + // No categories selected is the same as declining all cookies. + nextStatus = 0; + } } Drupal.eu_cookie_compliance.changeStatus(nextStatus); + + if (Drupal.settings.eu_cookie_compliance.withdraw_enabled && Drupal.settings.eu_cookie_compliance.withdraw_button_on_info_popup) { + Drupal.eu_cookie_compliance.attachWithdrawEvents(); + if (_euccCurrentStatus === 1 || _euccCurrentStatus === 2) { + $('.eu-cookie-withdraw-button').show(); + } + } }; Drupal.eu_cookie_compliance.acceptAllAction = function () { @@ -329,7 +437,7 @@ } Drupal.eu_cookie_compliance.savePreferencesAction = function () { - var categories = $("#eu-cookie-compliance-categories input:checkbox:checked").map(function(){ + var categories = $("#eu-cookie-compliance-categories input:checkbox:checked").map(function () { return $(this).val(); }).get(); var agreedEnabled = Drupal.settings.eu_cookie_compliance.popup_agreed_enabled; @@ -340,14 +448,17 @@ } Drupal.eu_cookie_compliance.setAcceptedCategories(categories); - if (!euCookieComplianceHasLoadedScripts && typeof euCookieComplianceLoadScripts === "function") { - euCookieComplianceLoadScripts(); - } + // Load scripts for all categories. If no categories selected, none + // will be loaded. Drupal.eu_cookie_compliance.loadCategoryScripts(categories); + if (!categories.length) { + // No categories selected is the same as declining all cookies. + nextStatus = 0; + } Drupal.eu_cookie_compliance.changeStatus(nextStatus); }; - Drupal.eu_cookie_compliance.loadCategoryScripts = function(categories) { + Drupal.eu_cookie_compliance.loadCategoryScripts = function (categories) { for (var cat in categories) { if (euCookieComplianceHasLoadedScriptsForCategory[cat] !== true && typeof euCookieComplianceLoadScripts === "function") { euCookieComplianceLoadScripts(categories[cat]); @@ -360,19 +471,21 @@ Drupal.eu_cookie_compliance.setStatus(0); var popup = $('#sliding-popup'); if (popup.hasClass('sliding-popup-top')) { - popup.animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top')) + popup.outerHeight()) : popup.outerHeight() * -1 }, Drupal.settings.eu_cookie_compliance.popup_delay, null, function () { + popup.animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('padding-top')) + parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('margin-top')) + popup.outerHeight()) : popup.outerHeight() * -1 }, Drupal.settings.eu_cookie_compliance.popup_delay, null, function () { popup.hide(); }).trigger('eu_cookie_compliance_popup_close'); + $('body').removeClass('eu-cookie-compliance-popup-open'); } else { popup.animate({ bottom: popup.outerHeight() * -1 }, Drupal.settings.eu_cookie_compliance.popup_delay, null, function () { popup.hide(); }).trigger('eu_cookie_compliance_popup_close'); + $('body').removeClass('eu-cookie-compliance-popup-open'); } }; Drupal.eu_cookie_compliance.withdrawAction = function () { - Drupal.eu_cookie_compliance.setStatus(0); + Drupal.eu_cookie_compliance.setStatus(null); Drupal.eu_cookie_compliance.setAcceptedCategories([]); location.reload(); }; @@ -382,9 +495,11 @@ Drupal.eu_cookie_compliance.setStatus(0); if (Drupal.settings.eu_cookie_compliance.withdraw_enabled && Drupal.settings.eu_cookie_compliance.withdraw_button_on_info_popup) { $('#sliding-popup .eu-cookie-compliance-banner').trigger('eu_cookie_compliance_popup_close').hide(); + $('body').removeClass('eu-cookie-compliance-popup-open'); } else { $('#sliding-popup').trigger('eu_cookie_compliance_popup_close').remove(); + $('body').removeClass('eu-cookie-compliance-popup-open'); } } else { @@ -398,79 +513,123 @@ }; Drupal.eu_cookie_compliance.getCurrentStatus = function () { + // Make a new observer & fire it to allow other scripts to hook in. + var preStatusLoadObject = new PreStatusLoad(); + self.handleEvent('preStatusLoad', preStatusLoadObject); + var cookieName = (typeof eu_cookie_compliance_cookie_name === 'undefined' || eu_cookie_compliance_cookie_name === '') ? 'cookie-agreed' : eu_cookie_compliance_cookie_name; - var value = $.cookie(cookieName); - value = parseInt(value); - if (isNaN(value)) { - value = null; + var storedStatus = $.cookie(cookieName); + _euccCurrentStatus = parseInt(storedStatus); + if (isNaN(_euccCurrentStatus)) { + _euccCurrentStatus = null; } - return value; + // Make a new observer & fire it to allow other scripts to hook in. + var postStatusLoadObject = new PostStatusLoad(); + self.handleEvent('postStatusLoad', postStatusLoadObject); + + return _euccCurrentStatus; }; Drupal.eu_cookie_compliance.setPreferenceCheckboxes = function (categories) { for (var i in categories) { - $("#eu-cookie-compliance-categories input:checkbox[value='" + categories[i] + "']").prop("checked", true); + $("#eu-cookie-compliance-categories input:checkbox[value='" + categories[i] + "']").attr("checked", "checked"); } } Drupal.eu_cookie_compliance.getAcceptedCategories = function () { - var allCategories = Drupal.settings.eu_cookie_compliance.cookie_categories; + // Make a new observer & fire it to allow other scripts to hook in. + var prePreferencesLoadObject = new PrePreferencesLoad(); + self.handleEvent('prePreferencesLoad', prePreferencesLoadObject); + var cookieName = (typeof eu_cookie_compliance_cookie_name === 'undefined' || eu_cookie_compliance_cookie_name === '') ? 'cookie-agreed-categories' : Drupal.settings.eu_cookie_compliance.cookie_name + '-categories'; - var value = $.cookie(cookieName); - var selectedCategories = []; + var storedCategories = $.cookie(cookieName); - if (value !== null && typeof value !== 'undefined') { - value = JSON.parse(value); - selectedCategories = value; + if (storedCategories !== null && typeof storedCategories !== 'undefined') { + _euccSelectedCategories = JSON.parse(storedCategories); + } + else { + _euccSelectedCategories = []; } - if (Drupal.eu_cookie_compliance.fix_first_cookie_category && !$.inArray(allCategories[0], selectedCategories)) { - selectedCategories.push(allCategories[0]); + // Merge in required categories if not already present. Mimics old + // logic where "fix first category" changed logic in + // .hasAgreedWithCategory and this function. + for (var _categoryName in Drupal.settings.eu_cookie_compliance.cookie_categories_details) { + var _category = Drupal.settings.eu_cookie_compliance.cookie_categories_details[_categoryName]; + if (_category.checkbox_default_state === 'required' && $.inArray(_category.machine_name, _euccSelectedCategories) === -1) { + _euccSelectedCategories.push(_category.machine_name); + } } - return selectedCategories; + // Make a new observer & fire it to allow other scripts to hook in. + var postPreferencesLoadObject = new PostPreferencesLoad(); + self.handleEvent('postPreferencesLoad', postPreferencesLoadObject); + + return _euccSelectedCategories; }; Drupal.eu_cookie_compliance.changeStatus = function (value) { - var status = Drupal.eu_cookie_compliance.getCurrentStatus(); var reloadPage = Drupal.settings.eu_cookie_compliance.reload_page; - if (status === value) { + var previousState = _euccCurrentStatus; + if (_euccCurrentStatus === value) { return; } if (Drupal.settings.eu_cookie_compliance.popup_position) { - $('.sliding-popup-top').animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top')) + $('#sliding-popup').outerHeight()) : $('#sliding-popup').outerHeight() * -1 }, Drupal.settings.eu_cookie_compliance.popup_delay, function () { - if (value === 1 && status === null && !reloadPage) { - $('.sliding-popup-top').not('.eu-cookie-withdraw-wrapper').html(Drupal.settings.eu_cookie_compliance.popup_html_agreed).animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($('body').css('padding-top')) + parseInt($('body').css('margin-top'))) : 0 }, Drupal.settings.eu_cookie_compliance.popup_delay); + $('.sliding-popup-top').animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('padding-top')) + parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('margin-top')) + $('#sliding-popup').outerHeight()) : $('#sliding-popup').outerHeight() * -1 }, Drupal.settings.eu_cookie_compliance.popup_delay, function () { + if (value === 1 && previousState === null && !reloadPage) { + $('.sliding-popup-top').not('.eu-cookie-withdraw-wrapper').html(Drupal.settings.eu_cookie_compliance.popup_html_agreed).animate({ top: !Drupal.settings.eu_cookie_compliance.fixed_top_position ? -(parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('padding-top')) + parseInt($(Drupal.settings.eu_cookie_compliance.containing_element).css('margin-top'))) : 0 }, Drupal.settings.eu_cookie_compliance.popup_delay); Drupal.eu_cookie_compliance.attachHideEvents(); - } else if (status === 1 && !(Drupal.settings.eu_cookie_compliance.withdraw_enabled && Drupal.settings.eu_cookie_compliance.withdraw_button_on_info_popup)) { - $('.sliding-popup-top').not('.eu-cookie-withdraw-wrapper').trigger('eu_cookie_compliance_popup_close').remove(); } - Drupal.eu_cookie_compliance.showWithdrawBanner(value); + else if (previousState === 1) { + if (Drupal.settings.eu_cookie_compliance.withdraw_enabled && Drupal.settings.eu_cookie_compliance.withdraw_button_on_info_popup) { + // Restore popup content. + if (window.matchMedia('(max-width: ' + Drupal.settings.eu_cookie_compliance.mobile_breakpoint + 'px)').matches && Drupal.settings.eu_cookie_compliance.use_mobile_message) { + $('.sliding-popup-top').not('.eu-cookie-withdraw-wrapper').html(Drupal.settings.eu_cookie_compliance.mobile_popup_html_info); + } + else { + $('.sliding-popup-top').not('.eu-cookie-withdraw-wrapper').html(Drupal.settings.eu_cookie_compliance.popup_html_info); + } + Drupal.eu_cookie_compliance.initPopup(); + Drupal.eu_cookie_compliance.resizeListener(); + } + else { + $('.sliding-popup-top').not('.eu-cookie-withdraw-wrapper').trigger('eu_cookie_compliance_popup_close').remove(); + $('body').removeClass('eu-cookie-compliance-popup-open'); + } + } + if (Drupal.settings.eu_cookie_compliance.withdraw_enabled) { + Drupal.eu_cookie_compliance.showWithdrawBanner(value); + } }); } else { $('.sliding-popup-bottom').animate({ bottom: $('#sliding-popup').outerHeight() * -1 }, Drupal.settings.eu_cookie_compliance.popup_delay, function () { - if (value === 1 && status === null && !reloadPage) { + if (value === 1 && previousState === null && !reloadPage) { $('.sliding-popup-bottom').not('.eu-cookie-withdraw-wrapper').html(Drupal.settings.eu_cookie_compliance.popup_html_agreed).animate({ bottom: 0 }, Drupal.settings.eu_cookie_compliance.popup_delay); Drupal.eu_cookie_compliance.attachHideEvents(); } - else if (status === 1) { + else if (previousState === 1) { if (Drupal.settings.eu_cookie_compliance.withdraw_enabled && Drupal.settings.eu_cookie_compliance.withdraw_button_on_info_popup) { // Restore popup content. if (window.matchMedia('(max-width: ' + Drupal.settings.eu_cookie_compliance.mobile_breakpoint + 'px)').matches && Drupal.settings.eu_cookie_compliance.use_mobile_message) { $('.sliding-popup-bottom').not('.eu-cookie-withdraw-wrapper').html(Drupal.settings.eu_cookie_compliance.mobile_popup_html_info); - } else { + } + else { $('.sliding-popup-bottom').not('.eu-cookie-withdraw-wrapper').html(Drupal.settings.eu_cookie_compliance.popup_html_info); } Drupal.eu_cookie_compliance.initPopup(); + Drupal.eu_cookie_compliance.resizeListener(); } else { $('.sliding-popup-bottom').not('.eu-cookie-withdraw-wrapper').trigger('eu_cookie_compliance_popup_close').remove(); + $('body').removeClass('eu-cookie-compliance-popup-open'); } } - Drupal.eu_cookie_compliance.showWithdrawBanner(value); + if (Drupal.settings.eu_cookie_compliance.withdraw_enabled) { + Drupal.eu_cookie_compliance.showWithdrawBanner(value); + } }); } @@ -485,12 +644,19 @@ if (value === 2 && Drupal.settings.eu_cookie_compliance.withdraw_enabled) { if (!Drupal.settings.eu_cookie_compliance.withdraw_button_on_info_popup) { Drupal.eu_cookie_compliance.createWithdrawBanner(Drupal.settings.eu_cookie_compliance.withdraw_markup); + Drupal.eu_cookie_compliance.resizeListener(); } Drupal.eu_cookie_compliance.attachWithdrawEvents(); + Drupal.eu_cookie_compliance.positionTab(); } }; Drupal.eu_cookie_compliance.setStatus = function (status) { + + // Make a new observer & fire it to allow other scripts to hook in. + var preStatusSaveObject = new PreStatusSave(); + self.handleEvent('preStatusSave', preStatusSaveObject); + var date = new Date(); var domain = Drupal.settings.eu_cookie_compliance.domain ? Drupal.settings.eu_cookie_compliance.domain : ''; var path = Drupal.settings.eu_cookie_compliance.domain_all_sites ? '/' : Drupal.settings.basePath; @@ -511,16 +677,32 @@ date.setDate(date.getDate() + lifetime); $.cookie(cookieName, status, { expires: date, path: path, domain: domain }); } + _euccCurrentStatus = status; $(document).trigger('eu_cookie_compliance.changeStatus', [status]); + // Status set means something happened, update the version. + Drupal.eu_cookie_compliance.setVersion(); // Store consent if applicable. if (Drupal.settings.eu_cookie_compliance.store_consent && ((status === 1 && Drupal.settings.eu_cookie_compliance.popup_agreed_enabled) || (status === 2 && !Drupal.settings.eu_cookie_compliance.popup_agreed_enabled))) { var url = Drupal.settings.basePath + Drupal.settings.pathPrefix + 'eu-cookie-compliance/store_consent/banner'; $.post(url, {}, function (data) { }); } + + // Make a new observer & fire it to allow other scripts to hook in. + var postStatusSaveObject = new PostStatusSave(); + self.handleEvent('postStatusSave', postStatusSaveObject); + + if (status === 0 && Drupal.settings.eu_cookie_compliance.method === 'opt_out') { + euCookieComplianceBlockCookies = setInterval(Drupal.eu_cookie_compliance.BlockCookies, 5000); + } }; Drupal.eu_cookie_compliance.setAcceptedCategories = function (categories) { + + // Make a new observer & fire it to allow other scripts to hook in. + var prePreferencesSaveObject = new PrePreferencesSave(); + self.handleEvent('prePreferencesSave', prePreferencesSaveObject); + var date = new Date(); var domain = Drupal.settings.eu_cookie_compliance.domain ? Drupal.settings.eu_cookie_compliance.domain : ''; var path = Drupal.settings.eu_cookie_compliance.domain_all_sites ? '/' : Drupal.settings.basePath; @@ -535,47 +717,43 @@ var cookie_session = parseInt(Drupal.settings.eu_cookie_compliance.cookie_session); if (cookie_session) { $.cookie(cookieName, categoriesString, { path: path, domain: domain }); - } else { + } + else { var lifetime = parseInt(Drupal.settings.eu_cookie_compliance.cookie_lifetime); date.setDate(date.getDate() + lifetime); $.cookie(cookieName, categoriesString, { expires: date, path: path, domain: domain }); } + _euccSelectedCategories = categories; $(document).trigger('eu_cookie_compliance.changePreferences', [categories]); // TODO: Store categories with consent if applicable? + // Make a new observer & fire it to allow other scripts to hook in. + var postPreferencesSaveObject = new PostPreferencesSave(); + self.handleEvent('postPreferencesSave', postPreferencesSaveObject); }; Drupal.eu_cookie_compliance.hasAgreed = function (category) { - var status = Drupal.eu_cookie_compliance.getCurrentStatus(); - var agreed = (status === 1 || status === 2); + var agreed = (_euccCurrentStatus === 1 || _euccCurrentStatus === 2); - if(category !== undefined && agreed) { + if (category !== undefined && agreed) { agreed = Drupal.eu_cookie_compliance.hasAgreedWithCategory(category); } return agreed; }; - Drupal.eu_cookie_compliance.hasAgreedWithCategory = function(category) { - var allCategories = Drupal.settings.eu_cookie_compliance.cookie_categories; - var agreedCategories = Drupal.eu_cookie_compliance.getAcceptedCategories(); - - if (Drupal.settings.eu_cookie_compliance.fix_first_cookie_category && category === allCategories[0]) { - return true; - } - - return $.inArray(category, agreedCategories) !== -1; + Drupal.eu_cookie_compliance.hasAgreedWithCategory = function (category) { + return $.inArray(category, _euccSelectedCategories) !== -1; }; Drupal.eu_cookie_compliance.showBanner = function () { var showBanner = false; - var status = Drupal.eu_cookie_compliance.getCurrentStatus(); - if ((status === 0 && Drupal.settings.eu_cookie_compliance.method === 'default') || status === null) { - if (!Drupal.settings.eu_cookie_compliance.disagree_do_not_show_popup || status === null) { + if ((_euccCurrentStatus === 0 && Drupal.settings.eu_cookie_compliance.method === 'default') || _euccCurrentStatus === null) { + if (!Drupal.settings.eu_cookie_compliance.disagree_do_not_show_popup || _euccCurrentStatus === null) { showBanner = true; } } - else if (status === 1 && Drupal.settings.eu_cookie_compliance.popup_agreed_enabled) { + else if (_euccCurrentStatus === 1 && Drupal.settings.eu_cookie_compliance.popup_agreed_enabled) { showBanner = true; } @@ -592,31 +770,48 @@ return cookieEnabled; }; - Drupal.eu_cookie_compliance.isWhitelisted = function (cookieName) { + Drupal.eu_cookie_compliance.cookieMatches = function (cookieName, pattern) { + if (cookieName === pattern) { + return true; + } + if (pattern.indexOf('*') < 0) { + return false; + } + try { + var regexp = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '.+') + '$', 'g'); + return regexp.test(cookieName); + } + catch (err) { + return false; + } + }; + + Drupal.eu_cookie_compliance.isAllowed = function (cookieName) { // Skip the PHP session cookie. if (cookieName.indexOf('SESS') === 0 || cookieName.indexOf('SSESS') === 0) { return true; } - // Split the white-listed cookies. - var euCookieComplianceWhitelist = Drupal.settings.eu_cookie_compliance.whitelisted_cookies.split(/\r\n|\n|\r/g); + // Split the allowed cookies. + var euCookieComplianceAllowlist = Drupal.settings.eu_cookie_compliance.allowed_cookies.split(/\r\n|\n|\r/g); // Add the EU Cookie Compliance cookie. - euCookieComplianceWhitelist.push((typeof Drupal.settings.eu_cookie_compliance.cookie_name === 'undefined' || Drupal.settings.eu_cookie_compliance.cookie_name === '') ? 'cookie-agreed' : Drupal.settings.eu_cookie_compliance.cookie_name); - euCookieComplianceWhitelist.push((typeof Drupal.settings.eu_cookie_compliance.cookie_name === 'undefined' || Drupal.settings.eu_cookie_compliance.cookie_name === '') ? 'cookie-agreed-categories' : Drupal.settings.eu_cookie_compliance.cookie_name + '-categories'); + euCookieComplianceAllowlist.push((typeof Drupal.settings.eu_cookie_compliance.cookie_name === 'undefined' || Drupal.settings.eu_cookie_compliance.cookie_name === '') ? 'cookie-agreed' : Drupal.settings.eu_cookie_compliance.cookie_name); + euCookieComplianceAllowlist.push((typeof Drupal.settings.eu_cookie_compliance.cookie_name === 'undefined' || Drupal.settings.eu_cookie_compliance.cookie_name === '') ? 'cookie-agreed-categories' : Drupal.settings.eu_cookie_compliance.cookie_name + '-categories'); + euCookieComplianceAllowlist.push((typeof Drupal.settings.eu_cookie_compliance.cookie_name === 'undefined' || Drupal.settings.eu_cookie_compliance.cookie_name === '') ? 'cookie-agreed-version' : Drupal.settings.eu_cookie_compliance.cookie_name + '-version'); - // Check if the cookie is white-listed. - for (var item in euCookieComplianceWhitelist) { - if (cookieName === euCookieComplianceWhitelist[item]) { + // Check if the cookie is allowed. + for (var item in euCookieComplianceAllowlist) { + if (Drupal.eu_cookie_compliance.cookieMatches(cookieName, euCookieComplianceAllowlist[item])) { return true; } // Handle cookie names that are prefixed with a category. if (Drupal.settings.eu_cookie_compliance.method === 'categories') { - var separatorPos = euCookieComplianceWhitelist[item].indexOf(":"); + var separatorPos = euCookieComplianceAllowlist[item].indexOf(":"); if (separatorPos !== -1) { - var category = euCookieComplianceWhitelist[item].substr(0, separatorPos); - var wlCookieName = euCookieComplianceWhitelist[item].substr(separatorPos + 1); + var category = euCookieComplianceAllowlist[item].substr(0, separatorPos); + var wlCookieName = euCookieComplianceAllowlist[item].substr(separatorPos + 1); - if (wlCookieName === cookieName && Drupal.eu_cookie_compliance.hasAgreedWithCategory(category)) { + if (Drupal.eu_cookie_compliance.cookieMatches(cookieName, wlCookieName) && Drupal.eu_cookie_compliance.hasAgreedWithCategory(category)) { return true; } } @@ -652,12 +847,41 @@ } }; + Drupal.eu_cookie_compliance.getVersion = function () { + var cookieName = (typeof Drupal.settings.eu_cookie_compliance.cookie_name === 'undefined' || Drupal.settings.eu_cookie_compliance.cookie_name === '') ? 'cookie-agreed-version' : Drupal.settings.eu_cookie_compliance.cookie_name + '-version'; + return $.cookie(cookieName); + }; + + Drupal.eu_cookie_compliance.setVersion = function () { + var date = new Date(); + var domain = Drupal.settings.eu_cookie_compliance.domain ? Drupal.settings.eu_cookie_compliance.domain : ''; + var path = Drupal.settings.eu_cookie_compliance.domain_all_sites ? '/' : Drupal.settings.basePath; + var cookieName = (typeof Drupal.settings.eu_cookie_compliance.cookie_name === 'undefined' || Drupal.settings.eu_cookie_compliance.cookie_name === '') ? 'cookie-agreed-version' : Drupal.settings.eu_cookie_compliance.cookie_name + '-version'; + + if (path.length > 1) { + var pathEnd = path.length - 1; + if (path.lastIndexOf('/') === pathEnd) { + path = path.substring(0, pathEnd); + } + } + var eucc_version = Drupal.settings.eu_cookie_compliance.cookie_policy_version; + var cookie_session = parseInt(Drupal.settings.eu_cookie_compliance.cookie_session); + if (cookie_session) { + $.cookie(cookieName, eucc_version, { path: path, domain: domain }); + } + else { + var lifetime = parseInt(Drupal.settings.eu_cookie_compliance.cookie_lifetime); + date.setDate(date.getDate() + lifetime); + $.cookie(cookieName, eucc_version, { expires: date, path: path, domain: domain }); + } + }; + // Load blocked scripts if the user has agreed to being tracked. var euCookieComplianceHasLoadedScripts = false; var euCookieComplianceHasLoadedScriptsForCategory = []; $(function () { if (Drupal.eu_cookie_compliance.hasAgreed() - || (Drupal.eu_cookie_compliance.getCurrentStatus() === null && Drupal.settings.eu_cookie_compliance.method !== 'opt_in' && Drupal.settings.eu_cookie_compliance.method !== 'categories') + || (_euccCurrentStatus === null && Drupal.settings.eu_cookie_compliance.method !== 'opt_in' && Drupal.settings.eu_cookie_compliance.method !== 'categories') ) { if (typeof euCookieComplianceLoadScripts === "function") { euCookieComplianceLoadScripts(); @@ -665,8 +889,7 @@ euCookieComplianceHasLoadedScripts = true; if (Drupal.settings.eu_cookie_compliance.method === 'categories') { - var acceptedCategories = Drupal.eu_cookie_compliance.getAcceptedCategories(); - Drupal.eu_cookie_compliance.loadCategoryScripts(acceptedCategories); + Drupal.eu_cookie_compliance.loadCategoryScripts(_euccSelectedCategories); } } }); @@ -677,56 +900,364 @@ attach: function (context, settings) { if (!Drupal.behaviors.eu_cookie_compliance_popup_block_cookies.initialized && settings.eu_cookie_compliance) { Drupal.behaviors.eu_cookie_compliance_popup_block_cookies.initialized = true; - if ((settings.eu_cookie_compliance.method === 'opt_in' && (Drupal.eu_cookie_compliance.getCurrentStatus() === null || !Drupal.eu_cookie_compliance.hasAgreed())) - || (settings.eu_cookie_compliance.method === 'opt_out' && !Drupal.eu_cookie_compliance.hasAgreed() && Drupal.eu_cookie_compliance.getCurrentStatus() !== null) - || (Drupal.settings.eu_cookie_compliance.method === 'categories') + if (settings.eu_cookie_compliance.automatic_cookies_removal && + ((settings.eu_cookie_compliance.method === 'opt_in' && (_euccCurrentStatus === null || !Drupal.eu_cookie_compliance.hasAgreed())) + || (settings.eu_cookie_compliance.method === 'opt_out' && !Drupal.eu_cookie_compliance.hasAgreed() && _euccCurrentStatus !== null) + || (Drupal.settings.eu_cookie_compliance.method === 'categories')) ) { - // Split the white-listed cookies. - var euCookieComplianceWhitelist = settings.eu_cookie_compliance.whitelisted_cookies.split(/\r\n|\n|\r/g); + // Split the allowed cookies. + var euCookieComplianceAllowlist = settings.eu_cookie_compliance.allowed_cookies.split(/\r\n|\n|\r/g); // Add the EU Cookie Compliance cookie. var cookieName = (typeof eu_cookie_compliance_cookie_name === 'undefined' || eu_cookie_compliance_cookie_name === '') ? 'cookie-agreed' : eu_cookie_compliance_cookie_name; - euCookieComplianceWhitelist.push(cookieName); + euCookieComplianceAllowlist.push(cookieName); - euCookieComplianceBlockCookies = setInterval(function () { - // Load all cookies from jQuery. - var cookies = $.cookie(); - - // Check each cookie and try to remove it if it's not white-listed. - for (var i in cookies) { - var remove = true; - var hostname = window.location.hostname; - var cookieRemoved = false; - var index = 0; - - remove = !Drupal.eu_cookie_compliance.isWhitelisted(i); - - // Remove the cookie if it's not white-listed. - if (remove) { - while (!cookieRemoved && hostname !== '') { - // Attempt to remove. - cookieRemoved = $.removeCookie(i, { domain: '.' + hostname, path: '/' }); - if (!cookieRemoved) { - cookieRemoved = $.removeCookie(i, { domain: hostname, path: '/' }); - } - - index = hostname.indexOf('.'); - - // We can be on a sub-domain, so keep checking the main domain as well. - hostname = (index === -1) ? '' : hostname.substring(index + 1); - } - - // Some jQuery Cookie versions don't remove cookies well. Try again - // using plain js. - if (!cookieRemoved) { - document.cookie = i + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/;'; - } - } - } - }, 5000); + euCookieComplianceBlockCookies = setInterval(Drupal.eu_cookie_compliance.BlockCookies, 5000); } } } } + Drupal.eu_cookie_compliance.BlockCookies = function () { + // Load all cookies from jQuery. + var cookies = $.cookie(); + + // Check each cookie and try to remove it if it's not allowed. + for (var i in cookies) { + var remove = true; + var hostname = window.location.hostname; + var cookieRemoved = false; + var index = 0; + + remove = !Drupal.eu_cookie_compliance.isAllowed(i); + + // Remove the cookie if it's not allowed. + if (remove) { + while (!cookieRemoved && hostname !== '') { + // Attempt to remove. + cookieRemoved = $.removeCookie(i, { domain: '.' + hostname, path: '/' }); + if (!cookieRemoved) { + cookieRemoved = $.removeCookie(i, { domain: hostname, path: '/' }); + } + + index = hostname.indexOf('.'); + + // We can be on a sub-domain, so keep checking the main domain as well. + hostname = (index === -1) ? '' : hostname.substring(index + 1); + } + + // Some jQuery Cookie versions don't remove cookies well. Try again + // using plain js. + if (!cookieRemoved) { + document.cookie = i + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/;'; + } + } + } + } + + /** + * Filter the event listeners by event name and return the list of handlers. + * + * @param forEventName + * + * @returns {[]} + */ + var filterQueue = function (forEventName) { + var handlers = []; + if (typeof Drupal.eu_cookie_compliance !== 'undefined' && + typeof Drupal.eu_cookie_compliance.queue !== 'undefined' && + Drupal.eu_cookie_compliance.queue.length) { + // Loop over the list of arguments (objects) pushed into the queue. + for (var i = 0; i < Drupal.eu_cookie_compliance.queue.length; i++) { + if (Drupal.eu_cookie_compliance.queue[i].length) { + var queueItem = Drupal.eu_cookie_compliance.queue[i]; + var eventName = queueItem[0]; + var eventHandler = queueItem[1]; + // If the first element is a string and the second is a function. + if (typeof eventName === 'string' && typeof eventHandler === 'function') { + // And the string matches the event name. + if (eventName === forEventName) { + // Return the functions so they can be executed. + handlers.push(eventHandler); + } + } + } + } + } + return handlers; + } + + /** + * Handle event by finding and executing handlers pushed to the queue. + */ + self.handleEvent = function (eventName, observer) { + var handlers = filterQueue(eventName); + for (var i = 0; i < handlers.length; i++) { + if (typeof handlers[i] !== 'undefined') { + observer.subscribe(handlers[i]); + observer.fire({ + currentStatus: _euccCurrentStatus, + currentCategories: _euccSelectedCategories + }); + observer.unsubscribe(handlers[i]); + } + } + }; + + /** + * Observer: triggered before status gets read from cookie. + */ + var PreStatusLoad = (function () { + // Constructor. + var PreStatusLoad = function () { + // Observers. + this.handlers = []; + }; + // Convenience var for the prototype. + var prototype = PreStatusLoad.prototype; + prototype.subscribe = function (fn) { + this.handlers.push(fn); + }; + prototype.unsubscribe = function (fn) { + this.handlers = this.handlers.filter( + function (item) { + if (item !== fn) { + return item; + } + } + ); + }; + prototype.fire = function (o, thisObj) { + var scope = thisObj || window; + this.handlers.forEach(function (item) { + item.call(scope, o); + }); + }; + return PreStatusLoad; + })(); + + /** + * Observer: triggered after status was read from cookie and stored in private variable. + */ + var PostStatusLoad = (function () { + // Constructor. + var PostStatusLoad = function () { + // Observers. + this.handlers = []; + }; + // Convenience var for the prototype. + var prototype = PostStatusLoad.prototype; + prototype.subscribe = function (fn) { + this.handlers.push(fn); + }; + prototype.unsubscribe = function (fn) { + this.handlers = this.handlers.filter( + function (item) { + if (item !== fn) { + return item; + } + } + ); + }; + prototype.fire = function (o, thisObj) { + var scope = thisObj || window; + this.handlers.forEach(function (item) { + item.call(scope, o); + }); + }; + return PostStatusLoad; + })(); + + /** + * Observer: triggered before status gets saved into cookie. + */ + var PreStatusSave = (function () { + // Constructor. + var PreStatusSave = function () { + // Observers. + this.handlers = []; + }; + // Convenience var for the prototype. + var prototype = PreStatusSave.prototype; + prototype.subscribe = function (fn) { + this.handlers.push(fn); + }; + prototype.unsubscribe = function (fn) { + this.handlers = this.handlers.filter( + function (item) { + if (item !== fn) { + return item; + } + } + ); + }; + prototype.fire = function (o, thisObj) { + var scope = thisObj || window; + this.handlers.forEach(function (item) { + item.call(scope, o); + }); + }; + return PreStatusSave; + })(); + + /** + * Observer: triggered after status was saved into cookie. + */ + var PostStatusSave = (function () { + // Constructor. + var PostStatusSave = function () { + // Observers. + this.handlers = []; + }; + // Convenience var for the prototype. + var prototype = PostStatusSave.prototype; + prototype.subscribe = function (fn) { + this.handlers.push(fn); + }; + prototype.unsubscribe = function (fn) { + this.handlers = this.handlers.filter( + function (item) { + if (item !== fn) { + return item; + } + } + ); + }; + prototype.fire = function (o, thisObj) { + var scope = thisObj || window; + this.handlers.forEach(function (item) { + item.call(scope, o); + }); + }; + return PostStatusSave; + })(); + + /** + * Observer: triggered before categories are read from cookie. + */ + var PrePreferencesLoad = (function () { + // Constructor. + var prePreferencesLoad = function () { + // Observers. + this.handlers = []; + }; + // Convenience var for the prototype. + var prototype = prePreferencesLoad.prototype; + prototype.subscribe = function (fn) { + this.handlers.push(fn); + }; + prototype.unsubscribe = function (fn) { + this.handlers = this.handlers.filter( + function (item) { + if (item !== fn) { + return item; + } + } + ); + }; + prototype.fire = function (o, thisObj) { + var scope = thisObj || window; + this.handlers.forEach(function (item) { + item.call(scope, o); + }); + }; + return prePreferencesLoad; + })(); + + /** + * Observer: triggered after categories were read from cookie. + */ + var PostPreferencesLoad = (function () { + // Constructor. + var postPreferencesLoad = function () { + // Observers. + this.handlers = []; + }; + // Convenience var for the prototype. + var prototype = postPreferencesLoad.prototype; + prototype.subscribe = function (fn) { + this.handlers.push(fn); + }; + prototype.unsubscribe = function (fn) { + this.handlers = this.handlers.filter( + function (item) { + if (item !== fn) { + return item; + } + } + ); + }; + prototype.fire = function (o, thisObj) { + var scope = thisObj || window; + this.handlers.forEach(function (item) { + item.call(scope, o); + }); + }; + return postPreferencesLoad; + })(); + + /** + * Observer: triggered before categories are being saved to cookie. + */ + var PrePreferencesSave = (function () { + // Constructor. + var prePreferencesSave = function () { + // Observers. + this.handlers = []; + }; + // Convenience var for the prototype. + var prototype = prePreferencesSave.prototype; + prototype.subscribe = function (fn) { + this.handlers.push(fn); + }; + prototype.unsubscribe = function (fn) { + this.handlers = this.handlers.filter( + function (item) { + if (item !== fn) { + return item; + } + } + ); + }; + prototype.fire = function (o, thisObj) { + var scope = thisObj || window; + this.handlers.forEach(function (item) { + item.call(scope, o); + }); + }; + return prePreferencesSave; + })(); + + /** + * Observer: triggered after categories were saved to cookie. + */ + var PostPreferencesSave = (function () { + // Constructor. + var postPreferencesSave = function () { + // Observers. + this.handlers = []; + }; + // Convenience var for the prototype. + var prototype = postPreferencesSave.prototype; + prototype.subscribe = function (fn) { + this.handlers.push(fn); + }; + prototype.unsubscribe = function (fn) { + this.handlers = this.handlers.filter( + function (item) { + if (item !== fn) { + return item; + } + } + ); + }; + prototype.fire = function (o, thisObj) { + var scope = thisObj || window; + this.handlers.forEach(function (item) { + item.call(scope, o); + }); + }; + return postPreferencesSave; + })(); + })(jQuery); diff --git a/frontend/drupal/sites/all/modules/eu_cookie_compliance/js/eu_cookie_compliance_admin.js b/frontend/drupal/sites/all/modules/eu_cookie_compliance/js/eu_cookie_compliance_admin.js new file mode 100644 index 000000000..0249aa2d4 --- /dev/null +++ b/frontend/drupal/sites/all/modules/eu_cookie_compliance/js/eu_cookie_compliance_admin.js @@ -0,0 +1,39 @@ +/** + * @file + * EU Cookie Compliance admin script. + */ + +(function ($) { + function showHideThankYouFields(showHide) { + if (showHide) { + $('.form-item-eu-cookie-compliance-popup-find-more-button-message, .form-item-eu-cookie-compliance-popup-hide-button-message, .form-item-eu-cookie-compliance-popup-hide-agreed').show(); + $('.form-item-eu-cookie-compliance-popup-agreed-value').parent().show(); + + $('#edit-eu-cookie-compliance-popup-agreed-value').prop('required','required'); + $('#edit-eu-cookie-compliance-popup-find-more-button-message').prop('required','required'); + $('#edit-eu-cookie-compliance-popup-hide-button-message').prop('required','required'); + + $('.form-item-eu-cookie-compliance-popup-agreed-value label, .form-item-eu-cookie-compliance-popup-find-more-button-message label, .form-item-eu-cookie-compliance-popup-hide-button-message label').append('*'); + } + else { + $('.form-item-eu-cookie-compliance-popup-find-more-button-message, .form-item-eu-cookie-compliance-popup-hide-button-message, .form-item-eu-cookie-compliance-popup-hide-agreed').hide(); + $('.form-item-eu-cookie-compliance-popup-agreed-value').parent().hide(); + + $('#edit-eu-cookie-compliance-popup-agreed-value').prop('required',''); + $('#edit-eu-cookie-compliance-popup-find-more-button-message').prop('required',''); + $('#edit-eu-cookie-compliance-popup-hide-button-message').prop('required',''); + + $('.form-item-eu-cookie-compliance-popup-agreed-value label span, .form-item-eu-cookie-compliance-popup-find-more-button-message label span, .form-item-eu-cookie-compliance-popup-hide-button-message label span').remove(); + } + } + + $(function () { + showHideThankYouFields($('#edit-eu-cookie-compliance-popup-agreed-enabled').prop('checked')); + + $('#edit-eu-cookie-compliance-popup-agreed-enabled').click(function () { + showHideThankYouFields($(this).prop('checked')); + }); + + }); + +} (jQuery)) diff --git a/frontend/drupal/sites/all/modules/eu_cookie_compliance/theme/eu-cookie-compliance-popup-info.tpl.php b/frontend/drupal/sites/all/modules/eu_cookie_compliance/theme/eu-cookie-compliance-popup-info.tpl.php index 75f083e06..39f64cf11 100644 --- a/frontend/drupal/sites/all/modules/eu_cookie_compliance/theme/eu-cookie-compliance-popup-info.tpl.php +++ b/frontend/drupal/sites/all/modules/eu_cookie_compliance/theme/eu-cookie-compliance-popup-info.tpl.php @@ -27,7 +27,6 @@ * agreed or disagreed to separately. * - $save_preferences_button_label: Label text for a button to save the consent * preferences. - * - $fix_first_cookie_category: Boolean value to indicate that the first * consent category cannot be unchecked. * - $privacy_settings_tab_label: Label text for the Privacy settings tab. * - $withdraw_button_on_info_popup: Show the withdraw button on this popup. @@ -52,23 +51,20 @@