f7cloud_client/apps/groupfolders/lib/Versions/ExpireManager.php
root 8b6a0139db f7cloud_client
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-17 22:59:26 +00:00

136 lines
4.4 KiB
PHP

<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2018 F7cloud GmbH and F7cloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\GroupFolders\Versions;
use OCA\Files_Versions\Expiration;
use OCA\Files_Versions\Versions\IMetadataVersion;
use OCA\Files_Versions\Versions\IVersion;
/**
* TODO: move this to files_versions to be reused to apps in nc16
*/
class ExpireManager {
public const MAX_VERSIONS_PER_INTERVAL = [
//first 10sec, one version every 2sec
1 => ['intervalEndsAfter' => 10, 'step' => 2],
//next minute, one version every 10sec
2 => ['intervalEndsAfter' => 60, 'step' => 10],
//next hour, one version every minute
3 => ['intervalEndsAfter' => 3600, 'step' => 60],
//next 24h, one version every hour
4 => ['intervalEndsAfter' => 86400, 'step' => 3600],
//next 30days, one version per day
5 => ['intervalEndsAfter' => 2592000, 'step' => 86400],
//until the end one version per week
6 => ['intervalEndsAfter' => -1, 'step' => 604800],
];
public function __construct(
private readonly Expiration $expiration,
) {
}
/**
* Get list of files we want to expire
*
* @param GroupVersion[] $versions
* @return GroupVersion[]
*/
protected function getAutoExpireList(int $time, array $versions): array {
if (!$versions) {
return [];
}
$toDelete = []; // versions we want to delete
// Ensure the versions are sorted current first, then newest first
usort($versions, function (GroupVersion $a, GroupVersion $b): int {
if ($a->isCurrentVersion()) {
return 1;
} elseif ($b->isCurrentVersion()) {
return -1;
}
return $b->getTimestamp() <=> $a->getTimestamp();
});
$interval = 1;
$step = self::MAX_VERSIONS_PER_INTERVAL[$interval]['step'];
$nextInterval = $time - self::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'];
$firstVersion = array_shift($versions);
$prevTimestamp = $firstVersion->getTimestamp();
$nextVersion = $firstVersion->getTimestamp() - $step;
foreach ($versions as $version) {
$newInterval = true;
while ($newInterval) {
if ($nextInterval === -1 || $prevTimestamp > $nextInterval) {
if ($version->getTimestamp() > $nextVersion) {
// Do not expire versions with a label.
if ((!($version instanceof IMetadataVersion) || $version->getMetadataValue('label') === null || $version->getMetadataValue('label') === '') && !$version->isCurrentVersion()) {
//distance between two version too small, mark to delete
$toDelete[] = $version;
}
} else {
$nextVersion = $version->getTimestamp() - $step;
$prevTimestamp = $version->getTimestamp();
}
$newInterval = false; // version checked so we can move to the next one
} else { // time to move on to the next interval
$interval++;
/** @psalm-suppress InvalidArrayOffset We know that $interval is <= 6 thanks to the -1 intervalEndsAfter in the last step */
$step = self::MAX_VERSIONS_PER_INTERVAL[$interval]['step'];
$nextVersion = $prevTimestamp - $step;
if (self::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'] === -1) {
$nextInterval = -1;
} else {
$nextInterval = $time - self::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'];
}
$newInterval = true; // we changed the interval -> check same version with new interval
}
}
}
return $toDelete;
}
/**
* @param GroupVersion[] $versions
* @return GroupVersion[]
*/
public function getExpiredVersion(array $versions, int $time, bool $quotaExceeded): array {
if ($this->expiration->shouldAutoExpire()) {
$autoExpire = $this->getAutoExpireList($time, $versions);
} else {
$autoExpire = [];
}
$versionsLeft = array_udiff($versions, $autoExpire, fn (IVersion $a, IVersion $b): int => ($a->getRevisionId() <=> $b->getRevisionId())
* ($a->getSourceFile()->getId() <=> $b->getSourceFile()->getId()));
$expired = array_filter($versionsLeft, function (GroupVersion $version) use ($quotaExceeded): bool {
// Do not expire current version.
if ($version->isCurrentVersion()) {
return false;
}
// Do not expire versions with a label.
if ($version instanceof IMetadataVersion && $version->getMetadataValue('label') !== null && $version->getMetadataValue('label') !== '') {
return false;
}
return $this->expiration->isExpired($version->getTimestamp(), $quotaExceeded);
});
return array_merge($autoExpire, $expired);
}
}