<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OC\Encryption;
use InvalidArgumentException;
use OC\Files\Filesystem;
use OC\Files\Mount;
use OC\Files\View;
use OCP\Encryption\Exceptions\GenericEncryptionException;
use Psr\Log\LoggerInterface;
/**
* update encrypted files, e.g. because a file was shared
*/
class Update {
/** @var View */
protected $view;
/** @var Util */
protected $util;
/** @var \OC\Files\Mount\Manager */
protected $mountManager;
/** @var Manager */
protected $encryptionManager;
/** @var string */
protected $uid;
/** @var File */
protected $file;
/** @var LoggerInterface */
protected $logger;
/**
* @param string $uid
*/
public function __construct(
View $view,
Util $util,
Mount\Manager $mountManager,
Manager $encryptionManager,
File $file,
LoggerInterface $logger,
$uid,
) {
$this->view = $view;
$this->util = $util;
$this->mountManager = $mountManager;
$this->encryptionManager = $encryptionManager;
$this->file = $file;
$this->logger = $logger;
$this->uid = $uid;
}
/**
* hook after file was shared
*
* @param array $params
*/
public function postShared($params) {
if ($this->encryptionManager->isEnabled()) {
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
$path = Filesystem::getPath($params['fileSource']);
[$owner, $ownerPath] = $this->getOwnerPath($path);
$absPath = '/' . $owner . '/files/' . $ownerPath;
$this->update($absPath);
}
}
}
/**
* hook after file was unshared
*
* @param array $params
*/
public function postUnshared($params) {
if ($this->encryptionManager->isEnabled()) {
if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
$path = Filesystem::getPath($params['fileSource']);
[$owner, $ownerPath] = $this->getOwnerPath($path);
$absPath = '/' . $owner . '/files/' . $ownerPath;
$this->update($absPath);
}
}
}
/**
* inform encryption module that a file was restored from the trash bin,
* e.g. to update the encryption keys
*
* @param array $params
*/
public function postRestore($params) {
if ($this->encryptionManager->isEnabled()) {
$path = Filesystem::normalizePath('/' . $this->uid . '/files/' . $params['filePath']);
$this->update($path);
}
}
/**
* inform encryption module that a file was renamed,
* e.g. to update the encryption keys
*
* @param array $params
*/
public function postRename($params) {
$source = $params['oldpath'];
$target = $params['newpath'];
if (
$this->encryptionManager->isEnabled() &&
dirname($source) !== dirname($target)
) {
[$owner, $ownerPath] = $this->getOwnerPath($target);
$absPath = '/' . $owner . '/files/' . $ownerPath;
$this->update($absPath);
}
}
/**
* get owner and path relative to data/<owner>/files
*
* @param string $path path to file for current user
* @return array ['owner' => $owner, 'path' => $path]
* @throws \InvalidArgumentException
*/
protected function getOwnerPath($path) {
$info = Filesystem::getFileInfo($path);
$owner = Filesystem::getOwner($path);
$view = new View('/' . $owner . '/files');
$path = $view->getPath($info->getId());
if ($path === null) {
throw new InvalidArgumentException('No file found for ' . $info->getId());
}
return [$owner, $path];
}
/**
* notify encryption module about added/removed users from a file/folder
*
* @param string $path relative to data/
* @throws Exceptions\ModuleDoesNotExistsException
*/
public function update($path) {
$encryptionModule = $this->encryptionManager->getEncryptionModule();
// if the encryption module doesn't encrypt the files on a per-user basis
// we have nothing to do here.
if ($encryptionModule->needDetailedAccessList() === falsepre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */<?php
namespace Test\Comments;
use OC\Comments\Comment;
use OC\Comments\ManagerFactory;
use OCP\Comments\IComment;
use OCP\Comments\ICommentsEventHandler;
use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
use OCP\IDBConnection;
use OCP\IUser;
use Test\TestCase;
/**
* Class ManagerTest
*
* @group DB
*/
class ManagerTest extends TestCase {
/** @var IDBConnection */
private $connection;
public function setUp() {
parent::setUp();
$this->connection = \OC::$server->getDatabaseConnection();
$sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*comments`');
$this->connection->prepare($sql)->execute();
}
protected function addDatabaseEntry($parentId, $topmostParentId, $creationDT = null, $latestChildDT = null, $objectId = null) {
if (is_null($creationDT)) {
$creationDT = new \DateTime();
}
if (is_null($latestChildDT)) {
$latestChildDT = new \DateTime('yesterday');
}
if (is_null($objectId)) {
$objectId = 'file64';
}
$qb = $this->connection->getQueryBuilder();
$qb
->insert('comments')
->values([
'parent_id' => $qb->createNamedParameter($parentId),
'topmost_parent_id' => $qb->createNamedParameter($topmostParentId),
'children_count' => $qb->createNamedParameter(2),
'actor_type' => $qb->createNamedParameter('users'),
'actor_id' => $qb->createNamedParameter('alice'),
'message' => $qb->createNamedParameter('nice one'),
'verb' => $qb->createNamedParameter('comment'),
'creation_timestamp' => $qb->createNamedParameter($creationDT, 'datetime'),
'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, 'datetime'),
'object_type' => $qb->createNamedParameter('files'),
'object_id' => $qb->createNamedParameter($objectId),
])
->execute();
return $qb->getLastInsertId();
}
protected function getManager() {
$factory = new ManagerFactory(\OC::$server);
return $factory->getManager();
}
/**
* @expectedException \OCP\Comments\NotFoundException
*/
public function testGetCommentNotFound() {
$manager = $this->getManager();
$manager->get('22');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testGetCommentNotFoundInvalidInput() {
$manager = $this->getManager();
$manager->get('unexisting22');
}
public function testGetComment() {
$manager = $this->getManager();
$creationDT = new \DateTime();
$latestChildDT = new \DateTime('yesterday');
$qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
$qb
->insert('comments')
->values([
'parent_id' => $qb->createNamedParameter('2'),
'topmost_parent_id' => $qb->createNamedParameter('1'),
'children_count' => $qb->createNamedParameter(2),
'actor_type' => $qb->createNamedParameter('users'),
'actor_id' => $qb->createNamedParameter('alice'),
'message' => $qb->createNamedParameter('nice one'),
'verb' => $qb->createNamedParameter('comment'),
'creation_timestamp' => $qb->createNamedParameter($creationDT, 'datetime'),
'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, 'datetime'),
'object_type' => $qb->createNamedParameter('files'),
'object_id' => $qb->createNamedParameter('file64'),
])
->execute();
$id = strval($qb->getLastInsertId());
$comment = $manager->get($id);
$this->assertTrue($comment instanceof IComment);
$this->assertSame($comment->getId(), $id);
$this->assertSame($comment->getParentId(), '2');
$this->assertSame($comment->getTopmostParentId(), '1');
$this->assertSame($comment->getChildrenCount(), 2);
$this->assertSame($comment->getActorType(), 'users');
$this->assertSame($comment->getActorId(), 'alice');
$this->assertSame($comment->getMessage(), 'nice one');
$this->assertSame($comment->getVerb(), 'comment');
$this->assertSame($comment->getObjectType(), 'files');
$this->assertSame($comment->getObjectId(), 'file64');
$this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $creationDT->getTimestamp());
$this->assertEquals($comment->getLatestChildDateTime(), $latestChildDT);
}
/**
* @expectedException \OCP\Comments\NotFoundException
*/
public function testGetTreeNotFound() {
$manager = $this->getManager();
$manager->getTree('22');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testGetTreeNotFoundInvalidIpnut() {
$manager = $this->getManager();
$manager->getTree('unexisting22');
}
public function testGetTree() {
$headId = $this->addDatabaseEntry(0, 0);
$this->addDatabaseEntry($headId, $headId, new \DateTime('-3 hours'));
$this->addDatabaseEntry($headId, $headId, new \DateTime('-2 hours'));
$id = $this->addDatabaseEntry($headId, $headId, new \DateTime('-1 hour'));
$manager = $this->getManager();
$tree = $manager->getTree($headId);
// Verifying the root comment
$this->assertTrue(isset($tree['comment']));
$this->assertTrue($tree['comment'] instanceof IComment);
$this->assertSame($tree['comment']->getId(), strval($headId));
$this->assertTrue(isset($tree['replies']));
$this->assertSame(count($tree['replies']), 3);
// one level deep
foreach ($tree['replies'] as $reply) {
$this->assertTrue($reply['comment'] instanceof IComment);
$this->assertSame($reply['comment']->getId(), strval($id));
$this->assertSame(count($reply['replies']), 0);
$id--;
}
}
public function testGetTreeNoReplies() {
$id = $this->addDatabaseEntry(0, 0);
$manager = $this->getManager();
$tree = $manager->getTree($id);
// Verifying the root comment
$this->assertTrue(isset($tree['comment']));
$this->assertTrue($tree['comment'] instanceof IComment);
$this->assertSame($tree['comment']->getId(), strval($id));
$this->assertTrue(isset($tree['replies']));
$this->assertSame(count($tree['replies']), 0);
// one level deep
foreach ($tree['replies'] as $reply) {
throw new \Exception('This ain`t happen');
}
}
public function testGetTreeWithLimitAndOffset() {
$headId = $this->addDatabaseEntry(0, 0);
$this->addDatabaseEntry($headId, $headId, new \DateTime('-3 hours'));
$this->addDatabaseEntry($headId, $headId, new \DateTime('-2 hours'));
$this->addDatabaseEntry($headId, $headId, new \DateTime('-1 hour'));
$idToVerify = $this->addDatabaseEntry($headId, $headId, new \DateTime());
$manager = $this->getManager();
for ($offset = 0; $offset < 3; $offset += 2) {
$tree = $manager->getTree(strval($headId), 2, $offset);
// Verifying the root comment
$this->assertTrue(isset($tree['comment']));
$this->assertTrue($tree['comment'] instanceof IComment);
$this->assertSame($tree['comment']->getId(), strval($headId));
$this->assertTrue(isset($tree['replies']));
$this->assertSame(count($tree['replies']), 2);
// one level deep
foreach ($tree['replies'] as $reply) {
$this->assertTrue($reply['comment'] instanceof IComment);
$this->assertSame($reply['comment']->getId(), strval($idToVerify));
$this->assertSame(count($reply['replies']), 0);
$idToVerify--;
}
}
}
public function testGetForObject() {
$this->addDatabaseEntry(0, 0);
$manager = $this->getManager();
$comments = $manager->getForObject('files', 'file64');
$this->assertTrue(is_array($comments));
$this->assertSame(count($comments), 1);
$this->assertTrue($comments[0] instanceof IComment);
$this->assertSame($comments[0]->getMessage(), 'nice one');
}
public function testGetForObjectWithLimitAndOffset() {
$this->addDatabaseEntry(0, 0, new \DateTime('-6 hours'));
$this->addDatabaseEntry(0, 0, new \DateTime('-5 hours'));
$this->addDatabaseEntry(1, 1, new \DateTime('-4 hours'));
$this->addDatabaseEntry(0, 0, new \DateTime('-3 hours'));
$this->addDatabaseEntry(2, 2, new \DateTime('-2 hours'));
$this->addDatabaseEntry(2, 2, new \DateTime('-1 hours'));
$idToVerify = $this->addDatabaseEntry(3, 1, new \DateTime());
$manager = $this->getManager();
$offset = 0;
do {
$comments = $manager->getForObject('files', 'file64', 3, $offset);
$this->assertTrue(is_array($comments));
foreach ($comments as $comment) {
$this->assertTrue($comment instanceof IComment);
$this->assertSame($comment->getMessage(), 'nice one');
$this->assertSame($comment->getId(), strval($idToVerify));
$idToVerify--;
}
$offset += 3;
} while (count($comments) > 0);
}
public function testGetForObjectWithDateTimeConstraint() {
$this->addDatabaseEntry(0, 0, new \DateTime('-6 hours'));
$this->addDatabaseEntry(0, 0, new \DateTime('-5 hours'));
$id1 = $this->addDatabaseEntry(0, 0, new \DateTime('-3 hours'));
$id2 = $this->addDatabaseEntry(2, 2, new \DateTime('-2 hours'));
$manager = $this->getManager();
$comments = $manager->getForObject('files', 'file64', 0, 0, new \DateTime('-4 hours'));
$this->assertSame(count($comments), 2);
$this->assertSame($comments[0]->getId(), strval($id2));
$this->assertSame($comments[1]->getId(), strval($id1));
}
public function testGetForObjectWithLimitAndOffsetAndDateTimeConstraint() {
$this->addDatabaseEntry(0, 0, new \DateTime('-7 hours'));
$this->addDatabaseEntry(0, 0, new \DateTime('-6 hours'));
$this->addDatabaseEntry(1, 1, new \DateTime('-5 hours'));
$this->addDatabaseEntry(0, 0, new \DateTime('-3 hours'));
$this->addDatabaseEntry(2, 2, new \DateTime('-2 hours'));
$this->addDatabaseEntry(2, 2, new \DateTime('-1 hours'));
$idToVerify = $this->addDatabaseEntry(3, 1, new \DateTime());
$manager = $this->getManager();
$offset = 0;
do {
$comments = $manager->getForObject('files', 'file64', 3, $offset, new \DateTime('-4 hours'));
$this->assertTrue(is_array($comments));
foreach ($comments as $comment) {
$this->assertTrue($comment instanceof IComment);
$this->assertSame($comment->getMessage(), 'nice one');
$this->assertSame($comment->getId(), strval($idToVerify));
$this->assertTrue(intval($comment->getId()) >= 4);
$idToVerify--;
}
$offset += 3;
} while (count($comments) > 0);
}
public function testGetNumberOfCommentsForObject() {
for ($i = 1; $i < 5; $i++) {
$this->addDatabaseEntry(0, 0);
}
$manager = $this->getManager();
$amount = $manager->getNumberOfCommentsForObject('untype', '00');
$this->assertSame($amount, 0);
$amount = $manager->getNumberOfCommentsForObject('files', 'file64');
$this->assertSame($amount, 4);
}
public function testGetNumberOfUnreadCommentsForFolder() {
$query = $this->connection->getQueryBuilder();
$query->insert('filecache')
->values([
'parent' => $query->createNamedParameter(1000),
'size' => $query->createNamedParameter(10),
'mtime' => $query->createNamedParameter(10),
'storage_mtime' => $query->createNamedParameter(10),
'path' => $query->createParameter('path'),
'path_hash' => $query->createParameter('path'),
]);
$fileIds = [];
for ($i = 0; $i < 4; $i++) {
$query->setParameter('path', 'path_' . $i);
$query->execute();
$fileIds[] = $query->getLastInsertId();
}
// 2 comment for 1111 with 1 before read marker
// 2 comments for 1112 with no read marker
// 1 comment for 1113 before read marker
// 1 comment for 1114 with no read marker
$this->addDatabaseEntry(0, 0, null, null, $fileIds[1]);
for ($i = 0; $i < 4; $i++) {
$this->addDatabaseEntry(0, 0, null, null, $fileIds[$i]);
}
$this->addDatabaseEntry(0, 0, (new \DateTime())->modify('-2 days'), null, $fileIds[0]);
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->createMock(IUser::class);
$user->expects($this->any())
->method('getUID')
->will($this->returnValue('comment_test'));
$manager = $this->getManager();
$manager->setReadMark('files', (string) $fileIds[0], (new \DateTime())->modify('-1 days'), $user);
$manager->setReadMark('files', (string) $fileIds[2], (new \DateTime()), $user);
$amount = $manager->getNumberOfUnreadCommentsForFolder(1000, $user);
$this->assertEquals([
$fileIds[0] => 1,
$fileIds[1] => 2,
$fileIds[3] => 1,
], $amount);
}
public function invalidCreateArgsProvider() {
return [
['', 'aId-1', 'oType-1', 'oId-1'],
['aType-1', '', 'oType-1', 'oId-1'],
['aType-1', 'aId-1', '', 'oId-1'],
['aType-1', 'aId-1', 'oType-1', ''],
[1, 'aId-1', 'oType-1', 'oId-1'],
['aType-1', 1, 'oType-1', 'oId-1'],
['aType-1', 'aId-1', 1, 'oId-1'],
['aType-1', 'aId-1', 'oType-1', 1],
];
}
/**
* @dataProvider invalidCreateArgsProvider
* @expectedException \InvalidArgumentException
* @param string $aType
* @param string $aId
* @param string $oType
* @param string $oId
*/
public function testCreateCommentInvalidArguments($aType, $aId, $oType, $oId) {
$manager = $this->getManager();
$manager->create($aType, $aId, $oType, $oId);
}
public function testCreateComment() {
$actorType = 'bot';
$actorId = 'bob';
$objectType = 'weather';
$objectId = 'bielefeld';
$comment = $this->getManager()->create($actorType, $actorId, $objectType, $objectId);
$this->assertTrue($comment instanceof IComment);
$this->assertSame($comment->getActorType(), $actorType);
$this->assertSame($comment->getActorId(), $actorId);
$this->assertSame($comment->getObjectType(), $objectType);
$this->assertSame($comment->getObjectId(), $objectId);
}
/**
* @expectedException \OCP\Comments\NotFoundException
*/
public function testDelete() {
$manager = $this->getManager();
$done = $manager->delete('404');
$this->assertFalse($done);
$done = $manager->delete('%');
$this->assertFalse($done);
$done = $manager->delete('');
$this->assertFalse($done);
$id = strval($this->addDatabaseEntry(0, 0));
$comment = $manager->get($id);
$this->assertTrue($comment instanceof IComment);
$done = $manager->delete($id);
$this->assertTrue($done);
$manager->get($id);
}
public function testSaveNew() {
$manager = $this->getManager();
$comment = new Comment();
$comment
->setActor('users', 'alice')
->setObject('files', 'file64')
->setMessage('very beautiful, I am impressed!')
->setVerb('comment');
$saveSuccessful = $manager->save($comment);
$this->assertTrue($saveSuccessful);
$this->assertTrue($comment->getId() !== '');
$this->assertTrue($comment->getId() !== '0');
$this->assertTrue(!is_null($comment->getCreationDateTime()));
$loadedComment = $manager->get($comment->getId());
$this->assertSame($comment->getMessage(), $loadedComment->getMessage());
$this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $loadedComment->getCreationDateTime()->getTimestamp());
}
public function testSaveUpdate() {
$manager = $this->getManager();
$comment = new Comment();
$comment
->setActor('users', 'alice')
->setObject('files', 'file64')
->setMessage('very beautiful, I am impressed!')
->setVerb('comment');
$manager->save($comment);
$comment->setMessage('very beautiful, I am really so much impressed!');
$manager->save($comment);
$loadedComment = $manager->get($comment->getId());
$this->assertSame($comment->getMessage(), $loadedComment->getMessage());
}
/**
* @expectedException \OCP\Comments\NotFoundException
*/
public function testSaveUpdateException() {
$manager = $this->getManager();
$comment = new Comment();
$comment
->setActor('users', 'alice')
->setObject('files', 'file64')
->setMessage('very beautiful, I am impressed!')
->setVerb('comment');
$manager->save($comment);
$manager->delete($comment->getId());
$comment->setMessage('very beautiful, I am really so much impressed!');
$manager->save($comment);
}
/**
* @expectedException \UnexpectedValueException
*/
public function testSaveIncomplete() {
$manager = $this->getManager();
$comment = new Comment();
$comment->setMessage('from no one to nothing');
$manager->save($comment);
}
public function testSaveAsChild() {
$id = $this->addDatabaseEntry(0, 0);
$manager = $this->getManager();
for ($i = 0; $i < 3; $i++) {
$comment = new Comment();
$comment
->setActor('users', 'alice')
->setObject('files', 'file64')
->setParentId(strval($id))
->setMessage('full ack')
->setVerb('comment')
// setting the creation time avoids using sleep() while making sure to test with different timestamps
->setCreationDateTime(new \DateTime('+' . $i . ' minutes'));
$manager->save($comment);
$this->assertSame($comment->getTopmostParentId(), strval($id));
$parentComment = $manager->get(strval($id));
$this->assertSame($parentComment->getChildrenCount(), $i + 1);
$this->assertEquals($parentComment->getLatestChildDateTime()->getTimestamp(), $comment->getCreationDateTime()->getTimestamp());
}
}
public function invalidActorArgsProvider() {
return
[
['', ''],
[1, 'alice'],
['users', 1],
];
}
/**
* @dataProvider invalidActorArgsProvider
* @expectedException \InvalidArgumentException
* @param string $type
* @param string $id
*/
public function testDeleteReferencesOfActorInvalidInput($type, $id) {
$manager = $this->getManager();
$manager->deleteReferencesOfActor($type, $id);
}
public function testDeleteReferencesOfActor() {
$ids = [];
$ids[] = $this->addDatabaseEntry(0, 0);
$ids[] = $this->addDatabaseEntry(0, 0);
$ids[] = $this->addDatabaseEntry(0, 0);
$manager = $this->getManager();
// just to make sure they are really set, with correct actor data
$comment = $manager->get(strval($ids[1]));
$this->assertSame($comment->getActorType(), 'users');
$this->assertSame($comment->getActorId(), 'alice');
$wasSuccessful = $manager->deleteReferencesOfActor('users', 'alice');
$this->assertTrue($wasSuccessful);
foreach ($ids as $id) {
$comment = $manager->get(strval($id));
$this->assertSame($comment->getActorType(), ICommentsManager::DELETED_USER);
$this->assertSame($comment->getActorId(), ICommentsManager::DELETED_USER);
}
// actor info is gone from DB, but when database interaction is alright,
// we still expect to get true back
$wasSuccessful = $manager->deleteReferencesOfActor('users', 'alice');
$this->assertTrue($wasSuccessful);
}
public function testDeleteReferencesOfActorWithUserManagement() {
$user = \OC::$server->getUserManager()->createUser('xenia', '123456');
$this->assertTrue($user instanceof IUser);
$manager = \OC::$server->getCommentsManager();
$comment = $manager->create('users', $user->getUID(), 'files', 'file64');
$comment
->setMessage('Most important comment I ever left on the Internet.')
->setVerb('comment');
$status = $manager->save($comment);
$this->assertTrue($status);
$commentID = $comment->getId();
$user->delete();
$comment = $manager->get($commentID);
$this->assertSame($comment->getActorType(), ICommentsManager::DELETED_USER);
$this->assertSame($comment->getActorId(), ICommentsManager::DELETED_USER);
}
public function invalidObjectArgsProvider() {
return
[
['', ''],
[1, 'file64'],
['files', 1],
];
}
/**
* @dataProvider invalidObjectArgsProvider
* @expectedException \InvalidArgumentException
* @param string $type
* @param string $id
*/
public function testDeleteCommentsAtObjectInvalidInput($type, $id) {
$manager = $this->getManager();
$manager->deleteCommentsAtObject($type, $id);
}
public function testDeleteCommentsAtObject() {
$ids = [];
$ids[] = $this->addDatabaseEntry(0, 0);
$ids[] = $this->addDatabaseEntry(0, 0);
$ids[] = $this->addDatabaseEntry(0, 0);
$manager = $this->getManager();
// just to make sure they are really set, with correct actor data
$comment = $manager->get(strval($ids[1]));
$this->assertSame($comment->getObjectType(), 'files');
$this->assertSame($comment->getObjectId(), 'file64');
$wasSuccessful = $manager->deleteCommentsAtObject('files', 'file64');
$this->assertTrue($wasSuccessful);
$verified = 0;
foreach ($ids as $id) {
try {
$manager->get(strval($id));
} catch (NotFoundException $e) {
$verified++;
}
}
$this->assertSame($verified, 3);
// actor info is gone from DB, but when database interaction is alright,
// we still expect to get true back
$wasSuccessful = $manager->deleteCommentsAtObject('files', 'file64');
$this->assertTrue($wasSuccessful);
}
public function testSetMarkRead() {
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->createMock(IUser::class);
$user->expects($this->any())
->method('getUID')
->will($this->returnValue('alice'));
$dateTimeSet = new \DateTime();
$manager = $this->getManager();
$manager->setReadMark('robot', '36', $dateTimeSet, $user);
$dateTimeGet = $manager->getReadMark('robot', '36', $user);
$this->assertEquals($dateTimeGet->getTimestamp(), $dateTimeSet->getTimestamp());
}
public function testSetMarkReadUpdate() {
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->createMock(IUser::class);
$user->expects($this->any())
->method('getUID')
->will($this->returnValue('alice'));
$dateTimeSet = new \DateTime('yesterday');
$manager = $this->getManager();
$manager->setReadMark('robot', '36', $dateTimeSet, $user);
$dateTimeSet = new \DateTime('today');
$manager->setReadMark('robot', '36', $dateTimeSet, $user);
$dateTimeGet = $manager->getReadMark('robot', '36', $user);
$this->assertEquals($dateTimeGet, $dateTimeSet);
}
public function testReadMarkDeleteUser() {
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->createMock(IUser::class);
$user->expects($this->any())
->method('getUID')
->will($this->returnValue('alice'));
$dateTimeSet = new \DateTime();
$manager = $this->getManager();
$manager->setReadMark('robot', '36', $dateTimeSet, $user);
$manager->deleteReadMarksFromUser($user);
$dateTimeGet = $manager->getReadMark('robot', '36', $user);
$this->assertNull($dateTimeGet);
}
public function testReadMarkDeleteObject() {
/** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
$user = $this->createMock(IUser::class);
$user->expects($this->any())
->method('getUID')
->will($this->returnValue('alice'));
$dateTimeSet = new \DateTime();
$manager = $this->getManager();
$manager->setReadMark('robot', '36', $dateTimeSet, $user);
$manager->deleteReadMarksOnObject('robot', '36');
$dateTimeGet = $manager->getReadMark('robot', '36', $user);
$this->assertNull($dateTimeGet);
}
public function testSendEvent() {
$handler1 = $this->getMockBuilder(ICommentsEventHandler::class)->getMock();
$handler1->expects($this->exactly(4))
->method('handle');
$handler2 = $this->getMockBuilder(ICommentsEventHandler::class)->getMock();
$handler1->expects($this->exactly(4))
->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());
}
public function testResolveDisplayName() {
$manager = $this->getManager();
$planetClosure = function ($name) {
return ucfirst($name);
};
$galaxyClosure = function ($name) {
return strtoupper($name);
};
$manager->registerDisplayNameResolver('planet', $planetClosure);
$manager->registerDisplayNameResolver('galaxy', $galaxyClosure);
$this->assertSame('Neptune', $manager->resolveDisplayName('planet', 'neptune'));
$this->assertSame('SOMBRERO', $manager->resolveDisplayName('galaxy', 'sombrero'));
}
/**
* @expectedException \OutOfBoundsException
*/
public function testRegisterResolverDuplicate() {
$manager = $this->getManager();
$planetClosure = function ($name) {
return ucfirst($name);
};
$manager->registerDisplayNameResolver('planet', $planetClosure);
$manager->registerDisplayNameResolver('planet', $planetClosure);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testRegisterResolverInvalidType() {
$manager = $this->getManager();
$planetClosure = function ($name) {
return ucfirst($name);
};
$manager->registerDisplayNameResolver(1337, $planetClosure);
}
/**
* @expectedException \OutOfBoundsException
*/
public function testResolveDisplayNameUnregisteredType() {
$manager = $this->getManager();
$planetClosure = function ($name) {
return ucfirst($name);
};
$manager->registerDisplayNameResolver('planet', $planetClosure);
$manager->resolveDisplayName('galaxy', 'sombrero');
}
public function testResolveDisplayNameDirtyResolver() {
$manager = $this->getManager();
$planetClosure = function () {
return null;
};
$manager->registerDisplayNameResolver('planet', $planetClosure);
$this->assertTrue(is_string($manager->resolveDisplayName('planet', 'neptune')));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testResolveDisplayNameInvalidType() {
$manager = $this->getManager();
$planetClosure = function () {
return null;
};
$manager->registerDisplayNameResolver('planet', $planetClosure);
$this->assertTrue(is_string($manager->resolveDisplayName(1337, 'neptune')));
}
}