diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
index 9f57b7d9c2c9..bd28e5c3c0a6 100644
--- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
@@ -31,6 +31,8 @@
namespace OCA\DAV\Connector\Sabre;
use OC\AppFramework\Http\Request;
+use OCA\DAV\Files\IProvidesAdditionalHeaders;
+use OCA\DAV\Meta\MetaFile;
use OCP\Files\ForbiddenException;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
@@ -231,6 +233,9 @@ function httpGet(RequestInterface $request, ResponseInterface $response) {
// adds a 'Content-Disposition: attachment' header
if ($this->downloadAttachment) {
$filename = $node->getName();
+ if ($node instanceof IProvidesAdditionalHeaders) {
+ $filename = $node->getContentDispositionFileName();
+ }
if ($this->request->isUserAgent(
[
Request::USER_AGENT_IE,
@@ -255,6 +260,13 @@ function httpGet(RequestInterface $request, ResponseInterface $response) {
// cause memory problems in the nginx process.
$response->addHeader('X-Accel-Buffering', 'no');
}
+
+ if ($node instanceof IProvidesAdditionalHeaders) {
+ $headers = $node->getHeaders();
+ if (is_array($headers)) {
+ $response->addHeaders($headers);
+ }
+ }
}
/**
diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php
index 79045907aef9..3d6d31e6ec53 100644
--- a/apps/dav/lib/Connector/Sabre/Node.php
+++ b/apps/dav/lib/Connector/Sabre/Node.php
@@ -384,6 +384,9 @@ public function changeLock($type) {
$this->fileView->changeLock($this->path, $type);
}
+ /**
+ * @return \OCP\Files\FileInfo
+ */
public function getFileInfo() {
return $this->info;
}
diff --git a/apps/dav/lib/Connector/Sabre/Server.php b/apps/dav/lib/Connector/Sabre/Server.php
index c01afff71c6b..a172ee6e9708 100644
--- a/apps/dav/lib/Connector/Sabre/Server.php
+++ b/apps/dav/lib/Connector/Sabre/Server.php
@@ -24,6 +24,8 @@
namespace OCA\DAV\Connector\Sabre;
+use OCA\DAV\DAV\CopyPlugin;
+
/**
* Class \OCA\DAV\Connector\Sabre\Server
*
@@ -40,5 +42,6 @@ public function __construct($treeOrNode = null) {
parent::__construct($treeOrNode);
self::$exposeVersion = false;
$this->enablePropfindDepthInfinity = true;
+ $this->addPlugin(new CopyPlugin());
}
}
diff --git a/apps/dav/lib/DAV/CopyPlugin.php b/apps/dav/lib/DAV/CopyPlugin.php
new file mode 100644
index 000000000000..6d412fb2ac3f
--- /dev/null
+++ b/apps/dav/lib/DAV/CopyPlugin.php
@@ -0,0 +1,103 @@
+
+ *
+ * @copyright Copyright (c) 2017, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+
+namespace OCA\DAV\DAV;
+
+use OCA\DAV\Connector\Sabre\Exception\Forbidden;
+use OCA\DAV\Connector\Sabre\File;
+use OCA\DAV\Files\ICopySource;
+use OCP\Files\ForbiddenException;
+use Sabre\DAV\IFile;
+use Sabre\DAV\Server;
+use Sabre\DAV\ServerPlugin;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * Class CopyPlugin - adds own implementation of the COPY method.
+ * This is necessary because we don't want the target to be deleted before the move.
+ *
+ * Deleting the target will kill the versions which is the wrong behavior.
+ *
+ * @package OCA\DAV\DAV
+ */
+class CopyPlugin extends ServerPlugin {
+
+ /** @var Server */
+ private $server;
+
+ /**
+ * @param Server $server
+ */
+ function initialize(Server $server) {
+ $this->server = $server;
+ $server->on('method:COPY', [$this, 'httpCopy'], 90);
+ }
+
+ /**
+ * WebDAV HTTP COPY method
+ *
+ * This method copies one uri to a different uri, and works much like the MOVE request
+ * A lot of the actual request processing is done in getCopyMoveInfo
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ * @throws Forbidden
+ */
+ function httpCopy(RequestInterface $request, ResponseInterface $response) {
+
+ try {
+
+ $path = $request->getPath();
+
+ $copyInfo = $this->server->getCopyAndMoveInfo($request);
+ $sourceNode = $this->server->tree->getNodeForPath($path);
+ $destinationNode = $copyInfo['destinationNode'];
+ if (!$copyInfo['destinationExists'] || !$destinationNode instanceof File || !$sourceNode instanceof IFile) {
+ return true;
+ }
+
+ if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) return false;
+
+ $copySuccess = false;
+ if ($sourceNode instanceof ICopySource) {
+ $copySuccess = $sourceNode->copy($destinationNode->getFileInfo()->getPath());
+ }
+ if (!$copySuccess) {
+ $destinationNode->put($sourceNode->get());
+ }
+
+ $this->server->emit('afterBind', [$copyInfo['destination']]);
+
+ $response->setHeader('Content-Length', '0');
+ $response->setStatus(204);
+
+ // Sending back false will interrupt the event chain and tell the server
+ // we've handled this method.
+ return false;
+ } catch (ForbiddenException $ex) {
+ throw new Forbidden($ex->getMessage(), $ex->getRetry());
+ }
+ }
+
+}
diff --git a/apps/dav/lib/Files/ICopySource.php b/apps/dav/lib/Files/ICopySource.php
new file mode 100644
index 000000000000..44e4ceff64ec
--- /dev/null
+++ b/apps/dav/lib/Files/ICopySource.php
@@ -0,0 +1,43 @@
+
+ *
+ * @copyright Copyright (c) 2017, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+
+namespace OCA\DAV\Files;
+
+
+/**
+ * Interface ICopySource
+ * This interface allows special handling of copy operations based on the copy source.
+ * This gives the developer the freedom to implement a more efficient copy operation.
+ *
+ * @package OCA\DAV\Files
+ */
+interface ICopySource {
+
+ /**
+ * Copies the source to the given destination.
+ * If the operation was not successful false is returned.
+ *
+ * @param string $destinationPath
+ * @return boolean
+ */
+ public function copy($destinationPath);
+}
diff --git a/apps/dav/lib/Files/IProvidesAdditionalHeaders.php b/apps/dav/lib/Files/IProvidesAdditionalHeaders.php
new file mode 100644
index 000000000000..88e9c9020770
--- /dev/null
+++ b/apps/dav/lib/Files/IProvidesAdditionalHeaders.php
@@ -0,0 +1,44 @@
+
+ *
+ * @copyright Copyright (c) 2017, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+
+namespace OCA\DAV\Files;
+
+
+/**
+ * Interface IProvidesAdditionalHeaders
+ * This interface allows to add additional headers to the response
+ *
+ * @package OCA\DAV\Files
+ */
+interface IProvidesAdditionalHeaders {
+
+ /**
+ * @return array
+ */
+ public function getHeaders();
+
+ /**
+ * @return string
+ */
+ public function getContentDispositionFileName();
+
+}
diff --git a/apps/dav/lib/Meta/MetaFile.php b/apps/dav/lib/Meta/MetaFile.php
index 3053f75817ee..b3e277b6361d 100644
--- a/apps/dav/lib/Meta/MetaFile.php
+++ b/apps/dav/lib/Meta/MetaFile.php
@@ -23,9 +23,18 @@
namespace OCA\DAV\Meta;
+use OC\Files\Meta\MetaFileVersionNode;
+use OCA\DAV\Files\ICopySource;
+use OCA\DAV\Files\IProvidesAdditionalHeaders;
use Sabre\DAV\File;
-class MetaFile extends File {
+/**
+ * Class MetaFile
+ * This is a Sabre based implementation of a file living in the /meta resource.
+ *
+ * @package OCA\DAV\Meta
+ */
+class MetaFile extends File implements ICopySource, IProvidesAdditionalHeaders {
/** @var \OCP\Files\File */
private $file;
@@ -74,7 +83,40 @@ public function getLastModified() {
return $this->file->getMTime();
}
+ /**
+ * @inheritdoc
+ */
public function getETag() {
return $this->file->getEtag();
}
+
+ /**
+ * @inheritdoc
+ */
+ public function copy($path) {
+ if ($this->file instanceof MetaFileVersionNode) {
+ return $this->file->copy($path);
+ }
+ return false;
+ }
+
+ /**
+ * @return array
+ */
+ public function getHeaders() {
+ if ($this->file instanceof \OCP\Files\IProvidesAdditionalHeaders) {
+ return $this->file->getHeaders();
+ }
+ return [];
+ }
+
+ /**
+ * @return string
+ */
+ public function getContentDispositionFileName() {
+ if ($this->file instanceof \OCP\Files\IProvidesAdditionalHeaders) {
+ return $this->file->getContentDispositionFileName();
+ }
+ return $this->getName();
+ }
}
diff --git a/apps/dav/lib/Meta/MetaFolder.php b/apps/dav/lib/Meta/MetaFolder.php
index 46082d69a777..c09973818e5b 100644
--- a/apps/dav/lib/Meta/MetaFolder.php
+++ b/apps/dav/lib/Meta/MetaFolder.php
@@ -28,6 +28,12 @@
use OCP\Files\Node;
use Sabre\DAV\Collection;
+/**
+ * Class MetaFolder
+ * This is a Sabre based implementation of a folder living in the /meta resource.
+ *
+ * @package OCA\DAV\Meta
+ */
class MetaFolder extends Collection {
/** @var Folder */
@@ -48,7 +54,7 @@ public function __construct(Folder $folder) {
function getChildren() {
$nodes = $this->folder->getDirectoryListing();
return array_map(function($node) {
- return static::nodeFactory($node);
+ return $this->nodeFactory($node);
}, $nodes);
}
@@ -59,7 +65,7 @@ function getName() {
return $this->folder->getName();
}
- public static function nodeFactory(Node $node) {
+ private function nodeFactory(Node $node) {
if ($node instanceof Folder) {
return new MetaFolder($node);
}
diff --git a/apps/dav/lib/Meta/RootCollection.php b/apps/dav/lib/Meta/RootCollection.php
index cf3b55a93bcc..59620b79895d 100644
--- a/apps/dav/lib/Meta/RootCollection.php
+++ b/apps/dav/lib/Meta/RootCollection.php
@@ -23,10 +23,8 @@
namespace OCA\DAV\Meta;
-use OCP\Files\File;
-use OCP\Files\Folder;
+use OC\Files\Meta\MetaFileIdNode;
use OCP\Files\IRootFolder;
-use OCP\Files\Node;
use OCP\Files\NotFoundException;
use Sabre\DAV\Collection;
use Sabre\DAV\Exception\MethodNotAllowed;
@@ -52,8 +50,11 @@ public function __construct(IRootFolder $rootFolder) {
public function getChild($name) {
try {
$child = $this->rootFolder->get("meta/$name");
- return MetaFolder::nodeFactory($child);
- } catch (NotFoundException $ex) {
+ if (!$child instanceof MetaFileIdNode) {
+ throw new NotFound();
+ }
+ return new MetaFolder($child);
+ } catch (NotFoundException $exception) {
throw new NotFound();
}
}
diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
index df2327d7b9b5..b8e445a15cdd 100644
--- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
+++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php
@@ -25,10 +25,19 @@
*/
namespace OCA\DAV\Tests\unit\Connector\Sabre;
+use OC\User\User;
+use OCA\DAV\Connector\Sabre\Directory;
+use OCA\DAV\Connector\Sabre\File;
use OCA\DAV\Connector\Sabre\FilesPlugin;
+use OCA\DAV\Connector\Sabre\Node;
+use OCA\DAV\Meta\MetaFile;
use OCP\Files\StorageNotAvailableException;
+use OCP\IConfig;
+use OCP\IRequest;
use Sabre\DAV\PropFind;
use Sabre\DAV\PropPatch;
+use Sabre\DAV\Server;
+use Sabre\DAV\Tree;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
use Test\TestCase;
@@ -53,12 +62,12 @@ class FilesPluginTest extends TestCase {
const DATA_FINGERPRINT_PROPERTYNAME = FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME;
/**
- * @var \Sabre\DAV\Server | \PHPUnit_Framework_MockObject_MockObject
+ * @var Server | \PHPUnit_Framework_MockObject_MockObject
*/
private $server;
/**
- * @var \Sabre\DAV\Tree | \PHPUnit_Framework_MockObject_MockObject
+ * @var Tree | \PHPUnit_Framework_MockObject_MockObject
*/
private $tree;
@@ -68,28 +77,28 @@ class FilesPluginTest extends TestCase {
private $plugin;
/**
- * @var \OCP\IConfig | \PHPUnit_Framework_MockObject_MockObject
+ * @var IConfig | \PHPUnit_Framework_MockObject_MockObject
*/
private $config;
/**
- * @var \OCP\IRequest | \PHPUnit_Framework_MockObject_MockObject
+ * @var IRequest | \PHPUnit_Framework_MockObject_MockObject
*/
private $request;
public function setUp() {
parent::setUp();
- $this->server = $this->getMockBuilder('\Sabre\DAV\Server')
+ $this->server = $this->getMockBuilder(Server::class)
->disableOriginalConstructor()
->getMock();
- $this->tree = $this->getMockBuilder('\Sabre\DAV\Tree')
+ $this->tree = $this->getMockBuilder(Tree::class)
->disableOriginalConstructor()
->getMock();
- $this->config = $this->createMock('\OCP\IConfig');
+ $this->config = $this->createMock(IConfig::class);
$this->config->expects($this->any())->method('getSystemValue')
->with($this->equalTo('data-fingerprint'), $this->equalTo(''))
->willReturn('my_fingerprint');
- $this->request = $this->createMock('\OCP\IRequest');
+ $this->request = $this->createMock(IRequest::class);
$this->plugin = new FilesPlugin(
$this->tree,
@@ -107,6 +116,7 @@ public function setUp() {
/**
* @param string $class
+ * @param string $path
* @return \PHPUnit_Framework_MockObject_MockObject
*/
private function createTestNode($class, $path = '/dummypath') {
@@ -149,8 +159,8 @@ private function createTestNode($class, $path = '/dummypath') {
}
public function testGetPropertiesForFile() {
- /** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit_Framework_MockObject_MockObject $node */
- $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
+ /** @var File | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->createTestNode(File::class);
$propFind = new PropFind(
'/dummyPath',
@@ -168,7 +178,7 @@ public function testGetPropertiesForFile() {
0
);
- $user = $this->getMockBuilder('\OC\User\User')
+ $user = $this->getMockBuilder(User::class)
->disableOriginalConstructor()->getMock();
$user
->expects($this->once())
@@ -204,8 +214,8 @@ public function testGetPropertiesForFile() {
}
public function testGetPropertiesStorageNotAvailable() {
- /** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit_Framework_MockObject_MockObject $node */
- $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
+ /** @var File | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->createTestNode(File::class);
$propFind = new PropFind(
'/dummyPath',
@@ -231,7 +241,7 @@ public function testGetPublicPermissions() {
$this->plugin = new FilesPlugin(
$this->tree,
$this->config,
- $this->createMock('\OCP\IRequest'),
+ $this->request,
true);
$this->plugin->initialize($this->server);
@@ -243,8 +253,8 @@ public function testGetPublicPermissions() {
0
);
- /** @var \OCA\DAV\Connector\Sabre\File | \PHPUnit_Framework_MockObject_MockObject $node */
- $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
+ /** @var File | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->createTestNode(File::class);
$node->expects($this->any())
->method('getDavPermissions')
->will($this->returnValue('DWCKMSR'));
@@ -258,8 +268,8 @@ public function testGetPublicPermissions() {
}
public function testGetPropertiesForDirectory() {
- /** @var \OCA\DAV\Connector\Sabre\Directory | \PHPUnit_Framework_MockObject_MockObject $node */
- $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\Directory');
+ /** @var Directory | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->createTestNode(Directory::class);
$propFind = new PropFind(
'/dummyPath',
@@ -293,8 +303,8 @@ public function testGetPropertiesForDirectory() {
}
public function testGetPropertiesForRootDirectory() {
- /** @var \OCA\DAV\Connector\Sabre\Directory | \PHPUnit_Framework_MockObject_MockObject $node */
- $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ /** @var Directory | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->getMockBuilder(Directory::class)
->disableOriginalConstructor()
->getMock();
$node->expects($this->any())->method('getPath')->willReturn('/');
@@ -328,8 +338,8 @@ public function testGetPropertiesForRootDirectory() {
* @expectedException \Sabre\DAV\Exception\NotFound
*/
public function testGetPropertiesWhenNoPermission() {
- /** @var \OCA\DAV\Connector\Sabre\Directory | \PHPUnit_Framework_MockObject_MockObject $node */
- $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory')
+ /** @var Directory | \PHPUnit_Framework_MockObject_MockObject $node */
+ $node = $this->getMockBuilder(Directory::class)
->disableOriginalConstructor()
->getMock();
$node->expects($this->any())->method('getPath')->willReturn('/');
@@ -358,7 +368,7 @@ public function testGetPropertiesWhenNoPermission() {
}
public function testUpdateProps() {
- $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File');
+ $node = $this->createTestNode(File::class);
$testDate = 'Fri, 13 Feb 2015 00:01:02 GMT';
@@ -433,14 +443,14 @@ public function testUpdatePropsForbidden() {
* @expectedExceptionMessage FolderA/test.txt cannot be deleted
*/
public function testMoveSrcNotDeletable() {
- $fileInfoFolderATestTXT = $this->getMockBuilder('\OCP\Files\FileInfo')
+ $fileInfoFolderATestTXT = $this->getMockBuilder(FileInfo::class)
->disableOriginalConstructor()
->getMock();
$fileInfoFolderATestTXT->expects($this->once())
->method('isDeletable')
->willReturn(false);
- $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ $node = $this->getMockBuilder(Node::class)
->disableOriginalConstructor()
->getMock();
$node->expects($this->once())
@@ -454,14 +464,14 @@ public function testMoveSrcNotDeletable() {
}
public function testMoveSrcDeletable() {
- $fileInfoFolderATestTXT = $this->getMockBuilder('\OCP\Files\FileInfo')
+ $fileInfoFolderATestTXT = $this->getMockBuilder(FileInfo::class)
->disableOriginalConstructor()
->getMock();
$fileInfoFolderATestTXT->expects($this->once())
->method('isDeletable')
->willReturn(true);
- $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ $node = $this->getMockBuilder(Node::class)
->disableOriginalConstructor()
->getMock();
$node->expects($this->once())
@@ -479,7 +489,7 @@ public function testMoveSrcDeletable() {
* @expectedExceptionMessage FolderA/test.txt does not exist
*/
public function testMoveSrcNotExist() {
- $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node')
+ $node = $this->getMockBuilder(Node::class)
->disableOriginalConstructor()
->getMock();
$node->expects($this->once())
@@ -507,21 +517,25 @@ public function downloadHeadersProvider() {
/**
* @dataProvider downloadHeadersProvider
+ * @param boolean $isClumsyAgent
+ * @param string $contentDispositionHeader
*/
public function testDownloadHeaders($isClumsyAgent, $contentDispositionHeader) {
+ /** @var RequestInterface | \PHPUnit_Framework_MockObject_MockObject $request */
$request = $this->getMockBuilder(RequestInterface::class)
->disableOriginalConstructor()
->getMock();
+ /** @var ResponseInterface | \PHPUnit_Framework_MockObject_MockObject $response */
$response = $this->getMockBuilder(ResponseInterface::class)
- ->disableOriginalConstructor()
- ->getMock();
+ ->disableOriginalConstructor()
+ ->getMock();
$request
->expects($this->once())
->method('getPath')
->will($this->returnValue('test/somefile.xml'));
- $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ $node = $this->getMockBuilder(File::class)
->disableOriginalConstructor()
->getMock();
$node
@@ -529,6 +543,11 @@ public function testDownloadHeaders($isClumsyAgent, $contentDispositionHeader) {
->method('getName')
->will($this->returnValue('somefile.xml'));
+ $node->expects($this->once())
+ ->method('getChecksum')
+ ->with('sha1')
+ ->willReturn('abcdefghijkl');
+
$this->tree
->expects($this->once())
->method('getNodeForPath')
@@ -541,13 +560,60 @@ public function testDownloadHeaders($isClumsyAgent, $contentDispositionHeader) {
->will($this->returnValue($isClumsyAgent));
$response
- ->expects($this->exactly(2))
+ ->expects($this->exactly(3))
->method('addHeader')
->withConsecutive(
['Content-Disposition', $contentDispositionHeader],
+ ['OC-Checksum', 'abcdefghijkl'],
['X-Accel-Buffering', 'no']
);
$this->plugin->httpGet($request, $response);
}
+
+ public function testAdditionalHeaders() {
+ /** @var RequestInterface | \PHPUnit_Framework_MockObject_MockObject $request */
+ $request = $this->getMockBuilder(RequestInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ /** @var ResponseInterface | \PHPUnit_Framework_MockObject_MockObject $response */
+ $response = $this->getMockBuilder(ResponseInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request
+ ->expects($this->once())
+ ->method('getPath')
+ ->willReturn('test/somefile.xml');
+
+ $node = $this->getMockBuilder(MetaFile::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node
+ ->expects($this->once())
+ ->method('getName')
+ ->willReturn('somefile.xml');
+
+ $this->tree
+ ->expects($this->once())
+ ->method('getNodeForPath')
+ ->with('test/somefile.xml')
+ ->willReturn($node);
+
+ $node
+ ->expects($this->once())
+ ->method('getHeaders')
+ ->willReturn([
+ 'a' => 'b'
+ ]);
+
+ $response
+ ->expects($this->once())
+ ->method('addHeaders')
+ ->with(
+ ['a' => 'b']
+ );
+
+ $this->plugin->httpGet($request, $response);
+ }
}
diff --git a/apps/dav/tests/unit/DAV/CopyPluginTest.php b/apps/dav/tests/unit/DAV/CopyPluginTest.php
new file mode 100644
index 000000000000..1bf14c8c9aa7
--- /dev/null
+++ b/apps/dav/tests/unit/DAV/CopyPluginTest.php
@@ -0,0 +1,118 @@
+
+ *
+ * @copyright Copyright (c) 2017, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+
+namespace OCA\DAV\Tests\unit\DAV;
+
+
+use OCA\DAV\Connector\Sabre\Directory;
+use OCA\DAV\Connector\Sabre\File;
+use OCA\DAV\DAV\CopyPlugin;
+use Sabre\DAV\ICollection;
+use Sabre\DAV\IFile;
+use Sabre\DAV\Server;
+use Sabre\DAV\Tree;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Test\TestCase;
+
+class CopyPluginTest extends TestCase {
+
+ /** @var Server | \PHPUnit_Framework_MockObject_MockObject */
+ private $server;
+ /** @var CopyPlugin */
+ private $plugin;
+ /** @var Tree | \PHPUnit_Framework_MockObject_MockObject */
+ private $tree;
+ /** @var RequestInterface | \PHPUnit_Framework_MockObject_MockObject */
+ private $request;
+ /** @var ResponseInterface | \PHPUnit_Framework_MockObject_MockObject */
+ private $response;
+
+ public function setUp() {
+ parent::setUp();
+ $this->plugin = new CopyPlugin();
+
+ $this->server = $this->createMock(Server::class);
+ $this->tree = $this->createMock(Tree::class);
+ $this->server->tree = $this->tree;
+ /** @var RequestInterface | \PHPUnit_Framework_MockObject_MockObject $request */
+ $this->request = $this->createMock(RequestInterface::class);
+ /** @var ResponseInterface | \PHPUnit_Framework_MockObject_MockObject $response */
+ $this->response = $this->createMock( ResponseInterface::class);
+
+ $this->plugin->initialize($this->server);
+ }
+
+ /**
+ * @dataProvider providesSourceAndDestinations
+ * @param bool $destinationExists
+ * @param $destinationNode
+ * @param $sourceNode
+ */
+ public function testCopyPluginReturnTrue($destinationExists, $destinationNode, $sourceNode) {
+
+ $this->tree->expects($this->once())->method('getNodeForPath')->willReturn($sourceNode);
+ $this->server->expects($this->once())->method('getCopyAndMoveInfo')->willReturn([
+ 'destinationExists' => $destinationExists,
+ 'destinationNode' => $destinationNode
+ ]);
+
+ $returnValue = $this->plugin->httpCopy($this->request, $this->response);
+ $this->assertTrue($returnValue);
+ }
+
+ public function providesSourceAndDestinations() {
+ return [
+ 'destination does not exist' => [false, null, null],
+ 'destination is not a File' => [true, $this->createMock(Directory::class), $this->createMock(IFile::class)],
+ 'source is not a IFile' => [true, $this->createMock(File::class), $this->createMock(ICollection::class)],
+ ];
+ }
+
+ public function testCopyPluginReturnFalse() {
+
+ $destinationNode = $this->createMock(File::class);
+ $sourceNode = $this->createMock(IFile::class);
+
+ $this->tree->expects($this->once())->method('getNodeForPath')->willReturn($sourceNode);
+ $this->server->expects($this->once())->method('getCopyAndMoveInfo')->willReturn([
+ 'destinationExists' => true,
+ 'destinationNode' => $destinationNode,
+ 'destination' => 'destination.txt'
+ ]);
+
+ // make sure the plugin properly emits beforeBind and afterBind
+ $this->server->expects($this->exactly(2))->method('emit')->withConsecutive(
+ ['beforeBind', ['destination.txt']], ['afterBind', ['destination.txt']])->willReturn(true);
+
+ // make sure the file content is actually copied over
+ $sourceNode->expects($this->once())->method('get')->willReturn('123456');
+ $destinationNode->expects($this->once())->method('put')->with('123456');
+
+ // make sure http status and content length are properly set
+ $this->response->expects($this->once())->method('setHeader')->with('Content-Length', '0');
+ $this->response->expects($this->once())->method('setStatus')->with(204);
+
+ $returnValue = $this->plugin->httpCopy($this->request, $this->response);
+ $this->assertFalse($returnValue);
+ }
+}
diff --git a/apps/files_versions/ajax/getVersions.php b/apps/files_versions/ajax/getVersions.php
deleted file mode 100644
index e7d3a0b011d3..000000000000
--- a/apps/files_versions/ajax/getVersions.php
+++ /dev/null
@@ -1,56 +0,0 @@
-
- * @author Björn Schießle
- * @author Frank Karlitschek
- * @author Lukas Reschke
- * @author Sam Tuke
- * @author Thomas Müller
- * @author Vincent Petry
- *
- * @copyright Copyright (c) 2017, ownCloud GmbH
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see
- *
- */
-OCP\JSON::checkLoggedIn();
-OCP\JSON::callCheck();
-OCP\JSON::checkAppEnabled('files_versions');
-
-$source = (string)$_GET['source'];
-$start = (int)$_GET['start'];
-list ($uid, $filename) = OCA\Files_Versions\Storage::getUidAndFilename($source);
-$count = 5; //show the newest revisions
-$versions = OCA\Files_Versions\Storage::getVersions($uid, $filename, $source);
-if( $versions ) {
-
- $endReached = false;
- if (count($versions) <= $start+$count) {
- $endReached = true;
- }
-
- $versions = array_slice($versions, $start, $count);
-
- // remove owner path from request to not disclose it to the recipient
- foreach ($versions as $version) {
- unset($version['path']);
- }
-
- \OCP\JSON::success(['data' => ['versions' => $versions, 'endReached' => $endReached]]);
-
-} else {
-
- \OCP\JSON::success(['data' => ['versions' => [], 'endReached' => true]]);
-
-}
diff --git a/apps/files_versions/ajax/rollbackVersion.php b/apps/files_versions/ajax/rollbackVersion.php
deleted file mode 100644
index 1d5f533eb297..000000000000
--- a/apps/files_versions/ajax/rollbackVersion.php
+++ /dev/null
@@ -1,40 +0,0 @@
-
- * @author Björn Schießle
- * @author Frank Karlitschek
- * @author Lukas Reschke
- * @author Robin Appelman
- * @author Sam Tuke
- * @author Thomas Müller
- * @author Thomas Tanghus
- *
- * @copyright Copyright (c) 2017, ownCloud GmbH
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see
- *
- */
-OCP\JSON::checkLoggedIn();
-OCP\JSON::checkAppEnabled('files_versions');
-OCP\JSON::callCheck();
-
-$file = (string)$_GET['file'];
-$revision=(int)$_GET['revision'];
-
-if(OCA\Files_Versions\Storage::rollback( $file, $revision )) {
- OCP\JSON::success(["data" => ["revision" => $revision, "file" => $file]]);
-}else{
- $l = \OC::$server->getL10N('files_versions');
- OCP\JSON::error(["data" => ["message" => $l->t("Could not revert: %s", [$file])]]);
-}
diff --git a/apps/files_versions/appinfo/routes.php b/apps/files_versions/appinfo/routes.php
index 25bc2d5cf68b..5e28348619c8 100644
--- a/apps/files_versions/appinfo/routes.php
+++ b/apps/files_versions/appinfo/routes.php
@@ -35,10 +35,3 @@ function() {
require_once __DIR__ . '/../ajax/preview.php';
});
-$this->create('files_versions_download', 'download.php')
- ->actionInclude('files_versions/download.php');
-$this->create('files_versions_ajax_getVersions', 'ajax/getVersions.php')
- ->actionInclude('files_versions/ajax/getVersions.php');
-$this->create('files_versions_ajax_rollbackVersion', 'ajax/rollbackVersion.php')
- ->actionInclude('files_versions/ajax/rollbackVersion.php');
-
diff --git a/apps/files_versions/download.php b/apps/files_versions/download.php
deleted file mode 100644
index 28ba05535255..000000000000
--- a/apps/files_versions/download.php
+++ /dev/null
@@ -1,48 +0,0 @@
-
- * @author Björn Schießle
- * @author Lukas Reschke
- * @author Morris Jobke
- * @author Roeland Jago Douma
- * @author Vincent Petry
- *
- * @copyright Copyright (c) 2017, ownCloud GmbH
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see
- *
- */
-
-OCP\JSON::checkAppEnabled('files_versions');
-OCP\JSON::checkLoggedIn();
-
-$file = $_GET['file'];
-$revision=(int)$_GET['revision'];
-
-list($uid, $filename) = OCA\Files_Versions\Storage::getUidAndFilename($file);
-
-$versionName = '/'.$uid.'/files_versions/'.$filename.'.v'.$revision;
-
-$view = new OC\Files\View('/');
-
-$ftype = \OC::$server->getMimeTypeDetector()->getSecureMimeType($view->getMimeType('/'.$uid.'/files/'.$filename));
-
-header('Content-Type:'.$ftype);
-OCP\Response::setContentDispositionHeader(basename($filename), 'attachment');
-OCP\Response::disableCaching();
-OCP\Response::setContentLengthHeader($view->filesize($versionName));
-
-OC_Util::obEnd();
-
-$view->readfile($versionName);
diff --git a/apps/files_versions/js/versioncollection.js b/apps/files_versions/js/versioncollection.js
index fdb12bae0a95..5ec4e21586ea 100644
--- a/apps/files_versions/js/versioncollection.js
+++ b/apps/files_versions/js/versioncollection.js
@@ -13,6 +13,8 @@
* @memberof OCA.Versions
*/
var VersionCollection = OC.Backbone.Collection.extend({
+ sync: OC.Backbone.davSync,
+
model: OCA.Versions.VersionModel,
/**
@@ -20,72 +22,34 @@
*/
_fileInfo: null,
- _endReached: false,
- _currentIndex: 0,
-
url: function() {
- var url = OC.generateUrl('/apps/files_versions/ajax/getVersions.php');
- var query = {
- source: this._fileInfo.getFullPath(),
- start: this._currentIndex
- };
- return url + '?' + OC.buildQueryString(query);
+ return OC.linkToRemoteBase('dav') + '/meta/' +
+ encodeURIComponent(this._fileInfo.get('id')) + '/v';
},
setFileInfo: function(fileInfo) {
this._fileInfo = fileInfo;
- // reset
- this._endReached = false;
- this._currentIndex = 0;
},
getFileInfo: function() {
return this._fileInfo;
},
- hasMoreResults: function() {
- return !this._endReached;
- },
-
- fetch: function(options) {
- if (!options || options.remove) {
- this._currentIndex = 0;
- }
- return OC.Backbone.Collection.prototype.fetch.apply(this, arguments);
- },
-
- /**
- * Fetch the next set of results
- */
- fetchNext: function() {
- if (!this.hasMoreResults()) {
- return null;
- }
- if (this._currentIndex === 0) {
- return this.fetch();
- }
- return this.fetch({remove: false});
- },
-
- reset: function() {
- this._currentIndex = 0;
- OC.Backbone.Collection.prototype.reset.apply(this, arguments);
- },
-
parse: function(result) {
var fullPath = this._fileInfo.getFullPath();
- var results = _.map(result.data.versions, function(version) {
- var revision = parseInt(version.version, 10);
+ var fileId = this._fileInfo.get('id');
+ var results = _.map(result, function(version) {
+ var revision = parseInt(version.id, 10);
return {
id: revision,
- name: version.name,
+ name: revision,
fullPath: fullPath,
timestamp: revision,
- size: version.size
+ versionId: revision,
+ size: version['{DAV:}getcontentlength'],
+ fileId: fileId
};
});
- this._endReached = result.data.endReached;
- this._currentIndex += results.length;
return results;
}
});
diff --git a/apps/files_versions/js/versionmodel.js b/apps/files_versions/js/versionmodel.js
index dc610fc21449..5551aa2430d2 100644
--- a/apps/files_versions/js/versionmodel.js
+++ b/apps/files_versions/js/versionmodel.js
@@ -20,31 +20,25 @@
revert: function(options) {
options = options ? _.clone(options) : {};
var model = this;
- var file = this.getFullPath();
- var revision = this.get('timestamp');
+ var client = new OC.Files.Client({
+ host: OC.getHost(),
+ root: OC.linkToRemoteBase('dav') + '/files/' + OC.getCurrentUser().uid,
+ useHTTPS: OC.getProtocol() === 'https'
+ });
- $.ajax({
- type: 'GET',
- url: OC.generateUrl('/apps/files_versions/ajax/rollbackVersion.php'),
- dataType: 'json',
- data: {
- file: file,
- revision: revision
- },
- success: function(response) {
- if (response.status === 'error') {
- if (options.error) {
- options.error.call(options.context, model, response, options);
- }
- model.trigger('error', model, response, options);
- } else {
- if (options.success) {
- options.success.call(options.context, model, response, options);
- }
- model.trigger('revert', model, response, options);
+ client.copy(this.getDownloadUrl(), this.getFullPath(), true, {}, {pathIsUrl: true})
+ .done(function() {
+ if (options.success) {
+ options.success.call(options.context, model, {}, options);
}
- }
- });
+ model.trigger('revert', model, options);
+ })
+ .fail(function () {
+ if (options.error) {
+ options.error.call(options.context, model, {}, options);
+ }
+ model.trigger('error', model, {}, options);
+ });
},
getFullPath: function() {
@@ -61,12 +55,9 @@
},
getDownloadUrl: function() {
- var url = OC.generateUrl('/apps/files_versions/download.php');
- var params = {
- file: this.get('fullPath'),
- revision: this.get('timestamp')
- };
- return url + '?' + OC.buildQueryString(params);
+ return OC.linkToRemoteBase('dav') + '/meta/' +
+ encodeURIComponent(this.get('fileId')) + '/v/' +
+ encodeURIComponent(this.get('versionId'));
}
});
diff --git a/apps/files_versions/js/versionstabview.js b/apps/files_versions/js/versionstabview.js
index abbfd4502db7..1faaf2d89884 100644
--- a/apps/files_versions/js/versionstabview.js
+++ b/apps/files_versions/js/versionstabview.js
@@ -37,8 +37,6 @@
'' +
'' +
'{{emptyResultLabel}}
' +
- '' +
'';
/**
@@ -54,8 +52,7 @@
$versionsContainer: null,
events: {
- 'click .revertVersion': '_onClickRevertVersion',
- 'click .showMoreVersions': '_onClickShowMoreVersions'
+ 'click .revertVersion': '_onClickRevertVersion'
},
initialize: function() {
@@ -73,19 +70,14 @@
},
nextPage: function() {
- if (this._loading || !this.collection.hasMoreResults()) {
+ if (this._loading) {
return;
}
if (this.collection.getFileInfo() && this.collection.getFileInfo().isDirectory()) {
return;
}
- this.collection.fetchNext();
- },
-
- _onClickShowMoreVersions: function(ev) {
- ev.preventDefault();
- this.nextPage();
+ this.collection.fetch();
},
_onClickRevertVersion: function(ev) {
@@ -100,8 +92,6 @@
ev.preventDefault();
revision = $target.attr('data-revision');
- this.$el.find('.versions, .showMoreVersions').addClass('hidden');
-
var versionModel = this.collection.get(revision);
versionModel.revert({
success: function() {
@@ -109,7 +99,7 @@
self.$versionsContainer.empty();
self.collection.setFileInfo(fileInfoModel);
self.collection.reset([], {silent: true});
- self.collection.fetchNext();
+ self.collection.fetch();
self.$el.find('.versions').removeClass('hidden');
@@ -151,13 +141,11 @@
_onRequest: function() {
this._toggleLoading(true);
- this.$el.find('.showMoreVersions').addClass('hidden');
},
_onEndRequest: function() {
this._toggleLoading(false);
this.$el.find('.empty').toggleClass('hidden', !!this.collection.length);
- this.$el.find('.showMoreVersions').toggleClass('hidden', !this.collection.hasMoreResults());
},
_onAddModel: function(model) {
@@ -218,7 +206,6 @@
render: function() {
this.$el.html(this.template({
emptyResultLabel: t('files_versions', 'No other versions available'),
- moreVersionsLabel: t('files_versions', 'More versions...')
}));
this.$el.find('.has-tooltip').tooltip();
this.$versionsContainer = this.$el.find('ul.versions');
diff --git a/apps/files_versions/lib/Storage.php b/apps/files_versions/lib/Storage.php
index 95866d2824a7..130be95b22e3 100644
--- a/apps/files_versions/lib/Storage.php
+++ b/apps/files_versions/lib/Storage.php
@@ -309,68 +309,53 @@ public static function renameOrCopy($sourcePath, $targetPath, $operation) {
}
- /**
- * Rollback to an old version of a file.
- *
- * @param string $file file name
- * @param int $revision revision timestamp
- */
- public static function rollback($file, $revision) {
-
- if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') {
- // add expected leading slash
- $file = '/' . ltrim($file, '/');
- list($uid, $filename) = self::getUidAndFilename($file);
- if ($uid === null || trim($filename, '/') === '') {
- return false;
- }
- $users_view = new View('/'.$uid);
- $files_view = new View('/'. User::getUser().'/files');
- if (!$files_view->isUpdatable($filename)) {
- return false;
- }
-
- $versionCreated = false;
+ public static function restoreVersion($uid, $filename, $fileToRestore, $revision) {
+ if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED) !== true) {
+ return false;
+ }
+ $users_view = new View('/'.$uid);
+ if (!$users_view->isUpdatable("/files$filename")) {
+ return false;
+ }
- //first create a new version
- $version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename);
- if (!$users_view->file_exists($version)) {
- $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename));
- $versionCreated = true;
- }
+ $versionCreated = false;
- $fileToRestore = 'files_versions' . $filename . '.v' . $revision;
-
- // Restore encrypted version of the old file for the newly restored file
- // This has to happen manually here since the file is manually copied below
- $oldVersion = $users_view->getFileInfo($fileToRestore)->getEncryptedVersion();
- $oldFileInfo = $users_view->getFileInfo($fileToRestore);
- $newFileInfo = $files_view->getFileInfo($filename);
- $cache = $newFileInfo->getStorage()->getCache();
- $cache->update(
- $newFileInfo->getId(), [
- 'encrypted' => $oldVersion,
- 'encryptedVersion' => $oldVersion,
- 'size' => $oldFileInfo->getSize()
- ]
- );
-
- // rollback
- if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) {
- $files_view->touch($file, $revision);
- Storage::scheduleExpire($uid, $file);
- \OC_Hook::emit('\OCP\Versions', 'rollback', [
- 'path' => $filename,
- 'revision' => $revision,
- ]);
- return true;
- } else if ($versionCreated) {
- self::deleteVersion($users_view, $version);
- }
+ //first create a new version
+ $version = 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename);
+ if (!$users_view->file_exists($version)) {
+ $users_view->copy('files'.$filename, 'files_versions'.$filename.'.v'.$users_view->filemtime('files'.$filename));
+ $versionCreated = true;
}
- return false;
+ // Restore encrypted version of the old file for the newly restored file
+ // This has to happen manually here since the file is manually copied below
+ $oldVersion = $users_view->getFileInfo($fileToRestore)->getEncryptedVersion();
+ $oldFileInfo = $users_view->getFileInfo($fileToRestore);
+ $newFileInfo = $users_view->getFileInfo("/files$filename");
+ $cache = $newFileInfo->getStorage()->getCache();
+ $cache->update(
+ $newFileInfo->getId(), [
+ 'encrypted' => $oldVersion,
+ 'encryptedVersion' => $oldVersion,
+ 'size' => $oldFileInfo->getSize()
+ ]
+ );
+
+ // rollback
+ if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) {
+ $users_view->touch("/files$filename", $revision);
+ Storage::scheduleExpire($uid, $filename);
+ \OC_Hook::emit('\OCP\Versions', 'rollback', [
+ 'path' => $filename,
+ 'user' => $uid,
+ 'revision' => $revision,
+ ]);
+ return true;
+ } else if ($versionCreated) {
+ self::deleteVersion($users_view, $version);
+ }
+ return true;
}
/**
diff --git a/apps/files_versions/tests/VersioningTest.php b/apps/files_versions/tests/VersioningTest.php
index e9b22e31455e..13c1e8f5a492 100644
--- a/apps/files_versions/tests/VersioningTest.php
+++ b/apps/files_versions/tests/VersioningTest.php
@@ -118,57 +118,6 @@ protected function tearDown() {
parent::tearDown();
}
-
- public function testMoveFileIntoSharedFolderAsRecipient() {
-
- \OC\Files\Filesystem::mkdir('folder1');
- $fileInfo = \OC\Files\Filesystem::getFileInfo('folder1');
-
- $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
- $share = \OC::$server->getShareManager()->newShare();
- $share->setNode($node)
- ->setShareType(\OCP\Share::SHARE_TYPE_USER)
- ->setSharedBy(self::TEST_VERSIONS_USER)
- ->setSharedWith(self::TEST_VERSIONS_USER2)
- ->setPermissions(\OCP\Constants::PERMISSION_ALL);
- $share = \OC::$server->getShareManager()->createShare($share);
-
- self::loginHelper(self::TEST_VERSIONS_USER2);
- $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions';
- \OC\Files\Filesystem::file_put_contents('test.txt', 'test file');
-
- $t1 = time();
- // second version is two weeks older, this way we make sure that no
- // version will be expired
- $t2 = $t1 - 60 * 60 * 24 * 14;
-
- $this->rootView->mkdir($versionsFolder2);
- // create some versions
- $v1 = $versionsFolder2 . '/test.txt.v' . $t1;
- $v2 = $versionsFolder2 . '/test.txt.v' . $t2;
-
- $this->rootView->file_put_contents($v1, 'version1');
- $this->rootView->file_put_contents($v2, 'version2');
-
- // move file into the shared folder as recipient
- \OC\Files\Filesystem::rename('/test.txt', '/folder1/test.txt');
-
- $this->assertFalse($this->rootView->file_exists($v1));
- $this->assertFalse($this->rootView->file_exists($v2));
-
- self::loginHelper(self::TEST_VERSIONS_USER);
-
- $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions';
-
- $v1Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t1;
- $v2Renamed = $versionsFolder1 . '/folder1/test.txt.v' . $t2;
-
- $this->assertTrue($this->rootView->file_exists($v1Renamed));
- $this->assertTrue($this->rootView->file_exists($v2Renamed));
-
- \OC::$server->getShareManager()->deleteShare($share);
- }
-
/**
* @medium
* test expire logic
@@ -430,106 +379,6 @@ public function testMoveFolder() {
}
- public function testMoveFolderIntoSharedFolderAsRecipient() {
-
- \OC\Files\Filesystem::mkdir('folder1');
-
- $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('folder1');
- $share = \OC::$server->getShareManager()->newShare();
- $share->setNode($node)
- ->setShareType(\OCP\Share::SHARE_TYPE_USER)
- ->setSharedBy(self::TEST_VERSIONS_USER)
- ->setSharedWith(self::TEST_VERSIONS_USER2)
- ->setPermissions(\OCP\Constants::PERMISSION_ALL);
- $share = \OC::$server->getShareManager()->createShare($share);
-
- self::loginHelper(self::TEST_VERSIONS_USER2);
- $versionsFolder2 = '/' . self::TEST_VERSIONS_USER2 . '/files_versions';
- \OC\Files\Filesystem::mkdir('folder2');
- \OC\Files\Filesystem::file_put_contents('folder2/test.txt', 'test file');
-
- $t1 = time();
- // second version is two weeks older, this way we make sure that no
- // version will be expired
- $t2 = $t1 - 60 * 60 * 24 * 14;
-
- $this->rootView->mkdir($versionsFolder2);
- $this->rootView->mkdir($versionsFolder2 . '/folder2');
- // create some versions
- $v1 = $versionsFolder2 . '/folder2/test.txt.v' . $t1;
- $v2 = $versionsFolder2 . '/folder2/test.txt.v' . $t2;
-
- $this->rootView->file_put_contents($v1, 'version1');
- $this->rootView->file_put_contents($v2, 'version2');
-
- // move file into the shared folder as recipient
- \OC\Files\Filesystem::rename('/folder2', '/folder1/folder2');
-
- $this->assertFalse($this->rootView->file_exists($v1));
- $this->assertFalse($this->rootView->file_exists($v2));
-
- self::loginHelper(self::TEST_VERSIONS_USER);
-
- $versionsFolder1 = '/' . self::TEST_VERSIONS_USER . '/files_versions';
-
- $v1Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t1;
- $v2Renamed = $versionsFolder1 . '/folder1/folder2/test.txt.v' . $t2;
-
- $this->assertTrue($this->rootView->file_exists($v1Renamed));
- $this->assertTrue($this->rootView->file_exists($v2Renamed));
-
- \OC::$server->getShareManager()->deleteShare($share);
- }
-
- public function testRenameSharedFile() {
-
- \OC\Files\Filesystem::file_put_contents("test.txt", "test file");
-
- $t1 = time();
- // second version is two weeks older, this way we make sure that no
- // version will be expired
- $t2 = $t1 - 60 * 60 * 24 * 14;
-
- $this->rootView->mkdir(self::USERS_VERSIONS_ROOT);
- // create some versions
- $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1;
- $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2;
- // the renamed versions should not exist! Because we only moved the mount point!
- $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1;
- $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2;
-
- $this->rootView->file_put_contents($v1, 'version1');
- $this->rootView->file_put_contents($v2, 'version2');
-
- $node = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER)->get('test.txt');
- $share = \OC::$server->getShareManager()->newShare();
- $share->setNode($node)
- ->setShareType(\OCP\Share::SHARE_TYPE_USER)
- ->setSharedBy(self::TEST_VERSIONS_USER)
- ->setSharedWith(self::TEST_VERSIONS_USER2)
- ->setPermissions(\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_SHARE);
- $share = \OC::$server->getShareManager()->createShare($share);
-
- self::loginHelper(self::TEST_VERSIONS_USER2);
-
- $this->assertTrue(\OC\Files\Filesystem::file_exists('test.txt'));
-
- // execute rename hook of versions app
- \OC\Files\Filesystem::rename('test.txt', 'test2.txt');
-
- self::loginHelper(self::TEST_VERSIONS_USER);
-
- $this->runCommands();
-
- $this->assertTrue($this->rootView->file_exists($v1));
- $this->assertTrue($this->rootView->file_exists($v2));
-
- $this->assertFalse($this->rootView->file_exists($v1Renamed));
- $this->assertFalse($this->rootView->file_exists($v2Renamed));
-
- \OC::$server->getShareManager()->deleteShare($share);
- }
-
public function testCopy() {
\OC\Files\Filesystem::file_put_contents("test.txt", "test file");
@@ -639,43 +488,9 @@ public function testRestoreCrossStorage() {
$this->doTestRestore();
}
- public function testRestoreNoPermission() {
- $this->loginAsUser(self::TEST_VERSIONS_USER);
-
- $userHome = \OC::$server->getUserFolder(self::TEST_VERSIONS_USER);
- $node = $userHome->newFolder('folder');
- $file = $node->newFile('test.txt');
-
- $share = \OC::$server->getShareManager()->newShare();
- $share->setNode($node)
- ->setShareType(\OCP\Share::SHARE_TYPE_USER)
- ->setSharedBy(self::TEST_VERSIONS_USER)
- ->setSharedWith(self::TEST_VERSIONS_USER2)
- ->setPermissions(\OCP\Constants::PERMISSION_READ);
- $share = \OC::$server->getShareManager()->createShare($share);
-
- $versions = $this->createAndCheckVersions(
- \OC\Files\Filesystem::getView(),
- 'folder/test.txt'
- );
-
- $file->putContent('test file');
-
- $this->loginAsUser(self::TEST_VERSIONS_USER2);
-
- $firstVersion = current($versions);
-
- $this->assertFalse(\OCA\Files_Versions\Storage::rollback('folder/test.txt', $firstVersion['version']), 'Revert did not happen');
-
- $this->loginAsUser(self::TEST_VERSIONS_USER);
-
- \OC::$server->getShareManager()->deleteShare($share);
- $this->assertEquals('test file', $file->getContent(), 'File content has not changed');
- }
-
/**
* @param string $hookName name of hook called
- * @param string $params variable to receive parameters provided by hook
+ * @param array $params variable to receive parameters provided by hook
*/
private function connectMockHooks($hookName, &$params) {
if ($hookName === null) {
@@ -733,14 +548,15 @@ private function doTestRestore() {
$params = [];
$this->connectMockHooks('rollback', $params);
- $this->assertTrue(\OCA\Files_Versions\Storage::rollback('sub/test.txt', $t2));
+ $v = $oldVersions["$t2#test.txt"];
+ $this->assertTrue(\OCA\Files_Versions\Storage::restoreVersion(self::TEST_VERSIONS_USER, $v['path'], $v['storage_location'], $t2));
$expectedParams = [
'path' => '/sub/test.txt',
+ 'user' => self::TEST_VERSIONS_USER,
+ 'revision' => $t2
];
- $this->assertEquals($expectedParams['path'], $params['path']);
- $this->assertTrue(array_key_exists('revision', $params));
- $this->assertTrue($params['revision'] > 0);
+ $this->assertEquals($expectedParams, $params);
$this->assertEquals('version2', $this->rootView->file_get_contents($filePath));
$info2 = $this->rootView->getFileInfo($filePath);
diff --git a/apps/files_versions/tests/js/versioncollectionSpec.js b/apps/files_versions/tests/js/versioncollectionSpec.js
deleted file mode 100644
index 87065fa1d361..000000000000
--- a/apps/files_versions/tests/js/versioncollectionSpec.js
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (c) 2015
- *
- * This file is licensed under the Affero General Public License version 3
- * or later.
- *
- * See the COPYING-README file.
- *
- */
-describe('OCA.Versions.VersionCollection', function() {
- var VersionCollection = OCA.Versions.VersionCollection;
- var collection, fileInfoModel;
-
- beforeEach(function() {
- fileInfoModel = new OCA.Files.FileInfoModel({
- path: '/subdir',
- name: 'some file.txt'
- });
- collection = new VersionCollection();
- collection.setFileInfo(fileInfoModel);
- });
- it('fetches the next page', function() {
- collection.fetchNext();
-
- expect(fakeServer.requests.length).toEqual(1);
- expect(fakeServer.requests[0].url).toEqual(
- OC.generateUrl('apps/files_versions/ajax/getVersions.php') +
- '?source=%2Fsubdir%2Fsome%20file.txt&start=0'
- );
- fakeServer.requests[0].respond(
- 200,
- { 'Content-Type': 'application/json' },
- JSON.stringify({
- status: 'success',
- data: {
- endReached: false,
- versions: [{
- version: 10000000,
- size: 123,
- name: 'some file.txt',
- fullPath: '/subdir/some file.txt'
- },{
- version: 15000000,
- size: 150,
- name: 'some file.txt',
- path: '/subdir/some file.txt'
- }]
- }
- })
- );
-
- expect(collection.length).toEqual(2);
- expect(collection.hasMoreResults()).toEqual(true);
-
- collection.fetchNext();
-
- expect(fakeServer.requests.length).toEqual(2);
- expect(fakeServer.requests[1].url).toEqual(
- OC.generateUrl('apps/files_versions/ajax/getVersions.php') +
- '?source=%2Fsubdir%2Fsome%20file.txt&start=2'
- );
- fakeServer.requests[1].respond(
- 200,
- { 'Content-Type': 'application/json' },
- JSON.stringify({
- status: 'success',
- data: {
- endReached: true,
- versions: [{
- version: 18000000,
- size: 123,
- name: 'some file.txt',
- path: '/subdir/some file.txt'
- }]
- }
- })
- );
-
- expect(collection.length).toEqual(3);
- expect(collection.hasMoreResults()).toEqual(false);
-
- collection.fetchNext();
-
- // no further requests
- expect(fakeServer.requests.length).toEqual(2);
- });
- it('properly parses the results', function() {
- collection.fetchNext();
-
- expect(fakeServer.requests.length).toEqual(1);
- expect(fakeServer.requests[0].url).toEqual(
- OC.generateUrl('apps/files_versions/ajax/getVersions.php') +
- '?source=%2Fsubdir%2Fsome%20file.txt&start=0'
- );
- fakeServer.requests[0].respond(
- 200,
- { 'Content-Type': 'application/json' },
- JSON.stringify({
- status: 'success',
- data: {
- endReached: false,
- versions: [{
- version: 10000000,
- size: 123,
- name: 'some file.txt',
- path: '/subdir/some file.txt'
- },{
- version: 15000000,
- size: 150,
- name: 'some file.txt',
- path: '/subdir/some file.txt'
- }]
- }
- })
- );
-
- expect(collection.length).toEqual(2);
-
- var model = collection.at(0);
- expect(model.get('id')).toEqual(10000000);
- expect(model.get('timestamp')).toEqual(10000000);
- expect(model.get('name')).toEqual('some file.txt');
- expect(model.get('fullPath')).toEqual('/subdir/some file.txt');
- expect(model.get('size')).toEqual(123);
-
- model = collection.at(1);
- expect(model.get('id')).toEqual(15000000);
- expect(model.get('timestamp')).toEqual(15000000);
- expect(model.get('name')).toEqual('some file.txt');
- expect(model.get('fullPath')).toEqual('/subdir/some file.txt');
- expect(model.get('size')).toEqual(150);
- });
- it('resets page counted when setting a new file info model', function() {
- collection.fetchNext();
-
- expect(fakeServer.requests.length).toEqual(1);
- fakeServer.requests[0].respond(
- 200,
- { 'Content-Type': 'application/json' },
- JSON.stringify({
- status: 'success',
- data: {
- endReached: true,
- versions: [{
- version: 18000000,
- size: 123,
- name: 'some file.txt',
- path: '/subdir/some file.txt'
- }]
- }
- })
- );
-
- expect(collection.hasMoreResults()).toEqual(false);
-
- collection.setFileInfo(fileInfoModel);
-
- expect(collection.hasMoreResults()).toEqual(true);
- });
-});
-
diff --git a/apps/files_versions/tests/js/versionmodelSpec.js b/apps/files_versions/tests/js/versionmodelSpec.js
index 0f1c06581d54..3aeaecab7cec 100644
--- a/apps/files_versions/tests/js/versionmodelSpec.js
+++ b/apps/files_versions/tests/js/versionmodelSpec.js
@@ -7,18 +7,32 @@
* See the COPYING-README file.
*
*/
+
+/* global dav */
describe('OCA.Versions.VersionModel', function() {
var VersionModel = OCA.Versions.VersionModel;
var model;
+ var requestStub;
+ var requestDeferred;
+
beforeEach(function() {
model = new VersionModel({
id: 10000000,
+ fileId: 10000000,
timestamp: 10000000,
fullPath: '/subdir/some file.txt',
name: 'some file.txt',
- size: 150
+ size: 150,
+ versionId: 123456789
});
+ OC.currentUser = 'user0';
+
+ requestDeferred = new $.Deferred();
+ requestStub = sinon.stub(dav.Client.prototype, 'request').returns(requestDeferred.promise());
+ });
+ afterEach(function() {
+ requestStub.restore();
});
it('returns the full path', function() {
@@ -32,8 +46,8 @@ describe('OCA.Versions.VersionModel', function() {
});
it('returns the download url', function() {
expect(model.getDownloadUrl())
- .toEqual(OC.generateUrl('/apps/files_versions/download.php') +
- '?file=%2Fsubdir%2Fsome%20file.txt&revision=10000000'
+ .toEqual(
+ OC.linkToRemoteBase('dav') + '/meta/10000000/v/123456789'
);
});
describe('reverting', function() {
@@ -49,27 +63,27 @@ describe('OCA.Versions.VersionModel', function() {
model.on('revert', revertEventStub);
model.on('error', errorStub);
});
+
it('tells the server to revert when calling the revert method', function() {
model.revert({
success: successStub
});
- expect(fakeServer.requests.length).toEqual(1);
- expect(fakeServer.requests[0].url)
- .toEqual(
- OC.generateUrl('/apps/files_versions/ajax/rollbackVersion.php') +
- '?file=%2Fsubdir%2Fsome+file.txt&revision=10000000'
- );
-
- fakeServer.requests[0].respond(
- 200,
- { 'Content-Type': 'application/json' },
- JSON.stringify({
- status: 'success',
- })
+ expect(requestStub.calledOnce).toEqual(true);
+ expect(requestStub.getCall(0).args[0]).toEqual('COPY');
+ expect(requestStub.getCall(0).args[1]).toEqual(
+ '/owncloud/remote.php/dav/meta/10000000/v/123456789'
+ );
+ expect(requestStub.getCall(0).args[2]['Destination']).toEqual(
+ OC.TestUtil.buildAbsoluteUrl('/owncloud/remote.php/dav/files/user0/subdir/some%20file.txt')
);
- expect(revertEventStub.calledOnce).toEqual(true);
+ requestDeferred.resolve({
+ status: 204,
+ body: ''
+ });
+
+ expect(revertEventStub.called).toEqual(true);
expect(successStub.calledOnce).toEqual(true);
expect(errorStub.notCalled).toEqual(true);
});
@@ -78,14 +92,12 @@ describe('OCA.Versions.VersionModel', function() {
success: successStub
});
- expect(fakeServer.requests.length).toEqual(1);
- fakeServer.requests[0].respond(
- 200,
- { 'Content-Type': 'application/json' },
- JSON.stringify({
- status: 'error',
- })
- );
+ expect(requestStub.calledOnce).toEqual(true);
+
+ requestDeferred.resolve({
+ status: 400,
+ body: ''
+ });
expect(revertEventStub.notCalled).toEqual(true);
expect(successStub.notCalled).toEqual(true);
diff --git a/apps/files_versions/tests/js/versionstabviewSpec.js b/apps/files_versions/tests/js/versionstabviewSpec.js
index 1c9c02de67d7..2e4bbb693c73 100644
--- a/apps/files_versions/tests/js/versionstabviewSpec.js
+++ b/apps/files_versions/tests/js/versionstabviewSpec.js
@@ -113,64 +113,6 @@ describe('OCA.Versions.VersionsTabView', function() {
expect($item.find('.preview').attr('src')).toEqual(version2.getPreviewUrl());
});
});
-
- describe('More versions', function() {
- var hasMoreResultsStub;
-
- beforeEach(function() {
- tabView.setFileInfo(fileInfoModel);
- fetchStub.reset();
- tabView.collection.set(testVersions);
- hasMoreResultsStub = sinon.stub(VersionCollection.prototype, 'hasMoreResults');
- });
- afterEach(function() {
- hasMoreResultsStub.restore();
- });
-
- it('shows "More versions" button when more versions are available', function() {
- hasMoreResultsStub.returns(true);
- tabView.collection.trigger('sync');
-
- expect(tabView.$el.find('.showMoreVersions').hasClass('hidden')).toEqual(false);
- });
- it('does not show "More versions" button when more versions are available', function() {
- hasMoreResultsStub.returns(false);
- tabView.collection.trigger('sync');
-
- expect(tabView.$el.find('.showMoreVersions').hasClass('hidden')).toEqual(true);
- });
- it('fetches and appends the next page when clicking the "More" button', function() {
- hasMoreResultsStub.returns(true);
-
- expect(fetchStub.notCalled).toEqual(true);
-
- tabView.$el.find('.showMoreVersions').click();
-
- expect(fetchStub.calledOnce).toEqual(true);
- });
- it('appends version to the list when added to collection', function() {
- var time3 = Date.UTC(2015, 6, 10, 1, 0, 0, 0) / 1000;
-
- var version3 = new VersionModel({
- id: time3,
- timestamp: time3,
- name: 'some file.txt',
- size: 54,
- fullPath: '/subdir/some file.txt'
- });
-
- tabView.collection.add(version3);
-
- expect(tabView.$el.find('.versions>li').length).toEqual(3);
-
- var $item = tabView.$el.find('.versions>li').eq(2);
- expect($item.find('.downloadVersion').attr('href')).toEqual(version3.getDownloadUrl());
- expect($item.find('.versiondate').text()).toEqual('7 days ago');
- expect($item.find('.revertVersion').length).toEqual(1);
- expect($item.find('.preview').attr('src')).toEqual(version3.getPreviewUrl());
- });
- });
-
describe('Reverting', function() {
var revertStub;
@@ -179,7 +121,7 @@ describe('OCA.Versions.VersionsTabView', function() {
tabView.setFileInfo(fileInfoModel);
tabView.collection.set(testVersions);
});
-
+
afterEach(function() {
revertStub.restore();
});
diff --git a/core/js/files/client.js b/core/js/files/client.js
index 81c58863ede8..222f5cadf55a 100644
--- a/core/js/files/client.js
+++ b/core/js/files/client.js
@@ -410,6 +410,9 @@
*/
_getSabreException: function(response) {
var result = {};
+ if (!response.body) {
+ return result;
+ }
var xml = response.xhr.responseXML;
var messages = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'message');
var exceptions = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'exception');
@@ -704,6 +707,49 @@
return promise;
},
+ _moveOrCopy: function(operation, path, destinationPath, allowOverwrite, headers, options) {
+ if (!path) {
+ throw 'Missing argument "path"';
+ }
+ if (!destinationPath) {
+ throw 'Missing argument "destinationPath"';
+ }
+ if (operation !== 'MOVE' && operation !== 'COPY') {
+ throw 'Invalid operation';
+ }
+
+ var self = this;
+ var deferred = $.Deferred();
+ var promise = deferred.promise();
+ options = _.extend({
+ 'pathIsUrl' : false,
+ 'destinationPathIsUrl' : false
+ }, options);
+ headers = _.extend({}, headers, {
+ 'Destination' : options.destinationPathIsUrl ? destinationPath : this._buildUrl(destinationPath)
+ });
+
+ if (!allowOverwrite) {
+ headers['Overwrite'] = 'F';
+ }
+
+ this._client.request(
+ operation,
+ options.pathIsUrl ? path : this._buildUrl(path),
+ headers
+ ).then(
+ function(result) {
+ if (self._isSuccessStatus(result.status)) {
+ deferred.resolve(result.status);
+ } else {
+ result = _.extend(result, self._getSabreException(result));
+ deferred.reject(result.status, result);
+ }
+ }
+ );
+ return promise;
+ },
+
/**
* Creates a directory
*
@@ -737,40 +783,24 @@
*
* @return {Promise} promise
*/
- move: function(path, destinationPath, allowOverwrite, headers) {
- if (!path) {
- throw 'Missing argument "path"';
- }
- if (!destinationPath) {
- throw 'Missing argument "destinationPath"';
- }
-
- var self = this;
- var deferred = $.Deferred();
- var promise = deferred.promise();
- headers = _.extend({}, headers, {
- 'Destination' : this._buildUrl(destinationPath)
- });
-
- if (!allowOverwrite) {
- headers['Overwrite'] = 'F';
- }
+ move: function(path, destinationPath, allowOverwrite, headers, options) {
+ return this._moveOrCopy('MOVE', path, destinationPath, allowOverwrite, headers, options);
+ },
- this._client.request(
- 'MOVE',
- this._buildUrl(path),
- headers
- ).then(
- function(result) {
- if (self._isSuccessStatus(result.status)) {
- deferred.resolve(result.status);
- } else {
- result = _.extend(result, self._getSabreException(result));
- deferred.reject(result.status, result);
- }
- }
- );
- return promise;
+ /**
+ * Copies path to another path
+ *
+ * @param {String} path path to copy
+ * @param {String} destinationPath destination path
+ * @param {boolean} [allowOverwrite=false] true to allow overwriting,
+ * false otherwise
+ * @param {Object} [headers=null] additional headers
+ *
+ * @return {Promise} promise
+ * @since 10.1.0
+ */
+ copy: function(path, destinationPath, allowOverwrite, headers, options) {
+ return this._moveOrCopy('COPY', path, destinationPath, allowOverwrite, headers, options);
},
/**
diff --git a/lib/private/Files/Meta/MetaFileVersionNode.php b/lib/private/Files/Meta/MetaFileVersionNode.php
index 56cd033838a4..8aa245baefff 100644
--- a/lib/private/Files/Meta/MetaFileVersionNode.php
+++ b/lib/private/Files/Meta/MetaFileVersionNode.php
@@ -25,9 +25,10 @@
use OC\Files\Node\AbstractFile;
use OC\Files\Node\File;
+use OCP\Files\ForbiddenException;
+use OCP\Files\IProvidesAdditionalHeaders;
use OCP\Files\IRootFolder;
use OCP\Files\Storage\IVersionedStorage;
-use OCP\Files\NotPermittedException;
use OCP\Files\Storage;
/**
@@ -36,7 +37,7 @@
*
* @package OC\Files\Meta
*/
-class MetaFileVersionNode extends AbstractFile {
+class MetaFileVersionNode extends AbstractFile implements IProvidesAdditionalHeaders {
/** @var string */
private $versionId;
@@ -102,12 +103,15 @@ public function getContent() {
public function copy($targetPath) {
$target = $this->root->get($targetPath);
if ($target instanceof File && $target->getId() === $this->parent->getId()) {
+ if (!$target->isUpdateable()) {
+ throw new ForbiddenException("Cannot write to $targetPath", false);
+ }
$this->storage->restoreVersion($this->internalPath, $this->versionId);
- return;
+ return true;
}
// for now we only allow restoring of a version
- throw new NotPermittedException();
+ return false;
}
public function getMTime() {
@@ -125,4 +129,19 @@ public function getEtag() {
public function fopen($mode) {
return $this->storage->getContentOfVersion($this->internalPath, $this->versionId);
}
+
+ /**
+ * @return array
+ */
+ public function getHeaders() {
+ return [];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getContentDispositionFileName() {
+ return basename($this->internalPath);
+ }
+
}
diff --git a/lib/private/Files/Meta/MetaVersionCollection.php b/lib/private/Files/Meta/MetaVersionCollection.php
index 8e81b1dfbdef..6b042643db84 100644
--- a/lib/private/Files/Meta/MetaVersionCollection.php
+++ b/lib/private/Files/Meta/MetaVersionCollection.php
@@ -79,11 +79,9 @@ public function getDirectoryListing() {
if (!$storage->instanceOfStorage(IVersionedStorage::class)) {
return [];
}
- $mimeType = $view->getMimeType($path);
/** @var IVersionedStorage | Storage $storage */
$versions = $storage->getVersions($internalPath);
- return array_map(function($version) use ($storage, $internalPath, $mimeType) {
- $version['mime-type'] = isset($version['mime-type']) ? $version['mime-type'] : $mimeType;
+ return array_map(function($version) use ($storage, $internalPath) {
return new MetaFileVersionNode($this, $this->root, $version, $storage, $internalPath);
}, $versions);
}
diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php
index c14fa71f6858..ebca69b0c969 100644
--- a/lib/private/Files/Storage/Common.php
+++ b/lib/private/Files/Storage/Common.php
@@ -694,8 +694,11 @@ public function getVersions($internalPath) {
}
list ($uid, $filename) = $this->convertInternalPathToGlobalPath($internalPath);
- return array_values(
- \OCA\Files_Versions\Storage::getVersions($uid, $filename));
+ return array_map(function ($version) use ($internalPath) {
+ $version['mime-type'] = $this->getMimeType($internalPath);
+ return $version;
+ }, array_values(
+ \OCA\Files_Versions\Storage::getVersions($uid, $filename)));
}
/**
@@ -730,9 +733,9 @@ public function getContentOfVersion($internalPath, $versionId) {
public function restoreVersion($internalPath, $versionId) {
// KISS implementation
if (!\OC_App::isEnabled('files_versions')) {
- return;
+ return false;
}
- list ($uid, $filename) = $this->convertInternalPathToGlobalPath($internalPath);
- \OCA\Files_Versions\Storage::rollback($filename, $versionId);
+ $v = $this->getVersion($internalPath, $versionId);
+ return \OCA\Files_Versions\Storage::restoreVersion($v['owner'], $v['path'], $v['storage_location'], $versionId);
}
}
diff --git a/lib/private/Preview.php b/lib/private/Preview.php
index ae9f547a5fd9..feeb7cd5b970 100644
--- a/lib/private/Preview.php
+++ b/lib/private/Preview.php
@@ -1334,8 +1334,9 @@ public static function post_delete_versions($args) {
*/
public static function post_delete($args, $prefix = '') {
$path = Files\Filesystem::normalizePath($args['path']);
+ $user = isset($args['user']) ? $args['user'] : \OC_User::getUser();
- $preview = new Preview(\OC_User::getUser(), $prefix, $path);
+ $preview = new Preview($user, $prefix, $path);
$preview->deleteAllPreviews();
}
diff --git a/lib/public/Files/IProvidesAdditionalHeaders.php b/lib/public/Files/IProvidesAdditionalHeaders.php
new file mode 100644
index 000000000000..8253a9fa3f14
--- /dev/null
+++ b/lib/public/Files/IProvidesAdditionalHeaders.php
@@ -0,0 +1,49 @@
+
+ *
+ * @copyright Copyright (c) 2017, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+
+namespace OCP\Files;
+
+
+/**
+ * Interface IProvidesAdditionalHeaders
+ * This interface allows to add additional headers to the response
+ *
+ * @package OCP\Files
+ * @since 10.0.5
+ */
+interface IProvidesAdditionalHeaders {
+
+ /**
+ * Returns an array of headers.
+ *
+ * @return array
+ * @since 10.0.5
+ */
+ public function getHeaders();
+
+ /**
+ * Returns the file name which is to be used for the content disposition
+ * @return string
+ * @since 10.0.5
+ */
+ public function getContentDispositionFileName();
+}
diff --git a/tests/lib/Files/MetaFilesTest.php b/tests/lib/Files/MetaFilesTest.php
index 2f02c78642d8..651736b4e427 100644
--- a/tests/lib/Files/MetaFilesTest.php
+++ b/tests/lib/Files/MetaFilesTest.php
@@ -27,6 +27,7 @@
use OC\Files\Meta\MetaFileVersionNode;
use OC\Files\Meta\MetaRootNode;
use OC\Files\Meta\MetaVersionCollection;
+use OC\Files\Node\File;
use OC\Files\View;
use OCA\Files_Versions\Hooks;
use OCP\Files\Folder;
@@ -57,7 +58,8 @@ public function testMetaInNodeAPI() {
$this->loginAsUser($userId);
// create file
- $fileName = "$userId/files/" . $this->getUniqueID('file') . '.txt';
+ $file = $this->getUniqueID('file') . '.txt';
+ $fileName = "$userId/files/$file";
$view = new View();
$view->file_put_contents($fileName, '1234');
$info = $view->getFileInfo($fileName);
@@ -86,13 +88,23 @@ public function testMetaInNodeAPI() {
$this->assertInstanceOf(MetaFileVersionNode::class, $children[0]);
$versionId = $children[0]->getName();
+ /** @var MetaFileVersionNode $metaNodeOfFile */
$metaNodeOfFile = \OC::$server->getRootFolder()->get("meta/{$info->getId()}/v/$versionId");
$this->assertInstanceOf(MetaFileVersionNode::class, $metaNodeOfFile);
$this->assertEquals($versionId, $metaNodeOfFile->getName());
+ $this->assertEquals(4, $metaNodeOfFile->getSize());
+ $this->assertEquals([], $metaNodeOfFile->getHeaders());
+ $this->assertEquals($file, $metaNodeOfFile->getContentDispositionFileName());
+ $this->assertEquals('text/plain', $metaNodeOfFile->getMimetype());
+ $this->assertEquals($info->getMTime(), $metaNodeOfFile->getMTime());
+ $this->assertTrue(is_string($metaNodeOfFile->getMTime()));
+ $this->assertTrue(strlen($metaNodeOfFile->getMTime()) > 0);
+
/** @var MetaFileVersionNode $metaNodeOfFile */
$this->assertEquals('1234', $metaNodeOfFile->getContent());
// restore a version using move
+ /** @var File $target */
$target = \OC::$server->getRootFolder()->get($fileName);
$this->assertEquals('1234567890', $target->getContent());
$metaNodeOfFile->copy($fileName);