diff options
Diffstat (limited to 'apps/dav')
-rw-r--r-- | apps/dav/l10n/zh_CN.js | 2 | ||||
-rw-r--r-- | apps/dav/l10n/zh_CN.json | 2 | ||||
-rw-r--r-- | apps/dav/lib/Connector/Sabre/Directory.php | 6 | ||||
-rw-r--r-- | apps/dav/lib/Connector/Sabre/Exception/InvalidPath.php | 5 | ||||
-rw-r--r-- | apps/dav/lib/Files/FileSearchBackend.php | 155 | ||||
-rw-r--r-- | apps/dav/tests/unit/Files/FileSearchBackendTest.php | 38 |
6 files changed, 148 insertions, 60 deletions
diff --git a/apps/dav/l10n/zh_CN.js b/apps/dav/l10n/zh_CN.js index 2e03a120e60..7b91e0d93ff 100644 --- a/apps/dav/l10n/zh_CN.js +++ b/apps/dav/l10n/zh_CN.js @@ -41,7 +41,9 @@ OC.L10N.register( "A calendar <strong>event</strong> was modified" : "日历中<strong>事件</strong>已经修改", "A calendar <strong>todo</strong> was modified" : "列表中<strong>待办事项</strong>已经修改", "Contact birthdays" : "联系人生日", + "Invitation canceled" : "邀请已取消", "Hello %s," : "%s你好,", + "Invitation updated" : "邀请已更新", "When:" : "时间:", "Where:" : "地点:", "Description:" : "描述:", diff --git a/apps/dav/l10n/zh_CN.json b/apps/dav/l10n/zh_CN.json index 60ea656ff51..95750bca882 100644 --- a/apps/dav/l10n/zh_CN.json +++ b/apps/dav/l10n/zh_CN.json @@ -39,7 +39,9 @@ "A calendar <strong>event</strong> was modified" : "日历中<strong>事件</strong>已经修改", "A calendar <strong>todo</strong> was modified" : "列表中<strong>待办事项</strong>已经修改", "Contact birthdays" : "联系人生日", + "Invitation canceled" : "邀请已取消", "Hello %s," : "%s你好,", + "Invitation updated" : "邀请已更新", "When:" : "时间:", "Where:" : "地点:", "Description:" : "描述:", diff --git a/apps/dav/lib/Connector/Sabre/Directory.php b/apps/dav/lib/Connector/Sabre/Directory.php index 6fe9d26614e..a7b8ea1755e 100644 --- a/apps/dav/lib/Connector/Sabre/Directory.php +++ b/apps/dav/lib/Connector/Sabre/Directory.php @@ -150,11 +150,11 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node $node->acquireLock(ILockingProvider::LOCK_SHARED); return $node->put($data); } catch (\OCP\Files\StorageNotAvailableException $e) { - throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage()); + throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage(), $e->getCode(), $e); } catch (InvalidPathException $ex) { - throw new InvalidPath($ex->getMessage()); + throw new InvalidPath($ex->getMessage(), false, $ex); } catch (ForbiddenException $ex) { - throw new Forbidden($ex->getMessage(), $ex->getRetry()); + throw new Forbidden($ex->getMessage(), $ex->getRetry(), $ex); } catch (LockedException $e) { throw new FileLocked($e->getMessage(), $e->getCode(), $e); } diff --git a/apps/dav/lib/Connector/Sabre/Exception/InvalidPath.php b/apps/dav/lib/Connector/Sabre/Exception/InvalidPath.php index 9d60b227612..346e21adc9d 100644 --- a/apps/dav/lib/Connector/Sabre/Exception/InvalidPath.php +++ b/apps/dav/lib/Connector/Sabre/Exception/InvalidPath.php @@ -36,9 +36,10 @@ class InvalidPath extends Exception { /** * @param string $message * @param bool $retry + * @param \Exception|null $previous */ - public function __construct($message, $retry = false) { - parent::__construct($message); + public function __construct($message, $retry = false, \Exception $previous = null) { + parent::__construct($message, 0, $previous); $this->retry = $retry; } diff --git a/apps/dav/lib/Files/FileSearchBackend.php b/apps/dav/lib/Files/FileSearchBackend.php index 9616d21b887..275d3dc8a42 100644 --- a/apps/dav/lib/Files/FileSearchBackend.php +++ b/apps/dav/lib/Files/FileSearchBackend.php @@ -45,10 +45,10 @@ use Sabre\DAV\Exception\NotFound; use SearchDAV\Backend\ISearchBackend; use SearchDAV\Backend\SearchPropertyDefinition; use SearchDAV\Backend\SearchResult; -use SearchDAV\XML\BasicSearch; -use SearchDAV\XML\Literal; -use SearchDAV\XML\Operator; -use SearchDAV\XML\Order; +use SearchDAV\Query\Query; +use SearchDAV\Query\Literal; +use SearchDAV\Query\Operator; +use SearchDAV\Query\Order; class FileSearchBackend implements ISearchBackend { /** @var CachingTree */ @@ -135,10 +135,10 @@ class FileSearchBackend implements ISearchBackend { } /** - * @param BasicSearch $search + * @param Query $search * @return SearchResult[] */ - public function search(BasicSearch $search) { + public function search(Query $search) { if (count($search->from) !== 1) { throw new \InvalidArgumentException('Searching more than one folder is not supported'); } @@ -157,7 +157,8 @@ class FileSearchBackend implements ISearchBackend { /** @var Folder $folder $results */ $results = $folder->search($query); - return array_map(function (Node $node) { + /** @var SearchResult[] $nodes */ + $nodes = array_map(function (Node $node) { if ($node instanceof Folder) { $davNode = new \OCA\DAV\Connector\Sabre\Directory($this->view, $node, $this->tree, $this->shareManager); } else { @@ -167,6 +168,90 @@ class FileSearchBackend implements ISearchBackend { $this->tree->cacheNode($davNode, $path); return new SearchResult($davNode, $path); }, $results); + + // Sort again, since the result from multiple storages is appended and not sorted + usort($nodes, function (SearchResult $a, SearchResult $b) use ($search) { + return $this->sort($a, $b, $search->orderBy); + }); + + // If a limit is provided use only return that number of files + if ($search->limit->maxResults !== 0) { + $nodes = \array_slice($nodes, 0, $search->limit->maxResults); + } + + return $nodes; + } + + private function sort(SearchResult $a, SearchResult $b, array $orders) { + /** @var Order $order */ + foreach ($orders as $order) { + $v1 = $this->getSearchResultProperty($a, $order->property); + $v2 = $this->getSearchResultProperty($b, $order->property); + + + if ($v1 === null && $v2 === null) { + continue; + } + if ($v1 === null) { + return $order->order === Order::ASC ? 1 : -1; + } + if ($v2 === null) { + return $order->order === Order::ASC ? -1 : 1; + } + + $s = $this->compareProperties($v1, $v2, $order); + if ($s === 0) { + continue; + } + + if ($order->order === Order::DESC) { + $s = -$s; + } + return $s; + } + + return 0; + } + + private function compareProperties($a, $b, Order $order) { + switch ($order->property->dataType) { + case SearchPropertyDefinition::DATATYPE_STRING: + return strcmp($a, $b); + case SearchPropertyDefinition::DATATYPE_BOOLEAN: + if ($a === $b) { + return 0; + } + if ($a === false) { + return -1; + } + return 1; + default: + if ($a === $b) { + return 0; + } + if ($a < $b) { + return -1; + } + return 1; + } + } + + private function getSearchResultProperty(SearchResult $result, SearchPropertyDefinition $property) { + /** @var \OCA\DAV\Connector\Sabre\Node $node */ + $node = $result->node; + + switch ($property->name) { + case '{DAV:}displayname': + return $node->getName(); + case '{DAV:}getlastmodified': + return $node->getLastModified(); + case FilesPlugin::SIZE_PROPERTYNAME: + return $node->getSize(); + case FilesPlugin::INTERNAL_FILEID_PROPERTYNAME: + return $node->getInternalFileId(); + default: + return null; + } } /** @@ -179,13 +264,14 @@ class FileSearchBackend implements ISearchBackend { } /** - * @param BasicSearch $query + * @param Query $query * @return ISearchQuery */ - private function transformQuery(BasicSearch $query) { - // TODO offset, limit + private function transformQuery(Query $query) { + // TODO offset + $limit = $query->limit; $orders = array_map([$this, 'mapSearchOrder'], $query->orderBy); - return new SearchQuery($this->transformSearchOperation($query->where), 0, 0, $orders, $this->user); + return new SearchQuery($this->transformSearchOperation($query->where), (int)$limit->maxResults, 0, $orders, $this->user); } /** @@ -217,7 +303,7 @@ class FileSearchBackend implements ISearchBackend { if (count($operator->arguments) !== 2) { throw new \InvalidArgumentException('Invalid number of arguments for ' . $trimmedType . ' operation'); } - if (!is_string($operator->arguments[0])) { + if (!($operator->arguments[0] instanceof SearchPropertyDefinition)) { throw new \InvalidArgumentException('Invalid argument 1 for ' . $trimmedType . ' operation, expected property'); } if (!($operator->arguments[1] instanceof Literal)) { @@ -232,11 +318,11 @@ class FileSearchBackend implements ISearchBackend { } /** - * @param string $propertyName + * @param SearchPropertyDefinition $property * @return string */ - private function mapPropertyNameToColumn($propertyName) { - switch ($propertyName) { + private function mapPropertyNameToColumn(SearchPropertyDefinition $property) { + switch ($property->name) { case '{DAV:}displayname': return 'name'; case '{DAV:}getcontenttype': @@ -252,33 +338,26 @@ class FileSearchBackend implements ISearchBackend { case FilesPlugin::INTERNAL_FILEID_PROPERTYNAME: return 'fileid'; default: - throw new \InvalidArgumentException('Unsupported property for search or order: ' . $propertyName); + throw new \InvalidArgumentException('Unsupported property for search or order: ' . $property->name); } } - private function castValue($propertyName, $value) { - $allProps = $this->getPropertyDefinitionsForScope('', ''); - foreach ($allProps as $prop) { - if ($prop->name === $propertyName) { - $dataType = $prop->dataType; - switch ($dataType) { - case SearchPropertyDefinition::DATATYPE_BOOLEAN: - return $value === 'yes'; - case SearchPropertyDefinition::DATATYPE_DECIMAL: - case SearchPropertyDefinition::DATATYPE_INTEGER: - case SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER: - return 0 + $value; - case SearchPropertyDefinition::DATATYPE_DATETIME: - if (is_numeric($value)) { - return 0 + $value; - } - $date = \DateTime::createFromFormat(\DateTime::ATOM, $value); - return ($date instanceof \DateTime) ? $date->getTimestamp() : 0; - default: - return $value; + private function castValue(SearchPropertyDefinition $property, $value) { + switch ($property->dataType) { + case SearchPropertyDefinition::DATATYPE_BOOLEAN: + return $value === 'yes'; + case SearchPropertyDefinition::DATATYPE_DECIMAL: + case SearchPropertyDefinition::DATATYPE_INTEGER: + case SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER: + return 0 + $value; + case SearchPropertyDefinition::DATATYPE_DATETIME: + if (is_numeric($value)) { + return 0 + $value; } - } + $date = \DateTime::createFromFormat(\DateTime::ATOM, $value); + return ($date instanceof \DateTime) ? $date->getTimestamp() : 0; + default: + return $value; } - return $value; } } diff --git a/apps/dav/tests/unit/Files/FileSearchBackendTest.php b/apps/dav/tests/unit/Files/FileSearchBackendTest.php index 14df99a278a..20a7566c2fd 100644 --- a/apps/dav/tests/unit/Files/FileSearchBackendTest.php +++ b/apps/dav/tests/unit/Files/FileSearchBackendTest.php @@ -38,6 +38,9 @@ use OCP\Files\IRootFolder; use OCP\Files\Search\ISearchComparison; use OCP\IUser; use OCP\Share\IManager; +use SearchDAV\Backend\SearchPropertyDefinition; +use SearchDAV\Query\Limit; +use SearchDAV\Query\Query; use SearchDAV\XML\BasicSearch; use SearchDAV\XML\Literal; use SearchDAV\XML\Operator; @@ -132,7 +135,7 @@ class FileSearchBackendTest extends TestCase { new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path') ])); - $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, '{DAV:}displayname', 'foo'); + $query = $this->getBasicQuery(\SearchDAV\Query\Operator::OPERATION_EQUAL, '{DAV:}displayname', 'foo'); $result = $this->search->search($query); $this->assertCount(1, $result); @@ -161,7 +164,7 @@ class FileSearchBackendTest extends TestCase { new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path') ])); - $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, '{DAV:}getcontenttype', 'foo'); + $query = $this->getBasicQuery(\SearchDAV\Query\Operator::OPERATION_EQUAL, '{DAV:}getcontenttype', 'foo'); $result = $this->search->search($query); $this->assertCount(1, $result); @@ -190,7 +193,7 @@ class FileSearchBackendTest extends TestCase { new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path') ])); - $query = $this->getBasicQuery(Operator::OPERATION_GREATER_THAN, FilesPlugin::SIZE_PROPERTYNAME, 10); + $query = $this->getBasicQuery(\SearchDAV\Query\Operator::OPERATION_GREATER_THAN, FilesPlugin::SIZE_PROPERTYNAME, 10); $result = $this->search->search($query); $this->assertCount(1, $result); @@ -219,7 +222,7 @@ class FileSearchBackendTest extends TestCase { new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path') ])); - $query = $this->getBasicQuery(Operator::OPERATION_GREATER_THAN, '{DAV:}getlastmodified', 10); + $query = $this->getBasicQuery(\SearchDAV\Query\Operator::OPERATION_GREATER_THAN, '{DAV:}getlastmodified', 10); $result = $this->search->search($query); $this->assertCount(1, $result); @@ -248,7 +251,7 @@ class FileSearchBackendTest extends TestCase { new \OC\Files\Node\Folder($this->rootFolder, $this->view, '/test/path') ])); - $query = $this->getBasicQuery(Operator::OPERATION_IS_COLLECTION, 'yes'); + $query = $this->getBasicQuery(\SearchDAV\Query\Operator::OPERATION_IS_COLLECTION, 'yes'); $result = $this->search->search($query); $this->assertCount(1, $result); @@ -266,29 +269,30 @@ class FileSearchBackendTest extends TestCase { $this->searchFolder->expects($this->never()) ->method('search'); - $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, '{DAV:}getetag', 'foo'); + $query = $this->getBasicQuery(\SearchDAV\Query\Operator::OPERATION_EQUAL, '{DAV:}getetag', 'foo'); $this->search->search($query); } private function getBasicQuery($type, $property, $value = null) { - $query = new BasicSearch(); - $scope = new Scope('/', 'infinite'); + $scope = new \SearchDAV\Query\Scope('/', 'infinite'); $scope->path = '/'; - $query->from = [$scope]; - $query->orderBy = []; - $query->select = []; + $from = [$scope]; + $orderBy = []; + $select = []; if (is_null($value)) { - $query->where = new Operator( + $where = new \SearchDAV\Query\Operator( $type, - [new Literal($property)] + [new \SearchDAV\Query\Literal($property)] ); } else { - $query->where = new Operator( + $where = new \SearchDAV\Query\Operator( $type, - [$property, new Literal($value)] + [new SearchPropertyDefinition($property, true, true, true), new \SearchDAV\Query\Literal($value)] ); } - return $query; + $limit = new Limit(); + + return new Query($select, $from, $where, $orderBy, $limit); } /** @@ -301,7 +305,7 @@ class FileSearchBackendTest extends TestCase { ->method('getNodeForPath') ->willReturn($davNode); - $query = $this->getBasicQuery(Operator::OPERATION_EQUAL, '{DAV:}displayname', 'foo'); + $query = $this->getBasicQuery(\SearchDAV\Query\Operator::OPERATION_EQUAL, '{DAV:}displayname', 'foo'); $this->search->search($query); } } |