From dd915682ada6754ffa85af97d5217877eae1eddf Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Wed, 10 Dec 2025 09:01:25 -0800 Subject: [PATCH 1/9] Add notification subscribe popup prompt --- acp/wpn_acp_module.php | 2 + adm/style/wpn_acp_settings.html | 5 + config/wpn_ucp.yml | 4 + .../en/webpushnotifications_module_acp.php | 2 + .../en/webpushnotifications_module_ucp.php | 4 + migrations/add_popup_prompt.php | 62 ++++++++++++ notification/method/webpush.php | 2 + .../event/overall_header_head_append.html | 1 + .../template/ucp_notifications_webpush.html | 1 + styles/all/template/webpush.js | 53 +++++++++++ styles/all/template/webpush_popup.html | 14 +++ styles/all/theme/phpbb_wpn.css | 95 +++++++++++++++++++ tests/controller/controller_webpush_test.php | 56 +++++++++++ tests/controller/fixtures/webpush.xml | 4 + tests/functional/functional_test.php | 28 ++++++ ucp/controller/webpush.php | 22 +++++ 16 files changed, 355 insertions(+) create mode 100644 migrations/add_popup_prompt.php create mode 100644 styles/all/template/webpush_popup.html diff --git a/acp/wpn_acp_module.php b/acp/wpn_acp_module.php index dbad44e..4015c4b 100644 --- a/acp/wpn_acp_module.php +++ b/acp/wpn_acp_module.php @@ -108,6 +108,7 @@ public function display_settings() 'WEBPUSH_VAPID_PRIVATE' => $this->config['wpn_webpush_vapid_private'] ? self::MASKED_PRIVATE_KEY : '', 'S_WEBPUSH_DROPDOWN_SUBSCRIBE' => $this->config['wpn_webpush_dropdown_subscribe'], 'S_WEBPUSH_METHOD_ENABLED' => $this->config['wpn_webpush_method_enabled'], + 'S_WEBPUSH_POPUP_PROMPT' => $this->config['wpn_webpush_popup_prompt'], 'U_ACTION' => $this->u_action, ]); @@ -133,6 +134,7 @@ public function save_settings() 'wpn_webpush_vapid_private'=> ['validate' => 'string:25:255', 'lang' => 'WEBPUSH_VAPID_PRIVATE'], 'wpn_webpush_dropdown_subscribe' => ['validate' => 'bool'], 'wpn_webpush_method_enabled' => ['validate' => 'bool'], + 'wpn_webpush_popup_prompt' => ['validate' => 'bool'], ]; // Do not validate and update private key field if the content is ******** and the key was already set diff --git a/adm/style/wpn_acp_settings.html b/adm/style/wpn_acp_settings.html index cdd3add..f1fa5af 100644 --- a/adm/style/wpn_acp_settings.html +++ b/adm/style/wpn_acp_settings.html @@ -39,6 +39,11 @@

{{ lang('WARNING') }}

+
+

{{ lang('WEBPUSH_POPUP_PROMPT_EXPLAIN') }}
+
+
+
  diff --git a/config/wpn_ucp.yml b/config/wpn_ucp.yml index f340196..235efc1 100644 --- a/config/wpn_ucp.yml +++ b/config/wpn_ucp.yml @@ -13,3 +13,7 @@ phpbb_webpushnotifications_ucp_push_subscribe_controller: phpbb_webpushnotifications_ucp_push_unsubscribe_controller: path: /push/unsubscribe defaults: { _controller: phpbb.wpn.ucp.controller.webpush:unsubscribe } + +phpbb_webpushnotifications_ucp_push_decline_popup_controller: + path: /push/decline-popup + defaults: { _controller: phpbb.wpn.ucp.controller.webpush:decline_popup } diff --git a/language/en/webpushnotifications_module_acp.php b/language/en/webpushnotifications_module_acp.php index 1da78c4..bf4edbe 100644 --- a/language/en/webpushnotifications_module_acp.php +++ b/language/en/webpushnotifications_module_acp.php @@ -50,5 +50,7 @@ 'WEBPUSH_METHOD_ENABLED_EXPLAIN'=> 'When this setting is enabled, users who have also enabled and allowed browser notifications will start receiving them automatically. They can visit the UCP Notification settings to disable any unwanted notifications.

If this setting is disabled, users will not receive any notifications, even if they have enabled push notifications, until they visit the UCP Notification settings to allow the specific notifications they wish to receive.', 'WEBPUSH_DROPDOWN_SUBSCRIBE' => 'Show web push settings in the notification dropdown', 'WEBPUSH_DROPDOWN_SUBSCRIBE_EXPLAIN'=> 'Show or hide the “Enable Web Push” toggle switch in the notification dropdown. This allows users to easily enable or disable push notifications from any page of the forum.', + 'WEBPUSH_POPUP_PROMPT' => 'Show popup prompt for unsubscribed members', + 'WEBPUSH_POPUP_PROMPT_EXPLAIN' => 'Display a popup message asking registered members if they want to receive push notifications. The popup will only appear to members who are not currently subscribed and have not previously declined.', 'WEBPUSH_INSECURE_SERVER_ERROR' => 'This board is not using a secure SSL/HTTPS protocol, which is required for enabling web push notifications. Alternatively, the server environment might be misconfigured. Ensure that the HTTPS and HEADER_CLIENT_PROTO server environment variables are correctly configured.', ]); diff --git a/language/en/webpushnotifications_module_ucp.php b/language/en/webpushnotifications_module_ucp.php index 9b7223f..6e8e183 100644 --- a/language/en/webpushnotifications_module_ucp.php +++ b/language/en/webpushnotifications_module_ucp.php @@ -49,4 +49,8 @@ 'NOTIFY_WEBPUSH_DROPDOWN_TITLE' => 'Visit notifications settings to set your preferred push notifications.', 'NOTIFY_WEBPUSH_DENIED' => 'You have denied notifications from this site. To enable push notifications, allow notifications from this site in your browser settings.', 'NOTIFY_WEBPUSH_DISABLED' => 'Push notifications not supported', + 'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Enable Push Notifications?', + 'NOTIFY_WEBPUSH_POPUP_MESSAGE' => 'Stay updated with instant notifications for new replies, mentions, and more.', + 'NOTIFY_WEBPUSH_POPUP_ALLOW' => 'Allow', + 'NOTIFY_WEBPUSH_POPUP_DECLINE' => 'Decline', ]); diff --git a/migrations/add_popup_prompt.php b/migrations/add_popup_prompt.php new file mode 100644 index 0000000..ad8b8e4 --- /dev/null +++ b/migrations/add_popup_prompt.php @@ -0,0 +1,62 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + */ + +namespace phpbb\webpushnotifications\migrations; + +use phpbb\db\migration\migration; + +class add_popup_prompt extends migration +{ + public function effectively_installed(): bool + { + return $this->config->offsetExists('wpn_webpush_popup_prompt'); + } + + public static function depends_on() + { + return ['\phpbb\webpushnotifications\migrations\add_acp_configs']; + } + + public function update_schema(): array + { + return [ + 'add_columns' => [ + $this->table_prefix . 'users' => [ + 'user_wpn_popup_declined' => ['BOOL', 0], + ], + ], + ]; + } + + public function revert_schema(): array + { + return [ + 'drop_columns' => [ + $this->table_prefix . 'users' => [ + 'user_wpn_popup_declined', + ], + ], + ]; + } + + public function update_data(): array + { + return [ + ['config.add', ['wpn_webpush_popup_prompt', false]], + ]; + } + + public function revert_data(): array + { + return [ + ['config.remove', ['wpn_webpush_popup_prompt']], + ]; + } +} diff --git a/notification/method/webpush.php b/notification/method/webpush.php index c1756c4..01f641f 100644 --- a/notification/method/webpush.php +++ b/notification/method/webpush.php @@ -396,10 +396,12 @@ public function get_ucp_template_data(helper $controller_helper, form_helper $fo 'NOTIFICATIONS_WEBPUSH_ENABLE' => ($this->config['load_notifications'] && $this->config['allow_board_notifications'] && $this->config['wpn_webpush_dropdown_subscribe']) || stripos($this->user->page['page'], 'notification_options'), 'U_WEBPUSH_SUBSCRIBE' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_subscribe_controller'), 'U_WEBPUSH_UNSUBSCRIBE' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_unsubscribe_controller'), + 'U_WEBPUSH_DECLINE_POPUP' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_decline_popup_controller'), 'VAPID_PUBLIC_KEY' => $this->config['wpn_webpush_vapid_public'], 'U_WEBPUSH_WORKER_URL' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_worker_controller'), 'SUBSCRIPTIONS' => $subscriptions, 'WEBPUSH_FORM_TOKENS' => $form_helper->get_form_tokens(\phpbb\webpushnotifications\ucp\controller\webpush::FORM_TOKEN_UCP), + 'S_WEBPUSH_POPUP_PROMPT' => $this->config['wpn_webpush_popup_prompt'] && empty($subscriptions) && !$this->user->data['user_wpn_popup_declined'] && $this->user->id() != ANONYMOUS && $this->user->data['user_type'] != USER_IGNORE, ]; } diff --git a/styles/all/template/event/overall_header_head_append.html b/styles/all/template/event/overall_header_head_append.html index 00c8172..5e26b0b 100644 --- a/styles/all/template/event/overall_header_head_append.html +++ b/styles/all/template/event/overall_header_head_append.html @@ -7,4 +7,5 @@ {% if NOTIFICATIONS_WEBPUSH_ENABLE %} {% include '@phpbb_webpushnotifications/ucp_notifications_webpush.html' %} + {% include '@phpbb_webpushnotifications/webpush_popup.html' %} {% endif %} diff --git a/styles/all/template/ucp_notifications_webpush.html b/styles/all/template/ucp_notifications_webpush.html index 16ebf96..9836618 100644 --- a/styles/all/template/ucp_notifications_webpush.html +++ b/styles/all/template/ucp_notifications_webpush.html @@ -3,6 +3,7 @@ serviceWorkerUrl: '{{ U_WEBPUSH_WORKER_URL }}', subscribeUrl: '{{ U_WEBPUSH_SUBSCRIBE }}', unsubscribeUrl: '{{ U_WEBPUSH_UNSUBSCRIBE }}', + declinePopupUrl: '{{ U_WEBPUSH_DECLINE_POPUP }}', ajaxErrorTitle: '{{ lang('AJAX_ERROR_TITLE') }}', vapidPublicKey: '{{ VAPID_PUBLIC_KEY }}', formTokens: { diff --git a/styles/all/template/webpush.js b/styles/all/template/webpush.js index 685f066..daf3107 100644 --- a/styles/all/template/webpush.js +++ b/styles/all/template/webpush.js @@ -33,6 +33,9 @@ function PhpbbWebpush() { /** @type {HTMLElement} Unsubscribe button */ let unsubscribeButton; + /** @type {string} URL to decline popup */ + let declinePopupUrl = ''; + /** * Init function for phpBB Web Push * @type {array} options @@ -41,6 +44,7 @@ function PhpbbWebpush() { serviceWorkerUrl = options.serviceWorkerUrl; subscribeUrl = options.subscribeUrl; unsubscribeUrl = options.unsubscribeUrl; + declinePopupUrl = options.declinePopupUrl; this.formTokens = options.formTokens; subscriptions = options.subscriptions; ajaxErrorTitle = options.ajaxErrorTitle; @@ -62,6 +66,7 @@ function PhpbbWebpush() { unsubscribeButton.addEventListener('click', unsubscribeButtonHandler); updateButtonState(); + initPopupPrompt(); }) .catch(error => { console.info(error); @@ -116,6 +121,50 @@ function PhpbbWebpush() { } } + /** + * Initialize popup prompt + */ + function initPopupPrompt() { + const popup = document.getElementById('wpn_popup_prompt'); + if (!popup || Notification.permission === 'denied') { + return; + } + + setTimeout(() => { + popup.style.display = 'flex'; + }, 1000); + + const allowBtn = document.getElementById('wpn_popup_allow'); + const declineBtn = document.getElementById('wpn_popup_decline'); + + if (allowBtn) { + allowBtn.addEventListener('click', () => { + popup.style.display = 'none'; + subscribeButtonHandler({ preventDefault: () => {} }); + }); + } + + if (declineBtn) { + declineBtn.addEventListener('click', () => { + popup.style.display = 'none'; + declinePopup(); + }); + } + } + + /** + * Send decline popup request + */ + function declinePopup() { + fetch(declinePopupUrl, { + method: 'POST', + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + body: getFormData({}), + }).catch(error => console.info(error)); + } + /** * Check whether subscription is valid * @@ -258,6 +307,10 @@ function PhpbbWebpush() { if ('form_tokens' in response) { updateFormTokens(response.form_tokens); } + const popup = document.getElementById('wpn_popup_prompt'); + if (popup) { + popup.style.display = 'none'; + } } } diff --git a/styles/all/template/webpush_popup.html b/styles/all/template/webpush_popup.html new file mode 100644 index 0000000..ef893c6 --- /dev/null +++ b/styles/all/template/webpush_popup.html @@ -0,0 +1,14 @@ +{% if S_WEBPUSH_POPUP_PROMPT %} + +{% endif %} diff --git a/styles/all/theme/phpbb_wpn.css b/styles/all/theme/phpbb_wpn.css index 415d0b2..25d5575 100644 --- a/styles/all/theme/phpbb_wpn.css +++ b/styles/all/theme/phpbb_wpn.css @@ -51,3 +51,98 @@ margin-top: 8px; } } + +.wpn-popup-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 9999; + display: flex; + align-items: flex-start; + justify-content: center; + padding-top: 20px; +} + +.wpn-popup-container { + background: #fff; + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); + max-width: 500px; + width: 90%; + animation: wpnSlideDown 0.3s ease-out; +} + +@keyframes wpnSlideDown { + from { + transform: translateY(-50px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +.wpn-popup-content { + padding: 24px; +} + +.wpn-popup-title { + margin: 0 0 12px; + font-size: 20px; + font-weight: 600; + color: #333; +} + +.wpn-popup-message { + margin: 0 0 20px; + font-size: 14px; + line-height: 1.5; + color: #666; +} + +.wpn-popup-buttons { + display: flex; + gap: 12px; + justify-content: flex-end; +} + +.wpn-popup-btn { + padding: 10px 20px; + border: none; + border-radius: 4px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; +} + +.wpn-popup-btn-allow { + background: #0066cc; + color: #fff; +} + +.wpn-popup-btn-allow:hover { + background: #0052a3; +} + +.wpn-popup-btn-decline { + background: #f0f0f0; + color: #666; +} + +.wpn-popup-btn-decline:hover { + background: #e0e0e0; +} + +@media (max-width: 500px) { + .wpn-popup-buttons { + flex-direction: column-reverse; + } + .wpn-popup-btn { + width: 100%; + } +} diff --git a/tests/controller/controller_webpush_test.php b/tests/controller/controller_webpush_test.php index 94cc97e..3d6feb9 100644 --- a/tests/controller/controller_webpush_test.php +++ b/tests/controller/controller_webpush_test.php @@ -420,6 +420,62 @@ public function test_sub_unsubscribe_success() $this->assertEmpty($this->get_subscription_data()); } + public function test_decline_popup_success() + { + $this->form_helper->method('check_form_tokens')->willReturn(true); + $this->request->method('is_ajax')->willReturn(true); + $this->user->data['user_id'] = 2; + $this->user->data['is_bot'] = false; + $this->user->data['user_type'] = USER_NORMAL; + + $response = $this->controller->decline_popup(); + + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertEquals(['success' => true], json_decode($response->getContent(), true)); + + $sql = 'SELECT user_wpn_popup_declined FROM phpbb_users WHERE user_id = 2'; + $result = $this->db->sql_query($sql); + $declined = $this->db->sql_fetchfield('user_wpn_popup_declined'); + $this->db->sql_freeresult($result); + + $this->assertEquals(1, $declined); + } + + public function test_subscribe_clears_declined_status() + { + $this->form_helper->method('check_form_tokens')->willReturn(true); + $this->request->method('is_ajax')->willReturn(true); + $this->user->data['user_id'] = 2; + $this->user->data['is_bot'] = false; + $this->user->data['user_type'] = USER_NORMAL; + + // First decline + $this->controller->decline_popup(); + + $sql = 'SELECT user_wpn_popup_declined FROM phpbb_users WHERE user_id = 2'; + $result = $this->db->sql_query($sql); + $declined = $this->db->sql_fetchfield('user_wpn_popup_declined'); + $this->db->sql_freeresult($result); + $this->assertEquals(1, $declined); + + // Then subscribe + $symfony_request = $this->createMock(\phpbb\symfony_request::class); + $symfony_request->method('get')->willReturn(json_encode([ + 'endpoint' => 'test_endpoint', + 'expiration_time' => 0, + 'keys' => ['p256dh' => 'test_p256dh', 'auth' => 'test_auth'] + ])); + + $this->controller->subscribe($symfony_request); + + // Check declined status is cleared + $sql = 'SELECT user_wpn_popup_declined FROM phpbb_users WHERE user_id = 2'; + $result = $this->db->sql_query($sql); + $declined = $this->db->sql_fetchfield('user_wpn_popup_declined'); + $this->db->sql_freeresult($result); + $this->assertEquals(0, $declined); + } + private function get_subscription_data() { $sql = 'SELECT * diff --git a/tests/controller/fixtures/webpush.xml b/tests/controller/fixtures/webpush.xml index f9f67fe..9a51734 100644 --- a/tests/controller/fixtures/webpush.xml +++ b/tests/controller/fixtures/webpush.xml @@ -48,12 +48,14 @@ user_permissions user_sig user_form_salt + user_wpn_popup_declined 1 Anonymous + 0 2 @@ -61,6 +63,7 @@ sua0m6f0ektt1g9z + 0 3 @@ -68,6 +71,7 @@ abcdef + 0 diff --git a/tests/functional/functional_test.php b/tests/functional/functional_test.php index 6527126..ee97c78 100644 --- a/tests/functional/functional_test.php +++ b/tests/functional/functional_test.php @@ -140,6 +140,34 @@ public function test_manifest() $this->assertEquals(json_encode($expected), self::get_content()); } + public function test_popup_prompt() + { + $this->login(); + $this->admin_login(); + + $this->add_lang_ext('phpbb/webpushnotifications', 'webpushnotifications_module_ucp'); + + // Assert popup is not present by default + $crawler = self::request('GET', 'index.php'); + $this->assertCount(0, $crawler->filter('#wpn_popup_prompt')); + + $this->set_acp_option('wpn_webpush_popup_prompt', 1); + + // Assert popup is present when enabled + $crawler = self::request('GET', 'index.php'); + $this->assertCount(1, $crawler->filter('#wpn_popup_prompt')); + $this->assertContainsLang('NOTIFY_WEBPUSH_POPUP_TITLE', $crawler->filter('.wpn-popup-title')->text()); + $this->assertContainsLang('NOTIFY_WEBPUSH_POPUP_MESSAGE', $crawler->filter('.wpn-popup-message')->text()); + $this->assertContainsLang('NOTIFY_WEBPUSH_POPUP_ALLOW', $crawler->filter('#wpn_popup_allow')->text()); + $this->assertContainsLang('NOTIFY_WEBPUSH_POPUP_DECLINE', $crawler->filter('#wpn_popup_decline')->text()); + + // Test decline popup route exists + $this->add_lang('common'); + self::request('POST', 'app.php/user/push/decline-popup', [], false); + $response = json_decode(self::get_content(), true); + $this->assertArrayHasKey('success', $response); + } + protected function set_acp_option($option, $value) { $crawler = self::request('GET', 'adm/index.php?i=-phpbb-webpushnotifications-acp-wpn_acp_module&mode=webpush&sid=' . $this->sid); diff --git a/ucp/controller/webpush.php b/ucp/controller/webpush.php index a8cb6ed..7137237 100644 --- a/ucp/controller/webpush.php +++ b/ucp/controller/webpush.php @@ -321,6 +321,11 @@ public function subscribe(symfony_request $symfony_request): JsonResponse ]); $this->db->sql_query($sql); + $sql = 'UPDATE ' . USERS_TABLE . ' + SET user_wpn_popup_declined = 0 + WHERE user_id = ' . (int) $this->user->id(); + $this->db->sql_query($sql); + return new JsonResponse([ 'success' => true, 'form_tokens' => $this->form_helper->get_form_tokens(self::FORM_TOKEN_UCP), @@ -352,6 +357,23 @@ public function unsubscribe(symfony_request $symfony_request): JsonResponse ]); } + /** + * Handle decline popup requests + * + * @return JsonResponse + */ + public function decline_popup(): JsonResponse + { + $this->check_subscribe_requests(); + + $sql = 'UPDATE ' . USERS_TABLE . ' + SET user_wpn_popup_declined = 1 + WHERE user_id = ' . (int) $this->user->id(); + $this->db->sql_query($sql); + + return new JsonResponse(['success' => true]); + } + /** * Takes an avatar string (usually in full html format already) and extracts the url. * If the avatar url is a relative path, it's converted to an absolute path. From 7f17201625473b890533298a668701f71028d545 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Wed, 10 Dec 2025 09:08:31 -0800 Subject: [PATCH 2/9] Clean up files --- .../ru/webpushnotifications_module_acp.php | 2 + .../ru/webpushnotifications_module_ucp.php | 4 ++ styles/all/theme/phpbb_wpn.css | 40 ++++++++++--------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/language/ru/webpushnotifications_module_acp.php b/language/ru/webpushnotifications_module_acp.php index dd94ea6..91691b2 100644 --- a/language/ru/webpushnotifications_module_acp.php +++ b/language/ru/webpushnotifications_module_acp.php @@ -50,5 +50,7 @@ 'WEBPUSH_METHOD_ENABLED_EXPLAIN'=> 'Если включено, то пользователи, подписавшиеся на браузерные push—уведомления, будут автоматически получать все их типы. Если отключено, то пользователи не будут получать браузерные push—уведомления до тех пор, пока хотя бы один их тип не выбран.

Отключить нежелательные или выбрать нужные типы браузерных push—уведомлений можно в настройках уведомлений в Личном разделе.', 'WEBPUSH_DROPDOWN_SUBSCRIBE' => 'Показать кнопку «Подписаться» в выпадающем меню уведомлений', 'WEBPUSH_DROPDOWN_SUBSCRIBE_EXPLAIN'=> 'Включить или отключить отображение кнопки «Подписаться» в выпадающем списке уведомлений. Если включено, то пользователи смогут подписываться на браузерные push-уведомления с любой страницы конференции.', + 'WEBPUSH_POPUP_PROMPT' => 'Show popup prompt for unsubscribed members', + 'WEBPUSH_POPUP_PROMPT_EXPLAIN' => 'Display a popup message asking registered members if they want to receive push notifications. The popup will only appear to members who are not currently subscribed and have not previously declined.', 'WEBPUSH_INSECURE_SERVER_ERROR' => 'На данной конференции не применяется защищённый протокол SSL/HTTPS, без которого использование браузерных push—уведомлений невозможно, либо соответствующие переменные серверного окружения неверно сконфигурированы. Убедитесь, что значения переменных серверного окружения HTTPS и/или HEADER_CLIENT_PROTO заданы верно.', ]); diff --git a/language/ru/webpushnotifications_module_ucp.php b/language/ru/webpushnotifications_module_ucp.php index b0fe007..d64ba96 100644 --- a/language/ru/webpushnotifications_module_ucp.php +++ b/language/ru/webpushnotifications_module_ucp.php @@ -49,4 +49,8 @@ 'NOTIFY_WEBPUSH_DROPDOWN_TITLE' => 'Посетите настройки уведомлений, чтобы установить предпочтительные типы браузерных уведомлений.', 'NOTIFY_WEBPUSH_DENIED' => 'Вы запретили браузерные уведомления для даного сайта. Для того, чтобы подписаться, необходимо их разрешить в настройках браузера.', 'NOTIFY_WEBPUSH_DISABLED' => 'Не поддерживается', + 'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Enable Push Notifications?', + 'NOTIFY_WEBPUSH_POPUP_MESSAGE' => 'Stay updated with instant notifications for new replies, mentions, and more.', + 'NOTIFY_WEBPUSH_POPUP_ALLOW' => 'Allow', + 'NOTIFY_WEBPUSH_POPUP_DECLINE' => 'Decline', ]); diff --git a/styles/all/theme/phpbb_wpn.css b/styles/all/theme/phpbb_wpn.css index 25d5575..1c7c24a 100644 --- a/styles/all/theme/phpbb_wpn.css +++ b/styles/all/theme/phpbb_wpn.css @@ -53,36 +53,37 @@ } .wpn-popup-overlay { + background: rgba(0, 0, 0, 0.5); position: fixed; + z-index: 9999; top: 0; left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.5); - z-index: 9999; display: flex; - align-items: flex-start; justify-content: center; + align-items: flex-start; + width: 100%; + height: 100%; padding-top: 20px; } .wpn-popup-container { - background: #fff; + background: #ffffff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); - max-width: 500px; width: 90%; + max-width: 500px; animation: wpnSlideDown 0.3s ease-out; } @keyframes wpnSlideDown { from { - transform: translateY(-50px); opacity: 0; + transform: translateY(-50px); } + to { - transform: translateY(0); opacity: 1; + transform: translateY(0); } } @@ -91,38 +92,38 @@ } .wpn-popup-title { - margin: 0 0 12px; font-size: 20px; font-weight: 600; - color: #333; + color: #333333; + margin: 0 0 12px; } .wpn-popup-message { - margin: 0 0 20px; font-size: 14px; line-height: 1.5; - color: #666; + color: #666666; + margin: 0 0 20px; } .wpn-popup-buttons { display: flex; - gap: 12px; justify-content: flex-end; + gap: 12px; } .wpn-popup-btn { - padding: 10px 20px; - border: none; - border-radius: 4px; font-size: 14px; font-weight: 500; + border: none; + border-radius: 4px; + padding: 10px 20px; cursor: pointer; transition: all 0.2s; } .wpn-popup-btn-allow { background: #0066cc; - color: #fff; + color: #ffffff; } .wpn-popup-btn-allow:hover { @@ -131,7 +132,7 @@ .wpn-popup-btn-decline { background: #f0f0f0; - color: #666; + color: #666666; } .wpn-popup-btn-decline:hover { @@ -142,6 +143,7 @@ .wpn-popup-buttons { flex-direction: column-reverse; } + .wpn-popup-btn { width: 100%; } From a8ff043bb2ce3910108854824cdc346e51d6e06c Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Wed, 10 Dec 2025 09:33:28 -0800 Subject: [PATCH 3/9] Show prompt based on device and service worker being subbed --- notification/method/webpush.php | 2 +- styles/all/template/webpush.js | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/notification/method/webpush.php b/notification/method/webpush.php index 01f641f..e762969 100644 --- a/notification/method/webpush.php +++ b/notification/method/webpush.php @@ -401,7 +401,7 @@ public function get_ucp_template_data(helper $controller_helper, form_helper $fo 'U_WEBPUSH_WORKER_URL' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_worker_controller'), 'SUBSCRIPTIONS' => $subscriptions, 'WEBPUSH_FORM_TOKENS' => $form_helper->get_form_tokens(\phpbb\webpushnotifications\ucp\controller\webpush::FORM_TOKEN_UCP), - 'S_WEBPUSH_POPUP_PROMPT' => $this->config['wpn_webpush_popup_prompt'] && empty($subscriptions) && !$this->user->data['user_wpn_popup_declined'] && $this->user->id() != ANONYMOUS && $this->user->data['user_type'] != USER_IGNORE, + 'S_WEBPUSH_POPUP_PROMPT' => $this->config['wpn_webpush_popup_prompt'] && !$this->user->data['user_wpn_popup_declined'] && $this->user->id() != ANONYMOUS && $this->user->data['user_type'] != USER_IGNORE, ]; } diff --git a/styles/all/template/webpush.js b/styles/all/template/webpush.js index daf3107..8f704cb 100644 --- a/styles/all/template/webpush.js +++ b/styles/all/template/webpush.js @@ -130,6 +130,27 @@ function PhpbbWebpush() { return; } + // Check if this browser already has a subscription + navigator.serviceWorker.getRegistration(serviceWorkerUrl) + .then(registration => { + if (typeof registration === 'undefined') { + showPopup(popup); + return; + } + + registration.pushManager.getSubscription() + .then(subscription => { + if (!isValidSubscription(subscription)) { + showPopup(popup); + } + }); + }); + } + + /** + * Show popup with event handlers + */ + function showPopup(popup) { setTimeout(() => { popup.style.display = 'flex'; }, 1000); From 73d88ef5a4840340999ef53dbc55ce93401c2b49 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Wed, 10 Dec 2025 10:26:57 -0800 Subject: [PATCH 4/9] Declined state should be stored per device / browser, not globally --- config/wpn_ucp.yml | 4 -- migrations/add_popup_prompt.php | 35 +----------- notification/method/webpush.php | 3 +- .../template/ucp_notifications_webpush.html | 1 - styles/all/template/webpush.js | 25 +++------ tests/controller/controller_webpush_test.php | 56 ------------------- tests/controller/fixtures/webpush.xml | 4 -- tests/functional/functional_test.php | 6 -- ucp/controller/webpush.php | 22 -------- 9 files changed, 11 insertions(+), 145 deletions(-) diff --git a/config/wpn_ucp.yml b/config/wpn_ucp.yml index 235efc1..f340196 100644 --- a/config/wpn_ucp.yml +++ b/config/wpn_ucp.yml @@ -13,7 +13,3 @@ phpbb_webpushnotifications_ucp_push_subscribe_controller: phpbb_webpushnotifications_ucp_push_unsubscribe_controller: path: /push/unsubscribe defaults: { _controller: phpbb.wpn.ucp.controller.webpush:unsubscribe } - -phpbb_webpushnotifications_ucp_push_decline_popup_controller: - path: /push/decline-popup - defaults: { _controller: phpbb.wpn.ucp.controller.webpush:decline_popup } diff --git a/migrations/add_popup_prompt.php b/migrations/add_popup_prompt.php index ad8b8e4..98ba3cd 100644 --- a/migrations/add_popup_prompt.php +++ b/migrations/add_popup_prompt.php @@ -14,7 +14,7 @@ class add_popup_prompt extends migration { - public function effectively_installed(): bool + public function effectively_installed() { return $this->config->offsetExists('wpn_webpush_popup_prompt'); } @@ -24,39 +24,10 @@ public static function depends_on() return ['\phpbb\webpushnotifications\migrations\add_acp_configs']; } - public function update_schema(): array + public function update_data() { return [ - 'add_columns' => [ - $this->table_prefix . 'users' => [ - 'user_wpn_popup_declined' => ['BOOL', 0], - ], - ], - ]; - } - - public function revert_schema(): array - { - return [ - 'drop_columns' => [ - $this->table_prefix . 'users' => [ - 'user_wpn_popup_declined', - ], - ], - ]; - } - - public function update_data(): array - { - return [ - ['config.add', ['wpn_webpush_popup_prompt', false]], - ]; - } - - public function revert_data(): array - { - return [ - ['config.remove', ['wpn_webpush_popup_prompt']], + ['config.add', ['wpn_webpush_popup_prompt', 0]], ]; } } diff --git a/notification/method/webpush.php b/notification/method/webpush.php index e762969..8f371ed 100644 --- a/notification/method/webpush.php +++ b/notification/method/webpush.php @@ -396,12 +396,11 @@ public function get_ucp_template_data(helper $controller_helper, form_helper $fo 'NOTIFICATIONS_WEBPUSH_ENABLE' => ($this->config['load_notifications'] && $this->config['allow_board_notifications'] && $this->config['wpn_webpush_dropdown_subscribe']) || stripos($this->user->page['page'], 'notification_options'), 'U_WEBPUSH_SUBSCRIBE' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_subscribe_controller'), 'U_WEBPUSH_UNSUBSCRIBE' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_unsubscribe_controller'), - 'U_WEBPUSH_DECLINE_POPUP' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_decline_popup_controller'), 'VAPID_PUBLIC_KEY' => $this->config['wpn_webpush_vapid_public'], 'U_WEBPUSH_WORKER_URL' => $controller_helper->route('phpbb_webpushnotifications_ucp_push_worker_controller'), 'SUBSCRIPTIONS' => $subscriptions, 'WEBPUSH_FORM_TOKENS' => $form_helper->get_form_tokens(\phpbb\webpushnotifications\ucp\controller\webpush::FORM_TOKEN_UCP), - 'S_WEBPUSH_POPUP_PROMPT' => $this->config['wpn_webpush_popup_prompt'] && !$this->user->data['user_wpn_popup_declined'] && $this->user->id() != ANONYMOUS && $this->user->data['user_type'] != USER_IGNORE, + 'S_WEBPUSH_POPUP_PROMPT' => $this->config['wpn_webpush_popup_prompt'] && $this->user->id() != ANONYMOUS && $this->user->data['user_type'] != USER_IGNORE, ]; } diff --git a/styles/all/template/ucp_notifications_webpush.html b/styles/all/template/ucp_notifications_webpush.html index 9836618..16ebf96 100644 --- a/styles/all/template/ucp_notifications_webpush.html +++ b/styles/all/template/ucp_notifications_webpush.html @@ -3,7 +3,6 @@ serviceWorkerUrl: '{{ U_WEBPUSH_WORKER_URL }}', subscribeUrl: '{{ U_WEBPUSH_SUBSCRIBE }}', unsubscribeUrl: '{{ U_WEBPUSH_UNSUBSCRIBE }}', - declinePopupUrl: '{{ U_WEBPUSH_DECLINE_POPUP }}', ajaxErrorTitle: '{{ lang('AJAX_ERROR_TITLE') }}', vapidPublicKey: '{{ VAPID_PUBLIC_KEY }}', formTokens: { diff --git a/styles/all/template/webpush.js b/styles/all/template/webpush.js index 8f704cb..51b8bff 100644 --- a/styles/all/template/webpush.js +++ b/styles/all/template/webpush.js @@ -33,9 +33,6 @@ function PhpbbWebpush() { /** @type {HTMLElement} Unsubscribe button */ let unsubscribeButton; - /** @type {string} URL to decline popup */ - let declinePopupUrl = ''; - /** * Init function for phpBB Web Push * @type {array} options @@ -44,7 +41,6 @@ function PhpbbWebpush() { serviceWorkerUrl = options.serviceWorkerUrl; subscribeUrl = options.subscribeUrl; unsubscribeUrl = options.unsubscribeUrl; - declinePopupUrl = options.declinePopupUrl; this.formTokens = options.formTokens; subscriptions = options.subscriptions; ajaxErrorTitle = options.ajaxErrorTitle; @@ -130,6 +126,11 @@ function PhpbbWebpush() { return; } + // Check if user declined on this browser + if (localStorage.getItem('wpn_popup_declined') === 'true') { + return; + } + // Check if this browser already has a subscription navigator.serviceWorker.getRegistration(serviceWorkerUrl) .then(registration => { @@ -168,24 +169,11 @@ function PhpbbWebpush() { if (declineBtn) { declineBtn.addEventListener('click', () => { popup.style.display = 'none'; - declinePopup(); + localStorage.setItem('wpn_popup_declined', 'true'); }); } } - /** - * Send decline popup request - */ - function declinePopup() { - fetch(declinePopupUrl, { - method: 'POST', - headers: { - 'X-Requested-With': 'XMLHttpRequest', - }, - body: getFormData({}), - }).catch(error => console.info(error)); - } - /** * Check whether subscription is valid * @@ -328,6 +316,7 @@ function PhpbbWebpush() { if ('form_tokens' in response) { updateFormTokens(response.form_tokens); } + localStorage.removeItem('wpn_popup_declined'); const popup = document.getElementById('wpn_popup_prompt'); if (popup) { popup.style.display = 'none'; diff --git a/tests/controller/controller_webpush_test.php b/tests/controller/controller_webpush_test.php index 3d6feb9..94cc97e 100644 --- a/tests/controller/controller_webpush_test.php +++ b/tests/controller/controller_webpush_test.php @@ -420,62 +420,6 @@ public function test_sub_unsubscribe_success() $this->assertEmpty($this->get_subscription_data()); } - public function test_decline_popup_success() - { - $this->form_helper->method('check_form_tokens')->willReturn(true); - $this->request->method('is_ajax')->willReturn(true); - $this->user->data['user_id'] = 2; - $this->user->data['is_bot'] = false; - $this->user->data['user_type'] = USER_NORMAL; - - $response = $this->controller->decline_popup(); - - $this->assertInstanceOf(JsonResponse::class, $response); - $this->assertEquals(['success' => true], json_decode($response->getContent(), true)); - - $sql = 'SELECT user_wpn_popup_declined FROM phpbb_users WHERE user_id = 2'; - $result = $this->db->sql_query($sql); - $declined = $this->db->sql_fetchfield('user_wpn_popup_declined'); - $this->db->sql_freeresult($result); - - $this->assertEquals(1, $declined); - } - - public function test_subscribe_clears_declined_status() - { - $this->form_helper->method('check_form_tokens')->willReturn(true); - $this->request->method('is_ajax')->willReturn(true); - $this->user->data['user_id'] = 2; - $this->user->data['is_bot'] = false; - $this->user->data['user_type'] = USER_NORMAL; - - // First decline - $this->controller->decline_popup(); - - $sql = 'SELECT user_wpn_popup_declined FROM phpbb_users WHERE user_id = 2'; - $result = $this->db->sql_query($sql); - $declined = $this->db->sql_fetchfield('user_wpn_popup_declined'); - $this->db->sql_freeresult($result); - $this->assertEquals(1, $declined); - - // Then subscribe - $symfony_request = $this->createMock(\phpbb\symfony_request::class); - $symfony_request->method('get')->willReturn(json_encode([ - 'endpoint' => 'test_endpoint', - 'expiration_time' => 0, - 'keys' => ['p256dh' => 'test_p256dh', 'auth' => 'test_auth'] - ])); - - $this->controller->subscribe($symfony_request); - - // Check declined status is cleared - $sql = 'SELECT user_wpn_popup_declined FROM phpbb_users WHERE user_id = 2'; - $result = $this->db->sql_query($sql); - $declined = $this->db->sql_fetchfield('user_wpn_popup_declined'); - $this->db->sql_freeresult($result); - $this->assertEquals(0, $declined); - } - private function get_subscription_data() { $sql = 'SELECT * diff --git a/tests/controller/fixtures/webpush.xml b/tests/controller/fixtures/webpush.xml index 9a51734..f9f67fe 100644 --- a/tests/controller/fixtures/webpush.xml +++ b/tests/controller/fixtures/webpush.xml @@ -48,14 +48,12 @@ user_permissions user_sig user_form_salt - user_wpn_popup_declined 1 Anonymous - 0 2 @@ -63,7 +61,6 @@ sua0m6f0ektt1g9z - 0 3 @@ -71,7 +68,6 @@ abcdef - 0 diff --git a/tests/functional/functional_test.php b/tests/functional/functional_test.php index ee97c78..76b08aa 100644 --- a/tests/functional/functional_test.php +++ b/tests/functional/functional_test.php @@ -160,12 +160,6 @@ public function test_popup_prompt() $this->assertContainsLang('NOTIFY_WEBPUSH_POPUP_MESSAGE', $crawler->filter('.wpn-popup-message')->text()); $this->assertContainsLang('NOTIFY_WEBPUSH_POPUP_ALLOW', $crawler->filter('#wpn_popup_allow')->text()); $this->assertContainsLang('NOTIFY_WEBPUSH_POPUP_DECLINE', $crawler->filter('#wpn_popup_decline')->text()); - - // Test decline popup route exists - $this->add_lang('common'); - self::request('POST', 'app.php/user/push/decline-popup', [], false); - $response = json_decode(self::get_content(), true); - $this->assertArrayHasKey('success', $response); } protected function set_acp_option($option, $value) diff --git a/ucp/controller/webpush.php b/ucp/controller/webpush.php index 7137237..a8cb6ed 100644 --- a/ucp/controller/webpush.php +++ b/ucp/controller/webpush.php @@ -321,11 +321,6 @@ public function subscribe(symfony_request $symfony_request): JsonResponse ]); $this->db->sql_query($sql); - $sql = 'UPDATE ' . USERS_TABLE . ' - SET user_wpn_popup_declined = 0 - WHERE user_id = ' . (int) $this->user->id(); - $this->db->sql_query($sql); - return new JsonResponse([ 'success' => true, 'form_tokens' => $this->form_helper->get_form_tokens(self::FORM_TOKEN_UCP), @@ -357,23 +352,6 @@ public function unsubscribe(symfony_request $symfony_request): JsonResponse ]); } - /** - * Handle decline popup requests - * - * @return JsonResponse - */ - public function decline_popup(): JsonResponse - { - $this->check_subscribe_requests(); - - $sql = 'UPDATE ' . USERS_TABLE . ' - SET user_wpn_popup_declined = 1 - WHERE user_id = ' . (int) $this->user->id(); - $this->db->sql_query($sql); - - return new JsonResponse(['success' => true]); - } - /** * Takes an avatar string (usually in full html format already) and extracts the url. * If the avatar url is a relative path, it's converted to an absolute path. From 01178f1dfd94c1f8c9fa40d3ec46b2c154fa6eb1 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Fri, 12 Dec 2025 18:23:58 -0800 Subject: [PATCH 5/9] Move local storage calls into wrappers --- styles/all/template/webpush.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/styles/all/template/webpush.js b/styles/all/template/webpush.js index 51b8bff..8acc355 100644 --- a/styles/all/template/webpush.js +++ b/styles/all/template/webpush.js @@ -127,7 +127,7 @@ function PhpbbWebpush() { } // Check if user declined on this browser - if (localStorage.getItem('wpn_popup_declined') === 'true') { + if (getDeclined() === 'true') { return; } @@ -169,7 +169,7 @@ function PhpbbWebpush() { if (declineBtn) { declineBtn.addEventListener('click', () => { popup.style.display = 'none'; - localStorage.setItem('wpn_popup_declined', 'true'); + setDeclined(); }); } } @@ -316,7 +316,7 @@ function PhpbbWebpush() { if ('form_tokens' in response) { updateFormTokens(response.form_tokens); } - localStorage.removeItem('wpn_popup_declined'); + resetDeclined(); const popup = document.getElementById('wpn_popup_prompt'); if (popup) { popup.style.display = 'none'; @@ -366,6 +366,16 @@ function PhpbbWebpush() { return outputArray; } + + function setDeclined() { + localStorage.setItem('wpn_popup_declined', 'true'); + } + function getDeclined() { + return localStorage.getItem('wpn_popup_declined'); + } + function resetDeclined() { + localStorage.removeItem('wpn_popup_declined'); + } } function domReady(callBack) { From f1a9751a81169821021de3420dca51aef695f72c Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Fri, 12 Dec 2025 18:25:07 -0800 Subject: [PATCH 6/9] Allow clicking on modal do dismiss and decline --- styles/all/template/webpush.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/styles/all/template/webpush.js b/styles/all/template/webpush.js index 8acc355..0968dff 100644 --- a/styles/all/template/webpush.js +++ b/styles/all/template/webpush.js @@ -158,20 +158,32 @@ function PhpbbWebpush() { const allowBtn = document.getElementById('wpn_popup_allow'); const declineBtn = document.getElementById('wpn_popup_decline'); + const overlay = document.getElementById('wpn_popup_prompt'); if (allowBtn) { - allowBtn.addEventListener('click', () => { + allowBtn.addEventListener('click', (event) => { + event.stopPropagation(); popup.style.display = 'none'; subscribeButtonHandler({ preventDefault: () => {} }); }); } if (declineBtn) { - declineBtn.addEventListener('click', () => { + declineBtn.addEventListener('click', (event) => { + event.stopPropagation(); popup.style.display = 'none'; setDeclined(); }); } + + if (overlay) { + overlay.addEventListener('click', (event) => { + if (event.target === overlay) { + popup.style.display = 'none'; + setDeclined(); + } + }); + } } /** From 5d514093768e6fa11da91c8e7416eb0a98fe5793 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Sat, 13 Dec 2025 11:17:49 -0800 Subject: [PATCH 7/9] Prompt message tweaks --- language/en/webpushnotifications_module_ucp.php | 4 ++-- styles/all/theme/phpbb_wpn.css | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/language/en/webpushnotifications_module_ucp.php b/language/en/webpushnotifications_module_ucp.php index 6e8e183..31ed884 100644 --- a/language/en/webpushnotifications_module_ucp.php +++ b/language/en/webpushnotifications_module_ucp.php @@ -49,8 +49,8 @@ 'NOTIFY_WEBPUSH_DROPDOWN_TITLE' => 'Visit notifications settings to set your preferred push notifications.', 'NOTIFY_WEBPUSH_DENIED' => 'You have denied notifications from this site. To enable push notifications, allow notifications from this site in your browser settings.', 'NOTIFY_WEBPUSH_DISABLED' => 'Push notifications not supported', - 'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Enable Push Notifications?', - 'NOTIFY_WEBPUSH_POPUP_MESSAGE' => 'Stay updated with instant notifications for new replies, mentions, and more.', + 'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Allow browser notifications?', + 'NOTIFY_WEBPUSH_POPUP_MESSAGE' => 'We would like to send you browser notifications for replies, private messages, and relevant forum activity. Optional — you can manage these settings at any time.', 'NOTIFY_WEBPUSH_POPUP_ALLOW' => 'Allow', 'NOTIFY_WEBPUSH_POPUP_DECLINE' => 'Decline', ]); diff --git a/styles/all/theme/phpbb_wpn.css b/styles/all/theme/phpbb_wpn.css index 1c7c24a..53d8913 100644 --- a/styles/all/theme/phpbb_wpn.css +++ b/styles/all/theme/phpbb_wpn.css @@ -92,7 +92,7 @@ } .wpn-popup-title { - font-size: 20px; + font-size: 18px; font-weight: 600; color: #333333; margin: 0 0 12px; From 42807cb01d22e77450f96608cd1ca0b2329b4a33 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Sun, 14 Dec 2025 07:53:52 -0800 Subject: [PATCH 8/9] Update language/ru/webpushnotifications_module_ucp.php Co-authored-by: rxu --- language/ru/webpushnotifications_module_ucp.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/language/ru/webpushnotifications_module_ucp.php b/language/ru/webpushnotifications_module_ucp.php index d64ba96..6b08e2b 100644 --- a/language/ru/webpushnotifications_module_ucp.php +++ b/language/ru/webpushnotifications_module_ucp.php @@ -49,8 +49,8 @@ 'NOTIFY_WEBPUSH_DROPDOWN_TITLE' => 'Посетите настройки уведомлений, чтобы установить предпочтительные типы браузерных уведомлений.', 'NOTIFY_WEBPUSH_DENIED' => 'Вы запретили браузерные уведомления для даного сайта. Для того, чтобы подписаться, необходимо их разрешить в настройках браузера.', 'NOTIFY_WEBPUSH_DISABLED' => 'Не поддерживается', - 'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Enable Push Notifications?', - 'NOTIFY_WEBPUSH_POPUP_MESSAGE' => 'Stay updated with instant notifications for new replies, mentions, and more.', - 'NOTIFY_WEBPUSH_POPUP_ALLOW' => 'Allow', - 'NOTIFY_WEBPUSH_POPUP_DECLINE' => 'Decline', + 'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Включить браузерные уведомления?', + 'NOTIFY_WEBPUSH_POPUP_MESSAGE' => 'Браузерные уведомления позволяют быстро получать информацию о новых ответах, личных сообщениях и других активностях на данной конференции. Функцию можно отключить или включить в любое время в настройках уведомлений в Личном разделе.', + 'NOTIFY_WEBPUSH_POPUP_ALLOW' => 'Включить', + 'NOTIFY_WEBPUSH_POPUP_DECLINE' => 'Отклонить', ]); From 125154df33c43e4b5e8a23a91e5e4b65a4631f75 Mon Sep 17 00:00:00 2001 From: Matt Friedman Date: Sun, 14 Dec 2025 07:54:07 -0800 Subject: [PATCH 9/9] Update language/ru/webpushnotifications_module_acp.php Co-authored-by: rxu --- language/ru/webpushnotifications_module_acp.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/language/ru/webpushnotifications_module_acp.php b/language/ru/webpushnotifications_module_acp.php index 91691b2..5232e58 100644 --- a/language/ru/webpushnotifications_module_acp.php +++ b/language/ru/webpushnotifications_module_acp.php @@ -50,7 +50,7 @@ 'WEBPUSH_METHOD_ENABLED_EXPLAIN'=> 'Если включено, то пользователи, подписавшиеся на браузерные push—уведомления, будут автоматически получать все их типы. Если отключено, то пользователи не будут получать браузерные push—уведомления до тех пор, пока хотя бы один их тип не выбран.

Отключить нежелательные или выбрать нужные типы браузерных push—уведомлений можно в настройках уведомлений в Личном разделе.', 'WEBPUSH_DROPDOWN_SUBSCRIBE' => 'Показать кнопку «Подписаться» в выпадающем меню уведомлений', 'WEBPUSH_DROPDOWN_SUBSCRIBE_EXPLAIN'=> 'Включить или отключить отображение кнопки «Подписаться» в выпадающем списке уведомлений. Если включено, то пользователи смогут подписываться на браузерные push-уведомления с любой страницы конференции.', - 'WEBPUSH_POPUP_PROMPT' => 'Show popup prompt for unsubscribed members', - 'WEBPUSH_POPUP_PROMPT_EXPLAIN' => 'Display a popup message asking registered members if they want to receive push notifications. The popup will only appear to members who are not currently subscribed and have not previously declined.', + 'WEBPUSH_POPUP_PROMPT' => 'Показывать всплывающее приглашение', + 'WEBPUSH_POPUP_PROMPT_EXPLAIN' => 'Показывать всплывающее сообщение зарегистрированным пользователям с приглашением подписаться на браузерные уведомления данной конференции. Сообщение будет показано только тем зарегистрированным пользователям, которые не подписаны на браузерные уведомления и ранее не отклоняли такое приглашение.', 'WEBPUSH_INSECURE_SERVER_ERROR' => 'На данной конференции не применяется защищённый протокол SSL/HTTPS, без которого использование браузерных push—уведомлений невозможно, либо соответствующие переменные серверного окружения неверно сконфигурированы. Убедитесь, что значения переменных серверного окружения HTTPS и/или HEADER_CLIENT_PROTO заданы верно.', ]);