summaryrefslogtreecommitdiffstats
path: root/apps/files_versions/lib
diff options
context:
space:
mode:
authorLouis Chemineau <louis@chmn.me>2022-11-29 14:52:02 +0100
committerLouis (Rebase PR Action) <artonge@users.noreply.github.com>2023-01-26 10:12:23 +0000
commit629de6c8c97f9a455944046737421b927b1e2d9b (patch)
tree25dc4a6e6dd5a339d3207a8c8fb325ecb71c1cbf /apps/files_versions/lib
parente82bfba114aa291fe6bbe1de3488c685d12489a1 (diff)
downloadnextcloud-server-629de6c8c97f9a455944046737421b927b1e2d9b.tar.gz
nextcloud-server-629de6c8c97f9a455944046737421b927b1e2d9b.zip
Support getting and patching version-label
Signed-off-by: Louis Chemineau <louis@chmn.me>
Diffstat (limited to 'apps/files_versions/lib')
-rw-r--r--apps/files_versions/lib/AppInfo/Application.php4
-rw-r--r--apps/files_versions/lib/Capabilities.php19
-rw-r--r--apps/files_versions/lib/Db/VersionEntity.php11
-rw-r--r--apps/files_versions/lib/Hooks.php49
-rw-r--r--apps/files_versions/lib/Sabre/Plugin.php30
-rw-r--r--apps/files_versions/lib/Sabre/VersionFile.php20
-rw-r--r--apps/files_versions/lib/Versions/INameableVersion.php37
-rw-r--r--apps/files_versions/lib/Versions/INameableVersionBackend.php36
-rw-r--r--apps/files_versions/lib/Versions/LegacyVersionsBackend.php102
-rw-r--r--apps/files_versions/lib/Versions/Version.php12
-rw-r--r--apps/files_versions/lib/Versions/VersionManager.php9
11 files changed, 294 insertions, 35 deletions
diff --git a/apps/files_versions/lib/AppInfo/Application.php b/apps/files_versions/lib/AppInfo/Application.php
index a6eab420742..08c49f280af 100644
--- a/apps/files_versions/lib/AppInfo/Application.php
+++ b/apps/files_versions/lib/AppInfo/Application.php
@@ -47,10 +47,11 @@ use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Files\Events\Node\BeforeNodeCopiedEvent;
use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
-use OCP\Files\Events\Node\BeforeNodeWrittenEvent;
use OCP\Files\Events\Node\NodeCopiedEvent;
use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Files\Events\Node\NodeRenamedEvent;
+use OCP\Files\Events\Node\BeforeNodeWrittenEvent;
+use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IServerContainer;
@@ -105,6 +106,7 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(LoadSidebar::class, LoadSidebarListener::class);
$context->registerEventListener(BeforeNodeWrittenEvent::class, Hooks::class);
+ $context->registerEventListener(NodeWrittenEvent::class, Hooks::class);
$context->registerEventListener(BeforeNodeDeletedEvent::class, Hooks::class);
$context->registerEventListener(NodeDeletedEvent::class, Hooks::class);
$context->registerEventListener(NodeRenamedEvent::class, Hooks::class);
diff --git a/apps/files_versions/lib/Capabilities.php b/apps/files_versions/lib/Capabilities.php
index b8602540ec8..031cabb83ec 100644
--- a/apps/files_versions/lib/Capabilities.php
+++ b/apps/files_versions/lib/Capabilities.php
@@ -24,19 +24,34 @@
*/
namespace OCA\Files_Versions;
+use OCP\App\IAppManager;
use OCP\Capabilities\ICapability;
+use OCP\IConfig;
class Capabilities implements ICapability {
-
+ private IConfig $config;
+ private IAppManager $appManager;
+
+ public function __construct(
+ IConfig $config,
+ IAppManager $appManager
+ ) {
+ $this->config = $config;
+ $this->appManager = $appManager;
+ }
+
/**
* Return this classes capabilities
*
* @return array
*/
public function getCapabilities() {
+ $groupFolderOrS3VersioningInstalled = $this->appManager->isInstalled('groupfolders') || !$this->appManager->isInstalled('groupfolders');
+
return [
'files' => [
- 'versioning' => true
+ 'versioning' => true,
+ 'version_labeling' => !$groupFolderOrS3VersioningInstalled && $this->config->getSystemValueBool('enable_version_labeling', true),
]
];
}
diff --git a/apps/files_versions/lib/Db/VersionEntity.php b/apps/files_versions/lib/Db/VersionEntity.php
index 5ef94215dd2..d5adbcfa104 100644
--- a/apps/files_versions/lib/Db/VersionEntity.php
+++ b/apps/files_versions/lib/Db/VersionEntity.php
@@ -59,7 +59,7 @@ class VersionEntity extends Entity implements JsonSerializable {
$this->addType('metadata', Types::JSON);
}
- public function jsonSerialize() {
+ public function jsonSerialize(): array {
return [
'id' => $this->id,
'file_id' => $this->fileId,
@@ -69,4 +69,13 @@ class VersionEntity extends Entity implements JsonSerializable {
'metadata' => $this->metadata,
];
}
+
+ public function getLabel(): string {
+ return $this->metadata['label'] ?? '';
+ }
+
+ public function setLabel(string $label): void {
+ $this->metadata['label'] = $label;
+ $this->markFieldUpdated('metadata');
+ }
} \ No newline at end of file
diff --git a/apps/files_versions/lib/Hooks.php b/apps/files_versions/lib/Hooks.php
index d4408190ea3..ebb0974c4ed 100644
--- a/apps/files_versions/lib/Hooks.php
+++ b/apps/files_versions/lib/Hooks.php
@@ -32,6 +32,8 @@ namespace OCA\Files_Versions;
use OC\Files\Filesystem;
use OC\Files\Mount\MoveableMount;
use OC\Files\View;
+use OCA\Files_Versions\Db\VersionEntity;
+use OCA\Files_Versions\Db\VersionsMapper;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Events\Node\BeforeNodeCopiedEvent;
@@ -41,16 +43,28 @@ use OCP\Files\Events\Node\BeforeNodeWrittenEvent;
use OCP\Files\Events\Node\NodeCopiedEvent;
use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Files\Events\Node\NodeRenamedEvent;
+use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\Files\Folder;
+use OCP\Files\IMimeTypeLoader;
use OCP\Files\Node;
class Hooks implements IEventListener {
- public Folder $userFolder;
+ private Folder $userFolder;
+ private VersionsMapper $versionsMapper;
+ /**
+ * @var array<int, bool>
+ */
+ private array $versionsCreated = [];
+ private IMimeTypeLoader $mimeTypeLoader;
public function __construct(
- Folder $userFolder
+ Folder $userFolder,
+ VersionsMapper $versionsMapper,
+ IMimeTypeLoader $mimeTypeLoader
) {
$this->userFolder = $userFolder;
+ $this->versionsMapper = $versionsMapper;
+ $this->mimeTypeLoader = $mimeTypeLoader;
}
public function handle(Event $event): void {
@@ -58,6 +72,10 @@ class Hooks implements IEventListener {
$this->write_hook($event->getNode());
}
+ if ($event instanceof NodeWrittenEvent) {
+ $this->post_write_hook($event->getNode());
+ }
+
if ($event instanceof BeforeNodeDeletedEvent) {
$this->pre_remove_hook($event->getNode());
}
@@ -88,9 +106,34 @@ class Hooks implements IEventListener {
*/
public function write_hook(Node $node): void {
$path = $this->userFolder->getRelativePath($node->getPath());
- Storage::store($path);
+ $result = Storage::store($path);
+
+ if ($result === false) {
+ return;
+ }
+
+ // Store the result of the version creation so it can be used in post_write_hook.
+ $this->versionsCreated[$node->getId()] = true;
}
+ /**
+ * listen to post_write event.
+ */
+ public function post_write_hook(Node $node): void {
+ if (!array_key_exists($node->getId(), $this->versionsCreated)) {
+ return;
+ }
+
+ unset($this->versionsCreated[$node->getId()]);
+
+ $versionEntity = new VersionEntity();
+ $versionEntity->setFileId($node->getId());
+ $versionEntity->setTimestamp($node->getMTime());
+ $versionEntity->setSize($node->getSize());
+ $versionEntity->setMimetype($this->mimeTypeLoader->getId($node->getMimetype()));
+ $versionEntity->setMetadata([]);
+ $this->versionsMapper->insert($versionEntity);
+ }
/**
* Erase versions of deleted file
diff --git a/apps/files_versions/lib/Sabre/Plugin.php b/apps/files_versions/lib/Sabre/Plugin.php
index 5a127b4251d..4fd17194ba6 100644
--- a/apps/files_versions/lib/Sabre/Plugin.php
+++ b/apps/files_versions/lib/Sabre/Plugin.php
@@ -29,19 +29,23 @@ namespace OCA\Files_Versions\Sabre;
use OC\AppFramework\Http\Request;
use OCP\IRequest;
use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\INode;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\PropPatch;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
class Plugin extends ServerPlugin {
+ private Server $server;
+ private IRequest $request;
- /** @var Server */
- private $server;
- /** @var IRequest */
- private $request;
+ public const VERSION_LABEL = '{http://nextcloud.org/ns}version-label';
- public function __construct(IRequest $request) {
+ public function __construct(
+ IRequest $request
+ ) {
$this->request = $request;
}
@@ -49,6 +53,8 @@ class Plugin extends ServerPlugin {
$this->server = $server;
$server->on('afterMethod:GET', [$this, 'afterGet']);
+ $server->on('propFind', [$this, 'propFind']);
+ $server->on('propPatch', [$this, 'propPatch']);
}
public function afterGet(RequestInterface $request, ResponseInterface $response) {
@@ -81,4 +87,18 @@ class Plugin extends ServerPlugin {
. '; filename="' . rawurlencode($filename) . '"');
}
}
+
+ public function propFind(PropFind $propFind, INode $node): void {
+ if ($node instanceof VersionFile) {
+ $propFind->handle(self::VERSION_LABEL, fn() => $node->getLabel());
+ }
+ }
+
+ public function propPatch($path, PropPatch $propPatch): void {
+ $node = $this->server->tree->getNodeForPath($path);
+
+ if ($node instanceof VersionFile) {
+ $propPatch->handle(self::VERSION_LABEL, fn ($label) => $node->setLabel($label));
+ }
+ }
}
diff --git a/apps/files_versions/lib/Sabre/VersionFile.php b/apps/files_versions/lib/Sabre/VersionFile.php
index b7c7e6db1a6..9018f75703d 100644
--- a/apps/files_versions/lib/Sabre/VersionFile.php
+++ b/apps/files_versions/lib/Sabre/VersionFile.php
@@ -26,6 +26,8 @@ declare(strict_types=1);
*/
namespace OCA\Files_Versions\Sabre;
+use OCA\Files_Versions\Versions\INameableVersion;
+use OCA\Files_Versions\Versions\INameableVersionBackend;
use OCA\Files_Versions\Versions\IVersion;
use OCA\Files_Versions\Versions\IVersionManager;
use OCP\Files\NotFoundException;
@@ -70,6 +72,7 @@ class VersionFile implements IFile {
}
public function delete() {
+ // TODO: implement version deletion
throw new Forbidden();
}
@@ -81,6 +84,23 @@ class VersionFile implements IFile {
throw new Forbidden();
}
+ public function getLabel(): ?string {
+ if ($this->version instanceof INameableVersion) {
+ return $this->version->getLabel();
+ } else {
+ return null;
+ }
+ }
+
+ public function setLabel($label): bool {
+ if ($this->versionManager instanceof INameableVersionBackend) {
+ $this->versionManager->setVersionLabel($this->version, $label);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
public function getLastModified(): int {
return $this->version->getTimestamp();
}
diff --git a/apps/files_versions/lib/Versions/INameableVersion.php b/apps/files_versions/lib/Versions/INameableVersion.php
new file mode 100644
index 00000000000..b6ddb951e25
--- /dev/null
+++ b/apps/files_versions/lib/Versions/INameableVersion.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
+ *
+ * @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\Versions;
+
+/**
+ * @since 26.0.0
+ */
+interface INameableVersion {
+ /**
+ * Get the user created label
+ *
+ * @return string
+ * @since 26.0.0
+ */
+ public function getLabel(): string;
+}
diff --git a/apps/files_versions/lib/Versions/INameableVersionBackend.php b/apps/files_versions/lib/Versions/INameableVersionBackend.php
new file mode 100644
index 00000000000..4a8c094cf18
--- /dev/null
+++ b/apps/files_versions/lib/Versions/INameableVersionBackend.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2022 Louis Chmn <louis@chmn.me>
+ *
+ * @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\Versions;
+
+/**
+ * @since 26.0.0
+ */
+interface INameableVersionBackend {
+ /**
+ * Set the label for a version.
+ *
+ * @since 26.0.0
+ */
+ public function setVersionLabel(IVersion $version, string $label): void;
+}
diff --git a/apps/files_versions/lib/Versions/LegacyVersionsBackend.php b/apps/files_versions/lib/Versions/LegacyVersionsBackend.php
index 4ea0985e113..90c7cb930d5 100644
--- a/apps/files_versions/lib/Versions/LegacyVersionsBackend.php
+++ b/apps/files_versions/lib/Versions/LegacyVersionsBackend.php
@@ -28,25 +28,36 @@ namespace OCA\Files_Versions\Versions;
use OC\Files\View;
use OCA\Files_Sharing\SharedStorage;
+use OCA\Files_Versions\Db\VersionEntity;
+use OCA\Files_Versions\Db\VersionsMapper;
use OCA\Files_Versions\Storage;
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\Files\Folder;
+use OCP\Files\IMimeTypeLoader;
use OCP\Files\IRootFolder;
+use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
use OCP\IUserManager;
-class LegacyVersionsBackend implements IVersionBackend {
- /** @var IRootFolder */
- private $rootFolder;
- /** @var IUserManager */
- private $userManager;
-
- public function __construct(IRootFolder $rootFolder, IUserManager $userManager) {
+class LegacyVersionsBackend implements IVersionBackend, INameableVersionBackend, IDeletableVersionBackend {
+ private IRootFolder $rootFolder;
+ private IUserManager $userManager;
+ private VersionsMapper $versionsMapper;
+ private IMimeTypeLoader $mimeTypeLoader;
+
+ public function __construct(
+ IRootFolder $rootFolder,
+ IUserManager $userManager,
+ VersionsMapper $versionsMapper,
+ IMimeTypeLoader $mimeTypeLoader
+ ) {
$this->rootFolder = $rootFolder;
$this->userManager = $userManager;
+ $this->versionsMapper = $versionsMapper;
+ $this->mimeTypeLoader = $mimeTypeLoader;
}
public function useBackendForStorage(IStorage $storage): bool {
@@ -63,21 +74,60 @@ class LegacyVersionsBackend implements IVersionBackend {
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
$nodes = $userFolder->getById($file->getId());
$file2 = array_pop($nodes);
- $versions = Storage::getVersions($user->getUID(), $userFolder->getRelativePath($file2->getPath()));
-
- return array_map(function (array $data) use ($file, $user) {
- return new Version(
- (int)$data['version'],
- (int)$data['version'],
- $data['name'],
- (int)$data['size'],
- $data['mimetype'],
- $data['path'],
+
+ $versions = $this->getVersionsForFileFromDB($file2, $user);
+
+ if (count($versions) > 0) {
+ return $versions;
+ }
+
+ // Insert the entry in the DB for the current version.
+ if ($file2->getSize() > 0) {
+ $versionEntity = new VersionEntity();
+ $versionEntity->setFileId($file2->getId());
+ $versionEntity->setTimestamp($file2->getMTime());
+ $versionEntity->setSize($file2->getSize());
+ $versionEntity->setMimetype($this->mimeTypeLoader->getId($file2->getMimetype()));
+ $versionEntity->setMetadata([]);
+ $this->versionsMapper->insert($versionEntity);
+ }
+
+ // Insert entries in the DB for existing versions.
+ $versionsOnFS = Storage::getVersions($user->getUID(), $userFolder->getRelativePath($file2->getPath()));
+ foreach ($versionsOnFS as $version) {
+ $versionEntity = new VersionEntity();
+ $versionEntity->setFileId($file2->getId());
+ $versionEntity->setTimestamp((int)$version['version']);
+ $versionEntity->setSize((int)$version['size']);
+ $versionEntity->setMimetype($this->mimeTypeLoader->getId($version['mimetype']));
+ $versionEntity->setMetadata([]);
+ $this->versionsMapper->insert($versionEntity);
+ }
+
+ return $this->getVersionsForFileFromDB($file2, $user);
+ }
+
+ /**
+ * @return IVersion[]
+ */
+ private function getVersionsForFileFromDB(Node $file, IUser $user): array {
+ $userFolder = $this->rootFolder->getUserFolder($user->getUID());
+
+ return array_map(
+ fn (VersionEntity $versionEntity) => new Version(
+ $versionEntity->getTimestamp(),
+ $versionEntity->getTimestamp(),
+ $file->getName(),
+ $versionEntity->getSize(),
+ $this->mimeTypeLoader->getMimetypeById($versionEntity->getMimetype()),
+ $userFolder->getRelativePath($file->getPath()),
$file,
$this,
- $user
- );
- }, $versions);
+ $user,
+ $versionEntity->getLabel(),
+ ),
+ $this->versionsMapper->findAllVersionsForFileId($file->getId())
+ );
}
public function createVersion(IUser $user, FileInfo $file) {
@@ -125,4 +175,16 @@ class LegacyVersionsBackend implements IVersionBackend {
$file = $versionFolder->get($userFolder->getRelativePath($sourceFile->getPath()) . '.v' . $revision);
return $file;
}
+
+ public function setVersionLabel(IVersion $version, string $label): void {
+ $versionEntity = $this->versionsMapper->findVersionForFileId(
+ $version->getSourceFile()->getId(),
+ $version->getTimestamp(),
+ );
+ if (trim($label) === '') {
+ $label = null;
+ }
+ $versionEntity->setLabel($label ?? '');
+ $this->versionsMapper->update($versionEntity);
+ }
}
diff --git a/apps/files_versions/lib/Versions/Version.php b/apps/files_versions/lib/Versions/Version.php
index 9979933ebc5..e87c2a593d7 100644
--- a/apps/files_versions/lib/Versions/Version.php
+++ b/apps/files_versions/lib/Versions/Version.php
@@ -28,7 +28,7 @@ namespace OCA\Files_Versions\Versions;
use OCP\Files\FileInfo;
use OCP\IUser;
-class Version implements IVersion {
+class Version implements IVersion, INameableVersion {
/** @var int */
private $timestamp;
@@ -38,6 +38,8 @@ class Version implements IVersion {
/** @var string */
private $name;
+ private string $label;
+
/** @var int */
private $size;
@@ -65,11 +67,13 @@ class Version implements IVersion {
string $path,
FileInfo $sourceFileInfo,
IVersionBackend $backend,
- IUser $user
+ IUser $user,
+ string $label = ''
) {
$this->timestamp = $timestamp;
$this->revisionId = $revisionId;
$this->name = $name;
+ $this->label = $label;
$this->size = $size;
$this->mimetype = $mimetype;
$this->path = $path;
@@ -102,6 +106,10 @@ class Version implements IVersion {
return $this->name;
}
+ public function getLabel(): string {
+ return $this->label;
+ }
+
public function getMimeType(): string {
return $this->mimetype;
}
diff --git a/apps/files_versions/lib/Versions/VersionManager.php b/apps/files_versions/lib/Versions/VersionManager.php
index 4700f1b208b..4787de7fdac 100644
--- a/apps/files_versions/lib/Versions/VersionManager.php
+++ b/apps/files_versions/lib/Versions/VersionManager.php
@@ -30,7 +30,7 @@ use OCP\Files\FileInfo;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
-class VersionManager implements IVersionManager {
+class VersionManager implements IVersionManager, INameableVersionBackend {
/** @var (IVersionBackend[])[] */
private $backends = [];
@@ -110,4 +110,11 @@ class VersionManager implements IVersionManager {
public function useBackendForStorage(IStorage $storage): bool {
return false;
}
+
+ public function setVersionLabel(IVersion $version, string $label): void {
+ $backend = $this->getBackendForStorage($version->getSourceFile()->getStorage());
+ if ($backend instanceof INameableVersionBackend) {
+ $backend->setVersionLabel($version, $label);
+ }
+ }
}