Import upstream v0.16.22, stripped
Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
This commit is contained in:
@@ -0,0 +1,656 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{calendar_event::CalendarSyntheticId, changes::state::JmapCacheState};
|
||||
use calcard::{
|
||||
common::{PartialDateTime, timezone::Tz},
|
||||
icalendar::{
|
||||
ICalendar, ICalendarComponent, ICalendarComponentType, ICalendarEntry, ICalendarParameter,
|
||||
ICalendarParameterName, ICalendarParameterValue, ICalendarParticipationRole,
|
||||
ICalendarProperty, ICalendarValue,
|
||||
},
|
||||
jscalendar::{
|
||||
JSCalendarDateTime, JSCalendarProperty, JSCalendarValue, import::ConversionOptions,
|
||||
},
|
||||
};
|
||||
use chrono::DateTime;
|
||||
use common::{Server, auth::AccessToken};
|
||||
use groupware::{
|
||||
cache::GroupwareCache,
|
||||
calendar::{
|
||||
CalendarEvent, EVENT_DRAFT, EVENT_HIDE_ATTENDEES, EVENT_INVITE_OTHERS, EVENT_INVITE_SELF,
|
||||
PREF_USE_DEFAULT_ALERTS, expand::CalendarEventExpansion,
|
||||
},
|
||||
};
|
||||
use jmap_proto::{
|
||||
method::get::{GetRequest, GetResponse},
|
||||
object::{JmapObjectId, calendar_event},
|
||||
request::IntoValid,
|
||||
};
|
||||
use jmap_tools::{Key, Map, Value};
|
||||
use std::{borrow::Cow, str::FromStr};
|
||||
use store::{
|
||||
ValueKey,
|
||||
ahash::{AHashMap, AHashSet},
|
||||
roaring::RoaringBitmap,
|
||||
write::{AlignedBytes, Archive},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::{
|
||||
acl::Acl,
|
||||
blob::BlobId,
|
||||
collection::{Collection, SyncCollection},
|
||||
id::Id,
|
||||
};
|
||||
|
||||
pub trait CalendarEventGet: Sync + Send {
|
||||
fn calendar_event_get(
|
||||
&self,
|
||||
request: GetRequest<calendar_event::CalendarEvent>,
|
||||
access_token: &AccessToken,
|
||||
) -> impl Future<Output = trc::Result<GetResponse<calendar_event::CalendarEvent>>> + Send;
|
||||
}
|
||||
|
||||
impl CalendarEventGet for Server {
|
||||
async fn calendar_event_get(
|
||||
&self,
|
||||
mut request: GetRequest<calendar_event::CalendarEvent>,
|
||||
access_token: &AccessToken,
|
||||
) -> trc::Result<GetResponse<calendar_event::CalendarEvent>> {
|
||||
let return_all_properties = request.properties.is_none();
|
||||
let properties = request.unwrap_properties(&[]);
|
||||
let account_id = request.account_id.document_id();
|
||||
let personal_id = access_token.personal_id(account_id, Collection::Calendar);
|
||||
let cache = self
|
||||
.fetch_dav_resources(
|
||||
access_token.account_id(),
|
||||
account_id,
|
||||
SyncCollection::Calendar,
|
||||
)
|
||||
.await?;
|
||||
let calendar_event_ids = if access_token.is_member(account_id) {
|
||||
cache.document_ids(false).collect::<RoaringBitmap>()
|
||||
} else {
|
||||
cache.shared_items(access_token, [Acl::ReadItems], true)
|
||||
};
|
||||
let (mut ids, has_synthetic_ids) = if let Some(rr) = request.ids.take() {
|
||||
let rr = rr.unwrap();
|
||||
if rr.len() > self.core.jmap.get_max_objects {
|
||||
return Err(trc::JmapEvent::RequestTooLarge.into_err());
|
||||
}
|
||||
let mut ids = Vec::with_capacity(rr.len());
|
||||
let mut has_synthetic_ids = false;
|
||||
|
||||
for id in rr.into_valid() {
|
||||
has_synthetic_ids |= id.is_synthetic();
|
||||
ids.push(id);
|
||||
}
|
||||
|
||||
(ids, has_synthetic_ids)
|
||||
} else {
|
||||
(
|
||||
calendar_event_ids
|
||||
.iter()
|
||||
.take(self.core.jmap.get_max_objects)
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>(),
|
||||
false,
|
||||
)
|
||||
};
|
||||
let mut response = GetResponse {
|
||||
account_id: request.account_id.into(),
|
||||
state: cache.get_state(false).into(),
|
||||
list: Vec::with_capacity(ids.len()),
|
||||
not_found: vec![],
|
||||
};
|
||||
let mut return_converted_props = !return_all_properties;
|
||||
let mut return_is_origin = false;
|
||||
let mut return_utc_dates = false;
|
||||
|
||||
let (jmap_properties, jscal_properties) = if !return_all_properties {
|
||||
let mut jmap_properties = Vec::with_capacity(4);
|
||||
let mut jscal_properties = Vec::with_capacity(properties.len());
|
||||
|
||||
for property in properties {
|
||||
match property {
|
||||
JSCalendarProperty::Id
|
||||
| JSCalendarProperty::BaseEventId
|
||||
| JSCalendarProperty::CalendarIds
|
||||
| JSCalendarProperty::IsDraft
|
||||
| JSCalendarProperty::UseDefaultAlerts
|
||||
| JSCalendarProperty::MayInviteSelf
|
||||
| JSCalendarProperty::MayInviteOthers
|
||||
| JSCalendarProperty::HideAttendees => {
|
||||
jmap_properties.push(property);
|
||||
}
|
||||
JSCalendarProperty::UtcStart | JSCalendarProperty::UtcEnd => {
|
||||
return_utc_dates = true;
|
||||
jmap_properties.push(property);
|
||||
}
|
||||
JSCalendarProperty::IsOrigin => {
|
||||
return_is_origin = true;
|
||||
jmap_properties.push(property);
|
||||
}
|
||||
_ => {
|
||||
if matches!(property, JSCalendarProperty::ICalendar) {
|
||||
return_converted_props = true;
|
||||
}
|
||||
|
||||
jscal_properties.push(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
(jmap_properties, jscal_properties)
|
||||
} else {
|
||||
return_is_origin = true;
|
||||
(
|
||||
vec![
|
||||
JSCalendarProperty::Id,
|
||||
JSCalendarProperty::CalendarIds,
|
||||
JSCalendarProperty::IsDraft,
|
||||
JSCalendarProperty::IsOrigin,
|
||||
],
|
||||
vec![],
|
||||
)
|
||||
};
|
||||
let current_account_info = self
|
||||
.account_info(access_token.account_id())
|
||||
.await
|
||||
.caused_by(trc::location!())?;
|
||||
let return_is_origin = if return_is_origin {
|
||||
if account_id == access_token.account_id() {
|
||||
Some(Cow::Borrowed(¤t_account_info))
|
||||
} else {
|
||||
Some(
|
||||
self.account_info(account_id)
|
||||
.await
|
||||
.map(Cow::Owned)
|
||||
.caused_by(trc::location!())?,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Sort by baseId
|
||||
let mut original_order: Option<AHashMap<Id, usize>> = None;
|
||||
if has_synthetic_ids {
|
||||
original_order = Some(ids.iter().enumerate().map(|(i, id)| (*id, i)).collect());
|
||||
ids.sort_unstable_by_key(|id| id.document_id());
|
||||
}
|
||||
let mut ids = ids.into_iter().peekable();
|
||||
|
||||
// Process arguments
|
||||
let override_range = if request.arguments.recurrence_overrides_after.is_some()
|
||||
|| request.arguments.recurrence_overrides_before.is_some()
|
||||
{
|
||||
let after = request
|
||||
.arguments
|
||||
.recurrence_overrides_after
|
||||
.map(|v| v.timestamp)
|
||||
.unwrap_or(i64::MIN);
|
||||
let before = request
|
||||
.arguments
|
||||
.recurrence_overrides_before
|
||||
.map(|v| v.timestamp)
|
||||
.unwrap_or(i64::MAX);
|
||||
if after < before {
|
||||
Some(after..before)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let default_tz = request.arguments.time_zone.unwrap_or(Tz::UTC);
|
||||
let reduce_participants = request.arguments.reduce_participants.unwrap_or(false);
|
||||
|
||||
while let Some(id) = ids.next() {
|
||||
// Obtain the calendar_event object
|
||||
let document_id = id.document_id();
|
||||
if !calendar_event_ids.contains(document_id) {
|
||||
response.push_not_found(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(_calendar_event) = self
|
||||
.store()
|
||||
.get_value::<Archive<AlignedBytes>>(ValueKey::archive(
|
||||
account_id,
|
||||
Collection::CalendarEvent,
|
||||
document_id,
|
||||
))
|
||||
.await?
|
||||
else {
|
||||
response.push_not_found(id);
|
||||
continue;
|
||||
};
|
||||
let mut calendar_event = _calendar_event
|
||||
.deserialize::<CalendarEvent>()
|
||||
.caused_by(trc::location!())?;
|
||||
|
||||
// Extract recurrence keys from synthetic ids
|
||||
let mut recurrence_keys = AHashSet::new();
|
||||
let mut include_base_event = false;
|
||||
if let Some(recurrence_key) = id.recurrence_key() {
|
||||
recurrence_keys.insert(recurrence_key);
|
||||
} else {
|
||||
include_base_event = true;
|
||||
}
|
||||
while let Some(next_id) = ids.peek() {
|
||||
if next_id.document_id() == document_id {
|
||||
if let Some(recurrence_key) = next_id.recurrence_key() {
|
||||
recurrence_keys.insert(recurrence_key);
|
||||
} else {
|
||||
include_base_event = true;
|
||||
}
|
||||
ids.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce participants
|
||||
if reduce_participants {
|
||||
for component in &mut calendar_event.data.event.components {
|
||||
if component.component_type.is_scheduling_object() {
|
||||
component.entries.retain(|entry| match &entry.name {
|
||||
ICalendarProperty::Attendee => {
|
||||
entry.parameters(&ICalendarParameterName::Role).any(|role| {
|
||||
matches!(
|
||||
role,
|
||||
ICalendarParameterValue::Role(
|
||||
ICalendarParticipationRole::Owner,
|
||||
),
|
||||
)
|
||||
}) || entry.calendar_address().is_some_and(|addr| {
|
||||
current_account_info
|
||||
.addresses()
|
||||
.iter()
|
||||
.any(|a| a.eq_ignore_ascii_case(addr))
|
||||
})
|
||||
}
|
||||
_ => true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expand synthetic ids
|
||||
let mut results = Vec::with_capacity(recurrence_keys.len() + 1);
|
||||
if !recurrence_keys.is_empty() {
|
||||
let ical = &calendar_event.data.event;
|
||||
if let Some(expansions) = calendar_event
|
||||
.data
|
||||
.expand_from_ids(&mut recurrence_keys, default_tz)
|
||||
{
|
||||
for expansion in expansions {
|
||||
let Some(recurrence_key) = expansion.recurrence_key() else {
|
||||
continue;
|
||||
};
|
||||
let component = &ical.components[expansion.comp_id as usize];
|
||||
let source_component = component;
|
||||
let is_recurrent = component.is_recurrent();
|
||||
let is_recurrent_or_override =
|
||||
is_recurrent || component.is_recurrence_override();
|
||||
let mut has_duration = false;
|
||||
let component_ids = &component.component_ids;
|
||||
let mut tz = None;
|
||||
let mut component = ICalendarComponent {
|
||||
component_type: component.component_type.clone(),
|
||||
component_ids: Vec::new(),
|
||||
entries: component
|
||||
.entries
|
||||
.iter()
|
||||
.filter(|entry| match &entry.name {
|
||||
ICalendarProperty::Dtstart
|
||||
| ICalendarProperty::Dtend
|
||||
| ICalendarProperty::Exdate
|
||||
| ICalendarProperty::Exrule
|
||||
| ICalendarProperty::Rdate
|
||||
| ICalendarProperty::Rrule
|
||||
| ICalendarProperty::RecurrenceId => {
|
||||
if let Some(new_tz) = entry
|
||||
.tz_id()
|
||||
.and_then(|id| Tz::from_str(id).ok())
|
||||
.filter(|tz| *tz != Tz::UTC)
|
||||
{
|
||||
tz = Some(new_tz);
|
||||
}
|
||||
false
|
||||
}
|
||||
ICalendarProperty::Due
|
||||
| ICalendarProperty::Completed
|
||||
| ICalendarProperty::Created => is_recurrent,
|
||||
ICalendarProperty::Duration => {
|
||||
has_duration = true;
|
||||
true
|
||||
}
|
||||
_ => true,
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>(),
|
||||
};
|
||||
|
||||
let tz = tz.unwrap_or(default_tz);
|
||||
let tz_name = tz.name().unwrap_or_default().to_string();
|
||||
|
||||
let start_timestamp = DateTime::from_timestamp(expansion.start, 0)
|
||||
.map(|dt| dt.with_timezone(&tz))
|
||||
.map(|dt| dt.naive_local())
|
||||
.map(|dt| dt.and_utc().timestamp())
|
||||
.unwrap_or(expansion.start);
|
||||
|
||||
let end_timestamp = DateTime::from_timestamp(expansion.end, 0)
|
||||
.map(|dt| dt.with_timezone(&tz))
|
||||
.map(|dt| dt.naive_local())
|
||||
.map(|dt| dt.and_utc().timestamp())
|
||||
.unwrap_or(expansion.end);
|
||||
|
||||
component.entries.push(ICalendarEntry {
|
||||
name: ICalendarProperty::Dtstart,
|
||||
params: vec![ICalendarParameter::tzid(tz_name.clone())],
|
||||
values: vec![ICalendarValue::PartialDateTime(Box::new(
|
||||
PartialDateTime::from_naive_timestamp(start_timestamp),
|
||||
))],
|
||||
});
|
||||
|
||||
if is_recurrent_or_override {
|
||||
component.entries.push(
|
||||
source_component
|
||||
.property(&ICalendarProperty::RecurrenceId)
|
||||
.filter(|entry| {
|
||||
entry
|
||||
.parameters(&ICalendarParameterName::Range)
|
||||
.next()
|
||||
.is_none()
|
||||
|| calendar_event
|
||||
.data
|
||||
.expand_single(expansion.comp_id, default_tz)
|
||||
.is_some_and(|first| {
|
||||
first.start_naive == expansion.start_naive
|
||||
})
|
||||
})
|
||||
.cloned()
|
||||
.unwrap_or_else(|| ICalendarEntry {
|
||||
name: ICalendarProperty::RecurrenceId,
|
||||
params: vec![ICalendarParameter::tzid(tz_name.clone())],
|
||||
values: vec![ICalendarValue::PartialDateTime(Box::new(
|
||||
PartialDateTime::from_naive_timestamp(start_timestamp),
|
||||
))],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if !has_duration {
|
||||
component.entries.push(ICalendarEntry {
|
||||
name: ICalendarProperty::Dtend,
|
||||
params: vec![ICalendarParameter::tzid(tz_name)],
|
||||
values: vec![ICalendarValue::PartialDateTime(Box::new(
|
||||
PartialDateTime::from_naive_timestamp(end_timestamp),
|
||||
))],
|
||||
});
|
||||
}
|
||||
|
||||
let mut expanded_ical = ICalendar {
|
||||
components: vec![
|
||||
ICalendarComponent {
|
||||
component_type: ICalendarComponentType::VCalendar,
|
||||
entries: vec![],
|
||||
component_ids: vec![1],
|
||||
},
|
||||
component,
|
||||
],
|
||||
};
|
||||
|
||||
if !component_ids.is_empty() {
|
||||
for component_id in component_ids {
|
||||
let mut sub_component =
|
||||
ical.components[*component_id as usize].clone();
|
||||
sub_component.component_ids.clear();
|
||||
let component_id = expanded_ical.components.len() as u32;
|
||||
expanded_ical.components.push(sub_component);
|
||||
expanded_ical.components[1].component_ids.push(component_id);
|
||||
}
|
||||
}
|
||||
|
||||
results.push((
|
||||
<Id as CalendarSyntheticId>::new(recurrence_key, document_id),
|
||||
expanded_ical,
|
||||
expansion,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for recurrence_key in recurrence_keys {
|
||||
response.push_not_found(<Id as CalendarSyntheticId>::new(
|
||||
recurrence_key,
|
||||
document_id,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if include_base_event {
|
||||
let mut event = std::mem::take(&mut calendar_event.data.event);
|
||||
|
||||
// Obtain UTC start/end if requested
|
||||
let expansion = if return_utc_dates
|
||||
&& let Some(expansion) = event
|
||||
.components
|
||||
.iter()
|
||||
.position(|c| {
|
||||
c.component_type.is_scheduling_object() && !c.is_recurrence_override()
|
||||
})
|
||||
.and_then(|comp_id| {
|
||||
calendar_event
|
||||
.data
|
||||
.expand_single(comp_id as u32, default_tz)
|
||||
}) {
|
||||
expansion
|
||||
} else {
|
||||
CalendarEventExpansion::default()
|
||||
};
|
||||
|
||||
// Remove recurrence ids
|
||||
if let Some(range) = &override_range {
|
||||
let remove_ids = event
|
||||
.components
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(comp_id, c)| {
|
||||
if c.is_recurrence_override()
|
||||
&& let Some(timestamp) = c
|
||||
.property(&ICalendarProperty::RecurrenceId)
|
||||
.and_then(|p| p.values.first())
|
||||
.and_then(|v| v.as_partial_date_time())
|
||||
.and_then(|v| v.to_date_time())
|
||||
.and_then(|v| v.to_date_time_with_tz(default_tz))
|
||||
.map(|v| v.timestamp())
|
||||
&& !range.contains(×tamp)
|
||||
{
|
||||
Some(comp_id as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<AHashSet<_>>();
|
||||
if !remove_ids.is_empty() {
|
||||
for component in &mut event.components {
|
||||
component
|
||||
.component_ids
|
||||
.retain(|id| !remove_ids.contains(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results.push((Id::from(document_id), event, expansion));
|
||||
}
|
||||
|
||||
for (id, ical, expansion) in results {
|
||||
let is_origin = return_is_origin.as_ref().is_some_and(|account| {
|
||||
ical.components
|
||||
.iter()
|
||||
.find(|c| c.component_type.is_scheduling_object())
|
||||
.and_then(|c| c.property(&ICalendarProperty::Organizer))
|
||||
.and_then(|v| v.calendar_address())
|
||||
.is_none_or(|v| {
|
||||
account
|
||||
.addresses()
|
||||
.iter()
|
||||
.any(|a| a.eq_ignore_ascii_case(v))
|
||||
})
|
||||
});
|
||||
|
||||
let jscal = ical
|
||||
.into_jscalendar_with_opt::<Id, BlobId>(
|
||||
ConversionOptions::default()
|
||||
.include_ical_components(return_converted_props)
|
||||
.return_first(true),
|
||||
)
|
||||
.into_inner();
|
||||
let mut result = if return_all_properties {
|
||||
jscal.into_object().unwrap()
|
||||
} else {
|
||||
let is_synthetic = id.is_synthetic();
|
||||
let is_null_for_synthetic = |property: &JSCalendarProperty<Id>| {
|
||||
is_synthetic
|
||||
&& matches!(
|
||||
property,
|
||||
JSCalendarProperty::RecurrenceRule
|
||||
| JSCalendarProperty::RecurrenceOverrides
|
||||
)
|
||||
};
|
||||
let mut result =
|
||||
Map::from_iter(jscal.into_expanded_object().filter(|(k, _)| {
|
||||
k.as_property().is_some_and(|p| {
|
||||
jscal_properties.contains(p) && !is_null_for_synthetic(p)
|
||||
})
|
||||
}));
|
||||
for property in jscal_properties
|
||||
.iter()
|
||||
.filter(|property| is_null_for_synthetic(property))
|
||||
{
|
||||
result.insert_unchecked(property.clone(), Value::Null);
|
||||
}
|
||||
result
|
||||
};
|
||||
|
||||
for property in &jmap_properties {
|
||||
match property {
|
||||
JSCalendarProperty::Id => {
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::Id,
|
||||
Value::Element(JSCalendarValue::Id(id)),
|
||||
);
|
||||
}
|
||||
JSCalendarProperty::BaseEventId => {
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::BaseEventId,
|
||||
if id.is_synthetic() {
|
||||
Value::Element(JSCalendarValue::Id(id.document_id().into()))
|
||||
} else {
|
||||
Value::Null
|
||||
},
|
||||
);
|
||||
}
|
||||
JSCalendarProperty::CalendarIds => {
|
||||
let mut obj = Map::with_capacity(calendar_event.names.len());
|
||||
for id in calendar_event.names.iter() {
|
||||
obj.insert_unchecked(
|
||||
JSCalendarProperty::IdValue(Id::from(id.parent_id)),
|
||||
true,
|
||||
);
|
||||
}
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::CalendarIds,
|
||||
Value::Object(obj),
|
||||
);
|
||||
}
|
||||
JSCalendarProperty::IsDraft => {
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::IsDraft,
|
||||
Value::Bool(calendar_event.flags & EVENT_DRAFT != 0),
|
||||
);
|
||||
}
|
||||
JSCalendarProperty::IsOrigin => {
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::IsOrigin,
|
||||
Value::Bool(is_origin),
|
||||
);
|
||||
}
|
||||
JSCalendarProperty::MayInviteSelf => {
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::MayInviteSelf,
|
||||
Value::Bool(calendar_event.flags & EVENT_INVITE_SELF != 0),
|
||||
);
|
||||
}
|
||||
JSCalendarProperty::MayInviteOthers => {
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::MayInviteOthers,
|
||||
Value::Bool(calendar_event.flags & EVENT_INVITE_OTHERS != 0),
|
||||
);
|
||||
}
|
||||
JSCalendarProperty::HideAttendees => {
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::HideAttendees,
|
||||
Value::Bool(calendar_event.flags & EVENT_HIDE_ATTENDEES != 0),
|
||||
);
|
||||
}
|
||||
|
||||
JSCalendarProperty::UtcStart => {
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::UtcStart,
|
||||
Value::Element(JSCalendarValue::DateTime(JSCalendarDateTime::new(
|
||||
expansion.start,
|
||||
false,
|
||||
))),
|
||||
);
|
||||
}
|
||||
JSCalendarProperty::UtcEnd => {
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::UtcEnd,
|
||||
Value::Element(JSCalendarValue::DateTime(JSCalendarDateTime::new(
|
||||
expansion.end,
|
||||
false,
|
||||
))),
|
||||
);
|
||||
}
|
||||
JSCalendarProperty::UseDefaultAlerts => {
|
||||
result.insert_unchecked(
|
||||
JSCalendarProperty::UseDefaultAlerts,
|
||||
Value::Bool(
|
||||
calendar_event
|
||||
.preferences(personal_id)
|
||||
.is_some_and(|v| v.flags & PREF_USE_DEFAULT_ALERTS != 0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
response.list.push(result.into());
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original order
|
||||
if let Some(original_order) = original_order {
|
||||
response.list.sort_by_key(|obj| {
|
||||
obj.as_object()
|
||||
.unwrap()
|
||||
.get(&Key::Property(JSCalendarProperty::<Id>::Id))
|
||||
.and_then(|v| v.as_element())
|
||||
.and_then(|v: &JSCalendarValue<Id, BlobId>| v.as_id())
|
||||
.and_then(|id| original_order.get(&id))
|
||||
.cloned()
|
||||
.unwrap_or(usize::MAX)
|
||||
});
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user