Append object URL to post content if object type is not Note

This commit is contained in:
silverpill 2022-07-28 14:09:57 +00:00
parent 85d35f9733
commit ecc4afe568
3 changed files with 69 additions and 8 deletions

View file

@ -8,4 +8,5 @@ rustflags = [
"-Aclippy::redundant_field_names", "-Aclippy::redundant_field_names",
"-Aclippy::unused_unit", "-Aclippy::unused_unit",
"-Aclippy::enum_variant_names", "-Aclippy::enum_variant_names",
"-Aclippy::format_push_string",
] ]

View file

@ -18,6 +18,12 @@ pub struct Attachment {
pub url: Option<String>, pub url: Option<String>,
} }
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Link {
pub href: String,
}
fn default_tag_type() -> String { HASHTAG.to_string() } fn default_tag_type() -> String { HASHTAG.to_string() }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
@ -80,6 +86,9 @@ pub struct Object {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub updated: Option<DateTime<Utc>>, pub updated: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<Value>,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]

View file

@ -1,11 +1,12 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use serde_json::{Value as JsonValue};
use tokio_postgres::GenericClient; use tokio_postgres::GenericClient;
use uuid::Uuid; use uuid::Uuid;
use crate::activitypub::{ use crate::activitypub::{
activity::{Attachment, Object}, activity::{Attachment, Link, Object},
constants::AP_PUBLIC, constants::AP_PUBLIC,
fetcher::fetchers::fetch_file, fetcher::fetchers::fetch_file,
fetcher::helpers::{ fetcher::helpers::{
@ -18,7 +19,7 @@ use crate::activitypub::{
vocabulary::{DOCUMENT, HASHTAG, IMAGE, MENTION, NOTE}, vocabulary::{DOCUMENT, HASHTAG, IMAGE, MENTION, NOTE},
}; };
use crate::config::Instance; use crate::config::Instance;
use crate::errors::{DatabaseError, ValidationError}; use crate::errors::{ConversionError, DatabaseError, ValidationError};
use crate::models::attachments::queries::create_attachment; use crate::models::attachments::queries::create_attachment;
use crate::models::posts::mentions::mention_to_address; use crate::models::posts::mentions::mention_to_address;
use crate::models::posts::queries::{ use crate::models::posts::queries::{
@ -46,15 +47,42 @@ fn get_note_author_id(object: &Object) -> Result<String, ValidationError> {
const CONTENT_MAX_SIZE: usize = 100000; const CONTENT_MAX_SIZE: usize = 100000;
fn parse_object_url(value: &JsonValue) -> Result<String, ConversionError> {
let object_url = match value {
JsonValue::String(string) => string.to_owned(),
other_value => {
let links: Vec<Link> = parse_property_value(other_value)?;
if let Some(link) = links.get(0) {
link.href.clone()
} else {
return Err(ConversionError);
}
},
};
Ok(object_url)
}
pub fn get_note_content(object: &Object) -> Result<String, ValidationError> { pub fn get_note_content(object: &Object) -> Result<String, ValidationError> {
let content = object.content.as_ref() let mut content = object.content.as_ref()
// Lemmy pages and PeerTube videos have "name" property // Lemmy pages and PeerTube videos have "name" property
.or(object.name.as_ref()) .or(object.name.as_ref())
.ok_or(ValidationError("no content"))?; .ok_or(ValidationError("no content"))?
.to_owned();
if object.object_type != NOTE {
if let Some(ref value) = object.url {
// Append link to object
let object_url = parse_object_url(value)
.map_err(|_| ValidationError("invalid object URL"))?;
content += &format!(
r#"<p><a href="{0}" target="_blank" rel="noopener">{0}</a></p>"#,
object_url,
);
};
};
if content.len() > CONTENT_MAX_SIZE { if content.len() > CONTENT_MAX_SIZE {
return Err(ValidationError("content is too long")); return Err(ValidationError("content is too long"));
}; };
let content_safe = clean_html(content); let content_safe = clean_html(&content);
Ok(content_safe) Ok(content_safe)
} }
@ -302,9 +330,12 @@ pub async fn handle_note(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::activitypub::activity::Object; use serde_json::json;
use crate::activitypub::actors::types::Actor; use crate::activitypub::{
use crate::activitypub::vocabulary::NOTE; activity::Object,
actors::types::Actor,
vocabulary::NOTE,
};
use super::*; use super::*;
#[test] #[test]
@ -318,6 +349,26 @@ mod tests {
assert_eq!(content, "test"); assert_eq!(content, "test");
} }
#[test]
fn test_get_note_content_from_video() {
let object = Object {
name: Some("test-name".to_string()),
content: Some("test-content".to_string()),
object_type: "Video".to_string(),
url: Some(json!([{
"type": "Link",
"mediaType": "text/html",
"href": "https://example.org/xyz",
}])),
..Default::default()
};
let content = get_note_content(&object).unwrap();
assert_eq!(
content,
r#"test-content<p><a href="https://example.org/xyz" target="_blank" rel="noopener">https://example.org/xyz</a></p>"#,
);
}
#[test] #[test]
fn test_get_note_visibility_public() { fn test_get_note_visibility_public() {
let author = DbActorProfile::default(); let author = DbActorProfile::default();