Overhauled notification system behavior
authorAlexander Ebert <ebert@woltlab.com>
Wed, 26 Nov 2014 19:21:17 +0000 (20:21 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 27 Nov 2014 01:24:27 +0000 (02:24 +0100)
13 files changed:
com.woltlab.wcf/templates/notificationList.tpl
com.woltlab.wcf/templates/notificationListOustanding.tpl
com.woltlab.wcf/templates/userPanel.tpl
com.woltlab.wcf/update_2.1.0_alpha_1.sql
wcfsetup/install/files/js/WCF.User.js
wcfsetup/install/files/lib/data/user/notification/UserNotificationAction.class.php
wcfsetup/install/files/lib/data/user/notification/UserNotificationEditor.class.php
wcfsetup/install/files/lib/page/NotificationListPage.class.php
wcfsetup/install/files/lib/system/user/notification/UserNotificationHandler.class.php
wcfsetup/install/files/lib/system/user/notification/event/AbstractUserNotificationEvent.class.php
wcfsetup/install/files/style/dropdown.less
wcfsetup/install/files/style/user.less
wcfsetup/setup/db/install.sql

index 44913d827f7507a240b59b7ff99e843a2efb4ac1..6445f4444266dc8e1afb4567e525104938ef66d6 100644 (file)
@@ -7,20 +7,11 @@
        <script data-relocate="true">
                //<![CDATA[
                $(function() {
-                       $('.contentNavigation .jsMarkAllAsConfirmed').click(function() {
-                               WCF.System.Confirmation.show(WCF.Language.get('wcf.user.notification.markAllAsConfirmed.confirmMessage'), function(action) {
-                                       if (action === 'confirm') {
-                                               new WCF.Action.Proxy({
-                                                       autoSend: true,
-                                                       data: {
-                                                               actionName: 'markAllAsConfirmed',
-                                                               className: 'wcf\\data\\user\\notification\\UserNotificationAction'
-                                                       },
-                                                       success: function() { window.location.reload(); }
-                                               });
-                                       }
-                               });
+                       WCF.Language.addObject({
+                               'wcf.user.notification.markAsConfirmed': '{lang}wcf.user.notification.markAsConfirmed{/lang}'
                        });
+                       
+                       new WCF.Notification.List();
                });
                //]]>
        </script>
                        </header>
                        
                        <div class="container marginTop">
-                               <ul class="containerList"{* id="userNotificationItemList"*}>
+                               <ul class="containerList userNotificationItemList">
                {/if}
-                               <li class="jsNotificationItem{if $notification[authors] > 1} groupedNotificationItem{/if}" data-notification-id="{@$notification[notificationID]}" data-link="{$notification[event]->getLink()}" data-is-grouped="{if $notification[authors] > 1}true{else}false{/if}">
+                               <li class="jsNotificationItem notificationItem{if $notification[authors] > 1} groupedNotificationItem{/if}" data-notification-id="{@$notification[notificationID]}" data-link="{$notification[event]->getLink()}" data-is-grouped="{if $notification[authors] > 1}true{else}false{/if}" data-is-confirmed="{if $notification[event]->isConfirmed()}true{else}false{/if}">
                                        <div class="box24">
                                                {if $notification[authors] < 2}
-                                                       {if $notification[event]->getAuthor()->userID}
-                                                               <a href="{link controller='User' object=$notification[event]->getAuthor()}{/link}" title="{$notification[event]->getAuthor()->username}" class="framed">{@$notification[event]->getAuthor()->getAvatar()->getImageTag(24)}</a>
-                                                       {else}
-                                                               <span class="framed">{@$notification[event]->getAuthor()->getAvatar()->getImageTag(24)}</span>
-                                                       {/if}   
+                                                       <div class="framed">
+                                                               {@$notification[event]->getAuthor()->getAvatar()->getImageTag(24)}
+                                                       </div>
                                                        
                                                        <div class="details">
                                                                <p>
@@ -89,7 +78,9 @@
                                                                <p><small>{@$notification[time]|time}</small></p>
                                                        </div>
                                                {else}
-                                                       <span class="icon icon24 fa-users"></span>
+                                                       <div class="framed">
+                                                               <span class="icon icon24 fa-users"></span>
+                                                       </div>
                                                        
                                                        <div class="details">
                                                                <p>
index 447d36da7865ff932b74bce137f6d2bc008c5f44..4dd91b735cc2a90427f99bfc0ee90cf7ce52d60c 100644 (file)
@@ -1,15 +1,13 @@
 {foreach from=$notifications[notifications] item=notification}
-       <li class="jsNotificationItem notificationItem{if $notification[event]->getAuthors()|count > 1} groupedNotificationItem{/if}" data-link="{$notification[event]->getLink()}" data-notification-id="{@$notification[notificationID]}">
+       <li class="jsNotificationItem notificationItem{if $notification[event]->getAuthors()|count > 1} groupedNotificationItem{/if}" data-link="{$notification[event]->getLink()}" data-notification-id="{@$notification[notificationID]}" data-is-confirmed="{if $notification[event]->isConfirmed()}true{else}false{/if}">
                <span class="box24">
-                       {if $notification[event]->getAuthors()|count < 2}
-                               <div class="framed">
+                       <div class="framed">
+                               {if $notification[event]->getAuthors()|count < 2}
                                        {@$notification[event]->getAuthor()->getAvatar()->getImageTag(24)}
-                               </div>
-                       {else}
-                               <div>
+                               {else}
                                        <span class="icon icon24 fa-users"></span>
-                               </div>
-                       {/if}
+                               {/if}
+                       </div>
                        
                        <div>
                                <h3>{if !$notification[event]->isConfirmed()}<span class="badge label newContentBadge">{lang}wcf.message.new{/lang}</span>{/if} {@$notification[event]->getMessage()}</h3>
index 66958478c939e2a69642736c3bc2e3c9b56a1c2f..a1adbde952e812892975675fd38bfced90af2983 100644 (file)
@@ -37,6 +37,7 @@
                                        $(function() {
                                                WCF.Language.addObject({
                                                        'wcf.user.notification.count': '{lang}wcf.user.notification.count{/lang}',
+                                                       'wcf.user.notification.markAsConfirmed': '{lang}wcf.user.notification.markAsConfirmed{/lang}',
                                                        'wcf.user.notification.markAllAsConfirmed': '{lang}wcf.user.notification.markAllAsConfirmed{/lang}',
                                                        'wcf.user.notification.markAllAsConfirmed.confirmMessage': '{lang}wcf.user.notification.markAllAsConfirmed.confirmMessage{/lang}',
                                                        'wcf.user.notification.noMoreNotifications': '{lang}wcf.user.notification.noMoreNotifications{/lang}',
index 3ef6563d0f867e60b396042bad949103c5a30c9b..410eca00b8777f91fb1acb24bd35860f67562a2c 100644 (file)
@@ -213,9 +213,10 @@ ALTER TABLE wcf1_user_notification ADD timesTriggered INT(10) NOT NULL DEFAULT 0
 ALTER TABLE wcf1_user_notification ADD guestTimesTriggered INT(10) NOT NULL DEFAULT 0;
 ALTER TABLE wcf1_user_notification ADD userID INT(10) NOT NULL;
 ALTER TABLE wcf1_user_notification ADD mailNotified TINYINT(1) NOT NULL DEFAULT 0;
-ALTER TABLE wcf1_user_notification ADD confirmed TINYINT(1) NOT NULL DEFAULT 0;
+ALTER TABLE wcf1_user_notification ADD confirmTime INT(10) NOT NULL DEFAULT 0;
 ALTER TABLE wcf1_user_notification ADD baseObjectID INT(10) NOT NULL DEFAULT 0;
-ALTER TABLE wcf1_user_notification ADD KEY (userID, eventID, objectID, confirmed);
+ALTER TABLE wcf1_user_notification ADD KEY (userID, eventID, objectID, confirmTime);
+ALTER TABLE wcf1_user_notification ADD KEY (userID, confirmTime);
 
 ALTER TABLE wcf1_user_notification_to_user DROP mailNotified;
 
index ba6c52a2c9d8a78e3050defa31a949c9c6db9412..7b6eda322a67b79de10f03116545de2c5f0184b1 100644 (file)
@@ -1249,7 +1249,97 @@ WCF.User.Registration.LostPassword = Class.extend({
  * @copyright  2001-2014 WoltLab GmbH
  * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  */
-WCF.Notification = {};
+WCF.Notification = { };
+
+/**
+ * Handles the notification list.
+ */
+WCF.Notification.List = Class.extend({
+       /**
+        * action proxy
+        * @var WCF.Action.Proxy
+        */
+       _proxy: null,
+       
+       /**
+        * Initializes the WCF.Notification.List object.
+        */
+       init: function() {
+               this._proxy = new WCF.Action.Proxy({
+                       success: $.proxy(this._success, this)
+               });
+               
+               // handle 'mark all as confirmed' buttons
+               $('.contentNavigation .jsMarkAllAsConfirmed').click(function() {
+                       WCF.System.Confirmation.show(WCF.Language.get('wcf.user.notification.markAllAsConfirmed.confirmMessage'), function(action) {
+                               if (action === 'confirm') {
+                                       new WCF.Action.Proxy({
+                                               autoSend: true,
+                                               data: {
+                                                       actionName: 'markAllAsConfirmed',
+                                                       className: 'wcf\\data\\user\\notification\\UserNotificationAction'
+                                               },
+                                               success: function() { window.location.reload(); }
+                                       });
+                               }
+                       });
+               });
+               
+               // handle regular items
+               this._convertList();
+       },
+       
+       /**
+        * Converts the notification item list to be in sync with the notification dropdown.
+        */
+       _convertList: function() {
+               $('.userNotificationItemList > .notificationItem').each((function(index, item) {
+                       var $item = $(item);
+                       
+                       if (!$.browser.mobile && !$item.data('isConfirmed')) {
+                               var $markAsConfirmed = $('<a href="#" class="icon icon24 fa-check green notificationItemMarkAsConfirmed jsTooltip" title="' + WCF.Language.get('wcf.user.notification.markAsConfirmed') + '" />').prependTo($item.find('> div.box24 > .framed'));
+                               $markAsConfirmed.click($.proxy(this._markAsConfirmed, this));
+                       }
+               }).bind(this));
+               
+               WCF.DOMNodeInsertedHandler.execute();
+       },
+       
+       /**
+        * Marks a single notification as confirmed.
+        * 
+        * @param       object          event
+        */
+       _markAsConfirmed: function(event) {
+               event.preventDefault();
+               
+               var $notificationID = $(event.currentTarget).parents('.notificationItem:eq(0)').data('notificationID');
+               
+               this._proxy.setOption('data', {
+                       actionName: 'markAsConfirmed',
+                       className: 'wcf\\data\\user\\notification\\UserNotificationAction',
+                       objectIDs: [ $notificationID ]
+               });
+               this._proxy.sendRequest();
+               
+               return false;
+       },
+       
+       /**
+        * Handles successful AJAX requests.
+        * 
+        * @param       object          data
+        * @param       string          textStatus
+        * @param       jQuery          jqXHR
+        */
+       _success: function(data, textStatus, jqXHR) {
+               var $item = $('.userNotificationItemList > .notificationItem[data-notification-id=' + data.returnValues.notificationID + ']');
+               
+               $item.data('isConfirmed', true);
+               $item.find('.notificationItemMarkAsConfirmed').remove();
+               $item.find('.newContentBadge').remove();
+       }
+});
 
 /**
  * Loads notification for the user panel.
@@ -1333,12 +1423,18 @@ WCF.Notification.UserPanel = WCF.UserPanel.extend({
                        var $badge = this._container.find('.badge');
                        if ($badge.length && parseInt($badge.text()) > 0) {
                                var $dropdownMenu = WCF.Dropdown.getDropdownMenu(this._container.wcfIdentify());
-                               if (!$dropdownMenu.is(':visible')) {
-                                       $dropdownMenu.children('li.jsNotificationItem').remove();
-                                       
-                                       $('<li class="jsDropdownPlaceholder"><span>' + WCF.Language.get('wcf.global.loading') + '</span></li>').prependTo($dropdownMenu);
+                               
+                               // check if there is at least one unconfirmed item
+                               var $count = 0;
+                               $dropdownMenu.children('li.jsNotificationItem').each(function() {
+                                       if (!$(this).data('isConfirmed')) {
+                                               $count++;
+                                       }
+                               });
+                               
+                               if (!$count && $count != $badge.text() && !$dropdownMenu.is(':visible')) {
+                                       this._resetList();
                                        
-                                       this._didLoad = false;
                                        this._super(event);
                                }
                        }
@@ -1354,7 +1450,7 @@ WCF.Notification.UserPanel = WCF.UserPanel.extend({
        _after: function(dropdownMenu) {
                var $items = WCF.Dropdown.getDropdownMenu(this._container.wcfIdentify()).children('li.jsNotificationItem');
                
-               $items.each(function(index, item) {
+               $items.each((function(index, item) {
                        var $item = $(item);
                        
                        if (!$.browser.msie) {
@@ -1362,12 +1458,37 @@ WCF.Notification.UserPanel = WCF.UserPanel.extend({
                                $('<a href="' + $item.data('link') + '" />').appendTo($item);
                        }
                        
+                       if (!$.browser.mobile && !$item.data('isConfirmed')) {
+                               var $markAsConfirmed = $('<a href="#" class="icon icon24 fa-check green notificationItemMarkAsConfirmed jsTooltip" title="' + WCF.Language.get('wcf.user.notification.markAsConfirmed') + '" />').prependTo($item.find('> span.box24 > .framed'));
+                               $markAsConfirmed.click($.proxy(this._markAsConfirmed, this));
+                       }
+                       
                        $item.click(function(event) {
                                if (event.target.tagName !== 'A') {
                                        window.location = $item.data('link');
                                }
                        });
+               }).bind(this));
+       },
+       
+       /**
+        * Marks a notification as confirmed.
+        * 
+        * @param       object          event
+        */
+       _markAsConfirmed: function(event) {
+               event.preventDefault();
+               
+               var $notificationID = $(event.currentTarget).parents('.notificationItem:eq(0)').data('notificationID');
+               
+               this._proxy.setOption('data', {
+                       actionName: 'markAsConfirmed',
+                       className: 'wcf\\data\\user\\notification\\UserNotificationAction',
+                       objectIDs: [ $notificationID ]
                });
+               this._proxy.sendRequest();
+               
+               return false;
        },
        
        /**
@@ -1390,8 +1511,23 @@ WCF.Notification.UserPanel = WCF.UserPanel.extend({
         */
        _success: function(data, textStatus, jqXHR) {
                switch (data.actionName) {
+                       case 'markAsConfirmed':
+                               WCF.Dropdown.getDropdownMenu(this._container.wcfIdentify()).children('li.jsNotificationItem').each(function(index, item) {
+                                       var $item = $(item);
+                                       if ($item.data('notificationID') == data.returnValues.notificationID) {
+                                               $item.data('isConfirmed', true);
+                                               $item.find('.notificationItemMarkAsConfirmed').remove();
+                                               $item.find('.newContentBadge').remove();
+                                               
+                                               return false;
+                                       }
+                               });
+                               
+                               this._updateBadge(data.returnValues.totalCount);
+                       break;
+                       
                        case 'markAllAsConfirmed':
-                               $('.jsNotificationItem').remove();
+                               this._resetList();
                        // fall through
                        case 'getOutstandingNotifications':
                                if (!data.returnValues || !data.returnValues.template) {
@@ -1417,6 +1553,18 @@ WCF.Notification.UserPanel = WCF.UserPanel.extend({
                }
        },
        
+       /**
+        * Resets the notification list to its initial state.
+        */
+       _resetList: function() {
+               var $dropdownMenu = WCF.Dropdown.getDropdownMenu(this._container.wcfIdentify());
+               $dropdownMenu.children('li.jsNotificationItem').remove();
+               
+               $('<li class="jsDropdownPlaceholder"><span>' + WCF.Language.get('wcf.global.loading') + '</span></li>').prependTo($dropdownMenu);
+               
+               this._didLoad = false;
+       },
+       
        /**
         * Updates user notification count.
         * 
index 93505601d72944465a5a3b2e9e73c7e03f314b3f..27e8118b8d45dcfa840032a08de7a32b7ded79ae 100644 (file)
@@ -5,6 +5,7 @@ use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\user\notification\UserNotificationHandler;
 use wcf\system\user\storage\UserStorageHandler;
 use wcf\system\WCF;
+use wcf\system\exception\PermissionDeniedException;
 
 /**
  * Executes user notification-related actions.
@@ -18,10 +19,10 @@ use wcf\system\WCF;
  */
 class UserNotificationAction extends AbstractDatabaseObjectAction {
        /**
-        * notification object
-        * @var \wcf\data\user\notification\UserNotification
+        * notification editor object
+        * @var \wcf\data\user\notification\UserNotificationEditor
         */
-       public $notification = null;
+       public $notificationEditor = null;
        
        /**
         * @see \wcf\data\AbstractDatabaseObjectAction::create()
@@ -88,7 +89,7 @@ class UserNotificationAction extends AbstractDatabaseObjectAction {
                $notificationList->getConditionBuilder()->add("eventID = ?", array($this->parameters['data']['eventID']));
                $notificationList->getConditionBuilder()->add("eventHash = ?", array($this->parameters['data']['eventHash']));
                $notificationList->getConditionBuilder()->add("userID IN (?)", array(array_keys($this->parameters['recipients'])));
-               $notificationList->getConditionBuilder()->add("confirmed = ?", array(0));
+               $notificationList->getConditionBuilder()->add("confirmTime = ?", array(0));
                $notificationList->readObjects();
                $existingNotifications = array();
                foreach ($notificationList as $notification) {
@@ -166,40 +167,36 @@ class UserNotificationAction extends AbstractDatabaseObjectAction {
                        'notifications' => $notifications
                ));
                
-               $markAsConfirmed = array();
-               foreach ($notifications['notifications'] as $notification) {
-                       if (!$notification['event']->isConfirmed()) {
-                               $markAsConfirmed[] = $notification['notificationID'];
-                       }
-               }
-               
-               if (!empty($markAsConfirmed)) {
-                       $conditions = new PreparedStatementConditionBuilder();
-                       $conditions->add("notificationID IN (?)", array($markAsConfirmed));
-                       
-                       // mark notifications as confirmed
-                       $sql = "UPDATE  wcf".WCF_N."_user_notification
-                               SET     confirmed = 1
-                               ".$conditions;
-                       $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute($conditions->getParameters());
-                       
-                       // delete notification_to_user assignments (mimic legacy notification system)
-                       $sql = "DELETE FROM     wcf".WCF_N."_user_notification_to_user
-                               ".$conditions;
-                       $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute($conditions->getParameters());
-                       
-                       // reset user storage
-                       UserStorageHandler::getInstance()->reset(array(WCF::getUser()->userID), 'userNotificationCount');
-               }
-               
                return array(
                        'template' => WCF::getTPL()->fetch('notificationListOustanding'),
                        'totalCount' => $notifications['notificationCount']
                );
        }
        
+       /**
+        * Validates parameters to mark a notification as confirmed.
+        */
+       public function validateMarkAsConfirmed() {
+               $this->notificationEditor = $this->getSingleObject();
+               if ($this->notificationEditor->userID != WCF::getUser()->userID) {
+                       throw new PermissionDeniedException();
+               }
+       }
+       
+       /**
+        * Marks a notification as confirmed.
+        * 
+        * @return      array
+        */
+       public function markAsConfirmed() {
+               UserNotificationHandler::getInstance()->markAsConfirmedByID($this->notificationEditor->notificationID);
+               
+               return array(
+                       'notificationID' => $this->notificationEditor->notificationID,
+                       'totalCount' => UserNotificationHandler::getInstance()->getNotificationCount(true)
+               );
+       }
+       
        /**
         * Validates parameters to mark all notifications of current user as confirmed.
         */
@@ -211,14 +208,20 @@ class UserNotificationAction extends AbstractDatabaseObjectAction {
        public function markAllAsConfirmed() {
                // remove notifications for this user
                $sql = "UPDATE  wcf".WCF_N."_user_notification
-                       SET     confirmed = ?
+                       SET     confirmTime = ?
                        WHERE   userID = ?";
                $statement = WCF::getDB()->prepareStatement($sql);
                $statement->execute(array(
-                       1,
+                       TIME_NOW,
                        WCF::getUser()->userID
                ));
                
+               // delete notification_to_user assignments (mimic legacy notification system)
+               $sql = "DELETE FROM     wcf".WCF_N."_user_notification_to_user
+                       WHERE           userID = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute(array(WCF::getUser()->userID));
+               
                // reset notification count
                UserStorageHandler::getInstance()->reset(array(WCF::getUser()->userID), 'userNotificationCount');
        }
index 37715e65d3c215f71c3bcd1990d76cd5ed5e6ebe..fd3b22287ff53c7e40a8145ce669c1fc5c9640e1 100644 (file)
@@ -24,7 +24,7 @@ class UserNotificationEditor extends DatabaseObjectEditor {
         */
        public function markAsConfirmed() {
                $this->update(array(
-                       'confirmed' => 1,
+                       'confirmTime' => TIME_NOW,
                        'mailNotified' => 1
                ));
                
index 7ac30e3ebe03ede010a287dbf4ace0c8535141ba..ec59942b05a0e9b60c755016bdc2b9ee1555c7dd 100644 (file)
@@ -1,9 +1,7 @@
 <?php
 namespace wcf\page;
-use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\menu\user\UserMenu;
 use wcf\system\user\notification\UserNotificationHandler;
-use wcf\system\user\storage\UserStorageHandler;
 use wcf\system\WCF;
 
 /**
@@ -52,34 +50,6 @@ class NotificationListPage extends MultipleLinkPage {
                parent::readData();
                
                $this->notifications = UserNotificationHandler::getInstance()->getNotifications($this->sqlLimit, $this->sqlOffset, true);
-               
-               $markAsConfirmed = array();
-               foreach ($this->notifications['notifications'] as $notification) {
-                       if (!$notification['event']->isConfirmed()) {
-                               $markAsConfirmed[] = $notification['notificationID'];
-                       }
-               }
-               
-               if (!empty($markAsConfirmed)) {
-                       $conditions = new PreparedStatementConditionBuilder();
-                       $conditions->add("notificationID IN (?)", array($markAsConfirmed));
-                       
-                       // mark notifications as confirmed
-                       $sql = "UPDATE  wcf".WCF_N."_user_notification
-                               SET     confirmed = 1
-                               ".$conditions;
-                       $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute($conditions->getParameters());
-                       
-                       // delete notification_to_user assignments (mimic legacy notification system)
-                       $sql = "DELETE FROM     wcf".WCF_N."_user_notification_to_user
-                               ".$conditions;
-                       $statement = WCF::getDB()->prepareStatement($sql);
-                       $statement->execute($conditions->getParameters());
-                       
-                       // reset user storage
-                       UserStorageHandler::getInstance()->reset(array(WCF::getUser()->userID), 'userNotificationCount');
-               }
        }
        
        /**
index d2cc14fc185db7b68176457f51bfea606c977649..30cbc19d575bc3136b50947d6ad9223639313c71 100644 (file)
@@ -110,7 +110,7 @@ class UserNotificationHandler extends SingletonFactory {
                $conditions->add("userID IN (?)", array($recipientIDs));
                $conditions->add("eventID = ?", array($event->eventID));
                $conditions->add("eventHash = ?", array($event->getEventHash()));
-               $conditions->add("confirmed = ?", array(0));
+               $conditions->add("confirmTime = ?", array(0));
                
                $sql = "SELECT  notificationID, userID
                        FROM    wcf".WCF_N."_user_notification
@@ -246,7 +246,7 @@ class UserNotificationHandler extends SingletonFactory {
                                        $sql = "SELECT  COUNT(*) AS count
                                                FROM    wcf".WCF_N."_user_notification
                                                WHERE   userID = ?
-                                                       AND confirmed = ?";
+                                                       AND confirmTime = ?";
                                        $statement = WCF::getDB()->prepareStatement($sql);
                                        $statement->execute(array(
                                                WCF::getUser()->userID,
@@ -323,7 +323,7 @@ class UserNotificationHandler extends SingletonFactory {
                }
                
                $returnValues = $this->processNotifications($notifications, true);
-               $returnValues['notificationCount'] = max($notificationCount - $count, 0);
+               $returnValues['notificationCount'] = $notificationCount;
                
                return $returnValues;
        }
@@ -344,7 +344,13 @@ class UserNotificationHandler extends SingletonFactory {
                $orderBy = 'notification.time DESC';
                if ($filterByConfirmed !== null) {
                        // fetch the oldest, unconfirmed notifications, order will be reversed using PHP
-                       $conditions->add("notification.confirmed = ?", array($filterByConfirmed));
+                       if ($filterByConfirmed == 0) {
+                               $conditions->add("notification.confirmTime = ?", array(0));
+                       }
+                       else {
+                               // consider only notifications marked as confirmed in the past 48 hours (86400 = 1 day)
+                               $conditions->add("notification.confirmTime >= ?", array(TIME_NOW - (2 * 86400)));
+                       }
                        
                        if ($filterByConfirmed = 0) {
                                $orderBy = 'notification.time ASC';
@@ -479,7 +485,7 @@ class UserNotificationHandler extends SingletonFactory {
                                'time' => $notification->time
                        );
                        
-                       $data['confirmed'] = $notification->confirmed;
+                       $data['confirmed'] = ($notification->confirmTime > 0);
                        
                        $notifications[] = $data;
                }
@@ -748,11 +754,11 @@ class UserNotificationHandler extends SingletonFactory {
                if (!empty($objectIDs)) $conditions->add("objectID IN (?)", array($objectIDs));
                
                $sql = "UPDATE  wcf".WCF_N."_user_notification
-                       SET     confirmed = ?
+                       SET     confirmTime = ?
                        ".$conditions;
                $statement = WCF::getDB()->prepareStatement($sql);
                $parameters = $conditions->getParameters();
-               array_unshift($parameters, 1);
+               array_unshift($parameters, TIME_NOW);
                $statement->execute($parameters);
                
                // delete notification_to_user assignments (mimic legacy notification system)
@@ -760,7 +766,7 @@ class UserNotificationHandler extends SingletonFactory {
                        WHERE           notificationID NOT IN (
                                                SELECT  notificationID
                                                FROM    wcf".WCF_N."_user_notification
-                                               WHERE   confirmed = ?
+                                               WHERE   confirmTime = ?
                                        )";
                $statement = WCF::getDB()->prepareStatement($sql);
                $statement->execute(array(0));
@@ -774,6 +780,47 @@ class UserNotificationHandler extends SingletonFactory {
                }
        }
        
+       /**
+        * Marks a single notification id as confirmed.
+        * 
+        * @param       integer         $notificationID
+        */
+       public function markAsConfirmedByID($notificationID) {
+               $this->markAsConfirmedByIDs(array($notificationID));
+       }
+       
+       /**
+        * Marks a list of notification ids as confirmed.
+        * 
+        * @param       array<integer>  $notificationIDs
+        */
+       public function markAsConfirmedByIDs(array $notificationIDs) {
+               if (empty($notificationIDs)) {
+                       return;
+               }
+               
+               $conditions = new PreparedStatementConditionBuilder();
+               $conditions->add("notificationID IN (?)", array($notificationIDs));
+               
+               // mark notifications as confirmed
+               $sql = "UPDATE  wcf".WCF_N."_user_notification
+                       SET     confirmTime = ?
+                       ".$conditions;
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $parameters = $conditions->getParameters();
+               array_unshift($parameters, TIME_NOW);
+               $statement->execute($parameters);
+               
+               // delete notification_to_user assignments (mimic legacy notification system)
+               $sql = "DELETE FROM     wcf".WCF_N."_user_notification_to_user
+                       ".$conditions;
+               $statement = WCF::getDB()->prepareStatement($sql);
+               $statement->execute($conditions->getParameters());
+               
+               // reset user storage
+               UserStorageHandler::getInstance()->reset(array(WCF::getUser()->userID), 'userNotificationCount');
+       }
+       
        /**
         * Returns the user's notification setting for the given event.
         * 
index a0dcac17eb9cb82a6793f3f78d0bae6ab39547cf..aefc2633d2c77a96fd2f8df029bc80ba03e80d81 100644 (file)
@@ -244,7 +244,7 @@ abstract class AbstractUserNotificationEvent extends DatabaseObjectDecorator imp
         * @see \wcf\system\user\notification\event\IUserNotificationEvent::isConfirmed()
         */
        public function isConfirmed() {
-               return ($this->notification->confirmed == 1);
+               return ($this->notification->confirmTime > 0);
        }
        
        /**
index 4a6f5e6486ce49d060ed4c0e6a469dbdb295d431..4c68aa1e5f0fd563ca524402d4bafece64ed6996 100644 (file)
                
                &.notificationItem {
                        &.groupedNotificationItem > span {
-                               > div:first-child {
-                                       padding: 4px 2px 2px;
+                               > .framed > span.fa-users:before {
+                                       position: relative;
+                                       top: 3px;
                                }
                                
                                > div + div {
                                }
                        }
                        
+                       &:hover > span > .framed {
+                               > .notificationItemMarkAsConfirmed {
+                                       height: 24px;   
+                                       display: block;
+                                       width: 24px;
+                                       
+                                       &:before {
+                                               position: relative;
+                                               top: 3px;
+                                       }
+                               }
+                               
+                               > .notificationItemMarkAsConfirmed ~ img,
+                               > .notificationItemMarkAsConfirmed ~ .fa-users {
+                                       display: none;
+                               }
+                       }
+                       
                        > span {
                                cursor: pointer;
                                white-space: normal;
+                               
+                               > .framed > .notificationItemMarkAsConfirmed {
+                                       display: none;
+                               }
                        }
                }
        }
index a4e32b62a675b10e8b48f1585e402342cab6ef77..d57c58236f6ff7e4244fced584b5aa011494a8b8 100644 (file)
 }
 
 /* user notifications */
-#userNotificationItemList > .groupedNotificationItem p > a {
-       font-weight: bold;
+.userNotificationItemList > .notificationItem {
+       &.groupedNotificationItem > div > .framed > span.fa-users:before {
+               position: relative;
+               top: 3px;
+       }
+       
+       &:hover > div > .framed {
+               > .notificationItemMarkAsConfirmed {
+                       height: 24px;   
+                       display: block;
+                       width: 24px;
+                       
+                       &:before {
+                               position: relative;
+                               top: 3px;
+                       }
+               }
+               
+               > .notificationItemMarkAsConfirmed ~ img,
+               > .notificationItemMarkAsConfirmed ~ .fa-users {
+                       display: none;
+               }
+       }
+       
+       > div > .framed > .notificationItemMarkAsConfirmed {
+               display: none;
+       }
 }
 
 .paidSubscriptionTeaserList {
index aa832846a4db12114f4ea8ce46c85b1b32701d6e..f84adb707dbfed9c1a7697e76ad72a05962b9b22 100644 (file)
@@ -1293,9 +1293,10 @@ CREATE TABLE wcf1_user_notification (
        userID INT(10) NOT NULL,
        time INT(10) NOT NULL DEFAULT 0,
        mailNotified TINYINT(1) NOT NULL DEFAULT 0,
-       confirmed TINYINT(1) NOT NULL DEFAULT 0,
+       confirmTime INT(10) NOT NULL DEFAULT 0,
        additionalData TEXT,
-       KEY (userID, eventID, objectID, confirmed)
+       KEY (userID, eventID, objectID, confirmTime),
+       KEY (userID, confirmTime)
 );
 
 -- notification authors (stacking)