Skip to content

Commit

Permalink
Add versioning to objectstore
Browse files Browse the repository at this point in the history
  • Loading branch information
DeepDiver1975 authored and patrickjahns committed Jun 11, 2018
1 parent f2bec85 commit 1ead426
Show file tree
Hide file tree
Showing 18 changed files with 273 additions and 29 deletions.
17 changes: 17 additions & 0 deletions apps/files_trashbin/tests/StorageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@

namespace OCA\Files_Trashbin\Tests;

use OC\Files\ObjectStore\ObjectStoreStorage;
use OC\Files\Filesystem;
use OC\Files\Storage\Temporary;
use OC\Files\View;
use OCP\Files\Storage;
use Test\TestCase;
use Test\Traits\UserTrait;

Expand Down Expand Up @@ -299,6 +301,7 @@ public function testDeleteFolderAsReadOnlyRecipient() {
* Test that deleted versions properly land in the trashbin.
*/
public function testDeleteVersionsOfFile() {
$this->markTestSkippedIfStorageHasOwnVersioning();
\OCA\Files_Versions\Hooks::connectHooks();

// trigger a version (multiple would not work because of the expire logic)
Expand Down Expand Up @@ -328,6 +331,7 @@ public function testDeleteVersionsOfFile() {
* Test that deleted versions properly land in the trashbin.
*/
public function testDeleteVersionsOfFolder() {
$this->markTestSkippedIfStorageHasOwnVersioning();
\OCA\Files_Versions\Hooks::connectHooks();

// trigger a version (multiple would not work because of the expire logic)
Expand Down Expand Up @@ -363,6 +367,7 @@ public function testDeleteVersionsOfFolder() {
* Test that deleted versions properly land in the trashbin when deleting as share recipient.
*/
public function testDeleteVersionsOfFileAsRecipient() {
$this->markTestSkippedIfStorageHasOwnVersioning();
\OCA\Files_Versions\Hooks::connectHooks();

$this->userView->mkdir('share');
Expand Down Expand Up @@ -415,6 +420,7 @@ public function testDeleteVersionsOfFileAsRecipient() {
* Test that deleted versions properly land in the trashbin when deleting as share recipient.
*/
public function testDeleteVersionsOfFolderAsRecipient() {
$this->markTestSkippedIfStorageHasOwnVersioning();
\OCA\Files_Versions\Hooks::connectHooks();

$this->userView->mkdir('share');
Expand Down Expand Up @@ -483,6 +489,7 @@ public function testDeleteVersionsOfFolderAsRecipient() {
* unlink() which should NOT trigger the version deletion logic.
*/
public function testKeepFileAndVersionsWhenMovingFileBetweenStorages() {
$this->markTestSkippedIfStorageHasOwnVersioning();
\OCA\Files_Versions\Hooks::connectHooks();

$storage2 = new Temporary([]);
Expand Down Expand Up @@ -524,6 +531,7 @@ public function testKeepFileAndVersionsWhenMovingFileBetweenStorages() {
* unlink() which should NOT trigger the version deletion logic.
*/
public function testKeepFileAndVersionsWhenMovingFolderBetweenStorages() {
$this->markTestSkippedIfStorageHasOwnVersioning();
\OCA\Files_Versions\Hooks::connectHooks();

$storage2 = new Temporary([]);
Expand Down Expand Up @@ -564,6 +572,7 @@ public function testKeepFileAndVersionsWhenMovingFolderBetweenStorages() {
* out of the shared folder
*/
public function testOwnerBackupWhenMovingFileOutOfShare() {
$this->markTestSkippedIfStorageHasOwnVersioning();
\OCA\Files_Versions\Hooks::connectHooks();

$this->userView->mkdir('share');
Expand Down Expand Up @@ -746,4 +755,12 @@ public function testSingleStorageDeleteFileLoggedOut() {
$this->userView->unlink('test.txt');
}
}

private function markTestSkippedIfStorageHasOwnVersioning() {
/** @var Storage $storage */
list($storage, $internalPath) = $this->userView->resolvePath('folder/inside.txt');
if ($storage->instanceOfStorage(ObjectStoreStorage::class)) {
$this->markTestSkipped();
}
}
}
8 changes: 3 additions & 5 deletions apps/files_versions/js/versioncollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,17 @@
parse: function(result) {
var fullPath = this._fileInfo.getFullPath();
var fileId = this._fileInfo.get('id');
var results = _.map(result, function(version) {
var revision = parseInt(version.id, 10);
return _.map(result, function(version) {
var revision = version.id;
return {
id: revision,
name: revision,
fullPath: fullPath,
timestamp: revision,
versionId: revision,
timestamp: moment(new Date(version['{DAV:}getlastmodified'])).format('X'),
size: version['{DAV:}getcontentlength'],
fileId: fileId
};
});
return results;
}
});

Expand Down
2 changes: 1 addition & 1 deletion apps/files_versions/js/versionmodel.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
getDownloadUrl: function() {
return OC.linkToRemoteBase('dav') + '/meta/' +
encodeURIComponent(this.get('fileId')) + '/v/' +
encodeURIComponent(this.get('versionId'));
encodeURIComponent(this.get('id'));
}
});

Expand Down
3 changes: 2 additions & 1 deletion apps/files_versions/js/versionstabview.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

(function() {
var TEMPLATE_ITEM =
'<li data-revision="{{timestamp}}">' +
'<li data-revision="{{versionId}}">' +
'<div>' +
'<div class="preview-container">' +
'<img class="preview" src="{{previewUrl}}"/>' +
Expand Down Expand Up @@ -186,6 +186,7 @@
var timestamp = version.get('timestamp') * 1000;
var size = version.has('size') ? version.get('size') : 0;
return _.extend({
versionId: version.get('id'),
formattedTimestamp: OC.Util.formatDate(timestamp),
relativeTimestamp: OC.Util.relativeModifiedDate(timestamp),
humanReadableSize: OC.Util.humanFileSize(size, true),
Expand Down
10 changes: 10 additions & 0 deletions apps/files_versions/lib/Storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
use OCA\Files_Versions\AppInfo\Application;
use OCA\Files_Versions\Command\Expire;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IVersionedStorage;
use OCP\Lock\ILockingProvider;
use OCP\User;

Expand Down Expand Up @@ -174,7 +175,16 @@ public static function store($filename) {
}

list($uid, $filename) = self::getUidAndFilename($filename);
/** @var \OCP\Files\Storage\IStorage $storage */
list($storage, $internalPath) = Filesystem::resolvePath($filename);
if ($storage->instanceOfStorage(IVersionedStorage::class)) {
/** @var IVersionedStorage $storage */
if ($storage->saveVersion($internalPath)) {
return true;
}
}

// fallback implementation below - need to go into class Common
$files_view = new View('/'.$uid .'/files');
$users_view = new View('/'.$uid);

Expand Down
11 changes: 11 additions & 0 deletions apps/files_versions/tests/VersioningTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@

require_once __DIR__ . '/../appinfo/app.php';

use OC\Files\ObjectStore\ObjectStoreStorage;
use OC\Files\Storage\Temporary;
use OCP\Files\Storage;
use Test\TestCase;

/**
Expand Down Expand Up @@ -873,6 +875,8 @@ public function testStoreVersionAsAnonymous() {
* @param string $path
*/
private function createAndCheckVersions(\OC\Files\View $view, $path) {
$this->markTestSkippedIfStorageHasOwnVersioning();

$view->file_put_contents($path, 'test file');
$view->file_put_contents($path, 'version 1');
$view->file_put_contents($path, 'version 2');
Expand Down Expand Up @@ -918,6 +922,13 @@ public static function loginHelper($user, $create = false) {
\OC::$server->getUserFolder($user);
}

private function markTestSkippedIfStorageHasOwnVersioning() {
/** @var Storage $storage */
list($storage, $internalPath) = $this->rootView->resolvePath(self::USERS_VERSIONS_ROOT);
if ($storage->instanceOfStorage(ObjectStoreStorage::class)) {
$this->markTestSkipped();
}
}
}

// extend the original class to make it possible to test protected methods
Expand Down
3 changes: 1 addition & 2 deletions apps/files_versions/tests/js/versionmodelSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ describe('OCA.Versions.VersionModel', function() {

beforeEach(function() {
model = new VersionModel({
id: 10000000,
id: 123456789,
fileId: 10000000,
timestamp: 10000000,
fullPath: '/subdir/some file.txt',
name: 'some file.txt',
size: 150,
versionId: 123456789
});
currentUserStub = sinon.stub(OC, 'getCurrentUser').returns({uid: 'user0'});

Expand Down
2 changes: 1 addition & 1 deletion core/js/files/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,7 @@
* @param {Object} [headers=null] additional headers
*
* @return {Promise} promise
* @since 10.1.0
* @since 10.0.5
*/
copy: function(path, destinationPath, allowOverwrite, headers, options) {
return this._moveOrCopy('COPY', path, destinationPath, allowOverwrite, headers, options);
Expand Down
5 changes: 2 additions & 3 deletions lib/private/Files/Meta/MetaFileVersionNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ public function copy($targetPath) {
if (!$target->isUpdateable()) {
throw new ForbiddenException("Cannot write to $targetPath", false);
}
$this->storage->restoreVersion($this->internalPath, $this->versionId);
return true;
return $this->storage->restoreVersion($this->internalPath, $this->versionId);
}

// for now we only allow restoring of a version
Expand All @@ -119,7 +118,7 @@ public function getMTime() {
}

public function getMimetype() {
return isset($this->versionInfo['mime-type']) ? $this->versionInfo['mime-type'] : 'application/octet-stream';
return isset($this->versionInfo['mimetype']) ? $this->versionInfo['mimetype'] : 'application/octet-stream';
}

public function getEtag() {
Expand Down
2 changes: 1 addition & 1 deletion lib/private/Files/Meta/MetaRootNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public function isCreatable() {
* @inheritdoc
*/
public function getPath() {
return $this->getName();
return '/meta';
}

/**
Expand Down
15 changes: 13 additions & 2 deletions lib/private/Files/Meta/MetaVersionCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,13 @@ public function getDirectoryListing() {
}
/** @var IVersionedStorage | Storage $storage */
$versions = $storage->getVersions($internalPath);
return array_map(function($version) use ($storage, $internalPath) {
return array_values(array_map(function($version) use ($storage, $internalPath, $view, $path) {
if (!isset($version['mimetype'])) {
$version['mimetype'] = $view->getMimeType($path);
}

return new MetaFileVersionNode($this, $this->root, $version, $storage, $internalPath);
}, $versions);
}, $versions));
}

/**
Expand All @@ -107,6 +111,9 @@ public function get($path) {
if ($version === null) {
throw new NotFoundException();
}
if (!isset($version['mimetype'])) {
$version['mimetype'] = $view->getMimeType($path);
}
return new MetaFileVersionNode($this, $this->root, $version, $storage, $internalPath);
}

Expand All @@ -120,4 +127,8 @@ public function getId() {
public function getName() {
return "v";
}

public function getPath() {
return "/meta/{$this->fileId}/v";
}
}
62 changes: 62 additions & 0 deletions lib/private/Files/ObjectStore/ObjectStoreStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@

use Icewind\Streams\IteratorDirectory;
use OC\Files\Cache\CacheEntry;
use OCP\Files\NotFoundException;
use OCP\Files\ObjectStore\IObjectStore;
use OCP\Files\ObjectStore\IVersionedObjectStorage;

class ObjectStoreStorage extends \OC\Files\Storage\Common {

Expand Down Expand Up @@ -428,4 +430,64 @@ public function writeBack($tmpFile) {
public function hasUpdated($path, $time) {
return false;
}

public function saveVersion($internalPath) {
if ($this->objectStore instanceof IVersionedObjectStorage) {
$stat = $this->stat($internalPath);
// There are cases in the current implementation where saveVersion
// is called before the file was even written.
// There is nothing to be done in this case.
// We return true to not trigger the fallback implementation
if ($stat === false) {
return true;
}
return $this->objectStore->saveVersion($this->getURN($stat['fileid']));
}
return parent::saveVersion($internalPath);
}

public function getVersions($internalPath) {
if ($this->objectStore instanceof IVersionedObjectStorage) {
$stat = $this->stat($internalPath);
if ($stat === false) {
throw new NotFoundException();
}
return $this->objectStore->getVersions($this->getURN($stat['fileid']));
}
return parent::getVersions($internalPath);
}

public function getVersion($internalPath, $versionId) {
if ($this->objectStore instanceof IVersionedObjectStorage) {
$stat = $this->stat($internalPath);
if ($stat === false) {
throw new NotFoundException();
}
return $this->objectStore->getVersion($this->getURN($stat['fileid']), $versionId);
}
return parent::getVersion($internalPath, $versionId);
}

public function getContentOfVersion($internalPath, $versionId) {
if ($this->objectStore instanceof IVersionedObjectStorage) {
$stat = $this->stat($internalPath);
if ($stat === false) {
throw new NotFoundException();
}
return $this->objectStore->getContentOfVersion($this->getURN($stat['fileid']), $versionId);
}
return parent::getContentOfVersion($internalPath, $versionId);
}

public function restoreVersion($internalPath, $versionId) {
if ($this->objectStore instanceof IVersionedObjectStorage) {
$stat = $this->stat($internalPath);
if ($stat === false) {
throw new NotFoundException();
}
return $this->objectStore->restoreVersion($this->getURN($stat['fileid']), $versionId);
}
return parent::restoreVersion($internalPath, $versionId);
}

}
7 changes: 6 additions & 1 deletion lib/private/Files/Storage/Common.php
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ public function getVersions($internalPath) {
list ($uid, $filename) = $this->convertInternalPathToGlobalPath($internalPath);

return array_map(function ($version) use ($internalPath) {
$version['mime-type'] = $this->getMimeType($internalPath);
$version['mimetype'] = $this->getMimeType($internalPath);
return $version;
}, array_values(
\OCA\Files_Versions\Storage::getVersions($uid, $filename)));
Expand Down Expand Up @@ -739,4 +739,9 @@ public function restoreVersion($internalPath, $versionId) {
$v = $this->getVersion($internalPath, $versionId);
return \OCA\Files_Versions\Storage::restoreVersion($v['owner'], $v['path'], $v['storage_location'], $versionId);
}

public function saveVersion($internalPath) {
// returning false here will trigger the fallback implementation
return false;
}
}
Loading

0 comments on commit 1ead426

Please sign in to comment.