aboutsummaryrefslogtreecommitdiffstats
path: root/apps/theming/lib/Jobs/MigrateBackgroundImages.php
diff options
context:
space:
mode:
Diffstat (limited to 'apps/theming/lib/Jobs/MigrateBackgroundImages.php')
-rw-r--r--apps/theming/lib/Jobs/MigrateBackgroundImages.php199
1 files changed, 199 insertions, 0 deletions
diff --git a/apps/theming/lib/Jobs/MigrateBackgroundImages.php b/apps/theming/lib/Jobs/MigrateBackgroundImages.php
new file mode 100644
index 00000000000..62e58f5e722
--- /dev/null
+++ b/apps/theming/lib/Jobs/MigrateBackgroundImages.php
@@ -0,0 +1,199 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\Theming\Jobs;
+
+use OCA\Theming\AppInfo\Application;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\IJobList;
+use OCP\BackgroundJob\QueuedJob;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\Files\AppData\IAppDataFactory;
+use OCP\Files\IAppData;
+use OCP\Files\NotFoundException;
+use OCP\Files\NotPermittedException;
+use OCP\Files\SimpleFS\ISimpleFolder;
+use OCP\IDBConnection;
+use Psr\Log\LoggerInterface;
+
+class MigrateBackgroundImages extends QueuedJob {
+ public const TIME_SENSITIVE = 0;
+
+ public const STAGE_PREPARE = 'prepare';
+ public const STAGE_EXECUTE = 'execute';
+ // will be saved in appdata/theming/global/
+ protected const STATE_FILE_NAME = '25_dashboard_to_theming_migration_users.json';
+
+ public function __construct(
+ ITimeFactory $time,
+ private IAppDataFactory $appDataFactory,
+ private IJobList $jobList,
+ private IDBConnection $dbc,
+ private IAppData $appData,
+ private LoggerInterface $logger,
+ ) {
+ parent::__construct($time);
+ }
+
+ protected function run(mixed $argument): void {
+ if (!is_array($argument) || !isset($argument['stage'])) {
+ throw new \Exception('Job ' . self::class . ' called with wrong argument');
+ }
+
+ switch ($argument['stage']) {
+ case self::STAGE_PREPARE:
+ $this->runPreparation();
+ break;
+ case self::STAGE_EXECUTE:
+ $this->runMigration();
+ break;
+ default:
+ break;
+ }
+ }
+
+ protected function runPreparation(): void {
+ try {
+ $selector = $this->dbc->getQueryBuilder();
+ $result = $selector->select('userid')
+ ->from('preferences')
+ ->where($selector->expr()->eq('appid', $selector->createNamedParameter('theming')))
+ ->andWhere($selector->expr()->eq('configkey', $selector->createNamedParameter('background')))
+ ->andWhere($selector->expr()->eq('configvalue', $selector->createNamedParameter('custom', IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR))
+ ->executeQuery();
+
+ $userIds = $result->fetchAll(\PDO::FETCH_COLUMN);
+ $this->storeUserIdsToProcess($userIds);
+ } catch (\Throwable $t) {
+ $this->jobList->add(self::class, ['stage' => self::STAGE_PREPARE]);
+ throw $t;
+ }
+ $this->jobList->add(self::class, ['stage' => self::STAGE_EXECUTE]);
+ }
+
+ /**
+ * @throws NotPermittedException
+ * @throws NotFoundException
+ */
+ protected function runMigration(): void {
+ $allUserIds = $this->readUserIdsToProcess();
+ $notSoFastMode = count($allUserIds) > 5000;
+ $dashboardData = $this->appDataFactory->get('dashboard');
+
+ $userIds = $notSoFastMode ? array_slice($allUserIds, 0, 5000) : $allUserIds;
+ foreach ($userIds as $userId) {
+ try {
+ // migration
+ $file = $dashboardData->getFolder($userId)->getFile('background.jpg');
+ $targetDir = $this->getUserFolder($userId);
+
+ if (!$targetDir->fileExists('background.jpg')) {
+ $targetDir->newFile('background.jpg', $file->getContent());
+ }
+ $file->delete();
+ } catch (NotFoundException|NotPermittedException $e) {
+ }
+ }
+
+ if ($notSoFastMode) {
+ $remainingUserIds = array_slice($allUserIds, 5000);
+ $this->storeUserIdsToProcess($remainingUserIds);
+ $this->jobList->add(self::class, ['stage' => self::STAGE_EXECUTE]);
+ } else {
+ $this->deleteStateFile();
+ }
+ }
+
+ /**
+ * @throws NotPermittedException
+ * @throws NotFoundException
+ */
+ protected function readUserIdsToProcess(): array {
+ $globalFolder = $this->appData->getFolder('global');
+ if ($globalFolder->fileExists(self::STATE_FILE_NAME)) {
+ $file = $globalFolder->getFile(self::STATE_FILE_NAME);
+ try {
+ $userIds = \json_decode($file->getContent(), true);
+ } catch (NotFoundException $e) {
+ $userIds = [];
+ }
+ if ($userIds === null) {
+ $userIds = [];
+ }
+ } else {
+ $userIds = [];
+ }
+ return $userIds;
+ }
+
+ /**
+ * @throws NotFoundException
+ */
+ protected function storeUserIdsToProcess(array $userIds): void {
+ $storableUserIds = \json_encode($userIds);
+ $globalFolder = $this->appData->getFolder('global');
+ try {
+ if ($globalFolder->fileExists(self::STATE_FILE_NAME)) {
+ $file = $globalFolder->getFile(self::STATE_FILE_NAME);
+ } else {
+ $file = $globalFolder->newFile(self::STATE_FILE_NAME);
+ }
+ $file->putContent($storableUserIds);
+ } catch (NotFoundException $e) {
+ } catch (NotPermittedException $e) {
+ $this->logger->warning('Lacking permissions to create {file}',
+ [
+ 'app' => 'theming',
+ 'file' => self::STATE_FILE_NAME,
+ 'exception' => $e,
+ ]
+ );
+ }
+ }
+
+ /**
+ * @throws NotFoundException
+ */
+ protected function deleteStateFile(): void {
+ $globalFolder = $this->appData->getFolder('global');
+ if ($globalFolder->fileExists(self::STATE_FILE_NAME)) {
+ $file = $globalFolder->getFile(self::STATE_FILE_NAME);
+ try {
+ $file->delete();
+ } catch (NotPermittedException $e) {
+ $this->logger->info('Could not delete {file} due to permissions. It is safe to delete manually inside data -> appdata -> theming -> global.',
+ [
+ 'app' => 'theming',
+ 'file' => $file->getName(),
+ 'exception' => $e,
+ ]
+ );
+ }
+ }
+ }
+
+ /**
+ * Get the root location for users theming data
+ */
+ protected function getUserFolder(string $userId): ISimpleFolder {
+ $themingData = $this->appDataFactory->get(Application::APP_ID);
+
+ try {
+ $rootFolder = $themingData->getFolder('users');
+ } catch (NotFoundException $e) {
+ $rootFolder = $themingData->newFolder('users');
+ }
+
+ try {
+ return $rootFolder->getFolder($userId);
+ } catch (NotFoundException $e) {
+ return $rootFolder->newFolder($userId);
+ }
+ }
+}