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,342 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{AddressBook, ArchivedAddressBook, ArchivedContactCard, ContactCard};
|
||||
use ahash::AHashSet;
|
||||
use calcard::{
|
||||
common::IanaString,
|
||||
vcard::{ArchivedVCardProperty, ArchivedVCardValue, VCardProperty},
|
||||
};
|
||||
use common::storage::index::{IndexValue, IndexableAndSerializableObject, IndexableObject};
|
||||
use nlp::language::{
|
||||
Language,
|
||||
detect::{LanguageDetector, MIN_LANGUAGE_SCORE},
|
||||
};
|
||||
use store::{
|
||||
search::{ContactSearchField, IndexDocument, SearchField},
|
||||
write::{IndexPropertyClass, SearchIndex, ValueClass},
|
||||
xxhash_rust::xxh3,
|
||||
};
|
||||
use types::{acl::AclGrant, collection::SyncCollection, field::ContactField};
|
||||
use utils::sanitize_email;
|
||||
|
||||
impl IndexableObject for AddressBook {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Acl {
|
||||
value: (&self.acls).into(),
|
||||
},
|
||||
IndexValue::Quota {
|
||||
used: self.size() as u32,
|
||||
},
|
||||
IndexValue::LogContainer {
|
||||
sync_collection: SyncCollection::AddressBook,
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexableObject for &ArchivedAddressBook {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Acl {
|
||||
value: self
|
||||
.acls
|
||||
.iter()
|
||||
.map(AclGrant::from)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
},
|
||||
IndexValue::Quota {
|
||||
used: self.size() as u32,
|
||||
},
|
||||
IndexValue::LogContainer {
|
||||
sync_collection: SyncCollection::AddressBook,
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexableAndSerializableObject for AddressBook {
|
||||
fn is_versioned() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexableObject for ContactCard {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Index {
|
||||
field: ContactField::Uid.into(),
|
||||
value: self.card.uid().into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: ContactField::Email.into(),
|
||||
value: self.emails().next().into(),
|
||||
},
|
||||
IndexValue::Property {
|
||||
field: ValueClass::IndexProperty(IndexPropertyClass::Integer {
|
||||
property: ContactField::CreatedToUpdated.into(),
|
||||
value: self.created as u64,
|
||||
}),
|
||||
value: self.modified.into(),
|
||||
},
|
||||
IndexValue::SearchIndex {
|
||||
index: SearchIndex::Contacts,
|
||||
hash: self.hashes().fold(0, |acc, hash| acc ^ hash),
|
||||
},
|
||||
IndexValue::Quota {
|
||||
used: self.size() as u32,
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::AddressBook,
|
||||
prefix: None,
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexableObject for &ArchivedContactCard {
|
||||
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
|
||||
[
|
||||
IndexValue::Index {
|
||||
field: ContactField::Uid.into(),
|
||||
value: self.card.uid().into(),
|
||||
},
|
||||
IndexValue::Index {
|
||||
field: ContactField::Email.into(),
|
||||
value: self.emails().next().into(),
|
||||
},
|
||||
IndexValue::Property {
|
||||
field: ValueClass::IndexProperty(IndexPropertyClass::Integer {
|
||||
property: ContactField::CreatedToUpdated.into(),
|
||||
value: self.created.to_native() as u64,
|
||||
}),
|
||||
value: (self.modified.to_native() as u64).into(),
|
||||
},
|
||||
IndexValue::SearchIndex {
|
||||
index: SearchIndex::Contacts,
|
||||
hash: self.hashes().fold(0, |acc, hash| acc ^ hash),
|
||||
},
|
||||
IndexValue::Quota {
|
||||
used: self.size() as u32,
|
||||
},
|
||||
IndexValue::LogItem {
|
||||
sync_collection: SyncCollection::AddressBook,
|
||||
prefix: None,
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexableAndSerializableObject for ContactCard {
|
||||
fn is_versioned() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressBook {
|
||||
pub fn size(&self) -> usize {
|
||||
self.dead_properties.size()
|
||||
+ self
|
||||
.preferences
|
||||
.iter()
|
||||
.map(|p| p.name.len() + p.description.as_ref().map_or(0, |n| n.len()))
|
||||
.sum::<usize>()
|
||||
+ self.name.len()
|
||||
+ std::mem::size_of::<AddressBook>()
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchivedAddressBook {
|
||||
pub fn size(&self) -> usize {
|
||||
self.dead_properties.size()
|
||||
+ self
|
||||
.preferences
|
||||
.iter()
|
||||
.map(|p| p.name.len() + p.description.as_ref().map_or(0, |n| n.len()))
|
||||
.sum::<usize>()
|
||||
+ self.name.len()
|
||||
+ std::mem::size_of::<AddressBook>()
|
||||
}
|
||||
}
|
||||
|
||||
impl ContactCard {
|
||||
pub fn size(&self) -> usize {
|
||||
self.dead_properties.size()
|
||||
+ self.display_name.as_ref().map_or(0, |n| n.len())
|
||||
+ self.names.iter().map(|n| n.name.len()).sum::<usize>()
|
||||
+ self.size as usize
|
||||
+ std::mem::size_of::<ContactCard>()
|
||||
}
|
||||
|
||||
pub fn hashes(&self) -> impl Iterator<Item = u64> {
|
||||
self.card
|
||||
.entries
|
||||
.iter()
|
||||
.filter(|e| {
|
||||
matches!(
|
||||
e.name,
|
||||
VCardProperty::Adr
|
||||
| VCardProperty::N
|
||||
| VCardProperty::Fn
|
||||
| VCardProperty::Title
|
||||
| VCardProperty::Org
|
||||
| VCardProperty::Note
|
||||
| VCardProperty::Nickname
|
||||
| VCardProperty::Email
|
||||
| VCardProperty::Kind
|
||||
| VCardProperty::Uid
|
||||
| VCardProperty::Member
|
||||
| VCardProperty::Impp
|
||||
| VCardProperty::Socialprofile
|
||||
| VCardProperty::Tel
|
||||
)
|
||||
})
|
||||
.flat_map(|e| e.values.iter().filter_map(|v| v.as_text()))
|
||||
.map(|v| xxh3::xxh3_64(v.as_bytes()))
|
||||
}
|
||||
|
||||
pub fn emails(&self) -> impl Iterator<Item = String> {
|
||||
self.card.properties(&VCardProperty::Email).flat_map(|e| {
|
||||
e.values
|
||||
.iter()
|
||||
.filter_map(|v| v.as_text().and_then(sanitize_email))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchivedContactCard {
|
||||
pub fn size(&self) -> usize {
|
||||
self.dead_properties.size()
|
||||
+ self.display_name.as_ref().map_or(0, |n| n.len())
|
||||
+ self.names.iter().map(|n| n.name.len()).sum::<usize>()
|
||||
+ self.size.to_native() as usize
|
||||
+ std::mem::size_of::<ContactCard>()
|
||||
}
|
||||
|
||||
pub fn hashes(&self) -> impl Iterator<Item = u64> {
|
||||
self.card
|
||||
.entries
|
||||
.iter()
|
||||
.filter(|e| {
|
||||
matches!(
|
||||
e.name,
|
||||
ArchivedVCardProperty::Adr
|
||||
| ArchivedVCardProperty::N
|
||||
| ArchivedVCardProperty::Fn
|
||||
| ArchivedVCardProperty::Title
|
||||
| ArchivedVCardProperty::Org
|
||||
| ArchivedVCardProperty::Note
|
||||
| ArchivedVCardProperty::Nickname
|
||||
| ArchivedVCardProperty::Email
|
||||
| ArchivedVCardProperty::Kind
|
||||
| ArchivedVCardProperty::Uid
|
||||
| ArchivedVCardProperty::Member
|
||||
| ArchivedVCardProperty::Impp
|
||||
| ArchivedVCardProperty::Socialprofile
|
||||
| ArchivedVCardProperty::Tel
|
||||
)
|
||||
})
|
||||
.flat_map(|e| e.values.iter().filter_map(|v| v.as_text()))
|
||||
.map(|v| xxh3::xxh3_64(v.as_bytes()))
|
||||
}
|
||||
|
||||
pub fn emails(&self) -> impl Iterator<Item = String> {
|
||||
self.card.properties(&VCardProperty::Email).flat_map(|e| {
|
||||
e.values
|
||||
.iter()
|
||||
.filter_map(|v| v.as_text().and_then(sanitize_email))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn index_document(
|
||||
&self,
|
||||
account_id: u32,
|
||||
document_id: u32,
|
||||
index_fields: &AHashSet<SearchField>,
|
||||
default_language: Language,
|
||||
) -> IndexDocument {
|
||||
let mut document = IndexDocument::new(SearchIndex::Contacts)
|
||||
.with_account_id(account_id)
|
||||
.with_document_id(document_id);
|
||||
let mut detector = LanguageDetector::new();
|
||||
|
||||
for entry in self.card.entries.iter() {
|
||||
let (is_text, is_keyword, field) = match entry.name {
|
||||
ArchivedVCardProperty::N => (false, false, ContactSearchField::Name),
|
||||
ArchivedVCardProperty::Nickname => (false, false, ContactSearchField::Nickname),
|
||||
ArchivedVCardProperty::Org => (false, false, ContactSearchField::Organization),
|
||||
ArchivedVCardProperty::Email => (false, false, ContactSearchField::Email),
|
||||
ArchivedVCardProperty::Tel => (false, false, ContactSearchField::Phone),
|
||||
ArchivedVCardProperty::Impp | ArchivedVCardProperty::Socialprofile => {
|
||||
(false, false, ContactSearchField::OnlineService)
|
||||
}
|
||||
ArchivedVCardProperty::Adr => (false, false, ContactSearchField::Address),
|
||||
ArchivedVCardProperty::Note => (true, false, ContactSearchField::Note),
|
||||
ArchivedVCardProperty::Kind => (false, true, ContactSearchField::Kind),
|
||||
ArchivedVCardProperty::Uid => (false, true, ContactSearchField::Uid),
|
||||
ArchivedVCardProperty::Member => (false, false, ContactSearchField::Member),
|
||||
_ => continue,
|
||||
};
|
||||
let field = SearchField::Contact(field);
|
||||
|
||||
if index_fields.is_empty() || index_fields.contains(&field) {
|
||||
for value in entry.values.iter() {
|
||||
match value {
|
||||
ArchivedVCardValue::Text(v) => {
|
||||
if !is_keyword {
|
||||
let lang = if is_text {
|
||||
detector.detect(v.as_str().trim(), MIN_LANGUAGE_SCORE);
|
||||
Language::Unknown
|
||||
} else {
|
||||
Language::None
|
||||
};
|
||||
|
||||
document.index_text(field.clone(), v, lang);
|
||||
} else {
|
||||
document.index_keyword(field.clone(), v.as_str());
|
||||
}
|
||||
}
|
||||
ArchivedVCardValue::Kind(v) => {
|
||||
document.index_keyword(field.clone(), v.as_str());
|
||||
}
|
||||
ArchivedVCardValue::Component(v) => {
|
||||
for item in v.iter() {
|
||||
document.index_text(field.clone(), item.trim(), Language::None);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
/*for param in entry.params.iter() {
|
||||
if let ArchivedVCardParameterValue::Text(value) = ¶m.value {
|
||||
let lang = if is_text {
|
||||
detector.detect(value.as_str(), MIN_LANGUAGE_SCORE);
|
||||
Language::Unknown
|
||||
} else {
|
||||
Language::None
|
||||
};
|
||||
document.index_text(field.clone(), value, lang);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
document.set_unknown_language(
|
||||
detector
|
||||
.most_frequent_language()
|
||||
.unwrap_or(default_language),
|
||||
);
|
||||
|
||||
document
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
pub mod index;
|
||||
pub mod storage;
|
||||
|
||||
use calcard::vcard::VCard;
|
||||
use common::DavName;
|
||||
use types::{acl::AclGrant, dead_property::DeadProperty};
|
||||
|
||||
#[derive(
|
||||
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
|
||||
)]
|
||||
#[rkyv(derive(Debug))]
|
||||
pub struct AddressBook {
|
||||
pub name: String,
|
||||
pub preferences: Vec<AddressBookPreferences>,
|
||||
pub subscribers: Vec<u32>,
|
||||
pub dead_properties: DeadProperty,
|
||||
pub acls: Vec<AclGrant>,
|
||||
pub created: i64,
|
||||
pub modified: i64,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
|
||||
)]
|
||||
#[rkyv(derive(Debug))]
|
||||
pub struct AddressBookPreferences {
|
||||
pub account_id: u32,
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub sort_order: u32,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
|
||||
)]
|
||||
pub struct ContactCard {
|
||||
pub names: Vec<DavName>,
|
||||
pub display_name: Option<String>,
|
||||
pub card: VCard,
|
||||
pub dead_properties: DeadProperty,
|
||||
pub created: i64,
|
||||
pub modified: i64,
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
impl AddressBook {
|
||||
pub fn preferences(&self, account_id: u32) -> &AddressBookPreferences {
|
||||
if self.preferences.len() == 1 {
|
||||
&self.preferences[0]
|
||||
} else {
|
||||
self.preferences
|
||||
.iter()
|
||||
.find(|p| p.account_id == account_id)
|
||||
.or_else(|| self.preferences.first())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn preferences_mut(&mut self, account_id: u32) -> &mut AddressBookPreferences {
|
||||
let idx = if let Some(idx) = self
|
||||
.preferences
|
||||
.iter()
|
||||
.position(|p| p.account_id == account_id)
|
||||
{
|
||||
idx
|
||||
} else {
|
||||
let mut preferences = self.preferences[0].clone();
|
||||
preferences.account_id = account_id;
|
||||
self.preferences.push(preferences);
|
||||
self.preferences.len() - 1
|
||||
};
|
||||
|
||||
&mut self.preferences[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchivedAddressBook {
|
||||
pub fn preferences(&self, account_id: u32) -> &ArchivedAddressBookPreferences {
|
||||
if self.preferences.len() == 1 {
|
||||
&self.preferences[0]
|
||||
} else {
|
||||
self.preferences
|
||||
.iter()
|
||||
.find(|p| p.account_id == account_id)
|
||||
.or_else(|| self.preferences.first())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ContactCard {
|
||||
pub fn added_addressbook_ids(
|
||||
&self,
|
||||
prev_data: &ArchivedContactCard,
|
||||
) -> impl Iterator<Item = u32> {
|
||||
self.names
|
||||
.iter()
|
||||
.filter(|m| prev_data.names.iter().all(|pm| pm.parent_id != m.parent_id))
|
||||
.map(|m| m.parent_id)
|
||||
}
|
||||
|
||||
pub fn removed_addressbook_ids(
|
||||
&self,
|
||||
prev_data: &ArchivedContactCard,
|
||||
) -> impl Iterator<Item = u32> {
|
||||
prev_data
|
||||
.names
|
||||
.iter()
|
||||
.filter(|m| self.names.iter().all(|pm| pm.parent_id != m.parent_id))
|
||||
.map(|m| m.parent_id.to_native())
|
||||
}
|
||||
|
||||
pub fn unchanged_addressbook_ids(
|
||||
&self,
|
||||
prev_data: &ArchivedContactCard,
|
||||
) -> impl Iterator<Item = u32> {
|
||||
self.names
|
||||
.iter()
|
||||
.filter(|m| prev_data.names.iter().any(|pm| pm.parent_id == m.parent_id))
|
||||
.map(|m| m.parent_id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::{AddressBook, ArchivedAddressBook, ArchivedContactCard, ContactCard};
|
||||
use crate::DestroyArchive;
|
||||
use common::{Server, auth::AccountTenantIds, storage::index::ObjectIndexBuilder};
|
||||
use store::{
|
||||
ValueKey,
|
||||
write::{AlignedBytes, Archive, BatchBuilder, now},
|
||||
};
|
||||
use trc::AddContext;
|
||||
use types::collection::{Collection, VanishedCollection};
|
||||
|
||||
impl ContactCard {
|
||||
pub fn update<'x>(
|
||||
self,
|
||||
changed_by: AccountTenantIds,
|
||||
card: Archive<&ArchivedContactCard>,
|
||||
account_id: u32,
|
||||
document_id: u32,
|
||||
batch: &'x mut BatchBuilder,
|
||||
) -> trc::Result<&'x mut BatchBuilder> {
|
||||
let mut new_card = self;
|
||||
|
||||
// Build card
|
||||
new_card.modified = now() as i64;
|
||||
|
||||
// Prepare write batch
|
||||
batch
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::ContactCard)
|
||||
.with_document(document_id)
|
||||
.custom(
|
||||
ObjectIndexBuilder::new()
|
||||
.with_current(card)
|
||||
.with_changes(new_card)
|
||||
.with_changed_by(changed_by),
|
||||
)
|
||||
.map(|b| b.commit_point())
|
||||
}
|
||||
|
||||
pub fn insert(
|
||||
self,
|
||||
changed_by: AccountTenantIds,
|
||||
account_id: u32,
|
||||
document_id: u32,
|
||||
batch: &mut BatchBuilder,
|
||||
) -> trc::Result<&mut BatchBuilder> {
|
||||
// Build card
|
||||
let mut card = self;
|
||||
let now = now() as i64;
|
||||
card.modified = now;
|
||||
card.created = now;
|
||||
|
||||
// Prepare write batch
|
||||
batch
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::ContactCard)
|
||||
.with_document(document_id)
|
||||
.custom(
|
||||
ObjectIndexBuilder::<(), _>::new()
|
||||
.with_changes(card)
|
||||
.with_changed_by(changed_by),
|
||||
)
|
||||
.map(|b| b.commit_point())
|
||||
}
|
||||
}
|
||||
|
||||
impl AddressBook {
|
||||
pub fn insert(
|
||||
self,
|
||||
changed_by: AccountTenantIds,
|
||||
account_id: u32,
|
||||
document_id: u32,
|
||||
batch: &mut BatchBuilder,
|
||||
) -> trc::Result<&mut BatchBuilder> {
|
||||
// Build address book
|
||||
let mut book = self;
|
||||
let now = now() as i64;
|
||||
book.modified = now;
|
||||
book.created = now;
|
||||
|
||||
// Prepare write batch
|
||||
batch
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::AddressBook)
|
||||
.with_document(document_id)
|
||||
.custom(
|
||||
ObjectIndexBuilder::<(), _>::new()
|
||||
.with_changes(book)
|
||||
.with_changed_by(changed_by),
|
||||
)
|
||||
.map(|b| b.commit_point())
|
||||
}
|
||||
|
||||
pub fn update<'x>(
|
||||
self,
|
||||
changed_by: AccountTenantIds,
|
||||
book: Archive<&ArchivedAddressBook>,
|
||||
account_id: u32,
|
||||
document_id: u32,
|
||||
batch: &'x mut BatchBuilder,
|
||||
) -> trc::Result<&'x mut BatchBuilder> {
|
||||
// Build address book
|
||||
let mut new_book = self;
|
||||
new_book.modified = now() as i64;
|
||||
|
||||
// Prepare write batch
|
||||
batch
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::AddressBook)
|
||||
.with_document(document_id)
|
||||
.custom(
|
||||
ObjectIndexBuilder::new()
|
||||
.with_current(book)
|
||||
.with_changes(new_book)
|
||||
.with_changed_by(changed_by),
|
||||
)
|
||||
.map(|b| b.commit_point())
|
||||
}
|
||||
}
|
||||
|
||||
impl DestroyArchive<Archive<&ArchivedAddressBook>> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn delete_with_cards(
|
||||
self,
|
||||
server: &Server,
|
||||
changed_by: AccountTenantIds,
|
||||
account_id: u32,
|
||||
document_id: u32,
|
||||
children_ids: Vec<u32>,
|
||||
delete_path: Option<String>,
|
||||
batch: &mut BatchBuilder,
|
||||
) -> trc::Result<()> {
|
||||
// Process deletions
|
||||
let addressbook_id = document_id;
|
||||
for document_id in children_ids {
|
||||
if let Some(card_) = server
|
||||
.store()
|
||||
.get_value::<Archive<AlignedBytes>>(ValueKey::archive(
|
||||
account_id,
|
||||
Collection::ContactCard,
|
||||
document_id,
|
||||
))
|
||||
.await?
|
||||
{
|
||||
DestroyArchive(
|
||||
card_
|
||||
.to_unarchived::<ContactCard>()
|
||||
.caused_by(trc::location!())?,
|
||||
)
|
||||
.delete(
|
||||
changed_by,
|
||||
account_id,
|
||||
document_id,
|
||||
addressbook_id,
|
||||
None,
|
||||
batch,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.delete(changed_by, account_id, document_id, delete_path, batch)
|
||||
}
|
||||
|
||||
pub fn delete(
|
||||
self,
|
||||
changed_by: AccountTenantIds,
|
||||
account_id: u32,
|
||||
document_id: u32,
|
||||
delete_path: Option<String>,
|
||||
batch: &mut BatchBuilder,
|
||||
) -> trc::Result<()> {
|
||||
let book = self.0;
|
||||
// Delete addressbook
|
||||
batch
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::AddressBook)
|
||||
.with_document(document_id)
|
||||
.custom(
|
||||
ObjectIndexBuilder::<_, ()>::new()
|
||||
.with_changed_by(changed_by)
|
||||
.with_current(book),
|
||||
)
|
||||
.caused_by(trc::location!())?;
|
||||
|
||||
if let Some(delete_path) = delete_path {
|
||||
batch.log_vanished_item(VanishedCollection::AddressBook, delete_path);
|
||||
}
|
||||
|
||||
batch.commit_point();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DestroyArchive<Archive<&ArchivedContactCard>> {
|
||||
pub fn delete(
|
||||
self,
|
||||
changed_by: AccountTenantIds,
|
||||
account_id: u32,
|
||||
document_id: u32,
|
||||
addressbook_id: u32,
|
||||
delete_path: Option<String>,
|
||||
batch: &mut BatchBuilder,
|
||||
) -> trc::Result<()> {
|
||||
let card = self.0;
|
||||
if let Some(delete_idx) = card
|
||||
.inner
|
||||
.names
|
||||
.iter()
|
||||
.position(|name| name.parent_id == addressbook_id)
|
||||
{
|
||||
batch
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::ContactCard);
|
||||
|
||||
if card.inner.names.len() > 1 {
|
||||
// Unlink addressbook id from card
|
||||
let mut new_card = card
|
||||
.deserialize::<ContactCard>()
|
||||
.caused_by(trc::location!())?;
|
||||
new_card.names.swap_remove(delete_idx);
|
||||
batch
|
||||
.with_document(document_id)
|
||||
.custom(
|
||||
ObjectIndexBuilder::new()
|
||||
.with_changed_by(changed_by)
|
||||
.with_current(card)
|
||||
.with_changes(new_card),
|
||||
)
|
||||
.caused_by(trc::location!())?;
|
||||
} else {
|
||||
// Delete card
|
||||
batch
|
||||
.with_document(document_id)
|
||||
.custom(
|
||||
ObjectIndexBuilder::<_, ()>::new()
|
||||
.with_changed_by(changed_by)
|
||||
.with_current(card),
|
||||
)
|
||||
.caused_by(trc::location!())?;
|
||||
}
|
||||
|
||||
if let Some(delete_path) = delete_path {
|
||||
batch.log_vanished_item(VanishedCollection::AddressBook, delete_path);
|
||||
}
|
||||
|
||||
batch.commit_point();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_all(
|
||||
self,
|
||||
changed_by: AccountTenantIds,
|
||||
account_id: u32,
|
||||
document_id: u32,
|
||||
batch: &mut BatchBuilder,
|
||||
) -> trc::Result<()> {
|
||||
batch
|
||||
.with_account_id(account_id)
|
||||
.with_collection(Collection::ContactCard)
|
||||
.with_document(document_id)
|
||||
.custom(
|
||||
ObjectIndexBuilder::<_, ()>::new()
|
||||
.with_changed_by(changed_by)
|
||||
.with_current(self.0),
|
||||
)
|
||||
.caused_by(trc::location!())
|
||||
.map(|b| {
|
||||
b.commit_point();
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user