diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml
index 393dc33df5b7d..0708c400ee854 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -10,7 +10,7 @@
WebDAV
WebDAV endpoint
WebDAV endpoint
- 2.0.0-dev.0
+ 2.0.0-dev.1
agpl
owncloud.org
DAV
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index 056e9fb9d5839..e2bf903181b1c 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -401,6 +401,8 @@
'OCA\\DAV\\Migration\\Version1038Date20260302000000' => $baseDir . '/../lib/Migration/Version1038Date20260302000000.php',
'OCA\\DAV\\Migration\\Version1039Date20260408000000' => $baseDir . '/../lib/Migration/Version1039Date20260408000000.php',
'OCA\\DAV\\Migration\\Version1040Date20260805000000' => $baseDir . '/../lib/Migration/Version1040Date20260805000000.php',
+ 'OCA\\DAV\\Migration\\Version2000Date20260703100001' => $baseDir . '/../lib/Migration/Version2000Date20260703100001.php',
+ 'OCA\\DAV\\Migration\\Version2000Date20260703100002' => $baseDir . '/../lib/Migration/Version2000Date20260703100002.php',
'OCA\\DAV\\Model\\ExampleEvent' => $baseDir . '/../lib/Model/ExampleEvent.php',
'OCA\\DAV\\Paginate\\LimitedCopyIterator' => $baseDir . '/../lib/Paginate/LimitedCopyIterator.php',
'OCA\\DAV\\Paginate\\PaginateCache' => $baseDir . '/../lib/Paginate/PaginateCache.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 840df1461d056..318660f02f8b7 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -416,6 +416,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1038Date20260302000000' => __DIR__ . '/..' . '/../lib/Migration/Version1038Date20260302000000.php',
'OCA\\DAV\\Migration\\Version1039Date20260408000000' => __DIR__ . '/..' . '/../lib/Migration/Version1039Date20260408000000.php',
'OCA\\DAV\\Migration\\Version1040Date20260805000000' => __DIR__ . '/..' . '/../lib/Migration/Version1040Date20260805000000.php',
+ 'OCA\\DAV\\Migration\\Version2000Date20260703100001' => __DIR__ . '/..' . '/../lib/Migration/Version2000Date20260703100001.php',
+ 'OCA\\DAV\\Migration\\Version2000Date20260703100002' => __DIR__ . '/..' . '/../lib/Migration/Version2000Date20260703100002.php',
'OCA\\DAV\\Model\\ExampleEvent' => __DIR__ . '/..' . '/../lib/Model/ExampleEvent.php',
'OCA\\DAV\\Paginate\\LimitedCopyIterator' => __DIR__ . '/..' . '/../lib/Paginate/LimitedCopyIterator.php',
'OCA\\DAV\\Paginate\\PaginateCache' => __DIR__ . '/..' . '/../lib/Paginate/PaginateCache.php',
diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php
index 59931eb6b0b53..d25fba96ab9b6 100644
--- a/apps/dav/lib/CalDAV/CalDavBackend.php
+++ b/apps/dav/lib/CalDAV/CalDavBackend.php
@@ -151,8 +151,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
'{http://apple.com/ns/ical/}calendar-order' => ['calendarorder', 'int'],
'{http://apple.com/ns/ical/}calendar-color' => ['calendarcolor', 'string'],
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_NEXTCLOUD . '}deleted-at' => ['deleted_at', 'int'],
- '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_NEXTCLOUD . '}default-alarm-part-day' => ['default_alarm_pday', 'int'],
- '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_NEXTCLOUD . '}default-alarm-full-day' => ['default_alarm_fday', 'int'],
+ '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_NEXTCLOUD . '}default-alarms-part-day' => ['default_alarms_pday', 'string'],
+ '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_NEXTCLOUD . '}default-alarms-full-day' => ['default_alarms_fday', 'string'],
];
/**
diff --git a/apps/dav/lib/Migration/Version2000Date20260703100001.php b/apps/dav/lib/Migration/Version2000Date20260703100001.php
new file mode 100644
index 0000000000000..baa054c94c828
--- /dev/null
+++ b/apps/dav/lib/Migration/Version2000Date20260703100001.php
@@ -0,0 +1,90 @@
+getTable('calendars');
+
+ if (!$calendarsTable->hasColumn('default_alarms_pday')) {
+ $calendarsTable->addColumn('default_alarms_pday', Types::TEXT, [
+ 'notnull' => false,
+ 'default' => null,
+ ]);
+ }
+
+ if (!$calendarsTable->hasColumn('default_alarms_fday')) {
+ $calendarsTable->addColumn('default_alarms_fday', Types::TEXT, [
+ 'notnull' => false,
+ 'default' => null,
+ ]);
+ }
+
+ return $schema;
+ }
+
+ #[Override]
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+ $calendarsTable = $schema->getTable('calendars');
+
+ if ($calendarsTable->hasColumn('default_alarm_pday') && $calendarsTable->hasColumn('default_alarms_pday')) {
+ $this->migrateLegacyIntColumn('default_alarm_pday', 'default_alarms_pday');
+ }
+
+ if ($calendarsTable->hasColumn('default_alarm_fday') && $calendarsTable->hasColumn('default_alarms_fday')) {
+ $this->migrateLegacyIntColumn('default_alarm_fday', 'default_alarms_fday');
+ }
+ }
+
+ /**
+ * Encode a single legacy trigger int as [{"trigger":N,"action":"DISPLAY"}]
+ * for all rows still missing the JSON column value.
+ */
+ private function migrateLegacyIntColumn(string $legacyColumn, string $jsonColumn): void {
+ $qb = $this->db->getQueryBuilder();
+ $qb->update('calendars')
+ ->set($jsonColumn, $qb->func()->concat(
+ $qb->expr()->literal('[{"trigger":'),
+ $legacyColumn,
+ $qb->expr()->literal(',"action":"DISPLAY"}]'),
+ ))
+ ->where($qb->expr()->isNotNull($legacyColumn))
+ ->andWhere($qb->expr()->isNull($jsonColumn));
+ $qb->executeStatement();
+ }
+}
diff --git a/apps/dav/lib/Migration/Version2000Date20260703100002.php b/apps/dav/lib/Migration/Version2000Date20260703100002.php
new file mode 100644
index 0000000000000..226243af26aa2
--- /dev/null
+++ b/apps/dav/lib/Migration/Version2000Date20260703100002.php
@@ -0,0 +1,42 @@
+getTable('calendars');
+
+ if ($calendarsTable->hasColumn('default_alarm_pday')) {
+ $calendarsTable->dropColumn('default_alarm_pday');
+ }
+
+ if ($calendarsTable->hasColumn('default_alarm_fday')) {
+ $calendarsTable->dropColumn('default_alarm_fday');
+ }
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
index 75bd0f118906f..9ef955bbbd1fb 100644
--- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
+++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php
@@ -2081,46 +2081,64 @@ public function testUnshare(): void {
}
- public function testDefaultAlarmProperties(): void {
+ public function testDefaultAlarmsProperties(): void {
$calendarId = $this->createTestCalendar();
- // Test setting both default alarm properties
+ $partDayProperty = '{http://nextcloud.com/ns}default-alarms-part-day';
+ $fullDayProperty = '{http://nextcloud.com/ns}default-alarms-full-day';
+
+ $partDayJson = json_encode([
+ ['trigger' => -86400, 'action' => 'EMAIL'],
+ ['trigger' => -900, 'action' => 'DISPLAY'],
+ ], JSON_THROW_ON_ERROR);
+ $fullDayJson = json_encode([
+ ['trigger' => -3600, 'action' => 'EMAIL'],
+ ], JSON_THROW_ON_ERROR);
+
+ // Set plural default-alarms properties
$patch = new PropPatch([
- '{http://nextcloud.com/ns}default-alarm-part-day' => -900,
- '{http://nextcloud.com/ns}default-alarm-full-day' => -3600,
+ $partDayProperty => $partDayJson,
+ $fullDayProperty => $fullDayJson,
]);
$this->backend->updateCalendar($calendarId, $patch);
$patch->commit();
- // Verify the properties were set
$calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER);
$this->assertCount(1, $calendars);
- $this->assertEquals(-900, $calendars[0]['{http://nextcloud.com/ns}default-alarm-part-day']);
- $this->assertEquals(-3600, $calendars[0]['{http://nextcloud.com/ns}default-alarm-full-day']);
-
- // Test updating to different values
+ $this->assertEquals($partDayJson, $calendars[0][$partDayProperty]);
+ $this->assertEquals($fullDayJson, $calendars[0][$fullDayProperty]);
+ $this->assertArrayNotHasKey('{http://nextcloud.com/ns}default-alarm-part-day', $calendars[0]);
+ $this->assertArrayNotHasKey('{http://nextcloud.com/ns}default-alarm-full-day', $calendars[0]);
+
+ // Update to different values
+ $updatedPartDayJson = json_encode([
+ ['trigger' => -1800, 'action' => 'DISPLAY'],
+ ], JSON_THROW_ON_ERROR);
+ $updatedFullDayJson = json_encode([
+ ['trigger' => -43200, 'action' => 'DISPLAY'],
+ ], JSON_THROW_ON_ERROR);
$patch = new PropPatch([
- '{http://nextcloud.com/ns}default-alarm-part-day' => -86400,
- '{http://nextcloud.com/ns}default-alarm-full-day' => -43200,
+ $partDayProperty => $updatedPartDayJson,
+ $fullDayProperty => $updatedFullDayJson,
]);
$this->backend->updateCalendar($calendarId, $patch);
$patch->commit();
$calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER);
- $this->assertEquals(-86400, $calendars[0]['{http://nextcloud.com/ns}default-alarm-part-day']);
- $this->assertEquals(-43200, $calendars[0]['{http://nextcloud.com/ns}default-alarm-full-day']);
+ $this->assertEquals($updatedPartDayJson, $calendars[0][$partDayProperty]);
+ $this->assertEquals($updatedFullDayJson, $calendars[0][$fullDayProperty]);
- // Test setting to null
+ // Clear properties
$patch = new PropPatch([
- '{http://nextcloud.com/ns}default-alarm-part-day' => null,
- '{http://nextcloud.com/ns}default-alarm-full-day' => null,
+ $partDayProperty => null,
+ $fullDayProperty => null,
]);
$this->backend->updateCalendar($calendarId, $patch);
$patch->commit();
$calendars = $this->backend->getCalendarsForUser(self::UNIT_TEST_USER);
- $this->assertNull($calendars[0]['{http://nextcloud.com/ns}default-alarm-part-day']);
- $this->assertNull($calendars[0]['{http://nextcloud.com/ns}default-alarm-full-day']);
+ $this->assertNull($calendars[0][$partDayProperty]);
+ $this->assertNull($calendars[0][$fullDayProperty]);
// Clean up
$this->backend->deleteCalendar($calendars[0]['id'], true);