From 95326c09e0193f07fa01b6ae7158509c1dbeeeba Mon Sep 17 00:00:00 2001 From: Baptiste Gelez Date: Wed, 31 Oct 2018 10:40:20 +0100 Subject: [PATCH] Federate article covers --- Cargo.lock | 1 + plume-models/Cargo.toml | 1 + plume-models/src/lib.rs | 1 + plume-models/src/medias.rs | 29 ++++++++++++++++++++++++++++- plume-models/src/posts.rs | 23 +++++++++++++++++++++-- 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5f50d39..7c8334b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1703,6 +1703,7 @@ dependencies = [ "canapi 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "diesel 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "guid-create 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.12 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/plume-models/Cargo.toml b/plume-models/Cargo.toml index 99ad6652..095c3d01 100644 --- a/plume-models/Cargo.toml +++ b/plume-models/Cargo.toml @@ -8,6 +8,7 @@ activitypub = "0.1.1" ammonia = "1.2.0" bcrypt = "0.2" canapi = "0.1" +guid-create = "0.1" heck = "0.3.0" lazy_static = "*" openssl = "0.10.11" diff --git a/plume-models/src/lib.rs b/plume-models/src/lib.rs index 525edc26..0283fda1 100644 --- a/plume-models/src/lib.rs +++ b/plume-models/src/lib.rs @@ -8,6 +8,7 @@ extern crate canapi; extern crate chrono; #[macro_use] extern crate diesel; +extern crate guid_create; extern crate heck; #[macro_use] extern crate lazy_static; diff --git a/plume-models/src/medias.rs b/plume-models/src/medias.rs index 713fbef8..8b220a0c 100644 --- a/plume-models/src/medias.rs +++ b/plume-models/src/medias.rs @@ -1,9 +1,15 @@ +use activitypub::object::Image; use diesel::{self, QueryDsl, ExpressionMethods, RunQueryDsl}; +use guid_create::GUID; +use reqwest; use serde_json; -use std::fs; +use std::{fs, path::Path}; + +use plume_common::activity_pub::Id; use {ap_url, Connection}; use instance::Instance; +use users::User; use schema::medias; #[derive(Clone, Identifiable, Queryable, Serialize)] @@ -94,4 +100,25 @@ impl Media { .execute(conn) .expect("Media::set_owner: owner update error"); } + + // TODO: merge with save_remote? + pub fn from_activity(conn: &Connection, image: Image) -> Option { + let remote_url = image.object_props.url_string().ok()?; + let ext = remote_url.rsplit('.').next().map(|ext| ext.to_owned()).unwrap_or("png".to_owned()); + let path = Path::new("static").join("media").join(format!("{}.{}", GUID::rand().to_string(), ext)); + + let mut dest = fs::File::create(path.clone()).ok()?; + reqwest::get(remote_url.as_str()).ok()? + .copy_to(&mut dest).ok()?; + + Some(Media::insert(conn, NewMedia { + file_path: path.to_str()?.to_string(), + alt_text: image.object_props.content_string().ok()?, + is_remote: true, + remote_url: None, + sensitive: image.object_props.summary_string().is_ok(), + content_warning: image.object_props.summary_string().ok(), + owner_id: User::from_url(conn, image.object_props.attributed_to_link_vec::().ok()?.into_iter().next()?.into())?.id + })) + } } diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index dc54ada4..6dba6bb2 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -1,7 +1,7 @@ use activitypub::{ activity::{Create, Delete, Update}, link, - object::{Article, Tombstone} + object::{Article, Image, Tombstone} }; use canapi::{Error, Provider}; use chrono::{NaiveDateTime, TimeZone, Utc}; @@ -22,6 +22,7 @@ use {BASE_URL, ap_url, Connection}; use blogs::Blog; use instance::Instance; use likes::Like; +use medias::Media; use mentions::Mention; use post_authors::*; use reshares::Reshare; @@ -358,6 +359,21 @@ impl Post { article.object_props.set_published_utctime(Utc.from_utc_datetime(&self.creation_date)).expect("Post::into_activity: published error"); article.object_props.set_summary_string(self.subtitle.clone()).expect("Post::into_activity: summary error"); article.object_props.tag = Some(json!(mentions_json)); + + if let Some(media_id) = self.cover_id { + let media = Media::get(conn, media_id).expect("Post::into_activity: get cover error"); + let mut cover = Image::default(); + cover.object_props.set_url_string(media.url(conn)).expect("Post::into_activity: icon.url error"); + if media.sensitive { + cover.object_props.set_summary_string(media.content_warning.unwrap_or(String::new())).expect("Post::into_activity: icon.summary error"); + } + cover.object_props.set_content_string(media.alt_text).expect("Post::into_activity: icon.content error"); + cover.object_props.set_attributed_to_link_vec(vec![ + User::get(conn, media.owner_id).expect("Post::into_activity: media owner not found").into_id() + ]).expect("Post::into_activity: icon.attributedTo error"); + article.object_props.set_icon_object(cover).expect("Post::into_activity: icon error"); + } + article.object_props.set_url_string(self.ap_url.clone()).expect("Post::into_activity: url error"); article.object_props.set_to_link_vec::(to.into_iter().map(Id::new).collect()).expect("Post::into_activity: to error"); article.object_props.set_cc_link_vec::(vec![]).expect("Post::into_activity: cc error"); @@ -538,6 +554,9 @@ impl FromActivity for Post { } }); + let cover = article.object_props.icon_object::().ok() + .and_then(|img| Media::from_activity(conn, img).map(|m| m.id)); + let title = article.object_props.name_string().expect("Post::from_activity: title error"); let post = Post::insert(conn, NewPost { blog_id: blog.expect("Post::from_activity: blog not found error").id, @@ -551,7 +570,7 @@ impl FromActivity for Post { creation_date: Some(article.object_props.published_utctime().expect("Post::from_activity: published error").naive_utc()), subtitle: article.object_props.summary_string().expect("Post::from_activity: summary error"), source: article.ap_object_props.source_object::().expect("Post::from_activity: source error").content, - cover_id: None, // TODO + cover_id: cover, }); for author in authors.into_iter() {