Improved notifications, now counting trigger count
authorAlexander Ebert <ebert@woltlab.com>
Thu, 19 Jun 2014 16:17:56 +0000 (18:17 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 19 Jun 2014 16:17:56 +0000 (18:17 +0200)
wcfsetup/install/files/lib/data/user/notification/UserNotificationAction.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/lib/system/user/notification/event/IUserNotificationEvent.class.php
wcfsetup/install/files/lib/system/user/notification/event/UserFollowFollowingUserNotificationEvent.class.php
wcfsetup/setup/db/install.sql

index ca46ccd38e816bda2420db72a2ae0f1418c4937c..ec11526ad1564ff769869c95e3a191e421ab803a 100644 (file)
@@ -86,7 +86,7 @@ class UserNotificationAction extends AbstractDatabaseObjectAction {
                // get existing notifications
                $notificationList = new UserNotificationList();
                $notificationList->getConditionBuilder()->add("eventID = ?", array($this->parameters['data']['eventID']));
-               $notificationList->getConditionBuilder()->add("objectID = ?", array($this->parameters['data']['objectID']));
+               $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->readObjects();
@@ -127,6 +127,21 @@ class UserNotificationAction extends AbstractDatabaseObjectAction {
                }
                WCF::getDB()->commitTransaction();
                
+               // update trigger count
+               $sql = "UPDATE  wcf".WCF_N."_user_notification
+                       SET     timesTriggered = timesTriggered + ?
+                       WHERE   notificationID = ?";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               
+               WCF::getDB()->beginTransaction();
+               foreach ($notifications as $notificationData) {
+                       $statement->execute(array(
+                               1,
+                               $notificationData['object']->notificationID
+                       ));
+               }
+               WCF::getDB()->commitTransaction();
+               
                return $notifications;
        }
        
index bd51ddee2b16a6ef33d6f9c90ced00bbf4264f95..344964ed6ad45a106e62b23fc648268a075bd0f7 100644 (file)
@@ -14,7 +14,6 @@ use wcf\system\database\util\PreparedStatementConditionBuilder;
 use wcf\system\exception\SystemException;
 use wcf\system\mail\Mail;
 use wcf\system\user\notification\event\IUserNotificationEvent;
-use wcf\system\user\notification\object\IStackableUserNotificationObject;
 use wcf\system\user\notification\object\IUserNotificationObject;
 use wcf\system\user\storage\UserStorageHandler;
 use wcf\system\SingletonFactory;
@@ -110,7 +109,7 @@ class UserNotificationHandler extends SingletonFactory {
                $conditions = new PreparedStatementConditionBuilder();
                $conditions->add("userID IN (?)", array($recipientIDs));
                $conditions->add("eventID = ?", array($event->eventID));
-               $conditions->add("objectID = ?", array($notificationObject->getObjectID()));
+               $conditions->add("eventHash = ?", array($event->getEventHash()));
                $conditions->add("confirmed = ?", array(0));
                
                $sql = "SELECT  notificationID, userID
@@ -123,34 +122,55 @@ class UserNotificationHandler extends SingletonFactory {
                        $notifications[$row['userID']] = $row['notificationID'];
                }
                
-               // skip recipients with outstanding notifications
-               if (!empty($notifications)) {
-                       // filter by author
+               // check if event supports stacking and author should be added
+               if (!empty($notifications) && $event->isStackable()) {
+                       $conditions = new PreparedStatementConditionBuilder();
+                       $conditions->add("notificationID IN (?)", array(array_values($notifications)));
                        if ($notificationObject->getAuthorID()) {
-                               $conditions = new PreparedStatementConditionBuilder();
-                               $conditions->add("notificationID IN (?)", array(array_values($notifications)));
                                $conditions->add("authorID = ?", array($notificationObject->getAuthorID()));
-                               
-                               $sql = "SELECT  notificationID
-                                       FROM    wcf".WCF_N."_user_notification_author
-                                       ".$conditions;
-                               $statement = WCF::getDB()->prepareStatement($sql);
-                               $statement->execute($conditions->getParameters());
-                               $notificationIDs = array();
-                               while ($row = $statement->fetchArray()) {
-                                       $notificationIDs[] = $row['notificationID'];
-                               }
-                               
+                       }
+                       else {
+                               $conditions->add("authorID IS NULL");
+                       }
+                       
+                       $sql = "SELECT  notificationID
+                               FROM    wcf".WCF_N."_user_notification_author
+                               ".$conditions;
+                       $statement = WCF::getDB()->prepareStatement($sql);
+                       $statement->execute($conditions->getParameters());
+                       $notificationIDs = array();
+                       while ($row = $statement->fetchArray()) {
+                               $notificationIDs[] = $row['notificationID'];
+                       }
+                       
+                       if (!empty($notificationIDs)) {
+                               // filter array of existing notifications and remove values which do not have a notification from this author yet (inverse logic!)
                                foreach ($notifications as $userID => $notificationID) {
-                                       // do not skip recipients with a similar notification but authored by somebody else
                                        if (!in_array($notificationID, $notificationIDs)) {
                                                unset($notifications[$userID]);
                                        }
                                }
+                               
+                               // update trigger count
+                               $sql = "UPDATE  wcf".WCF_N."_user_notification
+                                       SET     timesTriggered = timesTriggered + ?
+                                       WHERE   notificationID = ?";
+                               $statement = WCF::getDB()->prepareStatement($sql);
+                               
+                               WCF::getDB()->beginTransaction();
+                               foreach ($notificationIDs as $notificationID) {
+                                       $statement->execute(array(
+                                               1,
+                                               $notificationID
+                                       ));
+                               }
+                               WCF::getDB()->commitTransaction();
                        }
-                       
-                       $recipientIDs = array_diff($recipientIDs, array_keys($notifications));
-                       if (empty($recipientIDs)) return;
+               }
+               
+               $recipientIDs = array_diff($recipientIDs, array_keys($notifications));
+               if (empty($recipientIDs)) {
+                       return;
                }
                
                // get recipients
@@ -165,7 +185,8 @@ class UserNotificationHandler extends SingletonFactory {
                                'data' => array(
                                        'eventID' => $event->eventID,
                                        'authorID' => ($event->getAuthorID() ?: null),
-                                       
+                                       'objectID' => $notificationObject->getObjectID(),
+                                       'eventHash' => $event->getEventHash(),
                                        'packageID' => $objectTypeObject->packageID,
                                        'time' => TIME_NOW,
                                        'additionalData' => serialize($additionalData)
@@ -173,15 +194,13 @@ class UserNotificationHandler extends SingletonFactory {
                                'recipients' => $recipients
                        );
                        
-                       if ($event->isStackable() && $notificationObject instanceof IStackableUserNotificationObject) {
-                               $data['data']['objectID'] = $notificationObject->getRelatedObjectID();
+                       if ($event->isStackable()) {
                                $data['notifications'] = $notifications;
                                
                                $action = new UserNotificationAction(array(), 'createStackable', $data);
                        }
                        else {
-                               $data['data']['objectID'] = $notificationObject->getObjectID();
-                               
+                               $data['data']['timesTriggered'] = 1;
                                $action = new UserNotificationAction(array(), 'createDefault', $data);
                        }
                        
@@ -277,7 +296,7 @@ class UserNotificationHandler extends SingletonFactory {
                if (!$showConfirmedNotifications) $conditions->add("notification.confirmed = ?", array(0));
                
                $sql = "SELECT          notification.notificationID, notification_event.eventID, notification.authorID,
-                                       notification.moreAuthors, object_type.objectType, notification.objectID,
+                                       notification.timesTriggered, object_type.objectType, notification.objectID,
                                        notification.additionalData,
                                        notification.time".($showConfirmedNotifications ? ", notification.confirmed" : "")."
                        FROM            wcf".WCF_N."_user_notification notification
@@ -371,7 +390,8 @@ class UserNotificationHandler extends SingletonFactory {
                                $notificationObjects[$notificationID],
                                $objectTypes[$event['objectType']]['objects'][$event['objectID']],
                                (isset($authors[$event['authorID']]) ? $authors[$event['authorID']] : $unknownAuthor),
-                               unserialize($event['additionalData'])
+                               unserialize($event['additionalData']),
+                               $event['timesTriggered']
                        );
                        
                        if (isset($authorToNotification[$notificationID])) {
index 5c3b7296da804cd3b41fc403ba0c19e41eea44ec..20a5a68d47d20b4314442f1f416619bb391348cc 100644 (file)
@@ -6,7 +6,6 @@ use wcf\data\user\UserProfile;
 use wcf\data\DatabaseObjectDecorator;
 use wcf\system\user\notification\object\IUserNotificationObject;
 use wcf\system\WCF;
-use wcf\util\StringUtil;
 
 /**
  * Provides default a implementation for user notification events.
@@ -40,7 +39,7 @@ abstract class AbstractUserNotificationEvent extends DatabaseObjectDecorator imp
         * notification stacking support
         * @var boolean
         */
-       protected $isStackable = false;
+       protected $stackable = false;
        
        /**
         * user notification
@@ -66,6 +65,12 @@ abstract class AbstractUserNotificationEvent extends DatabaseObjectDecorator imp
         */
        protected $language = null;
        
+       /**
+        * notification trigger count
+        * @var integer
+        */
+       protected $timesTriggered = 0;
+       
        /**
         * @see \wcf\system\user\notification\event\IUserNotificationEvent::setAuthors()
         */
@@ -76,11 +81,12 @@ abstract class AbstractUserNotificationEvent extends DatabaseObjectDecorator imp
        /**
         * @see \wcf\system\user\notification\event\IUserNotificationEvent::setObject()
         */
-       public function setObject(UserNotification $notification, IUserNotificationObject $object, UserProfile $author, array $additionalData = array()) {
+       public function setObject(UserNotification $notification, IUserNotificationObject $object, UserProfile $author, array $additionalData = array(), $timesTriggered = 0) {
                $this->notification = $notification;
                $this->userNotificationObject = $object;
                $this->author = $author;
                $this->additionalData = $additionalData;
+               $this->timesTriggered = $timesTriggered;
        }
        
        /**
@@ -149,6 +155,13 @@ abstract class AbstractUserNotificationEvent extends DatabaseObjectDecorator imp
                return $this->getMessage();
        }
        
+       /**
+        * @see \wcf\system\user\notification\event\IUserNotificationEvent::getEventHash()
+        */
+       public function getEventHash() {
+               return sha1($this->eventID . '-' . $this->userNotificationObject->getObjectID());
+       }
+       
        /**
         * @see \wcf\system\user\notification\event\IUserNotificationEvent::setLanguage()
         */
@@ -170,6 +183,6 @@ abstract class AbstractUserNotificationEvent extends DatabaseObjectDecorator imp
         * @see \wcf\system\user\notification\event\IUserNotificationEvent::isStackable()
         */
        public function isStackable() {
-               return $this->isStackable;
+               return $this->stackable;
        }
 }
index bf8f23da7feb3bc8e08e04a5243fa9dca9d685dc..f883b42739dd97a5eda90817363c1d1a55f9d3fa 100644 (file)
@@ -88,6 +88,13 @@ interface IUserNotificationEvent extends IDatabaseObjectProcessor {
         */
        public function setAuthors(array $authors);
        
+       /**
+        * Returns a unique identifier of the event.
+        * 
+        * @return      string
+        */
+       public function getEventHash();
+       
        /**
         * Sets the object for the event.
         * 
@@ -95,8 +102,9 @@ interface IUserNotificationEvent extends IDatabaseObjectProcessor {
         * @param       \wcf\system\user\notification\object\IUserNotificationObject    $object
         * @param       \wcf\data\user\UserProfile                                      $author
         * @param       array<mixed>                                                    $additionalData
+        * @param       integer                                                         $timesTriggered
         */
-       public function setObject(UserNotification $notification, IUserNotificationObject $object, UserProfile $author, array $additionalData = array());
+       public function setObject(UserNotification $notification, IUserNotificationObject $object, UserProfile $author, array $additionalData = array(), $timesTriggered = 0);
        
        /**
         * Sets the language for the event
index e98073a3f64aec08ee3338ed74904b60029d3526..475eea54300917c65c5647fcd96865f04336c3f2 100644 (file)
@@ -14,9 +14,9 @@ use wcf\system\request\LinkHandler;
  */
 class UserFollowFollowingUserNotificationEvent extends AbstractUserNotificationEvent {
        /**
-        * @see \wcf\system\user\notification\event\AbstractUserNotificationEvent::$isStackable
+        * @see \wcf\system\user\notification\event\AbstractUserNotificationEvent::$stackable
         */
-       protected $isStackable = true;
+       protected $stackable = true;
        
        /**
         * @see \wcf\system\user\notification\event\IUserNotificationEvent::getTitle()
@@ -62,4 +62,11 @@ class UserFollowFollowingUserNotificationEvent extends AbstractUserNotificationE
        public function getLink() {
                return LinkHandler::getInstance()->getLink('User', array('object' => $this->author));
        }
+       
+       /**
+        * @see \wcf\system\user\notification\event\IUserNotificationEvent::getEventHash()
+        */
+       public function getEventHash() {
+               return sha1($this->eventID . '-' . $this->userNotificationObject->followUserID);
+       }
 }
index f5df2f9b369c680364eb5844f5109e945f23fdcd..5083ede344d2dd4ebbfd88bd2ed88c45440f6035 100644 (file)
@@ -1212,8 +1212,9 @@ CREATE TABLE wcf1_user_notification (
        packageID INT(10) NOT NULL, -- DEPRECATED
        eventID INT(10) NOT NULL,
        objectID INT(10) NOT NULL DEFAULT 0,
+       eventHash VARCHAR(40) NOT NULL DEFAULT '',
        authorID INT(10) NULL,
-       moreAuthors INT(10) NOT NULL DEFAULT 0,
+       timesTriggered INT(10) NOT NULL DEFAULT 0,
        userID INT(10) NOT NULL,
        time INT(10) NOT NULL DEFAULT 0,
        mailNotified TINYINT(1) NOT NULL DEFAULT 0,
@@ -1228,7 +1229,6 @@ CREATE TABLE wcf1_user_notification_author (
        notificationID INT(10) NOT NULL,
        authorID INT(10) NOT NULL,
        time INT(10) NOT NULL DEFAULT 0,
-       canceled TINYINT(1) NOT NULL DEFAULT 0,
        UNIQUE KEY (notificationID, authorID)
 );