mirror of
https://github.com/nextcloud/server.git
synced 2026-02-03 20:41:22 -05:00
fix: add repair step for cleanup shares with excess (2)
Signed-off-by: Robin Appelman <robin@icewind.nl>
This commit is contained in:
parent
ebfdbf86b9
commit
aedae2c601
4 changed files with 135 additions and 0 deletions
|
|
@ -92,6 +92,7 @@ return array(
|
|||
'OCA\\Files_Sharing\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
|
||||
'OCA\\Files_Sharing\\OpenMetrics\\SharesCountMetric' => $baseDir . '/../lib/OpenMetrics/SharesCountMetric.php',
|
||||
'OCA\\Files_Sharing\\OrphanHelper' => $baseDir . '/../lib/OrphanHelper.php',
|
||||
'OCA\\Files_Sharing\\Repair\\CleanupShareTarget' => $baseDir . '/../lib/Repair/CleanupShareTarget.php',
|
||||
'OCA\\Files_Sharing\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php',
|
||||
'OCA\\Files_Sharing\\Scanner' => $baseDir . '/../lib/Scanner.php',
|
||||
'OCA\\Files_Sharing\\Settings\\Personal' => $baseDir . '/../lib/Settings/Personal.php',
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ class ComposerStaticInitFiles_Sharing
|
|||
'OCA\\Files_Sharing\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
|
||||
'OCA\\Files_Sharing\\OpenMetrics\\SharesCountMetric' => __DIR__ . '/..' . '/../lib/OpenMetrics/SharesCountMetric.php',
|
||||
'OCA\\Files_Sharing\\OrphanHelper' => __DIR__ . '/..' . '/../lib/OrphanHelper.php',
|
||||
'OCA\\Files_Sharing\\Repair\\CleanupShareTarget' => __DIR__ . '/..' . '/../lib/Repair/CleanupShareTarget.php',
|
||||
'OCA\\Files_Sharing\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php',
|
||||
'OCA\\Files_Sharing\\Scanner' => __DIR__ . '/..' . '/../lib/Scanner.php',
|
||||
'OCA\\Files_Sharing\\Settings\\Personal' => __DIR__ . '/..' . '/../lib/Settings/Personal.php',
|
||||
|
|
|
|||
131
apps/files_sharing/lib/Repair/CleanupShareTarget.php
Normal file
131
apps/files_sharing/lib/Repair/CleanupShareTarget.php
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Sharing\Repair;
|
||||
|
||||
use OC\Files\SetupManager;
|
||||
use OCA\Files_Sharing\ShareTargetValidator;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Files\Config\ICachedMountInfo;
|
||||
use OCP\Files\Config\IUserMountCache;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUserManager;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\IRepairStep;
|
||||
use OCP\Share\IManager;
|
||||
use OCP\Share\IProviderFactory;
|
||||
use OCP\Share\IShare;
|
||||
|
||||
class CleanupShareTarget implements IRepairStep {
|
||||
/** we only care about shares with a user target,
|
||||
* since the underling group/deck/talk share doesn't get moved
|
||||
*/
|
||||
private const USER_SHARE_TYPES = [
|
||||
IShare::TYPE_USER,
|
||||
IShare::TYPE_USERGROUP,
|
||||
IShare::TYPE_DECK_USER,
|
||||
11 // TYPE_USERROOM
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private readonly IDBConnection $connection,
|
||||
private readonly IManager $shareManager,
|
||||
private readonly IProviderFactory $shareProviderFactory,
|
||||
private readonly ShareTargetValidator $shareTargetValidator,
|
||||
private readonly IUserManager $userManager,
|
||||
private readonly SetupManager $setupManager,
|
||||
private readonly IUserMountCache $userMountCache,
|
||||
) {
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getName() {
|
||||
return 'Cleanup share names with false conflicts';
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function run(IOutput $output) {
|
||||
$count = $this->countProblemShares();
|
||||
if ($count === 0) {
|
||||
return;
|
||||
}
|
||||
$output->startProgress($count);
|
||||
|
||||
$lastUser = '';
|
||||
$userMounts = [];
|
||||
|
||||
foreach ($this->getProblemShares() as $shareInfo) {
|
||||
$recipient = $this->userManager->getExistingUser($shareInfo['share_with']);
|
||||
$share = $this->shareProviderFactory
|
||||
->getProviderForType((int)$shareInfo['share_type'])
|
||||
->getShareById($shareInfo['id'], $recipient->getUID());
|
||||
|
||||
// since we ordered the share by user, we can reuse the last data until we get to the next user
|
||||
if ($lastUser !== $recipient->getUID()) {
|
||||
$lastUser = $recipient->getUID();
|
||||
|
||||
$this->setupManager->tearDown();
|
||||
$this->setupManager->setupForUser($recipient);
|
||||
$mounts = $this->userMountCache->getMountsForUser($recipient);
|
||||
$mountPoints = array_map(fn (ICachedMountInfo $mount) => $mount->getMountPoint(), $mounts);
|
||||
$userMounts = array_combine($mountPoints, $mounts);
|
||||
}
|
||||
|
||||
$oldTarget = $share->getTarget();
|
||||
$newTarget = $this->cleanTarget($oldTarget);
|
||||
$share->setTarget($newTarget);
|
||||
$this->shareManager->moveShare($share, $recipient->getUID());
|
||||
|
||||
$this->shareTargetValidator->verifyMountPoint(
|
||||
$recipient,
|
||||
$share,
|
||||
$userMounts,
|
||||
[$share],
|
||||
);
|
||||
|
||||
$oldMountPoint = "/{$recipient->getUID()}/files$oldTarget/";
|
||||
$newMountPoint = "/{$recipient->getUID()}/files$newTarget/";
|
||||
$userMounts[$newMountPoint] = $userMounts[$oldMountPoint];
|
||||
unset($userMounts[$oldMountPoint]);
|
||||
|
||||
$output->advance();
|
||||
}
|
||||
$output->finishProgress();
|
||||
$output->info("Fixed $count shares");
|
||||
}
|
||||
|
||||
private function countProblemShares(): int {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->select($query->func()->count('id'))
|
||||
->from('share')
|
||||
->where($query->expr()->like('file_target', $query->createNamedParameter('% (_) (_)%')))
|
||||
->andWhere($query->expr()->in('share_type', $query->createNamedParameter(self::USER_SHARE_TYPES, IQueryBuilder::PARAM_INT_ARRAY), IQueryBuilder::PARAM_INT_ARRAY));
|
||||
return (int)$query->executeQuery()->fetchOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Traversable<array{id: string, share_type: string, share_with: string}>
|
||||
*/
|
||||
private function getProblemShares(): \Traversable {
|
||||
$query = $this->connection->getQueryBuilder();
|
||||
$query->select('id', 'share_type', 'share_with')
|
||||
->from('share')
|
||||
->where($query->expr()->like('file_target', $query->createNamedParameter('% (_) (_)%')))
|
||||
->andWhere($query->expr()->in('share_type', $query->createNamedParameter(self::USER_SHARE_TYPES, IQueryBuilder::PARAM_INT_ARRAY), IQueryBuilder::PARAM_INT_ARRAY))
|
||||
->orderBy('share_with')
|
||||
->addOrderBy('id');
|
||||
$result = $query->executeQuery();
|
||||
/** @var \Traversable<array{id: string, share_type: string, share_with: string}> $rows */
|
||||
$rows = $result->iterateAssociative();
|
||||
return $rows;
|
||||
}
|
||||
|
||||
private function cleanTarget(string $target): string {
|
||||
return preg_replace('/( \([2-9]\)){2,}/', '', $target);
|
||||
}
|
||||
}
|
||||
|
|
@ -60,6 +60,7 @@ use OC\Repair\RepairMimeTypes;
|
|||
use OC\Template\JSCombiner;
|
||||
use OCA\DAV\Migration\DeleteSchedulingObjects;
|
||||
use OCA\DAV\Migration\RemoveObjectProperties;
|
||||
use OCA\Files_Sharing\Repair\CleanupShareTarget;
|
||||
use OCP\AppFramework\QueryException;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Collaboration\Resources\IManager;
|
||||
|
|
@ -221,6 +222,7 @@ class Repair implements IOutput {
|
|||
),
|
||||
\OCP\Server::get(DeleteSchedulingObjects::class),
|
||||
\OC::$server->get(RemoveObjectProperties::class),
|
||||
\OCP\Server::get(CleanupShareTarget::class),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue