This commit is contained in:
Robin Appelman 2022-04-08 17:28:32 +02:00
commit 4d51484de3
22 changed files with 103 additions and 72 deletions

View file

@ -21,7 +21,6 @@
namespace SearchDAV\Backend; namespace SearchDAV\Backend;
use Sabre\DAV\INode; use Sabre\DAV\INode;
class SearchResult { class SearchResult {

View file

@ -33,19 +33,19 @@ use SearchDAV\XML\Scope;
class QueryParser extends Service { class QueryParser extends Service {
public $namespaceMap = [ public $namespaceMap = [
'DAV:' => 'd', 'DAV:' => 'd',
'http://sabredav.org/ns' => 's', 'http://sabredav.org/ns' => 's',
'http://www.w3.org/2001/XMLSchema' => 'xs', 'http://www.w3.org/2001/XMLSchema' => 'xs',
SearchPlugin::SEARCHDAV_NS => 'sd' SearchPlugin::SEARCHDAV_NS => 'sd'
]; ];
public function __construct() { public function __construct() {
$this->elementMap = [ $this->elementMap = [
'{DAV:}literal' => Literal::class, '{DAV:}literal' => Literal::class,
'{DAV:}searchrequest' => Element\KeyValue::class, '{DAV:}searchrequest' => Element\KeyValue::class,
'{DAV:}query-schema-discovery' => Element\KeyValue::class, '{DAV:}query-schema-discovery' => Element\KeyValue::class,
'{DAV:}basicsearch' => BasicSearch::class, '{DAV:}basicsearch' => BasicSearch::class,
'{DAV:}select' => function (Reader $reader) { '{DAV:}select' => function (Reader $reader) {
return \Sabre\Xml\Deserializer\keyValue($reader, '{DAV:}scope')['{DAV:}prop']; return \Sabre\Xml\Deserializer\keyValue($reader, '{DAV:}scope')['{DAV:}prop'];
}, },
'{DAV:}from' => function (Reader $reader) { '{DAV:}from' => function (Reader $reader) {
@ -61,20 +61,20 @@ class QueryParser extends Service {
}, $reader->parseGetElements()); }, $reader->parseGetElements());
return (isset($operators[0])) ? $operators[0] : null; return (isset($operators[0])) ? $operators[0] : null;
}, },
'{DAV:}prop' => Element\Elements::class, '{DAV:}prop' => Element\Elements::class,
'{DAV:}order' => Order::class, '{DAV:}order' => Order::class,
'{DAV:}eq' => Operator::class, '{DAV:}eq' => Operator::class,
'{DAV:}gt' => Operator::class, '{DAV:}gt' => Operator::class,
'{DAV:}gte' => Operator::class, '{DAV:}gte' => Operator::class,
'{DAV:}lt' => Operator::class, '{DAV:}lt' => Operator::class,
'{DAV:}lte' => Operator::class, '{DAV:}lte' => Operator::class,
'{DAV:}and' => Operator::class, '{DAV:}and' => Operator::class,
'{DAV:}or' => Operator::class, '{DAV:}or' => Operator::class,
'{DAV:}like' => Operator::class, '{DAV:}like' => Operator::class,
'{DAV:}contains' => Operator::class, '{DAV:}contains' => Operator::class,
'{DAV:}not' => Operator::class, '{DAV:}not' => Operator::class,
'{DAV:}is-collection' => Operator::class, '{DAV:}is-collection' => Operator::class,
'{DAV:}limit' => Limit::class, '{DAV:}limit' => Limit::class,
]; ];
} }
} }

View file

@ -85,8 +85,10 @@ class SearchHandler {
$response->setBody($e->getMessage()); $response->setBody($e->getMessage());
return false; return false;
} }
$data = $this->server->generateMultiStatus(iterator_to_array($this->getPropertiesIteratorResults($results, $data = $this->server->generateMultiStatus(iterator_to_array($this->getPropertiesIteratorResults(
$query->select)), false); $results,
$query->select
)), false);
$response->setBody($data); $response->setBody($data);
return false; return false;
} }

View file

@ -21,7 +21,6 @@
namespace SearchDAV\Query; namespace SearchDAV\Query;
class Limit { class Limit {
/** /**
* @var integer * @var integer

View file

@ -21,7 +21,6 @@
namespace SearchDAV\Query; namespace SearchDAV\Query;
class Literal { class Literal {
/** /**
* @var string|boolean|\DateTime|integer * @var string|boolean|\DateTime|integer

View file

@ -21,7 +21,6 @@
namespace SearchDAV\Query; namespace SearchDAV\Query;
use SearchDAV\Backend\SearchPropertyDefinition; use SearchDAV\Backend\SearchPropertyDefinition;
class Order { class Order {

View file

@ -21,7 +21,6 @@
namespace SearchDAV\Query; namespace SearchDAV\Query;
use SearchDAV\Backend\SearchPropertyDefinition; use SearchDAV\Backend\SearchPropertyDefinition;
class Query { class Query {

View file

@ -21,7 +21,6 @@
namespace SearchDAV\Query; namespace SearchDAV\Query;
class Scope { class Scope {
/** /**
* @var string * @var string

View file

@ -70,7 +70,7 @@ class BasicSearch implements XmlDeserializable {
* @return BasicSearch * @return BasicSearch
* @throws ParseException * @throws ParseException
*/ */
static function xmlDeserialize(Reader $reader): BasicSearch { public static function xmlDeserialize(Reader $reader): BasicSearch {
$search = new self(); $search = new self();
$elements = \Sabre\Xml\Deserializer\keyValue($reader); $elements = \Sabre\Xml\Deserializer\keyValue($reader);

View file

@ -21,7 +21,6 @@
namespace SearchDAV\XML; namespace SearchDAV\XML;
use Sabre\Xml\Writer; use Sabre\Xml\Writer;
use Sabre\Xml\XmlSerializable; use Sabre\Xml\XmlSerializable;
@ -38,10 +37,10 @@ class BasicSearchSchema implements XmlSerializable {
$this->properties = $properties; $this->properties = $properties;
} }
function xmlSerialize(Writer $writer) { public function xmlSerialize(Writer $writer) {
$childs = array_map(function(PropDesc $propDesc) { $childs = array_map(function (PropDesc $propDesc) {
return [ return [
'name' => '{DAV:}propdesc', 'name' => '{DAV:}propdesc',
'value' => $propDesc 'value' => $propDesc
]; ];
}, $this->properties); }, $this->properties);

View file

@ -29,7 +29,7 @@ use SearchDAV\DAV\SearchPlugin;
* The limit and offset of a search query * The limit and offset of a search query
*/ */
class Limit extends \SearchDAV\Query\Limit implements XmlDeserializable { class Limit extends \SearchDAV\Query\Limit implements XmlDeserializable {
static function xmlDeserialize(Reader $reader): Limit { public static function xmlDeserialize(Reader $reader): Limit {
$limit = new self(); $limit = new self();
$elements = \Sabre\Xml\Deserializer\keyValue($reader); $elements = \Sabre\Xml\Deserializer\keyValue($reader);

View file

@ -21,12 +21,11 @@
namespace SearchDAV\XML; namespace SearchDAV\XML;
use Sabre\Xml\Reader; use Sabre\Xml\Reader;
use Sabre\Xml\XmlDeserializable; use Sabre\Xml\XmlDeserializable;
class Literal extends \SearchDAV\Query\Literal implements XmlDeserializable { class Literal extends \SearchDAV\Query\Literal implements XmlDeserializable {
static function xmlDeserialize(Reader $reader): Literal { public static function xmlDeserialize(Reader $reader): Literal {
$literal = new self(); $literal = new self();
if ($reader->isEmptyElement) { if ($reader->isEmptyElement) {

View file

@ -56,7 +56,7 @@ class Operator implements XmlDeserializable {
$this->arguments = $arguments; $this->arguments = $arguments;
} }
static function xmlDeserialize(Reader $reader): Operator { public static function xmlDeserialize(Reader $reader): Operator {
$operator = new self(); $operator = new self();
$operator->type = $reader->getClark() ?? ''; $operator->type = $reader->getClark() ?? '';

View file

@ -21,7 +21,6 @@
namespace SearchDAV\XML; namespace SearchDAV\XML;
use Sabre\Xml\Reader; use Sabre\Xml\Reader;
use Sabre\Xml\XmlDeserializable; use Sabre\Xml\XmlDeserializable;
@ -50,7 +49,7 @@ class Order implements XmlDeserializable {
$this->order = $order; $this->order = $order;
} }
static function xmlDeserialize(Reader $reader): Order { public static function xmlDeserialize(Reader $reader): Order {
$order = new self(); $order = new self();
$childs = \Sabre\Xml\Deserializer\keyValue($reader); $childs = \Sabre\Xml\Deserializer\keyValue($reader);

View file

@ -21,7 +21,6 @@
namespace SearchDAV\XML; namespace SearchDAV\XML;
use Sabre\Xml\Writer; use Sabre\Xml\Writer;
use Sabre\Xml\XmlSerializable; use Sabre\Xml\XmlSerializable;
@ -47,7 +46,7 @@ class PropDesc implements XmlSerializable {
*/ */
public $sortable; public $sortable;
function xmlSerialize(Writer $writer) { public function xmlSerialize(Writer $writer) {
$data = [ $data = [
'{DAV:}dataType' => [$this->dataType => null] '{DAV:}dataType' => [$this->dataType => null]
]; ];
@ -62,7 +61,7 @@ class PropDesc implements XmlSerializable {
} }
$writer->write(array_map(function ($propName) { $writer->write(array_map(function ($propName) {
return [ return [
'name' => '{DAV:}prop', 'name' => '{DAV:}prop',
'value' => $propName 'value' => $propName
]; ];
}, $this->properties)); }, $this->properties));

View file

@ -21,7 +21,6 @@
namespace SearchDAV\XML; namespace SearchDAV\XML;
use Sabre\DAV\Xml\Element\Response; use Sabre\DAV\Xml\Element\Response;
use Sabre\Xml\Writer; use Sabre\Xml\Writer;
@ -38,16 +37,15 @@ class QueryDiscoverResponse extends Response {
* @param BasicSearchSchema|null $schema * @param BasicSearchSchema|null $schema
* @param null|int|string $httpStatus * @param null|int|string $httpStatus
*/ */
function __construct($href, BasicSearchSchema $schema = null, $httpStatus = null) { public function __construct($href, BasicSearchSchema $schema = null, $httpStatus = null) {
if ($httpStatus !== null) { if ($httpStatus !== null) {
$httpStatus = (string)$httpStatus; $httpStatus = (string)$httpStatus;
} }
parent::__construct($href, [], $httpStatus); parent::__construct($href, [], $httpStatus);
$this->schema = $schema; $this->schema = $schema;
} }
function xmlSerialize(Writer $writer) { public function xmlSerialize(Writer $writer) {
if ($status = $this->getHTTPStatus()) { if ($status = $this->getHTTPStatus()) {
$writer->writeElement('{DAV:}status', 'HTTP/1.1 ' . $status . ' ' . \Sabre\HTTP\Response::$statusCodes[$status]); $writer->writeElement('{DAV:}status', 'HTTP/1.1 ' . $status . ' ' . \Sabre\HTTP\Response::$statusCodes[$status]);
} }
@ -55,7 +53,7 @@ class QueryDiscoverResponse extends Response {
if ($this->schema) { if ($this->schema) {
$writer->writeElement('{DAV:}query-schema', [ $writer->writeElement('{DAV:}query-schema', [
'{DAV:}basicsearchschema' => $this->schema '{DAV:}basicsearchschema' => $this->schema
]); ]);
} }
} }

View file

@ -25,7 +25,7 @@ use Sabre\Xml\Reader;
use Sabre\Xml\XmlDeserializable; use Sabre\Xml\XmlDeserializable;
class Scope extends \SearchDAV\Query\Scope implements XmlDeserializable { class Scope extends \SearchDAV\Query\Scope implements XmlDeserializable {
static function xmlDeserialize(Reader $reader): Scope { public static function xmlDeserialize(Reader $reader): Scope {
$scope = new self(); $scope = new self();
$values = \Sabre\Xml\Deserializer\keyValue($reader); $values = \Sabre\Xml\Deserializer\keyValue($reader);

View file

@ -21,7 +21,6 @@
namespace SearchDAV\XML; namespace SearchDAV\XML;
use Sabre\Xml\Writer; use Sabre\Xml\Writer;
use Sabre\Xml\XmlSerializable; use Sabre\Xml\XmlSerializable;
@ -30,7 +29,7 @@ class SupportedQueryGrammar implements XmlSerializable {
public $grammar = self::GRAMMAR_BASICSEARCH; public $grammar = self::GRAMMAR_BASICSEARCH;
function xmlSerialize(Writer $writer) { public function xmlSerialize(Writer $writer) {
$writer->startElement('{DAV:}supported-query-grammar'); $writer->startElement('{DAV:}supported-query-grammar');
$writer->startElement('{DAV:}grammar'); $writer->startElement('{DAV:}grammar');
$writer->startElement($this->grammar); $writer->startElement($this->grammar);

View file

@ -54,6 +54,5 @@ class DummyBackend implements ISearchBackend {
} }
public function preloadPropertyFor(array $nodes, array $requestProperties): void { public function preloadPropertyFor(array $nodes, array $requestProperties): void {
} }
} }

View file

@ -26,7 +26,7 @@ use Sabre\DAV\Server;
use SearchDAV\DAV\PathHelper; use SearchDAV\DAV\PathHelper;
class PathHelperTest extends TestCase { class PathHelperTest extends TestCase {
public function uriProvider(){ public function uriProvider() {
return [ return [
['/', '', ''], ['/', '', ''],
['/index.php/', 'foo', 'foo'], ['/index.php/', 'foo', 'foo'],

View file

@ -33,7 +33,6 @@ use SearchDAV\XML\Order;
use SearchDAV\XML\Scope; use SearchDAV\XML\Scope;
use SearchDAV\XML\SupportedQueryGrammar; use SearchDAV\XML\SupportedQueryGrammar;
class QueryParserTest extends TestCase { class QueryParserTest extends TestCase {
public function testParseBasicQuery() { public function testParseBasicQuery() {
$query = file_get_contents(__DIR__ . '/basicquery.xml'); $query = file_get_contents(__DIR__ . '/basicquery.xml');

View file

@ -180,12 +180,22 @@ class SearchPluginTest extends TestCase {
$this->searchBackend->expects($this->once()) $this->searchBackend->expects($this->once())
->method('getPropertyDefinitionsForScope') ->method('getPropertyDefinitionsForScope')
->willReturn([ ->willReturn([
new SearchPropertyDefinition('{DAV:}getcontentlength', true, true, true, new SearchPropertyDefinition(
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), '{DAV:}getcontentlength',
true,
true,
true,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER
),
new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true), new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true),
new SearchPropertyDefinition('{DAV:}displayname', true, true, true), new SearchPropertyDefinition('{DAV:}displayname', true, true, true),
new SearchPropertyDefinition('{http://ns.nextcloud.com:}fileid', false, true, true, new SearchPropertyDefinition(
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), '{http://ns.nextcloud.com:}fileid',
false,
true,
true,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER
),
]); ]);
$plugin->searchHandler($request, $response); $plugin->searchHandler($request, $response);
@ -274,8 +284,13 @@ class SearchPluginTest extends TestCase {
->method('isValidScope') ->method('isValidScope')
->willReturn(true); ->willReturn(true);
$lengthProp = new SearchPropertyDefinition('{DAV:}getcontentlength', true, true, true, $lengthProp = new SearchPropertyDefinition(
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER); '{DAV:}getcontentlength',
true,
true,
true,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER
);
$orderBy = [ $orderBy = [
new \SearchDAV\Query\Order($lengthProp, \SearchDAV\Query\Order::ASC), new \SearchDAV\Query\Order($lengthProp, \SearchDAV\Query\Order::ASC),
]; ];
@ -347,8 +362,13 @@ class SearchPluginTest extends TestCase {
->method('getArbiterPath') ->method('getArbiterPath')
->willReturn('foo'); ->willReturn('foo');
$lengthProp = new SearchPropertyDefinition('{DAV:}getcontentlength', true, true, true, $lengthProp = new SearchPropertyDefinition(
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER); '{DAV:}getcontentlength',
true,
true,
true,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER
);
$plugin = new SearchPlugin($this->searchBackend); $plugin = new SearchPlugin($this->searchBackend);
$server = new Server(); $server = new Server();
$plugin->initialize($server); $plugin->initialize($server);
@ -483,8 +503,13 @@ class SearchPluginTest extends TestCase {
$this->searchBackend->expects($this->once()) $this->searchBackend->expects($this->once())
->method('getPropertyDefinitionsForScope') ->method('getPropertyDefinitionsForScope')
->willReturn([ ->willReturn([
new SearchPropertyDefinition('{http://ns.nextcloud.com:}fileid', false, true, true, new SearchPropertyDefinition(
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), '{http://ns.nextcloud.com:}fileid',
false,
true,
true,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER
),
]); ]);
$plugin->searchHandler($request, $response); $plugin->searchHandler($request, $response);
@ -518,10 +543,20 @@ class SearchPluginTest extends TestCase {
$this->searchBackend->expects($this->any()) $this->searchBackend->expects($this->any())
->method('getPropertyDefinitionsForScope') ->method('getPropertyDefinitionsForScope')
->willReturn([ ->willReturn([
new SearchPropertyDefinition('{http://ns.nextcloud.com:}fileid', false, true, true, new SearchPropertyDefinition(
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), '{http://ns.nextcloud.com:}fileid',
new SearchPropertyDefinition('{DAV:}getcontentlength', true, true, true, false,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), true,
true,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER
),
new SearchPropertyDefinition(
'{DAV:}getcontentlength',
true,
true,
true,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER
),
]); ]);
$plugin->searchHandler($request, $response); $plugin->searchHandler($request, $response);
@ -555,15 +590,24 @@ class SearchPluginTest extends TestCase {
$this->searchBackend->expects($this->any()) $this->searchBackend->expects($this->any())
->method('getPropertyDefinitionsForScope') ->method('getPropertyDefinitionsForScope')
->willReturn([ ->willReturn([
new SearchPropertyDefinition('{http://ns.nextcloud.com:}fileid', false, true, true, new SearchPropertyDefinition(
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), '{http://ns.nextcloud.com:}fileid',
new SearchPropertyDefinition('{DAV:}getcontentlength', true, true, true, false,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER), true,
true,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER
),
new SearchPropertyDefinition(
'{DAV:}getcontentlength',
true,
true,
true,
SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER
),
]); ]);
$plugin->searchHandler($request, $response); $plugin->searchHandler($request, $response);
$this->assertEquals(400, $response->getStatus()); $this->assertEquals(400, $response->getStatus());
} }
} }