diff options
Diffstat (limited to 'apps/dav/lib/Comments')
-rw-r--r-- | apps/dav/lib/Comments/CommentNode.php | 259 | ||||
-rw-r--r-- | apps/dav/lib/Comments/CommentsPlugin.php | 232 | ||||
-rw-r--r-- | apps/dav/lib/Comments/EntityCollection.php | 168 | ||||
-rw-r--r-- | apps/dav/lib/Comments/EntityTypeCollection.php | 89 | ||||
-rw-r--r-- | apps/dav/lib/Comments/RootCollection.php | 169 |
5 files changed, 917 insertions, 0 deletions
diff --git a/apps/dav/lib/Comments/CommentNode.php b/apps/dav/lib/Comments/CommentNode.php new file mode 100644 index 00000000000..5dbefa82d93 --- /dev/null +++ b/apps/dav/lib/Comments/CommentNode.php @@ -0,0 +1,259 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\DAV\Comments; + +use OCP\Comments\IComment; +use OCP\Comments\ICommentsManager; +use OCP\Comments\MessageTooLongException; +use OCP\IUserManager; +use OCP\IUserSession; +use Psr\Log\LoggerInterface; +use Sabre\DAV\Exception\BadRequest; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\MethodNotAllowed; +use Sabre\DAV\PropPatch; + +class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties { + public const NS_OWNCLOUD = 'http://owncloud.org/ns'; + + public const PROPERTY_NAME_UNREAD = '{http://owncloud.org/ns}isUnread'; + public const PROPERTY_NAME_MESSAGE = '{http://owncloud.org/ns}message'; + public const PROPERTY_NAME_ACTOR_DISPLAYNAME = '{http://owncloud.org/ns}actorDisplayName'; + public const PROPERTY_NAME_MENTIONS = '{http://owncloud.org/ns}mentions'; + public const PROPERTY_NAME_MENTION = '{http://owncloud.org/ns}mention'; + public const PROPERTY_NAME_MENTION_TYPE = '{http://owncloud.org/ns}mentionType'; + public const PROPERTY_NAME_MENTION_ID = '{http://owncloud.org/ns}mentionId'; + public const PROPERTY_NAME_MENTION_DISPLAYNAME = '{http://owncloud.org/ns}mentionDisplayName'; + + /** @var array list of properties with key being their name and value their setter */ + protected $properties = []; + + /** + * CommentNode constructor. + */ + public function __construct( + protected ICommentsManager $commentsManager, + public IComment $comment, + protected IUserManager $userManager, + protected IUserSession $userSession, + protected LoggerInterface $logger, + ) { + $methods = get_class_methods($this->comment); + $methods = array_filter($methods, function ($name) { + return str_starts_with($name, 'get'); + }); + foreach ($methods as $getter) { + if ($getter === 'getMentions') { + continue; // special treatment + } + $name = '{' . self::NS_OWNCLOUD . '}' . lcfirst(substr($getter, 3)); + $this->properties[$name] = $getter; + } + } + + /** + * returns a list of all possible property names + * + * @return array + */ + public static 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}verb', + '{http://owncloud.org/ns}actorType', + '{http://owncloud.org/ns}actorId', + '{http://owncloud.org/ns}creationDateTime', + '{http://owncloud.org/ns}latestChildDateTime', + '{http://owncloud.org/ns}objectType', + '{http://owncloud.org/ns}objectId', + // re-used property names are defined as constants + self::PROPERTY_NAME_MESSAGE, + self::PROPERTY_NAME_ACTOR_DISPLAYNAME, + self::PROPERTY_NAME_UNREAD, + self::PROPERTY_NAME_MENTIONS, + self::PROPERTY_NAME_MENTION, + self::PROPERTY_NAME_MENTION_TYPE, + self::PROPERTY_NAME_MENTION_ID, + self::PROPERTY_NAME_MENTION_DISPLAYNAME, + ]; + } + + protected function checkWriteAccessOnComment() { + $user = $this->userSession->getUser(); + if ($this->comment->getActorType() !== 'users' + || is_null($user) + || $this->comment->getActorId() !== $user->getUID() + ) { + throw new Forbidden('Only authors are allowed to edit their comment.'); + } + } + + /** + * Deleted the current node + * + * @return void + */ + public function delete() { + $this->checkWriteAccessOnComment(); + $this->commentsManager->delete($this->comment->getId()); + } + + /** + * Returns the name of the node. + * + * This is used to generate the url. + * + * @return string + */ + public function getName() { + return $this->comment->getId(); + } + + /** + * Renames the node + * + * @param string $name The new name + * @throws MethodNotAllowed + */ + public function setName($name) { + throw new MethodNotAllowed(); + } + + /** + * Returns the last modification time, as a unix timestamp + */ + public function getLastModified(): ?int { + return null; + } + + /** + * update the comment's message + * + * @param $propertyValue + * @return bool + * @throws BadRequest + * @throws \Exception + */ + public function updateComment($propertyValue) { + $this->checkWriteAccessOnComment(); + try { + $this->comment->setMessage($propertyValue); + $this->commentsManager->save($this->comment); + return true; + } catch (\Exception $e) { + $this->logger->error($e->getMessage(), ['app' => 'dav/comments', 'exception' => $e]); + if ($e instanceof MessageTooLongException) { + $msg = 'Message exceeds allowed character limit of '; + throw new BadRequest($msg . IComment::MAX_MESSAGE_LENGTH, 0, $e); + } + throw $e; + } + } + + /** + * 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 + */ + public function propPatch(PropPatch $propPatch) { + // other properties than 'message' are read only + $propPatch->handle(self::PROPERTY_NAME_MESSAGE, [$this, 'updateComment']); + } + + /** + * 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 + */ + public 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::PROPERTY_NAME_ACTOR_DISPLAYNAME] = $displayName; + } + + $result[self::PROPERTY_NAME_MENTIONS] = $this->composeMentionsPropertyValue(); + + $unread = null; + $user = $this->userSession->getUser(); + if (!is_null($user)) { + $readUntil = $this->commentsManager->getReadMark( + $this->comment->getObjectType(), + $this->comment->getObjectId(), + $user + ); + if (is_null($readUntil)) { + $unread = 'true'; + } else { + $unread = $this->comment->getCreationDateTime() > $readUntil; + // re-format for output + $unread = $unread ? 'true' : 'false'; + } + } + $result[self::PROPERTY_NAME_UNREAD] = $unread; + + return $result; + } + + /** + * transforms a mentions array as returned from IComment->getMentions to an + * array with DAV-compatible structure that can be assigned to the + * PROPERTY_NAME_MENTION property. + * + * @return array + */ + protected function composeMentionsPropertyValue() { + return array_map(function ($mention) { + try { + $displayName = $this->commentsManager->resolveDisplayName($mention['type'], $mention['id']); + } catch (\OutOfBoundsException $e) { + $this->logger->error($e->getMessage(), ['exception' => $e]); + // No displayname, upon client's discretion what to display. + $displayName = ''; + } + + return [ + self::PROPERTY_NAME_MENTION => [ + self::PROPERTY_NAME_MENTION_TYPE => $mention['type'], + self::PROPERTY_NAME_MENTION_ID => $mention['id'], + self::PROPERTY_NAME_MENTION_DISPLAYNAME => $displayName, + ] + ]; + }, $this->comment->getMentions()); + } +} diff --git a/apps/dav/lib/Comments/CommentsPlugin.php b/apps/dav/lib/Comments/CommentsPlugin.php new file mode 100644 index 00000000000..2ab7d6ee018 --- /dev/null +++ b/apps/dav/lib/Comments/CommentsPlugin.php @@ -0,0 +1,232 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\DAV\Comments; + +use OCP\AppFramework\Http; +use OCP\Comments\IComment; +use OCP\Comments\ICommentsManager; +use OCP\Comments\MessageTooLongException; +use OCP\IUserSession; +use Sabre\DAV\Exception\BadRequest; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\Exception\ReportNotSupported; +use Sabre\DAV\Exception\UnsupportedMediaType; +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 + public const NS_OWNCLOUD = 'http://owncloud.org/ns'; + + public const REPORT_NAME = '{http://owncloud.org/ns}filter-comments'; + public const REPORT_PARAM_LIMIT = '{http://owncloud.org/ns}limit'; + public const REPORT_PARAM_OFFSET = '{http://owncloud.org/ns}offset'; + public const REPORT_PARAM_TIMESTAMP = '{http://owncloud.org/ns}datetime'; + + /** @var \Sabre\DAV\Server $server */ + private $server; + + /** + * Comments plugin + * + * @param ICommentsManager $commentsManager + * @param IUserSession $userSession + */ + public function __construct( + protected ICommentsManager $commentsManager, + protected IUserSession $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 + */ + public function initialize(Server $server) { + $this->server = $server; + if (!str_starts_with($this->server->getRequestUri(), 'comments/')) { + return; + } + + $this->server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; + + $this->server->xml->classMap['DateTime'] = function (Writer $writer, \DateTime $value): void { + $writer->write(\Sabre\HTTP\toDate($value)); + }; + + $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(); + $node = $this->server->tree->getNodeForPath($path); + if (!$node instanceof EntityCollection) { + return null; + } + + $data = $request->getBodyAsString(); + $comment = $this->createComment( + $node->getName(), + $node->getId(), + $data, + $request->getHeader('Content-Type') + ); + + // update read marker for the current user/poster to avoid + // having their own comments marked as unread + $node->setReadMarker(null); + + $url = rtrim($request->getUrl(), '/') . '/' . urlencode($comment->getId()); + + $response->setHeader('Content-Location', $url); + + // created + $response->setStatus(Http::STATUS_CREATED); + return false; + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * + * @param string $uri + * @return array + */ + public function getSupportedReportSet($uri) { + return [self::REPORT_NAME]; + } + + /** + * REPORT operations to look for comments + * + * @param string $reportName + * @param array $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 || $reportName !== self::REPORT_NAME) { + 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((string)$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(Http::STATUS_MULTI_STATUS); + $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, 512, JSON_THROW_ON_ERROR); + } 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); + $comment->setMessage($data['message']); + $comment->setVerb($data['verb']); + $this->commentsManager->save($comment); + return $comment; + } catch (\InvalidArgumentException $e) { + throw new BadRequest('Invalid input values', 0, $e); + } catch (MessageTooLongException $e) { + $msg = 'Message exceeds allowed character limit of '; + throw new BadRequest($msg . IComment::MAX_MESSAGE_LENGTH, 0, $e); + } + } +} diff --git a/apps/dav/lib/Comments/EntityCollection.php b/apps/dav/lib/Comments/EntityCollection.php new file mode 100644 index 00000000000..33c58ee44d2 --- /dev/null +++ b/apps/dav/lib/Comments/EntityCollection.php @@ -0,0 +1,168 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\DAV\Comments; + +use OCP\Comments\ICommentsManager; +use OCP\Comments\NotFoundException; +use OCP\IUserManager; +use OCP\IUserSession; +use Psr\Log\LoggerInterface; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\IProperties; +use Sabre\DAV\PropPatch; + +/** + * 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 implements IProperties { + public const PROPERTY_NAME_READ_MARKER = '{http://owncloud.org/ns}readMarker'; + + /** + * @param string $id + * @param string $name + * @param ICommentsManager $commentsManager + * @param IUserManager $userManager + * @param IUserSession $userSession + * @param LoggerInterface $logger + */ + public function __construct( + protected $id, + $name, + ICommentsManager $commentsManager, + IUserManager $userManager, + IUserSession $userSession, + protected LoggerInterface $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->name = $name; + $this->commentsManager = $commentsManager; + $this->userManager = $userManager; + $this->userSession = $userSession; + } + + /** + * 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 + */ + public function getChild($name) { + try { + $comment = $this->commentsManager->get($name); + return new CommentNode( + $this->commentsManager, + $comment, + $this->userManager, + $this->userSession, + $this->logger + ); + } catch (NotFoundException $e) { + throw new NotFound(); + } + } + + /** + * Returns an array with all the child nodes + * + * @return \Sabre\DAV\INode[] + */ + public 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[] + */ + public 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->userSession, + $this->logger + ); + } + return $result; + } + + /** + * Checks if a child-node with the specified name exists + * + * @param string $name + * @return bool + */ + public function childExists($name) { + try { + $this->commentsManager->get($name); + return true; + } catch (NotFoundException $e) { + return false; + } + } + + /** + * Sets the read marker to the specified date for the logged in user + */ + public function setReadMarker(?string $value): bool { + $dateTime = new \DateTime($value ?? 'now'); + $user = $this->userSession->getUser(); + $this->commentsManager->setReadMark($this->name, $this->id, $dateTime, $user); + return true; + } + + /** + * @inheritdoc + */ + public function propPatch(PropPatch $propPatch) { + $propPatch->handle(self::PROPERTY_NAME_READ_MARKER, [$this, 'setReadMarker']); + } + + /** + * @inheritdoc + */ + public function getProperties($properties) { + $marker = null; + $user = $this->userSession->getUser(); + if (!is_null($user)) { + $marker = $this->commentsManager->getReadMark($this->name, $this->id, $user); + } + return [self::PROPERTY_NAME_READ_MARKER => $marker]; + } +} diff --git a/apps/dav/lib/Comments/EntityTypeCollection.php b/apps/dav/lib/Comments/EntityTypeCollection.php new file mode 100644 index 00000000000..1c8533ca375 --- /dev/null +++ b/apps/dav/lib/Comments/EntityTypeCollection.php @@ -0,0 +1,89 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\DAV\Comments; + +use OCP\Comments\ICommentsManager; +use OCP\IUserManager; +use OCP\IUserSession; +use Psr\Log\LoggerInterface; +use Sabre\DAV\Exception\MethodNotAllowed; +use Sabre\DAV\Exception\NotFound; + +/** + * 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 { + public function __construct( + string $name, + ICommentsManager $commentsManager, + protected IUserManager $userManager, + IUserSession $userSession, + protected LoggerInterface $logger, + protected \Closure $childExistsFunction, + ) { + $name = trim($name); + if (empty($name)) { + throw new \InvalidArgumentException('"name" parameter must be non-empty string'); + } + $this->name = $name; + $this->commentsManager = $commentsManager; + $this->userSession = $userSession; + } + + /** + * 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 + */ + public function getChild($name) { + if (!$this->childExists($name)) { + throw new NotFound('Entity does not exist or is not available'); + } + return new EntityCollection( + $name, + $this->name, + $this->commentsManager, + $this->userManager, + $this->userSession, + $this->logger + ); + } + + /** + * Returns an array with all the child nodes + * + * @return \Sabre\DAV\INode[] + * @throws MethodNotAllowed + */ + public 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 + */ + public function childExists($name) { + return call_user_func($this->childExistsFunction, $name); + } +} diff --git a/apps/dav/lib/Comments/RootCollection.php b/apps/dav/lib/Comments/RootCollection.php new file mode 100644 index 00000000000..493d73ec531 --- /dev/null +++ b/apps/dav/lib/Comments/RootCollection.php @@ -0,0 +1,169 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OCA\DAV\Comments; + +use OCP\Comments\CommentsEntityEvent; +use OCP\Comments\ICommentsManager; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IUserManager; +use OCP\IUserSession; +use Psr\Log\LoggerInterface; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotAuthenticated; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\ICollection; + +class RootCollection implements ICollection { + /** @var EntityTypeCollection[]|null */ + private ?array $entityTypeCollections = null; + protected string $name = 'comments'; + + public function __construct( + protected ICommentsManager $commentsManager, + protected IUserManager $userManager, + protected IUserSession $userSession, + protected IEventDispatcher $dispatcher, + protected LoggerInterface $logger, + ) { + } + + /** + * 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 ($this->entityTypeCollections !== null) { + return; + } + $user = $this->userSession->getUser(); + if (is_null($user)) { + throw new NotAuthenticated(); + } + + $event = new CommentsEntityEvent(); + $this->dispatcher->dispatchTyped($event); + $this->dispatcher->dispatch(CommentsEntityEvent::EVENT_ENTITY, $event); + + $this->entityTypeCollections = []; + foreach ($event->getEntityCollections() as $entity => $entityExistsFunction) { + $this->entityTypeCollections[$entity] = new EntityTypeCollection( + $entity, + $this->commentsManager, + $this->userManager, + $this->userSession, + $this->logger, + $entityExistsFunction + ); + } + } + + /** + * 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 + */ + public function createFile($name, $data = null) { + throw new Forbidden('Cannot create comments by id'); + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @throws Forbidden + */ + public 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 + */ + public 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[] + */ + public function getChildren() { + $this->initCollections(); + assert(!is_null($this->entityTypeCollections)); + return $this->entityTypeCollections; + } + + /** + * Checks if a child-node with the specified name exists + * + * @param string $name + * @return bool + */ + public function childExists($name) { + $this->initCollections(); + assert(!is_null($this->entityTypeCollections)); + return isset($this->entityTypeCollections[$name]); + } + + /** + * Deleted the current node + * + * @throws Forbidden + */ + public 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 + */ + public function getName() { + return $this->name; + } + + /** + * Renames the node + * + * @param string $name The new name + * @throws Forbidden + */ + public function setName($name) { + throw new Forbidden('Permission denied to rename this collection'); + } + + /** + * Returns the last modification time, as a unix timestamp + * + * @return ?int + */ + public function getLastModified() { + return null; + } +} |