mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 01:12:24 +00:00
Support read-only calendar store as preparation for birthday calendars
This commit is contained in:
@@ -89,7 +89,16 @@ pub enum CalendarProp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, From, Into)]
|
#[derive(Clone, Debug, From, Into)]
|
||||||
pub struct CalendarResource(Calendar);
|
pub struct CalendarResource {
|
||||||
|
pub cal: Calendar,
|
||||||
|
pub read_only: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CalendarResource> for Calendar {
|
||||||
|
fn from(value: CalendarResource) -> Self {
|
||||||
|
value.cal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Resource for CalendarResource {
|
impl Resource for CalendarResource {
|
||||||
type PropName = CalendarPropName;
|
type PropName = CalendarPropName;
|
||||||
@@ -98,7 +107,7 @@ impl Resource for CalendarResource {
|
|||||||
type PrincipalResource = PrincipalResource;
|
type PrincipalResource = PrincipalResource;
|
||||||
|
|
||||||
fn get_resourcetype(&self) -> Resourcetype {
|
fn get_resourcetype(&self) -> Resourcetype {
|
||||||
if self.0.subscription_url.is_none() {
|
if self.cal.subscription_url.is_none() {
|
||||||
Resourcetype(&[
|
Resourcetype(&[
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_DAV, "collection"),
|
ResourcetypeInner(rustical_dav::namespace::NS_DAV, "collection"),
|
||||||
ResourcetypeInner(rustical_dav::namespace::NS_CALDAV, "calendar"),
|
ResourcetypeInner(rustical_dav::namespace::NS_CALDAV, "calendar"),
|
||||||
@@ -118,18 +127,20 @@ impl Resource for CalendarResource {
|
|||||||
prop: &Self::PropName,
|
prop: &Self::PropName,
|
||||||
) -> Result<Self::Prop, Self::Error> {
|
) -> Result<Self::Prop, Self::Error> {
|
||||||
Ok(match prop {
|
Ok(match prop {
|
||||||
CalendarPropName::Displayname => CalendarProp::Displayname(self.0.displayname.clone()),
|
CalendarPropName::Displayname => {
|
||||||
CalendarPropName::CalendarColor => CalendarProp::CalendarColor(self.0.color.clone()),
|
CalendarProp::Displayname(self.cal.displayname.clone())
|
||||||
|
}
|
||||||
|
CalendarPropName::CalendarColor => CalendarProp::CalendarColor(self.cal.color.clone()),
|
||||||
CalendarPropName::CalendarDescription => {
|
CalendarPropName::CalendarDescription => {
|
||||||
CalendarProp::CalendarDescription(self.0.description.clone())
|
CalendarProp::CalendarDescription(self.cal.description.clone())
|
||||||
}
|
}
|
||||||
CalendarPropName::CalendarTimezone => {
|
CalendarPropName::CalendarTimezone => {
|
||||||
CalendarProp::CalendarTimezone(self.0.timezone.clone())
|
CalendarProp::CalendarTimezone(self.cal.timezone.clone())
|
||||||
}
|
}
|
||||||
CalendarPropName::CalendarTimezoneId => {
|
CalendarPropName::CalendarTimezoneId => {
|
||||||
CalendarProp::CalendarTimezoneId(self.0.timezone_id.clone())
|
CalendarProp::CalendarTimezoneId(self.cal.timezone_id.clone())
|
||||||
}
|
}
|
||||||
CalendarPropName::CalendarOrder => CalendarProp::CalendarOrder(Some(self.0.order)),
|
CalendarPropName::CalendarOrder => CalendarProp::CalendarOrder(Some(self.cal.order)),
|
||||||
CalendarPropName::SupportedCalendarComponentSet => {
|
CalendarPropName::SupportedCalendarComponentSet => {
|
||||||
CalendarProp::SupportedCalendarComponentSet(SupportedCalendarComponentSet::default())
|
CalendarProp::SupportedCalendarComponentSet(SupportedCalendarComponentSet::default())
|
||||||
}
|
}
|
||||||
@@ -142,7 +153,8 @@ impl Resource for CalendarResource {
|
|||||||
CalendarPropName::Transports => CalendarProp::Transports(Default::default()),
|
CalendarPropName::Transports => CalendarProp::Transports(Default::default()),
|
||||||
CalendarPropName::Topic => {
|
CalendarPropName::Topic => {
|
||||||
// TODO: Add salt since this could be public
|
// TODO: Add salt since this could be public
|
||||||
let url = CalendarResource::get_url(rmap, [&self.0.principal, &self.0.id]).unwrap();
|
let url =
|
||||||
|
CalendarResource::get_url(rmap, [&self.cal.principal, &self.cal.id]).unwrap();
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(url);
|
hasher.update(url);
|
||||||
let topic = format!("{:x}", hasher.finalize());
|
let topic = format!("{:x}", hasher.finalize());
|
||||||
@@ -152,39 +164,42 @@ impl Resource for CalendarResource {
|
|||||||
CalendarPropName::SupportedReportSet => {
|
CalendarPropName::SupportedReportSet => {
|
||||||
CalendarProp::SupportedReportSet(SupportedReportSet::default())
|
CalendarProp::SupportedReportSet(SupportedReportSet::default())
|
||||||
}
|
}
|
||||||
CalendarPropName::SyncToken => CalendarProp::SyncToken(self.0.format_synctoken()),
|
CalendarPropName::SyncToken => CalendarProp::SyncToken(self.cal.format_synctoken()),
|
||||||
CalendarPropName::Getctag => CalendarProp::Getctag(self.0.format_synctoken()),
|
CalendarPropName::Getctag => CalendarProp::Getctag(self.cal.format_synctoken()),
|
||||||
CalendarPropName::Source => {
|
CalendarPropName::Source => {
|
||||||
CalendarProp::Source(self.0.subscription_url.to_owned().map(HrefElement::from))
|
CalendarProp::Source(self.cal.subscription_url.to_owned().map(HrefElement::from))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_prop(&mut self, prop: Self::Prop) -> Result<(), rustical_dav::Error> {
|
fn set_prop(&mut self, prop: Self::Prop) -> Result<(), rustical_dav::Error> {
|
||||||
|
if self.read_only {
|
||||||
|
return Err(rustical_dav::Error::PropReadOnly);
|
||||||
|
}
|
||||||
match prop {
|
match prop {
|
||||||
CalendarProp::Displayname(displayname) => {
|
CalendarProp::Displayname(displayname) => {
|
||||||
self.0.displayname = displayname;
|
self.cal.displayname = displayname;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarProp::CalendarColor(color) => {
|
CalendarProp::CalendarColor(color) => {
|
||||||
self.0.color = color;
|
self.cal.color = color;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarProp::CalendarDescription(description) => {
|
CalendarProp::CalendarDescription(description) => {
|
||||||
self.0.description = description;
|
self.cal.description = description;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarProp::CalendarTimezone(timezone) => {
|
CalendarProp::CalendarTimezone(timezone) => {
|
||||||
self.0.timezone = timezone;
|
self.cal.timezone = timezone;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarProp::CalendarTimezoneId(timezone_id) => {
|
CalendarProp::CalendarTimezoneId(timezone_id) => {
|
||||||
// TODO: Set or remove timezone accordingly
|
// TODO: Set or remove timezone accordingly
|
||||||
self.0.timezone_id = timezone_id;
|
self.cal.timezone_id = timezone_id;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarProp::CalendarOrder(order) => {
|
CalendarProp::CalendarOrder(order) => {
|
||||||
self.0.order = order.unwrap_or_default();
|
self.cal.order = order.unwrap_or_default();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarProp::SupportedCalendarComponentSet(_) => {
|
CalendarProp::SupportedCalendarComponentSet(_) => {
|
||||||
@@ -204,29 +219,32 @@ impl Resource for CalendarResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn remove_prop(&mut self, prop: &Self::PropName) -> Result<(), rustical_dav::Error> {
|
fn remove_prop(&mut self, prop: &Self::PropName) -> Result<(), rustical_dav::Error> {
|
||||||
|
if self.read_only {
|
||||||
|
return Err(rustical_dav::Error::PropReadOnly);
|
||||||
|
}
|
||||||
match prop {
|
match prop {
|
||||||
CalendarPropName::Displayname => {
|
CalendarPropName::Displayname => {
|
||||||
self.0.displayname = None;
|
self.cal.displayname = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarPropName::CalendarColor => {
|
CalendarPropName::CalendarColor => {
|
||||||
self.0.color = None;
|
self.cal.color = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarPropName::CalendarDescription => {
|
CalendarPropName::CalendarDescription => {
|
||||||
self.0.description = None;
|
self.cal.description = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarPropName::CalendarTimezone => {
|
CalendarPropName::CalendarTimezone => {
|
||||||
self.0.timezone = None;
|
self.cal.timezone = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarPropName::CalendarTimezoneId => {
|
CalendarPropName::CalendarTimezoneId => {
|
||||||
self.0.timezone_id = None;
|
self.cal.timezone_id = None;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarPropName::CalendarOrder => {
|
CalendarPropName::CalendarOrder => {
|
||||||
self.0.order = 0;
|
self.cal.order = 0;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
CalendarPropName::SupportedCalendarComponentSet => {
|
CalendarPropName::SupportedCalendarComponentSet => {
|
||||||
@@ -251,12 +269,15 @@ impl Resource for CalendarResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_owner(&self) -> Option<&str> {
|
fn get_owner(&self) -> Option<&str> {
|
||||||
Some(&self.0.principal)
|
Some(&self.cal.principal)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
||||||
// TODO: read-only for subscription
|
if self.cal.subscription_url.is_some() {
|
||||||
Ok(UserPrivilegeSet::owner_only(self.0.principal == user.id))
|
return Ok(UserPrivilegeSet::read_only());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(UserPrivilegeSet::owner_only(self.cal.principal == user.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +297,10 @@ impl<C: CalendarStore + ?Sized> ResourceService for CalendarResourceService<C> {
|
|||||||
.get_calendar(principal, cal_id)
|
.get_calendar(principal, cal_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|_e| Error::NotFound)?;
|
.map_err(|_e| Error::NotFound)?;
|
||||||
Ok(calendar.into())
|
Ok(CalendarResource {
|
||||||
|
cal: calendar,
|
||||||
|
read_only: self.cal_store.is_read_only(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_members(
|
async fn get_members(
|
||||||
|
|||||||
@@ -122,7 +122,10 @@ impl<C: CalendarStore + ?Sized> ResourceService for PrincipalResourceService<C>
|
|||||||
.map(|cal| {
|
.map(|cal| {
|
||||||
(
|
(
|
||||||
CalendarResource::get_url(rmap, vec![principal, &cal.id]).unwrap(),
|
CalendarResource::get_url(rmap, vec![principal, &cal.id]).unwrap(),
|
||||||
cal.into(),
|
CalendarResource {
|
||||||
|
cal,
|
||||||
|
read_only: self.cal_store.is_read_only(),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
|
|||||||
@@ -63,6 +63,16 @@ impl UserPrivilegeSet {
|
|||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_only() -> Self {
|
||||||
|
Self {
|
||||||
|
privileges: HashSet::from([
|
||||||
|
UserPrivilege::Read,
|
||||||
|
UserPrivilege::ReadAcl,
|
||||||
|
UserPrivilege::ReadCurrentUserPrivilegeSet,
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<const N: usize> From<[UserPrivilege; N]> for UserPrivilegeSet {
|
impl<const N: usize> From<[UserPrivilege; N]> for UserPrivilegeSet {
|
||||||
|
|||||||
@@ -61,4 +61,6 @@ pub trait CalendarStore: Send + Sync + 'static {
|
|||||||
cal_id: &str,
|
cal_id: &str,
|
||||||
object_id: &str,
|
object_id: &str,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
|
fn is_read_only(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,4 +140,8 @@ impl<AS: AddressbookStore> CalendarStore for ContactBirthdayStore<AS> {
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
Err(Error::ReadOnly)
|
Err(Error::ReadOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_read_only(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -379,4 +379,8 @@ impl CalendarStore for SqliteStore {
|
|||||||
|
|
||||||
Ok((objects, deleted_objects, new_synctoken))
|
Ok((objects, deleted_objects, new_synctoken))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_read_only(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user