mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 17:02:32 +00:00
Compare commits
1 Commits
v0.5.1
...
feature/ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41039242ee |
@@ -58,6 +58,13 @@ pub async fn route_get<C: CalendarStore, S: SubscriptionStore>(
|
||||
params: None,
|
||||
});
|
||||
}
|
||||
if calendar.color.is_some() {
|
||||
ical_calendar_builder = ical_calendar_builder.set(Property {
|
||||
name: "X-RUSTICAL-COLOR".to_owned(),
|
||||
value: calendar.color,
|
||||
params: None,
|
||||
});
|
||||
}
|
||||
let mut ical_calendar = ical_calendar_builder.build();
|
||||
|
||||
for object in &objects {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod mkcalendar;
|
||||
// pub mod post;
|
||||
pub mod get;
|
||||
pub mod put;
|
||||
pub mod report;
|
||||
|
||||
101
crates/caldav/src/calendar/methods/put.rs
Normal file
101
crates/caldav/src/calendar/methods/put.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::calendar::prop::SupportedCalendarComponent;
|
||||
use crate::calendar::{self, CalendarResourceService};
|
||||
use crate::{Error, calendar_set};
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::StatusCode;
|
||||
use ical::generator::Emitter;
|
||||
use ical::parser::ical::component::IcalTimeZone;
|
||||
use ical::{IcalParser, parser::Component};
|
||||
use rustical_ical::CalendarObjectType;
|
||||
use rustical_store::{Calendar, CalendarStore, SubscriptionStore, auth::User};
|
||||
use tracing::instrument;
|
||||
|
||||
#[instrument(skip(cal_store))]
|
||||
pub async fn route_put<C: CalendarStore, S: SubscriptionStore>(
|
||||
Path((principal, cal_id)): Path<(String, String)>,
|
||||
State(CalendarResourceService { cal_store, .. }): State<CalendarResourceService<C, S>>,
|
||||
user: User,
|
||||
body: String,
|
||||
) -> Result<Response, Error> {
|
||||
if !user.is_principal(&principal) {
|
||||
return Err(crate::Error::Unauthorized);
|
||||
}
|
||||
|
||||
let mut parser = IcalParser::new(body.as_bytes());
|
||||
let cal = parser
|
||||
.next()
|
||||
.ok_or(rustical_ical::Error::MissingCalendar)?
|
||||
.map_err(rustical_ical::Error::from)?;
|
||||
if parser.next().is_some() {
|
||||
return Err(rustical_ical::Error::InvalidData(
|
||||
"multiple calendars, only one allowed".to_owned(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
if !cal.alarms.is_empty() || !cal.free_busys.is_empty() {
|
||||
return Err(rustical_ical::Error::InvalidData(
|
||||
"Importer does not support VALARM and VFREEBUSY components".to_owned(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let mut objects = vec![];
|
||||
for event in cal.events {}
|
||||
for todo in cal.todos {}
|
||||
for journal in cal.journals {}
|
||||
|
||||
let timezones: HashMap<String, IcalTimeZone> = cal
|
||||
.timezones
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter_map(|timezone| {
|
||||
let timezone_prop = timezone.get_property("TZID")?.to_owned();
|
||||
let tzid = timezone_prop.value?;
|
||||
Some((tzid, timezone))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let displayname = cal.get_property("X-WR-CALNAME").and_then(|prop| prop.value);
|
||||
let description = cal.get_property("X-WR-CALDESC").and_then(|prop| prop.value);
|
||||
let color = cal
|
||||
.get_property("X-RUSTICAL-COLOR")
|
||||
.and_then(|prop| prop.value);
|
||||
let timezone_id = cal
|
||||
.get_property("X-WR-TIMEZONE")
|
||||
.and_then(|prop| prop.value);
|
||||
let timezone = timezone_id
|
||||
.and_then(|tzid| timezones.get(&tzid))
|
||||
.map(|timezone| timezone.generate());
|
||||
|
||||
let mut components = vec![CalendarObjectType::Event, CalendarObjectType::Todo];
|
||||
if !cal.journals.is_empty() {
|
||||
components.push(CalendarObjectType::Journal);
|
||||
}
|
||||
|
||||
let calendar = Calendar {
|
||||
principal: principal.clone(),
|
||||
id: cal_id,
|
||||
displayname,
|
||||
description,
|
||||
color,
|
||||
timezone_id,
|
||||
timezone,
|
||||
components,
|
||||
subscription_url: None,
|
||||
push_topic: uuid::Uuid::new_v4().to_string(),
|
||||
synctoken: 0,
|
||||
deleted_at: None,
|
||||
order: 0,
|
||||
};
|
||||
|
||||
cal_store
|
||||
.import_calendar(&principal, calendar, objects)
|
||||
.await?;
|
||||
|
||||
Ok(StatusCode::CREATED.into_response())
|
||||
}
|
||||
@@ -81,4 +81,11 @@ pub trait CalendarStore: Send + Sync + 'static {
|
||||
) -> Result<(), Error>;
|
||||
|
||||
fn is_read_only(&self) -> bool;
|
||||
|
||||
async fn import_calendar(
|
||||
&self,
|
||||
principal: &str,
|
||||
calendar: Calendar,
|
||||
objects: Vec<CalendarObject>,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
@@ -158,4 +158,13 @@ impl<AS: AddressbookStore> CalendarStore for ContactBirthdayStore<AS> {
|
||||
fn is_read_only(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn import_calendar(
|
||||
&self,
|
||||
_principal: &str,
|
||||
_calendar: Calendar,
|
||||
_objects: Vec<CalendarObject>,
|
||||
) -> Result<(), Error> {
|
||||
Err(Error::ReadOnly)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,6 +673,33 @@ impl CalendarStore for SqliteCalendarStore {
|
||||
fn is_read_only(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[instrument(skip(calendar, objects))]
|
||||
async fn import_calendar(
|
||||
&self,
|
||||
principal: &str,
|
||||
calendar: Calendar,
|
||||
objects: Vec<CalendarObject>,
|
||||
) -> Result<(), Error> {
|
||||
let mut tx = self.db.begin().await.map_err(crate::Error::from)?;
|
||||
|
||||
let cal_id = calendar.id.clone();
|
||||
Self::_insert_calendar(&mut *tx, calendar).await?;
|
||||
|
||||
for object in objects {
|
||||
Self::_put_object(
|
||||
&mut *tx,
|
||||
principal.to_owned(),
|
||||
cal_id.clone(),
|
||||
object,
|
||||
false,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
tx.commit().await.map_err(crate::Error::from)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Logs an operation to the events
|
||||
|
||||
Reference in New Issue
Block a user