From 5738f56dfe0be84a7a7ad458f2864fabee068af5 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Sat, 4 Jan 2025 16:48:15 +0100 Subject: [PATCH] caldav: Add calendar-timezone-id --- ...0eb22832d0c16d690cebbcb7e546cab3e942d64.json} | 6 +++--- ...7d62ed755035e88b0d9363b71aac8aedff1cbb6.json} | 14 ++++++++++---- ...d2c793a69d9bbaa1a1a9ec16611ad1b1d303d5d6.json | 12 ++++++++++++ ...e86aa680e0831613cc9cb87cb19928fc538d35c.json} | 14 ++++++++++---- ...3a4bc454d8b162d7afbeebfc448211990cd8823.json} | 14 ++++++++++---- ...ec3a4af1c2f34515288bade397b87a1e43d789df.json | 12 ------------ crates/caldav/src/calendar/methods/mkcalendar.rs | 2 ++ crates/caldav/src/calendar/resource.rs | 15 +++++++++++++++ crates/caldav/src/lib.rs | 2 +- crates/store/src/calendar/calendar.rs | 1 + crates/store/src/calendar/event.rs | 2 -- crates/store_sqlite/migrations/1_init.sql | 1 + crates/store_sqlite/src/calendar_store.rs | 16 +++++++++------- 13 files changed, 74 insertions(+), 37 deletions(-) rename .sqlx/{query-c7a56d737fc8627427d0be202c3c77bb95c1a25b6a3c0c389ef0ad4f468320ee.json => query-0d0bd23849e3e9492531fa8ce0eb22832d0c16d690cebbcb7e546cab3e942d64.json} (59%) rename .sqlx/{query-7d27bdb54fbc8e65e5482fa9bc974e57c1fc7abcc5c7ca6bbb4944554381285c.json => query-24ece624823b418fa96a867f57d62ed755035e88b0d9363b71aac8aedff1cbb6.json} (78%) create mode 100644 .sqlx/query-32969555ebbf5d7220c1c0d2d2c793a69d9bbaa1a1a9ec16611ad1b1d303d5d6.json rename .sqlx/{query-97d66b4b76bca677badd9087a6bab978a54d36e4bb12ea77a6d52b9dfa08312c.json => query-9770a3a1c85aa92e7787ede67e86aa680e0831613cc9cb87cb19928fc538d35c.json} (78%) rename .sqlx/{query-38e44b7a271d9be098f00b233100fbce750a4e167ee547abccbefd36a089f399.json => query-a74288844bf11fd318c3e688d3a4bc454d8b162d7afbeebfc448211990cd8823.json} (78%) delete mode 100644 .sqlx/query-eba774e8d8afa04d33443b42ec3a4af1c2f34515288bade397b87a1e43d789df.json diff --git a/.sqlx/query-c7a56d737fc8627427d0be202c3c77bb95c1a25b6a3c0c389ef0ad4f468320ee.json b/.sqlx/query-0d0bd23849e3e9492531fa8ce0eb22832d0c16d690cebbcb7e546cab3e942d64.json similarity index 59% rename from .sqlx/query-c7a56d737fc8627427d0be202c3c77bb95c1a25b6a3c0c389ef0ad4f468320ee.json rename to .sqlx/query-0d0bd23849e3e9492531fa8ce0eb22832d0c16d690cebbcb7e546cab3e942d64.json index 79cd4da..36e97b5 100644 --- a/.sqlx/query-c7a56d737fc8627427d0be202c3c77bb95c1a25b6a3c0c389ef0ad4f468320ee.json +++ b/.sqlx/query-0d0bd23849e3e9492531fa8ce0eb22832d0c16d690cebbcb7e546cab3e942d64.json @@ -1,12 +1,12 @@ { "db_name": "SQLite", - "query": "UPDATE calendars SET principal = ?, id = ?, displayname = ?, description = ?, \"order\" = ?, color = ?, timezone = ?\n WHERE (principal, id) = (?, ?)", + "query": "UPDATE calendars SET principal = ?, id = ?, displayname = ?, description = ?, \"order\" = ?, color = ?, timezone = ?, timezone_id = ?\n WHERE (principal, id) = (?, ?)", "describe": { "columns": [], "parameters": { - "Right": 9 + "Right": 10 }, "nullable": [] }, - "hash": "c7a56d737fc8627427d0be202c3c77bb95c1a25b6a3c0c389ef0ad4f468320ee" + "hash": "0d0bd23849e3e9492531fa8ce0eb22832d0c16d690cebbcb7e546cab3e942d64" } diff --git a/.sqlx/query-7d27bdb54fbc8e65e5482fa9bc974e57c1fc7abcc5c7ca6bbb4944554381285c.json b/.sqlx/query-24ece624823b418fa96a867f57d62ed755035e88b0d9363b71aac8aedff1cbb6.json similarity index 78% rename from .sqlx/query-7d27bdb54fbc8e65e5482fa9bc974e57c1fc7abcc5c7ca6bbb4944554381285c.json rename to .sqlx/query-24ece624823b418fa96a867f57d62ed755035e88b0d9363b71aac8aedff1cbb6.json index 10b7c10..ec17f6b 100644 --- a/.sqlx/query-7d27bdb54fbc8e65e5482fa9bc974e57c1fc7abcc5c7ca6bbb4944554381285c.json +++ b/.sqlx/query-24ece624823b418fa96a867f57d62ed755035e88b0d9363b71aac8aedff1cbb6.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT principal, id, synctoken, displayname, \"order\", description, color, timezone, deleted_at, subscription_url\n FROM calendars\n WHERE principal = ? AND deleted_at IS NULL", + "query": "SELECT principal, id, synctoken, displayname, \"order\", description, color, timezone, timezone_id, deleted_at, subscription_url\n FROM calendars\n WHERE principal = ? AND deleted_at IS NULL", "describe": { "columns": [ { @@ -44,13 +44,18 @@ "type_info": "Text" }, { - "name": "deleted_at", + "name": "timezone_id", "ordinal": 8, + "type_info": "Text" + }, + { + "name": "deleted_at", + "ordinal": 9, "type_info": "Datetime" }, { "name": "subscription_url", - "ordinal": 9, + "ordinal": 10, "type_info": "Text" } ], @@ -67,8 +72,9 @@ true, true, true, + true, true ] }, - "hash": "7d27bdb54fbc8e65e5482fa9bc974e57c1fc7abcc5c7ca6bbb4944554381285c" + "hash": "24ece624823b418fa96a867f57d62ed755035e88b0d9363b71aac8aedff1cbb6" } diff --git a/.sqlx/query-32969555ebbf5d7220c1c0d2d2c793a69d9bbaa1a1a9ec16611ad1b1d303d5d6.json b/.sqlx/query-32969555ebbf5d7220c1c0d2d2c793a69d9bbaa1a1a9ec16611ad1b1d303d5d6.json new file mode 100644 index 0000000..42098de --- /dev/null +++ b/.sqlx/query-32969555ebbf5d7220c1c0d2d2c793a69d9bbaa1a1a9ec16611ad1b1d303d5d6.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO calendars (principal, id, displayname, description, \"order\", color, timezone, timezone_id)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 8 + }, + "nullable": [] + }, + "hash": "32969555ebbf5d7220c1c0d2d2c793a69d9bbaa1a1a9ec16611ad1b1d303d5d6" +} diff --git a/.sqlx/query-97d66b4b76bca677badd9087a6bab978a54d36e4bb12ea77a6d52b9dfa08312c.json b/.sqlx/query-9770a3a1c85aa92e7787ede67e86aa680e0831613cc9cb87cb19928fc538d35c.json similarity index 78% rename from .sqlx/query-97d66b4b76bca677badd9087a6bab978a54d36e4bb12ea77a6d52b9dfa08312c.json rename to .sqlx/query-9770a3a1c85aa92e7787ede67e86aa680e0831613cc9cb87cb19928fc538d35c.json index 12f490a..0f51cdf 100644 --- a/.sqlx/query-97d66b4b76bca677badd9087a6bab978a54d36e4bb12ea77a6d52b9dfa08312c.json +++ b/.sqlx/query-9770a3a1c85aa92e7787ede67e86aa680e0831613cc9cb87cb19928fc538d35c.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT principal, id, synctoken, displayname, \"order\", description, color, timezone, deleted_at, subscription_url\n FROM calendars\n WHERE principal = ? AND deleted_at IS NOT NULL", + "query": "SELECT principal, id, synctoken, displayname, \"order\", description, color, timezone, timezone_id, deleted_at, subscription_url\n FROM calendars\n WHERE principal = ? AND deleted_at IS NOT NULL", "describe": { "columns": [ { @@ -44,13 +44,18 @@ "type_info": "Text" }, { - "name": "deleted_at", + "name": "timezone_id", "ordinal": 8, + "type_info": "Text" + }, + { + "name": "deleted_at", + "ordinal": 9, "type_info": "Datetime" }, { "name": "subscription_url", - "ordinal": 9, + "ordinal": 10, "type_info": "Text" } ], @@ -67,8 +72,9 @@ true, true, true, + true, true ] }, - "hash": "97d66b4b76bca677badd9087a6bab978a54d36e4bb12ea77a6d52b9dfa08312c" + "hash": "9770a3a1c85aa92e7787ede67e86aa680e0831613cc9cb87cb19928fc538d35c" } diff --git a/.sqlx/query-38e44b7a271d9be098f00b233100fbce750a4e167ee547abccbefd36a089f399.json b/.sqlx/query-a74288844bf11fd318c3e688d3a4bc454d8b162d7afbeebfc448211990cd8823.json similarity index 78% rename from .sqlx/query-38e44b7a271d9be098f00b233100fbce750a4e167ee547abccbefd36a089f399.json rename to .sqlx/query-a74288844bf11fd318c3e688d3a4bc454d8b162d7afbeebfc448211990cd8823.json index cafbae4..5d24d85 100644 --- a/.sqlx/query-38e44b7a271d9be098f00b233100fbce750a4e167ee547abccbefd36a089f399.json +++ b/.sqlx/query-a74288844bf11fd318c3e688d3a4bc454d8b162d7afbeebfc448211990cd8823.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT principal, id, synctoken, \"order\", displayname, description, color, timezone, deleted_at, subscription_url\n FROM calendars\n WHERE (principal, id) = (?, ?)", + "query": "SELECT principal, id, synctoken, \"order\", displayname, description, color, timezone, timezone_id, deleted_at, subscription_url\n FROM calendars\n WHERE (principal, id) = (?, ?)", "describe": { "columns": [ { @@ -44,13 +44,18 @@ "type_info": "Text" }, { - "name": "deleted_at", + "name": "timezone_id", "ordinal": 8, + "type_info": "Text" + }, + { + "name": "deleted_at", + "ordinal": 9, "type_info": "Datetime" }, { "name": "subscription_url", - "ordinal": 9, + "ordinal": 10, "type_info": "Text" } ], @@ -67,8 +72,9 @@ true, true, true, + true, true ] }, - "hash": "38e44b7a271d9be098f00b233100fbce750a4e167ee547abccbefd36a089f399" + "hash": "a74288844bf11fd318c3e688d3a4bc454d8b162d7afbeebfc448211990cd8823" } diff --git a/.sqlx/query-eba774e8d8afa04d33443b42ec3a4af1c2f34515288bade397b87a1e43d789df.json b/.sqlx/query-eba774e8d8afa04d33443b42ec3a4af1c2f34515288bade397b87a1e43d789df.json deleted file mode 100644 index 6b80dd3..0000000 --- a/.sqlx/query-eba774e8d8afa04d33443b42ec3a4af1c2f34515288bade397b87a1e43d789df.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "INSERT INTO calendars (principal, id, displayname, description, \"order\", color, timezone)\n VALUES (?, ?, ?, ?, ?, ?, ?)", - "describe": { - "columns": [], - "parameters": { - "Right": 7 - }, - "nullable": [] - }, - "hash": "eba774e8d8afa04d33443b42ec3a4af1c2f34515288bade397b87a1e43d789df" -} diff --git a/crates/caldav/src/calendar/methods/mkcalendar.rs b/crates/caldav/src/calendar/methods/mkcalendar.rs index 5ec0f3c..06128c1 100644 --- a/crates/caldav/src/calendar/methods/mkcalendar.rs +++ b/crates/caldav/src/calendar/methods/mkcalendar.rs @@ -27,6 +27,7 @@ pub struct SupportedCalendarComponentSetElement { #[derive(XmlDeserialize, Clone, Debug)] pub struct MkcolCalendarProp { + // TODO: Add namespaces resourcetype: Option, displayname: Option, calendar_description: Option, @@ -70,6 +71,7 @@ pub async fn route_mkcalendar( order: request.order.unwrap_or(0), displayname: request.displayname, timezone: request.calendar_timezone, + timezone_id: request.calendar_timezone_id, color: request.calendar_color, description: request.calendar_description, deleted_at: None, diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index 544530f..5e65cce 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -61,6 +61,9 @@ pub enum CalendarProp { CalendarDescription(Option), #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] CalendarTimezone(Option), + // https://datatracker.ietf.org/doc/html/rfc7809 + #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] + CalendarTimezoneId(Option), #[xml(ns = "rustical_dav::namespace::NS_ICAL")] CalendarOrder(Option), #[xml(ns = "rustical_dav::namespace::NS_CALDAV", skip_deserializing)] @@ -138,6 +141,9 @@ impl Resource for CalendarResource { CalendarPropName::CalendarTimezone => { CalendarProp::CalendarTimezone(self.0.timezone.clone()) } + CalendarPropName::CalendarTimezoneId => { + CalendarProp::CalendarTimezoneId(self.0.timezone_id.clone()) + } CalendarPropName::CalendarOrder => CalendarProp::CalendarOrder(Some(self.0.order)), CalendarPropName::SupportedCalendarComponentSet => { CalendarProp::SupportedCalendarComponentSet(SupportedCalendarComponentSet::default()) @@ -186,6 +192,11 @@ impl Resource for CalendarResource { self.0.timezone = timezone; Ok(()) } + CalendarProp::CalendarTimezoneId(timezone_id) => { + // TODO: Set or remove timezone accordingly + self.0.timezone_id = timezone_id; + Ok(()) + } CalendarProp::CalendarOrder(order) => { self.0.order = order.unwrap_or_default(); Ok(()) @@ -224,6 +235,10 @@ impl Resource for CalendarResource { self.0.timezone = None; Ok(()) } + CalendarPropName::CalendarTimezoneId => { + self.0.timezone_id = None; + Ok(()) + } CalendarPropName::CalendarOrder => { self.0.order = 0; Ok(()) diff --git a/crates/caldav/src/lib.rs b/crates/caldav/src/lib.rs index dd5664c..ae87c82 100644 --- a/crates/caldav/src/lib.rs +++ b/crates/caldav/src/lib.rs @@ -40,7 +40,7 @@ pub fn configure_dav( .insert_header(( HeaderName::from_static("dav"), HeaderValue::from_static( - "1, 2, 3, access-control, calendar-access, extended-mkcol", + "1, 2, 3, access-control, calendar-access, extended-mkcol, calendar-no-timezone", ), )) .finish(); diff --git a/crates/store/src/calendar/calendar.rs b/crates/store/src/calendar/calendar.rs index 1731f1b..4a9d2cf 100644 --- a/crates/store/src/calendar/calendar.rs +++ b/crates/store/src/calendar/calendar.rs @@ -11,6 +11,7 @@ pub struct Calendar { pub description: Option, pub color: Option, pub timezone: Option, + pub timezone_id: Option, pub deleted_at: Option, pub synctoken: i64, pub subscription_url: Option, diff --git a/crates/store/src/calendar/event.rs b/crates/store/src/calendar/event.rs index 0d4e2da..55f4ec0 100644 --- a/crates/store/src/calendar/event.rs +++ b/crates/store/src/calendar/event.rs @@ -16,7 +16,6 @@ pub struct EventObject { impl EventObject { pub fn get_first_occurence(&self) -> Result, Error> { - // This is safe since we enforce the event's existance in the constructor if let Some(dtstart) = self.event.get_property("DTSTART") { CalDateTime::parse_prop(dtstart, &self.timezones) } else { @@ -25,7 +24,6 @@ impl EventObject { } pub fn get_last_occurence(&self) -> Result, Error> { - // This is safe since we enforce the event's existence in the constructor if let Some(_rrule) = self.event.get_property("RRULE") { // TODO: understand recurrence rules return Ok(None); diff --git a/crates/store_sqlite/migrations/1_init.sql b/crates/store_sqlite/migrations/1_init.sql index a3c412b..423da2f 100644 --- a/crates/store_sqlite/migrations/1_init.sql +++ b/crates/store_sqlite/migrations/1_init.sql @@ -7,6 +7,7 @@ CREATE TABLE calendars ( "order" INT DEFAULT 0 NOT NULL, color TEXT, timezone TEXT, + timezone_id TEXT, deleted_at DATETIME, subscription_url TEXT, PRIMARY KEY (principal, id) diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index c8814fb..9d51d3a 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -62,7 +62,7 @@ impl CalendarStore for SqliteStore { async fn get_calendar(&self, principal: &str, id: &str) -> Result { let cal = sqlx::query_as!( Calendar, - r#"SELECT principal, id, synctoken, "order", displayname, description, color, timezone, deleted_at, subscription_url + r#"SELECT principal, id, synctoken, "order", displayname, description, color, timezone, timezone_id, deleted_at, subscription_url FROM calendars WHERE (principal, id) = (?, ?)"#, principal, @@ -77,7 +77,7 @@ impl CalendarStore for SqliteStore { async fn get_calendars(&self, principal: &str) -> Result, Error> { let cals = sqlx::query_as!( Calendar, - r#"SELECT principal, id, synctoken, displayname, "order", description, color, timezone, deleted_at, subscription_url + r#"SELECT principal, id, synctoken, displayname, "order", description, color, timezone, timezone_id, deleted_at, subscription_url FROM calendars WHERE principal = ? AND deleted_at IS NULL"#, principal @@ -91,7 +91,7 @@ impl CalendarStore for SqliteStore { async fn get_deleted_calendars(&self, principal: &str) -> Result, Error> { let cals = sqlx::query_as!( Calendar, - r#"SELECT principal, id, synctoken, displayname, "order", description, color, timezone, deleted_at, subscription_url + r#"SELECT principal, id, synctoken, displayname, "order", description, color, timezone, timezone_id, deleted_at, subscription_url FROM calendars WHERE principal = ? AND deleted_at IS NOT NULL"#, principal @@ -104,15 +104,16 @@ impl CalendarStore for SqliteStore { #[instrument] async fn insert_calendar(&self, calendar: Calendar) -> Result<(), Error> { sqlx::query!( - r#"INSERT INTO calendars (principal, id, displayname, description, "order", color, timezone) - VALUES (?, ?, ?, ?, ?, ?, ?)"#, + r#"INSERT INTO calendars (principal, id, displayname, description, "order", color, timezone, timezone_id) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)"#, calendar.principal, calendar.id, calendar.displayname, calendar.description, calendar.order, calendar.color, - calendar.timezone + calendar.timezone, + calendar.timezone_id ) .execute(&self.db) .await.map_err(crate::Error::from)?; @@ -127,7 +128,7 @@ impl CalendarStore for SqliteStore { calendar: Calendar, ) -> Result<(), Error> { let result = sqlx::query!( - r#"UPDATE calendars SET principal = ?, id = ?, displayname = ?, description = ?, "order" = ?, color = ?, timezone = ? + r#"UPDATE calendars SET principal = ?, id = ?, displayname = ?, description = ?, "order" = ?, color = ?, timezone = ?, timezone_id = ? WHERE (principal, id) = (?, ?)"#, calendar.principal, calendar.id, @@ -136,6 +137,7 @@ impl CalendarStore for SqliteStore { calendar.order, calendar.color, calendar.timezone, + calendar.timezone_id, principal, id ).execute(&self.db).await.map_err(crate::Error::from)?;