Profiles: Add more audience options to submissions
- In addition to Followers/Unlisted/Public, now there's two toggles to limit to logged-in users, or to limit to the current server Adding federated login-required for submissions is a TODO item. - Don't accept federated updates older than the submission's updated field
This commit is contained in:
parent
8253a211ae
commit
597bbe94b9
|
@ -167,6 +167,7 @@ pub(super) fn note(
|
||||||
return Ok(Err(RecoverableError::MissingImages(missing_files)));
|
return Ok(Err(RecoverableError::MissingImages(missing_files)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log::warn!("logged_in_only not implmented");
|
||||||
return Ok(Ok(Box::new(CreateSubmission {
|
return Ok(Ok(Box::new(CreateSubmission {
|
||||||
note_apub_id: Some(note_id.to_owned()),
|
note_apub_id: Some(note_id.to_owned()),
|
||||||
profile_id,
|
profile_id,
|
||||||
|
@ -175,6 +176,8 @@ pub(super) fn note(
|
||||||
published: Some(published.into()),
|
published: Some(published.into()),
|
||||||
files: existing_files,
|
files: existing_files,
|
||||||
visibility: find_visibility(note),
|
visibility: find_visibility(note),
|
||||||
|
local_only: false,
|
||||||
|
logged_in_only: false,
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,6 +371,7 @@ pub(super) fn update_note(
|
||||||
.and_then(|c| c.as_single_xsd_string())
|
.and_then(|c| c.as_single_xsd_string())
|
||||||
.map(|s| s.to_owned());
|
.map(|s| s.to_owned());
|
||||||
let published = note.published().req("published")?;
|
let published = note.published().req("published")?;
|
||||||
|
let updated = note.updated().req("updated")?;
|
||||||
|
|
||||||
let mut missing_files = vec![];
|
let mut missing_files = vec![];
|
||||||
let mut existing_files = vec![];
|
let mut existing_files = vec![];
|
||||||
|
@ -408,6 +412,7 @@ pub(super) fn update_note(
|
||||||
return Ok(Err(RecoverableError::MissingImages(missing_files)));
|
return Ok(Err(RecoverableError::MissingImages(missing_files)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log::warn!("logged_in_only not implmented");
|
||||||
Ok(Ok(Box::new(UpdateSubmission {
|
Ok(Ok(Box::new(UpdateSubmission {
|
||||||
submission_id,
|
submission_id,
|
||||||
title,
|
title,
|
||||||
|
@ -416,9 +421,12 @@ pub(super) fn update_note(
|
||||||
description_source: None,
|
description_source: None,
|
||||||
visibility: Some(find_visibility(note)),
|
visibility: Some(find_visibility(note)),
|
||||||
published: Some(published.into()),
|
published: Some(published.into()),
|
||||||
|
updated: Some(updated.into()),
|
||||||
removed_files: None,
|
removed_files: None,
|
||||||
new_files: None,
|
new_files: None,
|
||||||
only_files: Some(existing_files),
|
only_files: Some(existing_files),
|
||||||
|
local_only: Some(false),
|
||||||
|
logged_in_only: Some(false),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,13 @@ use crate::{
|
||||||
|
|
||||||
impl Action for CreateComment {
|
impl Action for CreateComment {
|
||||||
fn perform(&self, ctx: &Context) -> Result<Option<Box<dyn Outbound + Send>>, Error> {
|
fn perform(&self, ctx: &Context) -> Result<Option<Box<dyn Outbound + Send>>, Error> {
|
||||||
let submissioner_id = ctx
|
let submission = ctx
|
||||||
.store
|
.store
|
||||||
.submissions
|
.submissions
|
||||||
.by_id(self.submission_id)?
|
.by_id(self.submission_id)?
|
||||||
.req("submission by id")?
|
.req("submission by id")?;
|
||||||
.profile_id();
|
|
||||||
|
let submissioner_id = submission.profile_id();
|
||||||
|
|
||||||
ctx.check_block(submissioner_id, self.profile_id)?;
|
ctx.check_block(submissioner_id, self.profile_id)?;
|
||||||
|
|
||||||
|
@ -69,24 +70,30 @@ impl Action for CreateComment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.is_local(self.profile_id)? && ctx.is_local(submissioner_id)? {
|
if !submission.is_local_only() {
|
||||||
return Ok(Some(Box::new(
|
if ctx.is_local(self.profile_id)? && ctx.is_local(submissioner_id)? {
|
||||||
crate::apub::results::AnnounceCommentCreated {
|
return Ok(Some(Box::new(
|
||||||
submissioner_id,
|
crate::apub::results::AnnounceCommentCreated {
|
||||||
|
submissioner_id,
|
||||||
|
comment_id: comment.id(),
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
} else if ctx.is_local(self.profile_id)? {
|
||||||
|
return Ok(Some(Box::new(crate::apub::results::CommentCreated {
|
||||||
comment_id: comment.id(),
|
comment_id: comment.id(),
|
||||||
},
|
})));
|
||||||
)));
|
} else if ctx.is_local(submissioner_id)? {
|
||||||
} else if ctx.is_local(self.profile_id)? {
|
if let Some(apub_id) = &self.note_apub_id {
|
||||||
return Ok(Some(Box::new(crate::apub::results::CommentCreated {
|
return Ok(Some(Box::new(crate::apub::results::RemoteCommentCreated {
|
||||||
|
note_apub_id: apub_id.clone(),
|
||||||
|
submissioner_id,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(Some(Box::new(crate::apub::results::LocalCommentCreated {
|
||||||
comment_id: comment.id(),
|
comment_id: comment.id(),
|
||||||
})));
|
})));
|
||||||
} else if ctx.is_local(submissioner_id)? {
|
|
||||||
if let Some(apub_id) = &self.note_apub_id {
|
|
||||||
return Ok(Some(Box::new(crate::apub::results::RemoteCommentCreated {
|
|
||||||
note_apub_id: apub_id.clone(),
|
|
||||||
submissioner_id,
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -118,31 +125,39 @@ impl Action for UpdateComment {
|
||||||
comment
|
comment
|
||||||
};
|
};
|
||||||
|
|
||||||
let submissioner_id = ctx
|
let submission = ctx
|
||||||
.store
|
.store
|
||||||
.submissions
|
.submissions
|
||||||
.by_id(comment.submission_id())?
|
.by_id(comment.submission_id())?
|
||||||
.req("submission by id")?
|
.req("submission by id")?;
|
||||||
.profile_id();
|
|
||||||
|
|
||||||
if ctx.is_local(comment.profile_id())? && ctx.is_local(submissioner_id)? {
|
let submissioner_id = submission.profile_id();
|
||||||
return Ok(Some(Box::new(
|
let is_local_only = submission.is_local_only();
|
||||||
crate::apub::results::AnnounceCommentUpdated {
|
|
||||||
submissioner_id,
|
if !is_local_only {
|
||||||
|
if ctx.is_local(comment.profile_id())? && ctx.is_local(submissioner_id)? {
|
||||||
|
return Ok(Some(Box::new(
|
||||||
|
crate::apub::results::AnnounceCommentUpdated {
|
||||||
|
submissioner_id,
|
||||||
|
comment_id: comment.id(),
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
} else if ctx.is_local(comment.profile_id())? {
|
||||||
|
return Ok(Some(Box::new(crate::apub::results::CommentUpdated {
|
||||||
comment_id: comment.id(),
|
comment_id: comment.id(),
|
||||||
},
|
})));
|
||||||
)));
|
} else if ctx.is_local(submissioner_id)? {
|
||||||
} else if ctx.is_local(comment.profile_id())? {
|
if let Some(apub_id) = &self.update_apub_id {
|
||||||
return Ok(Some(Box::new(crate::apub::results::CommentUpdated {
|
return Ok(Some(Box::new(crate::apub::results::RemoteCommentUpdated {
|
||||||
|
update_apub_id: apub_id.to_owned(),
|
||||||
|
submissioner_id,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(Some(Box::new(crate::apub::results::LocalCommentUpdated {
|
||||||
comment_id: comment.id(),
|
comment_id: comment.id(),
|
||||||
})));
|
})));
|
||||||
} else if ctx.is_local(submissioner_id)? {
|
|
||||||
if let Some(apub_id) = &self.update_apub_id {
|
|
||||||
return Ok(Some(Box::new(crate::apub::results::RemoteCommentUpdated {
|
|
||||||
update_apub_id: apub_id.to_owned(),
|
|
||||||
submissioner_id,
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
|
@ -269,6 +269,8 @@ pub struct CreateSubmission {
|
||||||
files: Vec<Uuid>,
|
files: Vec<Uuid>,
|
||||||
published: Option<DateTime<Utc>>,
|
published: Option<DateTime<Utc>>,
|
||||||
visibility: Visibility,
|
visibility: Visibility,
|
||||||
|
local_only: bool,
|
||||||
|
logged_in_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CreateSubmission {
|
impl CreateSubmission {
|
||||||
|
@ -281,6 +283,8 @@ impl CreateSubmission {
|
||||||
files: vec![file_id],
|
files: vec![file_id],
|
||||||
published: None,
|
published: None,
|
||||||
visibility: Visibility::Public,
|
visibility: Visibility::Public,
|
||||||
|
local_only: false,
|
||||||
|
logged_in_only: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,19 +296,17 @@ pub struct UpdateSubmission {
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
description_source: Option<String>,
|
description_source: Option<String>,
|
||||||
visibility: Option<Visibility>,
|
visibility: Option<Visibility>,
|
||||||
|
local_only: Option<bool>,
|
||||||
|
logged_in_only: Option<bool>,
|
||||||
published: Option<DateTime<Utc>>,
|
published: Option<DateTime<Utc>>,
|
||||||
|
updated: Option<DateTime<Utc>>,
|
||||||
removed_files: Option<Vec<Uuid>>,
|
removed_files: Option<Vec<Uuid>>,
|
||||||
new_files: Option<Vec<Uuid>>,
|
new_files: Option<Vec<Uuid>>,
|
||||||
only_files: Option<Vec<Uuid>>,
|
only_files: Option<Vec<Uuid>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpdateSubmission {
|
impl UpdateSubmission {
|
||||||
pub fn from_text(
|
pub fn from_text(submission_id: Uuid, title: String, description: Option<String>) -> Self {
|
||||||
submission_id: Uuid,
|
|
||||||
title: String,
|
|
||||||
description: Option<String>,
|
|
||||||
visibility: Option<Visibility>,
|
|
||||||
) -> Self {
|
|
||||||
let title_source = if title.trim().is_empty() {
|
let title_source = if title.trim().is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -325,8 +327,34 @@ impl UpdateSubmission {
|
||||||
title_source,
|
title_source,
|
||||||
description: description_source.as_deref().map(|s| bbcode(s, |v| v)),
|
description: description_source.as_deref().map(|s| bbcode(s, |v| v)),
|
||||||
description_source,
|
description_source,
|
||||||
visibility,
|
visibility: None,
|
||||||
|
local_only: None,
|
||||||
|
logged_in_only: None,
|
||||||
published: None,
|
published: None,
|
||||||
|
updated: Some(Utc::now()),
|
||||||
|
removed_files: None,
|
||||||
|
new_files: None,
|
||||||
|
only_files: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_visibility(
|
||||||
|
submission_id: Uuid,
|
||||||
|
visibility: Visibility,
|
||||||
|
local_only: bool,
|
||||||
|
logged_in_only: bool,
|
||||||
|
) -> Self {
|
||||||
|
UpdateSubmission {
|
||||||
|
submission_id,
|
||||||
|
title: None,
|
||||||
|
title_source: None,
|
||||||
|
description: None,
|
||||||
|
description_source: None,
|
||||||
|
visibility: Some(visibility),
|
||||||
|
local_only: Some(local_only),
|
||||||
|
logged_in_only: Some(logged_in_only),
|
||||||
|
published: None,
|
||||||
|
updated: Some(Utc::now()),
|
||||||
removed_files: None,
|
removed_files: None,
|
||||||
new_files: None,
|
new_files: None,
|
||||||
only_files: None,
|
only_files: None,
|
||||||
|
@ -341,7 +369,10 @@ impl UpdateSubmission {
|
||||||
description: None,
|
description: None,
|
||||||
description_source: None,
|
description_source: None,
|
||||||
visibility: None,
|
visibility: None,
|
||||||
|
local_only: None,
|
||||||
|
logged_in_only: None,
|
||||||
published: None,
|
published: None,
|
||||||
|
updated: Some(Utc::now()),
|
||||||
removed_files: None,
|
removed_files: None,
|
||||||
new_files: Some(vec![file_id]),
|
new_files: Some(vec![file_id]),
|
||||||
only_files: None,
|
only_files: None,
|
||||||
|
@ -357,6 +388,9 @@ impl UpdateSubmission {
|
||||||
description_source: None,
|
description_source: None,
|
||||||
visibility: None,
|
visibility: None,
|
||||||
published: None,
|
published: None,
|
||||||
|
updated: Some(Utc::now()),
|
||||||
|
local_only: None,
|
||||||
|
logged_in_only: None,
|
||||||
removed_files: Some(vec![file_id]),
|
removed_files: Some(vec![file_id]),
|
||||||
new_files: None,
|
new_files: None,
|
||||||
only_files: None,
|
only_files: None,
|
||||||
|
@ -371,7 +405,10 @@ impl UpdateSubmission {
|
||||||
description: None,
|
description: None,
|
||||||
description_source: None,
|
description_source: None,
|
||||||
visibility: None,
|
visibility: None,
|
||||||
|
local_only: None,
|
||||||
|
logged_in_only: None,
|
||||||
published: Some(Utc::now()),
|
published: Some(Utc::now()),
|
||||||
|
updated: None,
|
||||||
removed_files: None,
|
removed_files: None,
|
||||||
new_files: None,
|
new_files: None,
|
||||||
only_files: None,
|
only_files: None,
|
||||||
|
|
|
@ -16,12 +16,17 @@ impl Action for CreateSubmission {
|
||||||
let submission_id = submission.id();
|
let submission_id = submission.id();
|
||||||
|
|
||||||
let mut changes = submission.update();
|
let mut changes = submission.update();
|
||||||
|
changes
|
||||||
|
.local_only(self.local_only)
|
||||||
|
.logged_in_only(self.logged_in_only);
|
||||||
|
|
||||||
if self.published.is_some() {
|
if self.published.is_some() {
|
||||||
changes.publish(self.published);
|
changes.published(self.published);
|
||||||
}
|
}
|
||||||
if let Some(description) = &self.description {
|
if let Some(description) = &self.description {
|
||||||
changes.description(&hyaenidae_content::html(description));
|
changes.description(&hyaenidae_content::html(description));
|
||||||
}
|
}
|
||||||
|
|
||||||
let submission = if changes.any_changes() {
|
let submission = if changes.any_changes() {
|
||||||
ctx.store.submissions.update(&changes)?
|
ctx.store.submissions.update(&changes)?
|
||||||
} else {
|
} else {
|
||||||
|
@ -61,9 +66,15 @@ impl Action for CreateSubmission {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.is_local(submission.profile_id())? && submission.published().is_some() {
|
if ctx.is_local(submission.profile_id())? && submission.published().is_some() {
|
||||||
return Ok(Some(Box::new(crate::apub::results::SubmissionCreated {
|
if submission.is_local_only() {
|
||||||
submission_id,
|
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() {
|
if submission.published().is_none() {
|
||||||
|
@ -90,7 +101,10 @@ impl Action for UpdateSubmission {
|
||||||
|
|
||||||
let mut changes = submission.update();
|
let mut changes = submission.update();
|
||||||
if let Some(published) = self.published {
|
if let Some(published) = self.published {
|
||||||
changes.publish(Some(published));
|
changes.published(Some(published));
|
||||||
|
}
|
||||||
|
if let Some(updated) = self.updated {
|
||||||
|
changes.updated(updated);
|
||||||
}
|
}
|
||||||
if let Some(title) = &self.title {
|
if let Some(title) = &self.title {
|
||||||
changes.title(&hyaenidae_content::html(title));
|
changes.title(&hyaenidae_content::html(title));
|
||||||
|
@ -107,6 +121,12 @@ impl Action for UpdateSubmission {
|
||||||
if let Some(visibility) = self.visibility {
|
if let Some(visibility) = self.visibility {
|
||||||
changes.visibility(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);
|
||||||
|
}
|
||||||
let submission = if changes.any_changes() {
|
let submission = if changes.any_changes() {
|
||||||
ctx.store.submissions.update(&changes)?
|
ctx.store.submissions.update(&changes)?
|
||||||
} else {
|
} else {
|
||||||
|
@ -178,7 +198,7 @@ impl Action for UpdateSubmission {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx.is_local(profile_id)? {
|
if !submission.is_local_only() && ctx.is_local(profile_id)? {
|
||||||
if newly_published {
|
if newly_published {
|
||||||
return Ok(Some(Box::new(crate::apub::results::SubmissionCreated {
|
return Ok(Some(Box::new(crate::apub::results::SubmissionCreated {
|
||||||
submission_id,
|
submission_id,
|
||||||
|
@ -188,6 +208,16 @@ impl Action for UpdateSubmission {
|
||||||
submission_id,
|
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)
|
Ok(None)
|
||||||
|
@ -238,7 +268,10 @@ impl Action for DeleteSubmission {
|
||||||
});
|
});
|
||||||
|
|
||||||
let profile_id = undo_submission.0.profile_id();
|
let profile_id = undo_submission.0.profile_id();
|
||||||
if ctx.is_local(profile_id)? && undo_submission.0.published().is_some() {
|
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 {
|
return Ok(Some(Box::new(crate::apub::results::SubmissionDeleted {
|
||||||
profile_id,
|
profile_id,
|
||||||
submission_id,
|
submission_id,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use super::{
|
use super::{
|
||||||
AnnounceCommentCreated, AnnounceCommentDeleted, AnnounceCommentUpdated, CommentCreated,
|
AnnounceCommentCreated, AnnounceCommentDeleted, AnnounceCommentUpdated, CommentCreated,
|
||||||
CommentDeleted, CommentUpdated, RemoteCommentCreated, RemoteCommentDeleted,
|
CommentDeleted, CommentUpdated, LocalCommentCreated, LocalCommentUpdated, RemoteCommentCreated,
|
||||||
RemoteCommentUpdated,
|
RemoteCommentDeleted, RemoteCommentUpdated,
|
||||||
};
|
};
|
||||||
use crate::{store::Comment, Context, Error, OnBehalfOf, Outbound, Required};
|
use crate::{store::Comment, Context, Error, OnBehalfOf, Outbound, Required};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
|
@ -173,6 +173,28 @@ impl Outbound for CommentCreated {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Outbound for LocalCommentCreated {
|
||||||
|
fn id(&self) -> Option<Uuid> {
|
||||||
|
Some(self.comment_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ready(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn behalf(&self, _: &Context) -> Result<OnBehalfOf, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inboxes(&self, _: &Context) -> Result<Vec<Url>, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_apub(&self, _: &Context) -> Result<AnyBase, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Outbound for AnnounceCommentCreated {
|
impl Outbound for AnnounceCommentCreated {
|
||||||
fn id(&self) -> Option<Uuid> {
|
fn id(&self) -> Option<Uuid> {
|
||||||
Some(self.comment_id)
|
Some(self.comment_id)
|
||||||
|
@ -300,6 +322,28 @@ impl Outbound for CommentUpdated {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Outbound for LocalCommentUpdated {
|
||||||
|
fn id(&self) -> Option<Uuid> {
|
||||||
|
Some(self.comment_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ready(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn behalf(&self, _: &Context) -> Result<OnBehalfOf, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inboxes(&self, _: &Context) -> Result<Vec<Url>, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_apub(&self, _: &Context) -> Result<AnyBase, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Outbound for AnnounceCommentUpdated {
|
impl Outbound for AnnounceCommentUpdated {
|
||||||
fn id(&self) -> Option<Uuid> {
|
fn id(&self) -> Option<Uuid> {
|
||||||
Some(self.comment_id)
|
Some(self.comment_id)
|
||||||
|
|
|
@ -39,6 +39,10 @@ pub(super) struct SubmissionCreated {
|
||||||
pub(super) submission_id: Uuid,
|
pub(super) submission_id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) struct LocalSubmissionCreated {
|
||||||
|
pub(super) submission_id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) struct UnpublishedSubmissionCreated {
|
pub(super) struct UnpublishedSubmissionCreated {
|
||||||
pub(super) submission_id: Uuid,
|
pub(super) submission_id: Uuid,
|
||||||
}
|
}
|
||||||
|
@ -47,6 +51,10 @@ pub(super) struct SubmissionUpdated {
|
||||||
pub(super) submission_id: Uuid,
|
pub(super) submission_id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) struct LocalSubmissionUpdated {
|
||||||
|
pub(super) submission_id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) struct SubmissionDeleted {
|
pub(super) struct SubmissionDeleted {
|
||||||
pub(super) profile_id: Uuid,
|
pub(super) profile_id: Uuid,
|
||||||
pub(super) submission_id: Uuid,
|
pub(super) submission_id: Uuid,
|
||||||
|
@ -56,6 +64,10 @@ pub(super) struct CommentCreated {
|
||||||
pub(super) comment_id: Uuid,
|
pub(super) comment_id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) struct LocalCommentCreated {
|
||||||
|
pub(super) comment_id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) struct AnnounceCommentCreated {
|
pub(super) struct AnnounceCommentCreated {
|
||||||
pub(super) submissioner_id: Uuid,
|
pub(super) submissioner_id: Uuid,
|
||||||
pub(super) comment_id: Uuid,
|
pub(super) comment_id: Uuid,
|
||||||
|
@ -70,6 +82,10 @@ pub(super) struct CommentUpdated {
|
||||||
pub(super) comment_id: Uuid,
|
pub(super) comment_id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) struct LocalCommentUpdated {
|
||||||
|
pub(super) comment_id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) struct AnnounceCommentUpdated {
|
pub(super) struct AnnounceCommentUpdated {
|
||||||
pub(super) submissioner_id: Uuid,
|
pub(super) submissioner_id: Uuid,
|
||||||
pub(super) comment_id: Uuid,
|
pub(super) comment_id: Uuid,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::{
|
use super::{
|
||||||
SubmissionCreated, SubmissionDeleted, SubmissionUpdated, UnpublishedSubmissionCreated,
|
LocalSubmissionCreated, LocalSubmissionUpdated, SubmissionCreated, SubmissionDeleted,
|
||||||
|
SubmissionUpdated, UnpublishedSubmissionCreated,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
store::{FileSource, Submission, Visibility},
|
store::{FileSource, Submission, Visibility},
|
||||||
|
@ -82,9 +83,15 @@ fn build_submission(
|
||||||
.set_summary(submission.title())
|
.set_summary(submission.title())
|
||||||
.set_published(published.into())
|
.set_published(published.into())
|
||||||
.set_attributed_to(person_id);
|
.set_attributed_to(person_id);
|
||||||
|
if let Some(updated) = submission.updated() {
|
||||||
|
note.set_updated(updated.into());
|
||||||
|
}
|
||||||
if let Some(description) = submission.description() {
|
if let Some(description) = submission.description() {
|
||||||
note.set_content(description);
|
note.set_content(description);
|
||||||
}
|
}
|
||||||
|
if submission.is_logged_in_only() {
|
||||||
|
log::warn!("logged_in_only is not implemented yet");
|
||||||
|
}
|
||||||
match submission.visibility() {
|
match submission.visibility() {
|
||||||
Visibility::Public => {
|
Visibility::Public => {
|
||||||
note.add_to(public());
|
note.add_to(public());
|
||||||
|
@ -136,6 +143,28 @@ impl Outbound for UnpublishedSubmissionCreated {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Outbound for LocalSubmissionCreated {
|
||||||
|
fn id(&self) -> Option<Uuid> {
|
||||||
|
Some(self.submission_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ready(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn behalf(&self, _: &Context) -> Result<OnBehalfOf, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inboxes(&self, _: &Context) -> Result<Vec<Url>, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_apub(&self, _: &Context) -> Result<AnyBase, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Outbound for SubmissionCreated {
|
impl Outbound for SubmissionCreated {
|
||||||
fn id(&self) -> Option<Uuid> {
|
fn id(&self) -> Option<Uuid> {
|
||||||
Some(self.submission_id)
|
Some(self.submission_id)
|
||||||
|
@ -244,6 +273,28 @@ impl Outbound for SubmissionUpdated {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Outbound for LocalSubmissionUpdated {
|
||||||
|
fn id(&self) -> Option<Uuid> {
|
||||||
|
Some(self.submission_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ready(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn behalf(&self, _: &Context) -> Result<OnBehalfOf, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inboxes(&self, _: &Context) -> Result<Vec<Url>, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_apub(&self, _: &Context) -> Result<AnyBase, Error> {
|
||||||
|
Err(Error::Invalid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Outbound for SubmissionDeleted {
|
impl Outbound for SubmissionDeleted {
|
||||||
fn id(&self) -> Option<Uuid> {
|
fn id(&self) -> Option<Uuid> {
|
||||||
None
|
None
|
||||||
|
|
|
@ -19,7 +19,9 @@ pub use profile::Store as ProfileStore;
|
||||||
pub use react::Store as ReactStore;
|
pub use react::Store as ReactStore;
|
||||||
pub use report::Store as ReportStore;
|
pub use report::Store as ReportStore;
|
||||||
pub use server::{Server, ServerChanges, Store as ServerStore};
|
pub use server::{Server, ServerChanges, Store as ServerStore};
|
||||||
pub use submission::Store as SubmissionStore;
|
pub use submission::{
|
||||||
|
Store as SubmissionStore, Submission, SubmissionChanges, SubmissionFileChanges, Visibility,
|
||||||
|
};
|
||||||
pub use term_search::TermSearch;
|
pub use term_search::TermSearch;
|
||||||
pub use view::Store as ViewStore;
|
pub use view::Store as ViewStore;
|
||||||
|
|
||||||
|
@ -398,96 +400,6 @@ impl File {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize)]
|
|
||||||
pub enum Visibility {
|
|
||||||
Public,
|
|
||||||
Unlisted,
|
|
||||||
Followers,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Submission {
|
|
||||||
id: Uuid,
|
|
||||||
profile_id: Uuid,
|
|
||||||
title: String,
|
|
||||||
title_source: Option<String>,
|
|
||||||
description: Option<String>,
|
|
||||||
description_source: Option<String>,
|
|
||||||
files: Vec<Uuid>,
|
|
||||||
published: Option<DateTime<Utc>>,
|
|
||||||
visibility: Visibility,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Submission {
|
|
||||||
pub fn update(&self) -> SubmissionChanges {
|
|
||||||
SubmissionChanges {
|
|
||||||
id: self.id,
|
|
||||||
title: None,
|
|
||||||
title_source: None,
|
|
||||||
description: None,
|
|
||||||
description_source: None,
|
|
||||||
visibility: None,
|
|
||||||
published: self.published,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_files(&self) -> SubmissionFileChanges {
|
|
||||||
SubmissionFileChanges {
|
|
||||||
id: self.id,
|
|
||||||
original_files: self.files.clone(),
|
|
||||||
files: self.files.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id(&self) -> Uuid {
|
|
||||||
self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn profile_id(&self) -> Uuid {
|
|
||||||
self.profile_id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn title(&self) -> &str {
|
|
||||||
&self.title
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn title_source(&self) -> Option<&str> {
|
|
||||||
self.title_source.as_deref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn description(&self) -> Option<&str> {
|
|
||||||
self.description.as_ref().map(|d| d.as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn description_source(&self) -> Option<&str> {
|
|
||||||
self.description_source.as_deref()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn files(&self) -> &[Uuid] {
|
|
||||||
&self.files
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn published(&self) -> Option<DateTime<Utc>> {
|
|
||||||
self.published
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn visibility(&self) -> Visibility {
|
|
||||||
self.visibility
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_public(&self) -> bool {
|
|
||||||
matches!(self.visibility, Visibility::Public)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_unlisted(&self) -> bool {
|
|
||||||
matches!(self.visibility, Visibility::Unlisted)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_followers_only(&self) -> bool {
|
|
||||||
matches!(self.visibility, Visibility::Followers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Comment {
|
pub enum Comment {
|
||||||
Submission(SubmissionComment),
|
Submission(SubmissionComment),
|
||||||
|
@ -742,81 +654,6 @@ impl ProfileImageChanges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SubmissionChanges {
|
|
||||||
id: Uuid,
|
|
||||||
title: Option<String>,
|
|
||||||
title_source: Option<String>,
|
|
||||||
description: Option<String>,
|
|
||||||
description_source: Option<String>,
|
|
||||||
visibility: Option<Visibility>,
|
|
||||||
published: Option<DateTime<Utc>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubmissionChanges {
|
|
||||||
pub(crate) fn title(&mut self, title: &str) -> &mut Self {
|
|
||||||
self.title = Some(title.to_owned());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn title_source(&mut self, title_source: &str) -> &mut Self {
|
|
||||||
self.title_source = Some(title_source.to_owned());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn description(&mut self, description: &str) -> &mut Self {
|
|
||||||
self.description = Some(description.to_owned());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn description_source(&mut self, description_source: &str) -> &mut Self {
|
|
||||||
self.description_source = Some(description_source.to_owned());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn visibility(&mut self, visibility: Visibility) -> &mut Self {
|
|
||||||
self.visibility = Some(visibility);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn publish(&mut self, time: Option<DateTime<Utc>>) -> &mut Self {
|
|
||||||
if self.published.is_none() {
|
|
||||||
self.published = time.or_else(|| Some(Utc::now()));
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn any_changes(&self) -> bool {
|
|
||||||
self.title.is_some()
|
|
||||||
|| self.description.is_some()
|
|
||||||
|| self.published.is_some()
|
|
||||||
|| self.visibility.is_some()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SubmissionFileChanges {
|
|
||||||
id: Uuid,
|
|
||||||
original_files: Vec<Uuid>,
|
|
||||||
files: Vec<Uuid>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SubmissionFileChanges {
|
|
||||||
pub fn add_file(&mut self, file: &File) -> &mut Self {
|
|
||||||
self.files.push(file.id);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn delete_file(&mut self, file: &File) -> &mut Self {
|
|
||||||
self.files.retain(|id| *id != file.id);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn any_changes(&self) -> bool {
|
|
||||||
self.original_files != self.files
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CommentChanges {
|
pub struct CommentChanges {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
|
@ -860,6 +697,9 @@ pub enum StoreError {
|
||||||
#[error("Provided value is too long")]
|
#[error("Provided value is too long")]
|
||||||
TooLong,
|
TooLong,
|
||||||
|
|
||||||
|
#[error("The updated data is older than our stored data")]
|
||||||
|
Outdated,
|
||||||
|
|
||||||
#[error("Provided value must not be empty")]
|
#[error("Provided value must not be empty")]
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
@ -911,16 +751,6 @@ fn count(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Visibility {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Visibility::Public => write!(f, "Public"),
|
|
||||||
Visibility::Unlisted => write!(f, "Unlisted"),
|
|
||||||
Visibility::Followers => write!(f, "Followers"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for OwnerSource {
|
impl fmt::Display for OwnerSource {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -1,9 +1,53 @@
|
||||||
use super::{StoreError, Submission, SubmissionChanges, SubmissionFileChanges, Undo, Visibility};
|
use super::{File, StoreError, Undo};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sled::{Db, Transactional, Tree};
|
use sled::{Db, Transactional, Tree};
|
||||||
use std::io::Cursor;
|
use std::{fmt, io::Cursor};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub enum Visibility {
|
||||||
|
Public,
|
||||||
|
Unlisted,
|
||||||
|
Followers,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Submission {
|
||||||
|
id: Uuid,
|
||||||
|
profile_id: Uuid,
|
||||||
|
title: String,
|
||||||
|
title_source: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
description_source: Option<String>,
|
||||||
|
files: Vec<Uuid>,
|
||||||
|
published: Option<DateTime<Utc>>,
|
||||||
|
updated: Option<DateTime<Utc>>,
|
||||||
|
visibility: Visibility,
|
||||||
|
local_only: bool,
|
||||||
|
logged_in_only: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SubmissionChanges {
|
||||||
|
id: Uuid,
|
||||||
|
title: Option<String>,
|
||||||
|
title_source: Option<String>,
|
||||||
|
description: Option<String>,
|
||||||
|
description_source: Option<String>,
|
||||||
|
visibility: Option<Visibility>,
|
||||||
|
published: Option<DateTime<Utc>>,
|
||||||
|
updated: Option<DateTime<Utc>>,
|
||||||
|
local_only: Option<bool>,
|
||||||
|
logged_in_only: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SubmissionFileChanges {
|
||||||
|
id: Uuid,
|
||||||
|
original_files: Vec<Uuid>,
|
||||||
|
files: Vec<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Store {
|
pub struct Store {
|
||||||
submission_tree: Tree,
|
submission_tree: Tree,
|
||||||
|
@ -19,14 +63,191 @@ struct StoredSubmission {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
profile_id: Uuid,
|
profile_id: Uuid,
|
||||||
title: String,
|
title: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
title_source: Option<String>,
|
title_source: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
description_source: Option<String>,
|
description_source: Option<String>,
|
||||||
files: Vec<Uuid>,
|
files: Vec<Uuid>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
published: Option<DateTime<Utc>>,
|
published: Option<DateTime<Utc>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
updated: Option<DateTime<Utc>>,
|
||||||
visibility: Visibility,
|
visibility: Visibility,
|
||||||
|
#[serde(default)]
|
||||||
|
local_only: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
logged_in_only: bool,
|
||||||
drafted_at: DateTime<Utc>,
|
drafted_at: DateTime<Utc>,
|
||||||
updated_at: DateTime<Utc>,
|
}
|
||||||
|
|
||||||
|
impl Submission {
|
||||||
|
pub fn update(&self) -> SubmissionChanges {
|
||||||
|
SubmissionChanges {
|
||||||
|
id: self.id,
|
||||||
|
title: None,
|
||||||
|
title_source: None,
|
||||||
|
description: None,
|
||||||
|
description_source: None,
|
||||||
|
visibility: None,
|
||||||
|
published: self.published,
|
||||||
|
updated: None,
|
||||||
|
local_only: None,
|
||||||
|
logged_in_only: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_files(&self) -> SubmissionFileChanges {
|
||||||
|
SubmissionFileChanges {
|
||||||
|
id: self.id,
|
||||||
|
original_files: self.files.clone(),
|
||||||
|
files: self.files.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> Uuid {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn profile_id(&self) -> Uuid {
|
||||||
|
self.profile_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn title(&self) -> &str {
|
||||||
|
&self.title
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn title_source(&self) -> Option<&str> {
|
||||||
|
self.title_source.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(&self) -> Option<&str> {
|
||||||
|
self.description.as_ref().map(|d| d.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description_source(&self) -> Option<&str> {
|
||||||
|
self.description_source.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn files(&self) -> &[Uuid] {
|
||||||
|
&self.files
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn published(&self) -> Option<DateTime<Utc>> {
|
||||||
|
self.published
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn updated(&self) -> Option<DateTime<Utc>> {
|
||||||
|
self.updated
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn visibility(&self) -> Visibility {
|
||||||
|
self.visibility
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_public(&self) -> bool {
|
||||||
|
matches!(self.visibility, Visibility::Public)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_unlisted(&self) -> bool {
|
||||||
|
matches!(self.visibility, Visibility::Unlisted)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_followers_only(&self) -> bool {
|
||||||
|
matches!(self.visibility, Visibility::Followers)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_local_only(&self) -> bool {
|
||||||
|
self.local_only
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_logged_in_only(&self) -> bool {
|
||||||
|
self.logged_in_only
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubmissionChanges {
|
||||||
|
pub(crate) fn title(&mut self, title: &str) -> &mut Self {
|
||||||
|
self.title = Some(title.to_owned());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn title_source(&mut self, title_source: &str) -> &mut Self {
|
||||||
|
self.title_source = Some(title_source.to_owned());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn description(&mut self, description: &str) -> &mut Self {
|
||||||
|
self.description = Some(description.to_owned());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn description_source(&mut self, description_source: &str) -> &mut Self {
|
||||||
|
self.description_source = Some(description_source.to_owned());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn visibility(&mut self, visibility: Visibility) -> &mut Self {
|
||||||
|
if self.visibility.is_none() {
|
||||||
|
self.visibility = Some(visibility);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn published(&mut self, time: Option<DateTime<Utc>>) -> &mut Self {
|
||||||
|
if self.published.is_none() {
|
||||||
|
self.published = time.or_else(|| Some(Utc::now()));
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn updated(&mut self, time: DateTime<Utc>) -> &mut Self {
|
||||||
|
if self.published.is_some() {
|
||||||
|
self.updated = Some(time);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn local_only(&mut self, local_only: bool) -> &mut Self {
|
||||||
|
if self.published.is_none() {
|
||||||
|
self.local_only = Some(local_only);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn logged_in_only(&mut self, logged_in_only: bool) -> &mut Self {
|
||||||
|
if self.published.is_none() {
|
||||||
|
self.logged_in_only = Some(logged_in_only);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn any_changes(&self) -> bool {
|
||||||
|
self.title.is_some()
|
||||||
|
|| self.description.is_some()
|
||||||
|
|| self.published.is_some()
|
||||||
|
|| self.updated.is_some()
|
||||||
|
|| self.visibility.is_some()
|
||||||
|
|| self.local_only.is_some()
|
||||||
|
|| self.logged_in_only.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SubmissionFileChanges {
|
||||||
|
pub(crate) fn add_file(&mut self, file: &File) -> &mut Self {
|
||||||
|
self.files.push(file.id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn delete_file(&mut self, file: &File) -> &mut Self {
|
||||||
|
self.files.retain(|id| *id != file.id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn any_changes(&self) -> bool {
|
||||||
|
self.original_files != self.files
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Store {
|
impl Store {
|
||||||
|
@ -66,9 +287,11 @@ impl Store {
|
||||||
description_source: None,
|
description_source: None,
|
||||||
files: vec![],
|
files: vec![],
|
||||||
published: None,
|
published: None,
|
||||||
|
updated: None,
|
||||||
visibility,
|
visibility,
|
||||||
|
local_only: false,
|
||||||
|
logged_in_only: false,
|
||||||
drafted_at: now,
|
drafted_at: now,
|
||||||
updated_at: now,
|
|
||||||
};
|
};
|
||||||
serde_json::to_writer(writer, &stored_submission)?;
|
serde_json::to_writer(writer, &stored_submission)?;
|
||||||
self.submission_tree
|
self.submission_tree
|
||||||
|
@ -139,6 +362,17 @@ impl Store {
|
||||||
let mut stored_submission: StoredSubmission =
|
let mut stored_submission: StoredSubmission =
|
||||||
serde_json::from_slice(&stored_submission_ivec)?;
|
serde_json::from_slice(&stored_submission_ivec)?;
|
||||||
|
|
||||||
|
if let Some(updated) = changes.updated {
|
||||||
|
if let Some(previously_updated) = stored_submission
|
||||||
|
.updated
|
||||||
|
.or_else(|| stored_submission.published)
|
||||||
|
{
|
||||||
|
if updated < previously_updated {
|
||||||
|
return Err(StoreError::Outdated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let already_published = stored_submission.published.is_some();
|
let already_published = stored_submission.published.is_some();
|
||||||
|
|
||||||
if let Some(title) = &changes.title {
|
if let Some(title) = &changes.title {
|
||||||
|
@ -156,8 +390,18 @@ impl Store {
|
||||||
if let Some(visibility) = changes.visibility {
|
if let Some(visibility) = changes.visibility {
|
||||||
stored_submission.visibility = visibility;
|
stored_submission.visibility = visibility;
|
||||||
}
|
}
|
||||||
|
if let Some(local_only) = changes.local_only {
|
||||||
|
stored_submission.local_only = local_only;
|
||||||
|
}
|
||||||
|
if let Some(logged_in_only) = changes.logged_in_only {
|
||||||
|
stored_submission.logged_in_only = logged_in_only;
|
||||||
|
}
|
||||||
|
if stored_submission.published.is_some() {
|
||||||
|
if let Some(updated) = changes.updated {
|
||||||
|
stored_submission.updated = Some(updated);
|
||||||
|
}
|
||||||
|
}
|
||||||
stored_submission.published = changes.published;
|
stored_submission.published = changes.published;
|
||||||
stored_submission.updated_at = Utc::now();
|
|
||||||
|
|
||||||
let stored_submission_vec = serde_json::to_vec(&stored_submission)?;
|
let stored_submission_vec = serde_json::to_vec(&stored_submission)?;
|
||||||
|
|
||||||
|
@ -211,7 +455,6 @@ impl Store {
|
||||||
serde_json::from_slice(&stored_submission_ivec)?;
|
serde_json::from_slice(&stored_submission_ivec)?;
|
||||||
|
|
||||||
stored_submission.files = changes.files.clone();
|
stored_submission.files = changes.files.clone();
|
||||||
stored_submission.updated_at = Utc::now();
|
|
||||||
|
|
||||||
let stored_submission_vec = serde_json::to_vec(&stored_submission)?;
|
let stored_submission_vec = serde_json::to_vec(&stored_submission)?;
|
||||||
|
|
||||||
|
@ -589,7 +832,20 @@ impl From<StoredSubmission> for Submission {
|
||||||
description_source: ss.description_source,
|
description_source: ss.description_source,
|
||||||
files: ss.files,
|
files: ss.files,
|
||||||
published: ss.published,
|
published: ss.published,
|
||||||
|
updated: ss.updated,
|
||||||
visibility: ss.visibility,
|
visibility: ss.visibility,
|
||||||
|
local_only: ss.local_only,
|
||||||
|
logged_in_only: ss.logged_in_only,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Visibility {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Visibility::Public => write!(f, "Public"),
|
||||||
|
Visibility::Unlisted => write!(f, "Unlisted"),
|
||||||
|
Visibility::Followers => write!(f, "Followers"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue