Permalink
Browse files

feature(notifications): it's now easier to alter translations for not…

…ifications

Instead of using the [prepare, notification:create:type:subtype] plugin hook,
it's now possible to alter the notification subject and body just by adding
translation for the key 'notification:subject:<action>:<type>:<subtype>' and
'notification:body:<action>:<type>:<subtype>'.

For example in mod/blog/languages/en.php:

return array(
    'notification:subject:publish:object:blog' => '%s posted a new blog: %s',
);
  • Loading branch information...
juho-jaakkola committed Nov 30, 2015
1 parent 01e9e7d commit 4677d482864095ae59b98b191a9a95d659af5ba1
@@ -183,7 +183,7 @@ public function __construct(\Elgg\Config $config) {
$queue_name = \Elgg\Notifications\NotificationsService::QUEUE_NAME;
$queue = new \Elgg\Queue\DatabaseQueue($queue_name, $c->db);
$sub = new \Elgg\Notifications\SubscriptionsService($c->db);
- return new \Elgg\Notifications\NotificationsService($sub, $queue, $c->hooks, $c->session, $c->translator);
+ return new \Elgg\Notifications\NotificationsService($sub, $queue, $c->hooks, $c->session, $c->translator, $c->entityTable);
});
$this->setFactory('persistentLogin', function(ServiceProvider $c) {
@@ -31,6 +31,9 @@ class NotificationsService {
/** @var \Elgg\I18n\Translator */
protected $translator;
+ /** @var \Elgg\Database\EntityTable */
+ protected $entities;
+
/** @var array Registered notification events */
protected $events = array();
@@ -50,19 +53,23 @@ class NotificationsService {
* @param \Elgg\Queue\Queue $queue Queue
* @param \Elgg\PluginHooksService $hooks Plugin hook service
* @param \ElggSession $session Session service
+ * @param \Elgg\I18n\Translator $translator Translator
+ * @param \Elgg\Database\EntityTable $entities Entity table
*/
public function __construct(
\Elgg\Notifications\SubscriptionsService $subscriptions,
\Elgg\Queue\Queue $queue,
\Elgg\PluginHooksService $hooks,
\ElggSession $session,
- \Elgg\I18n\Translator $translator) {
+ \Elgg\I18n\Translator $translator,
+ \Elgg\Database\EntityTable $entities) {
$this->subscriptions = $subscriptions;
$this->queue = $queue;
$this->hooks = $hooks;
$this->session = $session;
$this->translator = $translator;
+ $this->entities = $entities;
}
/**
@@ -255,7 +262,8 @@ protected function sendNotifications($event, $subscriptions) {
*/
protected function sendNotification(\Elgg\Notifications\Event $event, $guid, $method) {
- $recipient = get_user($guid);
+ $recipient = $this->entities->get($guid, 'user');
+ /* @var \ElggUser $recipient */
if (!$recipient || $recipient->isBanned()) {
return false;
}
@@ -284,8 +292,8 @@ protected function sendNotification(\Elgg\Notifications\Event $event, $guid, $me
'object' => $object,
);
- $subject = $this->translator->translate('notification:subject', array($actor->name), $language);
- $body = $this->translator->translate('notification:body', array($object->getURL()), $language);
+ $subject = $this->getNotificationSubject($event, $recipient);
+ $body = $this->getNotificationBody($event, $recipient);
$notification = new \Elgg\Notifications\Notification($event->getActor(), $recipient, $language, $subject, $body, '', $params);
@@ -315,6 +323,100 @@ protected function sendNotification(\Elgg\Notifications\Event $event, $guid, $me
}
}
+ /**
+ * Get subject for the notification
+ *
+ * Plugins can define a subtype specific subject simply by providing a
+ * translation for the string "notification:subject:<action>:<type>:<subtype".
+ *
+ * For example in mod/blog/languages/en.php:
+ *
+ * 'notification:subject:publish:object:blog' => '%s published a blog called %s'
+ *
+ * @param Event $event Notification event
+ * @param \ElggUser $recipient Notification recipient
+ * @return string Notification subject in the recipient's language
+ */
+ private function getNotificationSubject(Event $event, \ElggUser $recipient) {
+ $actor = $event->getActor();
+ $object = $event->getObject();
+ /* @var \ElggObject $object */
+ $language = $recipient->language;
+
+ // Check custom notification subject for the action/type/subtype combination
+ $subject_key = "notification:{$event->getDescription()}:subject";
+ if ($this->translator->languageKeyExists($subject_key, $language)) {
+ return $this->translator->translate($subject_key, array(
+ $actor->name,
+ $object->getDisplayName(),
+ ), $language);
+ }
+
+ // Fall back to default subject
+ return $this->translator->translate('notification:subject', array($actor->name), $language);
+ }
+
+ /**
+ * Get body for the notification
+ *
+ * Plugin can define a subtype specific body simply by providing a
+ * translation for the string "notification:body:<action>:<type>:<subtype".
+ *
+ * For example in mod/blog/languages/en.php:
+ *
+ * 'notification:body:publish:object:blog' => '
+ * Hi %s!
+ *
+ * %s has created a new post called "%s" in the group %s.
+ *
+ * It says:
+ *
+ * "%s"
+ *
+ * You can comment the post here:
+ * %s
+ * ',
+ *
+ * The arguments passed into the translation are:
+ * 1. Recipient's name
+ * 2. Name of the user who triggered the notification
+ * 3. Title of the content
+ * 4. Name of the content's container
+ * 5. The actual content (entity's 'description' field)
+ * 6. URL to the content
+ *
+ * Argument swapping can be used to change the order of the parameters.
+ * See http://php.net/manual/en/function.sprintf.php#example-5427
+ *
+ * @param Event $event Notification event
+ * @param \ElggUser $recipient Notification recipient
+ * @return string Notification body in the recipient's language
+ */
+ private function getNotificationBody(Event $event, \ElggUser $recipient) {
+ $actor = $event->getActor();
+ $object = $event->getObject();
+ /* @var \ElggObject $object */
+ $language = $recipient->language;
+
+ // Check custom notification body for the action/type/subtype combination
+ $body_key = "notification:{$event->getDescription()}:body";
+ if ($this->translator->languageKeyExists($body_key, $language)) {
+ $container = $object->getContainerEntity();
+
+ return $this->translator->translate($body_key, array(
+ $recipient->name,
+ $actor->name,
+ $object->getDisplayName(),
+ $container->getDisplayName(),
+ $object->description,
+ $object->getURL(),
+ ), $language);
+ }
+
+ // Fall back to default body
+ return $this->translator->translate('notification:body', array($object->getURL()), $language);
+ }
+
/**
* Register a deprecated notification handler
*
@@ -12,14 +12,32 @@ public function setUp() {
->disableOriginalConstructor()
->getMock();
$this->sub = new \Elgg\Notifications\SubscriptionsService($dbMock);
+ $this->translator = new \Elgg\I18n\Translator();
$this->session = \ElggSession::getMock();
+ // User mock that supports calling $user->isBanned()
+ $user_123 = $this->getMockBuilder('\ElggEntity')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user_123->expects($this->any())
+ ->method('isBanned')
+ ->will($this->returnValue(false));
+
+ // Database mock that returns the user_123
+ $this->entities = $this->getMockBuilder('\Elgg\Database\EntityTable')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->entities->expects($this->any())
+ ->method('get')
+ ->with($this->equalTo('123'))
+ ->will($this->returnValue($user_123));
+
// Event class has dependency on elgg_get_logged_in_user_guid()
_elgg_services()->setValue('session', $this->session);
}
public function testRegisterEvent() {
- $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session);
+ $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session, $this->translator, $this->entities);
$service->registerEvent('foo', 'bar');
$events = array(
@@ -38,7 +56,7 @@ public function testRegisterEvent() {
}
public function testUnregisterEvent() {
- $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session);
+ $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session, $this->translator, $this->entities);
$service->registerEvent('foo', 'bar');
$this->assertTrue($service->unregisterEvent('foo', 'bar'));
@@ -51,15 +69,15 @@ public function testUnregisterEvent() {
}
public function testRegisterMethod() {
- $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session);
+ $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session, $this->translator, $this->entities);
$service->registerMethod('foo');
$methods = array('foo' => 'foo');
$this->assertEquals($methods, $service->getMethods());
}
public function testUnregisterMethod() {
- $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session);
+ $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session, $this->translator, $this->entities);
$service->registerMethod('foo');
$this->assertTrue($service->unregisterMethod('foo'));
@@ -68,7 +86,7 @@ public function testUnregisterMethod() {
}
public function testEnqueueEvent() {
- $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session);
+ $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session, $this->translator, $this->entities);
$service->registerEvent('object', 'bar');
$object = new \ElggObject();
@@ -96,7 +114,7 @@ public function testEnqueueEventHook() {
$mock->expects($this->once())
->method('trigger')
->with('enqueue', 'notification', $params, true);
- $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $mock, $this->session);
+ $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $mock, $this->session, $this->translator, $this->entities);
$service->registerEvent('object', 'bar');
$service->enqueueEvent('create', 'object', $object);
}
@@ -106,7 +124,7 @@ public function testStoppingEnqueueEvent() {
$mock->expects($this->once())
->method('trigger')
->will($this->returnValue(false));
- $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $mock, $this->session);
+ $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $mock, $this->session, $this->translator, $this->entities);
$service->registerEvent('object', 'bar');
$object = new \ElggObject();
@@ -116,7 +134,7 @@ public function testStoppingEnqueueEvent() {
}
public function testProcessQueueNoEvents() {
- $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session);
+ $service = new \Elgg\Notifications\NotificationsService($this->sub, $this->queue, $this->hooks, $this->session, $this->translator, $this->entities);
$this->assertEquals(0, $service->processQueue(time() + 10));
}
@@ -130,7 +148,7 @@ public function testProcessQueueThreeEvents() {
$mock->expects($this->exactly(3))
->method('getSubscriptions')
->will($this->returnValue(array()));
- $service = new \Elgg\Notifications\NotificationsService($mock, $this->queue, $this->hooks, $this->session);
+ $service = new \Elgg\Notifications\NotificationsService($mock, $this->queue, $this->hooks, $this->session, $this->translator, $this->entities);
$service->registerEvent('object', 'bar');
$object = new \ElggObject();
@@ -151,7 +169,7 @@ public function testProcessQueueTimesout() {
false);
$mock->expects($this->exactly(0))
->method('getSubscriptions');
- $service = new \Elgg\Notifications\NotificationsService($mock, $this->queue, $this->hooks, $this->session);
+ $service = new \Elgg\Notifications\NotificationsService($mock, $this->queue, $this->hooks, $this->session, $this->translator, $this->entities);
$service->registerEvent('object', 'bar');
$object = new \ElggObject();

0 comments on commit 4677d48

Please sign in to comment.