Merge pull request #52879 from nextcloud/bug/noid/clear-dav-photo-cache

Add command to clear dav's photo cache
add-integration-tests-for-deleting-a-file-copied-from-a-share
Christoph Wurst 5 months ago committed by GitHub
commit 80b21cdc6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      apps/dav/appinfo/info.xml
  2. 6
      apps/dav/appinfo/v1/carddav.php
  3. 1
      apps/dav/composer/composer/autoload_classmap.php
  4. 1
      apps/dav/composer/composer/autoload_static.php
  5. 8
      apps/dav/lib/AppInfo/Application.php
  6. 24
      apps/dav/lib/CardDAV/PhotoCache.php
  7. 75
      apps/dav/lib/Command/ClearContactsPhotoCache.php
  8. 6
      apps/dav/lib/Server.php
  9. 3
      lib/private/Repair.php
  10. 24
      lib/private/Repair/NC16/CleanupCardDAVPhotoCache.php

@ -57,6 +57,7 @@
<commands>
<command>OCA\DAV\Command\ClearCalendarUnshares</command>
<command>OCA\DAV\Command\ClearContactsPhotoCache</command>
<command>OCA\DAV\Command\CreateAddressBook</command>
<command>OCA\DAV\Command\CreateCalendar</command>
<command>OCA\DAV\Command\CreateSubscription</command>

@ -23,7 +23,6 @@ use OCA\DAV\Connector\Sabre\Principal;
use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\AppData\IAppDataFactory;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroupManager;
@ -98,10 +97,7 @@ if ($debugging) {
$server->addPlugin(new \Sabre\DAV\Sync\Plugin());
$server->addPlugin(new \Sabre\CardDAV\VCFExportPlugin());
$server->addPlugin(new ImageExportPlugin(new PhotoCache(
Server::get(IAppDataFactory::class)->get('dav-photocache'),
Server::get(LoggerInterface::class)
)));
$server->addPlugin(new ImageExportPlugin(Server::get(PhotoCache::class)));
$server->addPlugin(new ExceptionLoggerPlugin('carddav', Server::get(LoggerInterface::class)));
$server->addPlugin(Server::get(CardDavRateLimitingPlugin::class));
$server->addPlugin(Server::get(CardDavValidatePlugin::class));

@ -156,6 +156,7 @@ return array(
'OCA\\DAV\\CardDAV\\Validation\\CardDavValidatePlugin' => $baseDir . '/../lib/CardDAV/Validation/CardDavValidatePlugin.php',
'OCA\\DAV\\CardDAV\\Xml\\Groups' => $baseDir . '/../lib/CardDAV/Xml/Groups.php',
'OCA\\DAV\\Command\\ClearCalendarUnshares' => $baseDir . '/../lib/Command/ClearCalendarUnshares.php',
'OCA\\DAV\\Command\\ClearContactsPhotoCache' => $baseDir . '/../lib/Command/ClearContactsPhotoCache.php',
'OCA\\DAV\\Command\\CreateAddressBook' => $baseDir . '/../lib/Command/CreateAddressBook.php',
'OCA\\DAV\\Command\\CreateCalendar' => $baseDir . '/../lib/Command/CreateCalendar.php',
'OCA\\DAV\\Command\\CreateSubscription' => $baseDir . '/../lib/Command/CreateSubscription.php',

@ -171,6 +171,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CardDAV\\Validation\\CardDavValidatePlugin' => __DIR__ . '/..' . '/../lib/CardDAV/Validation/CardDavValidatePlugin.php',
'OCA\\DAV\\CardDAV\\Xml\\Groups' => __DIR__ . '/..' . '/../lib/CardDAV/Xml/Groups.php',
'OCA\\DAV\\Command\\ClearCalendarUnshares' => __DIR__ . '/..' . '/../lib/Command/ClearCalendarUnshares.php',
'OCA\\DAV\\Command\\ClearContactsPhotoCache' => __DIR__ . '/..' . '/../lib/Command/ClearContactsPhotoCache.php',
'OCA\\DAV\\Command\\CreateAddressBook' => __DIR__ . '/..' . '/../lib/Command/CreateAddressBook.php',
'OCA\\DAV\\Command\\CreateCalendar' => __DIR__ . '/..' . '/../lib/Command/CreateCalendar.php',
'OCA\\DAV\\Command\\CreateSubscription' => __DIR__ . '/..' . '/../lib/Command/CreateSubscription.php',

@ -20,7 +20,6 @@ use OCA\DAV\CalDAV\Reminder\NotificationProviderManager;
use OCA\DAV\CalDAV\Reminder\Notifier;
use OCA\DAV\Capabilities;
use OCA\DAV\CardDAV\ContactsManager;
use OCA\DAV\CardDAV\PhotoCache;
use OCA\DAV\CardDAV\SyncService;
use OCA\DAV\Events\AddressBookCreatedEvent;
use OCA\DAV\Events\AddressBookDeletedEvent;
@ -82,7 +81,6 @@ use OCP\Config\BeforePreferenceSetEvent;
use OCP\Contacts\IManager as IContactsManager;
use OCP\DB\Events\AddMissingIndicesEvent;
use OCP\Federation\Events\TrustedServerRemovedEvent;
use OCP\Files\AppData\IAppDataFactory;
use OCP\IUserSession;
use OCP\Server;
use OCP\Settings\Events\DeclarativeSettingsGetValueEvent;
@ -112,12 +110,6 @@ class Application extends App implements IBootstrap {
public function register(IRegistrationContext $context): void {
$context->registerServiceAlias('CardDAVSyncService', SyncService::class);
$context->registerService(PhotoCache::class, function (ContainerInterface $c) {
return new PhotoCache(
$c->get(IAppDataFactory::class)->get('dav-photocache'),
$c->get(LoggerInterface::class)
);
});
$context->registerService(AppCalendarPlugin::class, function (ContainerInterface $c) {
return new AppCalendarPlugin(
$c->get(ICalendarManager::class),

@ -3,8 +3,10 @@
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\CardDAV;
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
@ -19,6 +21,7 @@ use Sabre\VObject\Property\Binary;
use Sabre\VObject\Reader;
class PhotoCache {
private ?IAppData $photoCacheAppData = null;
/** @var array */
public const ALLOWED_CONTENT_TYPES = [
@ -30,12 +33,9 @@ class PhotoCache {
'image/avif' => 'avif',
];
/**
* PhotoCache constructor.
*/
public function __construct(
protected IAppData $appData,
protected LoggerInterface $logger,
private IAppDataFactory $appDataFactory,
private LoggerInterface $logger,
) {
}
@ -142,13 +142,12 @@ class PhotoCache {
private function getFolder(int $addressBookId, string $cardUri, bool $createIfNotExists = true): ISimpleFolder {
$hash = md5($addressBookId . ' ' . $cardUri);
try {
return $this->appData->getFolder($hash);
return $this->getPhotoCacheAppData()->getFolder($hash);
} catch (NotFoundException $e) {
if ($createIfNotExists) {
return $this->appData->newFolder($hash);
} else {
throw $e;
return $this->getPhotoCacheAppData()->newFolder($hash);
}
throw $e;
}
}
@ -265,4 +264,11 @@ class PhotoCache {
// that's OK, nothing to do
}
}
private function getPhotoCacheAppData(): IAppData {
if ($this->photoCacheAppData === null) {
$this->photoCacheAppData = $this->appDataFactory->get('dav-photocache');
}
return $this->photoCacheAppData;
}
}

@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\DAV\Command;
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\NotPermittedException;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
#[AsCommand(
name: 'dav:clear-contacts-photo-cache',
description: 'Clear cached contact photos',
hidden: false,
)]
class ClearContactsPhotoCache extends Command {
public function __construct(
private IAppDataFactory $appDataFactory,
) {
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int {
$photoCacheAppData = $this->appDataFactory->get('dav-photocache');
$folders = $photoCacheAppData->getDirectoryListing();
$countFolders = count($folders);
if ($countFolders === 0) {
$output->writeln('No cached contact photos found.');
return self::SUCCESS;
}
$output->writeln('Found ' . count($folders) . ' cached contact photos.');
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion('Please confirm to clear the contacts photo cache [y/n] ', true);
if ($helper->ask($input, $output, $question) === false) {
$output->writeln('Clearing the contacts photo cache aborted.');
return self::SUCCESS;
}
$progressBar = new ProgressBar($output, $countFolders);
$progressBar->start();
foreach ($folders as $folder) {
try {
$folder->delete();
} catch (NotPermittedException) {
}
$progressBar->advance();
}
$progressBar->finish();
$output->writeln('');
$output->writeln('Contacts photo cache cleared.');
return self::SUCCESS;
}
}

@ -72,7 +72,6 @@ use OCP\Comments\ICommentsManager;
use OCP\Defaults;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\IFilenameValidator;
use OCP\Files\IRootFolder;
use OCP\FilesMetadata\IFilesMetadataManager;
@ -216,10 +215,7 @@ class Server {
$this->server->addPlugin(new VCFExportPlugin());
$this->server->addPlugin(new MultiGetExportPlugin());
$this->server->addPlugin(new HasPhotoPlugin());
$this->server->addPlugin(new ImageExportPlugin(new PhotoCache(
\OCP\Server::get(IAppDataFactory::class)->get('dav-photocache'),
$logger)
));
$this->server->addPlugin(new ImageExportPlugin(\OCP\Server::get(PhotoCache::class)));
$this->server->addPlugin(\OCP\Server::get(CardDavRateLimitingPlugin::class));
$this->server->addPlugin(\OCP\Server::get(CardDavValidatePlugin::class));

@ -61,6 +61,7 @@ use OCP\AppFramework\QueryException;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Collaboration\Resources\IManager;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\AppData\IAppDataFactory;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDBConnection;
@ -173,7 +174,7 @@ class Repair implements IOutput {
\OCP\Server::get(ClearGeneratedAvatarCache::class),
new AddPreviewBackgroundCleanupJob(\OC::$server->getJobList()),
new AddCleanupUpdaterBackupsJob(\OC::$server->getJobList()),
new CleanupCardDAVPhotoCache(\OC::$server->getConfig(), \OC::$server->getAppDataDir('dav-photocache'), \OC::$server->get(LoggerInterface::class)),
new CleanupCardDAVPhotoCache(\OC::$server->getConfig(), \OCP\Server::get(IAppDataFactory::class), \OC::$server->get(LoggerInterface::class)),
new AddClenupLoginFlowV2BackgroundJob(\OC::$server->getJobList()),
new RemoveLinkShares(\OC::$server->getDatabaseConnection(), \OC::$server->getConfig(), \OC::$server->getGroupManager(), \OC::$server->get(INotificationManager::class), \OCP\Server::get(ITimeFactory::class)),
new ClearCollectionsAccessCache(\OC::$server->getConfig(), \OCP\Server::get(IManager::class)),

@ -6,9 +6,10 @@ declare(strict_types=1);
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Repair\NC16;
use OCP\Files\IAppData;
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;
@ -27,18 +28,11 @@ use RuntimeException;
* photo could be returned for this vcard. These invalid files are removed by this migration step.
*/
class CleanupCardDAVPhotoCache implements IRepairStep {
/** @var IConfig */
private $config;
/** @var IAppData */
private $appData;
private LoggerInterface $logger;
public function __construct(IConfig $config, IAppData $appData, LoggerInterface $logger) {
$this->config = $config;
$this->appData = $appData;
$this->logger = $logger;
public function __construct(
private IConfig $config,
private IAppDataFactory $appDataFactory,
private LoggerInterface $logger,
) {
}
public function getName(): string {
@ -46,8 +40,10 @@ class CleanupCardDAVPhotoCache implements IRepairStep {
}
private function repair(IOutput $output): void {
$photoCacheAppData = $this->appDataFactory->get('dav-photocache');
try {
$folders = $this->appData->getDirectoryListing();
$folders = $photoCacheAppData->getDirectoryListing();
} catch (NotFoundException $e) {
return;
} catch (RuntimeException $e) {

Loading…
Cancel
Save