Add version collection to DAV

listed as
versions/<username>/versions/<file id>/<timastamp>

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
This commit is contained in:
Roeland Jago Douma 2018-04-30 20:39:45 +02:00
parent c8cb42fd26
commit d40afac1b9
No known key found for this signature in database
GPG Key ID: F941078878347C0C
9 changed files with 449 additions and 1 deletions

View File

@ -8,7 +8,7 @@
This application automatically maintains older versions of files that are changed. When enabled, a hidden versions folder is provisioned in every users directory and is used to store old file versions. A user can revert to an older version through the web interface at any time, with the replaced file becoming a version. The app automatically manages the versions folder to ensure the user doesnt run out of Quota because of versions.
In addition to the expiry of versions, the versions app makes certain never to use more than 50% of the users currently available free space. If stored versions exceed this limit, the app will delete the oldest versions first until it meets this limit. More information is available in the Versions documentation.
</description>
<version>1.7.0</version>
<version>1.7.1</version>
<licence>agpl</licence>
<author>Frank Karlitschek</author>
<author>Bjoern Schiessle</author>
@ -16,6 +16,7 @@
<default_enable/>
<types>
<filesystem/>
<dav/>
</types>
<documentation>
<user>user-versions</user>
@ -34,4 +35,10 @@
<command>OCA\Files_Versions\Command\CleanUp</command>
<command>OCA\Files_Versions\Command\ExpireVersions</command>
</commands>
<sabre>
<collections>
<collection>OCA\Files_Versions\Sabre\RootCollection</collection>
</collections>
</sabre>
</info>

View File

@ -16,5 +16,10 @@ return array(
'OCA\\Files_Versions\\Events\\CreateVersionEvent' => $baseDir . '/../lib/Events/CreateVersionEvent.php',
'OCA\\Files_Versions\\Expiration' => $baseDir . '/../lib/Expiration.php',
'OCA\\Files_Versions\\Hooks' => $baseDir . '/../lib/Hooks.php',
'OCA\\Files_Versions\\Sabre\\RootCollection' => $baseDir . '/../lib/Sabre/RootCollection.php',
'OCA\\Files_Versions\\Sabre\\VersionCollection' => $baseDir . '/../lib/Sabre/VersionCollection.php',
'OCA\\Files_Versions\\Sabre\\VersionFile' => $baseDir . '/../lib/Sabre/VersionFile.php',
'OCA\\Files_Versions\\Sabre\\VersionHome' => $baseDir . '/../lib/Sabre/VersionHome.php',
'OCA\\Files_Versions\\Sabre\\VersionRoot' => $baseDir . '/../lib/Sabre/VersionRoot.php',
'OCA\\Files_Versions\\Storage' => $baseDir . '/../lib/Storage.php',
);

View File

@ -31,6 +31,11 @@ class ComposerStaticInitFiles_Versions
'OCA\\Files_Versions\\Events\\CreateVersionEvent' => __DIR__ . '/..' . '/../lib/Events/CreateVersionEvent.php',
'OCA\\Files_Versions\\Expiration' => __DIR__ . '/..' . '/../lib/Expiration.php',
'OCA\\Files_Versions\\Hooks' => __DIR__ . '/..' . '/../lib/Hooks.php',
'OCA\\Files_Versions\\Sabre\\RootCollection' => __DIR__ . '/..' . '/../lib/Sabre/RootCollection.php',
'OCA\\Files_Versions\\Sabre\\VersionCollection' => __DIR__ . '/..' . '/../lib/Sabre/VersionCollection.php',
'OCA\\Files_Versions\\Sabre\\VersionFile' => __DIR__ . '/..' . '/../lib/Sabre/VersionFile.php',
'OCA\\Files_Versions\\Sabre\\VersionHome' => __DIR__ . '/..' . '/../lib/Sabre/VersionHome.php',
'OCA\\Files_Versions\\Sabre\\VersionRoot' => __DIR__ . '/..' . '/../lib/Sabre/VersionRoot.php',
'OCA\\Files_Versions\\Storage' => __DIR__ . '/..' . '/../lib/Storage.php',
);

View File

@ -23,6 +23,7 @@
namespace OCA\Files_Versions\AppInfo;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\AppFramework\App;
use OCA\Files_Versions\Expiration;
use OCP\AppFramework\Utility\ITimeFactory;
@ -48,5 +49,17 @@ class Application extends App {
$c->query(ITimeFactory::class)
);
});
/*
* Register $principalBackend for the DAV collection
*/
$container->registerService('principalBackend', function () {
return new Principal(
\OC::$server->getUserManager(),
\OC::$server->getGroupManager(),
\OC::$server->getShareManager(),
\OC::$server->getUserSession()
);
});
}
}

View File

@ -0,0 +1,65 @@
<?php
/**
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Versions\Sabre;
use OCP\Files\IRootFolder;
use Sabre\DAV\INode;
use Sabre\DAVACL\AbstractPrincipalCollection;
use Sabre\DAVACL\PrincipalBackend;
class RootCollection extends AbstractPrincipalCollection {
/** @var IRootFolder */
private $rootFolder;
public function __construct(PrincipalBackend\BackendInterface $principalBackend,
IRootFolder $rootFolder) {
parent::__construct($principalBackend, 'principals/users');
$this->rootFolder = $rootFolder;
}
/**
* This method returns a node for a principal.
*
* The passed array contains principal information, and is guaranteed to
* at least contain a uri item. Other properties may or may not be
* supplied by the authentication backend.
*
* @param array $principalInfo
* @return INode
*/
public function getChildForPrincipal(array $principalInfo) {
list(,$name) = \Sabre\Uri\split($principalInfo['uri']);
$user = \OC::$server->getUserSession()->getUser();
if (is_null($user) || $name !== $user->getUID()) {
throw new \Sabre\DAV\Exception\Forbidden();
}
return new VersionHome($principalInfo, $this->rootFolder);
}
public function getName() {
return 'versions';
}
}

View File

@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
/**
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Versions\Sabre;
use OCA\Files_Versions\Storage;
use OCP\Files\File;
use OCP\Files\Folder;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
class VersionCollection implements ICollection {
/** @var Folder */
private $userFolder;
/** @var File */
private $file;
/** @var string */
private $userId;
public function __construct(Folder $userFolder, File $file, string $userId) {
$this->userFolder = $userFolder;
$this->file = $file;
$this->userId = $userId;
}
public function createFile($name, $data = null) {
throw new Forbidden();
}
public function createDirectory($name) {
throw new Forbidden();
}
public function getChild($name) {
throw new NotFound();
}
public function getChildren(): array {
$versions = Storage::getVersions($this->userId, $this->userFolder->getRelativePath($this->file->getPath()));
return array_map(function (array $data) {
return new VersionFile($data);
}, $versions);
}
public function childExists($name): bool {
try {
$this->getChild($name);
return true;
} catch (NotFound $e) {
return false;
}
}
public function delete() {
throw new Forbidden();
}
public function getName(): string {
return (string)$this->file->getId();
}
public function setName($name) {
throw new Forbidden();
}
public function getLastModified(): int {
return 0;
}
}

View File

@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
/**
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Versions\Sabre;
use OCP\Files\FileInfo;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\IFile;
class VersionFile implements IFile {
/** @var array */
private $data;
public function __construct(array $data) {
$this->data = $data;
}
public function put($data) {
throw new Forbidden();
}
public function get() {
throw new Forbidden();
}
public function getContentType(): string {
return $this->data['mimetype'];
}
public function getETag(): string {
return $this->data['version'];
}
public function getSize(): int {
return $this->data['size'];
}
public function delete() {
throw new Forbidden();
}
public function getName(): string {
return $this->data['version'];
}
public function setName($name) {
throw new Forbidden();
}
public function getLastModified(): int {
return (int)$this->data['version'];
}
}

View File

@ -0,0 +1,86 @@
<?php
/**
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Versions\Sabre;
use OCP\Files\IRootFolder;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\ICollection;
class VersionHome implements ICollection {
/** @var array */
private $principalInfo;
/** @var IRootFolder */
private $rootFolder;
public function __construct(array $principalInfo, IRootFolder $rootFolder) {
$this->principalInfo = $principalInfo;
$this->rootFolder = $rootFolder;
}
public function delete() {
throw new Forbidden();
}
public function getName(): string {
list(,$name) = \Sabre\Uri\split($this->principalInfo['uri']);
return $name;
}
public function setName($name) {
throw new Forbidden();
}
public function createFile($name, $data = null) {
throw new Forbidden();
}
public function createDirectory($name) {
throw new Forbidden();
}
public function getChild($name) {
list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']);
if ($name === 'versions') {
return new VersionRoot($userId, $this->rootFolder);
}
}
public function getChildren() {
list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']);
return [
new VersionRoot($userId, $this->rootFolder),
];
}
public function childExists($name) {
return $name === 'versions' || $name === 'restore';
}
public function getLastModified() {
return 0;
}
}

View File

@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
/**
* @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Files_Versions\Sabre;
use OCP\Files\File;
use OCP\Files\IRootFolder;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
class VersionRoot implements ICollection {
/** @var string */
private $userId;
/** @var IRootFolder */
private $rootFolder;
public function __construct(string $userId, IRootFolder $rootFolder) {
$this->userId = $userId;
$this->rootFolder = $rootFolder;
}
public function delete() {
throw new Forbidden();
}
public function getName(): string {
return 'versions';
}
public function setName($name) {
throw new Forbidden();
}
public function createFile($name, $data = null) {
throw new Forbidden();
}
public function createDirectory($name) {
throw new Forbidden();
}
public function getChild($name) {
$userFolder = $this->rootFolder->getUserFolder($this->userId);
$fileId = (int)$name;
$nodes = $userFolder->getById($fileId);
if ($nodes === []) {
throw new NotFound();
}
$node = array_pop($nodes);
if (!$node instanceof File) {
throw new NotFound();
}
return new VersionCollection($userFolder, $node, $this->userId);
}
public function getChildren(): array {
return [];
}
public function childExists($name): bool {
try {
$this->getChild($name);
return true;
} catch (NotFound $e) {
return false;
}
}
public function getLastModified(): int {
return 0;
}
}