Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Introduce pagination in files-filter report
Signed-off-by: Thomas Müller <[email protected]>
  • Loading branch information
DeepDiver1975 authored and icewind1991 committed Oct 31, 2017
commit 58cc324e835c01ed870c320f98a21d0b5e85a9d7
63 changes: 25 additions & 38 deletions apps/dav/lib/Connector/Sabre/FilesReportPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
namespace OCA\DAV\Connector\Sabre;

use OC\Files\View;
use OCA\DAV\Files\Xml\FilterRequest;
use Sabre\DAV\Exception\PreconditionFailed;
use Sabre\DAV\Exception\BadRequest;
use Sabre\DAV\ServerPlugin;
Expand Down Expand Up @@ -139,6 +140,8 @@ public function initialize(\Sabre\DAV\Server $server) {

$server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';

$server->xml->elementMap[self::REPORT_NAME] = FilterRequest::class;

$this->server = $server;
$this->server->on('report', array($this, 'onReport'));
}
Expand All @@ -159,51 +162,44 @@ public function getSupportedReportSet($uri) {
* REPORT operations to look for files
*
* @param string $reportName
* @param $report
* @param mixed $report
* @param string $uri
* @return bool
* @throws BadRequest
* @throws PreconditionFailed
* @internal param $ [] $report
*/
public function onReport($reportName, $report, $uri) {

$reportTargetNode = $this->server->tree->getNodeForPath($uri);
if (!$reportTargetNode instanceof Directory || $reportName !== self::REPORT_NAME) {
return;
}

$ns = '{' . $this::NS_OWNCLOUD . '}';
$requestedProps = [];
$filterRules = [];

// parse report properties and gather filter info
foreach ($report as $reportProps) {
$name = $reportProps['name'];
if ($name === $ns . 'filter-rules') {
$filterRules = $reportProps['value'];
} else if ($name === '{DAV:}prop') {
// propfind properties
foreach ($reportProps['value'] as $propVal) {
$requestedProps[] = $propVal['name'];
}
$requestedProps = $report->properties;
$filterRules = $report->filters;

if (empty($filterRules['systemtag']) && is_null($filterRules['favorite'])) {
// load all
$results = $reportTargetNode->getChildren();
} else {
// gather all file ids matching filter
try {
$resultFileIds = $this->processFilterRules($filterRules);
} catch (TagNotFoundException $e) {
throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e);
}
}

if (empty($filterRules)) {
// an empty filter would return all existing files which would be slow
throw new BadRequest('Missing filter-rule block in request');
// find sabre nodes by file id, restricted to the root node path
$results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds);
}

// gather all file ids matching filter
try {
$resultFileIds = $this->processFilterRules($filterRules);
} catch (TagNotFoundException $e) {
throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e);
if (!is_null($report->limit)) {
$length = $report->limit['size'];
$offset = $report->limit['page'] * $length;
$results = array_slice($results, $offset, $length);
}

// find sabre nodes by file id, restricted to the root node path
$results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds);

$filesUri = $this->getFilesBaseUri($uri, $reportTargetNode->getPath());
$responses = $this->prepareResponses($filesUri, $requestedProps, $results);

Expand Down Expand Up @@ -252,18 +248,9 @@ private function getFilesBaseUri($uri, $subPath) {
* @throws TagNotFoundException whenever a tag was not found
*/
protected function processFilterRules($filterRules) {
$ns = '{' . $this::NS_OWNCLOUD . '}';
$resultFileIds = null;
$systemTagIds = [];
$favoriteFilter = null;
foreach ($filterRules as $filterRule) {
if ($filterRule['name'] === $ns . 'systemtag') {
$systemTagIds[] = $filterRule['value'];
}
if ($filterRule['name'] === $ns . 'favorite') {
$favoriteFilter = true;
}
}
$systemTagIds = $filterRules['systemtag'];
$favoriteFilter = $filterRules['favorite'];

if ($favoriteFilter !== null) {
$resultFileIds = $this->fileTagger->load('files')->getFavorites();
Expand Down
104 changes: 104 additions & 0 deletions apps/dav/lib/Files/Xml/FilterRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace OCA\DAV\Files\Xml;

use Sabre\Xml\Element\Base;
use Sabre\Xml\Element\KeyValue;
use Sabre\Xml\Reader;
use Sabre\Xml\XmlDeserializable;

class FilterRequest implements XmlDeserializable {

/**
* An array with requested properties.
*
* @var array
*/
public $properties;

/**
* @var array
*/
public $filters;

/**
* @var array
*/
public $limit;

/**
* The deserialize method is called during xml parsing.
*
* This method is called statically, this is because in theory this method
* may be used as a type of constructor, or factory method.
*
* Often you want to return an instance of the current class, but you are
* free to return other data as well.
*
* You are responsible for advancing the reader to the next element. Not
* doing anything will result in a never-ending loop.
*
* If you just want to skip parsing for this element altogether, you can
* just call $reader->next();
*
* $reader->parseInnerTree() will parse the entire sub-tree, and advance to
* the next element.
*
* @param Reader $reader
* @return mixed
*/
static function xmlDeserialize(Reader $reader) {
$elems = (array)$reader->parseInnerTree([
'{DAV:}prop' => KeyValue::class,
'{http://owncloud.org/ns}filter-rules' => Base::class,
'{http://owncloud.org/ns}limit' => Base::class
]);

$newProps = [
'filters' => [
'systemtag' => [],
'favorite' => null
],
'properties' => [],
'limit' => null,
];

if (!is_array($elems)) {
$elems = [];
}

foreach ($elems as $elem) {

switch ($elem['name']) {

case '{DAV:}prop' :
$newProps['properties'] = array_keys($elem['value']);
break;
case '{http://owncloud.org/ns}filter-rules' :

foreach ($elem['value'] as $tag) {
if ($tag['name'] === '{http://owncloud.org/ns}systemtag') {
$newProps['filters']['systemtag'][] = $tag['value'];
}
if ($tag['name'] === '{http://owncloud.org/ns}favorite') {
$newProps['filters']['favorite'] = true;
}
}
break;
case '{http://owncloud.org/ns}limit' :
// TODO verify page and size
$newProps['limit'] = $elem['attributes'];
break;

}

}

$obj = new self();
foreach ($newProps as $key => $value) {
$obj->$key = $value;
}

return $obj;
}
}
57 changes: 25 additions & 32 deletions apps/dav/tests/unit/Connector/Sabre/FilesReportPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

use OCA\DAV\Connector\Sabre\Directory;
use OCA\DAV\Connector\Sabre\FilesReportPlugin as FilesReportPluginImplementation;
use OCA\DAV\Files\Xml\FilterRequest;
use OCP\Files\File;
use OCP\IConfig;
use OCP\IPreview;
Expand Down Expand Up @@ -185,21 +186,11 @@ public function testOnReportInvalidReportName() {
public function testOnReport() {
$path = 'test';

$parameters = [
[
'name' => '{DAV:}prop',
'value' => [
['name' => '{DAV:}getcontentlength', 'value' => ''],
['name' => '{http://owncloud.org/ns}size', 'value' => ''],
],
],
[
'name' => '{http://owncloud.org/ns}filter-rules',
'value' => [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
],
],
$parameters = new FilterRequest();
$parameters->properties = ['{DAV:}getcontentlength', '{http://owncloud.org/ns}size'];
$parameters->filters = [
'systemtag' => [123, 456],
'favorite' => null
];

$this->groupManager->expects($this->any())
Expand Down Expand Up @@ -441,7 +432,8 @@ public function testProcessFilterRulesSingle() {
]);

$rules = [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
'systemtag' => ['123'],
'favorite' => null
];

$this->assertEquals(['111', '222'], $this->invokePrivate($this->plugin, 'processFilterRules', [$rules]));
Expand All @@ -463,9 +455,10 @@ public function testProcessFilterRulesAndCondition() {
['456', 'files', 0, '', ['222', '333']],
]);


$rules = [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
'systemtag' => ['123', '456'],
'favorite' => null
];

$this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
Expand All @@ -488,8 +481,8 @@ public function testProcessFilterRulesAndConditionWithOneEmptyResult() {
]);

$rules = [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
'systemtag' => ['123', '456'],
'favorite' => null
];

$this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
Expand All @@ -512,8 +505,8 @@ public function testProcessFilterRulesAndConditionWithFirstEmptyResult() {
]);

$rules = [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
'systemtag' => ['123', '456'],
'favorite' => null
];

$this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
Expand All @@ -538,9 +531,8 @@ public function testProcessFilterRulesAndConditionWithEmptyMidResult() {
]);

$rules = [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '789'],
'systemtag' => ['123', '456', '789'],
'favorite' => null
];

$this->assertEquals([], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
Expand Down Expand Up @@ -585,8 +577,8 @@ public function testProcessFilterRulesInvisibleTagAsAdmin() {
->will($this->returnValue(['222', '333']));

$rules = [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
'systemtag' => ['123', '456'],
'favorite' => null
];

$this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
Expand Down Expand Up @@ -626,8 +618,8 @@ public function testProcessFilterRulesInvisibleTagAsUser() {
->will($this->returnValue([$tag1, $tag2]));

$rules = [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
'systemtag' => ['123', '456'],
'favorite' => null
];

$this->invokePrivate($this->plugin, 'processFilterRules', [$rules]);
Expand Down Expand Up @@ -673,16 +665,17 @@ public function testProcessFilterRulesVisibleTagAsUser() {
->will($this->returnValue(['222', '333']));

$rules = [
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'],
['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'],
'systemtag' => ['123', '456'],
'favorite' => null
];

$this->assertEquals(['222'], array_values($this->invokePrivate($this->plugin, 'processFilterRules', [$rules])));
}

public function testProcessFavoriteFilter() {
$rules = [
['name' => '{http://owncloud.org/ns}favorite', 'value' => '1'],
'systemtag' => [],
'favorite' => true
];

$this->privateTags->expects($this->once())
Expand Down