mirror of
https://codeberg.org/icewind/SearchDAV.git
synced 2026-06-03 09:34:08 +02:00
Documentation
This commit is contained in:
parent
88db2ae6c1
commit
3f23e989f0
8 changed files with 152 additions and 21 deletions
|
|
@ -23,6 +23,7 @@ namespace SearchDAV\Backend;
|
|||
|
||||
use Sabre\DAV\INode;
|
||||
use SearchDAV\XML\BasicSearch;
|
||||
use SearchDAV\XML\Scope;
|
||||
|
||||
interface ISearchBackend {
|
||||
/**
|
||||
|
|
@ -31,6 +32,11 @@ interface ISearchBackend {
|
|||
* The search arbiter is the URI that the client will send it's SEARCH requests to
|
||||
* Note that this is not required to be the same as the search scopes which determine what to search in
|
||||
*
|
||||
* The returned value should be a path relative the root of the dav server.
|
||||
*
|
||||
* For example, if you want to support SEARCH requests on `https://example.com/dav.php/search`
|
||||
* with the sabre/dav server listening on `/dav.php` you should return `search` as arbiter path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getArbiterPath();
|
||||
|
|
@ -38,23 +44,43 @@ interface ISearchBackend {
|
|||
/**
|
||||
* Whether or not the search backend supports search requests on this scope
|
||||
*
|
||||
* @param string $href
|
||||
* @param string|integer $depth 0, 1 or 'inifinite'
|
||||
* The scope defines the resource that it being searched, such as a folder or address book.
|
||||
*
|
||||
* Note that a search arbiter has no inherit limitations on which scopes it can support and scopes
|
||||
* that reside on a different dav server entirely might be considered valid by an implementation.
|
||||
*
|
||||
* One example use case for this would be a service that provides additional indexing on a 3rd party service.
|
||||
*
|
||||
* @param string $href an absolute uri of the search scope
|
||||
* @param string|integer $depth 0, 1 or 'infinite'
|
||||
* @param string|null $path the path of the search scope relative to the dav server, or null if the scope is outside the dav server
|
||||
* @return bool
|
||||
*/
|
||||
public function isValidScope($href, $depth);
|
||||
public function isValidScope($href, $depth, $path);
|
||||
|
||||
/**
|
||||
* List the available properties that can be used in search
|
||||
*
|
||||
* This is used to tell the search client what properties can be queried, used to filter and used to sort.
|
||||
*
|
||||
* Since sabre's PropFind handling mechanism is used to return the properties to the client, it's required that all
|
||||
* properties which are listed as selectable have a PropFind handler set.
|
||||
*
|
||||
* @param string $href an absolute uri of the search scope
|
||||
* @param string|null $path the path of the search scope relative to the dav server, or null if the scope is outside the dav server
|
||||
* @return SearchPropertyDefinition[]
|
||||
*/
|
||||
public function getPropertyDefinitionsForScope($href);
|
||||
public function getPropertyDefinitionsForScope($href, $path);
|
||||
|
||||
/**
|
||||
* @param INode $searchNode the DAV node that the search request was made to
|
||||
* Preform the search request
|
||||
*
|
||||
* The search results consist of the uri for the found resource and an INode describing the resource
|
||||
* To return the properties requested by the query sabre's existing PropFind method is used, thus the search implementation
|
||||
* is not required to collect these properties and is free to ignore the `select` part of the query
|
||||
*
|
||||
* @param BasicSearch $query
|
||||
* @return SearchResult[]
|
||||
*/
|
||||
public function search(INode $searchNode, BasicSearch $query);
|
||||
public function search(BasicSearch $query);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,9 +54,10 @@ class QueryParser extends Service {
|
|||
},
|
||||
'{DAV:}scope' => Scope::class,
|
||||
'{DAV:}where' => function (Reader $reader) {
|
||||
return array_map(function ($element) {
|
||||
$operators = array_map(function ($element) {
|
||||
return $element['value'];
|
||||
}, $reader->parseGetElements());
|
||||
return (isset($operators[0])) ? $operators[0] : null;
|
||||
},
|
||||
'{DAV:}prop' => Element\Elements::class,
|
||||
'{DAV:}order' => Order::class,
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
namespace SearchDAV\DAV;
|
||||
|
||||
use Sabre\DAV\Exception\BadRequest;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\INode;
|
||||
use Sabre\DAV\Node;
|
||||
use Sabre\DAV\PropFind;
|
||||
|
|
@ -70,6 +71,14 @@ class SearchPlugin extends ServerPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
private function getPathFromUri($uri) {
|
||||
try {
|
||||
return ($uri === '' && $this->server->getBaseUri() === '/') ? '' : $this->server->calculateUri($uri);
|
||||
} catch (Forbidden $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SEARCH is allowed for users files
|
||||
*
|
||||
|
|
@ -77,7 +86,7 @@ class SearchPlugin extends ServerPlugin {
|
|||
* @return array
|
||||
*/
|
||||
public function getHTTPMethods($uri) {
|
||||
$path = ($uri === '' && $this->server->getBaseUri() === '/') ? '' : $this->server->calculateUri($uri);
|
||||
$path = $this->getPathFromUri($uri);
|
||||
if ($this->searchBackend->getArbiterPath() === $path) {
|
||||
return ['SEARCH'];
|
||||
} else {
|
||||
|
|
@ -112,10 +121,9 @@ class SearchPlugin extends ServerPlugin {
|
|||
}
|
||||
/** @var BasicSearch $query */
|
||||
$query = $xml['{DAV:}basicsearch'];
|
||||
$node = $this->server->tree->getNodeForPath($request->getPath());
|
||||
$response->setStatus(207);
|
||||
$response->setHeader('Content-Type', 'application/xml; charset="utf-8"');
|
||||
$results = $this->searchBackend->search($node, $query);
|
||||
$results = $this->searchBackend->search($query);
|
||||
$data = $this->server->generateMultiStatus($this->getPropertiesIteratorResults($results, $query->select), false);
|
||||
$response->setBody($data);
|
||||
return false;
|
||||
|
|
@ -126,11 +134,12 @@ class SearchPlugin extends ServerPlugin {
|
|||
/** @var BasicSearch $query */
|
||||
$query = $xml['{DAV:}basicsearch'];
|
||||
$scopes = $query->from;
|
||||
$results = array_map(function(Scope $scope) {
|
||||
if ($this->searchBackend->isValidScope($scope->href, $scope->depth)) {
|
||||
$searchProperties = $this->searchBackend->getPropertyDefinitionsForScope($scope->href);
|
||||
$results = array_map(function (Scope $scope) {
|
||||
$scopePath = $this->getPathFromUri($scope->href);
|
||||
if ($this->searchBackend->isValidScope($scope->href, $scope->depth, $scopePath)) {
|
||||
$searchProperties = $this->searchBackend->getPropertyDefinitionsForScope($scope->href, $scopePath);
|
||||
$searchSchema = $this->getBasicSearchForProperties($searchProperties);
|
||||
return new QueryDiscoverResponse($scope->href, $searchSchema , 200);
|
||||
return new QueryDiscoverResponse($scope->href, $searchSchema, 200);
|
||||
} else {
|
||||
return new QueryDiscoverResponse($scope->href, null, 404); // TODO something other than 404? 403 maybe
|
||||
}
|
||||
|
|
@ -185,9 +194,9 @@ class SearchPlugin extends ServerPlugin {
|
|||
|
||||
private function hashDefinition(SearchPropertyDefinition $definition) {
|
||||
return $definition->dataType
|
||||
. (($definition->searchable) ? '1' : '0')
|
||||
. (($definition->sortable) ? '1' : '0')
|
||||
. (($definition->selectable) ? '1' : '0');
|
||||
. (($definition->searchable) ? '1' : '0')
|
||||
. (($definition->sortable) ? '1' : '0')
|
||||
. (($definition->selectable) ? '1' : '0');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -24,11 +24,14 @@ namespace SearchDAV\XML;
|
|||
use Sabre\Xml\Reader;
|
||||
use Sabre\Xml\XmlDeserializable;
|
||||
|
||||
/**
|
||||
* The object representation of a search query made by the client
|
||||
*/
|
||||
class BasicSearch implements XmlDeserializable {
|
||||
/**
|
||||
* @var string[]
|
||||
*
|
||||
* The list of properties to be selected in clark notation
|
||||
* The list of properties to be selected, specified in clark notation
|
||||
*/
|
||||
public $select;
|
||||
/**
|
||||
|
|
@ -38,11 +41,20 @@ class BasicSearch implements XmlDeserializable {
|
|||
*/
|
||||
public $from;
|
||||
/**
|
||||
* @var Operator[]
|
||||
* @var Operator
|
||||
*
|
||||
* The search operator, either a comparison ('gt', 'eq', ...) or a boolean operator ('and', 'or', 'not')
|
||||
*/
|
||||
public $where;
|
||||
/**
|
||||
* @var Order[]
|
||||
*
|
||||
* The list of order operations that should be used to order the results.
|
||||
*
|
||||
* Each order operations consists of a property to sort on and a sort direction.
|
||||
* If more then one order operations are specified, the comparisons for ordering should
|
||||
* be applied in the order that the order operations are defined in with the earlier comparisons being
|
||||
* more significant.
|
||||
*/
|
||||
public $orderBy;
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ class Operator implements XmlDeserializable {
|
|||
* - string: property name for comparison
|
||||
* - Literal: literal value for comparison
|
||||
* - Operation: nested operation for and/or/not operations
|
||||
*
|
||||
* Which type and what number of argument an Operator takes depends on the operator type.
|
||||
*/
|
||||
public $arguments;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,17 @@ class Order implements XmlDeserializable {
|
|||
const ASC = 'ascending';
|
||||
const DESC = 'descending';
|
||||
|
||||
public $properties;
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* The property that should be sorted on.
|
||||
*/
|
||||
public $property;
|
||||
/**
|
||||
* @var string 'ascending' or 'descending'
|
||||
*
|
||||
* The sort direction
|
||||
*/
|
||||
public $order;
|
||||
|
||||
static function xmlDeserialize(Reader $reader) {
|
||||
|
|
@ -38,7 +48,7 @@ class Order implements XmlDeserializable {
|
|||
$childs = \Sabre\Xml\Deserializer\keyValue($reader);
|
||||
|
||||
$order->order = isset($childs['{DAV:}descending']) ? self::DESC : self::ASC;
|
||||
$order->properties = $childs['{DAV:}prop'];
|
||||
$order->property = $childs['{DAV:}prop'][0];
|
||||
|
||||
return $order;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,20 @@ use Sabre\Xml\Reader;
|
|||
use Sabre\Xml\XmlDeserializable;
|
||||
|
||||
class Scope implements XmlDeserializable {
|
||||
/**
|
||||
* @var string
|
||||
*
|
||||
* The absolute url of the search scope
|
||||
*/
|
||||
public $href;
|
||||
|
||||
/**
|
||||
* @var string|int 0, 1 or 'infinite'
|
||||
*
|
||||
* How deep the search query should be with 0 being only the scope itself,
|
||||
* 1 being all direct child entries of the scope and infinite being all entries
|
||||
* in the scope collection at any depth.
|
||||
*/
|
||||
public $depth;
|
||||
|
||||
static function xmlDeserialize(Reader $reader) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue