From 595698730af6296fe37235a865d37a367e816c85 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Mon, 17 Aug 2026 11:26:30 -0700 Subject: [PATCH 1/2] Improve language ans link to Privacy Policy --- language/en/common.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language/en/common.php b/language/en/common.php index 84e0eb1..6494019 100644 --- a/language/en/common.php +++ b/language/en/common.php @@ -30,7 +30,7 @@ 'CONSENTMANAGER_DEFAULT_BANNER_TITLE' => 'We value your privacy', 'CONSENTMANAGER_DEFAULT_BANNER_TEXT' => 'This forum uses cookies to keep you signed in, secure your account, and ensure the site works properly. With your consent, we may also use optional cookies and similar technologies for analytics, marketing, and embedded media.', 'CONSENTMANAGER_DEFAULT_BANNER_SUBTEXT' => 'You can change your preferences at any time in the Privacy Settings.', - 'CONSENTMANAGER_PRIVACY_POLICY_LINK' => 'Read our %s here.', + 'CONSENTMANAGER_PRIVACY_POLICY_LINK' => 'See our %s for more information.', 'CONSENTMANAGER_CATEGORY_NECESSARY' => 'Necessary', 'CONSENTMANAGER_CATEGORY_NECESSARY_EXPLAIN' => 'Required for forum security, authentication, and essential site functionality.', 'CONSENTMANAGER_CATEGORY_ANALYTICS' => 'Analytics', From a94537e8cd7ca9320fc4d7a0ba77b00fe097989f Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Mon, 17 Aug 2026 11:26:57 -0700 Subject: [PATCH 2/2] Delete cookies should delete consent managers cookies too --- event/listener.php | 11 +++++ service/consent_manager.php | 36 +++++++++++++--- .../event/overall_header_head_append.html | 11 +++++ styles/all/template/js/consentmanager.js | 41 ++++++++++++++++++- tests/event/listener_test.php | 11 +++++ tests/functional/frontend_test.php | 3 +- tests/javascript/consentmanager.test.js | 35 +++++++++++++++- tests/service/consent_manager_test.php | 22 ++++++++-- 8 files changed, 158 insertions(+), 12 deletions(-) diff --git a/event/listener.php b/event/listener.php index 714ee22..ba95aae 100644 --- a/event/listener.php +++ b/event/listener.php @@ -63,9 +63,20 @@ public static function getSubscribedEvents() 'core.text_formatter_s9e_configure_after' => [['configure_iframe_embeds', -10]], 'core.text_formatter_s9e_renderer_setup' => 'configure_iframe_renderer', 'core.page_header_after' => 'inject_frontend', + 'core.ucp_delete_cookies' => 'clear_browser_storage', ]; } + /** + * Mark Consent Manager browser storage for deletion after confirmation. + * + * @return void + */ + public function clear_browser_storage() + { + $this->template->assign_var('S_CONSENTMANAGER_CLEAR_STORAGE', true); + } + /** * Transform s9e-rendered iframe output into consent-aware placeholders. * diff --git a/service/consent_manager.php b/service/consent_manager.php index f181242..2b6e806 100644 --- a/service/consent_manager.php +++ b/service/consent_manager.php @@ -23,8 +23,8 @@ class consent_manager implements consent_manager_interface { - public const STORAGE_KEY = 'phpbb_consent_manager'; - public const COOKIE_NAME = 'phpbb_consent_manager'; + public const STORAGE_KEY = 'consent_manager'; + public const COOKIE_NAME = 'consent_manager'; /** @var consent_cache */ protected $consent_cache; @@ -320,6 +320,8 @@ public function build_frontend_payload($log_url, $log_hash) return [ 'storageKey' => $this->get_storage_key(), 'cookieName' => $this->get_cookie_name(), + 'cookiePath' => $this->config['cookie_path'], + 'cookieDomain' => $this->get_cookie_domain(), 'version' => $this->get_version(), 'requiredCategories' => $this->get_required_category_ids($categories), 'enabledCategories' => $this->get_enabled_category_ids($categories), @@ -548,7 +550,7 @@ public function normalize_categories(array $categories) */ public function get_storage_key() { - return self::STORAGE_KEY; + return $this->get_prefixed_storage_name(self::STORAGE_KEY); } /** @@ -558,7 +560,7 @@ public function get_storage_key() */ public function get_cookie_name() { - return self::COOKIE_NAME; + return $this->get_prefixed_storage_name(self::COOKIE_NAME); } /** @@ -651,7 +653,7 @@ protected function get_server_consent_state() $this->server_consent_state_loaded = true; $this->server_consent_state = null; - $raw = $this->request->raw_variable(self::COOKIE_NAME, '', request_interface::COOKIE); + $raw = $this->request->raw_variable($this->get_cookie_name(), '', request_interface::COOKIE); if (!is_string($raw) || $raw === '') { return null; @@ -674,6 +676,30 @@ protected function get_server_consent_state() return $this->server_consent_state; } + /** + * Prefix browser storage names consistently with phpBB cookies. + * + * @param string $name Unprefixed storage name + * + * @return string + */ + protected function get_prefixed_storage_name($name) + { + return $this->config['cookie_name'] . '_' . $name; + } + + /** + * Return the cookie domain phpBB will use when deleting board cookies. + * + * @return string + */ + protected function get_cookie_domain() + { + $domain = $this->config['cookie_domain']; + + return (!$domain || $domain === '127.0.0.1' || strpos($domain, '.') === false) ? '' : $domain; + } + /** * Allow extensions to register consent-aware integrations. * diff --git a/styles/all/template/event/overall_header_head_append.html b/styles/all/template/event/overall_header_head_append.html index a0f21e8..03b29be 100644 --- a/styles/all/template/event/overall_header_head_append.html +++ b/styles/all/template/event/overall_header_head_append.html @@ -9,6 +9,7 @@ const googleConsentTypes = googleConsentMode.types || {}; window.phpbbConsentManagerPayload = payload; + window.phpbbConsentManagerDeleteCookiesUrl = '{{ U_DELETE_COOKIES|e('js') }}'; window.phpbbConsentManagerLang = { mediaPlaceholderLabel: '{{ lang('CONSENTMANAGER_MEDIA_PLACEHOLDER')|e('js') }}' }; @@ -44,6 +45,16 @@ return ''; } + {% if S_CONSENTMANAGER_CLEAR_STORAGE %} + try + { + window.localStorage.removeItem(payload.storageKey); + } + catch (error) + { + } + {% endif %} + function loadState() { let raw = ''; diff --git a/styles/all/template/js/consentmanager.js b/styles/all/template/js/consentmanager.js index e533934..8cd4520 100644 --- a/styles/all/template/js/consentmanager.js +++ b/styles/all/template/js/consentmanager.js @@ -100,7 +100,12 @@ function setCookie(name, value, maxAge) { - let cookie = name + '=' + encodeURIComponent(value) + '; path=/; SameSite=Lax'; + let cookie = name + '=' + encodeURIComponent(value) + '; path=' + payload.cookiePath + '; SameSite=Lax'; + + if (payload.cookieDomain) + { + cookie += '; domain=' + payload.cookieDomain; + } if (window.location.protocol === 'https:') { @@ -275,6 +280,39 @@ return state; } + function sameRequestUrl(left, right) + { + if (!left || !right) + { + return false; + } + + const leftLink = document.createElement('a'); + const rightLink = document.createElement('a'); + leftLink.href = left; + rightLink.href = right; + + return leftLink.pathname === rightLink.pathname; + } + + function bindDeleteCookiesCleanup() + { + if (!window.jQuery || !window.phpbbConsentManagerDeleteCookiesUrl) + { + return; + } + + window.jQuery(document).ajaxSuccess(function(event, request, settings, response) { + if (sameRequestUrl(settings.url, window.phpbbConsentManagerDeleteCookiesUrl) + && response + && typeof response.S_CONFIRM_ACTION === 'undefined' + && response.REFRESH_DATA) + { + removeStoredState(); + } + }); + } + function unique(items) { const deduplicated = []; @@ -1190,6 +1228,7 @@ }; window.consentManager = api; + bindDeleteCookiesCleanup(); applyGoogleConsentMode(); diff --git a/tests/event/listener_test.php b/tests/event/listener_test.php index 38aebe5..c06e915 100644 --- a/tests/event/listener_test.php +++ b/tests/event/listener_test.php @@ -51,9 +51,20 @@ public function test_get_subscribed_events() 'core.text_formatter_s9e_configure_after' => [['configure_iframe_embeds', -10]], 'core.text_formatter_s9e_renderer_setup' => 'configure_iframe_renderer', 'core.page_header_after' => 'inject_frontend', + 'core.ucp_delete_cookies' => 'clear_browser_storage', ], \phpbb\consentmanager\event\listener::getSubscribedEvents()); } + public function test_clear_browser_storage_assigns_template_flag() + { + $template = $this->createMock('\phpbb\template\template'); + $template->expects(self::once()) + ->method('assign_var') + ->with('S_CONSENTMANAGER_CLEAR_STORAGE', true); + + $this->create_listener(null, null, $template)->clear_browser_storage(); + } + public function test_configure_iframe_embeds_delegates_to_media_manager() { $configurator = new \s9e\TextFormatter\Configurator(); diff --git a/tests/functional/frontend_test.php b/tests/functional/frontend_test.php index cdb10e7..e32ed4f 100644 --- a/tests/functional/frontend_test.php +++ b/tests/functional/frontend_test.php @@ -34,7 +34,8 @@ public function test_frontend_markup_is_injected_on_board_pages() $this->assertContainsLang('CONSENTMANAGER_SETTINGS_TITLE', $crawler->filter('#consent-manager-link')->text()); $this->assertSame(2, $crawler->filter('.consent-manager-policy-link')->count()); $this->assertSame(1, $payload['version']); - $this->assertSame('phpbb_consent_manager', $payload['storageKey']); + $this->assertStringEndsWith('_consent_manager', $payload['storageKey']); + $this->assertSame($payload['storageKey'], $payload['cookieName']); $this->assertSame($this->lang('CONSENTMANAGER_MEDIA_PLACEHOLDER'), $this->extract_media_placeholder_label($content)); $this->assertSame(array('necessary'), $payload['requiredCategories']); $this->assertContains('analytics', $payload['optionalCategories']); diff --git a/tests/javascript/consentmanager.test.js b/tests/javascript/consentmanager.test.js index 3b52951..68b76df 100644 --- a/tests/javascript/consentmanager.test.js +++ b/tests/javascript/consentmanager.test.js @@ -13,6 +13,8 @@ function createPayload(overrides) { version: '2026-04-28', storageKey: 'phpbb-consent-state', cookieName: 'phpbb_consent_state', + cookiePath: '/', + cookieDomain: '', logEndpoint: '/app.php/consent/log', logHash: 'test-hash', categories: [ @@ -114,6 +116,7 @@ function setupConsentManager(options) { const { window } = dom; const requests = []; const gtagCalls = settings.gtagCalls || []; + let ajaxSuccessCallback = null; Object.defineProperty(window.document, 'readyState', { configurable: true, @@ -175,6 +178,17 @@ function setupConsentManager(options) { window.document.cookie = payload.cookieName + '=' + encodeURIComponent(JSON.stringify(settings.cookieState)); } + if (settings.withJquery) { + window.jQuery = function() { + return { + ajaxSuccess: function(callback) { + ajaxSuccessCallback = callback; + } + }; + }; + window.phpbbConsentManagerDeleteCookiesUrl = '/delete_cookies'; + } + if (!settings.withoutPayload) { window.phpbbConsentManagerPayload = payload; } @@ -192,7 +206,10 @@ function setupConsentManager(options) { payload, requests, gtagCalls, - jsdomErrors + jsdomErrors, + triggerAjaxSuccess: function(url, response) { + ajaxSuccessCallback({}, {}, { url }, response); + } }; } @@ -209,6 +226,22 @@ test('exits cleanly when payload is missing', () => { expect(jsdomErrors).toEqual([]); }); +test('successful AJAX cookie deletion clears consent browser storage', () => { + const storedState = createState([ 'necessary', 'analytics' ], '2026-04-28T00:00:00.000Z'); + const { window, document, payload, triggerAjaxSuccess } = setupConsentManager({ + localState: storedState, + withJquery: true + }); + + triggerAjaxSuccess('/delete_cookies?sid=changed-by-confirmation', { + REFRESH_DATA: { url: '/', time: 3 } + }); + + expect(window.localStorage.getItem(payload.storageKey)).toBeNull(); + expect(getCookieValue(document, payload.cookieName)).toBe(''); + expect(window.consentManager.getState()).toEqual(storedState); +}); + test('prefers the newest stored state and synchronizes cookie and local storage', () => { const localState = createState([ 'necessary', 'marketing' ], '2026-04-27T00:00:00.000Z'); const cookieState = createState([ 'necessary', 'analytics' ], '2026-04-28T00:00:00.000Z'); diff --git a/tests/service/consent_manager_test.php b/tests/service/consent_manager_test.php index b282ff5..df812b7 100644 --- a/tests/service/consent_manager_test.php +++ b/tests/service/consent_manager_test.php @@ -67,14 +67,17 @@ protected function setUp(): void public function test_public_metadata_methods() { $manager = $this->get_manager(array( + 'cookie_name' => 'phpbb3_test', + 'cookie_path' => '/forum', + 'cookie_domain' => '.example.com', 'consentmanager_analytics_enabled' => 1, 'consentmanager_marketing_enabled' => 0, 'consentmanager_media_enabled' => 1, 'consentmanager_consent_version' => 7, )); - self::assertSame('phpbb_consent_manager', $manager->get_storage_key()); - self::assertSame('phpbb_consent_manager', $manager->get_cookie_name()); + self::assertSame('phpbb3_test_consent_manager', $manager->get_storage_key()); + self::assertSame('phpbb3_test_consent_manager', $manager->get_cookie_name()); self::assertSame(7, $manager->get_version()); self::assertTrue($manager->is_supported_category('analytics')); self::assertTrue($manager->is_supported_category('media')); @@ -605,12 +608,19 @@ public function test_build_frontend_payload_collects_registered_and_configured_i }); $manager = $this->get_manager(array( + 'cookie_name' => 'phpbb3_payload', + 'cookie_path' => '/board', + 'cookie_domain' => '.example.com', 'consentmanager_marketing_enabled' => 0, 'consentmanager_consent_version' => 7, ), $this->get_submitted_integrations_json(), $dispatcher); $payload = $manager->build_frontend_payload('/app.php/consent/log', 'deadbeef'); + self::assertSame('phpbb3_payload_consent_manager', $payload['storageKey']); + self::assertSame('phpbb3_payload_consent_manager', $payload['cookieName']); + self::assertSame('/board', $payload['cookiePath']); + self::assertSame('.example.com', $payload['cookieDomain']); self::assertSame(array('necessary'), $payload['requiredCategories']); self::assertSame(array('necessary', 'analytics', 'media'), $payload['enabledCategories']); self::assertSame(array('analytics', 'media'), $payload['optionalCategories']); @@ -967,7 +977,7 @@ public function test_has_server_consent_returns_true_for_required_category_and_f public function test_has_server_consent_reuses_cached_cookie_state() { $raw_variable_args = [ - \phpbb\consentmanager\service\consent_manager::COOKIE_NAME, + 'phpbb3_test_consent_manager', '', \phpbb\request\request_interface::COOKIE, ]; @@ -981,6 +991,7 @@ public function test_has_server_consent_reuses_cached_cookie_state() ])); $manager = $this->get_manager([ + 'cookie_name' => 'phpbb3_test', 'consentmanager_consent_version' => 3, ], '', null, null, null, $request); @@ -1131,6 +1142,9 @@ protected function get_manager_constructor_args(array $config_values = array(), } $config = new \phpbb\config\config(array_merge(array( + 'cookie_name' => 'phpbb3_test', + 'cookie_path' => '/', + 'cookie_domain' => '', 'consentmanager_analytics_enabled' => 1, 'consentmanager_marketing_enabled' => 1, 'consentmanager_media_enabled' => 1, @@ -1203,7 +1217,7 @@ protected function get_cookie_request($cookie_value) $request = $this->createMock('\phpbb\request\request_interface'); $request->method('raw_variable') ->willReturnCallback(function ($name, $default, $super_global = null) use ($cookie_value) { - if ($name === \phpbb\consentmanager\service\consent_manager::COOKIE_NAME && $super_global === \phpbb\request\request_interface::COOKIE) + if ($name === 'phpbb3_test_consent_manager' && $super_global === \phpbb\request\request_interface::COOKIE) { return $cookie_value; }