Skip to content

Commit

Permalink
Feature/version metadata (#39126)
Browse files Browse the repository at this point in the history
Feature/version metadata
  • Loading branch information
pushnov-i authored Nov 18, 2021
1 parent 6a55f75 commit 49110b5
Show file tree
Hide file tree
Showing 12 changed files with 507 additions and 59 deletions.
24 changes: 24 additions & 0 deletions apps/dav/lib/Meta/MetaFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use OCA\DAV\Files\ICopySource;
use OCA\DAV\Files\IProvidesAdditionalHeaders;
use OCA\DAV\Files\IFileNode;
use OCP\Files\IProvidesVersionAuthor;
use OCP\Files\Node;
use Sabre\DAV\File;

Expand Down Expand Up @@ -126,4 +127,27 @@ public function getContentDispositionFileName() {
public function getNode() {
return $this->file;
}

/**
* @return string
*/
public function getVersionAuthor() : string {
if ($this->file instanceof IProvidesVersionAuthor) {
return $this->file->getEditedBy();
}
return '';
}

/**
* @return string
*/
public function getVersionAuthorName() : string {
if ($this->file instanceof IProvidesVersionAuthor) {
$uid = $this->file->getEditedBy();
$manager = \OC::$server->getUserManager();
$user = $manager->get($uid);
return $user !== null ? $user->getDisplayName() : '';
}
return '';
}
}
9 changes: 9 additions & 0 deletions apps/dav/lib/Meta/MetaPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class MetaPlugin extends ServerPlugin {
public const NS_OWNCLOUD = 'http://owncloud.org/ns';
public const PATH_FOR_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}meta-path-for-user';

public const VERSION_EDITED_BY_PROPERTYNAME = '{http://owncloud.org/ns}meta-version-edited-by';
public const VERSION_EDITED_BY_PROPERTYNAME_NAME = '{http://owncloud.org/ns}meta-version-edited-by-name';
/**
* Reference to main server object
*
Expand Down Expand Up @@ -97,6 +99,13 @@ public function handleGetProperties(PropFind $propFind, INode $node) {
$file = \current($files);
return $baseFolder->getRelativePath($file->getPath());
});
} elseif ($node instanceof MetaFile) {
$propFind->handle(self::VERSION_EDITED_BY_PROPERTYNAME, function () use ($node) {
return $node->getVersionAuthor();
});
$propFind->handle(self::VERSION_EDITED_BY_PROPERTYNAME_NAME, function () use ($node) {
return $node->getVersionAuthorName();
});
}
}
}
16 changes: 16 additions & 0 deletions apps/files_trashbin/lib/Trashbin.php
Original file line number Diff line number Diff line change
Expand Up @@ -400,19 +400,35 @@ private static function retainVersions($filename, $owner, $ownerPath, $timestamp
$rootView = new View('/');

if ($rootView->is_dir($owner . '/files_versions/' . $ownerPath)) {
$metadataFileExists = $rootView->file_exists($owner . '/files_versions/' . $ownerPath . '.json');

if ($owner !== $user || $forceCopy) {
self::copy_recursive($owner . '/files_versions/' . $ownerPath, $owner . '/files_trashbin/versions/' . \basename($ownerPath) . '.d' . $timestamp, $rootView);
if ($metadataFileExists) {
self::copy_recursive($owner . '/files_versions/' . $ownerPath . '.json', $owner . '/files_trashbin/versions/' . \basename($ownerPath) . '.json' . '.d' . $timestamp, $rootView);
}
}
if (!$forceCopy) {
self::move($rootView, $owner . '/files_versions/' . $ownerPath, $user . '/files_trashbin/versions/' . $filename . '.d' . $timestamp);
if ($metadataFileExists) {
self::move($rootView, $owner . '/files_versions/' . $ownerPath . '.json', $user . '/files_trashbin/versions/' . $filename . '.json' . '.d' . $timestamp);
}
}
} elseif ($versions = \OCA\Files_Versions\Storage::getVersions($owner, $ownerPath)) {
foreach ($versions as $v) {
$metaVersionExists = $rootView->file_exists($owner . '/files_versions' . $v['path'] . '.v' . $v['version'] . '.json');

if ($owner !== $user || $forceCopy) {
self::copy($rootView, $owner . '/files_versions' . $v['path'] . '.v' . $v['version'], $owner . '/files_trashbin/versions/' . $v['name'] . '.v' . $v['version'] . '.d' . $timestamp);
if ($metaVersionExists) {
self::move($rootView, $owner . '/files_versions' . $v['path'] . '.v' . $v['version'] . '.json', $owner . '/files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.json' . '.d' . $timestamp);
}
}
if (!$forceCopy) {
self::move($rootView, $owner . '/files_versions' . $v['path'] . '.v' . $v['version'], $user . '/files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.d' . $timestamp);
if ($metaVersionExists) {
self::move($rootView, $owner . '/files_versions' . $v['path'] . '.v' . $v['version'] . '.json', $user . '/files_trashbin/versions/' . $filename . '.v' . $v['version'] . '.json' . '.d' . $timestamp);
}
}
}
}
Expand Down
26 changes: 23 additions & 3 deletions apps/files_versions/js/versioncollection.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,29 @@
/* global moment */

(function() {

_.extend(OC.Files.Client, {
PROPERTY_FILEID: '{' + OC.Files.Client.NS_OWNCLOUD + '}id',
PROPERTY_VERSION_EDITED_BY: '{' + OC.Files.Client.NS_OWNCLOUD + '}meta-version-edited-by',
PROPERTY_VERSION_EDITED_BY_NAME: '{' + OC.Files.Client.NS_OWNCLOUD + '}meta-version-edited-by-name',
});

/**
* @memberof OCA.Versions
*/
var VersionCollection = OC.Backbone.Collection.extend({
sync: OC.Backbone.davSync,

davProperties: {
'meta-version-edited-by': OC.Files.Client.PROPERTY_VERSION_EDITED_BY,
'meta-version-edited-by-name': OC.Files.Client.PROPERTY_VERSION_EDITED_BY_NAME,
'id': OC.Files.Client.PROPERTY_FILEID,
'getlastmodified': OC.Files.Client.PROPERTY_GETLASTMODIFIED,
'getcontentlength': OC.Files.Client.PROPERTY_GETCONTENTLENGTH,
'resourcetype': OC.Files.Client.PROPERTY_RESOURCETYPE,
'getcontenttype': OC.Files.Client.PROPERTY_GETCONTENTTYPE,
},

model: OCA.Versions.VersionModel,

/**
Expand Down Expand Up @@ -46,9 +63,12 @@
id: revision,
name: revision,
fullPath: fullPath,
timestamp: moment(new Date(version['{DAV:}getlastmodified'])).format('X'),
size: version['{DAV:}getcontentlength'],
mimetype: version['{DAV:}getcontenttype'],
timestamp: moment(new Date(version.getlastmodified)).format('X'),
relativeTimestamp: moment(new Date(version.getlastmodified)).fromNow(),
size: version.getcontentlength,
mimetype: version.getcontenttype,
editedBy: version['meta-version-edited-by'],
editedByName: version['meta-version-edited-by-name'],
fileId: fileId
};
});
Expand Down
5 changes: 4 additions & 1 deletion apps/files_versions/js/versionstabview.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
'{{#hasDetails}}' +
'<div class="version-details">' +
'<span class="size has-tooltip" title="{{altSize}}">{{humanReadableSize}}</span>' +
'<span title="{{editedBy}}">{{editedByName}}</span>' +
'</div>' +
'{{/hasDetails}}' +
'</div>' +
Expand Down Expand Up @@ -213,7 +214,9 @@
revertIconUrl: OC.imagePath('core', 'actions/history'),
previewUrl: getPreviewUrl(version),
revertLabel: t('files_versions', 'Restore'),
canRevert: (this.collection.getFileInfo().get('permissions') & OC.PERMISSION_UPDATE) !== 0
canRevert: (this.collection.getFileInfo().get('permissions') & OC.PERMISSION_UPDATE) !== 0,
editedBy: version.has('editedBy'),
editedByName: version.has('editedByName')
}, version.attributes);
},

Expand Down
1 change: 1 addition & 0 deletions apps/files_versions/lib/Hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Hooks {
public static function connectHooks() {
// Listen to write signals
\OCP\Util::connectHook('OC_Filesystem', 'write', 'OCA\Files_Versions\Hooks', 'write_hook');

// Listen to delete and rename signals
\OCP\Util::connectHook('OC_Filesystem', 'post_delete', 'OCA\Files_Versions\Hooks', 'remove_hook');
\OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Files_Versions\Hooks', 'pre_remove_hook');
Expand Down
64 changes: 59 additions & 5 deletions apps/files_versions/lib/Storage.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@

use OC\Files\Filesystem;
use OC\Files\View;
use OC\Share\Constants;
use OCA\DAV\Meta\MetaPlugin;
use OCA\Files_Versions\AppInfo\Application;
use OCA\Files_Versions\Command\Expire;
use OCP\Files\NotFoundException;
Expand Down Expand Up @@ -192,13 +194,26 @@ public static function store($filename) {
// store a new version of a file
$mtime = $users_view->filemtime('files/' . $filename);
$sourceFileInfo = $users_view->getFileInfo("files/$filename");
if ($users_view->copy("files/$filename", "files_versions/$filename.v$mtime")) {

$versionFileName = "files_versions/$filename.v$mtime";
if ($users_view->copy("files/$filename", $versionFileName)) {
// call getFileInfo to enforce a file cache entry for the new version
$users_view->getFileInfo("files_versions/$filename.v$mtime");
$users_view->getFileInfo($versionFileName);
// update checksum of the version
$users_view->putFileInfo("files_versions/$filename.v$mtime", [
$users_view->putFileInfo($versionFileName, [
'checksum' => $sourceFileInfo->getChecksum(),
]);

$config = \OC::$server->getConfig();
if ($config->getSystemValue('file_storage.save_version_author', false) === true) {
$user = \OC::$server->getUserSession()->getUser();
if ($user !== null && !$users_view->file_exists($versionFileName . '.json')) {
$metaDataKey = MetaPlugin::VERSION_EDITED_BY_PROPERTYNAME;
$metadata = [$metaDataKey => $user->getUID()];
$metadataJsonObject = \json_encode($metadata);
$users_view->file_put_contents($versionFileName . '.json', $metadataJsonObject);
}
}
}
}
}
Expand Down Expand Up @@ -254,6 +269,9 @@ public static function delete($path) {
];
\OC_Hook::emit('\OCP\Versions', 'preDelete', $hookData);
self::deleteVersion($view, $filename . '.v' . $v['version']);
if ($view->file_exists($path . ".json")) {
$view->unlink($path . ".json");
}
\OC_Hook::emit('\OCP\Versions', 'delete', $hookData);
}
}
Expand Down Expand Up @@ -310,6 +328,14 @@ public static function renameOrCopy($sourcePath, $targetPath, $operation) {
'/' . $sourceOwner . '/files_versions/' . $sourcePath.'.v' . $v['version'],
'/' . $targetOwner . '/files_versions/' . $targetPath.'.v'.$v['version']
);
// move each version json file that holds the name of the user that've made an edit
$sourceMetaDataFile = '/' . $sourceOwner . '/files_versions/' . $sourcePath . '.v' . $v['version'] . '.json';
if ($rootView->file_exists($sourceMetaDataFile)) {
$rootView->$operation(
$sourceMetaDataFile,
'/' . $targetOwner . '/files_versions/' . $targetPath . '.v' . $v['version'] . '.json'
);
}
}
}

Expand All @@ -328,13 +354,21 @@ public static function restoreVersion($uid, $filename, $fileToRestore, $revision
return false;
}

$metaDataEnabled = \OC::$server->getConfig()->getSystemValue('file_storage.save_version_author', false);
$versionCreated = false;

//first create a new version
//first create a new version and metadata if enabled
$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));
$users_view->copy('files'.$filename, $version);
$versionCreated = true;

if ($metaDataEnabled === true) {
$metaTargetPath = $version . '.json';
$metaDataKey = MetaPlugin::VERSION_EDITED_BY_PROPERTYNAME;
$metadataJsonObject = \json_encode([$metaDataKey => $uid]);
$users_view->file_put_contents($metaTargetPath, $metadataJsonObject);
}
}

// Restore encrypted version of the old file for the newly restored file
Expand All @@ -356,11 +390,20 @@ public static function restoreVersion($uid, $filename, $fileToRestore, $revision
if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) {
$users_view->touch("/files$filename", $revision);
Storage::scheduleExpire($uid, $filename);

if ($metaDataEnabled && $users_view->file_exists($fileToRestore . '.json')) {
$users_view->unlink($fileToRestore . '.json');
list($storage, $internalPath) = $users_view->resolvePath($fileToRestore . '.json');
$cache = $storage->getCache($internalPath);
$cache->remove($internalPath);
}

\OC_Hook::emit('\OCP\Versions', 'rollback', [
'path' => $filename,
'user' => $uid,
'revision' => $revision,
]);

return true;
} elseif ($versionCreated) {
self::deleteVersion($users_view, $version);
Expand Down Expand Up @@ -456,6 +499,16 @@ public static function getVersions($uid, $filename) {
$versions[$key]['etag'] = $view->getETag($dir . '/' . $entryName);
$versions[$key]['storage_location'] = "$dir/$entryName";
$versions[$key]['owner'] = $uid;

$jsonMetadataFile = $dir . '/' . $entryName . '.json';
if ($view->file_exists($jsonMetadataFile)) {
$metaDataFileContents = $view->file_get_contents($jsonMetadataFile);
if ($decoded = \json_decode($metaDataFileContents, true)) {
if (isset($decoded[MetaPlugin::VERSION_EDITED_BY_PROPERTYNAME])) {
$versions[$key]['edited_by'] = $decoded[MetaPlugin::VERSION_EDITED_BY_PROPERTYNAME];
}
}
}
}
}
}
Expand Down Expand Up @@ -561,6 +614,7 @@ protected static function getExpireList($time, $versions, $quotaExceeded = false

/**
* get list of files we want to expire
*
* @param array $versions list of versions
* @param integer $time
* @return array containing the list of to deleted versions and the size of them
Expand Down
Loading

0 comments on commit 49110b5

Please sign in to comment.