From 305635905730545003f1280452ecc86f4dab0186 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc=20Farr=C3=A9?= <contact@marc.fun>
Date: Mon, 6 Jan 2025 10:31:14 +0000
Subject: [PATCH 1/3] Fix: Delete DeviceID after Logout

---
 Events.php                      |  8 +++-
 config.php                      |  1 +
 controllers/TokenController.php | 72 +++++++++++++--------------------
 docs/CHANGELOG.md               |  4 ++
 module.json                     |  2 +-
 5 files changed, 41 insertions(+), 46 deletions(-)

diff --git a/Events.php b/Events.php
index a36da48..64575eb 100644
--- a/Events.php
+++ b/Events.php
@@ -8,6 +8,7 @@
 use humhub\modules\fcmPush\components\MailerMessage;
 use humhub\modules\fcmPush\components\NotificationTargetProvider;
 use humhub\modules\fcmPush\helpers\MobileAppHelper;
+use humhub\modules\fcmPush\helpers\WebAppHelper;
 use humhub\modules\fcmPush\services\DriverService;
 use humhub\modules\fcmPush\widgets\PushNotificationInfoWidget;
 use humhub\modules\notification\targets\MobileTargetProvider;
@@ -134,9 +135,14 @@ public static function onAfterLogin()
         Yii::$app->session->set(MobileAppHelper::SESSION_VAR_REGISTER_NOTIFICATION, 1);
     }
 
-    public static function onAfterLogout()
+    public static function onBeforeLogout()
     {
         Yii::$app->session->set(MobileAppHelper::SESSION_VAR_UNREGISTER_NOTIFICATION, 1);
+        Yii::$app->view->registerJs('humhub.modules.firebase.unregisterNotification();');
+    }
+
+    public static function onAfterLogout()
+    {
         Yii::$app->session->set(MobileAppHelper::SESSION_VAR_SHOW_OPENER, 1);
     }
 
diff --git a/config.php b/config.php
index 9491c93..43d60e2 100644
--- a/config.php
+++ b/config.php
@@ -23,6 +23,7 @@
         [LayoutAddons::class, LayoutAddons::EVENT_INIT, [Events::class, 'onLayoutAddonInit']],
         [Application::class, Application::EVENT_BEFORE_REQUEST, [Events::class, 'onBeforeRequest']],
         [User::class, User::EVENT_AFTER_LOGIN, [Events::class, 'onAfterLogin']],
+        [User::class, User::EVENT_BEFORE_LOGOUT, [Events::class, 'onBeforeLogout']],
         [User::class, User::EVENT_AFTER_LOGOUT, [Events::class, 'onAfterLogout']],
         [AuthChoice::class, AuthChoice::EVENT_BEFORE_RUN, [Events::class, 'onAuthChoiceBeforeRun']],
         //[NotificationInfoWidget::class, \humhub\widgets\BaseStack::EVENT_RUN, [Events::class, 'onNotificationInfoWidget']],
diff --git a/controllers/TokenController.php b/controllers/TokenController.php
index 11de51c..4c357b2 100644
--- a/controllers/TokenController.php
+++ b/controllers/TokenController.php
@@ -30,52 +30,20 @@ public function beforeAction($action)
 
     public function actionUpdate()
     {
-        $this->requireLogin();
-
-        $driver = (new DriverService($this->module->getConfigureForm()))->getWebDriver();
-        if (!$driver) {
-            Yii::$app->response->statusCode = 400;
-            return $this->asJson(['success' => false, 'message' => 'No push driver available!']);
-        }
-
-        return $this->asJson([
-            'success' => (
-                (new TokenService())->storeTokenForUser(
-                    Yii::$app->user->getIdentity(),
-                    $driver,
-                    Yii::$app->request->post('token'),
-                )
-            ),
-        ]);
+        return $this->update(false);
     }
 
     public function actionUpdateMobileApp()
     {
-        $this->requireLogin();
-
-        $driver = (new DriverService($this->module->getConfigureForm()))->getMobileAppDriver();
-        if (!$driver) {
-            Yii::error('Could not update token for mobile app. No driver available.', 'fcm-push');
-
-            Yii::$app->response->statusCode = 400;
-            return $this->asJson(['success' => false, 'message' => 'No push driver available!']);
-        }
-
-        return $this->asJson([
-            'success' => (
-                (new TokenService())->storeTokenForUser(
-                    Yii::$app->user->getIdentity(),
-                    $driver,
-                    Yii::$app->request->post('token'),
-                )
-            ),
-        ]);
+        return $this->update(true);
     }
 
-
     public function actionDeleteMobileApp()
     {
-        $driver = (new DriverService($this->module->getConfigureForm()))->getMobileAppDriver();
+        $driverService = new DriverService($this->module->getConfigureForm());
+        $tokenService = new TokenService();
+
+        $driver = $driverService->getMobileAppDriver();
         if (!$driver) {
             Yii::error('Could not delete token for mobile app. No driver available.', 'fcm-push');
 
@@ -88,19 +56,35 @@ public function actionDeleteMobileApp()
         }
 
         return $this->asJson([
-            'success' => (
-                (new TokenService())->deleteToken(
-                    Yii::$app->request->post('token'),
-                )
+            'success' => $tokenService->deleteToken(
+                Yii::$app->request->post('token'),
             ),
         ]);
     }
 
-    private function requireLogin(): void
+    private function update(bool $mobile)
     {
         if (Yii::$app->user->isGuest) {
             throw new HttpException(401, 'Login required!');
         }
-    }
 
+        $driverService = new DriverService($this->module->getConfigureForm());
+        $tokenService = new TokenService();
+
+        $driver = $mobile ? $driverService->getMobileAppDriver() : $driverService->getWebDriver();
+        if (!$driver) {
+            Yii::error('Could not update token for ' . ($mobile ? 'mobile' : 'web') . ' app. No driver available.', 'fcm-push');
+
+            Yii::$app->response->statusCode = 400;
+            return $this->asJson(['success' => false, 'message' => 'No push driver available!']);
+        }
+
+        return $this->asJson([
+            'success' => $tokenService->storeTokenForUser(
+                Yii::$app->user->getIdentity(),
+                $driver,
+                Yii::$app->request->post('token'),
+            ),
+        ]);
+    }
 }
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index a616ef8..364fb95 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -1,6 +1,10 @@
 Changelog
 =========
 
+2.1.3 (Unreleased)
+-------------------------
+- Fix: Delete DeviceID after Logout
+
 2.1.2 (December 19, 2024)
 -------------------------
 - Fix #262: Switch Network doesn't work 
diff --git a/module.json b/module.json
index 106ab4e..86647bc 100644
--- a/module.json
+++ b/module.json
@@ -11,7 +11,7 @@
     "humhub": {
         "minVersion": "1.14"
     },
-    "version": "2.1.2",
+    "version": "2.1.3",
     "screenshots": [
         "resources/screenshot1.PNG",
         "resources/screenshot2.PNG"

From 141c19fb4090a0c4c346ec4771ae17eb38b7f96b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc=20Farr=C3=A9?= <contact@marc.fun>
Date: Mon, 6 Jan 2025 16:16:11 +0000
Subject: [PATCH 2/3] Implement unregisterNotification

---
 Events.php                      | 24 ++++++++++---------
 assets/FcmPushAsset.php         |  1 +
 config.php                      |  1 -
 controllers/TokenController.php | 42 ++++++++++++++++++++-------------
 docs/CHANGELOG.md               |  2 +-
 helpers/WebAppHelper.php        | 19 +++++++++++++++
 resources/js/humhub.firebase.js | 29 +++++++++++++++++++++++
 views/admin/mobile-app.php      |  5 +++-
 8 files changed, 93 insertions(+), 30 deletions(-)
 create mode 100644 helpers/WebAppHelper.php

diff --git a/Events.php b/Events.php
index 64575eb..d8435f2 100644
--- a/Events.php
+++ b/Events.php
@@ -102,22 +102,28 @@ public static function onLayoutAddonInit($event)
             Yii::$app->session->remove(MobileAppHelper::SESSION_VAR_REGISTER_NOTIFICATION);
         }
 
-        // Before logout
+        // After logout
+        if (Yii::$app->session->has(WebAppHelper::SESSION_VAR_UNREGISTER_NOTIFICATION)) {
+            static::registerAssets();
+            WebAppHelper::unregisterNotificationScript();
+            Yii::$app->session->remove(WebAppHelper::SESSION_VAR_UNREGISTER_NOTIFICATION);
+        }
         if (Yii::$app->session->has(MobileAppHelper::SESSION_VAR_UNREGISTER_NOTIFICATION)) {
             MobileAppHelper::unregisterNotificationScript();
             Yii::$app->session->remove(MobileAppHelper::SESSION_VAR_UNREGISTER_NOTIFICATION);
         }
-
-        // After logout
         if (Yii::$app->session->has(MobileAppHelper::SESSION_VAR_SHOW_OPENER)) {
             MobileAppHelper::registerShowOpenerScript();
             Yii::$app->session->remove(MobileAppHelper::SESSION_VAR_SHOW_OPENER);
         }
 
-        if (Yii::$app->user->isGuest) {
-            return;
+        if (!Yii::$app->user->isGuest) {
+            static::registerAssets();
         }
+    }
 
+    private static function registerAssets()
+    {
         /** @var Module $module */
         $module = Yii::$app->getModule('fcm-push');
 
@@ -135,14 +141,10 @@ public static function onAfterLogin()
         Yii::$app->session->set(MobileAppHelper::SESSION_VAR_REGISTER_NOTIFICATION, 1);
     }
 
-    public static function onBeforeLogout()
-    {
-        Yii::$app->session->set(MobileAppHelper::SESSION_VAR_UNREGISTER_NOTIFICATION, 1);
-        Yii::$app->view->registerJs('humhub.modules.firebase.unregisterNotification();');
-    }
-
     public static function onAfterLogout()
     {
+        Yii::$app->session->set(WebAppHelper::SESSION_VAR_UNREGISTER_NOTIFICATION, 1);
+        Yii::$app->session->set(MobileAppHelper::SESSION_VAR_UNREGISTER_NOTIFICATION, 1);
         Yii::$app->session->set(MobileAppHelper::SESSION_VAR_SHOW_OPENER, 1);
     }
 
diff --git a/assets/FcmPushAsset.php b/assets/FcmPushAsset.php
index 05a476e..4e5cb86 100644
--- a/assets/FcmPushAsset.php
+++ b/assets/FcmPushAsset.php
@@ -39,6 +39,7 @@ public static function register($view)
         if ($pushDriver !== null) {
             Yii::$app->view->registerJsConfig('firebase', [
                 'tokenUpdateUrl' => Url::to(['/fcm-push/token/update']),
+                'tokenDeleteUrl' => Url::to(['/fcm-push/token/delete']),
                 'senderId' => $pushDriver->getSenderId(),
                 'projectId' => $module->getConfigureForm()->getJsonParam('project_id'),
                 'apiKey' => $module->getConfigureForm()->firebaseApiKey,
diff --git a/config.php b/config.php
index 43d60e2..9491c93 100644
--- a/config.php
+++ b/config.php
@@ -23,7 +23,6 @@
         [LayoutAddons::class, LayoutAddons::EVENT_INIT, [Events::class, 'onLayoutAddonInit']],
         [Application::class, Application::EVENT_BEFORE_REQUEST, [Events::class, 'onBeforeRequest']],
         [User::class, User::EVENT_AFTER_LOGIN, [Events::class, 'onAfterLogin']],
-        [User::class, User::EVENT_BEFORE_LOGOUT, [Events::class, 'onBeforeLogout']],
         [User::class, User::EVENT_AFTER_LOGOUT, [Events::class, 'onAfterLogout']],
         [AuthChoice::class, AuthChoice::EVENT_BEFORE_RUN, [Events::class, 'onAuthChoiceBeforeRun']],
         //[NotificationInfoWidget::class, \humhub\widgets\BaseStack::EVENT_RUN, [Events::class, 'onNotificationInfoWidget']],
diff --git a/controllers/TokenController.php b/controllers/TokenController.php
index 4c357b2..d062cd1 100644
--- a/controllers/TokenController.php
+++ b/controllers/TokenController.php
@@ -38,51 +38,61 @@ public function actionUpdateMobileApp()
         return $this->update(true);
     }
 
+    public function actionDelete()
+    {
+        return $this->delete(false);
+    }
+
     public function actionDeleteMobileApp()
     {
+        return $this->delete(true);
+    }
+
+    private function update(bool $mobile)
+    {
+        if (Yii::$app->user->isGuest) {
+            throw new HttpException(401, 'Login required!');
+        }
+
         $driverService = new DriverService($this->module->getConfigureForm());
         $tokenService = new TokenService();
 
-        $driver = $driverService->getMobileAppDriver();
+        $driver = $mobile ? $driverService->getMobileAppDriver() : $driverService->getWebDriver();
         if (!$driver) {
-            Yii::error('Could not delete token for mobile app. No driver available.', 'fcm-push');
+            Yii::error('Could not update token for ' . ($mobile ? 'mobile' : 'web') . ' app. No driver available.', 'fcm-push');
 
             Yii::$app->response->statusCode = 400;
             return $this->asJson(['success' => false, 'message' => 'No push driver available!']);
         }
 
-        if (empty(Yii::$app->request->post('token'))) {
-            return $this->asJson(['success' => false, 'message' => 'No token given!']);
-        }
-
         return $this->asJson([
-            'success' => $tokenService->deleteToken(
+            'success' => $tokenService->storeTokenForUser(
+                Yii::$app->user->getIdentity(),
+                $driver,
                 Yii::$app->request->post('token'),
             ),
         ]);
     }
 
-    private function update(bool $mobile)
+    private function delete(bool $mobile)
     {
-        if (Yii::$app->user->isGuest) {
-            throw new HttpException(401, 'Login required!');
-        }
-
         $driverService = new DriverService($this->module->getConfigureForm());
         $tokenService = new TokenService();
 
         $driver = $mobile ? $driverService->getMobileAppDriver() : $driverService->getWebDriver();
         if (!$driver) {
-            Yii::error('Could not update token for ' . ($mobile ? 'mobile' : 'web') . ' app. No driver available.', 'fcm-push');
+            Yii::error('Could not delete token for ' . ($mobile ? 'mobile' : 'web') . ' app. No driver available.', 'fcm-push');
 
             Yii::$app->response->statusCode = 400;
             return $this->asJson(['success' => false, 'message' => 'No push driver available!']);
         }
 
+        if (empty(Yii::$app->request->post('token'))) {
+            return $this->asJson(['success' => false, 'message' => 'No token given!']);
+        }
+
         return $this->asJson([
-            'success' => $tokenService->storeTokenForUser(
-                Yii::$app->user->getIdentity(),
-                $driver,
+            'success' => $tokenService->deleteToken(
                 Yii::$app->request->post('token'),
             ),
         ]);
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index 364fb95..24742a5 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -3,7 +3,7 @@ Changelog
 
 2.1.3 (Unreleased)
 -------------------------
-- Fix: Delete DeviceID after Logout
+- Fix #32: Delete DeviceID after Logout
 
 2.1.2 (December 19, 2024)
 -------------------------
diff --git a/helpers/WebAppHelper.php b/helpers/WebAppHelper.php
new file mode 100644
index 0000000..b9b1ea3
--- /dev/null
+++ b/helpers/WebAppHelper.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace humhub\modules\fcmPush\helpers;
+
+use Yii;
+
+class WebAppHelper
+{
+    public const SESSION_VAR_UNREGISTER_NOTIFICATION = 'mobileAppUnregisterNotification';
+
+    public static function unregisterNotificationScript()
+    {
+        if (MobileAppHelper::isAppRequest()) {
+            return;
+        }
+
+        Yii::$app->view->registerJs('humhub.modules.firebase.unregisterNotification();');
+    }
+}
diff --git a/resources/js/humhub.firebase.js b/resources/js/humhub.firebase.js
index ecb39aa..c0fc4f2 100644
--- a/resources/js/humhub.firebase.js
+++ b/resources/js/humhub.firebase.js
@@ -64,6 +64,21 @@ humhub.module('firebase', function (module, require, $) {
         }
     };
 
+    const deleteTokenToServer = function (token) {
+        const that = this;
+        if (that.isTokenSentToServer(token)) {
+            module.log.info("Delete FCM Push Token to Server");
+            $.ajax({
+                method: "POST",
+                url: that.tokenDeleteUrl(),
+                data: {token: token},
+                success: function (data) {
+                    that.deleteTokenLocalStore();
+                }
+            });
+        }
+    };
+
     const isTokenSentToServer = function (token) {
         return (this.getTokenLocalStore() === token);
     };
@@ -96,10 +111,21 @@ humhub.module('firebase', function (module, require, $) {
         return item.value;
     };
 
+    const unregisterNotification = function () {
+        const token = this.getTokenLocalStore();
+        if (token) {
+            this.deleteTokenToServer(token);
+        }
+    }
+
     const tokenUpdateUrl = function () {
         return module.config.tokenUpdateUrl;
     };
 
+    const tokenDeleteUrl = function () {
+        return module.config.tokenDeleteUrl;
+    };
+
     const senderId = function () {
         return module.config.senderId;
     };
@@ -109,11 +135,14 @@ humhub.module('firebase', function (module, require, $) {
 
         isTokenSentToServer,
         sendTokenToServer,
+        deleteTokenToServer,
         afterServiceWorkerRegistration,
+        unregisterNotification,
 
         // Config Vars
         senderId,
         tokenUpdateUrl,
+        tokenDeleteUrl,
 
         // LocalStore Helper
         setTokenLocalStore,
diff --git a/views/admin/mobile-app.php b/views/admin/mobile-app.php
index e043837..a1297b4 100644
--- a/views/admin/mobile-app.php
+++ b/views/admin/mobile-app.php
@@ -86,7 +86,7 @@
                 <h4>Registered FireBase Devices (Current User)</h4>
 
                 <?php
-                $tokens = FcmUser::findAll(['user_id' => Yii::$app->user->id]);
+                $tokens = FcmUser::find()->where(['user_id' => Yii::$app->user->id])->orderBy('created_at DESC')->all();
                 ?>
 
                 <?php if (count($tokens) === 0): ?>
@@ -105,8 +105,11 @@
 
                             &middot;
                             <?= $fcm->sender_id ?>
+
                             &middot;
+                            <?= Yii::$app->formatter->asDatetime($fcm->created_at, 'short') ?>
 
+                            &middot;
                             <?= Html::a('Delete', ['mobile-app', 'deleteToken' => $fcm->id, 'confirm' => 'PWA: You may need to delete token from localStorage to trigger resave!']) ?>
                         </li>
                     <?php endforeach; ?>

From 7356b594d719e102b8d0d5ef97d91ec56cd6dd63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc=20Farr=C3=A9?= <contact@marc.fun>
Date: Mon, 6 Jan 2025 19:07:17 +0000
Subject: [PATCH 3/3] 
 https://github.com/humhub/fcm-push/pull/63#discussion_r1904417431

---
 controllers/TokenController.php | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/controllers/TokenController.php b/controllers/TokenController.php
index d062cd1..3a59a6d 100644
--- a/controllers/TokenController.php
+++ b/controllers/TokenController.php
@@ -30,25 +30,25 @@ public function beforeAction($action)
 
     public function actionUpdate()
     {
-        return $this->update(false);
+        return $this->update(false, Yii::$app->request->post('token'));
     }
 
     public function actionUpdateMobileApp()
     {
-        return $this->update(true);
+        return $this->update(true, Yii::$app->request->post('token'));
     }
 
     public function actionDelete()
     {
-        return $this->delete(false);
+        return $this->delete(false, Yii::$app->request->post('token'));
     }
 
     public function actionDeleteMobileApp()
     {
-        return $this->delete(true);
+        return $this->delete(true, Yii::$app->request->post('token'));
     }
 
-    private function update(bool $mobile)
+    private function update(bool $mobile, ?string $token)
     {
         if (Yii::$app->user->isGuest) {
             throw new HttpException(401, 'Login required!');
@@ -69,12 +69,12 @@ private function update(bool $mobile)
             'success' => $tokenService->storeTokenForUser(
                 Yii::$app->user->getIdentity(),
                 $driver,
-                Yii::$app->request->post('token'),
+                $token,
             ),
         ]);
     }
 
-    private function delete(bool $mobile)
+    private function delete(bool $mobile, ?string $token)
     {
         $driverService = new DriverService($this->module->getConfigureForm());
         $tokenService = new TokenService();
@@ -92,9 +92,7 @@ private function delete(bool $mobile)
         }
 
         return $this->asJson([
-            'success' => $tokenService->deleteToken(
-                Yii::$app->request->post('token'),
-            ),
+            'success' => $tokenService->deleteToken($token),
         ]);
     }
 }