Updated notification lists and implementation details
authorAlexander Ebert <ebert@woltlab.com>
Wed, 18 Jun 2014 16:36:37 +0000 (18:36 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Wed, 18 Jun 2014 16:36:37 +0000 (18:36 +0200)
com.woltlab.wcf/templates/notificationList.tpl
com.woltlab.wcf/templates/notificationListOustanding.tpl
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/object/IStackableUserNotificationObject.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/user/notification/object/UserFollowUserNotificationObject.class.php
wcfsetup/install/files/style/user.less
wcfsetup/setup/db/install.sql

index d1ecb74e59dccbce9b95782035b0a5f05f88ce8d..5b0f6787c336020f9ef3f756df4205ffff68cb85 100644 (file)
 
 {if $notifications[notifications]}
        <div class="container marginTop">
-               <ul class="containerList">
+               <ul class="containerList" id="userNotificationItemList">
                        {foreach from=$notifications[notifications] item=$notification}
-                               <li class="jsNotificationItem" data-notification-id="{@$notification[notificationID]}" data-link="{$notification[event]->getLink()}">
+                               <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}">
                                        <div class="box48">
-                                               {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(48)}</a>
-                                               {else}
-                                                       <span class="framed">{@$notification[event]->getAuthor()->getAvatar()->getImageTag(48)}</span>
-                                               {/if}   
-                                               
-                                               <div class="details">
-                                                       <div class="containerHeadline">
-                                                               <h3>
-                                                                       {if !$notification[confirmed]}<span class="badge label newContentBadge">{lang}wcf.message.new{/lang}</span>{/if}
-                                                                       
-                                                                       {if $notification[event]->getAuthor()->userID}
-                                                                               <a href="{link controller='User' object=$notification[event]->getAuthor()}{/link}" class="userLink" data-user-id="{@$notification[event]->getAuthor()->userID}">{$notification[event]->getAuthor()->username}</a>
-                                                                       {else}
-                                                                               {$notification[event]->getAuthor()->username}
-                                                                       {/if}
-                                                               </h3> 
-                                                               <small>{@$notification[time]|time}</small>
-                                                       </div>
+                                               {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(48)}</a>
+                                                       {else}
+                                                               <span class="framed">{@$notification[event]->getAuthor()->getAvatar()->getImageTag(48)}</span>
+                                                       {/if}   
                                                        
-                                                       <p>{@$notification[event]->getMessage()}</p>
+                                                       <div class="details">
+                                                               <div class="containerHeadline">
+                                                                       <h3>
+                                                                               {if !$notification[confirmed]}<span class="badge label newContentBadge">{lang}wcf.message.new{/lang}</span>{/if}
+                                                                               
+                                                                               {if $notification[event]->getAuthor()->userID}
+                                                                                       <a href="{link controller='User' object=$notification[event]->getAuthor()}{/link}" class="userLink" data-user-id="{@$notification[event]->getAuthor()->userID}">{$notification[event]->getAuthor()->username}</a>
+                                                                               {else}
+                                                                                       {$notification[event]->getAuthor()->username}
+                                                                               {/if}
+                                                                       </h3> 
+                                                                       <small>{@$notification[time]|time}</small>
+                                                               </div>
+                                                               
+                                                               <p>{@$notification[event]->getMessage()}</p>
+                                                               
+                                                               {if !$notification[confirmed]}
+                                                                       <nav class="jsMobileNavigation buttonGroupNavigation">
+                                                                               <ul class="buttonList iconList jsOnly">
+                                                                                       <li><a class="jsMarkAsConfirmed jsTooltip" title="{lang}wcf.user.notification.markAsConfirmed{/lang}"><span class="icon icon16 fa-check"></span></a></li>
+                                                                               </ul>
+                                                                       </nav>
+                                                               {/if}
+                                                       </div>
+                                               {else}
+                                                       <span class="icon icon48 fa-users"></span>
                                                        
-                                                       {if !$notification[confirmed]}
-                                                               <nav class="jsMobileNavigation buttonGroupNavigation">
-                                                                       <ul class="buttonList iconList jsOnly">
-                                                                               <li><a class="jsMarkAsConfirmed jsTooltip" title="{lang}wcf.user.notification.markAsConfirmed{/lang}"><span class="icon icon16 fa-check"></span></a></li>
-                                                                       </ul>
-                                                               </nav>
-                                                       {/if}
-                                               </div>
+                                                       <div class="details">
+                                                               <div class="containerHeadline">
+                                                                       <h3>
+                                                                               {if !$notification[confirmed]}<span class="badge label newContentBadge">{lang}wcf.message.new{/lang}</span>{/if}
+                                                                               
+                                                                               {$notification[event]->getTitle()}
+                                                                       </h3> 
+                                                                       <small>{@$notification[time]|time}</small>
+                                                               </div>
+                                                               
+                                                               <p>{@$notification[event]->getMessage()}</p>
+                                                               
+                                                               <ul style="margin-top: 4px">
+                                                                       {foreach from=$notification[event]->getAuthors() item=author}
+                                                                               <li style="display: inline-block" class="jsTooltip" title="{$author->username}"><a href="{link controller='User' object=$author}{/link}" class="framed">{@$author->getAvatar()->getImageTag(24)}</a></li>
+                                                                       {/foreach}
+                                                               </ul>
+                                                               
+                                                               {if !$notification[confirmed]}
+                                                                       <nav class="jsMobileNavigation buttonGroupNavigation">
+                                                                               <ul class="buttonList iconList jsOnly">
+                                                                                       <li><a class="jsMarkAsConfirmed jsTooltip" title="{lang}wcf.user.notification.markAsConfirmed{/lang}"><span class="icon icon16 fa-check"></span></a></li>
+                                                                               </ul>
+                                                                       </nav>
+                                                               {/if}
+                                                       </div>
+                                               {/if}
                                        </div>
                                </li>
                        {/foreach}
index 92a3b28912f2738a8caf30158a017739f2dcf836..b9e23a95deb1a04004cc9883502faece6e387aa2 100644 (file)
@@ -1,13 +1,23 @@
 {foreach from=$notifications[notifications] item=notification}
-       <li class="jsNotificationItem notificationItem" 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]}">
                <a class="box24">
-                       <div class="framed">
-                               {@$notification[event]->getAuthor()->getAvatar()->getImageTag(24)}
-                       </div>
-                       <div>
-                               <h3>{@$notification[event]->getMessage()}</h3>
-                               <small>{$notification[event]->getAuthor()->username} - {@$notification[time]|time}</small>
-                       </div>
+                       {if $notification[event]->getAuthors()|count < 2}
+                               <div class="framed">
+                                       {@$notification[event]->getAuthor()->getAvatar()->getImageTag(24)}
+                               </div>
+                               <div>
+                                       <h3>{@$notification[event]->getMessage()}</h3>
+                                       <small>{$notification[event]->getAuthor()->username} - {@$notification[time]|time}</small>
+                               </div>
+                       {else}
+                               <div>
+                                       <span class="icon icon24 fa-users"></span>
+                               </div>
+                               <div>
+                                       <h3>{$notification[event]->getTitle()}</h3>
+                                       <small>{@$notification[time]|time}</small>
+                               </div>
+                       {/if}
                </a>
        </li>
 {/foreach}
\ No newline at end of file
index 87ed2ce2fc56854b6bfd58c394942cfb591e9c66..ca46ccd38e816bda2420db72a2ae0f1418c4937c 100644 (file)
@@ -1,12 +1,11 @@
 <?php
 namespace wcf\data\user\notification;
 use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\system\exception\UserInputException;
 use wcf\system\user\notification\UserNotificationHandler;
 use wcf\system\user\storage\UserStorageHandler;
 use wcf\system\WCF;
-use wcf\system\exception\UserInputException;
-use wcf\system\exception\PermissionDeniedException;
-use wcf\system\database\util\PreparedStatementConditionBuilder;
 
 /**
  * Executes user notification-related actions.
@@ -43,10 +42,46 @@ class UserNotificationAction extends AbstractDatabaseObjectAction {
                return $notification;
        }
        
+       /**
+        * Creates a simple notification without stacking support, applies to legacy notifications too.
+        * 
+        * @return      array<array>
+        */
        public function createDefault() {
-               die("createDefault");
+               foreach ($this->parameters['recipients'] as $recipient) {
+                       $this->parameters['data']['userID'] = $recipient->userID;
+                       $notification = $this->create();
+                       
+                       $notifications[$recipient->userID] = array(
+                               'isNew' => true,
+                               'object' => $notification
+                       );
+               }
+               
+               // insert author
+               $sql = "INSERT INTO     wcf".WCF_N."_user_notification_author
+                                       (notificationID, authorID, time)
+                       VALUES          (?, ?, ?)";
+               $statement = WCF::getDB()->prepareStatement($sql);
+               
+               WCF::getDB()->beginTransaction();
+               foreach ($notifications as $notificationData) {
+                       $statement->execute(array(
+                               $notificationData['object']->notificationID,
+                               ($this->parameters['authorID'] ?: null),
+                               TIME_NOW
+                       ));
+               }
+               WCF::getDB()->commitTransaction();
+               
+               return $notifications;
        }
        
+       /**
+        * Creates a notification or adds another author to an existing one.
+        * 
+        * @return      array<array>
+        */
        public function createStackable() {
                // get existing notifications
                $notificationList = new UserNotificationList();
@@ -77,9 +112,9 @@ class UserNotificationAction extends AbstractDatabaseObjectAction {
                }
                
                // insert author
-               $sql = "INSERT INTO     wcf".WCF_N."_user_notification_author
-                                       (notificationID, authorID, time)
-                       VALUES          (?, ?, ?)";
+               $sql = "INSERT IGNORE INTO      wcf".WCF_N."_user_notification_author
+                                               (notificationID, authorID, time)
+                       VALUES                  (?, ?, ?)";
                $statement = WCF::getDB()->prepareStatement($sql);
                
                WCF::getDB()->beginTransaction();
@@ -162,7 +197,7 @@ class UserNotificationAction extends AbstractDatabaseObjectAction {
         */
        public function markAllAsConfirmed() {
                // remove notifications for this user
-               $sql = "UPDATE  wcf".WCF_N."_user_notification_to_user
+               $sql = "UPDATE  wcf".WCF_N."_user_notification
                        SET     confirmed = ?
                        WHERE   userID = ?";
                $statement = WCF::getDB()->prepareStatement($sql);
index f6d96c63594d93e5127fa2e11ac89259bab91458..bd51ddee2b16a6ef33d6f9c90ced00bbf4264f95 100644 (file)
@@ -14,6 +14,7 @@ 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;
@@ -164,43 +165,29 @@ class UserNotificationHandler extends SingletonFactory {
                                'data' => array(
                                        'eventID' => $event->eventID,
                                        'authorID' => ($event->getAuthorID() ?: null),
-                                       'objectID' => $notificationObject->getObjectID(),
+                                       
+                                       'packageID' => $objectTypeObject->packageID,
                                        'time' => TIME_NOW,
                                        'additionalData' => serialize($additionalData)
                                ),
                                'recipients' => $recipients
                        );
                        
-                       if ($event->isStackable()) {
+                       if ($event->isStackable() && $notificationObject instanceof IStackableUserNotificationObject) {
+                               $data['data']['objectID'] = $notificationObject->getRelatedObjectID();
                                $data['notifications'] = $notifications;
                                
                                $action = new UserNotificationAction(array(), 'createStackable', $data);
                        }
                        else {
+                               $data['data']['objectID'] = $notificationObject->getObjectID();
+                               
                                $action = new UserNotificationAction(array(), 'createDefault', $data);
                        }
                        
                        $result = $action->executeAction();
                        $notifications = $result['returnValues'];
                        
-                       /*
-                       // create new notification
-                       $action = new UserNotificationAction(array(), 'create', array(
-                               'authorID' => ($event->getAuthorID() ?: null),
-                               'data' => array(
-                                       'eventID' => $event->eventID,
-                                       'authorID' => ($event->getAuthorID() ?: null),
-                                       'objectID' => $notificationObject->getObjectID(),
-                                       'time' => TIME_NOW,
-                                       'additionalData' => serialize($additionalData)
-                               ),
-                               'recipients' => $recipients
-                       ));
-                       $result = $action->executeAction();
-                       $notifications = $result['returnValues'];
-                       */
-                       
-                       // TODO: move -> DBOAction?
                        // send notifications
                        foreach ($recipients as $recipient) {
                                if ($recipient->mailNotificationType == 'instant') {
diff --git a/wcfsetup/install/files/lib/system/user/notification/object/IStackableUserNotificationObject.class.php b/wcfsetup/install/files/lib/system/user/notification/object/IStackableUserNotificationObject.class.php
new file mode 100644 (file)
index 0000000..76a5d2e
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace wcf\system\user\notification\object;
+
+/**
+ * This interface should be implemented by every object which supports stackable notifications.
+ * 
+ * @author     Alexander Ebert
+ * @copyright  2001-2014 WoltLab GmbH
+ * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package    com.woltlab.wcf
+ * @subpackage system.user.notification.object
+ * @category   Community Framework
+ */
+interface IStackableUserNotificationObject extends IUserNotificationObject {
+       /**
+        * Returns the ID of the related object.
+        * 
+        * @return      integer
+        */
+       public function getRelatedObjectID();
+}
index c5672a57bc1107722446947f3d409abb3d1bee96..c74ac38b02480eb04f61fe3a6a2079808f5e8047 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 namespace wcf\system\user\notification\object;
 use wcf\data\DatabaseObjectDecorator;
-use wcf\system\user\notification\object\IUserNotificationObject;
 
 /**
  * Represents a following user as a notification object.
@@ -13,7 +12,7 @@ use wcf\system\user\notification\object\IUserNotificationObject;
  * @subpackage system.user.notification.object
  * @category   Community Framework
  */
-class UserFollowUserNotificationObject extends DatabaseObjectDecorator implements IUserNotificationObject {
+class UserFollowUserNotificationObject extends DatabaseObjectDecorator implements IStackableUserNotificationObject {
        /**
         * @see \wcf\data\DatabaseObjectDecorator::$baseClass
         */
@@ -41,9 +40,9 @@ class UserFollowUserNotificationObject extends DatabaseObjectDecorator implement
        }
        
        /**
-        * @see \wcf\data\DatabaseObjectDecorator::getObjectID()
+        * @see \wcf\system\user\notification\object\IStackableUserNotificationObject::getRelatedObjectID()
         */
-       public function getObjectID() {
-               return $this->followUserID;
+       public function getRelatedObjectID() {
+               return $this->followUserID; 
        }
 }
index 336549333490ab80057d355d85276318127f52bb..f9534f9f60c23de5e57433b353c30a6395e98d23 100644 (file)
                }
        }
 }
+
+/* user notifications */
+#userNotificationItemList > .groupedNotificationItem p > a {
+       font-weight: bold;
+}
index 2b886ba7865c2020e9c9301511fb2ba00206589f..f5df2f9b369c680364eb5844f5109e945f23fdcd 100644 (file)
@@ -1209,6 +1209,7 @@ CREATE TABLE wcf1_user_menu_item (
 DROP TABLE IF EXISTS wcf1_user_notification;
 CREATE TABLE wcf1_user_notification (
        notificationID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+       packageID INT(10) NOT NULL, -- DEPRECATED
        eventID INT(10) NOT NULL,
        objectID INT(10) NOT NULL DEFAULT 0,
        authorID INT(10) NULL,