The first and second developer spotlight dealt with the new administrator’s features. But of course there were some great new features for the members of the community as well:
Notification stacking
The current notification system has got a big downside: Similar notifications were not grouped together. This restricted the possibilities, as you could not send notifications for likes and other events happening at a similar rate. It would completely clutter the notification center. Therefore we introduced notification stacking. Making use of stacking only requires small changes to your IUserNotificationEvent implementation:
- Set the attribute $stackable to true
- Override getEventHash() in case the default implementation (eventID + objectID) does not fit. The system groups notifications with the same hash.
- Update getTitle() and getMessage()
…
/**
* @see \wcf\system\user\notification\event\IUserNotificationEvent::getTitle()
*/
public function getTitle() {
$count = count($this->getAuthors());
// this notification was triggered by multiple users
if ($count > 1) {
return $this->getLanguage()->getDynamicVariable('wcf.user.notification.comment.like.title.stacked', array(
'count' => $count,
// the number of times this notification was triggered
'timesTriggered' => $this->notification->timesTriggered
));
}
return $this->getLanguage()->get('wcf.user.notification.comment.like.title');
}
/**
* @see \wcf\system\user\notification\event\IUserNotificationEvent::getMessage()
*/
public function getMessage() {
$authors = array_values($this->getAuthors());
$count = count($authors);
$owner = null;
if ($this->additionalData['objectID'] != WCF::getUser()->userID) {
$owner = CommentDataHandler::getInstance()->getUser($this->additionalData['objectID']);
}
// this notification was triggered by multiple users
if ($count > 1) {
return $this->getLanguage()->getDynamicVariable('wcf.user.notification.comment.like.message.stacked', array(
'author' => $this->author,
'authors' => $authors,
'count' => $count,
'others' => $count - 1,
'owner' => $owner
));
}
return $this->getLanguage()->getDynamicVariable('wcf.user.notification.comment.like.message', array(
'author' => $this->author,
'owner' => $owner
));
}
…
Display More
Like notifications
Read “Notification stacking” first, please.
One of the most requested feature was notifications for likes. You definitely should not leave them out when updating your plugin to make use of the new features of Community Framework 2.1!
The sendNotification(Like $like) method of your \wcf\data\like\object\ILikeObject implementation will be called when you should send a new notification. As the actual objects can differ a lot there is unfortunately no default implementation for this method. We provided you with a default IUserNotificationObject implementation and a default IUserNotificationObjectType implementation, though. For a proper implementation the following is required:
- An object type
- A user notification event definition
- Implementation of sendNotification()
- Implementation of the IUserNotificationEvent
- Some language items
If you already implemented notifications the following should look familiar:
…
<type>
<name>com.woltlab.gallery.likeableImage.notification</name>
<!-- it's a notification -->
<definitionname>com.woltlab.wcf.notification.objectType</definitionname>
<!-- this class is a default implementation provided by us -->
<classname>wcf\system\user\notification\object\type\LikeUserNotificationObjectType</classname>
<!-- the category of this notification -->
<category>com.woltlab.gallery</category>
</type>
…
Display More
…
<event>
<!-- you may name this as you like, we recommend sticking to 'like', though -->
<name>like</name>
<!-- this must match the <name> in your objectType.xml -->
<objecttype>com.woltlab.gallery.likeableImage.notification</objecttype>
<classname>gallery\system\user\notification\event\ImageLikeUserNotificationEvent</classname>
<!-- specifies, whether the notification should be enabled by default -->
<preset>1</preset>
</event>
…
Display More
…
public function sendNotification(Like $like) {
// don't send a notification, if a user likes his own content
if ($this->object->userID != WCF::getUser()->userID) {
// LikeUserNotificationObject is a default implementation
$notificationObject = new LikeUserNotificationObject($like);
// send the notification
// 'like' must match the <name> in userNotificationEvent.xml
// 'com.woltlab.gallery.likeableImage.notification' must match the <name> in objectType.xml
UserNotificationHandler::getInstance()->fireEvent('like', 'com.woltlab.gallery.likeableImage.notification', $notificationObject, array($this->object->userID), array(
'objectID' => $this->object->imageID
));
}
}
…
Display More
<?php
namespace gallery\system\user\notification\event;
use gallery\system\image\ImageDataHandler;
use wcf\system\request\LinkHandler;
use wcf\system\user\notification\event\AbstractSharedUserNotificationEvent;
/**
* User notification event for image likes.
*
* @author Matthias Schmidt
* @copyright 2001-2014 WoltLab GmbH
* @license WoltLab License <http://www.woltlab.com/license-agreement.html>
* @package com.woltlab.gallery
* @subpackage system.user.notification.event
* @category Community Gallery
*/
class ImageLikeUserNotificationEvent extends AbstractSharedUserNotificationEvent {
/**
* @see \wcf\system\user\notification\event\AbstractUserNotificationEvent::$stackable
*/
protected $stackable = true;
/**
* @see \wcf\system\user\notification\event\AbstractSharedUserNotificationEvent::prepare()
*/
protected function prepare() {
// AbstractSharedUserNotificationEvents allow you to reduce the number of database queries
// by collecting the needed data beforehand
ImageDataHandler::getInstance()->cacheImageID($this->additionalData['objectID']);
}
/**
* @see \wcf\system\user\notification\event\IUserNotificationEvent::getTitle()
*/
public function getTitle() {
$count = count($this->getAuthors());
// notification was stacked
if ($count > 1) {
return $this->getLanguage()->getDynamicVariable('gallery.image.like.notification.title.stacked', array(
'count' => $count,
'timesTriggered' => $this->notification->timesTriggered
));
}
return $this->getLanguage()->get('gallery.image.like.notification.title');
}
/**
* @see \wcf\system\user\notification\event\IUserNotificationEvent::getMessage()
*/
public function getMessage() {
$image = ImageDataHandler::getInstance()->getImage($this->additionalData['objectID']);
$authors = array_values($this->getAuthors());
$count = count($authors);
// notification was stacked
if ($count > 1) {
return $this->getLanguage()->getDynamicVariable('gallery.image.like.notification.message.stacked', array(
'author' => $this->author,
'authors' => $authors,
'count' => $count,
'others' => $count - 1,
'image' => $image
));
}
return $this->getLanguage()->getDynamicVariable('gallery.image.like.notification.message', array(
'author' => $this->author,
'image' => $image
));
}
/**
* @see \wcf\system\user\notification\event\IUserNotificationEvent::getLink()
*/
public function getLink() {
$image = ImageDataHandler::getInstance()->getImage($this->additionalData['objectID']);
return LinkHandler::getInstance()->getLink('Image', array(
'application' => 'gallery',
'object' => $image
));
}
/**
* @see \wcf\system\user\notification\event\IUserNotificationEvent::getEventHash()
*/
public function getEventHash() {
return sha1($this->eventID . '-' . $this->additionalData['objectID']);
}
/**
* @see \wcf\system\user\notification\event\IUserNotificationEvent::supportsEmailNotification()
*/
public function supportsEmailNotification() {
// likes could be received in rapid succession, don’t send emails for every single one!
return false;
}
}
Display More
Embedded objects
You could not properly quote attachments in Community Framework 2.0 for performance reasons. Community Framework 2.1 solves this issue by the embedded object system. You are now able to register the objects that are embedded in your message and can afterwards fetch them with a single function call. This allows for some other great futures, such as avatars beside quotes as well. This is how you integrate the embedded object system:
…
<type>
<name>com.woltlab.wbb.post</name>
<definitionname>com.woltlab.wcf.message</definitionname>
</type>
…
When adding a new message:
…
// registerObjects() returns true if there are embedded objects
// 'com.woltlab.wbb.post' must match your objectType.xml
if (MessageEmbeddedObjectManager::getInstance()->registerObjects('com.woltlab.wbb.post', $post->postID, $post->message)) {
$postEditor->update(array(
'hasEmbeddedObjects' => 1
));
}
…
When updating a message:
…
// no need to update, if the message did not change
if (isset($this->parameters['data']['message'])) {
foreach ($this->objects as $object) {
if ($object->hasEmbeddedObjects != MessageEmbeddedObjectManager::getInstance()->registerObjects('com.woltlab.wbb.post', $object->postID, $this->parameters['data']['message'])) {
$object->update(array(
'hasEmbeddedObjects' => ($object->hasEmbeddedObjects ? 0 : 1)
));
}
}
}
…
Display More
When deleting a message:
…
MessageEmbeddedObjectManager::getInstance()->removeObjects('com.woltlab.wbb.post', $postIDs);
…
When loading messages (example taken from quick reply):
…
MessageEmbeddedObjectManager::getInstance()->loadObjects('com.woltlab.wbb.post', array($post->postID));
…
When showing messages:
…
// this replaces AttachmentBBCode::setObjectID($this->postID);
MessageEmbeddedObjectManager::getInstance()->setActiveMessage('com.woltlab.wbb.post', $this->postID);
…
This feature is not forward compatible: The installation will fail in Community Framework 2.0, due to the missing object type definition!