@@ -0,0 +1,211 @@ | |||
<?php | |||
/** | |||
* @author Arthur Schiwon <blizzz@owncloud.com> | |||
* | |||
* @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\DAV\Comments; | |||
use OCP\Comments\IComment; | |||
use OCP\Comments\ICommentsManager; | |||
use OCP\ILogger; | |||
use OCP\IUserManager; | |||
use Sabre\DAV\Exception\MethodNotAllowed; | |||
use Sabre\DAV\PropPatch; | |||
class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties { | |||
const NS_OWNCLOUD = 'http://owncloud.org/ns'; | |||
/** @var IComment */ | |||
public $comment; | |||
/** @var ICommentsManager */ | |||
protected $commentsManager; | |||
/** @var ILogger */ | |||
protected $logger; | |||
/** @var array list of properties with key being their name and value their setter */ | |||
protected $properties = []; | |||
/** @var IUserManager */ | |||
protected $userManager; | |||
/** | |||
* CommentNode constructor. | |||
* | |||
* @param ICommentsManager $commentsManager | |||
* @param IComment $comment | |||
* @param IUserManager $userManager | |||
* @param ILogger $logger | |||
*/ | |||
public function __construct( | |||
ICommentsManager $commentsManager, | |||
IComment $comment, | |||
IUserManager $userManager, | |||
ILogger $logger | |||
) { | |||
$this->commentsManager = $commentsManager; | |||
$this->comment = $comment; | |||
$this->logger = $logger; | |||
$methods = get_class_methods($this->comment); | |||
$methods = array_filter($methods, function($name){ | |||
return strpos($name, 'get') === 0; | |||
}); | |||
foreach($methods as $getter) { | |||
$name = '{'.self::NS_OWNCLOUD.'}' . lcfirst(substr($getter, 3)); | |||
$this->properties[$name] = $getter; | |||
} | |||
$this->userManager = $userManager; | |||
} | |||
/** | |||
* returns a list of all possible property names | |||
* | |||
* @return array | |||
*/ | |||
static public function getPropertyNames() { | |||
return [ | |||
'{http://owncloud.org/ns}id', | |||
'{http://owncloud.org/ns}parentId', | |||
'{http://owncloud.org/ns}topmostParentId', | |||
'{http://owncloud.org/ns}childrenCount', | |||
'{http://owncloud.org/ns}message', | |||
'{http://owncloud.org/ns}verb', | |||
'{http://owncloud.org/ns}actorType', | |||
'{http://owncloud.org/ns}actorId', | |||
'{http://owncloud.org/ns}actorDisplayName', | |||
'{http://owncloud.org/ns}creationDateTime', | |||
'{http://owncloud.org/ns}latestChildDateTime', | |||
'{http://owncloud.org/ns}objectType', | |||
'{http://owncloud.org/ns}objectId', | |||
]; | |||
} | |||
/** | |||
* Deleted the current node | |||
* | |||
* @return void | |||
*/ | |||
function delete() { | |||
$this->commentsManager->delete($this->comment->getId()); | |||
} | |||
/** | |||
* Returns the name of the node. | |||
* | |||
* This is used to generate the url. | |||
* | |||
* @return string | |||
*/ | |||
function getName() { | |||
return $this->comment->getId(); | |||
} | |||
/** | |||
* Renames the node | |||
* | |||
* @param string $name The new name | |||
* @throws MethodNotAllowed | |||
*/ | |||
function setName($name) { | |||
throw new MethodNotAllowed(); | |||
} | |||
/** | |||
* Returns the last modification time, as a unix timestamp | |||
* | |||
* @return int | |||
*/ | |||
function getLastModified() { | |||
// we do not have a separate "mDateTime" field for updates currently. | |||
return $this->comment->getCreationDateTime()->getTimestamp(); | |||
} | |||
/** | |||
* update the comment's message | |||
* | |||
* @param $propertyValue | |||
* @return bool | |||
*/ | |||
public function updateComment($propertyValue) { | |||
try { | |||
$this->comment->setMessage($propertyValue); | |||
$this->commentsManager->save($this->comment); | |||
return true; | |||
} catch (\Exception $e) { | |||
$this->logger->logException($e, ['app' => 'dav/comments']); | |||
return false; | |||
} | |||
} | |||
/** | |||
* Updates properties on this node. | |||
* | |||
* This method received a PropPatch object, which contains all the | |||
* information about the update. | |||
* | |||
* To update specific properties, call the 'handle' method on this object. | |||
* Read the PropPatch documentation for more information. | |||
* | |||
* @param PropPatch $propPatch | |||
* @return void | |||
*/ | |||
function propPatch(PropPatch $propPatch) { | |||
// other properties than 'message' are read only | |||
$propPatch->handle('{'.self::NS_OWNCLOUD.'}message', [$this, 'updateComment']); | |||
$propPatch->commit(); | |||
} | |||
/** | |||
* Returns a list of properties for this nodes. | |||
* | |||
* The properties list is a list of propertynames the client requested, | |||
* encoded in clark-notation {xmlnamespace}tagname | |||
* | |||
* If the array is empty, it means 'all properties' were requested. | |||
* | |||
* Note that it's fine to liberally give properties back, instead of | |||
* conforming to the list of requested properties. | |||
* The Server class will filter out the extra. | |||
* | |||
* @param array $properties | |||
* @return array | |||
*/ | |||
function getProperties($properties) { | |||
$properties = array_keys($this->properties); | |||
$result = []; | |||
foreach($properties as $property) { | |||
$getter = $this->properties[$property]; | |||
if(method_exists($this->comment, $getter)) { | |||
$result[$property] = $this->comment->$getter(); | |||
} | |||
} | |||
if($this->comment->getActorType() === 'users') { | |||
$user = $this->userManager->get($this->comment->getActorId()); | |||
$displayName = is_null($user) ? null : $user->getDisplayName(); | |||
$result['{' . self::NS_OWNCLOUD . '}actorDisplayName'] = $displayName; | |||
} | |||
return $result; | |||
} | |||
} |
@@ -0,0 +1,243 @@ | |||
<?php | |||
/** | |||
* @author Arthur Schiwon <blizzz@owncloud.com> | |||
* | |||
* @copyright Copyright (c) 2015, 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\DAV\Comments; | |||
use OCP\Comments\IComment; | |||
use OCP\Comments\ICommentsManager; | |||
use OCP\IUserSession; | |||
use Sabre\DAV\Exception\BadRequest; | |||
use Sabre\DAV\Exception\ReportNotSupported; | |||
use Sabre\DAV\Exception\UnsupportedMediaType; | |||
use Sabre\DAV\Exception\NotFound; | |||
use Sabre\DAV\Server; | |||
use Sabre\DAV\ServerPlugin; | |||
use Sabre\DAV\Xml\Element\Response; | |||
use Sabre\DAV\Xml\Response\MultiStatus; | |||
use Sabre\HTTP\RequestInterface; | |||
use Sabre\HTTP\ResponseInterface; | |||
use Sabre\Xml\Writer; | |||
/** | |||
* Sabre plugin to handle comments: | |||
*/ | |||
class CommentsPlugin extends ServerPlugin { | |||
// namespace | |||
const NS_OWNCLOUD = 'http://owncloud.org/ns'; | |||
const REPORT_PARAM_LIMIT = '{http://owncloud.org/ns}limit'; | |||
const REPORT_PARAM_OFFSET = '{http://owncloud.org/ns}offset'; | |||
const REPORT_PARAM_TIMESTAMP = '{http://owncloud.org/ns}datetime'; | |||
/** @var ICommentsManager */ | |||
protected $commentsManager; | |||
/** @var \Sabre\DAV\Server $server */ | |||
private $server; | |||
/** @var \OCP\IUserSession */ | |||
protected $userSession; | |||
/** | |||
* Comments plugin | |||
* | |||
* @param ICommentsManager $commentsManager | |||
* @param IUserSession $userSession | |||
*/ | |||
public function __construct(ICommentsManager $commentsManager, IUserSession $userSession) { | |||
$this->commentsManager = $commentsManager; | |||
$this->userSession = $userSession; | |||
} | |||
/** | |||
* This initializes the plugin. | |||
* | |||
* This function is called by Sabre\DAV\Server, after | |||
* addPlugin is called. | |||
* | |||
* This method should set up the required event subscriptions. | |||
* | |||
* @param Server $server | |||
* @return void | |||
*/ | |||
function initialize(Server $server) { | |||
$this->server = $server; | |||
if(strpos($this->server->getRequestUri(), 'comments/') !== 0) { | |||
return; | |||
} | |||
$this->server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; | |||
$this->server->xml->classMap['DateTime'] = function(Writer $writer, \DateTime $value) { | |||
$writer->write($value->format('Y-m-d H:m:i')); | |||
}; | |||
$this->server->on('report', [$this, 'onReport']); | |||
$this->server->on('method:POST', [$this, 'httpPost']); | |||
} | |||
/** | |||
* POST operation on Comments collections | |||
* | |||
* @param RequestInterface $request request object | |||
* @param ResponseInterface $response response object | |||
* @return null|false | |||
*/ | |||
public function httpPost(RequestInterface $request, ResponseInterface $response) { | |||
$path = $request->getPath(); | |||
// Making sure the node exists | |||
try { | |||
$node = $this->server->tree->getNodeForPath($path); | |||
} catch (NotFound $e) { | |||
return null; | |||
} | |||
if ($node instanceof EntityCollection) { | |||
$data = $request->getBodyAsString(); | |||
$comment = $this->createComment( | |||
$node->getName(), | |||
$node->getId(), | |||
$data, | |||
$request->getHeader('Content-Type') | |||
); | |||
$url = $request->getUrl() . '/' . urlencode($comment->getId()); | |||
$response->setHeader('Content-Location', $url); | |||
// created | |||
$response->setStatus(201); | |||
return false; | |||
} | |||
} | |||
/** | |||
* REPORT operations to look for comments | |||
* | |||
* @param string $reportName | |||
* @param [] $report | |||
* @param string $uri | |||
* @return bool | |||
* @throws NotFound | |||
* @throws ReportNotSupported | |||
*/ | |||
public function onReport($reportName, $report, $uri) { | |||
$node = $this->server->tree->getNodeForPath($uri); | |||
if(!$node instanceof EntityCollection) { | |||
throw new ReportNotSupported(); | |||
} | |||
$args = ['limit' => 0, 'offset' => 0, 'datetime' => null]; | |||
$acceptableParameters = [ | |||
$this::REPORT_PARAM_LIMIT, | |||
$this::REPORT_PARAM_OFFSET, | |||
$this::REPORT_PARAM_TIMESTAMP | |||
]; | |||
$ns = '{' . $this::NS_OWNCLOUD . '}'; | |||
foreach($report as $parameter) { | |||
if(!in_array($parameter['name'], $acceptableParameters) || empty($parameter['value'])) { | |||
continue; | |||
} | |||
$args[str_replace($ns, '', $parameter['name'])] = $parameter['value']; | |||
} | |||
if(!is_null($args['datetime'])) { | |||
$args['datetime'] = new \DateTime($args['datetime']); | |||
} | |||
$results = $node->findChildren($args['limit'], $args['offset'], $args['datetime']); | |||
$responses = []; | |||
foreach($results as $node) { | |||
$nodePath = $this->server->getRequestUri() . '/' . $node->comment->getId(); | |||
$resultSet = $this->server->getPropertiesForPath($nodePath, CommentNode::getPropertyNames()); | |||
if(isset($resultSet[0]) && isset($resultSet[0][200])) { | |||
$responses[] = new Response( | |||
$this->server->getBaseUri() . $nodePath, | |||
[200 => $resultSet[0][200]], | |||
200 | |||
); | |||
} | |||
} | |||
$xml = $this->server->xml->write( | |||
'{DAV:}multistatus', | |||
new MultiStatus($responses) | |||
); | |||
$this->server->httpResponse->setStatus(207); | |||
$this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); | |||
$this->server->httpResponse->setBody($xml); | |||
return false; | |||
} | |||
/** | |||
* Creates a new comment | |||
* | |||
* @param string $objectType e.g. "files" | |||
* @param string $objectId e.g. the file id | |||
* @param string $data JSON encoded string containing the properties of the tag to create | |||
* @param string $contentType content type of the data | |||
* @return IComment newly created comment | |||
* | |||
* @throws BadRequest if a field was missing | |||
* @throws UnsupportedMediaType if the content type is not supported | |||
*/ | |||
private function createComment($objectType, $objectId, $data, $contentType = 'application/json') { | |||
if (explode(';', $contentType)[0] === 'application/json') { | |||
$data = json_decode($data, true); | |||
} else { | |||
throw new UnsupportedMediaType(); | |||
} | |||
$actorType = $data['actorType']; | |||
$actorId = null; | |||
if($actorType === 'users') { | |||
$user = $this->userSession->getUser(); | |||
if(!is_null($user)) { | |||
$actorId = $user->getUID(); | |||
} | |||
} | |||
if(is_null($actorId)) { | |||
throw new BadRequest('Invalid actor "' . $actorType .'"'); | |||
} | |||
try { | |||
$comment = $this->commentsManager->create($actorType, $actorId, $objectType, $objectId); | |||
$properties = [ | |||
'message' => 'setMessage', | |||
'verb' => 'setVerb', | |||
]; | |||
foreach($properties as $property => $setter) { | |||
$comment->$setter($data[$property]); | |||
} | |||
$this->commentsManager->save($comment); | |||
return $comment; | |||
} catch (\InvalidArgumentException $e) { | |||
throw new BadRequest('Invalid input values', 0, $e); | |||
} | |||
} | |||
} |
@@ -0,0 +1,148 @@ | |||
<?php | |||
/** | |||
* @author Arthur Schiwon <blizzz@owncloud.com> | |||
* | |||
* @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\DAV\Comments; | |||
use OCP\Comments\ICommentsManager; | |||
use OCP\Files\Folder; | |||
use OCP\ILogger; | |||
use OCP\IUserManager; | |||
use Sabre\DAV\Exception\NotFound; | |||
/** | |||
* Class EntityCollection | |||
* | |||
* this represents a specific holder of comments, identified by an entity type | |||
* (class member $name) and an entity id (class member $id). | |||
* | |||
* @package OCA\DAV\Comments | |||
*/ | |||
class EntityCollection extends RootCollection { | |||
/** @var Folder */ | |||
protected $fileRoot; | |||
/** @var string */ | |||
protected $id; | |||
/** @var ILogger */ | |||
protected $logger; | |||
/** | |||
* @param string $id | |||
* @param string $name | |||
* @param ICommentsManager $commentsManager | |||
* @param Folder $fileRoot | |||
* @param IUserManager $userManager | |||
* @param ILogger $logger | |||
*/ | |||
public function __construct( | |||
$id, | |||
$name, | |||
ICommentsManager $commentsManager, | |||
Folder $fileRoot, | |||
IUserManager $userManager, | |||
ILogger $logger | |||
) { | |||
foreach(['id', 'name'] as $property) { | |||
$$property = trim($$property); | |||
if(empty($$property) || !is_string($$property)) { | |||
throw new \InvalidArgumentException('"' . $property . '" parameter must be non-empty string'); | |||
} | |||
} | |||
$this->id = $id; | |||
$this->name = $name; | |||
$this->commentsManager = $commentsManager; | |||
$this->fileRoot = $fileRoot; | |||
$this->logger = $logger; | |||
$this->userManager = $userManager; | |||
} | |||
/** | |||
* returns the ID of this entity | |||
* | |||
* @return string | |||
*/ | |||
public function getId() { | |||
return $this->id; | |||
} | |||
/** | |||
* Returns a specific child node, referenced by its name | |||
* | |||
* This method must throw Sabre\DAV\Exception\NotFound if the node does not | |||
* exist. | |||
* | |||
* @param string $name | |||
* @return \Sabre\DAV\INode | |||
* @throws NotFound | |||
*/ | |||
function getChild($name) { | |||
try { | |||
$comment = $this->commentsManager->get($name); | |||
return new CommentNode($this->commentsManager, $comment, $this->userManager, $this->logger); | |||
} catch (\OCP\Comments\NotFoundException $e) { | |||
throw new NotFound(); | |||
} | |||
} | |||
/** | |||
* Returns an array with all the child nodes | |||
* | |||
* @return \Sabre\DAV\INode[] | |||
*/ | |||
function getChildren() { | |||
return $this->findChildren(); | |||
} | |||
/** | |||
* Returns an array of comment nodes. Result can be influenced by offset, | |||
* limit and date time parameters. | |||
* | |||
* @param int $limit | |||
* @param int $offset | |||
* @param \DateTime|null $datetime | |||
* @return CommentNode[] | |||
*/ | |||
function findChildren($limit = 0, $offset = 0, \DateTime $datetime = null) { | |||
$comments = $this->commentsManager->getForObject($this->name, $this->id, $limit, $offset, $datetime); | |||
$result = []; | |||
foreach($comments as $comment) { | |||
$result[] = new CommentNode($this->commentsManager, $comment, $this->userManager, $this->logger); | |||
} | |||
return $result; | |||
} | |||
/** | |||
* Checks if a child-node with the specified name exists | |||
* | |||
* @param string $name | |||
* @return bool | |||
*/ | |||
function childExists($name) { | |||
try { | |||
$this->commentsManager->get($name); | |||
return true; | |||
} catch (\OCP\Comments\NotFoundException $e) { | |||
return false; | |||
} | |||
} | |||
} | |||
@@ -0,0 +1,120 @@ | |||
<?php | |||
/** | |||
* @author Arthur Schiwon <blizzz@owncloud.com> | |||
* | |||
* @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\DAV\Comments; | |||
use OCP\Comments\ICommentsManager; | |||
use OCP\Files\Folder; | |||
use OCP\ILogger; | |||
use OCP\IUserManager; | |||
use Sabre\DAV\Exception\Forbidden; | |||
use Sabre\DAV\Exception\MethodNotAllowed; | |||
/** | |||
* Class EntityTypeCollection | |||
* | |||
* This is collection on the type of things a user can leave comments on, for | |||
* example: 'files'. | |||
* | |||
* Its children are instances of EntityCollection (representing a specific | |||
* object, for example the file by id). | |||
* | |||
* @package OCA\DAV\Comments | |||
*/ | |||
class EntityTypeCollection extends RootCollection { | |||
/** @var Folder */ | |||
protected $fileRoot; | |||
/** @var ILogger */ | |||
protected $logger; | |||
/** | |||
* @param string $name | |||
* @param ICommentsManager $commentsManager | |||
* @param Folder $fileRoot | |||
* @param IUserManager $userManager | |||
* @param ILogger $logger | |||
*/ | |||
public function __construct( | |||
$name, | |||
ICommentsManager $commentsManager, | |||
Folder $fileRoot, | |||
IUserManager $userManager, | |||
ILogger $logger | |||
) { | |||
$name = trim($name); | |||
if(empty($name) || !is_string($name)) { | |||
throw new \InvalidArgumentException('"name" parameter must be non-empty string'); | |||
} | |||
$this->name = $name; | |||
$this->commentsManager = $commentsManager; | |||
$this->fileRoot = $fileRoot; | |||
$this->logger = $logger; | |||
$this->userManager = $userManager; | |||
} | |||
/** | |||
* Returns a specific child node, referenced by its name | |||
* | |||
* This method must throw Sabre\DAV\Exception\NotFound if the node does not | |||
* exist. | |||
* | |||
* @param string $name | |||
* @return \Sabre\DAV\INode | |||
* @throws Forbidden | |||
*/ | |||
function getChild($name) { | |||
if(!$this->childExists($name)) { | |||
throw new Forbidden('Entity does not exist or is not available'); | |||
} | |||
return new EntityCollection( | |||
$name, | |||
$this->name, | |||
$this->commentsManager, | |||
$this->fileRoot, | |||
$this->userManager, | |||
$this->logger | |||
); | |||
} | |||
/** | |||
* Returns an array with all the child nodes | |||
* | |||
* @return \Sabre\DAV\INode[] | |||
* @throws MethodNotAllowed | |||
*/ | |||
function getChildren() { | |||
throw new MethodNotAllowed('No permission to list folder contents'); | |||
} | |||
/** | |||
* Checks if a child-node with the specified name exists | |||
* | |||
* @param string $name | |||
* @return bool | |||
*/ | |||
function childExists($name) { | |||
$nodes = $this->fileRoot->getById($name); | |||
return !empty($nodes); | |||
} | |||
} |
@@ -0,0 +1,204 @@ | |||
<?php | |||
/** | |||
* @author Arthur Schiwon <blizzz@owncloud.com> | |||
* | |||
* @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\DAV\Comments; | |||
use OCP\Comments\ICommentsManager; | |||
use OCP\Files\IRootFolder; | |||
use OCP\ILogger; | |||
use OCP\IUserManager; | |||
use OCP\IUserSession; | |||
use Sabre\DAV\Exception\NotAuthenticated; | |||
use Sabre\DAV\Exception\Forbidden; | |||
use Sabre\DAV\Exception\NotFound; | |||
use Sabre\DAV\ICollection; | |||
class RootCollection implements ICollection { | |||
/** @var EntityTypeCollection[] */ | |||
private $entityTypeCollections = []; | |||
/** @var ICommentsManager */ | |||
protected $commentsManager; | |||
/** @var string */ | |||
protected $name = 'comments'; | |||
/** @var ILogger */ | |||
protected $logger; | |||
/** @var IUserManager */ | |||
protected $userManager; | |||
/** | |||
* @var IUserSession | |||
*/ | |||
protected $userSession; | |||
/** | |||
* @var IRootFolder | |||
*/ | |||
protected $rootFolder; | |||
/** | |||
* @param ICommentsManager $commentsManager | |||
* @param IUserManager $userManager | |||
* @param IUserSession $userSession | |||
* @param IRootFolder $rootFolder | |||
* @param ILogger $logger | |||
*/ | |||
public function __construct( | |||
ICommentsManager $commentsManager, | |||
IUserManager $userManager, | |||
IUserSession $userSession, | |||
IRootFolder $rootFolder, | |||
ILogger $logger) | |||
{ | |||
$this->commentsManager = $commentsManager; | |||
$this->logger = $logger; | |||
$this->userManager = $userManager; | |||
$this->userSession = $userSession; | |||
$this->rootFolder = $rootFolder; | |||
} | |||
/** | |||
* initializes the collection. At this point of time, we need the logged in | |||
* user. Since it is not the case when the instance is created, we cannot | |||
* have this in the constructor. | |||
* | |||
* @throws NotAuthenticated | |||
*/ | |||
protected function initCollections() { | |||
if(!empty($this->entityTypeCollections)) { | |||
return; | |||
} | |||
$user = $this->userSession->getUser(); | |||
if(is_null($user)) { | |||
throw new NotAuthenticated(); | |||
} | |||
$userFolder = $this->rootFolder->getUserFolder($user->getUID()); | |||
$this->entityTypeCollections['files'] = new EntityTypeCollection( | |||
'files', | |||
$this->commentsManager, | |||
$userFolder, | |||
$this->userManager, | |||
$this->logger | |||
); | |||
} | |||
/** | |||
* Creates a new file in the directory | |||
* | |||
* @param string $name Name of the file | |||
* @param resource|string $data Initial payload | |||
* @return null|string | |||
* @throws Forbidden | |||
*/ | |||
function createFile($name, $data = null) { | |||
throw new Forbidden('Cannot create comments by id'); | |||
} | |||
/** | |||
* Creates a new subdirectory | |||
* | |||
* @param string $name | |||
* @throws Forbidden | |||
*/ | |||
function createDirectory($name) { | |||
throw new Forbidden('Permission denied to create collections'); | |||
} | |||
/** | |||
* Returns a specific child node, referenced by its name | |||
* | |||
* This method must throw Sabre\DAV\Exception\NotFound if the node does not | |||
* exist. | |||
* | |||
* @param string $name | |||
* @return \Sabre\DAV\INode | |||
* @throws NotFound | |||
*/ | |||
function getChild($name) { | |||
$this->initCollections(); | |||
if(isset($this->entityTypeCollections[$name])) { | |||
return $this->entityTypeCollections[$name]; | |||
} | |||
throw new NotFound('Entity type "' . $name . '" not found."'); | |||
} | |||
/** | |||
* Returns an array with all the child nodes | |||
* | |||
* @return \Sabre\DAV\INode[] | |||
*/ | |||
function getChildren() { | |||
$this->initCollections(); | |||
return $this->entityTypeCollections; | |||
} | |||
/** | |||
* Checks if a child-node with the specified name exists | |||
* | |||
* @param string $name | |||
* @return bool | |||
*/ | |||
function childExists($name) { | |||
$this->initCollections(); | |||
return isset($this->entityTypeCollections[$name]); | |||
} | |||
/** | |||
* Deleted the current node | |||
* | |||
* @throws Forbidden | |||
*/ | |||
function delete() { | |||
throw new Forbidden('Permission denied to delete this collection'); | |||
} | |||
/** | |||
* Returns the name of the node. | |||
* | |||
* This is used to generate the url. | |||
* | |||
* @return string | |||
*/ | |||
function getName() { | |||
return $this->name; | |||
} | |||
/** | |||
* Renames the node | |||
* | |||
* @param string $name The new name | |||
* @throws Forbidden | |||
*/ | |||
function setName($name) { | |||
throw new Forbidden('Permission denied to rename this collection'); | |||
} | |||
/** | |||
* Returns the last modification time, as a unix timestamp | |||
* | |||
* @return int | |||
*/ | |||
function getLastModified() { | |||
return null; | |||
} | |||
} |
@@ -70,6 +70,13 @@ class RootCollection extends SimpleCollection { | |||
\OC::$server->getUserSession(), | |||
\OC::$server->getGroupManager() | |||
); | |||
$commentsCollection = new Comments\RootCollection( | |||
\OC::$server->getCommentsManager(), | |||
\OC::$server->getUserManager(), | |||
\OC::$server->getUserSession(), | |||
\OC::$server->getRootFolder(), | |||
\OC::$server->getLogger() | |||
); | |||
$usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend); | |||
$usersAddressBookRoot = new AddressBookRoot($userPrincipalBackend, $usersCardDavBackend, 'principals/users'); | |||
@@ -91,6 +98,7 @@ class RootCollection extends SimpleCollection { | |||
$systemAddressBookRoot]), | |||
$systemTagCollection, | |||
$systemTagRelationsCollection, | |||
$commentsCollection, | |||
]; | |||
parent::__construct('root', $children); |
@@ -90,6 +90,12 @@ class Server { | |||
// system tags plugins | |||
$this->server->addPlugin(new \OCA\DAV\SystemTag\SystemTagPlugin(\OC::$server->getSystemTagManager())); | |||
// comments plugin | |||
$this->server->addPlugin(new \OCA\DAV\Comments\CommentsPlugin( | |||
\OC::$server->getCommentsManager(), | |||
\OC::$server->getUserSession() | |||
)); | |||
// Finder on OS X requires Class 2 WebDAV support (locking), since we do | |||
// not provide locking we emulate it using a fake locking plugin. | |||
if($request->isUserAgent(['/WebDAVFS/'])) { |
@@ -0,0 +1,215 @@ | |||
<?php | |||
/** | |||
* @author Arthur Schiwon <blizzz@owncloud.com> | |||
* | |||
* @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\DAV\Tests\Unit\Comments; | |||
use OCA\DAV\Comments\CommentNode; | |||
class CommentsNode extends \Test\TestCase { | |||
protected $commentsManager; | |||
protected $comment; | |||
protected $node; | |||
protected $userManager; | |||
protected $logger; | |||
public function setUp() { | |||
parent::setUp(); | |||
$this->commentsManager = $this->getMock('\OCP\Comments\ICommentsManager'); | |||
$this->comment = $this->getMock('\OCP\Comments\IComment'); | |||
$this->userManager = $this->getMock('\OCP\IUserManager'); | |||
$this->logger = $this->getMock('\OCP\ILogger'); | |||
$this->node = new CommentNode($this->commentsManager, $this->comment, $this->userManager, $this->logger); | |||
} | |||
public function testDelete() { | |||
$this->comment->expects($this->once()) | |||
->method('getId') | |||
->will($this->returnValue('19')); | |||
$this->commentsManager->expects($this->once()) | |||
->method('delete') | |||
->with('19'); | |||
$this->node->delete(); | |||
} | |||
public function testGetName() { | |||
$id = '19'; | |||
$this->comment->expects($this->once()) | |||
->method('getId') | |||
->will($this->returnValue($id)); | |||
$this->assertSame($this->node->getName(), $id); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\MethodNotAllowed | |||
*/ | |||
public function testSetName() { | |||
$this->node->setName('666'); | |||
} | |||
public function testGetLastModified() { | |||
$dateTime = new \DateTime('2016-01-10 18:48:00'); | |||
$this->comment->expects($this->once()) | |||
->method('getCreationDateTime') | |||
->will($this->returnValue($dateTime)); | |||
$this->assertSame($this->node->getLastModified(), $dateTime->getTimestamp()); | |||
} | |||
public function testUpdateComment() { | |||
$msg = 'Hello Earth'; | |||
$this->comment->expects($this->once()) | |||
->method('setMessage') | |||
->with($msg); | |||
$this->commentsManager->expects($this->once()) | |||
->method('save') | |||
->with($this->comment); | |||
$this->assertTrue($this->node->updateComment($msg)); | |||
} | |||
public function testUpdateCommentException() { | |||
$msg = null; | |||
$this->comment->expects($this->once()) | |||
->method('setMessage') | |||
->with($msg) | |||
->will($this->throwException(new \Exception('buh!'))); | |||
$this->commentsManager->expects($this->never()) | |||
->method('save'); | |||
$this->logger->expects($this->once()) | |||
->method('logException'); | |||
$this->assertFalse($this->node->updateComment($msg)); | |||
} | |||
public function testPropPatch() { | |||
$propPatch = $this->getMockBuilder('Sabre\DAV\PropPatch') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$propPatch->expects($this->once()) | |||
->method('handle') | |||
->with('{http://owncloud.org/ns}message'); | |||
$propPatch->expects($this->once()) | |||
->method('commit'); | |||
$this->node->propPatch($propPatch); | |||
} | |||
public function testGetProperties() { | |||
$ns = '{http://owncloud.org/ns}'; | |||
$expected = [ | |||
$ns . 'id' => '123', | |||
$ns . 'parentId' => '12', | |||
$ns . 'topmostParentId' => '2', | |||
$ns . 'childrenCount' => 3, | |||
$ns . 'message' => 'such a nice file you have…', | |||
$ns . 'verb' => 'comment', | |||
$ns . 'actorType' => 'users', | |||
$ns . 'actorId' => 'alice', | |||
$ns . 'actorDisplayName' => 'Alice of Wonderland', | |||
$ns . 'creationDateTime' => new \DateTime('2016-01-10 18:48:00'), | |||
$ns . 'latestChildDateTime' => new \DateTime('2016-01-12 18:48:00'), | |||
$ns . 'objectType' => 'files', | |||
$ns . 'objectId' => '1848', | |||
]; | |||
$this->comment->expects($this->once()) | |||
->method('getId') | |||
->will($this->returnValue($expected[$ns . 'id'])); | |||
$this->comment->expects($this->once()) | |||
->method('getParentId') | |||
->will($this->returnValue($expected[$ns . 'parentId'])); | |||
$this->comment->expects($this->once()) | |||
->method('getTopmostParentId') | |||
->will($this->returnValue($expected[$ns . 'topmostParentId'])); | |||
$this->comment->expects($this->once()) | |||
->method('getChildrenCount') | |||
->will($this->returnValue($expected[$ns . 'childrenCount'])); | |||
$this->comment->expects($this->once()) | |||
->method('getMessage') | |||
->will($this->returnValue($expected[$ns . 'message'])); | |||
$this->comment->expects($this->once()) | |||
->method('getVerb') | |||
->will($this->returnValue($expected[$ns . 'verb'])); | |||
$this->comment->expects($this->exactly(2)) | |||
->method('getActorType') | |||
->will($this->returnValue($expected[$ns . 'actorType'])); | |||
$this->comment->expects($this->exactly(2)) | |||
->method('getActorId') | |||
->will($this->returnValue($expected[$ns . 'actorId'])); | |||
$this->comment->expects($this->once()) | |||
->method('getCreationDateTime') | |||
->will($this->returnValue($expected[$ns . 'creationDateTime'])); | |||
$this->comment->expects($this->once()) | |||
->method('getLatestChildDateTime') | |||
->will($this->returnValue($expected[$ns . 'latestChildDateTime'])); | |||
$this->comment->expects($this->once()) | |||
->method('getObjectType') | |||
->will($this->returnValue($expected[$ns . 'objectType'])); | |||
$this->comment->expects($this->once()) | |||
->method('getObjectId') | |||
->will($this->returnValue($expected[$ns . 'objectId'])); | |||
$user = $this->getMockBuilder('\OCP\IUser') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$user->expects($this->once()) | |||
->method('getDisplayName') | |||
->will($this->returnValue($expected[$ns . 'actorDisplayName'])); | |||
$this->userManager->expects($this->once()) | |||
->method('get') | |||
->with('alice') | |||
->will($this->returnValue($user)); | |||
$properties = $this->node->getProperties(null); | |||
foreach($properties as $name => $value) { | |||
$this->assertTrue(isset($expected[$name])); | |||
$this->assertSame($expected[$name], $value); | |||
unset($expected[$name]); | |||
} | |||
$this->assertTrue(empty($expected)); | |||
} | |||
} |
@@ -0,0 +1,631 @@ | |||
<?php | |||
/** | |||
* @author Arthur Schiwon <blizzz@owncloud.com> | |||
* | |||
* @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\DAV\Tests\Unit\Comments; | |||
use OC\Comments\Comment; | |||
use OCA\DAV\Comments\CommentsPlugin as CommentsPluginImplementation; | |||
class CommentsPlugin extends \Test\TestCase { | |||
/** @var \Sabre\DAV\Server */ | |||
private $server; | |||
/** @var \Sabre\DAV\Tree */ | |||
private $tree; | |||
/** @var \OCP\Comments\ICommentsManager */ | |||
private $commentsManager; | |||
/** @var \OCP\IUserSession */ | |||
private $userSession; | |||
/** @var CommentsPluginImplementation */ | |||
private $plugin; | |||
public function setUp() { | |||
parent::setUp(); | |||
$this->tree = $this->getMockBuilder('\Sabre\DAV\Tree') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$this->server = $this->getMockBuilder('\Sabre\DAV\Server') | |||
->setConstructorArgs([$this->tree]) | |||
->setMethods(['getRequestUri']) | |||
->getMock(); | |||
$this->commentsManager = $this->getMock('\OCP\Comments\ICommentsManager'); | |||
$this->userSession = $this->getMock('\OCP\IUserSession'); | |||
$this->plugin = new CommentsPluginImplementation($this->commentsManager, $this->userSession); | |||
} | |||
public function testCreateComment() { | |||
$commentData = [ | |||
'actorType' => 'users', | |||
'verb' => 'comment', | |||
'message' => 'my first comment', | |||
]; | |||
$comment = new Comment([ | |||
'objectType' => 'files', | |||
'objectId' => '42', | |||
'actorType' => 'users', | |||
'actorId' => 'alice' | |||
] + $commentData); | |||
$comment->setId('23'); | |||
$path = 'comments/files/42'; | |||
$requestData = json_encode($commentData); | |||
$user = $this->getMock('OCP\IUser'); | |||
$user->expects($this->once()) | |||
->method('getUID') | |||
->will($this->returnValue('alice')); | |||
$node = $this->getMockBuilder('\OCA\DAV\Comments\EntityCollection') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$node->expects($this->once()) | |||
->method('getName') | |||
->will($this->returnValue('files')); | |||
$node->expects($this->once()) | |||
->method('getId') | |||
->will($this->returnValue('42')); | |||
$this->commentsManager->expects($this->once()) | |||
->method('create') | |||
->with('users', 'alice', 'files', '42') | |||
->will($this->returnValue($comment)); | |||
$this->userSession->expects($this->once()) | |||
->method('getUser') | |||
->will($this->returnValue($user)); | |||
// technically, this is a shortcut. Inbetween EntityTypeCollection would | |||
// be returned, but doing it exactly right would not be really | |||
// unit-testing like, as it would require to haul in a lot of other | |||
// things. | |||
$this->tree->expects($this->any()) | |||
->method('getNodeForPath') | |||
->with('/' . $path) | |||
->will($this->returnValue($node)); | |||
$request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$request->expects($this->once()) | |||
->method('getPath') | |||
->will($this->returnValue('/' . $path)); | |||
$request->expects($this->once()) | |||
->method('getBodyAsString') | |||
->will($this->returnValue($requestData)); | |||
$request->expects($this->once()) | |||
->method('getHeader') | |||
->with('Content-Type') | |||
->will($this->returnValue('application/json')); | |||
$request->expects($this->once()) | |||
->method('getUrl') | |||
->will($this->returnValue('http://example.com/dav/' . $path)); | |||
$response->expects($this->once()) | |||
->method('setHeader') | |||
->with('Content-Location', 'http://example.com/dav/' . $path . '/23'); | |||
$this->server->expects($this->any()) | |||
->method('getRequestUri') | |||
->will($this->returnValue($path)); | |||
$this->plugin->initialize($this->server); | |||
$this->plugin->httpPost($request, $response); | |||
} | |||
public function testCreateCommentInvalidObject() { | |||
$commentData = [ | |||
'actorType' => 'users', | |||
'verb' => 'comment', | |||
'message' => 'my first comment', | |||
]; | |||
$comment = new Comment([ | |||
'objectType' => 'files', | |||
'objectId' => '666', | |||
'actorType' => 'users', | |||
'actorId' => 'alice' | |||
] + $commentData); | |||
$comment->setId('23'); | |||
$path = 'comments/files/666'; | |||
$user = $this->getMock('OCP\IUser'); | |||
$user->expects($this->never()) | |||
->method('getUID'); | |||
$node = $this->getMockBuilder('\OCA\DAV\Comments\EntityCollection') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$node->expects($this->never()) | |||
->method('getName'); | |||
$node->expects($this->never()) | |||
->method('getId'); | |||
$this->commentsManager->expects($this->never()) | |||
->method('create'); | |||
$this->userSession->expects($this->never()) | |||
->method('getUser'); | |||
// technically, this is a shortcut. Inbetween EntityTypeCollection would | |||
// be returned, but doing it exactly right would not be really | |||
// unit-testing like, as it would require to haul in a lot of other | |||
// things. | |||
$this->tree->expects($this->any()) | |||
->method('getNodeForPath') | |||
->with('/' . $path) | |||
->will($this->throwException(new \Sabre\DAV\Exception\NotFound())); | |||
$request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$request->expects($this->once()) | |||
->method('getPath') | |||
->will($this->returnValue('/' . $path)); | |||
$request->expects($this->never()) | |||
->method('getBodyAsString'); | |||
$request->expects($this->never()) | |||
->method('getHeader') | |||
->with('Content-Type'); | |||
$request->expects($this->never()) | |||
->method('getUrl'); | |||
$response->expects($this->never()) | |||
->method('setHeader'); | |||
$this->server->expects($this->any()) | |||
->method('getRequestUri') | |||
->will($this->returnValue($path)); | |||
$this->plugin->initialize($this->server); | |||
$this->plugin->httpPost($request, $response); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\BadRequest | |||
*/ | |||
public function testCreateCommentInvalidActor() { | |||
$commentData = [ | |||
'actorType' => 'robots', | |||
'verb' => 'comment', | |||
'message' => 'my first comment', | |||
]; | |||
$comment = new Comment([ | |||
'objectType' => 'files', | |||
'objectId' => '42', | |||
'actorType' => 'users', | |||
'actorId' => 'alice' | |||
] + $commentData); | |||
$comment->setId('23'); | |||
$path = 'comments/files/42'; | |||
$requestData = json_encode($commentData); | |||
$user = $this->getMock('OCP\IUser'); | |||
$user->expects($this->never()) | |||
->method('getUID'); | |||
$node = $this->getMockBuilder('\OCA\DAV\Comments\EntityCollection') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$node->expects($this->once()) | |||
->method('getName') | |||
->will($this->returnValue('files')); | |||
$node->expects($this->once()) | |||
->method('getId') | |||
->will($this->returnValue('42')); | |||
$this->commentsManager->expects($this->never()) | |||
->method('create'); | |||
$this->userSession->expects($this->never()) | |||
->method('getUser'); | |||
// technically, this is a shortcut. Inbetween EntityTypeCollection would | |||
// be returned, but doing it exactly right would not be really | |||
// unit-testing like, as it would require to haul in a lot of other | |||
// things. | |||
$this->tree->expects($this->any()) | |||
->method('getNodeForPath') | |||
->with('/' . $path) | |||
->will($this->returnValue($node)); | |||
$request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$request->expects($this->once()) | |||
->method('getPath') | |||
->will($this->returnValue('/' . $path)); | |||
$request->expects($this->once()) | |||
->method('getBodyAsString') | |||
->will($this->returnValue($requestData)); | |||
$request->expects($this->once()) | |||
->method('getHeader') | |||
->with('Content-Type') | |||
->will($this->returnValue('application/json')); | |||
$request->expects($this->never()) | |||
->method('getUrl'); | |||
$response->expects($this->never()) | |||
->method('setHeader'); | |||
$this->server->expects($this->any()) | |||
->method('getRequestUri') | |||
->will($this->returnValue($path)); | |||
$this->plugin->initialize($this->server); | |||
$this->plugin->httpPost($request, $response); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\UnsupportedMediaType | |||
*/ | |||
public function testCreateCommentUnsupportedMediaType() { | |||
$commentData = [ | |||
'actorType' => 'users', | |||
'verb' => 'comment', | |||
'message' => 'my first comment', | |||
]; | |||
$comment = new Comment([ | |||
'objectType' => 'files', | |||
'objectId' => '42', | |||
'actorType' => 'users', | |||
'actorId' => 'alice' | |||
] + $commentData); | |||
$comment->setId('23'); | |||
$path = 'comments/files/42'; | |||
$requestData = json_encode($commentData); | |||
$user = $this->getMock('OCP\IUser'); | |||
$user->expects($this->never()) | |||
->method('getUID'); | |||
$node = $this->getMockBuilder('\OCA\DAV\Comments\EntityCollection') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$node->expects($this->once()) | |||
->method('getName') | |||
->will($this->returnValue('files')); | |||
$node->expects($this->once()) | |||
->method('getId') | |||
->will($this->returnValue('42')); | |||
$this->commentsManager->expects($this->never()) | |||
->method('create'); | |||
$this->userSession->expects($this->never()) | |||
->method('getUser'); | |||
// technically, this is a shortcut. Inbetween EntityTypeCollection would | |||
// be returned, but doing it exactly right would not be really | |||
// unit-testing like, as it would require to haul in a lot of other | |||
// things. | |||
$this->tree->expects($this->any()) | |||
->method('getNodeForPath') | |||
->with('/' . $path) | |||
->will($this->returnValue($node)); | |||
$request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$request->expects($this->once()) | |||
->method('getPath') | |||
->will($this->returnValue('/' . $path)); | |||
$request->expects($this->once()) | |||
->method('getBodyAsString') | |||
->will($this->returnValue($requestData)); | |||
$request->expects($this->once()) | |||
->method('getHeader') | |||
->with('Content-Type') | |||
->will($this->returnValue('application/trumpscript')); | |||
$request->expects($this->never()) | |||
->method('getUrl'); | |||
$response->expects($this->never()) | |||
->method('setHeader'); | |||
$this->server->expects($this->any()) | |||
->method('getRequestUri') | |||
->will($this->returnValue($path)); | |||
$this->plugin->initialize($this->server); | |||
$this->plugin->httpPost($request, $response); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\BadRequest | |||
*/ | |||
public function testCreateCommentInvalidPayload() { | |||
$commentData = [ | |||
'actorType' => 'users', | |||
'verb' => '', | |||
'message' => '', | |||
]; | |||
$comment = new Comment([ | |||
'objectType' => 'files', | |||
'objectId' => '42', | |||
'actorType' => 'users', | |||
'actorId' => 'alice', | |||
'message' => 'dummy', | |||
'verb' => 'dummy' | |||
]); | |||
$comment->setId('23'); | |||
$path = 'comments/files/42'; | |||
$requestData = json_encode($commentData); | |||
$user = $this->getMock('OCP\IUser'); | |||
$user->expects($this->once()) | |||
->method('getUID') | |||
->will($this->returnValue('alice')); | |||
$node = $this->getMockBuilder('\OCA\DAV\Comments\EntityCollection') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$node->expects($this->once()) | |||
->method('getName') | |||
->will($this->returnValue('files')); | |||
$node->expects($this->once()) | |||
->method('getId') | |||
->will($this->returnValue('42')); | |||
$this->commentsManager->expects($this->once()) | |||
->method('create') | |||
->with('users', 'alice', 'files', '42') | |||
->will($this->returnValue($comment)); | |||
$this->commentsManager->expects($this->any()) | |||
->method('setMessage') | |||
->with('') | |||
->will($this->throwException(new \InvalidArgumentException())); | |||
$this->commentsManager->expects($this->any()) | |||
->method('setVerb') | |||
->with('') | |||
->will($this->throwException(new \InvalidArgumentException())); | |||
$this->userSession->expects($this->once()) | |||
->method('getUser') | |||
->will($this->returnValue($user)); | |||
// technically, this is a shortcut. Inbetween EntityTypeCollection would | |||
// be returned, but doing it exactly right would not be really | |||
// unit-testing like, as it would require to haul in a lot of other | |||
// things. | |||
$this->tree->expects($this->any()) | |||
->method('getNodeForPath') | |||
->with('/' . $path) | |||
->will($this->returnValue($node)); | |||
$request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$request->expects($this->once()) | |||
->method('getPath') | |||
->will($this->returnValue('/' . $path)); | |||
$request->expects($this->once()) | |||
->method('getBodyAsString') | |||
->will($this->returnValue($requestData)); | |||
$request->expects($this->once()) | |||
->method('getHeader') | |||
->with('Content-Type') | |||
->will($this->returnValue('application/json')); | |||
$request->expects($this->never()) | |||
->method('getUrl'); | |||
$response->expects($this->never()) | |||
->method('setHeader'); | |||
$this->server->expects($this->any()) | |||
->method('getRequestUri') | |||
->will($this->returnValue($path)); | |||
$this->plugin->initialize($this->server); | |||
$this->plugin->httpPost($request, $response); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\ReportNotSupported | |||
*/ | |||
public function testOnReportInvalidNode() { | |||
$path = 'totally/unrelated/13'; | |||
$this->tree->expects($this->any()) | |||
->method('getNodeForPath') | |||
->with('/' . $path) | |||
->will($this->returnValue($this->getMock('\Sabre\DAV\INode'))); | |||
$this->server->expects($this->any()) | |||
->method('getRequestUri') | |||
->will($this->returnValue($path)); | |||
$this->plugin->initialize($this->server); | |||
$this->plugin->onReport('', [], '/' . $path); | |||
} | |||
public function testOnReportDateTimeEmpty() { | |||
$path = 'comments/files/42'; | |||
$parameters = [ | |||
[ | |||
'name' => '{http://owncloud.org/ns}limit', | |||
'value' => 5, | |||
], | |||
[ | |||
'name' => '{http://owncloud.org/ns}offset', | |||
'value' => 10, | |||
], | |||
[ | |||
'name' => '{http://owncloud.org/ns}datetime', | |||
'value' => '', | |||
] | |||
]; | |||
$node = $this->getMockBuilder('\OCA\DAV\Comments\EntityCollection') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$node->expects($this->once()) | |||
->method('findChildren') | |||
->with(5, 10, null) | |||
->will($this->returnValue([])); | |||
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$response->expects($this->once()) | |||
->method('setHeader') | |||
->with('Content-Type', 'application/xml; charset=utf-8'); | |||
$response->expects($this->once()) | |||
->method('setStatus') | |||
->with(207); | |||
$response->expects($this->once()) | |||
->method('setBody'); | |||
$this->tree->expects($this->any()) | |||
->method('getNodeForPath') | |||
->with('/' . $path) | |||
->will($this->returnValue($node)); | |||
$this->server->expects($this->any()) | |||
->method('getRequestUri') | |||
->will($this->returnValue($path)); | |||
$this->server->httpResponse = $response; | |||
$this->plugin->initialize($this->server); | |||
$this->plugin->onReport('', $parameters, '/' . $path); | |||
} | |||
public function testOnReport() { | |||
$path = 'comments/files/42'; | |||
$parameters = [ | |||
[ | |||
'name' => '{http://owncloud.org/ns}limit', | |||
'value' => 5, | |||
], | |||
[ | |||
'name' => '{http://owncloud.org/ns}offset', | |||
'value' => 10, | |||
], | |||
[ | |||
'name' => '{http://owncloud.org/ns}datetime', | |||
'value' => '2016-01-10 18:48:00', | |||
] | |||
]; | |||
$node = $this->getMockBuilder('\OCA\DAV\Comments\EntityCollection') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$node->expects($this->once()) | |||
->method('findChildren') | |||
->with(5, 10, new \DateTime($parameters[2]['value'])) | |||
->will($this->returnValue([])); | |||
$response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') | |||
->disableOriginalConstructor() | |||
->getMock(); | |||
$response->expects($this->once()) | |||
->method('setHeader') | |||
->with('Content-Type', 'application/xml; charset=utf-8'); | |||
$response->expects($this->once()) | |||
->method('setStatus') | |||
->with(207); | |||
$response->expects($this->once()) | |||
->method('setBody'); | |||
$this->tree->expects($this->any()) | |||
->method('getNodeForPath') | |||
->with('/' . $path) | |||
->will($this->returnValue($node)); | |||
$this->server->expects($this->any()) | |||
->method('getRequestUri') | |||
->will($this->returnValue($path)); | |||
$this->server->httpResponse = $response; | |||
$this->plugin->initialize($this->server); | |||
$this->plugin->onReport('', $parameters, '/' . $path); | |||
} | |||
} |
@@ -0,0 +1,113 @@ | |||
<?php | |||
/** | |||
* @author Arthur Schiwon <blizzz@owncloud.com> | |||
* | |||
* @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\DAV\Tests\Unit\Comments; | |||
class EntityCollection extends \Test\TestCase { | |||
protected $commentsManager; | |||
protected $folder; | |||
protected $userManager; | |||
protected $logger; | |||
protected $collection; | |||
public function setUp() { | |||
parent::setUp(); | |||
$this->commentsManager = $this->getMock('\OCP\Comments\ICommentsManager'); | |||
$this->folder = $this->getMock('\OCP\Files\Folder'); | |||
$this->userManager = $this->getMock('\OCP\IUserManager'); | |||
$this->logger = $this->getMock('\OCP\ILogger'); | |||
$this->collection = new \OCA\DAV\Comments\EntityCollection( | |||
'19', | |||
'files', | |||
$this->commentsManager, | |||
$this->folder, | |||
$this->userManager, | |||
$this->logger | |||
); | |||
} | |||
public function testGetId() { | |||
$this->assertSame($this->collection->getId(), '19'); | |||
} | |||
public function testGetChild() { | |||
$this->commentsManager->expects($this->once()) | |||
->method('get') | |||
->with('55') | |||
->will($this->returnValue($this->getMock('\OCP\Comments\IComment'))); | |||
$node = $this->collection->getChild('55'); | |||
$this->assertTrue($node instanceof \OCA\DAV\Comments\CommentNode); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\NotFound | |||
*/ | |||
public function testGetChildException() { | |||
$this->commentsManager->expects($this->once()) | |||
->method('get') | |||
->with('55') | |||
->will($this->throwException(new \OCP\Comments\NotFoundException())); | |||
$this->collection->getChild('55'); | |||
} | |||
public function testGetChildren() { | |||
$this->commentsManager->expects($this->once()) | |||
->method('getForObject') | |||
->with('files', '19') | |||
->will($this->returnValue([$this->getMock('\OCP\Comments\IComment')])); | |||
$result = $this->collection->getChildren(); | |||
$this->assertSame(count($result), 1); | |||
$this->assertTrue($result[0] instanceof \OCA\DAV\Comments\CommentNode); | |||
} | |||
public function testFindChildren() { | |||
$dt = new \DateTime('2016-01-10 18:48:00'); | |||
$this->commentsManager->expects($this->once()) | |||
->method('getForObject') | |||
->with('files', '19', 5, 15, $dt) | |||
->will($this->returnValue([$this->getMock('\OCP\Comments\IComment')])); | |||
$result = $this->collection->findChildren(5, 15, $dt); | |||
$this->assertSame(count($result), 1); | |||
$this->assertTrue($result[0] instanceof \OCA\DAV\Comments\CommentNode); | |||
} | |||
public function testChildExistsTrue() { | |||
$this->assertTrue($this->collection->childExists('44')); | |||
} | |||
public function testChildExistsFalse() { | |||
$this->commentsManager->expects($this->once()) | |||
->method('get') | |||
->with('44') | |||
->will($this->throwException(new \OCP\Comments\NotFoundException())); | |||
$this->assertFalse($this->collection->childExists('44')); | |||
} | |||
} |
@@ -0,0 +1,94 @@ | |||
<?php | |||
/** | |||
* @author Arthur Schiwon <blizzz@owncloud.com> | |||
* | |||
* @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\DAV\Tests\Unit\Comments; | |||
use OCA\DAV\Comments\EntityCollection as EntityCollectionImplemantation; | |||
class EntityTypeCollection extends \Test\TestCase { | |||
protected $commentsManager; | |||
protected $folder; | |||
protected $userManager; | |||
protected $logger; | |||
protected $collection; | |||
public function setUp() { | |||
parent::setUp(); | |||
$this->commentsManager = $this->getMock('\OCP\Comments\ICommentsManager'); | |||
$this->folder = $this->getMock('\OCP\Files\Folder'); | |||
$this->userManager = $this->getMock('\OCP\IUserManager'); | |||
$this->logger = $this->getMock('\OCP\ILogger'); | |||
$this->collection = new \OCA\DAV\Comments\EntityTypeCollection( | |||
'files', | |||
$this->commentsManager, | |||
$this->folder, | |||
$this->userManager, | |||
$this->logger | |||
); | |||
} | |||
public function testChildExistsYes() { | |||
$this->folder->expects($this->once()) | |||
->method('getById') | |||
->with('17') | |||
->will($this->returnValue([$this->getMock('\OCP\Files\Node')])); | |||
$this->assertTrue($this->collection->childExists('17')); | |||
} | |||
public function testChildExistsNo() { | |||
$this->folder->expects($this->once()) | |||
->method('getById') | |||
->will($this->returnValue([])); | |||
$this->assertFalse($this->collection->childExists('17')); | |||
} | |||
public function testGetChild() { | |||
$this->folder->expects($this->once()) | |||
->method('getById') | |||
->with('17') | |||
->will($this->returnValue([$this->getMock('\OCP\Files\Node')])); | |||
$ec = $this->collection->getChild('17'); | |||
$this->assertTrue($ec instanceof EntityCollectionImplemantation); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\Forbidden | |||
*/ | |||
public function testGetChildException() { | |||
$this->folder->expects($this->once()) | |||
->method('getById') | |||
->with('17') | |||
->will($this->returnValue([])); | |||
$this->collection->getChild('17'); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\MethodNotAllowed | |||
*/ | |||
public function testGetChildren() { | |||
$this->collection->getChildren(); | |||
} | |||
} |
@@ -0,0 +1,160 @@ | |||
<?php | |||
/** | |||
* @author Arthur Schiwon <blizzz@owncloud.com> | |||
* | |||
* @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\DAV\Tests\Unit\Comments; | |||
use OCA\DAV\Comments\EntityTypeCollection as EntityTypeCollectionImplementation; | |||
class RootCollection extends \Test\TestCase { | |||
protected $commentsManager; | |||
protected $userManager; | |||
protected $logger; | |||
protected $collection; | |||
protected $userSession; | |||
protected $rootFolder; | |||
protected $user; | |||
public function setUp() { | |||
parent::setUp(); | |||
$this->user = $this->getMock('\OCP\IUser'); | |||
$this->commentsManager = $this->getMock('\OCP\Comments\ICommentsManager'); | |||
$this->userManager = $this->getMock('\OCP\IUserManager'); | |||
$this->userSession = $this->getMock('\OCP\IUserSession'); | |||
$this->rootFolder = $this->getMock('\OCP\Files\IRootFolder'); | |||
$this->logger = $this->getMock('\OCP\ILogger'); | |||
$this->collection = new \OCA\DAV\Comments\RootCollection( | |||
$this->commentsManager, | |||
$this->userManager, | |||
$this->userSession, | |||
$this->rootFolder, | |||
$this->logger | |||
); | |||
} | |||
protected function prepareForInitCollections() { | |||
$this->user->expects($this->any()) | |||
->method('getUID') | |||
->will($this->returnValue('alice')); | |||
$this->userSession->expects($this->once()) | |||
->method('getUser') | |||
->will($this->returnValue($this->user)); | |||
$this->rootFolder->expects($this->once()) | |||
->method('getUserFolder') | |||
->with('alice') | |||
->will($this->returnValue($this->getMock('\OCP\Files\Folder'))); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\Forbidden | |||
*/ | |||
public function testCreateFile() { | |||
$this->collection->createFile('foo'); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\Forbidden | |||
*/ | |||
public function testCreateDirectory() { | |||
$this->collection->createDirectory('foo'); | |||
} | |||
public function testGetChild() { | |||
$this->prepareForInitCollections(); | |||
$etc = $this->collection->getChild('files'); | |||
$this->assertTrue($etc instanceof EntityTypeCollectionImplementation); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\NotFound | |||
*/ | |||
public function testGetChildInvalid() { | |||
$this->prepareForInitCollections(); | |||
$this->collection->getChild('robots'); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\NotAuthenticated | |||
*/ | |||
public function testGetChildNoAuth() { | |||
$this->collection->getChild('files'); | |||
} | |||
public function testGetChildren() { | |||
$this->prepareForInitCollections(); | |||
$children = $this->collection->getChildren(); | |||
$this->assertFalse(empty($children)); | |||
foreach($children as $child) { | |||
$this->assertTrue($child instanceof EntityTypeCollectionImplementation); | |||
} | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\NotAuthenticated | |||
*/ | |||
public function testGetChildrenNoAuth() { | |||
$this->collection->getChildren(); | |||
} | |||
public function testChildExistsYes() { | |||
$this->prepareForInitCollections(); | |||
$this->assertTrue($this->collection->childExists('files')); | |||
} | |||
public function testChildExistsNo() { | |||
$this->prepareForInitCollections(); | |||
$this->assertFalse($this->collection->childExists('robots')); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\NotAuthenticated | |||
*/ | |||
public function testChildExistsNoAuth() { | |||
$this->collection->childExists('files'); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\Forbidden | |||
*/ | |||
public function testDelete() { | |||
$this->collection->delete(); | |||
} | |||
public function testGetName() { | |||
$this->assertSame('comments', $this->collection->getName()); | |||
} | |||
/** | |||
* @expectedException \Sabre\DAV\Exception\Forbidden | |||
*/ | |||
public function testSetName() { | |||
$this->collection->setName('foobar'); | |||
} | |||
public function testGetLastModified() { | |||
$this->assertSame(null, $this->collection->getLastModified()); | |||
} | |||
} |
@@ -284,9 +284,9 @@ class Comment implements IComment { | |||
} | |||
/** | |||
* returns the timestamp of the most recent child | |||
* returns the DateTime of the most recent child, if set, otherwise null | |||
* | |||
* @return int | |||
* @return \DateTime|null | |||
* @since 9.0.0 | |||
*/ | |||
public function getLatestChildDateTime() { |
@@ -58,7 +58,9 @@ class Manager implements ICommentsManager { | |||
$data['parent_id'] = strval($data['parent_id']); | |||
$data['topmost_parent_id'] = strval($data['topmost_parent_id']); | |||
$data['creation_timestamp'] = new \DateTime($data['creation_timestamp']); | |||
$data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']); | |||
if (!is_null($data['latest_child_timestamp'])) { | |||
$data['latest_child_timestamp'] = new \DateTime($data['latest_child_timestamp']); | |||
} | |||
$data['children_count'] = intval($data['children_count']); | |||
return $data; | |||
} |