270 lines
9 KiB
Rust
270 lines
9 KiB
Rust
use crate::{
|
|
apub::actions::{CreateSubmission, DeleteComment, DeleteSubmission, UpdateSubmission},
|
|
Action, Context, Error, Outbound, Required,
|
|
};
|
|
use std::collections::HashSet;
|
|
|
|
impl Action for CreateSubmission {
|
|
fn perform(&self, ctx: &Context) -> Result<Option<Box<dyn Outbound + Send>>, Error> {
|
|
log::debug!("CreateSubmission");
|
|
let submission = ctx.store.submissions.create(
|
|
self.profile_id,
|
|
&hyaenidae_content::html(&self.title),
|
|
self.visibility,
|
|
)?;
|
|
|
|
let submission_id = submission.id();
|
|
|
|
let mut changes = submission.update();
|
|
changes
|
|
.local_only(self.local_only)
|
|
.logged_in_only(self.logged_in_only)
|
|
.sensitive(self.sensitive);
|
|
|
|
if self.published.is_some() {
|
|
changes.published(self.published);
|
|
}
|
|
if let Some(description) = &self.description {
|
|
changes.description(&hyaenidae_content::html(description));
|
|
}
|
|
if let Some(updated) = self.updated {
|
|
changes.updated(updated);
|
|
}
|
|
|
|
for file in &self.files {
|
|
changes.add_file(*file);
|
|
}
|
|
|
|
let submission = if changes.any_changes() {
|
|
ctx.store.submissions.update(&changes)?
|
|
} else {
|
|
submission
|
|
};
|
|
|
|
if let Some(apub_id) = &self.note_apub_id {
|
|
ctx.apub.submission(apub_id, submission_id)?;
|
|
}
|
|
|
|
if let Some(published) = submission.published() {
|
|
let profile_id = self.profile_id;
|
|
let ctx_clone = ctx.clone();
|
|
ctx.spawn_blocking(move || {
|
|
for follower_id in ctx_clone.store.view.follows.forward_iter(profile_id) {
|
|
if let Ok(Some(true)) = ctx_clone.store.profiles.is_local(follower_id) {
|
|
ctx_clone
|
|
.store
|
|
.view
|
|
.submissions
|
|
.new(follower_id, submission_id, published);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
if ctx.is_local(submission.profile_id())? && submission.published().is_some() {
|
|
if submission.is_local_only() {
|
|
return Ok(Some(Box::new(
|
|
crate::apub::results::LocalSubmissionCreated { submission_id },
|
|
)));
|
|
} else {
|
|
return Ok(Some(Box::new(crate::apub::results::SubmissionCreated {
|
|
submission_id,
|
|
})));
|
|
}
|
|
}
|
|
|
|
if submission.published().is_none() {
|
|
return Ok(Some(Box::new(
|
|
crate::apub::results::UnpublishedSubmissionCreated { submission_id },
|
|
)));
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
impl Action for UpdateSubmission {
|
|
fn perform(&self, ctx: &Context) -> Result<Option<Box<dyn Outbound + Send>>, Error> {
|
|
log::debug!("UpdateSubmission");
|
|
let submission_id = self.submission_id;
|
|
let submission = ctx
|
|
.store
|
|
.submissions
|
|
.by_id(submission_id)?
|
|
.req("submission by id")?;
|
|
|
|
let initial_published = submission.published().is_some();
|
|
|
|
let mut changes = submission.update();
|
|
if let Some(published) = self.published {
|
|
changes.published(Some(published));
|
|
}
|
|
if let Some(updated) = self.updated {
|
|
changes.updated(updated);
|
|
}
|
|
if let Some(title) = &self.title {
|
|
changes.title(&hyaenidae_content::html(title));
|
|
}
|
|
if let Some(title_source) = &self.title_source {
|
|
changes.title_source(title_source);
|
|
}
|
|
if let Some(description) = &self.description {
|
|
changes.description(&hyaenidae_content::html(description));
|
|
}
|
|
if let Some(description_source) = &self.description_source {
|
|
changes.description_source(description_source);
|
|
}
|
|
if let Some(visibility) = self.visibility {
|
|
changes.visibility(visibility);
|
|
}
|
|
if let Some(local_only) = self.local_only {
|
|
changes.local_only(local_only);
|
|
}
|
|
if let Some(logged_in_only) = self.logged_in_only {
|
|
changes.logged_in_only(logged_in_only);
|
|
}
|
|
if let Some(sensitive) = self.sensitive {
|
|
changes.sensitive(sensitive);
|
|
}
|
|
|
|
let mut removed_file_ids = HashSet::new();
|
|
if let Some(files) = &self.only_files {
|
|
for file in submission.files() {
|
|
changes.delete_file(*file);
|
|
removed_file_ids.insert(*file);
|
|
}
|
|
|
|
for file in files {
|
|
changes.add_file(*file);
|
|
removed_file_ids.remove(file);
|
|
}
|
|
} else {
|
|
if let Some(removed) = &self.removed_files {
|
|
for file in removed {
|
|
changes.delete_file(*file);
|
|
removed_file_ids.insert(*file);
|
|
}
|
|
}
|
|
if let Some(new) = &self.new_files {
|
|
for file in new {
|
|
changes.add_file(*file);
|
|
removed_file_ids.remove(file);
|
|
}
|
|
}
|
|
}
|
|
|
|
for file_id in removed_file_ids {
|
|
ctx.spawner.purge_file(file_id);
|
|
}
|
|
|
|
let submission = if changes.any_changes() {
|
|
ctx.store.submissions.update(&changes)?
|
|
} else {
|
|
submission
|
|
};
|
|
|
|
let newly_published = !initial_published && submission.published().is_some();
|
|
let profile_id = submission.profile_id();
|
|
|
|
if newly_published {
|
|
if let Some(published) = submission.published() {
|
|
let ctx_clone = ctx.clone();
|
|
ctx.spawn_blocking(move || {
|
|
for follower_id in ctx_clone.store.view.follows.forward_iter(profile_id) {
|
|
if let Ok(Some(true)) = ctx_clone.store.profiles.is_local(follower_id) {
|
|
ctx_clone.store.view.submissions.new(
|
|
follower_id,
|
|
submission_id,
|
|
published,
|
|
);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
if !submission.is_local_only() && ctx.is_local(profile_id)? {
|
|
if newly_published {
|
|
return Ok(Some(Box::new(crate::apub::results::SubmissionCreated {
|
|
submission_id,
|
|
})));
|
|
} else if submission.published().is_some() {
|
|
return Ok(Some(Box::new(crate::apub::results::SubmissionUpdated {
|
|
submission_id,
|
|
})));
|
|
}
|
|
} else {
|
|
if newly_published {
|
|
return Ok(Some(Box::new(
|
|
crate::apub::results::LocalSubmissionCreated { submission_id },
|
|
)));
|
|
} else {
|
|
return Ok(Some(Box::new(
|
|
crate::apub::results::LocalSubmissionUpdated { submission_id },
|
|
)));
|
|
}
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
fn delete_comment(comment_id: uuid::Uuid, ctx: &Context) -> Result<(), Error> {
|
|
Action::perform(
|
|
&DeleteComment {
|
|
delete_apub_id: None,
|
|
comment_id,
|
|
},
|
|
ctx,
|
|
)?;
|
|
Ok(())
|
|
}
|
|
|
|
impl Action for DeleteSubmission {
|
|
fn perform(&self, ctx: &Context) -> Result<Option<Box<dyn Outbound + Send>>, Error> {
|
|
let submission_id = self.submission_id;
|
|
let opt = ctx.store.submissions.delete(submission_id)?;
|
|
|
|
if let Some(undo_submission) = opt {
|
|
if undo_submission.0.published().is_some() {
|
|
let note_apub_id = ctx
|
|
.apub
|
|
.apub_for_submission(submission_id)?
|
|
.req("apub for submission")?;
|
|
ctx.apub.delete_object(¬e_apub_id)?;
|
|
}
|
|
|
|
let ctx_clone = ctx.clone();
|
|
ctx.spawn_blocking(move || {
|
|
for comment_id in ctx_clone.store.comments.for_submission(submission_id) {
|
|
if let Err(e) = delete_comment(comment_id, &ctx_clone) {
|
|
log::error!("Failed to delete comment {}: {}", comment_id, e);
|
|
}
|
|
}
|
|
});
|
|
|
|
for file_id in undo_submission.0.files() {
|
|
ctx.spawner.purge_file(*file_id);
|
|
}
|
|
|
|
let ctx_clone = ctx.clone();
|
|
ctx.spawn_blocking(move || {
|
|
ctx_clone.store.view.submissions.remove(submission_id);
|
|
});
|
|
|
|
let profile_id = undo_submission.0.profile_id();
|
|
if !undo_submission.0.is_local_only()
|
|
&& ctx.is_local(profile_id)?
|
|
&& undo_submission.0.published().is_some()
|
|
{
|
|
return Ok(Some(Box::new(crate::apub::results::SubmissionDeleted {
|
|
profile_id,
|
|
submission_id,
|
|
})));
|
|
}
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
}
|