@@ -17,62 +17,62 @@ |
||
| 17 | 17 | |
| 18 | 18 | class CalDAVSettings implements IDelegatedSettings { |
| 19 | 19 | |
| 20 | - private const defaults = [ |
|
| 21 | - 'sendInvitations' => 'yes', |
|
| 22 | - 'generateBirthdayCalendar' => 'yes', |
|
| 23 | - 'sendEventReminders' => 'yes', |
|
| 24 | - 'sendEventRemindersToSharedUsers' => 'yes', |
|
| 25 | - 'sendEventRemindersPush' => 'yes', |
|
| 26 | - ]; |
|
| 20 | + private const defaults = [ |
|
| 21 | + 'sendInvitations' => 'yes', |
|
| 22 | + 'generateBirthdayCalendar' => 'yes', |
|
| 23 | + 'sendEventReminders' => 'yes', |
|
| 24 | + 'sendEventRemindersToSharedUsers' => 'yes', |
|
| 25 | + 'sendEventRemindersPush' => 'yes', |
|
| 26 | + ]; |
|
| 27 | 27 | |
| 28 | - /** |
|
| 29 | - * CalDAVSettings constructor. |
|
| 30 | - * |
|
| 31 | - * @param IConfig $config |
|
| 32 | - * @param IInitialState $initialState |
|
| 33 | - */ |
|
| 34 | - public function __construct( |
|
| 35 | - private IConfig $config, |
|
| 36 | - private IInitialState $initialState, |
|
| 37 | - private IURLGenerator $urlGenerator, |
|
| 38 | - private IAppManager $appManager, |
|
| 39 | - ) { |
|
| 40 | - } |
|
| 28 | + /** |
|
| 29 | + * CalDAVSettings constructor. |
|
| 30 | + * |
|
| 31 | + * @param IConfig $config |
|
| 32 | + * @param IInitialState $initialState |
|
| 33 | + */ |
|
| 34 | + public function __construct( |
|
| 35 | + private IConfig $config, |
|
| 36 | + private IInitialState $initialState, |
|
| 37 | + private IURLGenerator $urlGenerator, |
|
| 38 | + private IAppManager $appManager, |
|
| 39 | + ) { |
|
| 40 | + } |
|
| 41 | 41 | |
| 42 | - public function getForm(): TemplateResponse { |
|
| 43 | - $this->initialState->provideInitialState('userSyncCalendarsDocUrl', $this->urlGenerator->linkToDocs('user-sync-calendars')); |
|
| 44 | - foreach (self::defaults as $key => $default) { |
|
| 45 | - $value = $this->config->getAppValue(Application::APP_ID, $key, $default); |
|
| 46 | - $this->initialState->provideInitialState($key, $value === 'yes'); |
|
| 47 | - } |
|
| 42 | + public function getForm(): TemplateResponse { |
|
| 43 | + $this->initialState->provideInitialState('userSyncCalendarsDocUrl', $this->urlGenerator->linkToDocs('user-sync-calendars')); |
|
| 44 | + foreach (self::defaults as $key => $default) { |
|
| 45 | + $value = $this->config->getAppValue(Application::APP_ID, $key, $default); |
|
| 46 | + $this->initialState->provideInitialState($key, $value === 'yes'); |
|
| 47 | + } |
|
| 48 | 48 | |
| 49 | - Util::addScript(Application::APP_ID, 'settings-admin-caldav'); |
|
| 50 | - Util::addStyle(Application::APP_ID, 'settings-admin-caldav'); |
|
| 51 | - return new TemplateResponse(Application::APP_ID, 'settings-admin-caldav'); |
|
| 52 | - } |
|
| 49 | + Util::addScript(Application::APP_ID, 'settings-admin-caldav'); |
|
| 50 | + Util::addStyle(Application::APP_ID, 'settings-admin-caldav'); |
|
| 51 | + return new TemplateResponse(Application::APP_ID, 'settings-admin-caldav'); |
|
| 52 | + } |
|
| 53 | 53 | |
| 54 | - public function getSection(): ?string { |
|
| 55 | - if (!$this->appManager->isBackendRequired(IAppManager::BACKEND_CALDAV)) { |
|
| 56 | - return null; |
|
| 57 | - } |
|
| 54 | + public function getSection(): ?string { |
|
| 55 | + if (!$this->appManager->isBackendRequired(IAppManager::BACKEND_CALDAV)) { |
|
| 56 | + return null; |
|
| 57 | + } |
|
| 58 | 58 | |
| 59 | - return 'groupware'; |
|
| 60 | - } |
|
| 59 | + return 'groupware'; |
|
| 60 | + } |
|
| 61 | 61 | |
| 62 | - /** |
|
| 63 | - * @return int |
|
| 64 | - */ |
|
| 65 | - public function getPriority() { |
|
| 66 | - return 10; |
|
| 67 | - } |
|
| 62 | + /** |
|
| 63 | + * @return int |
|
| 64 | + */ |
|
| 65 | + public function getPriority() { |
|
| 66 | + return 10; |
|
| 67 | + } |
|
| 68 | 68 | |
| 69 | - public function getName(): ?string { |
|
| 70 | - return null; // Only setting in this section |
|
| 71 | - } |
|
| 69 | + public function getName(): ?string { |
|
| 70 | + return null; // Only setting in this section |
|
| 71 | + } |
|
| 72 | 72 | |
| 73 | - public function getAuthorizedAppConfig(): array { |
|
| 74 | - return [ |
|
| 75 | - 'dav' => ['/(' . implode('|', array_keys(self::defaults)) . ')/'] |
|
| 76 | - ]; |
|
| 77 | - } |
|
| 73 | + public function getAuthorizedAppConfig(): array { |
|
| 74 | + return [ |
|
| 75 | + 'dav' => ['/(' . implode('|', array_keys(self::defaults)) . ')/'] |
|
| 76 | + ]; |
|
| 77 | + } |
|
| 78 | 78 | } |
@@ -19,56 +19,56 @@ |
||
| 19 | 19 | use OCP\Util; |
| 20 | 20 | |
| 21 | 21 | class ExampleContentSettings implements ISettings { |
| 22 | - public function __construct( |
|
| 23 | - private readonly IAppConfig $appConfig, |
|
| 24 | - private readonly IInitialState $initialState, |
|
| 25 | - private readonly IAppManager $appManager, |
|
| 26 | - private readonly ExampleEventService $exampleEventService, |
|
| 27 | - private readonly ExampleContactService $exampleContactService, |
|
| 28 | - ) { |
|
| 29 | - } |
|
| 22 | + public function __construct( |
|
| 23 | + private readonly IAppConfig $appConfig, |
|
| 24 | + private readonly IInitialState $initialState, |
|
| 25 | + private readonly IAppManager $appManager, |
|
| 26 | + private readonly ExampleEventService $exampleEventService, |
|
| 27 | + private readonly ExampleContactService $exampleContactService, |
|
| 28 | + ) { |
|
| 29 | + } |
|
| 30 | 30 | |
| 31 | - public function getForm(): TemplateResponse { |
|
| 32 | - $calendarEnabled = $this->appManager->isEnabledForUser('calendar'); |
|
| 33 | - $contactsEnabled = $this->appManager->isEnabledForUser('contacts'); |
|
| 34 | - $this->initialState->provideInitialState('calendarEnabled', $calendarEnabled); |
|
| 35 | - $this->initialState->provideInitialState('contactsEnabled', $contactsEnabled); |
|
| 31 | + public function getForm(): TemplateResponse { |
|
| 32 | + $calendarEnabled = $this->appManager->isEnabledForUser('calendar'); |
|
| 33 | + $contactsEnabled = $this->appManager->isEnabledForUser('contacts'); |
|
| 34 | + $this->initialState->provideInitialState('calendarEnabled', $calendarEnabled); |
|
| 35 | + $this->initialState->provideInitialState('contactsEnabled', $contactsEnabled); |
|
| 36 | 36 | |
| 37 | - if ($calendarEnabled) { |
|
| 38 | - $enableDefaultEvent = $this->exampleEventService->shouldCreateExampleEvent(); |
|
| 39 | - $this->initialState->provideInitialState('create_example_event', $enableDefaultEvent); |
|
| 40 | - $this->initialState->provideInitialState( |
|
| 41 | - 'has_custom_example_event', |
|
| 42 | - $this->exampleEventService->hasCustomExampleEvent(), |
|
| 43 | - ); |
|
| 44 | - } |
|
| 37 | + if ($calendarEnabled) { |
|
| 38 | + $enableDefaultEvent = $this->exampleEventService->shouldCreateExampleEvent(); |
|
| 39 | + $this->initialState->provideInitialState('create_example_event', $enableDefaultEvent); |
|
| 40 | + $this->initialState->provideInitialState( |
|
| 41 | + 'has_custom_example_event', |
|
| 42 | + $this->exampleEventService->hasCustomExampleEvent(), |
|
| 43 | + ); |
|
| 44 | + } |
|
| 45 | 45 | |
| 46 | - if ($contactsEnabled) { |
|
| 47 | - $this->initialState->provideInitialState( |
|
| 48 | - 'enableDefaultContact', |
|
| 49 | - $this->exampleContactService->isDefaultContactEnabled(), |
|
| 50 | - ); |
|
| 51 | - $this->initialState->provideInitialState( |
|
| 52 | - 'hasCustomDefaultContact', |
|
| 53 | - $this->appConfig->getAppValueBool('hasCustomDefaultContact'), |
|
| 54 | - ); |
|
| 55 | - } |
|
| 46 | + if ($contactsEnabled) { |
|
| 47 | + $this->initialState->provideInitialState( |
|
| 48 | + 'enableDefaultContact', |
|
| 49 | + $this->exampleContactService->isDefaultContactEnabled(), |
|
| 50 | + ); |
|
| 51 | + $this->initialState->provideInitialState( |
|
| 52 | + 'hasCustomDefaultContact', |
|
| 53 | + $this->appConfig->getAppValueBool('hasCustomDefaultContact'), |
|
| 54 | + ); |
|
| 55 | + } |
|
| 56 | 56 | |
| 57 | - Util::addStyle(Application::APP_ID, 'settings-admin-example-content'); |
|
| 58 | - Util::addScript(Application::APP_ID, 'settings-admin-example-content'); |
|
| 59 | - return new TemplateResponse(Application::APP_ID, 'settings-admin-example-content'); |
|
| 60 | - } |
|
| 57 | + Util::addStyle(Application::APP_ID, 'settings-admin-example-content'); |
|
| 58 | + Util::addScript(Application::APP_ID, 'settings-admin-example-content'); |
|
| 59 | + return new TemplateResponse(Application::APP_ID, 'settings-admin-example-content'); |
|
| 60 | + } |
|
| 61 | 61 | |
| 62 | - public function getSection(): ?string { |
|
| 63 | - if (!$this->appManager->isEnabledForUser('contacts') |
|
| 64 | - && !$this->appManager->isEnabledForUser('calendar')) { |
|
| 65 | - return null; |
|
| 66 | - } |
|
| 62 | + public function getSection(): ?string { |
|
| 63 | + if (!$this->appManager->isEnabledForUser('contacts') |
|
| 64 | + && !$this->appManager->isEnabledForUser('calendar')) { |
|
| 65 | + return null; |
|
| 66 | + } |
|
| 67 | 67 | |
| 68 | - return 'groupware'; |
|
| 69 | - } |
|
| 68 | + return 'groupware'; |
|
| 69 | + } |
|
| 70 | 70 | |
| 71 | - public function getPriority(): int { |
|
| 72 | - return 10; |
|
| 73 | - } |
|
| 71 | + public function getPriority(): int { |
|
| 72 | + return 10; |
|
| 73 | + } |
|
| 74 | 74 | } |
@@ -16,47 +16,47 @@ |
||
| 16 | 16 | |
| 17 | 17 | class RefreshWebcalJobRegistrar implements IRepairStep { |
| 18 | 18 | |
| 19 | - /** |
|
| 20 | - * FixBirthdayCalendarComponent constructor. |
|
| 21 | - * |
|
| 22 | - * @param IDBConnection $connection |
|
| 23 | - * @param IJobList $jobList |
|
| 24 | - */ |
|
| 25 | - public function __construct( |
|
| 26 | - private IDBConnection $connection, |
|
| 27 | - private IJobList $jobList, |
|
| 28 | - ) { |
|
| 29 | - } |
|
| 30 | - |
|
| 31 | - /** |
|
| 32 | - * @inheritdoc |
|
| 33 | - */ |
|
| 34 | - public function getName() { |
|
| 35 | - return 'Registering background jobs to update cache for webcal calendars'; |
|
| 36 | - } |
|
| 37 | - |
|
| 38 | - /** |
|
| 39 | - * @inheritdoc |
|
| 40 | - */ |
|
| 41 | - public function run(IOutput $output): void { |
|
| 42 | - $query = $this->connection->getQueryBuilder(); |
|
| 43 | - $query->select(['principaluri', 'uri']) |
|
| 44 | - ->from('calendarsubscriptions'); |
|
| 45 | - $stmt = $query->executeQuery(); |
|
| 46 | - |
|
| 47 | - $count = 0; |
|
| 48 | - while ($row = $stmt->fetchAssociative()) { |
|
| 49 | - $args = [ |
|
| 50 | - 'principaluri' => $row['principaluri'], |
|
| 51 | - 'uri' => $row['uri'], |
|
| 52 | - ]; |
|
| 53 | - |
|
| 54 | - if (!$this->jobList->has(RefreshWebcalJob::class, $args)) { |
|
| 55 | - $this->jobList->add(RefreshWebcalJob::class, $args); |
|
| 56 | - $count++; |
|
| 57 | - } |
|
| 58 | - } |
|
| 59 | - |
|
| 60 | - $output->info("Added $count background jobs to update webcal calendars"); |
|
| 61 | - } |
|
| 19 | + /** |
|
| 20 | + * FixBirthdayCalendarComponent constructor. |
|
| 21 | + * |
|
| 22 | + * @param IDBConnection $connection |
|
| 23 | + * @param IJobList $jobList |
|
| 24 | + */ |
|
| 25 | + public function __construct( |
|
| 26 | + private IDBConnection $connection, |
|
| 27 | + private IJobList $jobList, |
|
| 28 | + ) { |
|
| 29 | + } |
|
| 30 | + |
|
| 31 | + /** |
|
| 32 | + * @inheritdoc |
|
| 33 | + */ |
|
| 34 | + public function getName() { |
|
| 35 | + return 'Registering background jobs to update cache for webcal calendars'; |
|
| 36 | + } |
|
| 37 | + |
|
| 38 | + /** |
|
| 39 | + * @inheritdoc |
|
| 40 | + */ |
|
| 41 | + public function run(IOutput $output): void { |
|
| 42 | + $query = $this->connection->getQueryBuilder(); |
|
| 43 | + $query->select(['principaluri', 'uri']) |
|
| 44 | + ->from('calendarsubscriptions'); |
|
| 45 | + $stmt = $query->executeQuery(); |
|
| 46 | + |
|
| 47 | + $count = 0; |
|
| 48 | + while ($row = $stmt->fetchAssociative()) { |
|
| 49 | + $args = [ |
|
| 50 | + 'principaluri' => $row['principaluri'], |
|
| 51 | + 'uri' => $row['uri'], |
|
| 52 | + ]; |
|
| 53 | + |
|
| 54 | + if (!$this->jobList->has(RefreshWebcalJob::class, $args)) { |
|
| 55 | + $this->jobList->add(RefreshWebcalJob::class, $args); |
|
| 56 | + $count++; |
|
| 57 | + } |
|
| 58 | + } |
|
| 59 | + |
|
| 60 | + $output->info("Added $count background jobs to update webcal calendars"); |
|
| 61 | + } |
|
| 62 | 62 | } |
@@ -16,67 +16,67 @@ |
||
| 16 | 16 | use Psr\Log\LoggerInterface; |
| 17 | 17 | |
| 18 | 18 | class BuildCalendarSearchIndexBackgroundJob extends QueuedJob { |
| 19 | - public function __construct( |
|
| 20 | - private IDBConnection $db, |
|
| 21 | - private CalDavBackend $calDavBackend, |
|
| 22 | - private LoggerInterface $logger, |
|
| 23 | - private IJobList $jobList, |
|
| 24 | - ITimeFactory $timeFactory, |
|
| 25 | - ) { |
|
| 26 | - parent::__construct($timeFactory); |
|
| 27 | - } |
|
| 19 | + public function __construct( |
|
| 20 | + private IDBConnection $db, |
|
| 21 | + private CalDavBackend $calDavBackend, |
|
| 22 | + private LoggerInterface $logger, |
|
| 23 | + private IJobList $jobList, |
|
| 24 | + ITimeFactory $timeFactory, |
|
| 25 | + ) { |
|
| 26 | + parent::__construct($timeFactory); |
|
| 27 | + } |
|
| 28 | 28 | |
| 29 | - public function run($argument) { |
|
| 30 | - $offset = (int)$argument['offset']; |
|
| 31 | - $stopAt = (int)$argument['stopAt']; |
|
| 29 | + public function run($argument) { |
|
| 30 | + $offset = (int)$argument['offset']; |
|
| 31 | + $stopAt = (int)$argument['stopAt']; |
|
| 32 | 32 | |
| 33 | - $this->logger->info('Building calendar index (' . $offset . '/' . $stopAt . ')'); |
|
| 33 | + $this->logger->info('Building calendar index (' . $offset . '/' . $stopAt . ')'); |
|
| 34 | 34 | |
| 35 | - $startTime = $this->time->getTime(); |
|
| 36 | - while (($this->time->getTime() - $startTime) < 15) { |
|
| 37 | - $offset = $this->buildIndex($offset, $stopAt); |
|
| 38 | - if ($offset >= $stopAt) { |
|
| 39 | - break; |
|
| 40 | - } |
|
| 41 | - } |
|
| 35 | + $startTime = $this->time->getTime(); |
|
| 36 | + while (($this->time->getTime() - $startTime) < 15) { |
|
| 37 | + $offset = $this->buildIndex($offset, $stopAt); |
|
| 38 | + if ($offset >= $stopAt) { |
|
| 39 | + break; |
|
| 40 | + } |
|
| 41 | + } |
|
| 42 | 42 | |
| 43 | - if ($offset >= $stopAt) { |
|
| 44 | - $this->logger->info('Building calendar index done'); |
|
| 45 | - } else { |
|
| 46 | - $this->jobList->add(self::class, [ |
|
| 47 | - 'offset' => $offset, |
|
| 48 | - 'stopAt' => $stopAt |
|
| 49 | - ]); |
|
| 50 | - $this->logger->info('New building calendar index job scheduled with offset ' . $offset); |
|
| 51 | - } |
|
| 52 | - } |
|
| 43 | + if ($offset >= $stopAt) { |
|
| 44 | + $this->logger->info('Building calendar index done'); |
|
| 45 | + } else { |
|
| 46 | + $this->jobList->add(self::class, [ |
|
| 47 | + 'offset' => $offset, |
|
| 48 | + 'stopAt' => $stopAt |
|
| 49 | + ]); |
|
| 50 | + $this->logger->info('New building calendar index job scheduled with offset ' . $offset); |
|
| 51 | + } |
|
| 52 | + } |
|
| 53 | 53 | |
| 54 | - /** |
|
| 55 | - * @param int $offset |
|
| 56 | - * @param int $stopAt |
|
| 57 | - * @return int |
|
| 58 | - */ |
|
| 59 | - private function buildIndex(int $offset, int $stopAt): int { |
|
| 60 | - $query = $this->db->getQueryBuilder(); |
|
| 61 | - $query->select(['id', 'calendarid', 'uri', 'calendardata']) |
|
| 62 | - ->from('calendarobjects') |
|
| 63 | - ->where($query->expr()->lte('id', $query->createNamedParameter($stopAt))) |
|
| 64 | - ->andWhere($query->expr()->gt('id', $query->createNamedParameter($offset))) |
|
| 65 | - ->orderBy('id', 'ASC') |
|
| 66 | - ->setMaxResults(500); |
|
| 54 | + /** |
|
| 55 | + * @param int $offset |
|
| 56 | + * @param int $stopAt |
|
| 57 | + * @return int |
|
| 58 | + */ |
|
| 59 | + private function buildIndex(int $offset, int $stopAt): int { |
|
| 60 | + $query = $this->db->getQueryBuilder(); |
|
| 61 | + $query->select(['id', 'calendarid', 'uri', 'calendardata']) |
|
| 62 | + ->from('calendarobjects') |
|
| 63 | + ->where($query->expr()->lte('id', $query->createNamedParameter($stopAt))) |
|
| 64 | + ->andWhere($query->expr()->gt('id', $query->createNamedParameter($offset))) |
|
| 65 | + ->orderBy('id', 'ASC') |
|
| 66 | + ->setMaxResults(500); |
|
| 67 | 67 | |
| 68 | - $result = $query->executeQuery(); |
|
| 69 | - while ($row = $result->fetchAssociative()) { |
|
| 70 | - $offset = $row['id']; |
|
| 68 | + $result = $query->executeQuery(); |
|
| 69 | + while ($row = $result->fetchAssociative()) { |
|
| 70 | + $offset = $row['id']; |
|
| 71 | 71 | |
| 72 | - $calendarData = $row['calendardata']; |
|
| 73 | - if (is_resource($calendarData)) { |
|
| 74 | - $calendarData = stream_get_contents($calendarData); |
|
| 75 | - } |
|
| 72 | + $calendarData = $row['calendardata']; |
|
| 73 | + if (is_resource($calendarData)) { |
|
| 74 | + $calendarData = stream_get_contents($calendarData); |
|
| 75 | + } |
|
| 76 | 76 | |
| 77 | - $this->calDavBackend->updateProperties($row['calendarid'], $row['uri'], $calendarData); |
|
| 78 | - } |
|
| 77 | + $this->calDavBackend->updateProperties($row['calendarid'], $row['uri'], $calendarData); |
|
| 78 | + } |
|
| 79 | 79 | |
| 80 | - return $offset; |
|
| 81 | - } |
|
| 80 | + return $offset; |
|
| 81 | + } |
|
| 82 | 82 | } |
@@ -20,72 +20,72 @@ |
||
| 20 | 20 | |
| 21 | 21 | class Version1025Date20240308063933 extends SimpleMigrationStep { |
| 22 | 22 | |
| 23 | - public function __construct( |
|
| 24 | - private IAppConfig $appConfig, |
|
| 25 | - private IDBConnection $db, |
|
| 26 | - ) { |
|
| 27 | - } |
|
| 28 | - |
|
| 29 | - /** |
|
| 30 | - * @param IOutput $output |
|
| 31 | - * @param Closure(): ISchemaWrapper $schemaClosure |
|
| 32 | - * @param array $options |
|
| 33 | - * @return null|ISchemaWrapper |
|
| 34 | - */ |
|
| 35 | - public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { |
|
| 36 | - /** @var ISchemaWrapper $schema */ |
|
| 37 | - $schema = $schemaClosure(); |
|
| 38 | - |
|
| 39 | - foreach (['addressbookchanges', 'calendarchanges'] as $tableName) { |
|
| 40 | - $table = $schema->getTable($tableName); |
|
| 41 | - if (!$table->hasColumn('created_at')) { |
|
| 42 | - $table->addColumn('created_at', Types::INTEGER, [ |
|
| 43 | - 'notnull' => true, |
|
| 44 | - 'length' => 4, |
|
| 45 | - 'default' => 0, |
|
| 46 | - ]); |
|
| 47 | - } |
|
| 48 | - } |
|
| 49 | - |
|
| 50 | - return $schema; |
|
| 51 | - } |
|
| 52 | - |
|
| 53 | - public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void { |
|
| 54 | - // The threshold is higher than the default of \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob |
|
| 55 | - // but small enough to fit into a cluster transaction size. |
|
| 56 | - // For a 50k users instance that would still keep 10 changes on average. |
|
| 57 | - $limit = max(1, (int)$this->appConfig->getAppValue('totalNumberOfSyncTokensToKeep', '500000')); |
|
| 58 | - |
|
| 59 | - foreach (['addressbookchanges', 'calendarchanges'] as $tableName) { |
|
| 60 | - $thresholdSelect = $this->db->getQueryBuilder(); |
|
| 61 | - $thresholdSelect->select('id') |
|
| 62 | - ->from($tableName) |
|
| 63 | - ->orderBy('id', 'desc') |
|
| 64 | - ->setFirstResult($limit) |
|
| 65 | - ->setMaxResults(1); |
|
| 66 | - $oldestIdResult = $thresholdSelect->executeQuery(); |
|
| 67 | - $oldestId = $oldestIdResult->fetchOne(); |
|
| 68 | - $oldestIdResult->closeCursor(); |
|
| 69 | - |
|
| 70 | - $qb = $this->db->getQueryBuilder(); |
|
| 71 | - |
|
| 72 | - $update = $qb->update($tableName) |
|
| 73 | - ->set('created_at', $qb->createNamedParameter(time(), IQueryBuilder::PARAM_INT)) |
|
| 74 | - ->where( |
|
| 75 | - $qb->expr()->eq('created_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)), |
|
| 76 | - ); |
|
| 77 | - |
|
| 78 | - // If there is a lot of data we only set timestamp for the most recent rows |
|
| 79 | - // because the rest will be deleted by \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob |
|
| 80 | - // anyway. |
|
| 81 | - if ($oldestId !== false) { |
|
| 82 | - $update->andWhere($qb->expr()->gt('id', $qb->createNamedParameter($oldestId, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)); |
|
| 83 | - } |
|
| 84 | - |
|
| 85 | - $updated = $update->executeStatement(); |
|
| 86 | - |
|
| 87 | - $output->debug('Added a default creation timestamp to ' . $updated . ' rows in ' . $tableName); |
|
| 88 | - } |
|
| 89 | - } |
|
| 23 | + public function __construct( |
|
| 24 | + private IAppConfig $appConfig, |
|
| 25 | + private IDBConnection $db, |
|
| 26 | + ) { |
|
| 27 | + } |
|
| 28 | + |
|
| 29 | + /** |
|
| 30 | + * @param IOutput $output |
|
| 31 | + * @param Closure(): ISchemaWrapper $schemaClosure |
|
| 32 | + * @param array $options |
|
| 33 | + * @return null|ISchemaWrapper |
|
| 34 | + */ |
|
| 35 | + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { |
|
| 36 | + /** @var ISchemaWrapper $schema */ |
|
| 37 | + $schema = $schemaClosure(); |
|
| 38 | + |
|
| 39 | + foreach (['addressbookchanges', 'calendarchanges'] as $tableName) { |
|
| 40 | + $table = $schema->getTable($tableName); |
|
| 41 | + if (!$table->hasColumn('created_at')) { |
|
| 42 | + $table->addColumn('created_at', Types::INTEGER, [ |
|
| 43 | + 'notnull' => true, |
|
| 44 | + 'length' => 4, |
|
| 45 | + 'default' => 0, |
|
| 46 | + ]); |
|
| 47 | + } |
|
| 48 | + } |
|
| 49 | + |
|
| 50 | + return $schema; |
|
| 51 | + } |
|
| 52 | + |
|
| 53 | + public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void { |
|
| 54 | + // The threshold is higher than the default of \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob |
|
| 55 | + // but small enough to fit into a cluster transaction size. |
|
| 56 | + // For a 50k users instance that would still keep 10 changes on average. |
|
| 57 | + $limit = max(1, (int)$this->appConfig->getAppValue('totalNumberOfSyncTokensToKeep', '500000')); |
|
| 58 | + |
|
| 59 | + foreach (['addressbookchanges', 'calendarchanges'] as $tableName) { |
|
| 60 | + $thresholdSelect = $this->db->getQueryBuilder(); |
|
| 61 | + $thresholdSelect->select('id') |
|
| 62 | + ->from($tableName) |
|
| 63 | + ->orderBy('id', 'desc') |
|
| 64 | + ->setFirstResult($limit) |
|
| 65 | + ->setMaxResults(1); |
|
| 66 | + $oldestIdResult = $thresholdSelect->executeQuery(); |
|
| 67 | + $oldestId = $oldestIdResult->fetchOne(); |
|
| 68 | + $oldestIdResult->closeCursor(); |
|
| 69 | + |
|
| 70 | + $qb = $this->db->getQueryBuilder(); |
|
| 71 | + |
|
| 72 | + $update = $qb->update($tableName) |
|
| 73 | + ->set('created_at', $qb->createNamedParameter(time(), IQueryBuilder::PARAM_INT)) |
|
| 74 | + ->where( |
|
| 75 | + $qb->expr()->eq('created_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)), |
|
| 76 | + ); |
|
| 77 | + |
|
| 78 | + // If there is a lot of data we only set timestamp for the most recent rows |
|
| 79 | + // because the rest will be deleted by \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob |
|
| 80 | + // anyway. |
|
| 81 | + if ($oldestId !== false) { |
|
| 82 | + $update->andWhere($qb->expr()->gt('id', $qb->createNamedParameter($oldestId, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)); |
|
| 83 | + } |
|
| 84 | + |
|
| 85 | + $updated = $update->executeStatement(); |
|
| 86 | + |
|
| 87 | + $output->debug('Added a default creation timestamp to ' . $updated . ' rows in ' . $tableName); |
|
| 88 | + } |
|
| 89 | + } |
|
| 90 | 90 | |
| 91 | 91 | } |
@@ -15,102 +15,102 @@ |
||
| 15 | 15 | |
| 16 | 16 | class RemoveClassifiedEventActivity implements IRepairStep { |
| 17 | 17 | |
| 18 | - public function __construct( |
|
| 19 | - private IDBConnection $connection, |
|
| 20 | - ) { |
|
| 21 | - } |
|
| 22 | - |
|
| 23 | - /** |
|
| 24 | - * @inheritdoc |
|
| 25 | - */ |
|
| 26 | - public function getName() { |
|
| 27 | - return 'Remove activity entries of private events'; |
|
| 28 | - } |
|
| 29 | - |
|
| 30 | - /** |
|
| 31 | - * @inheritdoc |
|
| 32 | - */ |
|
| 33 | - public function run(IOutput $output) { |
|
| 34 | - if (!$this->connection->tableExists('activity')) { |
|
| 35 | - return; |
|
| 36 | - } |
|
| 37 | - |
|
| 38 | - $deletedEvents = $this->removePrivateEventActivity(); |
|
| 39 | - $deletedEvents += $this->removeConfidentialUncensoredEventActivity(); |
|
| 40 | - |
|
| 41 | - $output->info("Removed $deletedEvents activity entries"); |
|
| 42 | - } |
|
| 43 | - |
|
| 44 | - protected function removePrivateEventActivity(): int { |
|
| 45 | - $deletedEvents = 0; |
|
| 46 | - |
|
| 47 | - $delete = $this->connection->getQueryBuilder(); |
|
| 48 | - $delete->delete('activity') |
|
| 49 | - ->where($delete->expr()->neq('affecteduser', $delete->createParameter('owner'))) |
|
| 50 | - ->andWhere($delete->expr()->eq('object_type', $delete->createParameter('type'))) |
|
| 51 | - ->andWhere($delete->expr()->eq('object_id', $delete->createParameter('calendar_id'))) |
|
| 52 | - ->andWhere($delete->expr()->like('subjectparams', $delete->createParameter('event_uid'))); |
|
| 53 | - |
|
| 54 | - $query = $this->connection->getQueryBuilder(); |
|
| 55 | - $query->select('c.principaluri', 'o.calendarid', 'o.uid') |
|
| 56 | - ->from('calendarobjects', 'o') |
|
| 57 | - ->leftJoin('o', 'calendars', 'c', $query->expr()->eq('c.id', 'o.calendarid')) |
|
| 58 | - ->where($query->expr()->eq('o.classification', $query->createNamedParameter(CalDavBackend::CLASSIFICATION_PRIVATE))); |
|
| 59 | - $result = $query->executeQuery(); |
|
| 60 | - |
|
| 61 | - while ($row = $result->fetchAssociative()) { |
|
| 62 | - if ($row['principaluri'] === null) { |
|
| 63 | - continue; |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - $delete->setParameter('owner', $this->getPrincipal($row['principaluri'])) |
|
| 67 | - ->setParameter('type', 'calendar') |
|
| 68 | - ->setParameter('calendar_id', $row['calendarid']) |
|
| 69 | - ->setParameter('event_uid', '%' . $this->connection->escapeLikeParameter('{"id":"' . $row['uid'] . '"') . '%'); |
|
| 70 | - $deletedEvents += $delete->executeStatement(); |
|
| 71 | - } |
|
| 72 | - $result->closeCursor(); |
|
| 73 | - |
|
| 74 | - return $deletedEvents; |
|
| 75 | - } |
|
| 76 | - |
|
| 77 | - protected function removeConfidentialUncensoredEventActivity(): int { |
|
| 78 | - $deletedEvents = 0; |
|
| 79 | - |
|
| 80 | - $delete = $this->connection->getQueryBuilder(); |
|
| 81 | - $delete->delete('activity') |
|
| 82 | - ->where($delete->expr()->neq('affecteduser', $delete->createParameter('owner'))) |
|
| 83 | - ->andWhere($delete->expr()->eq('object_type', $delete->createParameter('type'))) |
|
| 84 | - ->andWhere($delete->expr()->eq('object_id', $delete->createParameter('calendar_id'))) |
|
| 85 | - ->andWhere($delete->expr()->like('subjectparams', $delete->createParameter('event_uid'))) |
|
| 86 | - ->andWhere($delete->expr()->notLike('subjectparams', $delete->createParameter('filtered_name'))); |
|
| 87 | - |
|
| 88 | - $query = $this->connection->getQueryBuilder(); |
|
| 89 | - $query->select('c.principaluri', 'o.calendarid', 'o.uid') |
|
| 90 | - ->from('calendarobjects', 'o') |
|
| 91 | - ->leftJoin('o', 'calendars', 'c', $query->expr()->eq('c.id', 'o.calendarid')) |
|
| 92 | - ->where($query->expr()->eq('o.classification', $query->createNamedParameter(CalDavBackend::CLASSIFICATION_CONFIDENTIAL))); |
|
| 93 | - $result = $query->executeQuery(); |
|
| 94 | - |
|
| 95 | - while ($row = $result->fetchAssociative()) { |
|
| 96 | - if ($row['principaluri'] === null) { |
|
| 97 | - continue; |
|
| 98 | - } |
|
| 99 | - |
|
| 100 | - $delete->setParameter('owner', $this->getPrincipal($row['principaluri'])) |
|
| 101 | - ->setParameter('type', 'calendar') |
|
| 102 | - ->setParameter('calendar_id', $row['calendarid']) |
|
| 103 | - ->setParameter('event_uid', '%' . $this->connection->escapeLikeParameter('{"id":"' . $row['uid'] . '"') . '%') |
|
| 104 | - ->setParameter('filtered_name', '%' . $this->connection->escapeLikeParameter('{"id":"' . $row['uid'] . '","name":"Busy"') . '%'); |
|
| 105 | - $deletedEvents += $delete->executeStatement(); |
|
| 106 | - } |
|
| 107 | - $result->closeCursor(); |
|
| 108 | - |
|
| 109 | - return $deletedEvents; |
|
| 110 | - } |
|
| 111 | - |
|
| 112 | - protected function getPrincipal(string $principalUri): string { |
|
| 113 | - $uri = explode('/', $principalUri); |
|
| 114 | - return array_pop($uri); |
|
| 115 | - } |
|
| 18 | + public function __construct( |
|
| 19 | + private IDBConnection $connection, |
|
| 20 | + ) { |
|
| 21 | + } |
|
| 22 | + |
|
| 23 | + /** |
|
| 24 | + * @inheritdoc |
|
| 25 | + */ |
|
| 26 | + public function getName() { |
|
| 27 | + return 'Remove activity entries of private events'; |
|
| 28 | + } |
|
| 29 | + |
|
| 30 | + /** |
|
| 31 | + * @inheritdoc |
|
| 32 | + */ |
|
| 33 | + public function run(IOutput $output) { |
|
| 34 | + if (!$this->connection->tableExists('activity')) { |
|
| 35 | + return; |
|
| 36 | + } |
|
| 37 | + |
|
| 38 | + $deletedEvents = $this->removePrivateEventActivity(); |
|
| 39 | + $deletedEvents += $this->removeConfidentialUncensoredEventActivity(); |
|
| 40 | + |
|
| 41 | + $output->info("Removed $deletedEvents activity entries"); |
|
| 42 | + } |
|
| 43 | + |
|
| 44 | + protected function removePrivateEventActivity(): int { |
|
| 45 | + $deletedEvents = 0; |
|
| 46 | + |
|
| 47 | + $delete = $this->connection->getQueryBuilder(); |
|
| 48 | + $delete->delete('activity') |
|
| 49 | + ->where($delete->expr()->neq('affecteduser', $delete->createParameter('owner'))) |
|
| 50 | + ->andWhere($delete->expr()->eq('object_type', $delete->createParameter('type'))) |
|
| 51 | + ->andWhere($delete->expr()->eq('object_id', $delete->createParameter('calendar_id'))) |
|
| 52 | + ->andWhere($delete->expr()->like('subjectparams', $delete->createParameter('event_uid'))); |
|
| 53 | + |
|
| 54 | + $query = $this->connection->getQueryBuilder(); |
|
| 55 | + $query->select('c.principaluri', 'o.calendarid', 'o.uid') |
|
| 56 | + ->from('calendarobjects', 'o') |
|
| 57 | + ->leftJoin('o', 'calendars', 'c', $query->expr()->eq('c.id', 'o.calendarid')) |
|
| 58 | + ->where($query->expr()->eq('o.classification', $query->createNamedParameter(CalDavBackend::CLASSIFICATION_PRIVATE))); |
|
| 59 | + $result = $query->executeQuery(); |
|
| 60 | + |
|
| 61 | + while ($row = $result->fetchAssociative()) { |
|
| 62 | + if ($row['principaluri'] === null) { |
|
| 63 | + continue; |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + $delete->setParameter('owner', $this->getPrincipal($row['principaluri'])) |
|
| 67 | + ->setParameter('type', 'calendar') |
|
| 68 | + ->setParameter('calendar_id', $row['calendarid']) |
|
| 69 | + ->setParameter('event_uid', '%' . $this->connection->escapeLikeParameter('{"id":"' . $row['uid'] . '"') . '%'); |
|
| 70 | + $deletedEvents += $delete->executeStatement(); |
|
| 71 | + } |
|
| 72 | + $result->closeCursor(); |
|
| 73 | + |
|
| 74 | + return $deletedEvents; |
|
| 75 | + } |
|
| 76 | + |
|
| 77 | + protected function removeConfidentialUncensoredEventActivity(): int { |
|
| 78 | + $deletedEvents = 0; |
|
| 79 | + |
|
| 80 | + $delete = $this->connection->getQueryBuilder(); |
|
| 81 | + $delete->delete('activity') |
|
| 82 | + ->where($delete->expr()->neq('affecteduser', $delete->createParameter('owner'))) |
|
| 83 | + ->andWhere($delete->expr()->eq('object_type', $delete->createParameter('type'))) |
|
| 84 | + ->andWhere($delete->expr()->eq('object_id', $delete->createParameter('calendar_id'))) |
|
| 85 | + ->andWhere($delete->expr()->like('subjectparams', $delete->createParameter('event_uid'))) |
|
| 86 | + ->andWhere($delete->expr()->notLike('subjectparams', $delete->createParameter('filtered_name'))); |
|
| 87 | + |
|
| 88 | + $query = $this->connection->getQueryBuilder(); |
|
| 89 | + $query->select('c.principaluri', 'o.calendarid', 'o.uid') |
|
| 90 | + ->from('calendarobjects', 'o') |
|
| 91 | + ->leftJoin('o', 'calendars', 'c', $query->expr()->eq('c.id', 'o.calendarid')) |
|
| 92 | + ->where($query->expr()->eq('o.classification', $query->createNamedParameter(CalDavBackend::CLASSIFICATION_CONFIDENTIAL))); |
|
| 93 | + $result = $query->executeQuery(); |
|
| 94 | + |
|
| 95 | + while ($row = $result->fetchAssociative()) { |
|
| 96 | + if ($row['principaluri'] === null) { |
|
| 97 | + continue; |
|
| 98 | + } |
|
| 99 | + |
|
| 100 | + $delete->setParameter('owner', $this->getPrincipal($row['principaluri'])) |
|
| 101 | + ->setParameter('type', 'calendar') |
|
| 102 | + ->setParameter('calendar_id', $row['calendarid']) |
|
| 103 | + ->setParameter('event_uid', '%' . $this->connection->escapeLikeParameter('{"id":"' . $row['uid'] . '"') . '%') |
|
| 104 | + ->setParameter('filtered_name', '%' . $this->connection->escapeLikeParameter('{"id":"' . $row['uid'] . '","name":"Busy"') . '%'); |
|
| 105 | + $deletedEvents += $delete->executeStatement(); |
|
| 106 | + } |
|
| 107 | + $result->closeCursor(); |
|
| 108 | + |
|
| 109 | + return $deletedEvents; |
|
| 110 | + } |
|
| 111 | + |
|
| 112 | + protected function getPrincipal(string $principalUri): string { |
|
| 113 | + $uri = explode('/', $principalUri); |
|
| 114 | + return array_pop($uri); |
|
| 115 | + } |
|
| 116 | 116 | } |
@@ -15,109 +15,109 @@ |
||
| 15 | 15 | use OCP\Migration\IRepairStep; |
| 16 | 16 | |
| 17 | 17 | class RemoveDeletedUsersCalendarSubscriptions implements IRepairStep { |
| 18 | - /** @var int */ |
|
| 19 | - private $progress = 0; |
|
| 20 | - |
|
| 21 | - /** @var int[] */ |
|
| 22 | - private $orphanSubscriptionIds = []; |
|
| 23 | - |
|
| 24 | - private const SUBSCRIPTIONS_CHUNK_SIZE = 1000; |
|
| 25 | - |
|
| 26 | - public function __construct( |
|
| 27 | - private IDBConnection $connection, |
|
| 28 | - private IUserManager $userManager, |
|
| 29 | - ) { |
|
| 30 | - } |
|
| 31 | - |
|
| 32 | - /** |
|
| 33 | - * @inheritdoc |
|
| 34 | - */ |
|
| 35 | - public function getName(): string { |
|
| 36 | - return 'Clean up old calendar subscriptions from deleted users that were not cleaned-up'; |
|
| 37 | - } |
|
| 38 | - |
|
| 39 | - /** |
|
| 40 | - * @inheritdoc |
|
| 41 | - */ |
|
| 42 | - public function run(IOutput $output) { |
|
| 43 | - $nbSubscriptions = $this->countSubscriptions(); |
|
| 44 | - |
|
| 45 | - $output->startProgress($nbSubscriptions); |
|
| 46 | - |
|
| 47 | - while ($this->progress < $nbSubscriptions) { |
|
| 48 | - $this->checkSubscriptions(); |
|
| 49 | - |
|
| 50 | - $this->progress += self::SUBSCRIPTIONS_CHUNK_SIZE; |
|
| 51 | - $output->advance(min(self::SUBSCRIPTIONS_CHUNK_SIZE, $nbSubscriptions)); |
|
| 52 | - } |
|
| 53 | - $output->finishProgress(); |
|
| 54 | - $this->deleteOrphanSubscriptions(); |
|
| 55 | - |
|
| 56 | - $output->info(sprintf('%d calendar subscriptions without an user have been cleaned up', count($this->orphanSubscriptionIds))); |
|
| 57 | - } |
|
| 58 | - |
|
| 59 | - /** |
|
| 60 | - * @throws Exception |
|
| 61 | - */ |
|
| 62 | - private function countSubscriptions(): int { |
|
| 63 | - $qb = $this->connection->getQueryBuilder(); |
|
| 64 | - $query = $qb->select($qb->func()->count('*')) |
|
| 65 | - ->from('calendarsubscriptions'); |
|
| 66 | - |
|
| 67 | - $result = $query->executeQuery(); |
|
| 68 | - $count = $result->fetchOne(); |
|
| 69 | - $result->closeCursor(); |
|
| 70 | - |
|
| 71 | - if ($count !== false) { |
|
| 72 | - $count = (int)$count; |
|
| 73 | - } else { |
|
| 74 | - $count = 0; |
|
| 75 | - } |
|
| 76 | - |
|
| 77 | - return $count; |
|
| 78 | - } |
|
| 79 | - |
|
| 80 | - /** |
|
| 81 | - * @throws Exception |
|
| 82 | - */ |
|
| 83 | - private function checkSubscriptions(): void { |
|
| 84 | - $qb = $this->connection->getQueryBuilder(); |
|
| 85 | - $query = $qb->selectDistinct(['id', 'principaluri']) |
|
| 86 | - ->from('calendarsubscriptions') |
|
| 87 | - ->setMaxResults(self::SUBSCRIPTIONS_CHUNK_SIZE) |
|
| 88 | - ->setFirstResult($this->progress); |
|
| 89 | - |
|
| 90 | - $result = $query->executeQuery(); |
|
| 91 | - while ($row = $result->fetchAssociative()) { |
|
| 92 | - $username = $this->getPrincipal($row['principaluri']); |
|
| 93 | - if (!$this->userManager->userExists($username)) { |
|
| 94 | - $this->orphanSubscriptionIds[] = (int)$row['id']; |
|
| 95 | - } |
|
| 96 | - } |
|
| 97 | - $result->closeCursor(); |
|
| 98 | - } |
|
| 99 | - |
|
| 100 | - /** |
|
| 101 | - * @throws Exception |
|
| 102 | - */ |
|
| 103 | - private function deleteOrphanSubscriptions(): void { |
|
| 104 | - foreach ($this->orphanSubscriptionIds as $orphanSubscriptionID) { |
|
| 105 | - $this->deleteOrphanSubscription($orphanSubscriptionID); |
|
| 106 | - } |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - /** |
|
| 110 | - * @throws Exception |
|
| 111 | - */ |
|
| 112 | - private function deleteOrphanSubscription(int $orphanSubscriptionID): void { |
|
| 113 | - $qb = $this->connection->getQueryBuilder(); |
|
| 114 | - $qb->delete('calendarsubscriptions') |
|
| 115 | - ->where($qb->expr()->eq('id', $qb->createNamedParameter($orphanSubscriptionID))) |
|
| 116 | - ->executeStatement(); |
|
| 117 | - } |
|
| 118 | - |
|
| 119 | - private function getPrincipal(string $principalUri): string { |
|
| 120 | - $uri = explode('/', $principalUri); |
|
| 121 | - return array_pop($uri); |
|
| 122 | - } |
|
| 18 | + /** @var int */ |
|
| 19 | + private $progress = 0; |
|
| 20 | + |
|
| 21 | + /** @var int[] */ |
|
| 22 | + private $orphanSubscriptionIds = []; |
|
| 23 | + |
|
| 24 | + private const SUBSCRIPTIONS_CHUNK_SIZE = 1000; |
|
| 25 | + |
|
| 26 | + public function __construct( |
|
| 27 | + private IDBConnection $connection, |
|
| 28 | + private IUserManager $userManager, |
|
| 29 | + ) { |
|
| 30 | + } |
|
| 31 | + |
|
| 32 | + /** |
|
| 33 | + * @inheritdoc |
|
| 34 | + */ |
|
| 35 | + public function getName(): string { |
|
| 36 | + return 'Clean up old calendar subscriptions from deleted users that were not cleaned-up'; |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | + /** |
|
| 40 | + * @inheritdoc |
|
| 41 | + */ |
|
| 42 | + public function run(IOutput $output) { |
|
| 43 | + $nbSubscriptions = $this->countSubscriptions(); |
|
| 44 | + |
|
| 45 | + $output->startProgress($nbSubscriptions); |
|
| 46 | + |
|
| 47 | + while ($this->progress < $nbSubscriptions) { |
|
| 48 | + $this->checkSubscriptions(); |
|
| 49 | + |
|
| 50 | + $this->progress += self::SUBSCRIPTIONS_CHUNK_SIZE; |
|
| 51 | + $output->advance(min(self::SUBSCRIPTIONS_CHUNK_SIZE, $nbSubscriptions)); |
|
| 52 | + } |
|
| 53 | + $output->finishProgress(); |
|
| 54 | + $this->deleteOrphanSubscriptions(); |
|
| 55 | + |
|
| 56 | + $output->info(sprintf('%d calendar subscriptions without an user have been cleaned up', count($this->orphanSubscriptionIds))); |
|
| 57 | + } |
|
| 58 | + |
|
| 59 | + /** |
|
| 60 | + * @throws Exception |
|
| 61 | + */ |
|
| 62 | + private function countSubscriptions(): int { |
|
| 63 | + $qb = $this->connection->getQueryBuilder(); |
|
| 64 | + $query = $qb->select($qb->func()->count('*')) |
|
| 65 | + ->from('calendarsubscriptions'); |
|
| 66 | + |
|
| 67 | + $result = $query->executeQuery(); |
|
| 68 | + $count = $result->fetchOne(); |
|
| 69 | + $result->closeCursor(); |
|
| 70 | + |
|
| 71 | + if ($count !== false) { |
|
| 72 | + $count = (int)$count; |
|
| 73 | + } else { |
|
| 74 | + $count = 0; |
|
| 75 | + } |
|
| 76 | + |
|
| 77 | + return $count; |
|
| 78 | + } |
|
| 79 | + |
|
| 80 | + /** |
|
| 81 | + * @throws Exception |
|
| 82 | + */ |
|
| 83 | + private function checkSubscriptions(): void { |
|
| 84 | + $qb = $this->connection->getQueryBuilder(); |
|
| 85 | + $query = $qb->selectDistinct(['id', 'principaluri']) |
|
| 86 | + ->from('calendarsubscriptions') |
|
| 87 | + ->setMaxResults(self::SUBSCRIPTIONS_CHUNK_SIZE) |
|
| 88 | + ->setFirstResult($this->progress); |
|
| 89 | + |
|
| 90 | + $result = $query->executeQuery(); |
|
| 91 | + while ($row = $result->fetchAssociative()) { |
|
| 92 | + $username = $this->getPrincipal($row['principaluri']); |
|
| 93 | + if (!$this->userManager->userExists($username)) { |
|
| 94 | + $this->orphanSubscriptionIds[] = (int)$row['id']; |
|
| 95 | + } |
|
| 96 | + } |
|
| 97 | + $result->closeCursor(); |
|
| 98 | + } |
|
| 99 | + |
|
| 100 | + /** |
|
| 101 | + * @throws Exception |
|
| 102 | + */ |
|
| 103 | + private function deleteOrphanSubscriptions(): void { |
|
| 104 | + foreach ($this->orphanSubscriptionIds as $orphanSubscriptionID) { |
|
| 105 | + $this->deleteOrphanSubscription($orphanSubscriptionID); |
|
| 106 | + } |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + /** |
|
| 110 | + * @throws Exception |
|
| 111 | + */ |
|
| 112 | + private function deleteOrphanSubscription(int $orphanSubscriptionID): void { |
|
| 113 | + $qb = $this->connection->getQueryBuilder(); |
|
| 114 | + $qb->delete('calendarsubscriptions') |
|
| 115 | + ->where($qb->expr()->eq('id', $qb->createNamedParameter($orphanSubscriptionID))) |
|
| 116 | + ->executeStatement(); |
|
| 117 | + } |
|
| 118 | + |
|
| 119 | + private function getPrincipal(string $principalUri): string { |
|
| 120 | + $uri = explode('/', $principalUri); |
|
| 121 | + return array_pop($uri); |
|
| 122 | + } |
|
| 123 | 123 | } |
@@ -69,7 +69,7 @@ discard block |
||
| 69 | 69 | $result->closeCursor(); |
| 70 | 70 | |
| 71 | 71 | if ($count !== false) { |
| 72 | - $count = (int)$count; |
|
| 72 | + $count = (int) $count; |
|
| 73 | 73 | } else { |
| 74 | 74 | $count = 0; |
| 75 | 75 | } |
@@ -91,7 +91,7 @@ discard block |
||
| 91 | 91 | while ($row = $result->fetchAssociative()) { |
| 92 | 92 | $username = $this->getPrincipal($row['principaluri']); |
| 93 | 93 | if (!$this->userManager->userExists($username)) { |
| 94 | - $this->orphanSubscriptionIds[] = (int)$row['id']; |
|
| 94 | + $this->orphanSubscriptionIds[] = (int) $row['id']; |
|
| 95 | 95 | } |
| 96 | 96 | } |
| 97 | 97 | $result->closeCursor(); |
@@ -16,105 +16,105 @@ |
||
| 16 | 16 | |
| 17 | 17 | class CalDAVRemoveEmptyValue implements IRepairStep { |
| 18 | 18 | |
| 19 | - public function __construct( |
|
| 20 | - private IDBConnection $db, |
|
| 21 | - private CalDavBackend $calDavBackend, |
|
| 22 | - private LoggerInterface $logger, |
|
| 23 | - ) { |
|
| 24 | - } |
|
| 25 | - |
|
| 26 | - public function getName() { |
|
| 27 | - return 'Fix broken values of calendar objects'; |
|
| 28 | - } |
|
| 29 | - |
|
| 30 | - public function run(IOutput $output) { |
|
| 31 | - $pattern = ';VALUE=:'; |
|
| 32 | - $count = $warnings = 0; |
|
| 33 | - |
|
| 34 | - $objects = $this->getInvalidObjects($pattern); |
|
| 35 | - |
|
| 36 | - $output->startProgress(count($objects)); |
|
| 37 | - foreach ($objects as $row) { |
|
| 38 | - $calObject = $this->calDavBackend->getCalendarObject((int)$row['calendarid'], $row['uri']); |
|
| 39 | - $data = preg_replace('/' . $pattern . '/', ':', $calObject['calendardata']); |
|
| 40 | - |
|
| 41 | - if ($data !== $calObject['calendardata']) { |
|
| 42 | - $output->advance(); |
|
| 43 | - |
|
| 44 | - try { |
|
| 45 | - $this->calDavBackend->getDenormalizedData($data); |
|
| 46 | - } catch (InvalidDataException $e) { |
|
| 47 | - $this->logger->info('Calendar object for calendar {cal} with uri {uri} still invalid', [ |
|
| 48 | - 'app' => 'dav', |
|
| 49 | - 'cal' => (int)$row['calendarid'], |
|
| 50 | - 'uri' => $row['uri'], |
|
| 51 | - ]); |
|
| 52 | - $warnings++; |
|
| 53 | - continue; |
|
| 54 | - } |
|
| 55 | - |
|
| 56 | - $this->calDavBackend->updateCalendarObject((int)$row['calendarid'], $row['uri'], $data); |
|
| 57 | - $count++; |
|
| 58 | - } |
|
| 59 | - } |
|
| 60 | - $output->finishProgress(); |
|
| 61 | - |
|
| 62 | - if ($warnings > 0) { |
|
| 63 | - $output->warning(sprintf('%d events could not be updated, see log file for more information', $warnings)); |
|
| 64 | - } |
|
| 65 | - if ($count > 0) { |
|
| 66 | - $output->info(sprintf('Updated %d events', $count)); |
|
| 67 | - } |
|
| 68 | - } |
|
| 69 | - |
|
| 70 | - protected function getInvalidObjects($pattern) { |
|
| 71 | - if ($this->db->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { |
|
| 72 | - $rows = []; |
|
| 73 | - $chunkSize = 500; |
|
| 74 | - $query = $this->db->getQueryBuilder(); |
|
| 75 | - $query->select($query->func()->count('*', 'num_entries')) |
|
| 76 | - ->from('calendarobjects'); |
|
| 77 | - $result = $query->executeQuery(); |
|
| 78 | - $count = $result->fetchOne(); |
|
| 79 | - $result->closeCursor(); |
|
| 80 | - |
|
| 81 | - $numChunks = ceil($count / $chunkSize); |
|
| 82 | - |
|
| 83 | - $query = $this->db->getQueryBuilder(); |
|
| 84 | - $query->select(['calendarid', 'uri', 'calendardata']) |
|
| 85 | - ->from('calendarobjects') |
|
| 86 | - ->setMaxResults($chunkSize); |
|
| 87 | - for ($chunk = 0; $chunk < $numChunks; $chunk++) { |
|
| 88 | - $query->setFirstResult($chunk * $chunkSize); |
|
| 89 | - $result = $query->executeQuery(); |
|
| 90 | - |
|
| 91 | - while ($row = $result->fetchAssociative()) { |
|
| 92 | - if (mb_strpos($row['calendardata'], $pattern) !== false) { |
|
| 93 | - unset($row['calendardata']); |
|
| 94 | - $rows[] = $row; |
|
| 95 | - } |
|
| 96 | - } |
|
| 97 | - $result->closeCursor(); |
|
| 98 | - } |
|
| 99 | - return $rows; |
|
| 100 | - } |
|
| 101 | - |
|
| 102 | - $query = $this->db->getQueryBuilder(); |
|
| 103 | - $query->select(['calendarid', 'uri']) |
|
| 104 | - ->from('calendarobjects') |
|
| 105 | - ->where($query->expr()->like( |
|
| 106 | - 'calendardata', |
|
| 107 | - $query->createNamedParameter( |
|
| 108 | - '%' . $this->db->escapeLikeParameter($pattern) . '%', |
|
| 109 | - IQueryBuilder::PARAM_STR |
|
| 110 | - ), |
|
| 111 | - IQueryBuilder::PARAM_STR |
|
| 112 | - )); |
|
| 113 | - |
|
| 114 | - $result = $query->executeQuery(); |
|
| 115 | - $rows = $result->fetchAllAssociative(); |
|
| 116 | - $result->closeCursor(); |
|
| 117 | - |
|
| 118 | - return $rows; |
|
| 119 | - } |
|
| 19 | + public function __construct( |
|
| 20 | + private IDBConnection $db, |
|
| 21 | + private CalDavBackend $calDavBackend, |
|
| 22 | + private LoggerInterface $logger, |
|
| 23 | + ) { |
|
| 24 | + } |
|
| 25 | + |
|
| 26 | + public function getName() { |
|
| 27 | + return 'Fix broken values of calendar objects'; |
|
| 28 | + } |
|
| 29 | + |
|
| 30 | + public function run(IOutput $output) { |
|
| 31 | + $pattern = ';VALUE=:'; |
|
| 32 | + $count = $warnings = 0; |
|
| 33 | + |
|
| 34 | + $objects = $this->getInvalidObjects($pattern); |
|
| 35 | + |
|
| 36 | + $output->startProgress(count($objects)); |
|
| 37 | + foreach ($objects as $row) { |
|
| 38 | + $calObject = $this->calDavBackend->getCalendarObject((int)$row['calendarid'], $row['uri']); |
|
| 39 | + $data = preg_replace('/' . $pattern . '/', ':', $calObject['calendardata']); |
|
| 40 | + |
|
| 41 | + if ($data !== $calObject['calendardata']) { |
|
| 42 | + $output->advance(); |
|
| 43 | + |
|
| 44 | + try { |
|
| 45 | + $this->calDavBackend->getDenormalizedData($data); |
|
| 46 | + } catch (InvalidDataException $e) { |
|
| 47 | + $this->logger->info('Calendar object for calendar {cal} with uri {uri} still invalid', [ |
|
| 48 | + 'app' => 'dav', |
|
| 49 | + 'cal' => (int)$row['calendarid'], |
|
| 50 | + 'uri' => $row['uri'], |
|
| 51 | + ]); |
|
| 52 | + $warnings++; |
|
| 53 | + continue; |
|
| 54 | + } |
|
| 55 | + |
|
| 56 | + $this->calDavBackend->updateCalendarObject((int)$row['calendarid'], $row['uri'], $data); |
|
| 57 | + $count++; |
|
| 58 | + } |
|
| 59 | + } |
|
| 60 | + $output->finishProgress(); |
|
| 61 | + |
|
| 62 | + if ($warnings > 0) { |
|
| 63 | + $output->warning(sprintf('%d events could not be updated, see log file for more information', $warnings)); |
|
| 64 | + } |
|
| 65 | + if ($count > 0) { |
|
| 66 | + $output->info(sprintf('Updated %d events', $count)); |
|
| 67 | + } |
|
| 68 | + } |
|
| 69 | + |
|
| 70 | + protected function getInvalidObjects($pattern) { |
|
| 71 | + if ($this->db->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) { |
|
| 72 | + $rows = []; |
|
| 73 | + $chunkSize = 500; |
|
| 74 | + $query = $this->db->getQueryBuilder(); |
|
| 75 | + $query->select($query->func()->count('*', 'num_entries')) |
|
| 76 | + ->from('calendarobjects'); |
|
| 77 | + $result = $query->executeQuery(); |
|
| 78 | + $count = $result->fetchOne(); |
|
| 79 | + $result->closeCursor(); |
|
| 80 | + |
|
| 81 | + $numChunks = ceil($count / $chunkSize); |
|
| 82 | + |
|
| 83 | + $query = $this->db->getQueryBuilder(); |
|
| 84 | + $query->select(['calendarid', 'uri', 'calendardata']) |
|
| 85 | + ->from('calendarobjects') |
|
| 86 | + ->setMaxResults($chunkSize); |
|
| 87 | + for ($chunk = 0; $chunk < $numChunks; $chunk++) { |
|
| 88 | + $query->setFirstResult($chunk * $chunkSize); |
|
| 89 | + $result = $query->executeQuery(); |
|
| 90 | + |
|
| 91 | + while ($row = $result->fetchAssociative()) { |
|
| 92 | + if (mb_strpos($row['calendardata'], $pattern) !== false) { |
|
| 93 | + unset($row['calendardata']); |
|
| 94 | + $rows[] = $row; |
|
| 95 | + } |
|
| 96 | + } |
|
| 97 | + $result->closeCursor(); |
|
| 98 | + } |
|
| 99 | + return $rows; |
|
| 100 | + } |
|
| 101 | + |
|
| 102 | + $query = $this->db->getQueryBuilder(); |
|
| 103 | + $query->select(['calendarid', 'uri']) |
|
| 104 | + ->from('calendarobjects') |
|
| 105 | + ->where($query->expr()->like( |
|
| 106 | + 'calendardata', |
|
| 107 | + $query->createNamedParameter( |
|
| 108 | + '%' . $this->db->escapeLikeParameter($pattern) . '%', |
|
| 109 | + IQueryBuilder::PARAM_STR |
|
| 110 | + ), |
|
| 111 | + IQueryBuilder::PARAM_STR |
|
| 112 | + )); |
|
| 113 | + |
|
| 114 | + $result = $query->executeQuery(); |
|
| 115 | + $rows = $result->fetchAllAssociative(); |
|
| 116 | + $result->closeCursor(); |
|
| 117 | + |
|
| 118 | + return $rows; |
|
| 119 | + } |
|
| 120 | 120 | } |
@@ -17,72 +17,72 @@ |
||
| 17 | 17 | use Psr\Log\LoggerInterface; |
| 18 | 18 | |
| 19 | 19 | class BuildSocialSearchIndexBackgroundJob extends QueuedJob { |
| 20 | - public function __construct( |
|
| 21 | - private IDBConnection $db, |
|
| 22 | - private CardDavBackend $davBackend, |
|
| 23 | - private LoggerInterface $logger, |
|
| 24 | - private IJobList $jobList, |
|
| 25 | - ITimeFactory $timeFactory, |
|
| 26 | - ) { |
|
| 27 | - parent::__construct($timeFactory); |
|
| 28 | - } |
|
| 20 | + public function __construct( |
|
| 21 | + private IDBConnection $db, |
|
| 22 | + private CardDavBackend $davBackend, |
|
| 23 | + private LoggerInterface $logger, |
|
| 24 | + private IJobList $jobList, |
|
| 25 | + ITimeFactory $timeFactory, |
|
| 26 | + ) { |
|
| 27 | + parent::__construct($timeFactory); |
|
| 28 | + } |
|
| 29 | 29 | |
| 30 | - public function run($argument) { |
|
| 31 | - $offset = $argument['offset']; |
|
| 32 | - $stopAt = $argument['stopAt']; |
|
| 30 | + public function run($argument) { |
|
| 31 | + $offset = $argument['offset']; |
|
| 32 | + $stopAt = $argument['stopAt']; |
|
| 33 | 33 | |
| 34 | - $this->logger->info('Indexing social profile data (' . $offset . '/' . $stopAt . ')'); |
|
| 34 | + $this->logger->info('Indexing social profile data (' . $offset . '/' . $stopAt . ')'); |
|
| 35 | 35 | |
| 36 | - $offset = $this->buildIndex($offset, $stopAt); |
|
| 36 | + $offset = $this->buildIndex($offset, $stopAt); |
|
| 37 | 37 | |
| 38 | - if ($offset >= $stopAt) { |
|
| 39 | - $this->logger->info('All contacts with social profiles indexed'); |
|
| 40 | - } else { |
|
| 41 | - $this->jobList->add(self::class, [ |
|
| 42 | - 'offset' => $offset, |
|
| 43 | - 'stopAt' => $stopAt |
|
| 44 | - ]); |
|
| 45 | - $this->logger->info('New social profile indexing job scheduled with offset ' . $offset); |
|
| 46 | - } |
|
| 47 | - } |
|
| 38 | + if ($offset >= $stopAt) { |
|
| 39 | + $this->logger->info('All contacts with social profiles indexed'); |
|
| 40 | + } else { |
|
| 41 | + $this->jobList->add(self::class, [ |
|
| 42 | + 'offset' => $offset, |
|
| 43 | + 'stopAt' => $stopAt |
|
| 44 | + ]); |
|
| 45 | + $this->logger->info('New social profile indexing job scheduled with offset ' . $offset); |
|
| 46 | + } |
|
| 47 | + } |
|
| 48 | 48 | |
| 49 | - /** |
|
| 50 | - * @param int $offset |
|
| 51 | - * @param int $stopAt |
|
| 52 | - * @return int |
|
| 53 | - */ |
|
| 54 | - private function buildIndex($offset, $stopAt) { |
|
| 55 | - $startTime = $this->time->getTime(); |
|
| 49 | + /** |
|
| 50 | + * @param int $offset |
|
| 51 | + * @param int $stopAt |
|
| 52 | + * @return int |
|
| 53 | + */ |
|
| 54 | + private function buildIndex($offset, $stopAt) { |
|
| 55 | + $startTime = $this->time->getTime(); |
|
| 56 | 56 | |
| 57 | - // get contacts with social profiles |
|
| 58 | - $query = $this->db->getQueryBuilder(); |
|
| 59 | - $query->select('id', 'addressbookid', 'uri', 'carddata') |
|
| 60 | - ->from('cards', 'c') |
|
| 61 | - ->orderBy('id', 'ASC') |
|
| 62 | - ->where($query->expr()->like('carddata', $query->createNamedParameter('%SOCIALPROFILE%'))) |
|
| 63 | - ->andWhere($query->expr()->gt('id', $query->createNamedParameter((int)$offset, IQueryBuilder::PARAM_INT))) |
|
| 64 | - ->setMaxResults(100); |
|
| 65 | - $social_cards = $query->executeQuery()->fetchAllAssociative(); |
|
| 57 | + // get contacts with social profiles |
|
| 58 | + $query = $this->db->getQueryBuilder(); |
|
| 59 | + $query->select('id', 'addressbookid', 'uri', 'carddata') |
|
| 60 | + ->from('cards', 'c') |
|
| 61 | + ->orderBy('id', 'ASC') |
|
| 62 | + ->where($query->expr()->like('carddata', $query->createNamedParameter('%SOCIALPROFILE%'))) |
|
| 63 | + ->andWhere($query->expr()->gt('id', $query->createNamedParameter((int)$offset, IQueryBuilder::PARAM_INT))) |
|
| 64 | + ->setMaxResults(100); |
|
| 65 | + $social_cards = $query->executeQuery()->fetchAllAssociative(); |
|
| 66 | 66 | |
| 67 | - if (empty($social_cards)) { |
|
| 68 | - return $stopAt; |
|
| 69 | - } |
|
| 67 | + if (empty($social_cards)) { |
|
| 68 | + return $stopAt; |
|
| 69 | + } |
|
| 70 | 70 | |
| 71 | - // refresh identified contacts in order to re-index |
|
| 72 | - foreach ($social_cards as $contact) { |
|
| 73 | - $offset = $contact['id']; |
|
| 74 | - $cardData = $contact['carddata']; |
|
| 75 | - if (is_resource($cardData) && (get_resource_type($cardData) === 'stream')) { |
|
| 76 | - $cardData = stream_get_contents($cardData); |
|
| 77 | - } |
|
| 78 | - $this->davBackend->updateCard($contact['addressbookid'], $contact['uri'], $cardData); |
|
| 71 | + // refresh identified contacts in order to re-index |
|
| 72 | + foreach ($social_cards as $contact) { |
|
| 73 | + $offset = $contact['id']; |
|
| 74 | + $cardData = $contact['carddata']; |
|
| 75 | + if (is_resource($cardData) && (get_resource_type($cardData) === 'stream')) { |
|
| 76 | + $cardData = stream_get_contents($cardData); |
|
| 77 | + } |
|
| 78 | + $this->davBackend->updateCard($contact['addressbookid'], $contact['uri'], $cardData); |
|
| 79 | 79 | |
| 80 | - // stop after 15sec (to be continued with next chunk) |
|
| 81 | - if (($this->time->getTime() - $startTime) > 15) { |
|
| 82 | - break; |
|
| 83 | - } |
|
| 84 | - } |
|
| 80 | + // stop after 15sec (to be continued with next chunk) |
|
| 81 | + if (($this->time->getTime() - $startTime) > 15) { |
|
| 82 | + break; |
|
| 83 | + } |
|
| 84 | + } |
|
| 85 | 85 | |
| 86 | - return $offset; |
|
| 87 | - } |
|
| 86 | + return $offset; |
|
| 87 | + } |
|
| 88 | 88 | } |
@@ -31,7 +31,7 @@ discard block |
||
| 31 | 31 | $offset = $argument['offset']; |
| 32 | 32 | $stopAt = $argument['stopAt']; |
| 33 | 33 | |
| 34 | - $this->logger->info('Indexing social profile data (' . $offset . '/' . $stopAt . ')'); |
|
| 34 | + $this->logger->info('Indexing social profile data ('.$offset.'/'.$stopAt.')'); |
|
| 35 | 35 | |
| 36 | 36 | $offset = $this->buildIndex($offset, $stopAt); |
| 37 | 37 | |
@@ -42,7 +42,7 @@ discard block |
||
| 42 | 42 | 'offset' => $offset, |
| 43 | 43 | 'stopAt' => $stopAt |
| 44 | 44 | ]); |
| 45 | - $this->logger->info('New social profile indexing job scheduled with offset ' . $offset); |
|
| 45 | + $this->logger->info('New social profile indexing job scheduled with offset '.$offset); |
|
| 46 | 46 | } |
| 47 | 47 | } |
| 48 | 48 | |
@@ -60,7 +60,7 @@ discard block |
||
| 60 | 60 | ->from('cards', 'c') |
| 61 | 61 | ->orderBy('id', 'ASC') |
| 62 | 62 | ->where($query->expr()->like('carddata', $query->createNamedParameter('%SOCIALPROFILE%'))) |
| 63 | - ->andWhere($query->expr()->gt('id', $query->createNamedParameter((int)$offset, IQueryBuilder::PARAM_INT))) |
|
| 63 | + ->andWhere($query->expr()->gt('id', $query->createNamedParameter((int) $offset, IQueryBuilder::PARAM_INT))) |
|
| 64 | 64 | ->setMaxResults(100); |
| 65 | 65 | $social_cards = $query->executeQuery()->fetchAllAssociative(); |
| 66 | 66 | |