Migrate towards using page.attachment field for url (ref #2144)

This commit is contained in:
Felix Ableitner 2022-03-31 20:01:29 +02:00 committed by Dessalines
parent 56b275acd4
commit 784f079f73
23 changed files with 116 additions and 55 deletions

4
Cargo.lock generated
View file

@ -4,9 +4,9 @@ version = 3
[[package]] [[package]]
name = "activitystreams-kinds" name = "activitystreams-kinds"
version = "0.1.2" version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0784e99afd032199d3ed70cefb8eb3a8d1aef15f7f2c4e68d033c4e12bb6079e" checksum = "6d014a4fb8828870b7b46bee6257b9a89d06188ae8d435381ba94f14c8c697d8"
dependencies = [ dependencies = [
"serde", "serde",
"url", "url",

View file

@ -21,7 +21,7 @@ lemmy_db_views_actor = { version = "=0.16.2", path = "../db_views_actor" }
lemmy_api_common = { version = "=0.16.2", path = "../api_common" } lemmy_api_common = { version = "=0.16.2", path = "../api_common" }
lemmy_websocket = { version = "=0.16.2", path = "../websocket" } lemmy_websocket = { version = "=0.16.2", path = "../websocket" }
diesel = "1.4.8" diesel = "1.4.8"
activitystreams-kinds = "0.1.2" activitystreams-kinds = "0.2.1"
bcrypt = "0.10.1" bcrypt = "0.10.1"
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }
serde_json = { version = "1.0.72", features = ["preserve_order"] } serde_json = { version = "1.0.72", features = ["preserve_order"] }

View file

@ -19,6 +19,12 @@
"mediaType": "text/markdown" "mediaType": "text/markdown"
}, },
"url": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg", "url": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg",
"attachment": [
{
"type": "Link",
"href": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg"
}
],
"commentsEnabled": true, "commentsEnabled": true,
"sensitive": false, "sensitive": false,
"stickied": false, "stickied": false,

View file

@ -19,6 +19,12 @@
"mediaType": "text/markdown" "mediaType": "text/markdown"
}, },
"url": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg", "url": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg",
"attachment": [
{
"type": "Link",
"href": "https://lemmy.ml/pictrs/image/xl8W7FZfk9.jpg"
}
],
"commentsEnabled": true, "commentsEnabled": true,
"sensitive": false, "sensitive": false,
"stickied": false, "stickied": false,

View file

@ -14,6 +14,12 @@
"mediaType": "text/markdown" "mediaType": "text/markdown"
}, },
"url": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png", "url": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png",
"attachment": [
{
"type": "Link",
"href": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png"
}
],
"image": { "image": {
"type": "Image", "type": "Image",
"url": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png" "url": "https://enterprise.lemmy.ml/pictrs/image/eOtYb9iEiB.png"

View file

@ -63,7 +63,7 @@ impl CreateOrUpdatePost {
let create_or_update = CreateOrUpdatePost::new(post, actor, &community, kind, context).await?; let create_or_update = CreateOrUpdatePost::new(post, actor, &community, kind, context).await?;
let id = create_or_update.id.clone(); let id = create_or_update.id.clone();
let activity = AnnouncableActivities::CreateOrUpdatePost(create_or_update); let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
send_activity_in_community(activity, &id, actor, &community, vec![], context).await send_activity_in_community(activity, &id, actor, &community, vec![], context).await
} }
} }

View file

@ -70,7 +70,7 @@ pub enum PersonInboxActivities {
#[activity_handler(LemmyContext)] #[activity_handler(LemmyContext)]
pub enum AnnouncableActivities { pub enum AnnouncableActivities {
CreateOrUpdateComment(CreateOrUpdateComment), CreateOrUpdateComment(CreateOrUpdateComment),
CreateOrUpdatePost(CreateOrUpdatePost), CreateOrUpdatePost(Box<CreateOrUpdatePost>),
Vote(Vote), Vote(Vote),
UndoVote(UndoVote), UndoVote(UndoVote),
Delete(Delete), Delete(Delete),

View file

@ -18,7 +18,7 @@ pub enum PostOrComment {
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum PageOrNote { pub enum PageOrNote {
Page(Page), Page(Box<Page>),
Note(Note), Note(Note),
} }
@ -85,7 +85,7 @@ impl ApubObject for PostOrComment {
) -> Result<Self, LemmyError> { ) -> Result<Self, LemmyError> {
Ok(match apub { Ok(match apub {
PageOrNote::Page(p) => PostOrComment::Post(Box::new( PageOrNote::Page(p) => PostOrComment::Post(Box::new(
ApubPost::from_apub(p, context, request_counter).await?, ApubPost::from_apub(*p, context, request_counter).await?,
)), )),
PageOrNote::Note(n) => PostOrComment::Comment(Box::new( PageOrNote::Note(n) => PostOrComment::Comment(Box::new(
ApubComment::from_apub(n, context, request_counter).await?, ApubComment::from_apub(n, context, request_counter).await?,

View file

@ -130,6 +130,18 @@ where
}) })
} }
pub(crate) fn deserialize_skip_error<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: Deserialize<'de> + Default,
D: Deserializer<'de>,
{
let result = Deserialize::deserialize(deserializer);
Ok(match result {
Ok(o) => o,
Err(_) => Default::default(),
})
}
pub enum EndpointType { pub enum EndpointType {
Community, Community,
Person, Person,

View file

@ -5,7 +5,7 @@ use crate::{
objects::read_from_string_or_source, objects::read_from_string_or_source,
protocol::{ protocol::{
objects::{note::Note, tombstone::Tombstone}, objects::{note::Note, tombstone::Tombstone},
SourceCompat, Source,
}, },
PostOrComment, PostOrComment,
}; };
@ -118,7 +118,7 @@ impl ApubObject for ApubComment {
cc: maa.ccs, cc: maa.ccs,
content: markdown_to_html(&self.content), content: markdown_to_html(&self.content),
media_type: Some(MediaTypeHtml::Html), media_type: Some(MediaTypeHtml::Html),
source: Some(SourceCompat::new(self.content.clone())), source: Some(Source::new(self.content.clone())),
in_reply_to, in_reply_to,
published: Some(convert_datetime(self.published)), published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime), updated: self.updated.map(convert_datetime),

View file

@ -7,7 +7,7 @@ use crate::{
protocol::{ protocol::{
objects::{group::Group, tombstone::Tombstone, Endpoints}, objects::{group::Group, tombstone::Tombstone, Endpoints},
ImageObject, ImageObject,
SourceCompat, Source,
}, },
}; };
use activitystreams_kinds::actor::GroupType; use activitystreams_kinds::actor::GroupType;
@ -87,7 +87,7 @@ impl ApubObject for ApubCommunity {
preferred_username: self.name.clone(), preferred_username: self.name.clone(),
name: Some(self.title.clone()), name: Some(self.title.clone()),
summary: self.description.as_ref().map(|b| markdown_to_html(b)), summary: self.description.as_ref().map(|b| markdown_to_html(b)),
source: self.description.clone().map(SourceCompat::new), source: self.description.clone().map(Source::new),
icon: self.icon.clone().map(ImageObject::new), icon: self.icon.clone().map(ImageObject::new),
image: self.banner.clone().map(ImageObject::new), image: self.banner.clone().map(ImageObject::new),
sensitive: Some(self.nsfw), sensitive: Some(self.nsfw),

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
check_is_apub_id_valid, check_is_apub_id_valid,
objects::{read_from_string_or_source_opt, verify_image_domain_matches}, objects::{read_from_string_or_source_opt, verify_image_domain_matches},
protocol::{objects::instance::Instance, ImageObject, SourceCompat}, protocol::{objects::instance::Instance, ImageObject, Source},
}; };
use activitystreams_kinds::actor::ServiceType; use activitystreams_kinds::actor::ServiceType;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
@ -77,7 +77,7 @@ impl ApubObject for ApubSite {
id: ObjectId::new(self.actor_id()), id: ObjectId::new(self.actor_id()),
name: self.name.clone(), name: self.name.clone(),
content: self.sidebar.as_ref().map(|d| markdown_to_html(d)), content: self.sidebar.as_ref().map(|d| markdown_to_html(d)),
source: self.sidebar.clone().map(SourceCompat::new), source: self.sidebar.clone().map(Source::new),
summary: self.description.clone(), summary: self.description.clone(),
media_type: self.sidebar.as_ref().map(|_| MediaTypeHtml::Html), media_type: self.sidebar.as_ref().map(|_| MediaTypeHtml::Html),
icon: self.icon.clone().map(ImageObject::new), icon: self.icon.clone().map(ImageObject::new),

View file

@ -1,4 +1,4 @@
use crate::protocol::{ImageObject, SourceCompat}; use crate::protocol::{ImageObject, Source};
use html2md::parse_html; use html2md::parse_html;
use lemmy_apub_lib::verify::verify_domains_match; use lemmy_apub_lib::verify::verify_domains_match;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -11,8 +11,8 @@ pub mod person;
pub mod post; pub mod post;
pub mod private_message; pub mod private_message;
pub(crate) fn read_from_string_or_source(raw: &str, source: &Option<SourceCompat>) -> String { pub(crate) fn read_from_string_or_source(raw: &str, source: &Option<Source>) -> String {
if let Some(SourceCompat::Lemmy(s)) = source { if let Some(s) = source {
s.content.clone() s.content.clone()
} else { } else {
parse_html(raw) parse_html(raw)
@ -21,9 +21,9 @@ pub(crate) fn read_from_string_or_source(raw: &str, source: &Option<SourceCompat
pub(crate) fn read_from_string_or_source_opt( pub(crate) fn read_from_string_or_source_opt(
raw: &Option<String>, raw: &Option<String>,
source: &Option<SourceCompat>, source: &Option<Source>,
) -> Option<String> { ) -> Option<String> {
if let Some(SourceCompat::Lemmy(s2)) = source { if let Some(s2) = source {
Some(s2.content.clone()) Some(s2.content.clone())
} else { } else {
raw.as_ref().map(|s| parse_html(s)) raw.as_ref().map(|s| parse_html(s))

View file

@ -12,7 +12,7 @@ use crate::{
Endpoints, Endpoints,
}, },
ImageObject, ImageObject,
SourceCompat, Source,
}, },
}; };
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
@ -99,7 +99,7 @@ impl ApubObject for ApubPerson {
preferred_username: self.name.clone(), preferred_username: self.name.clone(),
name: self.display_name.clone(), name: self.display_name.clone(),
summary: self.bio.as_ref().map(|b| markdown_to_html(b)), summary: self.bio.as_ref().map(|b| markdown_to_html(b)),
source: self.bio.clone().map(SourceCompat::new), source: self.bio.clone().map(Source::new),
icon: self.avatar.clone().map(ImageObject::new), icon: self.avatar.clone().map(ImageObject::new),
image: self.banner.clone().map(ImageObject::new), image: self.banner.clone().map(ImageObject::new),
matrix_user_id: self.matrix_user_id.clone(), matrix_user_id: self.matrix_user_id.clone(),

View file

@ -4,11 +4,11 @@ use crate::{
objects::read_from_string_or_source_opt, objects::read_from_string_or_source_opt,
protocol::{ protocol::{
objects::{ objects::{
page::{Page, PageType}, page::{Attachment, Page, PageType},
tombstone::Tombstone, tombstone::Tombstone,
}, },
ImageObject, ImageObject,
SourceCompat, Source,
}, },
}; };
use activitystreams_kinds::public; use activitystreams_kinds::public;
@ -110,8 +110,9 @@ impl ApubObject for ApubPost {
name: self.name.clone(), name: self.name.clone(),
content: self.body.as_ref().map(|b| markdown_to_html(b)), content: self.body.as_ref().map(|b| markdown_to_html(b)),
media_type: Some(MediaTypeHtml::Html), media_type: Some(MediaTypeHtml::Html),
source: self.body.clone().map(SourceCompat::new), source: self.body.clone().map(Source::new),
url: self.url.clone().map(|u| u.into()), url: self.url.clone().map(|u| u.into()),
attachment: self.url.clone().map(Attachment::new).into_iter().collect(),
image: self.thumbnail_url.clone().map(ImageObject::new), image: self.thumbnail_url.clone().map(ImageObject::new),
comments_enabled: Some(!self.locked), comments_enabled: Some(!self.locked),
sensitive: Some(self.nsfw), sensitive: Some(self.nsfw),
@ -160,8 +161,13 @@ impl ApubObject for ApubPost {
.await?; .await?;
let community = page.extract_community(context, request_counter).await?; let community = page.extract_community(context, request_counter).await?;
let url = if let Some(attachment) = page.attachment.first() {
Some(attachment.href.clone())
} else {
page.url
};
let thumbnail_url: Option<Url> = page.image.map(|i| i.url); let thumbnail_url: Option<Url> = page.image.map(|i| i.url);
let (metadata_res, pictrs_thumbnail) = if let Some(url) = &page.url { let (metadata_res, pictrs_thumbnail) = if let Some(url) = &url {
fetch_site_data(context.client(), &context.settings(), Some(url)).await fetch_site_data(context.client(), &context.settings(), Some(url)).await
} else { } else {
(None, thumbnail_url) (None, thumbnail_url)
@ -173,8 +179,8 @@ impl ApubObject for ApubPost {
let body_slurs_removed = read_from_string_or_source_opt(&page.content, &page.source) let body_slurs_removed = read_from_string_or_source_opt(&page.content, &page.source)
.map(|s| remove_slurs(&s, &context.settings().slur_regex())); .map(|s| remove_slurs(&s, &context.settings().slur_regex()));
let form = PostForm { let form = PostForm {
name: page.name, name: page.name.clone(),
url: page.url.map(|u| u.into()), url: url.map(Into::into),
body: body_slurs_removed, body: body_slurs_removed,
creator_id: creator.id, creator_id: creator.id,
community_id: community.id, community_id: community.id,

View file

@ -2,7 +2,7 @@ use crate::{
objects::read_from_string_or_source, objects::read_from_string_or_source,
protocol::{ protocol::{
objects::chat_message::{ChatMessage, ChatMessageType}, objects::chat_message::{ChatMessage, ChatMessageType},
SourceCompat, Source,
}, },
}; };
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
@ -90,7 +90,7 @@ impl ApubObject for ApubPrivateMessage {
to: [ObjectId::new(recipient.actor_id)], to: [ObjectId::new(recipient.actor_id)],
content: markdown_to_html(&self.content), content: markdown_to_html(&self.content),
media_type: Some(MediaTypeHtml::Html), media_type: Some(MediaTypeHtml::Html),
source: Some(SourceCompat::new(self.content.clone())), source: Some(Source::new(self.content.clone())),
published: Some(convert_datetime(self.published)), published: Some(convert_datetime(self.published)),
updated: self.updated.map(convert_datetime), updated: self.updated.map(convert_datetime),
}; };

View file

@ -1,11 +1,9 @@
use activitystreams_kinds::object::ImageType; use activitystreams_kinds::object::ImageType;
use serde::{Deserialize, Serialize};
use url::Url;
use lemmy_apub_lib::values::MediaTypeMarkdown; use lemmy_apub_lib::values::MediaTypeMarkdown;
use lemmy_db_schema::newtypes::DbUrl; use lemmy_db_schema::newtypes::DbUrl;
use serde_json::Value; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use url::Url;
pub mod activities; pub mod activities;
pub(crate) mod collections; pub(crate) mod collections;
@ -18,20 +16,12 @@ pub struct Source {
pub(crate) media_type: MediaTypeMarkdown, pub(crate) media_type: MediaTypeMarkdown,
} }
#[derive(Clone, Debug, Deserialize, Serialize)] impl Source {
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub(crate) enum SourceCompat {
Lemmy(Source),
Other(Value),
}
impl SourceCompat {
pub(crate) fn new(content: String) -> Self { pub(crate) fn new(content: String) -> Self {
SourceCompat::Lemmy(Source { Source {
content, content,
media_type: MediaTypeMarkdown::Markdown, media_type: MediaTypeMarkdown::Markdown,
}) }
} }
} }

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
objects::{person::ApubPerson, private_message::ApubPrivateMessage}, objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::SourceCompat, protocol::Source,
}; };
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml}; use lemmy_apub_lib::{object_id::ObjectId, values::MediaTypeHtml};
@ -19,7 +19,9 @@ pub struct ChatMessage {
pub(crate) content: String, pub(crate) content: String,
pub(crate) media_type: Option<MediaTypeHtml>, pub(crate) media_type: Option<MediaTypeHtml>,
pub(crate) source: Option<SourceCompat>, #[serde(default)]
#[serde(deserialize_with = "crate::deserialize_skip_error")]
pub(crate) source: Option<Source>,
pub(crate) published: Option<DateTime<FixedOffset>>, pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,
} }

View file

@ -9,7 +9,7 @@ use crate::{
read_from_string_or_source_opt, read_from_string_or_source_opt,
verify_image_domain_matches, verify_image_domain_matches,
}, },
protocol::{objects::Endpoints, ImageObject, SourceCompat}, protocol::{objects::Endpoints, ImageObject, Source},
}; };
use activitystreams_kinds::actor::GroupType; use activitystreams_kinds::actor::GroupType;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
@ -40,7 +40,9 @@ pub struct Group {
/// title /// title
pub(crate) name: Option<String>, pub(crate) name: Option<String>,
pub(crate) summary: Option<String>, pub(crate) summary: Option<String>,
pub(crate) source: Option<SourceCompat>, #[serde(default)]
#[serde(deserialize_with = "crate::deserialize_skip_error")]
pub(crate) source: Option<Source>,
pub(crate) icon: Option<ImageObject>, pub(crate) icon: Option<ImageObject>,
/// banner /// banner
pub(crate) image: Option<ImageObject>, pub(crate) image: Option<ImageObject>,

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
objects::instance::ApubSite, objects::instance::ApubSite,
protocol::{ImageObject, SourceCompat}, protocol::{ImageObject, Source},
}; };
use activitystreams_kinds::actor::ServiceType; use activitystreams_kinds::actor::ServiceType;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
@ -25,7 +25,9 @@ pub struct Instance {
// sidebar // sidebar
pub(crate) content: Option<String>, pub(crate) content: Option<String>,
pub(crate) source: Option<SourceCompat>, #[serde(default)]
#[serde(deserialize_with = "crate::deserialize_skip_error")]
pub(crate) source: Option<Source>,
// short instance description // short instance description
pub(crate) summary: Option<String>, pub(crate) summary: Option<String>,
pub(crate) media_type: Option<MediaTypeHtml>, pub(crate) media_type: Option<MediaTypeHtml>,

View file

@ -2,7 +2,7 @@ use crate::{
fetcher::post_or_comment::PostOrComment, fetcher::post_or_comment::PostOrComment,
mentions::Mention, mentions::Mention,
objects::{comment::ApubComment, person::ApubPerson, post::ApubPost}, objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
protocol::SourceCompat, protocol::Source,
}; };
use activitystreams_kinds::object::NoteType; use activitystreams_kinds::object::NoteType;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
@ -32,7 +32,9 @@ pub struct Note {
pub(crate) in_reply_to: ObjectId<PostOrComment>, pub(crate) in_reply_to: ObjectId<PostOrComment>,
pub(crate) media_type: Option<MediaTypeHtml>, pub(crate) media_type: Option<MediaTypeHtml>,
pub(crate) source: Option<SourceCompat>, #[serde(default)]
#[serde(deserialize_with = "crate::deserialize_skip_error")]
pub(crate) source: Option<Source>,
pub(crate) published: Option<DateTime<FixedOffset>>, pub(crate) published: Option<DateTime<FixedOffset>>,
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,
#[serde(default)] #[serde(default)]

View file

@ -1,7 +1,8 @@
use crate::{ use crate::{
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::{ImageObject, SourceCompat}, protocol::{ImageObject, Source},
}; };
use activitystreams_kinds::link::LinkType;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use itertools::Itertools; use itertools::Itertools;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
@ -10,6 +11,7 @@ use lemmy_apub_lib::{
traits::{ActivityHandler, ApubObject}, traits::{ActivityHandler, ApubObject},
values::MediaTypeHtml, values::MediaTypeHtml,
}; };
use lemmy_db_schema::newtypes::DbUrl;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -39,8 +41,15 @@ pub struct Page {
pub(crate) cc: Vec<Url>, pub(crate) cc: Vec<Url>,
pub(crate) content: Option<String>, pub(crate) content: Option<String>,
pub(crate) media_type: Option<MediaTypeHtml>, pub(crate) media_type: Option<MediaTypeHtml>,
pub(crate) source: Option<SourceCompat>, #[serde(default)]
#[serde(deserialize_with = "crate::deserialize_skip_error")]
pub(crate) source: Option<Source>,
/// deprecated, use attachment field
pub(crate) url: Option<Url>, pub(crate) url: Option<Url>,
/// most software uses array type for attachment field, so we do the same. nevertheless, we only
/// use the first item
#[serde(default)]
pub(crate) attachment: Vec<Attachment>,
pub(crate) image: Option<ImageObject>, pub(crate) image: Option<ImageObject>,
pub(crate) comments_enabled: Option<bool>, pub(crate) comments_enabled: Option<bool>,
pub(crate) sensitive: Option<bool>, pub(crate) sensitive: Option<bool>,
@ -49,6 +58,13 @@ pub struct Page {
pub(crate) updated: Option<DateTime<FixedOffset>>, pub(crate) updated: Option<DateTime<FixedOffset>>,
} }
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Attachment {
pub(crate) href: Url,
pub(crate) r#type: LinkType,
}
impl Page { impl Page {
/// Only mods can change the post's stickied/locked status. So if either of these is changed from /// Only mods can change the post's stickied/locked status. So if either of these is changed from
/// the current value, it is a mod action and needs to be verified as such. /// the current value, it is a mod action and needs to be verified as such.
@ -89,6 +105,15 @@ impl Page {
} }
} }
impl Attachment {
pub(crate) fn new(url: DbUrl) -> Attachment {
Attachment {
href: url.into(),
r#type: Default::default(),
}
}
}
// Used for community outbox, so that it can be compatible with Pleroma/Mastodon. // Used for community outbox, so that it can be compatible with Pleroma/Mastodon.
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl ActivityHandler for Page { impl ActivityHandler for Page {

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
objects::person::ApubPerson, objects::person::ApubPerson,
protocol::{objects::Endpoints, ImageObject, SourceCompat}, protocol::{objects::Endpoints, ImageObject, Source},
}; };
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey}; use lemmy_apub_lib::{object_id::ObjectId, signatures::PublicKey};
@ -31,7 +31,9 @@ pub struct Person {
/// displayname /// displayname
pub(crate) name: Option<String>, pub(crate) name: Option<String>,
pub(crate) summary: Option<String>, pub(crate) summary: Option<String>,
pub(crate) source: Option<SourceCompat>, #[serde(default)]
#[serde(deserialize_with = "crate::deserialize_skip_error")]
pub(crate) source: Option<Source>,
/// user avatar /// user avatar
pub(crate) icon: Option<ImageObject>, pub(crate) icon: Option<ImageObject>,
/// user banner /// user banner