aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav/lib/Migration
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav/lib/Migration')
-rw-r--r--apps/dav/lib/Migration/BuildCalendarSearchIndex.php57
-rw-r--r--apps/dav/lib/Migration/BuildCalendarSearchIndexBackgroundJob.php82
-rw-r--r--apps/dav/lib/Migration/BuildSocialSearchIndex.php65
-rw-r--r--apps/dav/lib/Migration/BuildSocialSearchIndexBackgroundJob.php88
-rw-r--r--apps/dav/lib/Migration/CalDAVRemoveEmptyValue.php120
-rw-r--r--apps/dav/lib/Migration/ChunkCleanup.php68
-rw-r--r--apps/dav/lib/Migration/CreateSystemAddressBookStep.php30
-rw-r--r--apps/dav/lib/Migration/DeleteSchedulingObjects.php39
-rw-r--r--apps/dav/lib/Migration/FixBirthdayCalendarComponent.php40
-rw-r--r--apps/dav/lib/Migration/RefreshWebcalJobRegistrar.php62
-rw-r--r--apps/dav/lib/Migration/RegenerateBirthdayCalendars.php50
-rw-r--r--apps/dav/lib/Migration/RegisterBuildReminderIndexBackgroundJob.php73
-rw-r--r--apps/dav/lib/Migration/RegisterUpdateCalendarResourcesRoomBackgroundJob.php30
-rw-r--r--apps/dav/lib/Migration/RemoveClassifiedEventActivity.php116
-rw-r--r--apps/dav/lib/Migration/RemoveDeletedUsersCalendarSubscriptions.php123
-rw-r--r--apps/dav/lib/Migration/RemoveObjectProperties.php48
-rw-r--r--apps/dav/lib/Migration/RemoveOrphanEventsAndContacts.php52
-rw-r--r--apps/dav/lib/Migration/Version1004Date20170825134824.php481
-rw-r--r--apps/dav/lib/Migration/Version1004Date20170919104507.php40
-rw-r--r--apps/dav/lib/Migration/Version1004Date20170924124212.php39
-rw-r--r--apps/dav/lib/Migration/Version1004Date20170926103422.php36
-rw-r--r--apps/dav/lib/Migration/Version1005Date20180413093149.php64
-rw-r--r--apps/dav/lib/Migration/Version1005Date20180530124431.php68
-rw-r--r--apps/dav/lib/Migration/Version1006Date20180619154313.php76
-rw-r--r--apps/dav/lib/Migration/Version1006Date20180628111625.php91
-rw-r--r--apps/dav/lib/Migration/Version1008Date20181030113700.php38
-rw-r--r--apps/dav/lib/Migration/Version1008Date20181105104826.php60
-rw-r--r--apps/dav/lib/Migration/Version1008Date20181105104833.php32
-rw-r--r--apps/dav/lib/Migration/Version1008Date20181105110300.php59
-rw-r--r--apps/dav/lib/Migration/Version1008Date20181105112049.php32
-rw-r--r--apps/dav/lib/Migration/Version1008Date20181114084440.php42
-rw-r--r--apps/dav/lib/Migration/Version1011Date20190725113607.php72
-rw-r--r--apps/dav/lib/Migration/Version1011Date20190806104428.php58
-rw-r--r--apps/dav/lib/Migration/Version1012Date20190808122342.php103
-rw-r--r--apps/dav/lib/Migration/Version1016Date20201109085907.php43
-rw-r--r--apps/dav/lib/Migration/Version1017Date20210216083742.php37
-rw-r--r--apps/dav/lib/Migration/Version1018Date20210312100735.php49
-rw-r--r--apps/dav/lib/Migration/Version1024Date20211221144219.php61
-rw-r--r--apps/dav/lib/Migration/Version1025Date20240308063933.php91
-rw-r--r--apps/dav/lib/Migration/Version1027Date20230504122946.php53
-rw-r--r--apps/dav/lib/Migration/Version1029Date20221114151721.php38
-rw-r--r--apps/dav/lib/Migration/Version1029Date20231004091403.php66
-rw-r--r--apps/dav/lib/Migration/Version1030Date20240205103243.php37
-rw-r--r--apps/dav/lib/Migration/Version1031Date20240610134258.php48
-rw-r--r--apps/dav/lib/Migration/Version1034Date20250813093701.php53
45 files changed, 3110 insertions, 0 deletions
diff --git a/apps/dav/lib/Migration/BuildCalendarSearchIndex.php b/apps/dav/lib/Migration/BuildCalendarSearchIndex.php
new file mode 100644
index 00000000000..d8f906f22ee
--- /dev/null
+++ b/apps/dav/lib/Migration/BuildCalendarSearchIndex.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\BackgroundJob\IJobList;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class BuildCalendarSearchIndex implements IRepairStep {
+
+ public function __construct(
+ private IDBConnection $db,
+ private IJobList $jobList,
+ private IConfig $config,
+ ) {
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return 'Registering building of calendar search index as background job';
+ }
+
+ /**
+ * @param IOutput $output
+ */
+ public function run(IOutput $output) {
+ // only run once
+ if ($this->config->getAppValue('dav', 'buildCalendarSearchIndex') === 'yes') {
+ $output->info('Repair step already executed');
+ return;
+ }
+
+ $query = $this->db->getQueryBuilder();
+ $query->select($query->createFunction('MAX(' . $query->getColumnName('id') . ')'))
+ ->from('calendarobjects');
+ $result = $query->executeQuery();
+ $maxId = (int)$result->fetchOne();
+ $result->closeCursor();
+
+ $output->info('Add background job');
+ $this->jobList->add(BuildCalendarSearchIndexBackgroundJob::class, [
+ 'offset' => 0,
+ 'stopAt' => $maxId
+ ]);
+
+ // if all were done, no need to redo the repair during next upgrade
+ $this->config->setAppValue('dav', 'buildCalendarSearchIndex', 'yes');
+ }
+}
diff --git a/apps/dav/lib/Migration/BuildCalendarSearchIndexBackgroundJob.php b/apps/dav/lib/Migration/BuildCalendarSearchIndexBackgroundJob.php
new file mode 100644
index 00000000000..da8f31e7d3d
--- /dev/null
+++ b/apps/dav/lib/Migration/BuildCalendarSearchIndexBackgroundJob.php
@@ -0,0 +1,82 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\IJobList;
+use OCP\BackgroundJob\QueuedJob;
+use OCP\IDBConnection;
+use Psr\Log\LoggerInterface;
+
+class BuildCalendarSearchIndexBackgroundJob extends QueuedJob {
+ public function __construct(
+ private IDBConnection $db,
+ private CalDavBackend $calDavBackend,
+ private LoggerInterface $logger,
+ private IJobList $jobList,
+ ITimeFactory $timeFactory,
+ ) {
+ parent::__construct($timeFactory);
+ }
+
+ public function run($arguments) {
+ $offset = (int)$arguments['offset'];
+ $stopAt = (int)$arguments['stopAt'];
+
+ $this->logger->info('Building calendar index (' . $offset . '/' . $stopAt . ')');
+
+ $startTime = $this->time->getTime();
+ while (($this->time->getTime() - $startTime) < 15) {
+ $offset = $this->buildIndex($offset, $stopAt);
+ if ($offset >= $stopAt) {
+ break;
+ }
+ }
+
+ if ($offset >= $stopAt) {
+ $this->logger->info('Building calendar index done');
+ } else {
+ $this->jobList->add(self::class, [
+ 'offset' => $offset,
+ 'stopAt' => $stopAt
+ ]);
+ $this->logger->info('New building calendar index job scheduled with offset ' . $offset);
+ }
+ }
+
+ /**
+ * @param int $offset
+ * @param int $stopAt
+ * @return int
+ */
+ private function buildIndex(int $offset, int $stopAt): int {
+ $query = $this->db->getQueryBuilder();
+ $query->select(['id', 'calendarid', 'uri', 'calendardata'])
+ ->from('calendarobjects')
+ ->where($query->expr()->lte('id', $query->createNamedParameter($stopAt)))
+ ->andWhere($query->expr()->gt('id', $query->createNamedParameter($offset)))
+ ->orderBy('id', 'ASC')
+ ->setMaxResults(500);
+
+ $result = $query->executeQuery();
+ while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
+ $offset = $row['id'];
+
+ $calendarData = $row['calendardata'];
+ if (is_resource($calendarData)) {
+ $calendarData = stream_get_contents($calendarData);
+ }
+
+ $this->calDavBackend->updateProperties($row['calendarid'], $row['uri'], $calendarData);
+ }
+
+ return $offset;
+ }
+}
diff --git a/apps/dav/lib/Migration/BuildSocialSearchIndex.php b/apps/dav/lib/Migration/BuildSocialSearchIndex.php
new file mode 100644
index 00000000000..a808034365a
--- /dev/null
+++ b/apps/dav/lib/Migration/BuildSocialSearchIndex.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\BackgroundJob\IJobList;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class BuildSocialSearchIndex implements IRepairStep {
+
+ /**
+ * @param IDBConnection $db
+ * @param IJobList $jobList
+ * @param IConfig $config
+ */
+ public function __construct(
+ private IDBConnection $db,
+ private IJobList $jobList,
+ private IConfig $config,
+ ) {
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return 'Register building of social profile search index as background job';
+ }
+
+ /**
+ * @param IOutput $output
+ */
+ public function run(IOutput $output) {
+ // only run once
+ if ($this->config->getAppValue('dav', 'builtSocialSearchIndex') === 'yes') {
+ $output->info('Repair step already executed');
+ return;
+ }
+
+ $query = $this->db->getQueryBuilder();
+ $query->select($query->func()->max('cardid'))
+ ->from('cards_properties')
+ ->where($query->expr()->eq('name', $query->createNamedParameter('X-SOCIALPROFILE')));
+ $maxId = (int)$query->execute()->fetchOne();
+
+ if ($maxId === 0) {
+ return;
+ }
+
+ $output->info('Add background job');
+ $this->jobList->add(BuildSocialSearchIndexBackgroundJob::class, [
+ 'offset' => 0,
+ 'stopAt' => $maxId
+ ]);
+
+ // no need to redo the repair during next upgrade
+ $this->config->setAppValue('dav', 'builtSocialSearchIndex', 'yes');
+ }
+}
diff --git a/apps/dav/lib/Migration/BuildSocialSearchIndexBackgroundJob.php b/apps/dav/lib/Migration/BuildSocialSearchIndexBackgroundJob.php
new file mode 100644
index 00000000000..fab61d56fd6
--- /dev/null
+++ b/apps/dav/lib/Migration/BuildSocialSearchIndexBackgroundJob.php
@@ -0,0 +1,88 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\CardDAV\CardDavBackend;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\IJobList;
+use OCP\BackgroundJob\QueuedJob;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use Psr\Log\LoggerInterface;
+
+class BuildSocialSearchIndexBackgroundJob extends QueuedJob {
+ public function __construct(
+ private IDBConnection $db,
+ private CardDavBackend $davBackend,
+ private LoggerInterface $logger,
+ private IJobList $jobList,
+ ITimeFactory $timeFactory,
+ ) {
+ parent::__construct($timeFactory);
+ }
+
+ public function run($arguments) {
+ $offset = $arguments['offset'];
+ $stopAt = $arguments['stopAt'];
+
+ $this->logger->info('Indexing social profile data (' . $offset . '/' . $stopAt . ')');
+
+ $offset = $this->buildIndex($offset, $stopAt);
+
+ if ($offset >= $stopAt) {
+ $this->logger->info('All contacts with social profiles indexed');
+ } else {
+ $this->jobList->add(self::class, [
+ 'offset' => $offset,
+ 'stopAt' => $stopAt
+ ]);
+ $this->logger->info('New social profile indexing job scheduled with offset ' . $offset);
+ }
+ }
+
+ /**
+ * @param int $offset
+ * @param int $stopAt
+ * @return int
+ */
+ private function buildIndex($offset, $stopAt) {
+ $startTime = $this->time->getTime();
+
+ // get contacts with social profiles
+ $query = $this->db->getQueryBuilder();
+ $query->select('id', 'addressbookid', 'uri', 'carddata')
+ ->from('cards', 'c')
+ ->orderBy('id', 'ASC')
+ ->where($query->expr()->like('carddata', $query->createNamedParameter('%SOCIALPROFILE%')))
+ ->andWhere($query->expr()->gt('id', $query->createNamedParameter((int)$offset, IQueryBuilder::PARAM_INT)))
+ ->setMaxResults(100);
+ $social_cards = $query->executeQuery()->fetchAll();
+
+ if (empty($social_cards)) {
+ return $stopAt;
+ }
+
+ // refresh identified contacts in order to re-index
+ foreach ($social_cards as $contact) {
+ $offset = $contact['id'];
+ $cardData = $contact['carddata'];
+ if (is_resource($cardData) && (get_resource_type($cardData) === 'stream')) {
+ $cardData = stream_get_contents($cardData);
+ }
+ $this->davBackend->updateCard($contact['addressbookid'], $contact['uri'], $cardData);
+
+ // stop after 15sec (to be continued with next chunk)
+ if (($this->time->getTime() - $startTime) > 15) {
+ break;
+ }
+ }
+
+ return $offset;
+ }
+}
diff --git a/apps/dav/lib/Migration/CalDAVRemoveEmptyValue.php b/apps/dav/lib/Migration/CalDAVRemoveEmptyValue.php
new file mode 100644
index 00000000000..24e182e46eb
--- /dev/null
+++ b/apps/dav/lib/Migration/CalDAVRemoveEmptyValue.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+use Psr\Log\LoggerInterface;
+use Sabre\VObject\InvalidDataException;
+
+class CalDAVRemoveEmptyValue implements IRepairStep {
+
+ public function __construct(
+ private IDBConnection $db,
+ private CalDavBackend $calDavBackend,
+ private LoggerInterface $logger,
+ ) {
+ }
+
+ public function getName() {
+ return 'Fix broken values of calendar objects';
+ }
+
+ public function run(IOutput $output) {
+ $pattern = ';VALUE=:';
+ $count = $warnings = 0;
+
+ $objects = $this->getInvalidObjects($pattern);
+
+ $output->startProgress(count($objects));
+ foreach ($objects as $row) {
+ $calObject = $this->calDavBackend->getCalendarObject((int)$row['calendarid'], $row['uri']);
+ $data = preg_replace('/' . $pattern . '/', ':', $calObject['calendardata']);
+
+ if ($data !== $calObject['calendardata']) {
+ $output->advance();
+
+ try {
+ $this->calDavBackend->getDenormalizedData($data);
+ } catch (InvalidDataException $e) {
+ $this->logger->info('Calendar object for calendar {cal} with uri {uri} still invalid', [
+ 'app' => 'dav',
+ 'cal' => (int)$row['calendarid'],
+ 'uri' => $row['uri'],
+ ]);
+ $warnings++;
+ continue;
+ }
+
+ $this->calDavBackend->updateCalendarObject((int)$row['calendarid'], $row['uri'], $data);
+ $count++;
+ }
+ }
+ $output->finishProgress();
+
+ if ($warnings > 0) {
+ $output->warning(sprintf('%d events could not be updated, see log file for more information', $warnings));
+ }
+ if ($count > 0) {
+ $output->info(sprintf('Updated %d events', $count));
+ }
+ }
+
+ protected function getInvalidObjects($pattern) {
+ if ($this->db->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) {
+ $rows = [];
+ $chunkSize = 500;
+ $query = $this->db->getQueryBuilder();
+ $query->select($query->func()->count('*', 'num_entries'))
+ ->from('calendarobjects');
+ $result = $query->executeQuery();
+ $count = $result->fetchOne();
+ $result->closeCursor();
+
+ $numChunks = ceil($count / $chunkSize);
+
+ $query = $this->db->getQueryBuilder();
+ $query->select(['calendarid', 'uri', 'calendardata'])
+ ->from('calendarobjects')
+ ->setMaxResults($chunkSize);
+ for ($chunk = 0; $chunk < $numChunks; $chunk++) {
+ $query->setFirstResult($chunk * $chunkSize);
+ $result = $query->executeQuery();
+
+ while ($row = $result->fetch()) {
+ if (mb_strpos($row['calendardata'], $pattern) !== false) {
+ unset($row['calendardata']);
+ $rows[] = $row;
+ }
+ }
+ $result->closeCursor();
+ }
+ return $rows;
+ }
+
+ $query = $this->db->getQueryBuilder();
+ $query->select(['calendarid', 'uri'])
+ ->from('calendarobjects')
+ ->where($query->expr()->like(
+ 'calendardata',
+ $query->createNamedParameter(
+ '%' . $this->db->escapeLikeParameter($pattern) . '%',
+ IQueryBuilder::PARAM_STR
+ ),
+ IQueryBuilder::PARAM_STR
+ ));
+
+ $result = $query->executeQuery();
+ $rows = $result->fetchAll();
+ $result->closeCursor();
+
+ return $rows;
+ }
+}
diff --git a/apps/dav/lib/Migration/ChunkCleanup.php b/apps/dav/lib/Migration/ChunkCleanup.php
new file mode 100644
index 00000000000..edd9a26109e
--- /dev/null
+++ b/apps/dav/lib/Migration/ChunkCleanup.php
@@ -0,0 +1,68 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\BackgroundJob\UploadCleanup;
+use OCP\BackgroundJob\IJobList;
+use OCP\Files\Folder;
+use OCP\Files\IRootFolder;
+use OCP\Files\NotFoundException;
+use OCP\IConfig;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class ChunkCleanup implements IRepairStep {
+
+ public function __construct(
+ private IConfig $config,
+ private IUserManager $userManager,
+ private IRootFolder $rootFolder,
+ private IJobList $jobList,
+ ) {
+ }
+
+ public function getName(): string {
+ return 'Chunk cleanup scheduler';
+ }
+
+ public function run(IOutput $output) {
+ // If we already ran this onec there is no need to run it again
+ if ($this->config->getAppValue('dav', 'chunks_migrated', '0') === '1') {
+ $output->info('Cleanup not required');
+ return;
+ }
+
+ $output->startProgress();
+ // Loop over all seen users
+ $this->userManager->callForSeenUsers(function (IUser $user) use ($output): void {
+ try {
+ $userFolder = $this->rootFolder->getUserFolder($user->getUID());
+ $userRoot = $userFolder->getParent();
+ /** @var Folder $uploadFolder */
+ $uploadFolder = $userRoot->get('uploads');
+ } catch (NotFoundException $e) {
+ // No folder so skipping
+ return;
+ }
+
+ // Insert a cleanup job for each folder we find
+ $uploads = $uploadFolder->getDirectoryListing();
+ foreach ($uploads as $upload) {
+ $this->jobList->add(UploadCleanup::class, ['uid' => $user->getUID(), 'folder' => $upload->getName()]);
+ }
+ $output->advance();
+ });
+ $output->finishProgress();
+
+
+ $this->config->setAppValue('dav', 'chunks_migrated', '1');
+ }
+}
diff --git a/apps/dav/lib/Migration/CreateSystemAddressBookStep.php b/apps/dav/lib/Migration/CreateSystemAddressBookStep.php
new file mode 100644
index 00000000000..ec07c72e7a7
--- /dev/null
+++ b/apps/dav/lib/Migration/CreateSystemAddressBookStep.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\CardDAV\SyncService;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class CreateSystemAddressBookStep implements IRepairStep {
+
+ public function __construct(
+ private SyncService $syncService,
+ ) {
+ }
+
+ public function getName(): string {
+ return 'Create system address book';
+ }
+
+ public function run(IOutput $output): void {
+ $this->syncService->ensureLocalSystemAddressBookExists();
+ }
+}
diff --git a/apps/dav/lib/Migration/DeleteSchedulingObjects.php b/apps/dav/lib/Migration/DeleteSchedulingObjects.php
new file mode 100644
index 00000000000..3cb3c9c9b10
--- /dev/null
+++ b/apps/dav/lib/Migration/DeleteSchedulingObjects.php
@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\BackgroundJob\DeleteOutdatedSchedulingObjects;
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\IJobList;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class DeleteSchedulingObjects implements IRepairStep {
+ public function __construct(
+ private IJobList $jobList,
+ private ITimeFactory $time,
+ private CalDavBackend $calDavBackend,
+ ) {
+ }
+
+ public function getName(): string {
+ return 'Handle outdated scheduling events';
+ }
+
+ public function run(IOutput $output): void {
+ $output->info('Cleaning up old scheduling events');
+ $time = $this->time->getTime() - (60 * 60);
+ $this->calDavBackend->deleteOutdatedSchedulingObjects($time, 50000);
+ if (!$this->jobList->has(DeleteOutdatedSchedulingObjects::class, null)) {
+ $output->info('Adding background job to delete old scheduling objects');
+ $this->jobList->add(DeleteOutdatedSchedulingObjects::class, null);
+ }
+ }
+}
diff --git a/apps/dav/lib/Migration/FixBirthdayCalendarComponent.php b/apps/dav/lib/Migration/FixBirthdayCalendarComponent.php
new file mode 100644
index 00000000000..6833ca2ffa6
--- /dev/null
+++ b/apps/dav/lib/Migration/FixBirthdayCalendarComponent.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016 ownCloud GmbH.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\CalDAV\BirthdayService;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class FixBirthdayCalendarComponent implements IRepairStep {
+
+ public function __construct(
+ private IDBConnection $connection,
+ ) {
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName() {
+ return 'Fix component of birthday calendars';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function run(IOutput $output) {
+ $query = $this->connection->getQueryBuilder();
+ $updated = $query->update('calendars')
+ ->set('components', $query->createNamedParameter('VEVENT'))
+ ->where($query->expr()->eq('uri', $query->createNamedParameter(BirthdayService::BIRTHDAY_CALENDAR_URI)))
+ ->executeStatement();
+
+ $output->info("$updated birthday calendars updated.");
+ }
+}
diff --git a/apps/dav/lib/Migration/RefreshWebcalJobRegistrar.php b/apps/dav/lib/Migration/RefreshWebcalJobRegistrar.php
new file mode 100644
index 00000000000..cd4b8b31f4d
--- /dev/null
+++ b/apps/dav/lib/Migration/RefreshWebcalJobRegistrar.php
@@ -0,0 +1,62 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\BackgroundJob\RefreshWebcalJob;
+use OCP\BackgroundJob\IJobList;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class RefreshWebcalJobRegistrar implements IRepairStep {
+
+ /**
+ * FixBirthdayCalendarComponent constructor.
+ *
+ * @param IDBConnection $connection
+ * @param IJobList $jobList
+ */
+ public function __construct(
+ private IDBConnection $connection,
+ private IJobList $jobList,
+ ) {
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName() {
+ return 'Registering background jobs to update cache for webcal calendars';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function run(IOutput $output) {
+ $query = $this->connection->getQueryBuilder();
+ $query->select(['principaluri', 'uri'])
+ ->from('calendarsubscriptions');
+ $stmt = $query->execute();
+
+ $count = 0;
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ $args = [
+ 'principaluri' => $row['principaluri'],
+ 'uri' => $row['uri'],
+ ];
+
+ if (!$this->jobList->has(RefreshWebcalJob::class, $args)) {
+ $this->jobList->add(RefreshWebcalJob::class, $args);
+ $count++;
+ }
+ }
+
+ $output->info("Added $count background jobs to update webcal calendars");
+ }
+}
diff --git a/apps/dav/lib/Migration/RegenerateBirthdayCalendars.php b/apps/dav/lib/Migration/RegenerateBirthdayCalendars.php
new file mode 100644
index 00000000000..ef8e9002e9d
--- /dev/null
+++ b/apps/dav/lib/Migration/RegenerateBirthdayCalendars.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\BackgroundJob\RegisterRegenerateBirthdayCalendars;
+use OCP\BackgroundJob\IJobList;
+use OCP\IConfig;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class RegenerateBirthdayCalendars implements IRepairStep {
+
+ /**
+ * @param IJobList $jobList
+ * @param IConfig $config
+ */
+ public function __construct(
+ private IJobList $jobList,
+ private IConfig $config,
+ ) {
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return 'Regenerating birthday calendars to use new icons and fix old birthday events without year';
+ }
+
+ /**
+ * @param IOutput $output
+ */
+ public function run(IOutput $output) {
+ // only run once
+ if ($this->config->getAppValue('dav', 'regeneratedBirthdayCalendarsForYearFix') === 'yes') {
+ $output->info('Repair step already executed');
+ return;
+ }
+
+ $output->info('Adding background jobs to regenerate birthday calendar');
+ $this->jobList->add(RegisterRegenerateBirthdayCalendars::class);
+
+ // if all were done, no need to redo the repair during next upgrade
+ $this->config->setAppValue('dav', 'regeneratedBirthdayCalendarsForYearFix', 'yes');
+ }
+}
diff --git a/apps/dav/lib/Migration/RegisterBuildReminderIndexBackgroundJob.php b/apps/dav/lib/Migration/RegisterBuildReminderIndexBackgroundJob.php
new file mode 100644
index 00000000000..7f74390f883
--- /dev/null
+++ b/apps/dav/lib/Migration/RegisterBuildReminderIndexBackgroundJob.php
@@ -0,0 +1,73 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\BackgroundJob\BuildReminderIndexBackgroundJob;
+use OCP\BackgroundJob\IJobList;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+/**
+ * Class RegisterBuildReminderIndexBackgroundJob
+ *
+ * @package OCA\DAV\Migration
+ */
+class RegisterBuildReminderIndexBackgroundJob implements IRepairStep {
+
+ /** @var string */
+ private const CONFIG_KEY = 'buildCalendarReminderIndex';
+
+ /**
+ * @param IDBConnection $db
+ * @param IJobList $jobList
+ * @param IConfig $config
+ */
+ public function __construct(
+ private IDBConnection $db,
+ private IJobList $jobList,
+ private IConfig $config,
+ ) {
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return 'Registering building of calendar reminder index as background job';
+ }
+
+ /**
+ * @param IOutput $output
+ */
+ public function run(IOutput $output) {
+ // only run once
+ if ($this->config->getAppValue('dav', self::CONFIG_KEY) === 'yes') {
+ $output->info('Repair step already executed');
+ return;
+ }
+
+ $query = $this->db->getQueryBuilder();
+ $query->select($query->createFunction('MAX(' . $query->getColumnName('id') . ')'))
+ ->from('calendarobjects');
+ $result = $query->executeQuery();
+ $maxId = (int)$result->fetchOne();
+ $result->closeCursor();
+
+ $output->info('Add background job');
+ $this->jobList->add(BuildReminderIndexBackgroundJob::class, [
+ 'offset' => 0,
+ 'stopAt' => $maxId
+ ]);
+
+ // if all were done, no need to redo the repair during next upgrade
+ $this->config->setAppValue('dav', self::CONFIG_KEY, 'yes');
+ }
+}
diff --git a/apps/dav/lib/Migration/RegisterUpdateCalendarResourcesRoomBackgroundJob.php b/apps/dav/lib/Migration/RegisterUpdateCalendarResourcesRoomBackgroundJob.php
new file mode 100644
index 00000000000..9d77aefafd2
--- /dev/null
+++ b/apps/dav/lib/Migration/RegisterUpdateCalendarResourcesRoomBackgroundJob.php
@@ -0,0 +1,30 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\BackgroundJob\UpdateCalendarResourcesRoomsBackgroundJob;
+use OCP\BackgroundJob\IJobList;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class RegisterUpdateCalendarResourcesRoomBackgroundJob implements IRepairStep {
+ public function __construct(
+ private readonly IJobList $jobList,
+ ) {
+ }
+
+ public function getName() {
+ return 'Register a background job to update rooms and resources';
+ }
+
+ public function run(IOutput $output) {
+ $this->jobList->add(UpdateCalendarResourcesRoomsBackgroundJob::class);
+ }
+}
diff --git a/apps/dav/lib/Migration/RemoveClassifiedEventActivity.php b/apps/dav/lib/Migration/RemoveClassifiedEventActivity.php
new file mode 100644
index 00000000000..f0d208f4f33
--- /dev/null
+++ b/apps/dav/lib/Migration/RemoveClassifiedEventActivity.php
@@ -0,0 +1,116 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class RemoveClassifiedEventActivity implements IRepairStep {
+
+ public function __construct(
+ private IDBConnection $connection,
+ ) {
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName() {
+ return 'Remove activity entries of private events';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function run(IOutput $output) {
+ if (!$this->connection->tableExists('activity')) {
+ return;
+ }
+
+ $deletedEvents = $this->removePrivateEventActivity();
+ $deletedEvents += $this->removeConfidentialUncensoredEventActivity();
+
+ $output->info("Removed $deletedEvents activity entries");
+ }
+
+ protected function removePrivateEventActivity(): int {
+ $deletedEvents = 0;
+
+ $delete = $this->connection->getQueryBuilder();
+ $delete->delete('activity')
+ ->where($delete->expr()->neq('affecteduser', $delete->createParameter('owner')))
+ ->andWhere($delete->expr()->eq('object_type', $delete->createParameter('type')))
+ ->andWhere($delete->expr()->eq('object_id', $delete->createParameter('calendar_id')))
+ ->andWhere($delete->expr()->like('subjectparams', $delete->createParameter('event_uid')));
+
+ $query = $this->connection->getQueryBuilder();
+ $query->select('c.principaluri', 'o.calendarid', 'o.uid')
+ ->from('calendarobjects', 'o')
+ ->leftJoin('o', 'calendars', 'c', $query->expr()->eq('c.id', 'o.calendarid'))
+ ->where($query->expr()->eq('o.classification', $query->createNamedParameter(CalDavBackend::CLASSIFICATION_PRIVATE)));
+ $result = $query->executeQuery();
+
+ while ($row = $result->fetch()) {
+ if ($row['principaluri'] === null) {
+ continue;
+ }
+
+ $delete->setParameter('owner', $this->getPrincipal($row['principaluri']))
+ ->setParameter('type', 'calendar')
+ ->setParameter('calendar_id', $row['calendarid'])
+ ->setParameter('event_uid', '%' . $this->connection->escapeLikeParameter('{"id":"' . $row['uid'] . '"') . '%');
+ $deletedEvents += $delete->executeStatement();
+ }
+ $result->closeCursor();
+
+ return $deletedEvents;
+ }
+
+ protected function removeConfidentialUncensoredEventActivity(): int {
+ $deletedEvents = 0;
+
+ $delete = $this->connection->getQueryBuilder();
+ $delete->delete('activity')
+ ->where($delete->expr()->neq('affecteduser', $delete->createParameter('owner')))
+ ->andWhere($delete->expr()->eq('object_type', $delete->createParameter('type')))
+ ->andWhere($delete->expr()->eq('object_id', $delete->createParameter('calendar_id')))
+ ->andWhere($delete->expr()->like('subjectparams', $delete->createParameter('event_uid')))
+ ->andWhere($delete->expr()->notLike('subjectparams', $delete->createParameter('filtered_name')));
+
+ $query = $this->connection->getQueryBuilder();
+ $query->select('c.principaluri', 'o.calendarid', 'o.uid')
+ ->from('calendarobjects', 'o')
+ ->leftJoin('o', 'calendars', 'c', $query->expr()->eq('c.id', 'o.calendarid'))
+ ->where($query->expr()->eq('o.classification', $query->createNamedParameter(CalDavBackend::CLASSIFICATION_CONFIDENTIAL)));
+ $result = $query->executeQuery();
+
+ while ($row = $result->fetch()) {
+ if ($row['principaluri'] === null) {
+ continue;
+ }
+
+ $delete->setParameter('owner', $this->getPrincipal($row['principaluri']))
+ ->setParameter('type', 'calendar')
+ ->setParameter('calendar_id', $row['calendarid'])
+ ->setParameter('event_uid', '%' . $this->connection->escapeLikeParameter('{"id":"' . $row['uid'] . '"') . '%')
+ ->setParameter('filtered_name', '%' . $this->connection->escapeLikeParameter('{"id":"' . $row['uid'] . '","name":"Busy"') . '%');
+ $deletedEvents += $delete->executeStatement();
+ }
+ $result->closeCursor();
+
+ return $deletedEvents;
+ }
+
+ protected function getPrincipal(string $principalUri): string {
+ $uri = explode('/', $principalUri);
+ return array_pop($uri);
+ }
+}
diff --git a/apps/dav/lib/Migration/RemoveDeletedUsersCalendarSubscriptions.php b/apps/dav/lib/Migration/RemoveDeletedUsersCalendarSubscriptions.php
new file mode 100644
index 00000000000..e2b2b701e74
--- /dev/null
+++ b/apps/dav/lib/Migration/RemoveDeletedUsersCalendarSubscriptions.php
@@ -0,0 +1,123 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\Exception;
+use OCP\IDBConnection;
+use OCP\IUserManager;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class RemoveDeletedUsersCalendarSubscriptions implements IRepairStep {
+ /** @var int */
+ private $progress = 0;
+
+ /** @var int[] */
+ private $orphanSubscriptionIds = [];
+
+ private const SUBSCRIPTIONS_CHUNK_SIZE = 1000;
+
+ public function __construct(
+ private IDBConnection $connection,
+ private IUserManager $userManager,
+ ) {
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName(): string {
+ return 'Clean up old calendar subscriptions from deleted users that were not cleaned-up';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function run(IOutput $output) {
+ $nbSubscriptions = $this->countSubscriptions();
+
+ $output->startProgress($nbSubscriptions);
+
+ while ($this->progress < $nbSubscriptions) {
+ $this->checkSubscriptions();
+
+ $this->progress += self::SUBSCRIPTIONS_CHUNK_SIZE;
+ $output->advance(min(self::SUBSCRIPTIONS_CHUNK_SIZE, $nbSubscriptions));
+ }
+ $output->finishProgress();
+ $this->deleteOrphanSubscriptions();
+
+ $output->info(sprintf('%d calendar subscriptions without an user have been cleaned up', count($this->orphanSubscriptionIds)));
+ }
+
+ /**
+ * @throws Exception
+ */
+ private function countSubscriptions(): int {
+ $qb = $this->connection->getQueryBuilder();
+ $query = $qb->select($qb->func()->count('*'))
+ ->from('calendarsubscriptions');
+
+ $result = $query->execute();
+ $count = $result->fetchOne();
+ $result->closeCursor();
+
+ if ($count !== false) {
+ $count = (int)$count;
+ } else {
+ $count = 0;
+ }
+
+ return $count;
+ }
+
+ /**
+ * @throws Exception
+ */
+ private function checkSubscriptions(): void {
+ $qb = $this->connection->getQueryBuilder();
+ $query = $qb->selectDistinct(['id', 'principaluri'])
+ ->from('calendarsubscriptions')
+ ->setMaxResults(self::SUBSCRIPTIONS_CHUNK_SIZE)
+ ->setFirstResult($this->progress);
+
+ $result = $query->execute();
+ while ($row = $result->fetch()) {
+ $username = $this->getPrincipal($row['principaluri']);
+ if (!$this->userManager->userExists($username)) {
+ $this->orphanSubscriptionIds[] = (int)$row['id'];
+ }
+ }
+ $result->closeCursor();
+ }
+
+ /**
+ * @throws Exception
+ */
+ private function deleteOrphanSubscriptions(): void {
+ foreach ($this->orphanSubscriptionIds as $orphanSubscriptionID) {
+ $this->deleteOrphanSubscription($orphanSubscriptionID);
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ private function deleteOrphanSubscription(int $orphanSubscriptionID): void {
+ $qb = $this->connection->getQueryBuilder();
+ $qb->delete('calendarsubscriptions')
+ ->where($qb->expr()->eq('id', $qb->createNamedParameter($orphanSubscriptionID)))
+ ->executeStatement();
+ }
+
+ private function getPrincipal(string $principalUri): string {
+ $uri = explode('/', $principalUri);
+ return array_pop($uri);
+ }
+}
diff --git a/apps/dav/lib/Migration/RemoveObjectProperties.php b/apps/dav/lib/Migration/RemoveObjectProperties.php
new file mode 100644
index 00000000000..f09293ae0bb
--- /dev/null
+++ b/apps/dav/lib/Migration/RemoveObjectProperties.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class RemoveObjectProperties implements IRepairStep {
+ private const RESOURCE_TYPE_PROPERTY = '{DAV:}resourcetype';
+ private const ME_CARD_PROPERTY = '{http://calendarserver.org/ns/}me-card';
+ private const CALENDAR_TRANSP_PROPERTY = '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp';
+
+ /**
+ * RemoveObjectProperties constructor.
+ *
+ * @param IDBConnection $connection
+ */
+ public function __construct(
+ private IDBConnection $connection,
+ ) {
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getName() {
+ return 'Remove invalid object properties';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function run(IOutput $output) {
+ $query = $this->connection->getQueryBuilder();
+ $updated = $query->delete('properties')
+ ->where($query->expr()->in('propertyname', $query->createNamedParameter([self::RESOURCE_TYPE_PROPERTY, self::ME_CARD_PROPERTY, self::CALENDAR_TRANSP_PROPERTY], IQueryBuilder::PARAM_STR_ARRAY)))
+ ->andWhere($query->expr()->eq('propertyvalue', $query->createNamedParameter('Object'), IQueryBuilder::PARAM_STR))
+ ->executeStatement();
+
+ $output->info("$updated invalid object properties removed.");
+ }
+}
diff --git a/apps/dav/lib/Migration/RemoveOrphanEventsAndContacts.php b/apps/dav/lib/Migration/RemoveOrphanEventsAndContacts.php
new file mode 100644
index 00000000000..143dc3cd1e6
--- /dev/null
+++ b/apps/dav/lib/Migration/RemoveOrphanEventsAndContacts.php
@@ -0,0 +1,52 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCA\DAV\BackgroundJob\CleanupOrphanedChildrenJob;
+use OCP\BackgroundJob\IJobList;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class RemoveOrphanEventsAndContacts implements IRepairStep {
+ public function __construct(
+ private readonly IJobList $jobList,
+ ) {
+ }
+
+ public function getName(): string {
+ return 'Queue jobs to clean up orphan event and contact data';
+ }
+
+ public function run(IOutput $output): void {
+ $this->queueJob('calendarobjects', 'calendars', 'calendarid', '%d events without a calendar have been cleaned up');
+ $this->queueJob('calendarobjects_props', 'calendarobjects', 'objectid', '%d properties without an events have been cleaned up');
+ $this->queueJob('calendarchanges', 'calendars', 'calendarid', '%d changes without a calendar have been cleaned up');
+
+ $this->queueJob('calendarobjects', 'calendarsubscriptions', 'calendarid', '%d cached events without a calendar subscription have been cleaned up');
+ $this->queueJob('calendarchanges', 'calendarsubscriptions', 'calendarid', '%d changes without a calendar subscription have been cleaned up');
+
+ $this->queueJob('cards', 'addressbooks', 'addressbookid', '%d contacts without an addressbook have been cleaned up');
+ $this->queueJob('cards_properties', 'cards', 'cardid', '%d properties without a contact have been cleaned up');
+ $this->queueJob('addressbookchanges', 'addressbooks', 'addressbookid', '%d changes without an addressbook have been cleaned up');
+ }
+
+ private function queueJob(
+ string $childTable,
+ string $parentTable,
+ string $parentId,
+ string $logMessage,
+ ): void {
+ $this->jobList->add(CleanupOrphanedChildrenJob::class, [
+ CleanupOrphanedChildrenJob::ARGUMENT_CHILD_TABLE => $childTable,
+ CleanupOrphanedChildrenJob::ARGUMENT_PARENT_TABLE => $parentTable,
+ CleanupOrphanedChildrenJob::ARGUMENT_PARENT_ID => $parentId,
+ CleanupOrphanedChildrenJob::ARGUMENT_LOG_MESSAGE => $logMessage,
+ ]);
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1004Date20170825134824.php b/apps/dav/lib/Migration/Version1004Date20170825134824.php
new file mode 100644
index 00000000000..4bf9637b697
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1004Date20170825134824.php
@@ -0,0 +1,481 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1004Date20170825134824 extends SimpleMigrationStep {
+ /**
+ * @param IOutput $output
+ * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ * @since 13.0.0
+ */
+ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if (!$schema->hasTable('addressbooks')) {
+ $table = $schema->createTable('addressbooks');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('principaluri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('displayname', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('uri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('description', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('synctoken', 'integer', [
+ 'notnull' => true,
+ 'default' => 1,
+ 'length' => 10,
+ 'unsigned' => true,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addUniqueIndex(['principaluri', 'uri'], 'addressbook_index');
+ }
+
+ if (!$schema->hasTable('cards')) {
+ $table = $schema->createTable('cards');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('addressbookid', 'integer', [
+ 'notnull' => true,
+ 'default' => 0,
+ ]);
+ $table->addColumn('carddata', 'blob', [
+ 'notnull' => false,
+ ]);
+ $table->addColumn('uri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('lastmodified', 'bigint', [
+ 'notnull' => false,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('etag', 'string', [
+ 'notnull' => false,
+ 'length' => 32,
+ ]);
+ $table->addColumn('size', 'bigint', [
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->setPrimaryKey(['id']);
+ }
+
+ if (!$schema->hasTable('addressbookchanges')) {
+ $table = $schema->createTable('addressbookchanges');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('uri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('synctoken', 'integer', [
+ 'notnull' => true,
+ 'default' => 1,
+ 'length' => 10,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('addressbookid', 'integer', [
+ 'notnull' => true,
+ ]);
+ $table->addColumn('operation', 'smallint', [
+ 'notnull' => true,
+ 'length' => 1,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['addressbookid', 'synctoken'], 'addressbookid_synctoken');
+ }
+
+ if (!$schema->hasTable('calendarobjects')) {
+ $table = $schema->createTable('calendarobjects');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('calendardata', 'blob', [
+ 'notnull' => false,
+ ]);
+ $table->addColumn('uri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('calendarid', 'integer', [
+ 'notnull' => true,
+ 'length' => 10,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('lastmodified', 'integer', [
+ 'notnull' => false,
+ 'length' => 10,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('etag', 'string', [
+ 'notnull' => false,
+ 'length' => 32,
+ ]);
+ $table->addColumn('size', 'bigint', [
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('componenttype', 'string', [
+ 'notnull' => false,
+ 'length' => 8,
+ ]);
+ $table->addColumn('firstoccurence', 'bigint', [
+ 'notnull' => false,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('lastoccurence', 'bigint', [
+ 'notnull' => false,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('uid', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('classification', 'integer', [
+ 'notnull' => false,
+ 'default' => 0,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addUniqueIndex(['calendarid', 'uri'], 'calobjects_index');
+ }
+
+ if (!$schema->hasTable('calendars')) {
+ $table = $schema->createTable('calendars');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('principaluri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('displayname', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('uri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('synctoken', 'integer', [
+ 'notnull' => true,
+ 'default' => 1,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('description', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('calendarorder', 'integer', [
+ 'notnull' => true,
+ 'default' => 0,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('calendarcolor', 'string', [
+ 'notnull' => false,
+ ]);
+ $table->addColumn('timezone', 'text', [
+ 'notnull' => false,
+ ]);
+ $table->addColumn('components', 'string', [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('transparent', 'smallint', [
+ 'notnull' => true,
+ 'length' => 1,
+ 'default' => 0,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addUniqueIndex(['principaluri', 'uri'], 'calendars_index');
+ } else {
+ $table = $schema->getTable('calendars');
+ $table->changeColumn('components', [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ }
+
+ if (!$schema->hasTable('calendarchanges')) {
+ $table = $schema->createTable('calendarchanges');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('uri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('synctoken', 'integer', [
+ 'notnull' => true,
+ 'default' => 1,
+ 'length' => 10,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('calendarid', 'integer', [
+ 'notnull' => true,
+ ]);
+ $table->addColumn('operation', 'smallint', [
+ 'notnull' => true,
+ 'length' => 1,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['calendarid', 'synctoken'], 'calendarid_synctoken');
+ }
+
+ if (!$schema->hasTable('calendarsubscriptions')) {
+ $table = $schema->createTable('calendarsubscriptions');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('uri', 'string', [
+ 'notnull' => false,
+ ]);
+ $table->addColumn('principaluri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('source', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('displayname', 'string', [
+ 'notnull' => false,
+ 'length' => 100,
+ ]);
+ $table->addColumn('refreshrate', 'string', [
+ 'notnull' => false,
+ 'length' => 10,
+ ]);
+ $table->addColumn('calendarorder', 'integer', [
+ 'notnull' => true,
+ 'default' => 0,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('calendarcolor', 'string', [
+ 'notnull' => false,
+ ]);
+ $table->addColumn('striptodos', 'smallint', [
+ 'notnull' => false,
+ 'length' => 1,
+ ]);
+ $table->addColumn('stripalarms', 'smallint', [
+ 'notnull' => false,
+ 'length' => 1,
+ ]);
+ $table->addColumn('stripattachments', 'smallint', [
+ 'notnull' => false,
+ 'length' => 1,
+ ]);
+ $table->addColumn('lastmodified', 'integer', [
+ 'notnull' => false,
+ 'unsigned' => true,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addUniqueIndex(['principaluri', 'uri'], 'calsub_index');
+ } else {
+ $table = $schema->getTable('calendarsubscriptions');
+ $table->changeColumn('lastmodified', [
+ 'notnull' => false,
+ 'unsigned' => true,
+ ]);
+ }
+
+ if (!$schema->hasTable('schedulingobjects')) {
+ $table = $schema->createTable('schedulingobjects');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('principaluri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('calendardata', 'blob', [
+ 'notnull' => false,
+ ]);
+ $table->addColumn('uri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('lastmodified', 'integer', [
+ 'notnull' => false,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('etag', 'string', [
+ 'notnull' => false,
+ 'length' => 32,
+ ]);
+ $table->addColumn('size', 'bigint', [
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['principaluri'], 'schedulobj_principuri_index');
+ $table->addIndex(['lastmodified'], 'schedulobj_lastmodified_idx');
+ }
+
+ if (!$schema->hasTable('cards_properties')) {
+ $table = $schema->createTable('cards_properties');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('addressbookid', 'bigint', [
+ 'notnull' => true,
+ 'length' => 11,
+ 'default' => 0,
+ ]);
+ $table->addColumn('cardid', 'bigint', [
+ 'notnull' => true,
+ 'length' => 11,
+ 'default' => 0,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('name', 'string', [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('value', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('preferred', 'integer', [
+ 'notnull' => true,
+ 'length' => 4,
+ 'default' => 1,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['cardid'], 'card_contactid_index');
+ $table->addIndex(['name'], 'card_name_index');
+ $table->addIndex(['value'], 'card_value_index');
+ }
+
+ if (!$schema->hasTable('calendarobjects_props')) {
+ $table = $schema->createTable('calendarobjects_props');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('calendarid', 'bigint', [
+ 'notnull' => true,
+ 'length' => 11,
+ 'default' => 0,
+ ]);
+ $table->addColumn('objectid', 'bigint', [
+ 'notnull' => true,
+ 'length' => 11,
+ 'default' => 0,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('name', 'string', [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('parameter', 'string', [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('value', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['objectid'], 'calendarobject_index');
+ $table->addIndex(['name'], 'calendarobject_name_index');
+ $table->addIndex(['value'], 'calendarobject_value_index');
+ }
+
+ if (!$schema->hasTable('dav_shares')) {
+ $table = $schema->createTable('dav_shares');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('principaluri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('type', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('access', 'smallint', [
+ 'notnull' => false,
+ 'length' => 1,
+ ]);
+ $table->addColumn('resourceid', 'integer', [
+ 'notnull' => true,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('publicuri', 'string', [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->setPrimaryKey(['id']);
+ $table->addUniqueIndex(['principaluri', 'resourceid', 'type', 'publicuri'], 'dav_shares_index');
+ // modified on 2024-6-21 to add performance improving indices on new instances
+ $table->addIndex(['resourceid', 'type'], 'dav_shares_resourceid_type');
+ $table->addIndex(['resourceid', 'access'], 'dav_shares_resourceid_access');
+ }
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1004Date20170919104507.php b/apps/dav/lib/Migration/Version1004Date20170919104507.php
new file mode 100644
index 00000000000..678d92d2b83
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1004Date20170919104507.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1004Date20170919104507 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ * @since 13.0.0
+ */
+ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $table = $schema->getTable('addressbooks');
+ $column = $table->getColumn('id');
+ $column->setUnsigned(true);
+
+ $table = $schema->getTable('calendarobjects');
+ $column = $table->getColumn('id');
+ $column->setUnsigned(true);
+
+ $table = $schema->getTable('calendarchanges');
+ $column = $table->getColumn('id');
+ $column->setUnsigned(true);
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1004Date20170924124212.php b/apps/dav/lib/Migration/Version1004Date20170924124212.php
new file mode 100644
index 00000000000..4d221e91132
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1004Date20170924124212.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1004Date20170924124212 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ * @since 13.0.0
+ */
+ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $table = $schema->getTable('cards');
+ // Dropped in Version1030Date20240205103243 because cards_abid is redundant with cards_abiduri
+ // $table->addIndex(['addressbookid'], 'cards_abid');
+ $table->addIndex(['addressbookid', 'uri'], 'cards_abiduri');
+
+ $table = $schema->getTable('cards_properties');
+ // Removed later on
+ // $table->addIndex(['addressbookid'], 'cards_prop_abid');
+ // Added later on
+ $table->addIndex(['addressbookid', 'name', 'value'], 'cards_prop_abid_name_value', );
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1004Date20170926103422.php b/apps/dav/lib/Migration/Version1004Date20170926103422.php
new file mode 100644
index 00000000000..ec56e035006
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1004Date20170926103422.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\Migration\BigIntMigration;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version1004Date20170926103422 extends BigIntMigration {
+
+ /**
+ * @return array Returns an array with the following structure
+ * ['table1' => ['column1', 'column2'], ...]
+ * @since 13.0.0
+ */
+ protected function getColumnsByTable() {
+ return [
+ 'addressbooks' => ['id'],
+ 'addressbookchanges' => ['id', 'addressbookid'],
+ 'calendars' => ['id'],
+ 'calendarchanges' => ['id', 'calendarid'],
+ 'calendarobjects' => ['id', 'calendarid'],
+ 'calendarobjects_props' => ['id', 'calendarid', 'objectid'],
+ 'calendarsubscriptions' => ['id'],
+ 'cards' => ['id', 'addressbookid'],
+ 'cards_properties' => ['id', 'addressbookid', 'cardid'],
+ 'dav_shares' => ['id', 'resourceid'],
+ 'schedulingobjects' => ['id'],
+ ];
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1005Date20180413093149.php b/apps/dav/lib/Migration/Version1005Date20180413093149.php
new file mode 100644
index 00000000000..db071c65d98
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1005Date20180413093149.php
@@ -0,0 +1,64 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1005Date20180413093149 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
+
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if (!$schema->hasTable('directlink')) {
+ $table = $schema->createTable('directlink');
+
+ $table->addColumn('id', Types::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('user_id', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('file_id', Types::BIGINT, [
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('token', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 60,
+ ]);
+ $table->addColumn('expiration', Types::BIGINT, [
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+
+ $table->setPrimaryKey(['id'], 'directlink_id_idx');
+ $table->addIndex(['token'], 'directlink_token_idx');
+ $table->addIndex(['expiration'], 'directlink_expiration_idx');
+
+ return $schema;
+ }
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1005Date20180530124431.php b/apps/dav/lib/Migration/Version1005Date20180530124431.php
new file mode 100644
index 00000000000..b5f9ff26962
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1005Date20180530124431.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1005Date20180530124431 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ * @since 13.0.0
+ */
+ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $types = ['resources', 'rooms'];
+ foreach ($types as $type) {
+ if (!$schema->hasTable('calendar_' . $type)) {
+ $table = $schema->createTable('calendar_' . $type);
+
+ $table->addColumn('id', Types::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('backend_id', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('resource_id', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('email', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('displayname', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('group_restrictions', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 4000,
+ ]);
+
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['backend_id', 'resource_id'], 'calendar_' . $type . '_bkdrsc');
+ $table->addIndex(['email'], 'calendar_' . $type . '_email');
+ $table->addIndex(['displayname'], 'calendar_' . $type . '_name');
+ }
+ }
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1006Date20180619154313.php b/apps/dav/lib/Migration/Version1006Date20180619154313.php
new file mode 100644
index 00000000000..231861a68c4
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1006Date20180619154313.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version1006Date20180619154313 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ * @since 13.0.0
+ */
+ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if (!$schema->hasTable('calendar_invitations')) {
+ $table = $schema->createTable('calendar_invitations');
+
+ $table->addColumn('id', Types::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('uid', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('recurrenceid', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('attendee', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('organizer', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('sequence', Types::BIGINT, [
+ 'notnull' => false,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('token', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 60,
+ ]);
+ $table->addColumn('expiration', Types::BIGINT, [
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['token'], 'calendar_invitation_tokens');
+
+ return $schema;
+ }
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1006Date20180628111625.php b/apps/dav/lib/Migration/Version1006Date20180628111625.php
new file mode 100644
index 00000000000..f4be26e6ad0
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1006Date20180628111625.php
@@ -0,0 +1,91 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1006Date20180628111625 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if ($schema->hasTable('calendarchanges')) {
+ $calendarChangesTable = $schema->getTable('calendarchanges');
+ $calendarChangesTable->addColumn('calendartype', Types::INTEGER, [
+ 'notnull' => true,
+ 'default' => 0,
+ ]);
+
+ if ($calendarChangesTable->hasIndex('calendarid_synctoken')) {
+ $calendarChangesTable->dropIndex('calendarid_synctoken');
+ }
+ $calendarChangesTable->addIndex(['calendarid', 'calendartype', 'synctoken'], 'calid_type_synctoken');
+ }
+
+ if ($schema->hasTable('calendarobjects')) {
+ $calendarObjectsTable = $schema->getTable('calendarobjects');
+ $calendarObjectsTable->addColumn('calendartype', Types::INTEGER, [
+ 'notnull' => true,
+ 'default' => 0,
+ ]);
+
+ if ($calendarObjectsTable->hasIndex('calobjects_index')) {
+ $calendarObjectsTable->dropIndex('calobjects_index');
+ }
+ $calendarObjectsTable->addUniqueIndex(['calendarid', 'calendartype', 'uri'], 'calobjects_index');
+ $calendarObjectsTable->addUniqueIndex(['calendarid', 'calendartype', 'uid'], 'calobjects_by_uid_index');
+ }
+
+ if ($schema->hasTable('calendarobjects_props')) {
+ $calendarObjectsPropsTable = $schema->getTable('calendarobjects_props');
+ $calendarObjectsPropsTable->addColumn('calendartype', Types::INTEGER, [
+ 'notnull' => true,
+ 'default' => 0,
+ ]);
+
+
+ if ($calendarObjectsPropsTable->hasIndex('calendarobject_index')) {
+ $calendarObjectsPropsTable->dropIndex('calendarobject_index');
+ }
+ if ($calendarObjectsPropsTable->hasIndex('calendarobject_name_index')) {
+ $calendarObjectsPropsTable->dropIndex('calendarobject_name_index');
+ }
+ if ($calendarObjectsPropsTable->hasIndex('calendarobject_value_index')) {
+ $calendarObjectsPropsTable->dropIndex('calendarobject_value_index');
+ }
+
+ $calendarObjectsPropsTable->addIndex(['objectid', 'calendartype'], 'calendarobject_index');
+ $calendarObjectsPropsTable->addIndex(['name', 'calendartype'], 'calendarobject_name_index');
+ $calendarObjectsPropsTable->addIndex(['value', 'calendartype'], 'calendarobject_value_index');
+ $calendarObjectsPropsTable->addIndex(['calendarid', 'calendartype'], 'calendarobject_calid_index');
+ }
+
+ if ($schema->hasTable('calendarsubscriptions')) {
+ $calendarSubscriptionsTable = $schema->getTable('calendarsubscriptions');
+ $calendarSubscriptionsTable->addColumn('synctoken', 'integer', [
+ 'notnull' => true,
+ 'default' => 1,
+ 'length' => 10,
+ 'unsigned' => true,
+ ]);
+ }
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1008Date20181030113700.php b/apps/dav/lib/Migration/Version1008Date20181030113700.php
new file mode 100644
index 00000000000..d2354a185df
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1008Date20181030113700.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1008Date20181030113700 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $table = $schema->getTable('cards');
+ $table->addColumn('uid', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 255
+ ]);
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1008Date20181105104826.php b/apps/dav/lib/Migration/Version1008Date20181105104826.php
new file mode 100644
index 00000000000..82612307cbb
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1008Date20181105104826.php
@@ -0,0 +1,60 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1008Date20181105104826 extends SimpleMigrationStep {
+
+ /**
+ * Version1008Date20181105104826 constructor.
+ *
+ * @param IDBConnection $connection
+ */
+ public function __construct(
+ private IDBConnection $connection,
+ ) {
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+ $table = $schema->getTable('calendarsubscriptions');
+
+ $table->addColumn('source_copy', Types::TEXT, [
+ 'notnull' => false,
+ 'length' => null,
+ ]);
+
+ return $schema;
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ */
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
+ $qb = $this->connection->getQueryBuilder();
+ $qb->update('calendarsubscriptions')
+ ->set('source_copy', 'source')
+ ->execute();
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1008Date20181105104833.php b/apps/dav/lib/Migration/Version1008Date20181105104833.php
new file mode 100644
index 00000000000..3d4094d8072
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1008Date20181105104833.php
@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1008Date20181105104833 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+ $table = $schema->getTable('calendarsubscriptions');
+ $table->dropColumn('source');
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1008Date20181105110300.php b/apps/dav/lib/Migration/Version1008Date20181105110300.php
new file mode 100644
index 00000000000..72e8dee1bf8
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1008Date20181105110300.php
@@ -0,0 +1,59 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1008Date20181105110300 extends SimpleMigrationStep {
+
+ /**
+ * Version1008Date20181105110300 constructor.
+ *
+ * @param IDBConnection $connection
+ */
+ public function __construct(
+ private IDBConnection $connection,
+ ) {
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+ $table = $schema->getTable('calendarsubscriptions');
+ $table->addColumn('source', Types::TEXT, [
+ 'notnull' => false,
+ 'length' => null,
+ ]);
+
+ return $schema;
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ */
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
+ $qb = $this->connection->getQueryBuilder();
+ $qb->update('calendarsubscriptions')
+ ->set('source', 'source_copy')
+ ->execute();
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1008Date20181105112049.php b/apps/dav/lib/Migration/Version1008Date20181105112049.php
new file mode 100644
index 00000000000..eb18eacb0b1
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1008Date20181105112049.php
@@ -0,0 +1,32 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1008Date20181105112049 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+ $table = $schema->getTable('calendarsubscriptions');
+ $table->dropColumn('source_copy');
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1008Date20181114084440.php b/apps/dav/lib/Migration/Version1008Date20181114084440.php
new file mode 100644
index 00000000000..3718f16f54a
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1008Date20181114084440.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1008Date20181114084440 extends SimpleMigrationStep {
+
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if ($schema->hasTable('calendarchanges')) {
+ $calendarChangesTable = $schema->getTable('calendarchanges');
+ if ($calendarChangesTable->hasIndex('calendarid_calendartype_synctoken')) {
+ $calendarChangesTable->dropIndex('calendarid_calendartype_synctoken');
+ }
+
+ if (!$calendarChangesTable->hasIndex('calid_type_synctoken')) {
+ $calendarChangesTable->addIndex(['calendarid', 'calendartype', 'synctoken'], 'calid_type_synctoken');
+ }
+ }
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1011Date20190725113607.php b/apps/dav/lib/Migration/Version1011Date20190725113607.php
new file mode 100644
index 00000000000..4524dca9c83
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1011Date20190725113607.php
@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version1011Date20190725113607 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ * @since 13.0.0
+ */
+ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $types = ['resource', 'room'];
+ foreach ($types as $type) {
+ if (!$schema->hasTable($this->getMetadataTableName($type))) {
+ $table = $schema->createTable($this->getMetadataTableName($type));
+
+ $table->addColumn('id', Types::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn($type . '_id', Types::BIGINT, [
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('key', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('value', Types::STRING, [
+ 'notnull' => false,
+ 'length' => 4000,
+ ]);
+
+ $table->setPrimaryKey(['id']);
+ $table->addIndex([$type . '_id', 'key'], $this->getMetadataTableName($type) . '_idk');
+ }
+ }
+
+ return $schema;
+ }
+
+ /**
+ * @param string $type
+ * @return string
+ */
+ private function getMetadataTableName(string $type):string {
+ return 'calendar_' . $type . 's_md';
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1011Date20190806104428.php b/apps/dav/lib/Migration/Version1011Date20190806104428.php
new file mode 100644
index 00000000000..183dcd4bf1e
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1011Date20190806104428.php
@@ -0,0 +1,58 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version1011Date20190806104428 extends SimpleMigrationStep {
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $table = $schema->createTable('dav_cal_proxy');
+ $table->addColumn('id', Types::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('owner_id', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->addColumn('proxy_id', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->addColumn('permissions', Types::INTEGER, [
+ 'notnull' => false,
+ 'length' => 4,
+ 'unsigned' => true,
+ ]);
+
+ $table->setPrimaryKey(['id']);
+ $table->addUniqueIndex(['owner_id', 'proxy_id', 'permissions'], 'dav_cal_proxy_uidx');
+ $table->addIndex(['proxy_id'], 'dav_cal_proxy_ipid');
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1012Date20190808122342.php b/apps/dav/lib/Migration/Version1012Date20190808122342.php
new file mode 100644
index 00000000000..717bcbc1119
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1012Date20190808122342.php
@@ -0,0 +1,103 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version1012Date20190808122342 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ * @since 17.0.0
+ */
+ public function changeSchema(IOutput $output,
+ \Closure $schemaClosure,
+ array $options):?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if (!$schema->hasTable('calendar_reminders')) {
+ $table = $schema->createTable('calendar_reminders');
+
+ $table->addColumn('id', Types::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('calendar_id', Types::BIGINT, [
+ 'notnull' => true,
+ 'length' => 11,
+ ]);
+ $table->addColumn('object_id', Types::BIGINT, [
+ 'notnull' => true,
+ 'length' => 11,
+ ]);
+ $table->addColumn('is_recurring', Types::SMALLINT, [
+ 'notnull' => false,
+ 'length' => 1,
+ ]);
+ $table->addColumn('uid', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('recurrence_id', Types::BIGINT, [
+ 'notnull' => false,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('is_recurrence_exception', Types::SMALLINT, [
+ 'notnull' => true,
+ 'length' => 1,
+ ]);
+ $table->addColumn('event_hash', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('alarm_hash', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('type', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('is_relative', Types::SMALLINT, [
+ 'notnull' => true,
+ 'length' => 1,
+ ]);
+ $table->addColumn('notification_date', Types::BIGINT, [
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('is_repeat_based', Types::SMALLINT, [
+ 'notnull' => true,
+ 'length' => 1,
+ ]);
+
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['object_id'], 'calendar_reminder_objid');
+ $table->addIndex(['uid', 'recurrence_id'], 'calendar_reminder_uidrec');
+
+ return $schema;
+ }
+
+ return null;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1016Date20201109085907.php b/apps/dav/lib/Migration/Version1016Date20201109085907.php
new file mode 100644
index 00000000000..f0f4b8b77c1
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1016Date20201109085907.php
@@ -0,0 +1,43 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1016Date20201109085907 extends SimpleMigrationStep {
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $result = $this->ensureColumnIsNullable($schema, 'calendar_reminders', 'is_recurring');
+
+ return $result ? $schema : null;
+ }
+
+ protected function ensureColumnIsNullable(ISchemaWrapper $schema, string $tableName, string $columnName): bool {
+ $table = $schema->getTable($tableName);
+ $column = $table->getColumn($columnName);
+
+ if ($column->getNotnull()) {
+ $column->setNotnull(false);
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1017Date20210216083742.php b/apps/dav/lib/Migration/Version1017Date20210216083742.php
new file mode 100644
index 00000000000..43bece200b8
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1017Date20210216083742.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version1017Date20210216083742 extends SimpleMigrationStep {
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $table = $schema->getTable('dav_cal_proxy');
+ if ($table->hasIndex('dav_cal_proxy_ioid')) {
+ $table->dropIndex('dav_cal_proxy_ioid');
+ }
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1018Date20210312100735.php b/apps/dav/lib/Migration/Version1018Date20210312100735.php
new file mode 100644
index 00000000000..8199d3e9cd2
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1018Date20210312100735.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1018Date20210312100735 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $calendarsTable = $schema->getTable('calendars');
+ $calendarsTable->addColumn('deleted_at', Types::INTEGER, [
+ 'notnull' => false,
+ 'length' => 4,
+ 'unsigned' => true,
+ ]);
+ $calendarsTable->addIndex([
+ 'principaluri',
+ 'deleted_at',
+ ], 'cals_princ_del_idx');
+
+ $calendarObjectsTable = $schema->getTable('calendarobjects');
+ $calendarObjectsTable->addColumn('deleted_at', Types::INTEGER, [
+ 'notnull' => false,
+ 'length' => 4,
+ 'unsigned' => true,
+ ]);
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1024Date20211221144219.php b/apps/dav/lib/Migration/Version1024Date20211221144219.php
new file mode 100644
index 00000000000..656a50809cd
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1024Date20211221144219.php
@@ -0,0 +1,61 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\DAV\Migration;
+
+use Closure;
+use Doctrine\DBAL\Schema\SchemaException;
+use OCA\DAV\DAV\CustomPropertiesBackend;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version1024Date20211221144219 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ */
+ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ * @throws SchemaException
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+ $propertiesTable = $schema->getTable('properties');
+
+ if ($propertiesTable->hasColumn('valuetype')) {
+ return null;
+ }
+ $propertiesTable->addColumn('valuetype', Types::SMALLINT, [
+ 'notnull' => false,
+ 'default' => CustomPropertiesBackend::PROPERTY_TYPE_STRING
+ ]);
+
+ return $schema;
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ */
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1025Date20240308063933.php b/apps/dav/lib/Migration/Version1025Date20240308063933.php
new file mode 100644
index 00000000000..d84acf8fea9
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1025Date20240308063933.php
@@ -0,0 +1,91 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\AppFramework\Services\IAppConfig;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\DB\Types;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1025Date20240308063933 extends SimpleMigrationStep {
+
+ public function __construct(
+ private IAppConfig $appConfig,
+ private IDBConnection $db,
+ ) {
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure(): ISchemaWrapper $schemaClosure
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ foreach (['addressbookchanges', 'calendarchanges'] as $tableName) {
+ $table = $schema->getTable($tableName);
+ if (!$table->hasColumn('created_at')) {
+ $table->addColumn('created_at', Types::INTEGER, [
+ 'notnull' => true,
+ 'length' => 4,
+ 'default' => 0,
+ ]);
+ }
+ }
+
+ return $schema;
+ }
+
+ public function postSchemaChange(IOutput $output, \Closure $schemaClosure, array $options): void {
+ // The threshold is higher than the default of \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob
+ // but small enough to fit into a cluster transaction size.
+ // For a 50k users instance that would still keep 10 changes on average.
+ $limit = max(1, (int)$this->appConfig->getAppValue('totalNumberOfSyncTokensToKeep', '500000'));
+
+ foreach (['addressbookchanges', 'calendarchanges'] as $tableName) {
+ $thresholdSelect = $this->db->getQueryBuilder();
+ $thresholdSelect->select('id')
+ ->from($tableName)
+ ->orderBy('id', 'desc')
+ ->setFirstResult($limit)
+ ->setMaxResults(1);
+ $oldestIdResult = $thresholdSelect->executeQuery();
+ $oldestId = $oldestIdResult->fetchColumn();
+ $oldestIdResult->closeCursor();
+
+ $qb = $this->db->getQueryBuilder();
+
+ $update = $qb->update($tableName)
+ ->set('created_at', $qb->createNamedParameter(time(), IQueryBuilder::PARAM_INT))
+ ->where(
+ $qb->expr()->eq('created_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)),
+ );
+
+ // If there is a lot of data we only set timestamp for the most recent rows
+ // because the rest will be deleted by \OCA\DAV\BackgroundJob\PruneOutdatedSyncTokensJob
+ // anyway.
+ if ($oldestId !== false) {
+ $update->andWhere($qb->expr()->gt('id', $qb->createNamedParameter($oldestId, IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT));
+ }
+
+ $updated = $update->executeStatement();
+
+ $output->debug('Added a default creation timestamp to ' . $updated . ' rows in ' . $tableName);
+ }
+ }
+
+}
diff --git a/apps/dav/lib/Migration/Version1027Date20230504122946.php b/apps/dav/lib/Migration/Version1027Date20230504122946.php
new file mode 100644
index 00000000000..89c192a8419
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1027Date20230504122946.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCA\DAV\CardDAV\SyncService;
+use OCP\DB\ISchemaWrapper;
+use OCP\IConfig;
+use OCP\IUserManager;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+use Psr\Log\LoggerInterface;
+use Throwable;
+
+class Version1027Date20230504122946 extends SimpleMigrationStep {
+ public function __construct(
+ private SyncService $syncService,
+ private LoggerInterface $logger,
+ private IUserManager $userManager,
+ private IConfig $config,
+ ) {
+ }
+ /**
+ * @param IOutput $output
+ * @param Closure(): ISchemaWrapper $schemaClosure
+ * @param array $options
+ */
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ if ($this->userManager->countSeenUsers() > 100 || $this->userManager->countUsersTotal(100) >= 100) {
+ $this->config->setAppValue('dav', 'needs_system_address_book_sync', 'yes');
+ $output->info('Could not sync system address books during update - too many user records have been found. Please call occ dav:sync-system-addressbook manually.');
+ return;
+ }
+
+ try {
+ $this->syncService->syncInstance();
+ $this->config->setAppValue('dav', 'needs_system_address_book_sync', 'no');
+ } catch (Throwable $e) {
+ $this->config->setAppValue('dav', 'needs_system_address_book_sync', 'yes');
+ $this->logger->error('Could not sync system address books during update', [
+ 'exception' => $e,
+ ]);
+ $output->warning('System address book sync failed. See logs for details');
+ }
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1029Date20221114151721.php b/apps/dav/lib/Migration/Version1029Date20221114151721.php
new file mode 100644
index 00000000000..dba5e0b1a48
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1029Date20221114151721.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Migration;
+
+use Closure;
+use Doctrine\DBAL\Schema\SchemaException;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1029Date20221114151721 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ * @throws SchemaException
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+ $calendarObjectsTable = $schema->getTable('calendarobjects');
+ if (!$calendarObjectsTable->hasIndex('calobj_clssfction_index')) {
+ $calendarObjectsTable->addIndex(['classification'], 'calobj_clssfction_index');
+ return $schema;
+ }
+ return null;
+ }
+
+}
diff --git a/apps/dav/lib/Migration/Version1029Date20231004091403.php b/apps/dav/lib/Migration/Version1029Date20231004091403.php
new file mode 100644
index 00000000000..1dcbf9c5dfc
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1029Date20231004091403.php
@@ -0,0 +1,66 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1029Date20231004091403 extends SimpleMigrationStep {
+ /**
+ * @param IOutput $output
+ * @param Closure(): ISchemaWrapper $schemaClosure
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if (!$schema->hasTable('dav_absence')) {
+ $table = $schema->createTable('dav_absence');
+ $table->addColumn('id', Types::INTEGER, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 4,
+ ]);
+ $table->addColumn('user_id', Types::STRING, [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->addColumn('first_day', Types::STRING, [
+ 'length' => 10,
+ 'notnull' => true,
+ ]);
+ $table->addColumn('last_day', Types::STRING, [
+ 'length' => 10,
+ 'notnull' => true,
+ ]);
+ $table->addColumn('status', Types::STRING, [
+ 'length' => 100,
+ 'notnull' => true,
+ ]);
+ $table->addColumn('message', Types::TEXT, [
+ 'notnull' => true,
+ ]);
+ $table->addUniqueIndex(['user_id'], 'dav_absence_uid_idx');
+ } else {
+ $table = $schema->getTable('dav_absence');
+ }
+
+ if ($table->getPrimaryKey() === null) {
+ $table->setPrimaryKey(['id'], 'dav_absence_id_idx');
+ }
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1030Date20240205103243.php b/apps/dav/lib/Migration/Version1030Date20240205103243.php
new file mode 100644
index 00000000000..72cbcafc444
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1030Date20240205103243.php
@@ -0,0 +1,37 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+class Version1030Date20240205103243 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure(): ISchemaWrapper $schemaClosure
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $tableCards = $schema->getTable('cards');
+
+ if ($tableCards->hasIndex('cards_abiduri') && $tableCards->hasIndex('cards_abid')) {
+ $tableCards->dropIndex('cards_abid');
+ }
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1031Date20240610134258.php b/apps/dav/lib/Migration/Version1031Date20240610134258.php
new file mode 100644
index 00000000000..c1242ceb7db
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1031Date20240610134258.php
@@ -0,0 +1,48 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
+use OCP\Migration\Attributes\AddColumn;
+use OCP\Migration\Attributes\ColumnType;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+
+#[AddColumn(table: 'dav_absence', name: 'replacement_user_id', type: ColumnType::STRING)]
+#[AddColumn(table: 'dav_absence', name: 'replacement_user_display_name', type: ColumnType::STRING)]
+class Version1031Date20240610134258 extends SimpleMigrationStep {
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ $tableDavAbsence = $schema->getTable('dav_absence');
+
+ if (!$tableDavAbsence->hasColumn('replacement_user_id')) {
+ $tableDavAbsence->addColumn('replacement_user_id', Types::STRING, [
+ 'notnull' => false,
+ 'default' => '',
+ 'length' => 64,
+ ]);
+ }
+
+ if (!$tableDavAbsence->hasColumn('replacement_user_display_name')) {
+ $tableDavAbsence->addColumn('replacement_user_display_name', Types::STRING, [
+ 'notnull' => false,
+ 'default' => '',
+ 'length' => 64,
+ ]);
+ }
+
+ return $schema;
+ }
+
+}
diff --git a/apps/dav/lib/Migration/Version1034Date20250813093701.php b/apps/dav/lib/Migration/Version1034Date20250813093701.php
new file mode 100644
index 00000000000..10be71f067b
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1034Date20250813093701.php
@@ -0,0 +1,53 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\SimpleMigrationStep;
+use Override;
+
+class Version1034Date20250813093701 extends SimpleMigrationStep {
+ public function __construct(
+ private IDBConnection $db,
+ ) {
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure(): ISchemaWrapper $schemaClosure
+ * @param array $options
+ */
+ #[Override]
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ $qb = $this->db->getQueryBuilder();
+ $qb->delete('properties')
+ ->where($qb->expr()->eq(
+ 'propertyname',
+ $qb->createNamedParameter(
+ '{http://owncloud.org/ns}calendar-enabled',
+ IQueryBuilder::PARAM_STR,
+ ),
+ IQueryBuilder::PARAM_STR,
+ ))
+ ->andWhere($qb->expr()->eq(
+ 'propertyvalue',
+ $qb->createNamedParameter(
+ '1',
+ IQueryBuilder::PARAM_STR,
+ ),
+ IQueryBuilder::PARAM_STR,
+ ))
+ ->executeStatement();
+ }
+}