summaryrefslogtreecommitdiffstats
path: root/apps/comments/lib
diff options
context:
space:
mode:
authorArthur Schiwon <blizzz@owncloud.com>2016-05-09 10:02:07 +0200
committerArthur Schiwon <blizzz@arthur-schiwon.de>2016-10-07 17:11:19 +0200
commite1073cf442613ac92878c8ded30a33db35b30e14 (patch)
tree43bf514650829b6a4684cb564c62b3785f60b188 /apps/comments/lib
parent9e7824f3edcb498447915a396e93678ddb6f5768 (diff)
downloadnextcloud-server-e1073cf442613ac92878c8ded30a33db35b30e14.tar.gz
nextcloud-server-e1073cf442613ac92878c8ded30a33db35b30e14.zip
Notificacations for simple @-mentioning in comments
(WIP) notify user when mentioned in comments Fix doc, and create absolute URL for as notification link. PSR-4 compatibility changes also move notification creation to comments app Do not notify yourself unit test for controller and application smaller fixes - translatable app name - remove doubles in mention array - micro perf optimization - display name: special label for deleted users, keep user id for users that could not be fetched from userManager Comment Notification-Listener Unit Test fix email adresses remove notification when triggering comment was deleted add and adjust tests add missing @license tags simplify NotificationsController registration appinfo simplification, php docs make string easier to translate adjust test replace dispatcher-based listeners with a registration method and interface safer to not pass optional data parameter to setSubject for marking as processed. ID and mention suffices Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de> update comment Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
Diffstat (limited to 'apps/comments/lib')
-rw-r--r--apps/comments/lib/AppInfo/Application.php35
-rw-r--r--apps/comments/lib/Controller/Notifications.php138
-rw-r--r--apps/comments/lib/EventHandler.php78
-rw-r--r--apps/comments/lib/Notification/Listener.php130
-rw-r--r--apps/comments/lib/Notification/Notifier.php115
5 files changed, 496 insertions, 0 deletions
diff --git a/apps/comments/lib/AppInfo/Application.php b/apps/comments/lib/AppInfo/Application.php
new file mode 100644
index 00000000000..f168779cd0d
--- /dev/null
+++ b/apps/comments/lib/AppInfo/Application.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCA\Comments\AppInfo;
+
+use OCA\Comments\Controller\Notifications;
+use OCP\AppFramework\App;
+
+class Application extends App {
+
+ public function __construct (array $urlParams = array()) {
+ parent::__construct('comments', $urlParams);
+ $container = $this->getContainer();
+
+ $container->registerAlias('NotificationsController', Notifications::class);
+ }
+}
diff --git a/apps/comments/lib/Controller/Notifications.php b/apps/comments/lib/Controller/Notifications.php
new file mode 100644
index 00000000000..f76f1605345
--- /dev/null
+++ b/apps/comments/lib/Controller/Notifications.php
@@ -0,0 +1,138 @@
+<?php
+
+/**
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Comments\Controller;
+
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\NotFoundResponse;
+use OCP\AppFramework\Http\RedirectResponse;
+use OCP\AppFramework\Http\Response;
+use OCP\Comments\IComment;
+use OCP\Comments\ICommentsManager;
+use OCP\Files\Folder;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\IUserSession;
+use OCP\Notification\IManager;
+
+/**
+ * Class Notifications
+ *
+ * @package OCA\Comments\Controller
+ */
+class Notifications extends Controller {
+ /** @var Folder */
+ protected $folder;
+
+ /** @var ICommentsManager */
+ protected $commentsManager;
+
+ /** @var IURLGenerator */
+ protected $urlGenerator;
+
+ /** @var IManager */
+ protected $notificationManager;
+
+ /** @var IUserSession */
+ protected $userSession;
+
+ /**
+ * Notifications constructor.
+ *
+ * @param string $appName
+ * @param IRequest $request
+ * @param ICommentsManager $commentsManager
+ * @param Folder $folder
+ * @param IURLGenerator $urlGenerator
+ * @param IManager $notificationManager
+ * @param IUserSession $userSession
+ */
+ public function __construct(
+ $appName,
+ IRequest $request,
+ ICommentsManager $commentsManager,
+ Folder $folder,
+ IURLGenerator $urlGenerator,
+ IManager $notificationManager,
+ IUserSession $userSession
+ ) {
+ parent::__construct($appName, $request);
+ $this->commentsManager = $commentsManager;
+ $this->folder = $folder;
+ $this->urlGenerator = $urlGenerator;
+ $this->notificationManager = $notificationManager;
+ $this->userSession = $userSession;
+ }
+
+ /**
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ *
+ * @param string $id the comment ID
+ * @return Response
+ */
+ public function view($id) {
+ try {
+ $comment = $this->commentsManager->get($id);
+ if($comment->getObjectType() !== 'files') {
+ return new NotFoundResponse();
+ }
+ $files = $this->folder->getById($comment->getObjectId());
+ if(count($files) === 0) {
+ $this->markProcessed($comment);
+ return new NotFoundResponse();
+ }
+
+ $dir = $this->folder->getRelativePath($files[0]->getParent()->getPath());
+ $url = $this->urlGenerator->linkToRouteAbsolute(
+ 'files.view.index',
+ [
+ 'dir' => $dir,
+ 'scrollto' => $files[0]->getName()
+ ]
+ );
+
+ $this->markProcessed($comment);
+
+ return new RedirectResponse($url);
+ } catch (\Exception $e) {
+ return new NotFoundResponse();
+ }
+ }
+
+ /**
+ * Marks the notification about a comment as processed
+ * @param IComment $comment
+ */
+ protected function markProcessed(IComment $comment) {
+ $user = $this->userSession->getUser();
+ if(is_null($user)) {
+ return;
+ }
+ $notification = $this->notificationManager->createNotification();
+ $notification->setApp('comments')
+ ->setObject('comment', $comment->getId())
+ ->setSubject('mention')
+ ->setUser($user->getUID());
+ $this->notificationManager->markProcessed($notification);
+ }
+}
diff --git a/apps/comments/lib/EventHandler.php b/apps/comments/lib/EventHandler.php
new file mode 100644
index 00000000000..a32bb8ffa71
--- /dev/null
+++ b/apps/comments/lib/EventHandler.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @copyright Copyright (c) 2016 Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Comments;
+
+use OCA\Comments\Activity\Listener as ActivityListener;
+use OCA\Comments\AppInfo\Application;
+use OCA\Comments\Notification\Listener as NotificationListener;
+use OCP\Comments\CommentsEvent;
+use OCP\Comments\ICommentsEventHandler;
+
+/**
+ * Class EventHandler
+ *
+ * @package OCA\Comments
+ */
+class EventHandler implements ICommentsEventHandler {
+
+ /** @var Application */
+ protected $app;
+
+ public function __construct(Application $app) {
+ $this->app = $app;
+ }
+
+ /**
+ * @param CommentsEvent $event
+ */
+ public function handle(CommentsEvent $event) {
+ if($event->getComment()->getObjectType() !== 'files') {
+ // this is a 'files'-specific Handler
+ return;
+ }
+
+ if( $event->getEvent() === CommentsEvent::EVENT_ADD
+ && $event instanceof CommentsEvent
+ ) {
+ $this->onAdd($event);
+ return;
+ }
+ }
+
+ /**
+ * @param CommentsEvent $event
+ */
+ private function onAdd(CommentsEvent $event) {
+ $c = $this->app->getContainer();
+
+ /** @var NotificationListener $notificationListener */
+ $notificationListener = $c->query(NotificationListener::class);
+ $notificationListener->evaluate($event);
+
+ /** @var ActivityListener $listener */
+ $activityListener = $c->query(ActivityListener::class);
+ $activityListener->commentEvent($event);
+ }
+}
diff --git a/apps/comments/lib/Notification/Listener.php b/apps/comments/lib/Notification/Listener.php
new file mode 100644
index 00000000000..5e979fd9bf0
--- /dev/null
+++ b/apps/comments/lib/Notification/Listener.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Comments\Notification;
+
+use OCP\Comments\CommentsEvent;
+use OCP\Comments\IComment;
+use OCP\IURLGenerator;
+use OCP\IUserManager;
+use OCP\Notification\IManager;
+
+class Listener {
+ /** @var IManager */
+ protected $notificationManager;
+
+ /** @var IUserManager */
+ protected $userManager;
+
+ /** @var IURLGenerator */
+ protected $urlGenerator;
+
+ /**
+ * Listener constructor.
+ *
+ * @param IManager $notificationManager
+ * @param IUserManager $userManager
+ * @param IURLGenerator $urlGenerator
+ */
+ public function __construct(
+ IManager $notificationManager,
+ IUserManager $userManager,
+ IURLGenerator $urlGenerator
+ ) {
+
+ $this->notificationManager = $notificationManager;
+ $this->userManager = $userManager;
+ $this->urlGenerator = $urlGenerator;
+ }
+
+ /**
+ * @param CommentsEvent $event
+ */
+ public function evaluate(CommentsEvent $event) {
+ $comment = $event->getComment();
+
+ if($comment->getObjectType() !== 'files') {
+ // comments App serves files only, other object types/apps need to
+ // register their own ICommentsEventHandler and trigger notifications
+ return;
+ }
+
+ $mentions = $this->extractMentions($comment->getMessage());
+ if(empty($mentions)) {
+ // no one to notify
+ return;
+ }
+
+ $notification = $this->instantiateNotification($comment);
+
+ foreach($mentions as $mention) {
+ $user = substr($mention, 1); // @username → username
+ if( ($comment->getActorType() === 'users' && $user === $comment->getActorId())
+ || !$this->userManager->userExists($user)
+ ) {
+ // do not notify unknown users or yourself
+ continue;
+ }
+
+ $notification->setUser($user);
+ if($event->getEvent() === CommentsEvent::EVENT_DELETE) {
+ $this->notificationManager->markProcessed($notification);
+ } else {
+ $this->notificationManager->notify($notification);
+ }
+ }
+ }
+
+ /**
+ * creates a notification instance and fills it with comment data
+ *
+ * @param IComment $comment
+ * @return \OCP\Notification\INotification
+ */
+ public function instantiateNotification(IComment $comment) {
+ $notification = $this->notificationManager->createNotification();
+ $notification
+ ->setApp('comments')
+ ->setObject('comment', $comment->getId())
+ ->setSubject('mention', [ $comment->getObjectType(), $comment->getObjectId() ])
+ ->setDateTime($comment->getCreationDateTime())
+ ->setLink($this->urlGenerator->linkToRouteAbsolute(
+ 'comments.Notifications.view',
+ ['id' => $comment->getId()])
+ );
+
+ return $notification;
+ }
+
+ /**
+ * extracts @-mentions out of a message body.
+ *
+ * @param string $message
+ * @return string[] containing the mentions, e.g. ['@alice', '@bob']
+ */
+ public function extractMentions($message) {
+ $ok = preg_match_all('/\B@[a-z0-9_\-@\.\']+/i', $message, $mentions);
+ if(!$ok || !isset($mentions[0]) || !is_array($mentions[0])) {
+ return [];
+ }
+ return array_unique($mentions[0]);
+ }
+}
diff --git a/apps/comments/lib/Notification/Notifier.php b/apps/comments/lib/Notification/Notifier.php
new file mode 100644
index 00000000000..df05f297301
--- /dev/null
+++ b/apps/comments/lib/Notification/Notifier.php
@@ -0,0 +1,115 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Comments\Notification;
+
+use OCP\Comments\ICommentsManager;
+use OCP\Comments\NotFoundException;
+use OCP\Files\Folder;
+use OCP\IUserManager;
+use OCP\L10N\IFactory;
+use OCP\Notification\INotification;
+use OCP\Notification\INotifier;
+
+class Notifier implements INotifier {
+
+ /** @var IFactory */
+ protected $l10nFactory;
+
+ /** @var Folder */
+ protected $userFolder;
+
+ /** @var ICommentsManager */
+ protected $commentsManager;
+
+ /** @var IUserManager */
+ protected $userManager;
+
+ public function __construct(
+ IFactory $l10nFactory,
+ Folder $userFolder,
+ ICommentsManager $commentsManager,
+ IUserManager $userManager
+ ) {
+ $this->l10nFactory = $l10nFactory;
+ $this->userFolder = $userFolder;
+ $this->commentsManager = $commentsManager;
+ $this->userManager = $userManager;
+ }
+
+ /**
+ * @param INotification $notification
+ * @param string $languageCode The code of the language that should be used to prepare the notification
+ * @return INotification
+ * @throws \InvalidArgumentException When the notification was not prepared by a notifier
+ */
+ public function prepare(INotification $notification, $languageCode) {
+ if($notification->getApp() !== 'comments') {
+ throw new \InvalidArgumentException();
+ }
+ try {
+ $comment = $this->commentsManager->get($notification->getObjectId());
+ } catch(NotFoundException $e) {
+ // needs to be converted to InvalidArgumentException, otherwise none Notifications will be shown at all
+ throw new \InvalidArgumentException('Comment not found', 0, $e);
+ }
+ $l = $this->l10nFactory->get('comments', $languageCode);
+ $displayName = $comment->getActorId();
+ $isDeletedActor = $comment->getActorType() === ICommentsManager::DELETED_USER;
+ if($comment->getActorType() === 'users') {
+ $commenter = $this->userManager->get($comment->getActorId());
+ if(!is_null($commenter)) {
+ $displayName = $commenter->getDisplayName();
+ }
+ }
+ switch($notification->getSubject()) {
+ case 'mention':
+ $parameters = $notification->getSubjectParameters();
+ if($parameters[0] !== 'files') {
+ throw new \InvalidArgumentException('Unsupported comment object');
+ }
+ $nodes = $this->userFolder->getById($parameters[1]);
+ if(empty($nodes)) {
+ throw new \InvalidArgumentException('Cannot resolve file id to Node instance');
+ }
+ $fileName = $nodes[0]->getName();
+ if($isDeletedActor) {
+ $subject = (string) $l->t(
+ 'You were mentioned in a comment on "%s" by a now deleted user.',
+ [ $fileName ]
+ );
+ } else {
+ $subject = (string) $l->t(
+ 'You were mentioned in a comment on "%s" by %s.',
+ [ $fileName, $displayName ]
+ );
+ }
+ $notification->setParsedSubject($subject);
+
+ return $notification;
+ break;
+
+ default:
+ throw new \InvalidArgumentException('Invalid subject');
+ }
+
+ }
+}