Browse Source

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>
tags/v11.0RC2
Arthur Schiwon 8 years ago
parent
commit
e1073cf442
No account linked to committer's email address

+ 21
- 10
apps/comments/appinfo/app.php View File

@@ -41,22 +41,33 @@ $activityManager = \OC::$server->getActivityManager();
$activityManager->registerExtension(function() {
$application = new \OCP\AppFramework\App('comments');
/** @var \OCA\Comments\Activity\Extension $extension */
$extension = $application->getContainer()->query('OCA\Comments\Activity\Extension');
$extension = $application->getContainer()->query(\OCA\Comments\Activity\Extension::class);
return $extension;
});

$managerListener = function(\OCP\Comments\CommentsEvent $event) use ($activityManager) {
$application = new \OCP\AppFramework\App('comments');
/** @var \OCA\Comments\Activity\Listener $listener */
$listener = $application->getContainer()->query('OCA\Comments\Activity\Listener');
$listener->commentEvent($event);
};

$eventDispatcher->addListener(\OCP\Comments\CommentsEvent::EVENT_ADD, $managerListener);

$eventDispatcher->addListener(\OCP\Comments\CommentsEntityEvent::EVENT_ENTITY, function(\OCP\Comments\CommentsEntityEvent $event) {
$event->addEntityCollection('files', function($name) {
$nodes = \OC::$server->getUserFolder()->getById(intval($name));
return !empty($nodes);
});
});

$notificationManager = \OC::$server->getNotificationManager();
$notificationManager->registerNotifier(
function() {
$application = new \OCP\AppFramework\App('comments');
return $application->getContainer()->query(\OCA\Comments\Notification\Notifier::class);
},
function () {
$l = \OC::$server->getL10N('comments');
return ['id' => 'comments', 'name' => $l->t('Comments')];
}
);

$commentsManager = \OC::$server->getCommentsManager();
$commentsManager->registerEventHandler(function () {
$application = new \OCP\AppFramework\App('comments');
/** @var \OCA\Comments\Activity\Extension $extension */
$handler = $application->getContainer()->query(\OCA\Comments\EventHandler::class);
return $handler;
});

+ 28
- 0
apps/comments/appinfo/routes.php View File

@@ -0,0 +1,28 @@
<?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/>
*
*/

use \OCA\Comments\AppInfo\Application;

$application = new Application();
$application->registerRoutes($this, ['routes' => [
['name' => 'Notifications#view', 'url' => '/notifications/view/{id}', 'verb' => 'GET'],
]]);

+ 35
- 0
apps/comments/lib/AppInfo/Application.php View File

@@ -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);
}
}

+ 138
- 0
apps/comments/lib/Controller/Notifications.php View File

@@ -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);
}
}

+ 78
- 0
apps/comments/lib/EventHandler.php View File

@@ -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);
}
}

+ 130
- 0
apps/comments/lib/Notification/Listener.php View File

@@ -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]);
}
}

+ 115
- 0
apps/comments/lib/Notification/Notifier.php View File

@@ -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');
}

}
}

+ 65
- 0
apps/comments/tests/Unit/AppInfo/ApplicationTest.php View File

@@ -0,0 +1,65 @@
<?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\Tests\Unit\AppInfo;

use OCA\Comments\AppInfo\Application;
use Test\TestCase;

/**
* Class ApplicationTest
*
* @group DB
*
* @package OCA\Comments\Tests\Unit\AppInfo
*/
class ApplicationTest extends TestCase {
protected function setUp() {
parent::setUp();
\OC::$server->getUserManager()->createUser('dummy', '456');
\OC::$server->getUserSession()->setUser(\OC::$server->getUserManager()->get('dummy'));
}

protected function tearDown() {
\OC::$server->getUserManager()->get('dummy')->delete();
parent::tearDown();
}

public function test() {
$app = new Application();
$c = $app->getContainer();

// assert service instances in the container are properly setup
$s = $c->query('NotificationsController');
$this->assertInstanceOf('OCA\Comments\Controller\Notifications', $s);

$services = [
'OCA\Comments\Activity\Extension',
'OCA\Comments\Activity\Listener',
'OCA\Comments\Notification\Listener'
];

foreach($services as $service) {
$s = $c->query($service);
$this->assertInstanceOf($service, $s);
}
}
}

+ 162
- 0
apps/comments/tests/Unit/Controller/NotificationsTest.php View File

@@ -0,0 +1,162 @@
<?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\Tests\Unit\Controller;

use OCA\Comments\Controller\Notifications;
use OCP\Comments\NotFoundException;
use Test\TestCase;

class NotificationsTest extends TestCase {
/** @var \OCA\Comments\Controller\Notifications */
protected $notificationsController;

/** @var \OCP\Comments\ICommentsManager|\PHPUnit_Framework_MockObject_MockObject */
protected $commentsManager;

/** @var \OCP\Files\Folder|\PHPUnit_Framework_MockObject_MockObject */
protected $folder;

/** @var \OCP\IUserSession|\PHPUnit_Framework_MockObject_MockObject */
protected $session;

/** @var \OCP\Notification\IManager|\PHPUnit_Framework_MockObject_MockObject */
protected $notificationManager;

protected function setUp() {
parent::setUp();

$this->commentsManager = $this->getMockBuilder('\OCP\Comments\ICommentsManager')->getMock();
$this->folder = $this->getMockBuilder('\OCP\Files\Folder')->getMock();
$this->session = $this->getMockBuilder('\OCP\IUserSession')->getMock();
$this->notificationManager = $this->getMockBuilder('\OCP\Notification\IManager')->getMock();

$this->notificationsController = new Notifications(
'comments',
$this->getMockBuilder('\OCP\IRequest')->getMock(),
$this->commentsManager,
$this->folder,
$this->getMockBuilder('\OCP\IURLGenerator')->getMock(),
$this->notificationManager,
$this->session
);
}
public function testViewSuccess() {
$comment = $this->getMockBuilder('\OCP\Comments\IComment')->getMock();
$comment->expects($this->any())
->method('getObjectType')
->will($this->returnValue('files'));

$this->commentsManager->expects($this->any())
->method('get')
->with('42')
->will($this->returnValue($comment));

$file = $this->getMockBuilder('\OCP\Files\Node')->getMock();
$file->expects($this->once())
->method('getParent')
->will($this->returnValue($this->getMockBuilder('\OCP\Files\Folder')->getMock()));

$this->folder->expects($this->once())
->method('getById')
->will($this->returnValue([$file]));

$this->session->expects($this->once())
->method('getUser')
->will($this->returnValue($this->getMockBuilder('\OCP\IUser')->getMock()));

$notification = $this->getMockBuilder('\OCP\Notification\INotification')->getMock();
$notification->expects($this->any())
->method($this->anything())
->will($this->returnValue($notification));

$this->notificationManager->expects($this->once())
->method('createNotification')
->will($this->returnValue($notification));
$this->notificationManager->expects($this->once())
->method('markProcessed')
->with($notification);

$response = $this->notificationsController->view('42');
$this->assertInstanceOf('\OCP\AppFramework\Http\RedirectResponse', $response);
}

public function testViewInvalidComment() {
$this->commentsManager->expects($this->any())
->method('get')
->with('42')
->will($this->throwException(new NotFoundException()));

$file = $this->getMockBuilder('\OCP\Files\Node')->getMock();
$file->expects($this->never())
->method('getParent');

$this->folder->expects($this->never())
->method('getById');

$this->session->expects($this->never())
->method('getUser');

$this->notificationManager->expects($this->never())
->method('createNotification');
$this->notificationManager->expects($this->never())
->method('markProcessed');

$response = $this->notificationsController->view('42');
$this->assertInstanceOf('\OCP\AppFramework\Http\NotFoundResponse', $response);
}

public function testViewNoFile() {
$comment = $this->getMockBuilder('\OCP\Comments\IComment')->getMock();
$comment->expects($this->any())
->method('getObjectType')
->will($this->returnValue('files'));

$this->commentsManager->expects($this->any())
->method('get')
->with('42')
->will($this->returnValue($comment));

$this->folder->expects($this->once())
->method('getById')
->will($this->returnValue([]));

$this->session->expects($this->once())
->method('getUser')
->will($this->returnValue($this->getMockBuilder('\OCP\IUser')->getMock()));

$notification = $this->getMockBuilder('\OCP\Notification\INotification')->getMock();
$notification->expects($this->any())
->method($this->anything())
->will($this->returnValue($notification));

$this->notificationManager->expects($this->once())
->method('createNotification')
->will($this->returnValue($notification));
$this->notificationManager->expects($this->once())
->method('markProcessed')
->with($notification);

$response = $this->notificationsController->view('42');
$this->assertInstanceOf('\OCP\AppFramework\Http\NotFoundResponse', $response);
}
}

+ 152
- 0
apps/comments/tests/Unit/EventHandlerTest.php View File

@@ -0,0 +1,152 @@
<?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\Tests\Unit\Notification;

use OCA\Comments\AppInfo\Application;
use OCA\Comments\EventHandler;
use OCP\Comments\CommentsEvent;
use OCP\Comments\IComment;
use OCA\Comments\Activity\Listener as ActivityListener;
use OCA\Comments\Notification\Listener as NotificationListener;
use OCP\IContainer;
use Test\TestCase;

class EventHandlerTest extends TestCase {
/** @var EventHandler */
protected $eventHandler;

/** @var Application|\PHPUnit_Framework_MockObject_MockObject */
protected $app;

protected function setUp() {
parent::setUp();

$this->app = $this->getMockBuilder(Application::class)
->disableOriginalConstructor()
->getMock();

$this->eventHandler = new EventHandler($this->app);
}

public function testNotFiles() {
/** @var IComment|\PHPUnit_Framework_MockObject_MockObject $comment */
$comment = $this->getMockBuilder(IComment::class)->getMock();
$comment->expects($this->once())
->method('getObjectType')
->willReturn('smiles');

/** @var CommentsEvent|\PHPUnit_Framework_MockObject_MockObject $event */
$event = $this->getMockBuilder(CommentsEvent::class)
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getComment')
->willReturn($comment);
$event->expects($this->never())
->method('getEvent');

$this->eventHandler->handle($event);
}

public function notHandledProvider() {
return [
[CommentsEvent::EVENT_DELETE],
[CommentsEvent::EVENT_UPDATE]
];
}

/**
* @dataProvider notHandledProvider
* @param $eventType
*/
public function testNotHandled($eventType) {
/** @var IComment|\PHPUnit_Framework_MockObject_MockObject $comment */
$comment = $this->getMockBuilder(IComment::class)->getMock();
$comment->expects($this->once())
->method('getObjectType')
->willReturn('files');

/** @var CommentsEvent|\PHPUnit_Framework_MockObject_MockObject $event */
$event = $this->getMockBuilder(CommentsEvent::class)
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getComment')
->willReturn($comment);
$event->expects($this->once())
->method('getEvent')
->willReturn($eventType);

// further processing does not happen, because $event methods cannot be
// access more than once.
$this->eventHandler->handle($event);
}

public function testHandled() {
/** @var IComment|\PHPUnit_Framework_MockObject_MockObject $comment */
$comment = $this->getMockBuilder(IComment::class)->getMock();
$comment->expects($this->once())
->method('getObjectType')
->willReturn('files');

/** @var CommentsEvent|\PHPUnit_Framework_MockObject_MockObject $event */
$event = $this->getMockBuilder(CommentsEvent::class)
->disableOriginalConstructor()
->getMock();
$event->expects($this->atLeastOnce())
->method('getComment')
->willReturn($comment);
$event->expects($this->atLeastOnce())
->method('getEvent')
->willReturn(CommentsEvent::EVENT_ADD);

$notificationListener = $this->getMockBuilder(NotificationListener::class)
->disableOriginalConstructor()
->getMock();
$notificationListener->expects($this->once())
->method('evaluate')
->with($event);

$activityListener = $this->getMockBuilder(ActivityListener::class)
->disableOriginalConstructor()
->getMock();
$activityListener->expects($this->once())
->method('commentEvent')
->with($event);

/** @var IContainer|\PHPUnit_Framework_MockObject_MockObject $c */
$c = $this->getMockBuilder(IContainer::class)->getMock();
$c->expects($this->exactly(2))
->method('query')
->withConsecutive([NotificationListener::class], [ActivityListener::class])
->willReturnOnConsecutiveCalls($notificationListener, $activityListener);

$this->app->expects($this->once())
->method('getContainer')
->willReturn($c);

$this->eventHandler->handle($event);
}

}

+ 360
- 0
apps/comments/tests/Unit/Notification/ListenerTest.php View File

@@ -0,0 +1,360 @@
<?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\Tests\Unit\Notification;

use OCA\Comments\Notification\Listener;
use OCP\Comments\CommentsEvent;
use OCP\Comments\IComment;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\Notification\IManager;
use OCP\Notification\INotification;
use Test\TestCase;

class ListenerTest extends TestCase {
/** @var IManager|\PHPUnit_Framework_MockObject_MockObject */
protected $notificationManager;

/** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */
protected $userManager;

/** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */
protected $urlGenerator;

/** @var Listener */
protected $listener;

protected function setUp() {
parent::setUp();

$this->notificationManager = $this->getMockBuilder('\OCP\Notification\IManager')->getMock();
$this->userManager = $this->getMockBuilder('\OCP\IUserManager')->getMock();
$this->urlGenerator = $this->getMockBuilder('OCP\IURLGenerator')->getMock();

$this->listener = new Listener(
$this->notificationManager,
$this->userManager,
$this->urlGenerator
);
}

public function eventProvider() {
return [
[CommentsEvent::EVENT_ADD, 'notify'],
[CommentsEvent::EVENT_DELETE, 'markProcessed']
];
}

/**
* @dataProvider eventProvider
* @param string $eventType
* @param string $notificationMethod
*/
public function testEvaluate($eventType, $notificationMethod) {
$message = '@foobar and @barfoo you should know, @foo@bar.com is valid' .
' and so is @bar@foo.org@foobar.io I hope that clarifies everything.' .
' cc @23452-4333-54353-2342 @yolo!';

/** @var IComment|\PHPUnit_Framework_MockObject_MockObject $comment */
$comment = $this->getMockBuilder('\OCP\Comments\IComment')->getMock();
$comment->expects($this->any())
->method('getObjectType')
->will($this->returnValue('files'));
$comment->expects($this->any())
->method('getCreationDateTime')
->will($this->returnValue(new \DateTime()));
$comment->expects($this->once())
->method('getMessage')
->will($this->returnValue($message));

/** @var CommentsEvent|\PHPUnit_Framework_MockObject_MockObject $event */
$event = $this->getMockBuilder('\OCP\Comments\CommentsEvent')
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getComment')
->will($this->returnValue($comment));
$event->expects(($this->any()))
->method(('getEvent'))
->will($this->returnValue($eventType));

/** @var INotification|\PHPUnit_Framework_MockObject_MockObject $notification */
$notification = $this->getMockBuilder('\OCP\Notification\INotification')->getMock();
$notification->expects($this->any())
->method($this->anything())
->will($this->returnValue($notification));
$notification->expects($this->exactly(6))
->method('setUser');

$this->notificationManager->expects($this->once())
->method('createNotification')
->will($this->returnValue($notification));
$this->notificationManager->expects($this->exactly(6))
->method($notificationMethod)
->with($this->isInstanceOf('\OCP\Notification\INotification'));

$this->userManager->expects($this->exactly(6))
->method('userExists')
->withConsecutive(
['foobar'],
['barfoo'],
['foo@bar.com'],
['bar@foo.org@foobar.io'],
['23452-4333-54353-2342'],
['yolo']
)
->will($this->returnValue(true));

$this->listener->evaluate($event);
}

/**
* @dataProvider eventProvider
* @param string $eventType
*/
public function testEvaluateNoMentions($eventType) {
$message = 'a boring comment without mentions';

/** @var IComment|\PHPUnit_Framework_MockObject_MockObject $comment */
$comment = $this->getMockBuilder('\OCP\Comments\IComment')->getMock();
$comment->expects($this->any())
->method('getObjectType')
->will($this->returnValue('files'));
$comment->expects($this->any())
->method('getCreationDateTime')
->will($this->returnValue(new \DateTime()));
$comment->expects($this->once())
->method('getMessage')
->will($this->returnValue($message));

/** @var CommentsEvent|\PHPUnit_Framework_MockObject_MockObject $event */
$event = $this->getMockBuilder('\OCP\Comments\CommentsEvent')
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getComment')
->will($this->returnValue($comment));
$event->expects(($this->any()))
->method(('getEvent'))
->will($this->returnValue($eventType));

$this->notificationManager->expects($this->never())
->method('createNotification');
$this->notificationManager->expects($this->never())
->method('notify');
$this->notificationManager->expects($this->never())
->method('markProcessed');

$this->userManager->expects($this->never())
->method('userExists');

$this->listener->evaluate($event);
}

public function testUnsupportedCommentObjectType() {
/** @var IComment|\PHPUnit_Framework_MockObject_MockObject $comment */
$comment = $this->getMockBuilder('\OCP\Comments\IComment')->getMock();
$comment->expects($this->once())
->method('getObjectType')
->will($this->returnValue('vcards'));
$comment->expects($this->never())
->method('getMessage');

/** @var CommentsEvent|\PHPUnit_Framework_MockObject_MockObject $event */
$event = $this->getMockBuilder('\OCP\Comments\CommentsEvent')
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getComment')
->will($this->returnValue($comment));
$event->expects(($this->any()))
->method(('getEvent'))
->will($this->returnValue(CommentsEvent::EVENT_ADD));

$this->listener->evaluate($event);
}

public function testEvaluateUserDoesNotExist() {
$message = '@foobar bla bla bla';

/** @var IComment|\PHPUnit_Framework_MockObject_MockObject $comment */
$comment = $this->getMockBuilder('\OCP\Comments\IComment')->getMock();
$comment->expects($this->any())
->method('getObjectType')
->will($this->returnValue('files'));
$comment->expects($this->any())
->method('getCreationDateTime')
->will($this->returnValue(new \DateTime()));
$comment->expects($this->once())
->method('getMessage')
->will($this->returnValue($message));

/** @var CommentsEvent|\PHPUnit_Framework_MockObject_MockObject $event */
$event = $this->getMockBuilder('\OCP\Comments\CommentsEvent')
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getComment')
->will($this->returnValue($comment));
$event->expects(($this->any()))
->method(('getEvent'))
->will($this->returnValue(CommentsEvent::EVENT_ADD));

/** @var INotification|\PHPUnit_Framework_MockObject_MockObject $notification */
$notification = $this->getMockBuilder('\OCP\Notification\INotification')->getMock();
$notification->expects($this->any())
->method($this->anything())
->will($this->returnValue($notification));
$notification->expects($this->never())
->method('setUser');

$this->notificationManager->expects($this->once())
->method('createNotification')
->will($this->returnValue($notification));
$this->notificationManager->expects($this->never())
->method('notify');

$this->userManager->expects($this->once())
->method('userExists')
->withConsecutive(
['foobar']
)
->will($this->returnValue(false));

$this->listener->evaluate($event);
}

/**
* @dataProvider eventProvider
* @param string $eventType
* @param string $notificationMethod
*/
public function testEvaluateOneMentionPerUser($eventType, $notificationMethod) {
$message = '@foobar bla bla bla @foobar';

/** @var IComment|\PHPUnit_Framework_MockObject_MockObject $comment */
$comment = $this->getMockBuilder('\OCP\Comments\IComment')->getMock();
$comment->expects($this->any())
->method('getObjectType')
->will($this->returnValue('files'));
$comment->expects($this->any())
->method('getCreationDateTime')
->will($this->returnValue(new \DateTime()));
$comment->expects($this->once())
->method('getMessage')
->will($this->returnValue($message));

/** @var CommentsEvent|\PHPUnit_Framework_MockObject_MockObject $event */
$event = $this->getMockBuilder('\OCP\Comments\CommentsEvent')
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getComment')
->will($this->returnValue($comment));
$event->expects(($this->any()))
->method(('getEvent'))
->will($this->returnValue($eventType));

/** @var INotification|\PHPUnit_Framework_MockObject_MockObject $notification */
$notification = $this->getMockBuilder('\OCP\Notification\INotification')->getMock();
$notification->expects($this->any())
->method($this->anything())
->will($this->returnValue($notification));
$notification->expects($this->once())
->method('setUser');

$this->notificationManager->expects($this->once())
->method('createNotification')
->will($this->returnValue($notification));
$this->notificationManager->expects($this->once())
->method($notificationMethod)
->with($this->isInstanceOf('\OCP\Notification\INotification'));

$this->userManager->expects($this->once())
->method('userExists')
->withConsecutive(
['foobar']
)
->will($this->returnValue(true));

$this->listener->evaluate($event);
}

/**
* @dataProvider eventProvider
* @param string $eventType
*/
public function testEvaluateNoSelfMention($eventType) {
$message = '@foobar bla bla bla';

/** @var IComment|\PHPUnit_Framework_MockObject_MockObject $comment */
$comment = $this->getMockBuilder('\OCP\Comments\IComment')->getMock();
$comment->expects($this->any())
->method('getObjectType')
->will($this->returnValue('files'));
$comment->expects($this->any())
->method('getActorType')
->will($this->returnValue('users'));
$comment->expects($this->any())
->method('getActorId')
->will($this->returnValue('foobar'));
$comment->expects($this->any())
->method('getCreationDateTime')
->will($this->returnValue(new \DateTime()));
$comment->expects($this->once())
->method('getMessage')
->will($this->returnValue($message));

/** @var CommentsEvent|\PHPUnit_Framework_MockObject_MockObject $event */
$event = $this->getMockBuilder('\OCP\Comments\CommentsEvent')
->disableOriginalConstructor()
->getMock();
$event->expects($this->once())
->method('getComment')
->will($this->returnValue($comment));
$event->expects(($this->any()))
->method(('getEvent'))
->will($this->returnValue($eventType));

/** @var INotification|\PHPUnit_Framework_MockObject_MockObject $notification */
$notification = $this->getMockBuilder('\OCP\Notification\INotification')->getMock();
$notification->expects($this->any())
->method($this->anything())
->will($this->returnValue($notification));
$notification->expects($this->never())
->method('setUser');

$this->notificationManager->expects($this->once())
->method('createNotification')
->will($this->returnValue($notification));
$this->notificationManager->expects($this->never())
->method('notify');
$this->notificationManager->expects($this->never())
->method('markProcessed');

$this->userManager->expects($this->never())
->method('userExists');

$this->listener->evaluate($event);
}

}

+ 499
- 0
apps/comments/tests/Unit/Notification/NotifierTest.php View File

@@ -0,0 +1,499 @@
<?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\Tests\Unit\Notification;

use OCA\Comments\Notification\Notifier;
use OCP\Comments\IComment;
use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
use OCP\Files\Folder;
use OCP\Files\Node;
use OCP\IL10N;
use OCP\IUser;
use OCP\IUserManager;
use OCP\L10N\IFactory;
use OCP\Notification\INotification;
use Test\TestCase;

class NotifierTest extends TestCase {

/** @var Notifier */
protected $notifier;

/** @var IFactory|\PHPUnit_Framework_MockObject_MockObject */
protected $l10nFactory;

/** @var Folder|\PHPUnit_Framework_MockObject_MockObject */
protected $folder;

/** @var ICommentsManager|\PHPUnit_Framework_MockObject_MockObject */
protected $commentsManager;

/** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */
protected $userManager;


/** @var string */
protected $lc = 'tlh_KX';

/** @var INotification|\PHPUnit_Framework_MockObject_MockObject */
protected $notification;

/** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */
protected $l;

/** @var IComment|\PHPUnit_Framework_MockObject_MockObject */
protected $comment;

protected function setUp() {
parent::setUp();

$this->l10nFactory = $this->getMockBuilder('OCP\L10N\IFactory')->getMock();
$this->folder = $this->getMockBuilder('OCP\Files\Folder')->getMock();
$this->commentsManager = $this->getMockBuilder('OCP\Comments\ICommentsManager')->getMock();
$this->userManager = $this->getMockBuilder('OCP\IUserManager')->getMock();

$this->notifier = new Notifier(
$this->l10nFactory,
$this->folder,
$this->commentsManager,
$this->userManager
);

$this->l = $this->getMockBuilder('OCP\IL10N')->getMock();
$this->notification = $this->getMockBuilder('OCP\Notification\INotification')->getMock();
$this->comment = $this->getMockBuilder('OCP\Comments\IComment')->getMock();
}

public function testPrepareSuccess() {
$fileName = 'Gre\'thor.odp';
$displayName = 'Huraga';
$message = 'You were mentioned in a comment on "Gre\'thor.odp" by Huraga.';

/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->getMockBuilder('OCP\IUser')->getMock();
$user->expects($this->once())
->method('getDisplayName')
->willReturn($displayName);

/** @var Node|\PHPUnit_Framework_MockObject_MockObject */
$node = $this->getMockBuilder('OCP\Files\Node')->getMock();
$node
->expects($this->once())
->method('getName')
->willReturn($fileName);

$this->folder
->expects($this->once())
->method('getById')
->with('678')
->willReturn([$node]);

$this->notification
->expects($this->once())
->method('getApp')
->willReturn('comments');
$this->notification
->expects($this->once())
->method('getSubject')
->willReturn('mention');
$this->notification
->expects($this->once())
->method('getSubjectParameters')
->willReturn(['files', '678']);
$this->notification
->expects($this->once())
->method('setParsedSubject')
->with($message);

$this->l
->expects($this->once())
->method('t')
->with('You were mentioned in a comment on "%s" by %s.', [$fileName, $displayName])
->willReturn($message);

$this->l10nFactory
->expects($this->once())
->method('get')
->willReturn($this->l);

$this->comment
->expects($this->any())
->method('getActorId')
->willReturn('huraga');
$this->comment
->expects($this->any())
->method('getActorType')
->willReturn('users');

$this->commentsManager
->expects(($this->once()))
->method('get')
->willReturn($this->comment);

$this->userManager
->expects($this->once())
->method('get')
->with('huraga')
->willReturn($user);

$this->notifier->prepare($this->notification, $this->lc);
}

public function testPrepareSuccessDeletedUser() {
$fileName = 'Gre\'thor.odp';
$displayName = 'a now deleted user';
$message = 'You were mentioned in a comment on "Gre\'thor.odp" by a now deleted user.';

/** @var Node|\PHPUnit_Framework_MockObject_MockObject */
$node = $this->getMockBuilder('OCP\Files\Node')->getMock();
$node
->expects($this->once())
->method('getName')
->willReturn($fileName);

$this->folder
->expects($this->once())
->method('getById')
->with('678')
->willReturn([$node]);

$this->notification
->expects($this->once())
->method('getApp')
->willReturn('comments');
$this->notification
->expects($this->once())
->method('getSubject')
->willReturn('mention');
$this->notification
->expects($this->once())
->method('getSubjectParameters')
->willReturn(['files', '678']);
$this->notification
->expects($this->once())
->method('setParsedSubject')
->with($message);

$this->l
->expects($this->once())
->method('t')
->with('You were mentioned in a comment on "%s" by a now deleted user.', [ $fileName ])
->willReturn($message);

$this->l10nFactory
->expects($this->once())
->method('get')
->willReturn($this->l);

$this->comment
->expects($this->any())
->method('getActorId')
->willReturn('huraga');
$this->comment
->expects($this->any())
->method('getActorType')
->willReturn(ICommentsManager::DELETED_USER);

$this->commentsManager
->expects(($this->once()))
->method('get')
->willReturn($this->comment);

$this->userManager
->expects($this->never())
->method('get');

$this->notifier->prepare($this->notification, $this->lc);
}

/**
* @expectedException \InvalidArgumentException
*/
public function testPrepareDifferentApp() {
$this->folder
->expects($this->never())
->method('getById');

$this->notification
->expects($this->once())
->method('getApp')
->willReturn('constructions');
$this->notification
->expects($this->never())
->method('getSubject');
$this->notification
->expects($this->never())
->method('getSubjectParameters');
$this->notification
->expects($this->never())
->method('setParsedSubject');

$this->l10nFactory
->expects($this->never())
->method('get');

$this->commentsManager
->expects(($this->never()))
->method('get');

$this->userManager
->expects($this->never())
->method('get');

$this->notifier->prepare($this->notification, $this->lc);
}

/**
* @expectedException \InvalidArgumentException
*/
public function testPrepareNotFound() {
$this->folder
->expects($this->never())
->method('getById');

$this->notification
->expects($this->once())
->method('getApp')
->willReturn('comments');
$this->notification
->expects($this->never())
->method('getSubject');
$this->notification
->expects($this->never())
->method('getSubjectParameters');
$this->notification
->expects($this->never())
->method('setParsedSubject');

$this->l10nFactory
->expects($this->never())
->method('get');

$this->commentsManager
->expects(($this->once()))
->method('get')
->willThrowException(new NotFoundException());

$this->userManager
->expects($this->never())
->method('get');

$this->notifier->prepare($this->notification, $this->lc);
}

/**
* @expectedException \InvalidArgumentException
*/
public function testPrepareDifferentSubject() {
$displayName = 'Huraga';

/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->getMockBuilder('OCP\IUser')->getMock();
$user->expects($this->once())
->method('getDisplayName')
->willReturn($displayName);

$this->folder
->expects($this->never())
->method('getById');

$this->notification
->expects($this->once())
->method('getApp')
->willReturn('comments');
$this->notification
->expects($this->once())
->method('getSubject')
->willReturn('unlike');
$this->notification
->expects($this->never())
->method('getSubjectParameters');
$this->notification
->expects($this->never())
->method('setParsedSubject');

$this->l
->expects($this->never())
->method('t');

$this->l10nFactory
->expects($this->once())
->method('get')
->willReturn($this->l);

$this->comment
->expects($this->any())
->method('getActorId')
->willReturn('huraga');
$this->comment
->expects($this->any())
->method('getActorType')
->willReturn('users');

$this->commentsManager
->expects(($this->once()))
->method('get')
->willReturn($this->comment);

$this->userManager
->expects($this->once())
->method('get')
->with('huraga')
->willReturn($user);

$this->notifier->prepare($this->notification, $this->lc);
}

/**
* @expectedException \InvalidArgumentException
*/
public function testPrepareNotFiles() {
$displayName = 'Huraga';

/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->getMockBuilder('OCP\IUser')->getMock();
$user->expects($this->once())
->method('getDisplayName')
->willReturn($displayName);

$this->folder
->expects($this->never())
->method('getById');

$this->notification
->expects($this->once())
->method('getApp')
->willReturn('comments');
$this->notification
->expects($this->once())
->method('getSubject')
->willReturn('mention');
$this->notification
->expects($this->once())
->method('getSubjectParameters')
->willReturn(['ships', '678']);
$this->notification
->expects($this->never())
->method('setParsedSubject');

$this->l
->expects($this->never())
->method('t');

$this->l10nFactory
->expects($this->once())
->method('get')
->willReturn($this->l);

$this->comment
->expects($this->any())
->method('getActorId')
->willReturn('huraga');
$this->comment
->expects($this->any())
->method('getActorType')
->willReturn('users');

$this->commentsManager
->expects(($this->once()))
->method('get')
->willReturn($this->comment);

$this->userManager
->expects($this->once())
->method('get')
->with('huraga')
->willReturn($user);

$this->notifier->prepare($this->notification, $this->lc);
}

/**
* @expectedException \InvalidArgumentException
*/
public function testPrepareUnresolvableFileID() {
$displayName = 'Huraga';

/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->getMockBuilder('OCP\IUser')->getMock();
$user->expects($this->once())
->method('getDisplayName')
->willReturn($displayName);

$this->folder
->expects($this->once())
->method('getById')
->with('678')
->willReturn([]);

$this->notification
->expects($this->once())
->method('getApp')
->willReturn('comments');
$this->notification
->expects($this->once())
->method('getSubject')
->willReturn('mention');
$this->notification
->expects($this->once())
->method('getSubjectParameters')
->willReturn(['files', '678']);
$this->notification
->expects($this->never())
->method('setParsedSubject');

$this->l
->expects($this->never())
->method('t');

$this->l10nFactory
->expects($this->once())
->method('get')
->willReturn($this->l);

$this->comment
->expects($this->any())
->method('getActorId')
->willReturn('huraga');
$this->comment
->expects($this->any())
->method('getActorType')
->willReturn('users');

$this->commentsManager
->expects(($this->once()))
->method('get')
->willReturn($this->comment);

$this->userManager
->expects($this->once())
->method('get')
->with('huraga')
->willReturn($user);

$this->notifier->prepare($this->notification, $this->lc);
}

}

+ 4
- 0
lib/private/AppFramework/DependencyInjection/DIContainer.php View File

@@ -161,6 +161,10 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return $this->getServer()->getRootFolder();
});

$this->registerService('OCP\\Files\\Folder', function() {
return $this->getServer()->getUserFolder();
});

$this->registerService('OCP\\Http\\Client\\IClientService', function($c) {
return $this->getServer()->getHTTPClientService();
});

+ 58
- 21
lib/private/Comments/Manager.php View File

@@ -26,6 +26,7 @@ namespace OC\Comments;
use Doctrine\DBAL\Exception\DriverException;
use OCP\Comments\CommentsEvent;
use OCP\Comments\IComment;
use OCP\Comments\ICommentsEventHandler;
use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
use OCP\DB\QueryBuilder\IQueryBuilder;
@@ -33,7 +34,6 @@ use OCP\IDBConnection;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IUser;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class Manager implements ICommentsManager {

@@ -46,30 +46,30 @@ class Manager implements ICommentsManager {
/** @var IConfig */
protected $config;

/** @var EventDispatcherInterface */
protected $dispatcher;

/** @var IComment[] */
protected $commentsCache = [];

/** @var \Closure[] */
protected $eventHandlerClosures = [];

/** @var ICommentsEventHandler[] */
protected $eventHandlers = [];

/**
* Manager constructor.
*
* @param IDBConnection $dbConn
* @param ILogger $logger
* @param IConfig $config
* @param EventDispatcherInterface $dispatcher
*/
public function __construct(
IDBConnection $dbConn,
ILogger $logger,
IConfig $config,
EventDispatcherInterface $dispatcher
IConfig $config
) {
$this->dbConn = $dbConn;
$this->logger = $logger;
$this->config = $config;
$this->dispatcher = $dispatcher;
}

/**
@@ -455,10 +455,7 @@ class Manager implements ICommentsManager {
}

if ($affectedRows > 0 && $comment instanceof IComment) {
$this->dispatcher->dispatch(CommentsEvent::EVENT_DELETE, new CommentsEvent(
CommentsEvent::EVENT_DELETE,
$comment
));
$this->sendEvent(CommentsEvent::EVENT_DELETE, $comment);
}

return ($affectedRows > 0);
@@ -525,13 +522,9 @@ class Manager implements ICommentsManager {

if ($affectedRows > 0) {
$comment->setId(strval($qb->getLastInsertId()));
$this->sendEvent(CommentsEvent::EVENT_ADD, $comment);
}

$this->dispatcher->dispatch(CommentsEvent::EVENT_ADD, new CommentsEvent(
CommentsEvent::EVENT_ADD,
$comment
));

return $affectedRows > 0;
}

@@ -565,10 +558,7 @@ class Manager implements ICommentsManager {
throw new NotFoundException('Comment to update does ceased to exist');
}

$this->dispatcher->dispatch(CommentsEvent::EVENT_UPDATE, new CommentsEvent(
CommentsEvent::EVENT_UPDATE,
$comment
));
$this->sendEvent(CommentsEvent::EVENT_UPDATE, $comment);

return $affectedRows > 0;
}
@@ -751,4 +741,51 @@ class Manager implements ICommentsManager {
}
return ($affectedRows > 0);
}

/**
* registers an Entity to the manager, so event notifications can be send
* to consumers of the comments infrastructure
*
* @param \Closure $closure
*/
public function registerEventHandler(\Closure $closure) {
$this->eventHandlerClosures[] = $closure;
$this->eventHandlers = [];
}

/**
* returns valid, registered entities
*
* @return \OCP\Comments\ICommentsEventHandler[]
*/
private function getEventHandlers() {
if(!empty($this->eventHandlers)) {
return $this->eventHandlers;
}

$this->eventHandlers = [];
foreach ($this->eventHandlerClosures as $name => $closure) {
$entity = $closure();
if (!($entity instanceof ICommentsEventHandler)) {
throw new \InvalidArgumentException('The given entity does not implement the ICommentsEntity interface');
}
$this->eventHandlers[$name] = $entity;
}

return $this->eventHandlers;
}

/**
* sends notifications to the registered entities
*
* @param $eventType
* @param IComment $comment
*/
private function sendEvent($eventType, IComment $comment) {
$entities = $this->getEventHandlers();
$event = new CommentsEvent($eventType, $comment);
foreach ($entities as $entity) {
$entity->handle($event);
}
}
}

+ 39
- 0
lib/public/Comments/ICommentsEventHandler.php View File

@@ -0,0 +1,39 @@
<?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 OCP\Comments;

/**
* Interface ICommentsEventHandler
*
* @package OCP\Comments
* @since 9.2.0
*/
interface ICommentsEventHandler {

/**
* @param CommentsEvent $event
* @since 9.2.0
*/
public function handle(CommentsEvent $event);
}

+ 9
- 0
lib/public/Comments/ICommentsManager.php View File

@@ -237,4 +237,13 @@ interface ICommentsManager {
*/
public function deleteReadMarksOnObject($objectType, $objectId);

/**
* registers an Entity to the manager, so event notifications can be send
* to consumers of the comments infrastructure
*
* @param \Closure $closure
* @since 9.2.0
*/
public function registerEventHandler(\Closure $closure);

}

+ 2
- 0
tests/lib/Comments/FakeManager.php View File

@@ -38,4 +38,6 @@ class FakeManager implements \OCP\Comments\ICommentsManager {
public function deleteReadMarksFromUser(\OCP\IUser $user) {}

public function deleteReadMarksOnObject($objectType, $objectId) {}

public function registerEventHandler(\Closure $closure) {}
}

+ 39
- 5
tests/lib/Comments/ManagerTest.php View File

@@ -2,6 +2,9 @@

namespace Test\Comments;

use OC\Comments\Comment;
use OCP\Comments\CommentsEvent;
use OCP\Comments\ICommentsEventHandler;
use OCP\Comments\ICommentsManager;
use OCP\IUser;
use Test\TestCase;
@@ -355,7 +358,7 @@ class ManagerTest extends TestCase {

public function testSaveNew() {
$manager = $this->getManager();
$comment = new \OC\Comments\Comment();
$comment = new Comment();
$comment
->setActor('users', 'alice')
->setObject('files', 'file64')
@@ -375,7 +378,7 @@ class ManagerTest extends TestCase {

public function testSaveUpdate() {
$manager = $this->getManager();
$comment = new \OC\Comments\Comment();
$comment = new Comment();
$comment
->setActor('users', 'alice')
->setObject('files', 'file64')
@@ -396,7 +399,7 @@ class ManagerTest extends TestCase {
*/
public function testSaveUpdateException() {
$manager = $this->getManager();
$comment = new \OC\Comments\Comment();
$comment = new Comment();
$comment
->setActor('users', 'alice')
->setObject('files', 'file64')
@@ -415,7 +418,7 @@ class ManagerTest extends TestCase {
*/
public function testSaveIncomplete() {
$manager = $this->getManager();
$comment = new \OC\Comments\Comment();
$comment = new Comment();
$comment->setMessage('from no one to nothing');
$manager->save($comment);
}
@@ -426,7 +429,7 @@ class ManagerTest extends TestCase {
$manager = $this->getManager();

for($i = 0; $i < 3; $i++) {
$comment = new \OC\Comments\Comment();
$comment = new Comment();
$comment
->setActor('users', 'alice')
->setObject('files', 'file64')
@@ -630,4 +633,35 @@ class ManagerTest extends TestCase {
$this->assertNull($dateTimeGet);
}

public function testSendEvent() {
$handler1 = $this->getMockBuilder(ICommentsEventHandler::class)->getMock();
$handler1->expects($this->exactly(3))
->method('handle');

$handler2 = $this->getMockBuilder(ICommentsEventHandler::class)->getMock();
$handler1->expects($this->exactly(3))
->method('handle');

$manager = $this->getManager();
$manager->registerEventHandler(function () use ($handler1) {return $handler1; });
$manager->registerEventHandler(function () use ($handler2) {return $handler2; });

$comment = new Comment();
$comment
->setActor('users', 'alice')
->setObject('files', 'file64')
->setMessage('very beautiful, I am impressed!')
->setVerb('comment');

// Add event
$manager->save($comment);

// Update event
$comment->setMessage('Different topic');
$manager->save($comment);

// Delete event
$manager->delete($comment->getId());
}

}

Loading…
Cancel
Save