Skip to content

Commit

Permalink
Introduce pagination in files-filter report
Browse files Browse the repository at this point in the history
  • Loading branch information
DeepDiver1975 committed Nov 11, 2016
1 parent 3d4d0e7 commit a108477
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 70 deletions.
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 @@ -23,6 +23,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 @@ -138,6 +139,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', [$this, 'onReport']);
}
Expand All @@ -158,51 +161,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 @@ -251,18 +247,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 @@ -24,6 +24,7 @@
namespace OCA\DAV\Tests\unit\Connector\Sabre;

use OCA\DAV\Connector\Sabre\FilesReportPlugin as FilesReportPluginImplementation;
use OCA\DAV\Files\Xml\FilterRequest;
use OCP\SystemTag\ISystemTagObjectMapper;
use OC\Files\View;
use OCP\Files\Folder;
Expand Down Expand Up @@ -154,21 +155,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 @@ -400,7 +391,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 @@ -422,9 +414,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 @@ -447,8 +440,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 @@ -471,8 +464,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 @@ -497,9 +490,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 @@ -540,8 +532,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 @@ -577,8 +569,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 @@ -620,16 +612,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

0 comments on commit a108477

Please sign in to comment.