Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions acp/wpn_acp_module.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
]);

Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions adm/style/wpn_acp_settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ <h3>{{ lang('WARNING') }}</h3>
<dd><label><input type="radio" class="radio" name="config[wpn_webpush_method_enabled]" value="1" {% if S_WEBPUSH_METHOD_ENABLED %}checked="checked"{% endif %}> {{ lang('YES') }}</label>
<label><input type="radio" class="radio" name="config[wpn_webpush_method_enabled]" value="0"{% if not S_WEBPUSH_METHOD_ENABLED %} checked="checked"{% endif %}> {{ lang('NO') }}</label></dd>
</dl>
<dl>
<dt><label>{{ lang('WEBPUSH_POPUP_PROMPT') ~ lang('COLON') }}</label><br><span>{{ lang('WEBPUSH_POPUP_PROMPT_EXPLAIN') }}</span></dt>
<dd><label><input type="radio" class="radio" name="config[wpn_webpush_popup_prompt]" value="1" {% if S_WEBPUSH_POPUP_PROMPT %}checked="checked"{% endif %}> {{ lang('YES') }}</label>
<label><input type="radio" class="radio" name="config[wpn_webpush_popup_prompt]" value="0"{% if not S_WEBPUSH_POPUP_PROMPT %} checked="checked"{% endif %}> {{ lang('NO') }}</label></dd>
</dl>
</fieldset>
<fieldset class="submit-buttons">
<input class="button1" type="submit" id="submit" name="submit" value="{{ lang('SUBMIT') }}">&nbsp;
Expand Down
2 changes: 2 additions & 0 deletions language/en/webpushnotifications_module_acp.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.<br><br>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 <em>HTTPS</em> and <em>HEADER_CLIENT_PROTO</em> server environment variables are correctly configured.',
]);
4 changes: 4 additions & 0 deletions language/en/webpushnotifications_module_ucp.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' => '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',
]);
2 changes: 2 additions & 0 deletions language/ru/webpushnotifications_module_acp.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,7 @@
'WEBPUSH_METHOD_ENABLED_EXPLAIN'=> 'Если включено, то пользователи, подписавшиеся на браузерные push—уведомления, будут автоматически получать все их типы. Если отключено, то пользователи не будут получать браузерные push—уведомления до тех пор, пока хотя бы один их тип не выбран.<br><br>Отключить нежелательные или выбрать нужные типы браузерных push—уведомлений можно в настройках уведомлений в Личном разделе.',
'WEBPUSH_DROPDOWN_SUBSCRIBE' => 'Показать кнопку «Подписаться» в выпадающем меню уведомлений',
'WEBPUSH_DROPDOWN_SUBSCRIBE_EXPLAIN'=> 'Включить или отключить отображение кнопки «Подписаться» в выпадающем списке уведомлений. Если включено, то пользователи смогут подписываться на браузерные push-уведомления с любой страницы конференции.',
'WEBPUSH_POPUP_PROMPT' => 'Показывать всплывающее приглашение',
'WEBPUSH_POPUP_PROMPT_EXPLAIN' => 'Показывать всплывающее сообщение зарегистрированным пользователям с приглашением подписаться на браузерные уведомления данной конференции. Сообщение будет показано только тем зарегистрированным пользователям, которые не подписаны на браузерные уведомления и ранее не отклоняли такое приглашение.',
'WEBPUSH_INSECURE_SERVER_ERROR' => 'На данной конференции не применяется защищённый протокол SSL/HTTPS, без которого использование браузерных push—уведомлений невозможно, либо соответствующие переменные серверного окружения неверно сконфигурированы. Убедитесь, что значения переменных серверного окружения <em>HTTPS</em> и/или <em>HEADER_CLIENT_PROTO</em> заданы верно.',
]);
4 changes: 4 additions & 0 deletions language/ru/webpushnotifications_module_ucp.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,8 @@
'NOTIFY_WEBPUSH_DROPDOWN_TITLE' => 'Посетите настройки уведомлений, чтобы установить предпочтительные типы браузерных уведомлений.',
'NOTIFY_WEBPUSH_DENIED' => 'Вы запретили браузерные уведомления для даного сайта. Для того, чтобы подписаться, необходимо их разрешить в настройках браузера.',
'NOTIFY_WEBPUSH_DISABLED' => 'Не поддерживается',
'NOTIFY_WEBPUSH_POPUP_TITLE' => 'Включить браузерные уведомления?',
'NOTIFY_WEBPUSH_POPUP_MESSAGE' => 'Браузерные уведомления позволяют быстро получать информацию о новых ответах, личных сообщениях и других активностях на данной конференции. Функцию можно отключить или включить в любое время в настройках уведомлений в Личном разделе.',
'NOTIFY_WEBPUSH_POPUP_ALLOW' => 'Включить',
'NOTIFY_WEBPUSH_POPUP_DECLINE' => 'Отклонить',
]);
33 changes: 33 additions & 0 deletions migrations/add_popup_prompt.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
/**
*
* phpBB Browser Push Notifications. An extension for the phpBB Forum Software package.
*
* @copyright (c) 2025, phpBB Limited <https://www.phpbb.com>
* @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()
{
return $this->config->offsetExists('wpn_webpush_popup_prompt');
}

public static function depends_on()
{
return ['\phpbb\webpushnotifications\migrations\add_acp_configs'];
}

public function update_data()
{
return [
['config.add', ['wpn_webpush_popup_prompt', 0]],
];
}
}
1 change: 1 addition & 0 deletions notification/method/webpush.php
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,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'] && $this->user->id() != ANONYMOUS && $this->user->data['user_type'] != USER_IGNORE,
];
}

Expand Down
1 change: 1 addition & 0 deletions styles/all/template/event/overall_header_head_append.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@

{% if NOTIFICATIONS_WEBPUSH_ENABLE %}
{% include '@phpbb_webpushnotifications/ucp_notifications_webpush.html' %}
{% include '@phpbb_webpushnotifications/webpush_popup.html' %}
{% endif %}
85 changes: 85 additions & 0 deletions styles/all/template/webpush.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function PhpbbWebpush() {
unsubscribeButton.addEventListener('click', unsubscribeButtonHandler);

updateButtonState();
initPopupPrompt();
})
.catch(error => {
console.info(error);
Expand Down Expand Up @@ -116,6 +117,75 @@ function PhpbbWebpush() {
}
}

/**
* Initialize popup prompt
*/
function initPopupPrompt() {
const popup = document.getElementById('wpn_popup_prompt');
if (!popup || Notification.permission === 'denied') {
return;
}

// Check if user declined on this browser
if (getDeclined() === 'true') {
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);

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', (event) => {
event.stopPropagation();
popup.style.display = 'none';
subscribeButtonHandler({ preventDefault: () => {} });
});
}

if (declineBtn) {
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();
}
});
}
}

/**
* Check whether subscription is valid
*
Expand Down Expand Up @@ -258,6 +328,11 @@ function PhpbbWebpush() {
if ('form_tokens' in response) {
updateFormTokens(response.form_tokens);
}
resetDeclined();
const popup = document.getElementById('wpn_popup_prompt');
if (popup) {
popup.style.display = 'none';
}
}
}

Expand Down Expand Up @@ -303,6 +378,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) {
Expand Down
14 changes: 14 additions & 0 deletions styles/all/template/webpush_popup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% if S_WEBPUSH_POPUP_PROMPT %}
<div id="wpn_popup_prompt" class="wpn-popup-overlay" style="display:none;">
<div class="wpn-popup-container">
<div class="wpn-popup-content">
<h3 class="wpn-popup-title">{{ lang('NOTIFY_WEBPUSH_POPUP_TITLE') }}</h3>
<p class="wpn-popup-message">{{ lang('NOTIFY_WEBPUSH_POPUP_MESSAGE') }}</p>
<div class="wpn-popup-buttons">
<button id="wpn_popup_allow" class="wpn-popup-btn wpn-popup-btn-allow">{{ lang('NOTIFY_WEBPUSH_POPUP_ALLOW') }}</button>
<button id="wpn_popup_decline" class="wpn-popup-btn wpn-popup-btn-decline">{{ lang('NOTIFY_WEBPUSH_POPUP_DECLINE') }}</button>
</div>
</div>
</div>
</div>
{% endif %}
97 changes: 97 additions & 0 deletions styles/all/theme/phpbb_wpn.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,100 @@
margin-top: 8px;
}
}

.wpn-popup-overlay {
background: rgba(0, 0, 0, 0.5);
position: fixed;
z-index: 9999;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: flex-start;
width: 100%;
height: 100%;
padding-top: 20px;
}

.wpn-popup-container {
background: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
width: 90%;
max-width: 500px;
animation: wpnSlideDown 0.3s ease-out;
}

@keyframes wpnSlideDown {
from {
opacity: 0;
transform: translateY(-50px);
}

to {
opacity: 1;
transform: translateY(0);
}
}

.wpn-popup-content {
padding: 24px;
}

.wpn-popup-title {
font-size: 18px;
font-weight: 600;
color: #333333;
margin: 0 0 12px;
}

.wpn-popup-message {
font-size: 14px;
line-height: 1.5;
color: #666666;
margin: 0 0 20px;
}

.wpn-popup-buttons {
display: flex;
justify-content: flex-end;
gap: 12px;
}

.wpn-popup-btn {
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: #ffffff;
}

.wpn-popup-btn-allow:hover {
background: #0052a3;
}

.wpn-popup-btn-decline {
background: #f0f0f0;
color: #666666;
}

.wpn-popup-btn-decline:hover {
background: #e0e0e0;
}

@media (max-width: 500px) {
.wpn-popup-buttons {
flex-direction: column-reverse;
}

.wpn-popup-btn {
width: 100%;
}
}
22 changes: 22 additions & 0 deletions tests/functional/functional_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,28 @@ 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());
}

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);
Expand Down