From 301670751cdceff5dfe994eb3a90725edf3f4511 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 25 Jun 2025 09:09:00 +0000 Subject: [PATCH 1/8] Renovate: Update dependency phpunit/phpunit to v12 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 073a8c2..b4da170 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ } ], "require-dev": { - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^12.0", "nextcloud/coding-standard": "^1.0.0", "nextcloud/ocp": "dev-stable25" }, From 85d63d5ae25b0d57caccb017c83540dd91beee5b Mon Sep 17 00:00:00 2001 From: Jonathan Treffler Date: Wed, 2 Jul 2025 14:35:58 +0200 Subject: [PATCH 2/8] implemented snapshot name parsing --- .l10nignore | 2 + l10n/.gitkeep | 0 lib/Entity/Snapshot.php | 26 +++++-- lib/Manager/PathManager.php | 4 +- lib/Manager/SnapshotManager.php | 103 +++++++++++++++++++++++--- lib/Service/DiffTaskResultService.php | 20 ++--- lib/Service/SettingsService.php | 2 +- src/AdminSettings.vue | 54 ++++++++++++-- 8 files changed, 175 insertions(+), 36 deletions(-) create mode 100644 .l10nignore create mode 100644 l10n/.gitkeep diff --git a/.l10nignore b/.l10nignore new file mode 100644 index 0000000..e505894 --- /dev/null +++ b/.l10nignore @@ -0,0 +1,2 @@ +js/ +vendor/ \ No newline at end of file diff --git a/l10n/.gitkeep b/l10n/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/Entity/Snapshot.php b/lib/Entity/Snapshot.php index e571a7d..d07be15 100644 --- a/lib/Entity/Snapshot.php +++ b/lib/Entity/Snapshot.php @@ -5,16 +5,30 @@ namespace OCA\GroupfolderFilesystemSnapshots\Entity; use JsonSerializable; class Snapshot implements JsonSerializable { - /** @var string */ - private $id; - - public function __construct(string $id) { - $this->id = $id; + public function __construct( + private string $id, + private string $name, + private ?\DateTimeImmutable $createdTimestamp = null, + ) { } + public function getId(): string { + return $this->id; + } + + public function getName(): string { + return $this->name; + } + + public function getCreatedTimestamp(): ?\DateTimeImmutable { + return $this->createdTimestamp; + } + public function jsonSerialize(): mixed { return [ - 'id' => $this->id + 'id' => $this->id, + 'name' => $this->name, + 'createdTimestamp' => $this->createdTimestamp?->getTimestamp(), ]; } } diff --git a/lib/Manager/PathManager.php b/lib/Manager/PathManager.php index 7b8a6e9..d7f0234 100644 --- a/lib/Manager/PathManager.php +++ b/lib/Manager/PathManager.php @@ -70,12 +70,12 @@ class PathManager { private function checkIfGroupfolderExists(int $groupfolderId): bool { $storageId = $this->getRootFolderStorageId(); if ($storageId === null) { - return "storage Id null"; + return false; } $folder = $this->groupfolderFolderManager->getFolder($groupfolderId, $storageId); if ($folder === false) { - return "Folder does not exist"; + return false; } return true; diff --git a/lib/Manager/SnapshotManager.php b/lib/Manager/SnapshotManager.php index a66def3..ac9594b 100644 --- a/lib/Manager/SnapshotManager.php +++ b/lib/Manager/SnapshotManager.php @@ -2,16 +2,21 @@ namespace OCA\GroupfolderFilesystemSnapshots\Manager; -use OCA\GroupfolderFilesystemSnapshots\Manager\PathManager; +use OCP\IL10N; +use OCA\GroupfolderFilesystemSnapshots\Manager\PathManager; +use OCA\GroupfolderFilesystemSnapshots\Service\SettingsService; use OCA\GroupfolderFilesystemSnapshots\Entity\Snapshot; class SnapshotManager { - private PathManager $pathManager; + private string $snapshotNamingScheme = ""; - - public function __construct(PathManager $pathManager){ - $this->pathManager = $pathManager; + public function __construct( + protected readonly IL10N $l10n, + private readonly PathManager $pathManager, + private readonly SettingsService $settingsService, + ){ + $this->snapshotNamingScheme = $this->settingsService->getAppValue("snapshot_naming_scheme"); } private function validSnapshotId(string $snapshotId) { @@ -27,20 +32,94 @@ class SnapshotManager { } } + private function createSnapshotEntity(string $id): Snapshot { + if ($this->snapshotNamingScheme === "zfs-auto-snapshot" && str_starts_with($id, "zfs-auto-snap")) { + if (str_starts_with($id, "zfs-auto-snap_hourly-")) { + $name = $this->l10n->t("Automated hourly backup"); + $datetimestring = str_replace("zfs-auto-snap_hourly-", "", $id); + } elseif (str_starts_with($id, "zfs-auto-snap_daily-")) { + $name = $this->l10n->t("Automated daily backup"); + $datetimestring = str_replace("zfs-auto-snap_daily-", "", $id); + } elseif (str_starts_with($id, "zfs-auto-snap_weekly-")) { + $name = $this->l10n->t("Automated weekly backup"); + $datetimestring = str_replace("zfs-auto-snap_weekly-", "", $id); + } elseif (str_starts_with($id, "zfs-auto-snap_monthly-")) { + $name = $this->l10n->t("Automated monthly backup"); + $datetimestring = str_replace("zfs-auto-snap_monthly-", "", $id); + } + + if(isset($datetimestring)) { + $datetimearray = explode("-", $datetimestring); + $timestring = array_pop($datetimearray); + + $year = (int)$datetimearray[0]; + $month = (int)$datetimearray[1]; + $day = (int)$datetimearray[2]; + $hour = (int)substr($timestring, 0, 2); + $minute = (int)substr($timestring, 2, 2); + + $createdTimestamp = (new \DateTimeImmutable()) + ->setDate($year, $month, $day) + ->setTime($hour, $minute); + } + + return new Snapshot( + id: $id, + name: $name ?: $id, + createdTimestamp: $createdTimestamp, + ); + } else { + return new Snapshot( + id: $id, + name: $id, + ); + } + } + function get(string $snapshotId) { if(self::snapshotExists($snapshotId)) { - return new Snapshot($snapshotId); + return $this->createSnapshotEntity($snapshotId); } else { return false; } - } - function getAll() { + function getAll(): array { + $snapshots = []; + $iterator = new \FilesystemIterator($this->pathManager->getFilesystemSnapshotsPath()); - foreach ($iterator as $fileinfo) { - if(!$fileinfo->isDir()) continue; - yield new Snapshot($fileinfo->getFilename()); - } + + foreach ($iterator as $fileinfo) { + if(!$fileinfo->isDir()) continue; + $snapshots[] = $this->createSnapshotEntity($fileinfo->getFilename()); + } + + return $snapshots; } + + function getAllGenerator(){ + $iterator = new \FilesystemIterator($this->pathManager->getFilesystemSnapshotsPath()); + + foreach ($iterator as $fileinfo) { + if(!$fileinfo->isDir()) continue; + yield $this->createSnapshotEntity($fileinfo->getFilename()); + } + } + + /** + * @var $subPathFilter Only return snapshots that have this subfolder in the specified groupfolder + */ + function getFilteredGenerator(int $groupfolderId, string $subDirectoryFilter) { + $iterator = new \FilesystemIterator($this->pathManager->getFilesystemSnapshotsPath()); + + $groupfolderSubdirectoryPath = $this->pathManager->getGroupFolderDirectory($groupfolderId, $subDirectoryFilter); + + foreach ($iterator as $fileinfo) { + if(!$fileinfo->isDir()) continue; + $snapshotId = $fileinfo->getFilename(); + $filterFullPath = $this->pathManager->convertToSnapshotPath($groupfolderSubdirectoryPath, $snapshotId); + if(!(is_dir($filterFullPath))) continue; + yield $this->createSnapshotEntity($snapshotId); + } + } } \ No newline at end of file diff --git a/lib/Service/DiffTaskResultService.php b/lib/Service/DiffTaskResultService.php index 399185b..ed2e750 100644 --- a/lib/Service/DiffTaskResultService.php +++ b/lib/Service/DiffTaskResultService.php @@ -58,7 +58,7 @@ class DiffTaskResultService { $diffTaskResult = $this->find($id); if($diffTaskResult->getReverted()) { - throw new AlreadyRevertedException; + throw new AlreadyRevertedException(); } $taskId = $diffTaskResult->getTaskId(); @@ -66,35 +66,35 @@ class DiffTaskResultService { $snapshotPath = $this->pathManager->getGroupFolderSnapshotDirectory($diffTask->getGroupfolderId(), $diffTask->getRelativePath(), $diffTask->getSnapshotId()); - $gruenerFolder = $this->pathManager->getGroupfolderMountById($diffTask->getGroupfolderId())->get($diffTask->getRelativePath()); + $parentFolder = $this->pathManager->getGroupfolderMountById($diffTask->getGroupfolderId())->get($diffTask->getRelativePath()); switch($diffTaskResult->getType()) { case "CREATION": - $currentFile = $this->getSubfolderMustExist($gruenerFolder, $diffTaskResult->getCurrentPath()); + $currentFile = $this->getSubfolderMustExist($parentFolder, $diffTaskResult->getCurrentPath()); $currentFile->delete(); break; case "RENAME": - $currentFile = $this->getSubfolderMustExist($gruenerFolder, $diffTaskResult->getCurrentPath()); - $beforeDirectory = $this->getOrCreateSubdirectoryByFilepath($gruenerFolder, $diffTaskResult->getBeforePath()); + $currentFile = $this->getSubfolderMustExist($parentFolder, $diffTaskResult->getCurrentPath()); + $beforeDirectory = $this->getOrCreateSubdirectoryByFilepath($parentFolder, $diffTaskResult->getBeforePath()); $currentFile->move($beforeDirectory->getPath() . DIRECTORY_SEPARATOR . basename($diffTaskResult->getBeforePath())); break; case "DELETION": $beforeFileFilesystemPath = $snapshotPath . DIRECTORY_SEPARATOR . $diffTaskResult->getBeforePath(); - $beforeDirectory = $this->getOrCreateSubdirectoryByFilepath($gruenerFolder, $diffTaskResult->getBeforePath()); + $beforeDirectory = $this->getOrCreateSubdirectoryByFilepath($parentFolder, $diffTaskResult->getBeforePath()); $restoredFile = $beforeDirectory->newFile(basename($diffTaskResult->getBeforePath())); $this->copyFilesystemFileToNextcloudFile($beforeFileFilesystemPath, $restoredFile); break; case "EDIT": - $currentFile = $this->getSubfolderMustExist($gruenerFolder, $diffTaskResult->getCurrentPath()); + $currentFile = $this->getSubfolderMustExist($parentFolder, $diffTaskResult->getCurrentPath()); $beforeFileFilesystemPath = $snapshotPath . DIRECTORY_SEPARATOR . $diffTaskResult->getBeforePath(); $this->copyFilesystemFileToNextcloudFile($beforeFileFilesystemPath, $currentFile); break; default: - throw new \Exception; + throw new Exception(); } return $this->mapper->markReverted($id); @@ -104,7 +104,7 @@ class DiffTaskResultService { if($parent->nodeExists($path)) { return $parent->get($path); } else { - throw new ChangesMadeSinceDiffException; + throw new ChangesMadeSinceDiffException(); } } @@ -119,7 +119,7 @@ class DiffTaskResultService { if($temp instanceof \OCP\Files\Folder) { $beforeDirectory = $temp; } else { - throw new ChangesMadeSinceDiffException; + throw new ChangesMadeSinceDiffException(); } } else { $beforeDirectory = $beforeDirectory->newFolder($subdir); diff --git a/lib/Service/SettingsService.php b/lib/Service/SettingsService.php index 9e9a56b..6c4bff0 100644 --- a/lib/Service/SettingsService.php +++ b/lib/Service/SettingsService.php @@ -9,7 +9,7 @@ use OCP\IConfig; class SettingsService { - private static array $VALID_APP_SETTINGS = ["filesystem_mountpoint_path", "filesystem_snapshots_path"]; + private static array $VALID_APP_SETTINGS = ["filesystem_mountpoint_path", "filesystem_snapshots_path", "snapshot_naming_scheme"]; public function __construct(private IConfig $config) { } diff --git a/src/AdminSettings.vue b/src/AdminSettings.vue index fd5a7a3..e605975 100644 --- a/src/AdminSettings.vue +++ b/src/AdminSettings.vue @@ -4,12 +4,13 @@ name="Groupfolder Filesystem Snapshots" :limit-width="false">
-
@@ -18,7 +19,7 @@