diff --git a/crates/caldav/src/calendar/methods/get.rs b/crates/caldav/src/calendar/methods/get.rs index 1375ca7..ca1cce2 100644 --- a/crates/caldav/src/calendar/methods/get.rs +++ b/crates/caldav/src/calendar/methods/get.rs @@ -43,24 +43,24 @@ pub async fn route_get( let mut ical_calendar_builder = IcalCalendarBuilder::version("4.0") .gregorian() .prodid("RustiCal"); - if calendar.displayname.is_some() { + if let Some(displayname) = calendar.meta.displayname { ical_calendar_builder = ical_calendar_builder.set(Property { name: "X-WR-CALNAME".to_owned(), - value: calendar.displayname, + value: Some(displayname), params: None, }); } - if calendar.description.is_some() { + if let Some(description) = calendar.meta.description { ical_calendar_builder = ical_calendar_builder.set(Property { name: "X-WR-CALDESC".to_owned(), - value: calendar.description, + value: Some(description), params: None, }); } - if calendar.timezone_id.is_some() { + if let Some(timezone_id) = calendar.timezone_id { ical_calendar_builder = ical_calendar_builder.set(Property { name: "X-WR-TIMEZONE".to_owned(), - value: calendar.timezone_id, + value: Some(timezone_id), params: None, }); } diff --git a/crates/caldav/src/calendar/methods/import.rs b/crates/caldav/src/calendar/methods/import.rs index c40aff6..0c2398b 100644 --- a/crates/caldav/src/calendar/methods/import.rs +++ b/crates/caldav/src/calendar/methods/import.rs @@ -10,7 +10,9 @@ use ical::{ parser::{Component, ComponentMut}, }; use rustical_ical::{CalendarObject, CalendarObjectType}; -use rustical_store::{Calendar, CalendarStore, SubscriptionStore, auth::Principal}; +use rustical_store::{ + Calendar, CalendarMetadata, CalendarStore, SubscriptionStore, auth::Principal, +}; use std::io::BufReader; use tracing::instrument; @@ -83,10 +85,12 @@ pub async fn route_import( let new_cal = Calendar { principal, id: cal_id, - displayname, - order: 0, - description, - color: None, + meta: CalendarMetadata { + displayname, + order: 0, + description, + color: None, + }, timezone_id, deleted_at: None, synctoken: 0, diff --git a/crates/caldav/src/calendar/methods/mkcalendar.rs b/crates/caldav/src/calendar/methods/mkcalendar.rs index 95ff9d6..770aa2b 100644 --- a/crates/caldav/src/calendar/methods/mkcalendar.rs +++ b/crates/caldav/src/calendar/methods/mkcalendar.rs @@ -8,7 +8,7 @@ use ical::IcalParser; use rustical_dav::xml::HrefElement; use rustical_ical::CalendarObjectType; use rustical_store::auth::Principal; -use rustical_store::{Calendar, CalendarStore, SubscriptionStore}; +use rustical_store::{Calendar, CalendarMetadata, CalendarStore, SubscriptionStore}; use rustical_xml::{Unparsed, XmlDeserialize, XmlDocument, XmlRootTag}; use tracing::instrument; @@ -112,11 +112,13 @@ pub async fn route_mkcalendar( let calendar = Calendar { id: cal_id.to_owned(), principal: principal.to_owned(), - order: request.calendar_order.unwrap_or(0), - displayname: request.displayname, + meta: CalendarMetadata { + order: request.calendar_order.unwrap_or(0), + displayname: request.displayname, + color: request.calendar_color, + description: request.calendar_description, + }, timezone_id, - color: request.calendar_color, - description: request.calendar_description, deleted_at: None, synctoken: 0, subscription_url: request.source.map(|href| href.href), diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index 9afdbe0..3c34261 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -128,10 +128,10 @@ impl Resource for CalendarResource { Ok(match prop { CalendarPropWrapperName::Calendar(prop) => CalendarPropWrapper::Calendar(match prop { CalendarPropName::CalendarColor => { - CalendarProp::CalendarColor(self.cal.color.clone()) + CalendarProp::CalendarColor(self.cal.meta.color.clone()) } CalendarPropName::CalendarDescription => { - CalendarProp::CalendarDescription(self.cal.description.clone()) + CalendarProp::CalendarDescription(self.cal.meta.description.clone()) } CalendarPropName::CalendarTimezone => { CalendarProp::CalendarTimezone(self.cal.timezone_id.as_ref().and_then(|tzid| { @@ -146,7 +146,7 @@ impl Resource for CalendarResource { CalendarProp::CalendarTimezoneId(self.cal.timezone_id.clone()) } CalendarPropName::CalendarOrder => { - CalendarProp::CalendarOrder(Some(self.cal.order)) + CalendarProp::CalendarOrder(Some(self.cal.meta.order)) } CalendarPropName::SupportedCalendarComponentSet => { CalendarProp::SupportedCalendarComponentSet(self.cal.components.clone().into()) @@ -187,11 +187,11 @@ impl Resource for CalendarResource { match prop { CalendarPropWrapper::Calendar(prop) => match prop { CalendarProp::CalendarColor(color) => { - self.cal.color = color; + self.cal.meta.color = color; Ok(()) } CalendarProp::CalendarDescription(description) => { - self.cal.description = description; + self.cal.meta.description = description; Ok(()) } CalendarProp::CalendarTimezone(timezone) => { @@ -236,7 +236,7 @@ impl Resource for CalendarResource { Ok(()) } CalendarProp::CalendarOrder(order) => { - self.cal.order = order.unwrap_or_default(); + self.cal.meta.order = order.unwrap_or_default(); Ok(()) } CalendarProp::SupportedCalendarComponentSet(comp_set) => { @@ -264,11 +264,11 @@ impl Resource for CalendarResource { match prop { CalendarPropWrapperName::Calendar(prop) => match prop { CalendarPropName::CalendarColor => { - self.cal.color = None; + self.cal.meta.color = None; Ok(()) } CalendarPropName::CalendarDescription => { - self.cal.description = None; + self.cal.meta.description = None; Ok(()) } CalendarPropName::CalendarTimezone | CalendarPropName::CalendarTimezoneId => { @@ -277,7 +277,7 @@ impl Resource for CalendarResource { } CalendarPropName::TimezoneServiceSet => Err(rustical_dav::Error::PropReadOnly), CalendarPropName::CalendarOrder => { - self.cal.order = 0; + self.cal.meta.order = 0; Ok(()) } CalendarPropName::SupportedCalendarComponentSet => { @@ -300,10 +300,10 @@ impl Resource for CalendarResource { } fn get_displayname(&self) -> Option<&str> { - self.cal.displayname.as_deref() + self.cal.meta.displayname.as_deref() } fn set_displayname(&mut self, name: Option) -> Result<(), rustical_dav::Error> { - self.cal.displayname = name; + self.cal.meta.displayname = name; Ok(()) } diff --git a/crates/frontend/public/templates/components/sections/calendars_section.html b/crates/frontend/public/templates/components/sections/calendars_section.html index eb6482d..afbbee6 100644 --- a/crates/frontend/public/templates/components/sections/calendars_section.html +++ b/crates/frontend/public/templates/components/sections/calendars_section.html @@ -1,13 +1,13 @@

{{ user.id }}'s Calendars

    {% for (meta, calendar) in calendars %} - {% let color = calendar.color.to_owned().unwrap_or("transparent".to_owned()) %} + {% let color = calendar.meta.color.to_owned().unwrap_or("transparent".to_owned()) %}
  • {%- if calendar.principal != user.id -%}{{ calendar.principal }}/{%- endif -%} - {{ calendar.displayname.to_owned().unwrap_or(calendar.id.to_owned()) }} + {{ calendar.meta.displayname.to_owned().unwrap_or(calendar.id.to_owned()) }}
    {% for comp in calendar.components %} {{ comp }} @@ -15,7 +15,7 @@
    - {% if let Some(description) = calendar.description %}{{ description }}{% endif %} + {% if let Some(description) = calendar.meta.description %}{{ description }}{% endif %} {% if let Some(subscription_url) = calendar.subscription_url %} {{ subscription_url }} @@ -29,9 +29,9 @@ principal="{{ calendar.principal }}" cal_id="{{ calendar.id }}" timezone_id="{{ calendar.timezone_id.as_deref().unwrap_or_default() }}" - displayname="{{ calendar.displayname.as_deref().unwrap_or_default() }}" - description="{{ calendar.description.as_deref().unwrap_or_default() }}" - color="{{ calendar.color.as_deref().unwrap_or_default() }}" + displayname="{{ calendar.meta.displayname.as_deref().unwrap_or_default() }}" + description="{{ calendar.meta.description.as_deref().unwrap_or_default() }}" + color="{{ calendar.meta.color.as_deref().unwrap_or_default() }}" components="{{ calendar.components | json }}" > @@ -51,13 +51,13 @@

    Deleted Calendars

      {% for (meta, calendar) in deleted_calendars %} - {% let color = calendar.color.to_owned().unwrap_or("transparent".to_owned()) %} + {% let color = calendar.meta.color.to_owned().unwrap_or("transparent".to_owned()) %}
    • {%- if calendar.principal != user.id -%}{{ calendar.principal }}/{%- endif -%} - {{ calendar.displayname.to_owned().unwrap_or(calendar.id.to_owned()) }} + {{ calendar.meta.displayname.to_owned().unwrap_or(calendar.id.to_owned()) }}
      {% for comp in calendar.components %} {{ comp }} @@ -65,7 +65,7 @@
      - {% if let Some(description) = calendar.description %}{{ description }}{% endif %} + {% if let Some(description) = calendar.meta.description %}{{ description }}{% endif %}
      {{ calendar.principal }}/{{ name }} -{% if let Some(description) = calendar.description %}

      {{ description }}

      {% endif%} +{% if let Some(description) = calendar.meta.description %}

      {{ description }}

      {% endif%} {% if let Some(subscription_url) = calendar.subscription_url %}

      Subscription URL

      diff --git a/crates/store/src/calendar.rs b/crates/store/src/calendar.rs index 94697ab..54d5721 100644 --- a/crates/store/src/calendar.rs +++ b/crates/store/src/calendar.rs @@ -6,13 +6,23 @@ use rustical_ical::CalendarObjectType; use serde::{Deserialize, Serialize}; #[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct Calendar { - pub principal: String, - pub id: String, +pub struct CalendarMetadata { + // Attributes that may be outsourced pub displayname: Option, pub order: i64, pub description: Option, pub color: Option, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct Calendar { + // Attributes that may be outsourced + #[serde(flatten)] + pub meta: CalendarMetadata, + + // Common calendar attributes + pub principal: String, + pub id: String, pub timezone_id: Option, pub deleted_at: Option, pub synctoken: i64, diff --git a/crates/store/src/contact_birthday_store.rs b/crates/store/src/contact_birthday_store.rs index 7f79b85..7a2da66 100644 --- a/crates/store/src/contact_birthday_store.rs +++ b/crates/store/src/contact_birthday_store.rs @@ -1,4 +1,6 @@ -use crate::{Addressbook, AddressbookStore, Calendar, CalendarStore, Error}; +use crate::{ + Addressbook, AddressbookStore, Calendar, CalendarStore, Error, calendar::CalendarMetadata, +}; use async_trait::async_trait; use derive_more::derive::Constructor; use rustical_ical::{AddressObject, CalendarObject, CalendarObjectType}; @@ -14,12 +16,14 @@ fn birthday_calendar(addressbook: Addressbook) -> Calendar { Calendar { principal: addressbook.principal, id: format!("{}{}", BIRTHDAYS_PREFIX, addressbook.id), - displayname: addressbook - .displayname - .map(|name| format!("{name} birthdays")), - order: 0, - description: None, - color: None, + meta: CalendarMetadata { + displayname: addressbook + .displayname + .map(|name| format!("{name} birthdays")), + order: 0, + description: None, + color: None, + }, timezone_id: None, deleted_at: addressbook.deleted_at, synctoken: addressbook.synctoken, diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs index d761193..fb95fec 100644 --- a/crates/store/src/lib.rs +++ b/crates/store/src/lib.rs @@ -22,7 +22,7 @@ pub use secret::Secret; pub use subscription_store::*; pub use addressbook::Addressbook; -pub use calendar::Calendar; +pub use calendar::{Calendar, CalendarMetadata}; #[derive(Debug, Clone)] pub enum CollectionOperationInfo { diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index 82edfc2..e7c0b4b 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -5,7 +5,7 @@ use derive_more::derive::Constructor; use rustical_ical::{CalDateTime, CalendarObject, CalendarObjectType}; use rustical_store::calendar_store::CalendarQuery; use rustical_store::synctoken::format_synctoken; -use rustical_store::{Calendar, CalendarStore, CollectionMetadata, Error}; +use rustical_store::{Calendar, CalendarMetadata, CalendarStore, CollectionMetadata, Error}; use rustical_store::{CollectionOperation, CollectionOperationInfo}; use sqlx::types::chrono::NaiveDateTime; use sqlx::{Acquire, Executor, Sqlite, SqlitePool, Transaction}; @@ -69,10 +69,12 @@ impl From for Calendar { Self { principal: value.principal, id: value.id, - displayname: value.displayname, - order: value.order, - description: value.description, - color: value.color, + meta: CalendarMetadata { + displayname: value.displayname, + order: value.order, + description: value.description, + color: value.color, + }, timezone_id: value.timezone_id, deleted_at: value.deleted_at, synctoken: value.synctoken, @@ -159,10 +161,10 @@ impl SqliteCalendarStore { VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#, calendar.principal, calendar.id, - calendar.displayname, - calendar.description, - calendar.order, - calendar.color, + calendar.meta.displayname, + calendar.meta.description, + calendar.meta.order, + calendar.meta.color, calendar.subscription_url, calendar.timezone_id, calendar.push_topic, @@ -189,10 +191,10 @@ impl SqliteCalendarStore { WHERE (principal, id) = (?, ?)"#, calendar.principal, calendar.id, - calendar.displayname, - calendar.description, - calendar.order, - calendar.color, + calendar.meta.displayname, + calendar.meta.description, + calendar.meta.order, + calendar.meta.color, calendar.timezone_id, calendar.push_topic, comp_event, comp_todo, comp_journal,