From b73fbd3768e5b9d7912ccc33b9219931b05ff93e Mon Sep 17 00:00:00 2001 From: Baptiste Gelez Date: Sun, 9 Dec 2018 18:43:34 +0100 Subject: [PATCH] License federation (#343) * Federate license * Make it possible to use no license --- plume-common/src/activity_pub/mod.rs | 9 +++++ plume-models/src/posts.rs | 49 +++++++++++++++++----------- po/de.po | 7 ++++ po/en.po | 6 ++++ po/fr.po | 7 ++++ po/gl.po | 7 ++++ po/it.po | 7 ++++ po/ja.po | 7 ++++ po/nb.po | 6 ++++ po/pl.po | 7 ++++ po/plume.pot | 6 ++++ po/ru.po | 7 ++++ src/routes/posts.rs | 28 +++++----------- src/routes/user.rs | 6 ++-- templates/posts/details.rs.html | 8 ++++- templates/posts/new.rs.html | 4 +-- 16 files changed, 126 insertions(+), 45 deletions(-) diff --git a/plume-common/src/activity_pub/mod.rs b/plume-common/src/activity_pub/mod.rs index 4dbd25a..01836af 100644 --- a/plume-common/src/activity_pub/mod.rs +++ b/plume-common/src/activity_pub/mod.rs @@ -220,3 +220,12 @@ pub struct Source { } impl Object for Source {} + +#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[serde(rename_all = "camelCase")] +pub struct Licensed { + #[activitystreams(concrete(String), functional)] + pub license: Option, +} + +impl Object for Licensed {} diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index a661f40..18446f5 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -1,4 +1,5 @@ use activitypub::{ + CustomObject, activity::{Create, Delete, Update}, link, object::{Article, Image, Tombstone}, @@ -18,7 +19,7 @@ use plume_api::posts::PostEndpoint; use plume_common::{ activity_pub::{ inbox::{Deletable, FromActivity}, - Hashtag, Id, IntoId, Source, PUBLIC_VISIBILTY, + Hashtag, Id, IntoId, Licensed, Source, PUBLIC_VISIBILTY, }, utils::md_to_html, }; @@ -32,6 +33,8 @@ use tags::Tag; use users::User; use {ap_url, Connection, BASE_URL}; +pub type LicensedArticle = CustomObject; + #[derive(Queryable, Identifiable, Serialize, Clone, AsChangeset)] #[changeset_options(treat_none_as_null = "true")] pub struct Post { @@ -418,7 +421,7 @@ impl Post { }) } - pub fn to_activity(&self, conn: &Connection) -> Article { + pub fn to_activity(&self, conn: &Connection) -> LicensedArticle { let mut to = self.get_receivers_urls(conn); to.push(PUBLIC_VISIBILTY.to_string()); @@ -516,7 +519,9 @@ impl Post { .object_props .set_cc_link_vec::(vec![]) .expect("Post::to_activity: cc error"); - article + let mut license = Licensed::default(); + license.set_license_string(self.license.clone()).expect("Post::to_activity: license error"); + LicensedArticle::new(article, license) } pub fn create_activity(&self, conn: &Connection) -> Create { @@ -527,7 +532,7 @@ impl Post { .expect("Post::create_activity: id error"); act.object_props .set_to_link_vec::( - article + article.object .object_props .to_link_vec() .expect("Post::create_activity: Couldn't copy 'to'"), @@ -535,7 +540,7 @@ impl Post { .expect("Post::create_activity: to error"); act.object_props .set_cc_link_vec::( - article + article.object .object_props .cc_link_vec() .expect("Post::create_activity: Couldn't copy 'cc'"), @@ -558,7 +563,7 @@ impl Post { .expect("Post::update_activity: id error"); act.object_props .set_to_link_vec::( - article + article.object .object_props .to_link_vec() .expect("Post::update_activity: Couldn't copy 'to'"), @@ -566,7 +571,7 @@ impl Post { .expect("Post::update_activity: to error"); act.object_props .set_cc_link_vec::( - article + article.object .object_props .cc_link_vec() .expect("Post::update_activity: Couldn't copy 'cc'"), @@ -577,44 +582,48 @@ impl Post { .expect("Post::update_activity: actor error"); act.update_props .set_object_object(article) - .expect("Article::update_activity: object error"); + .expect("Post::update_activity: object error"); act } - pub fn handle_update(conn: &Connection, updated: &Article, searcher: &Searcher) { - let id = updated + pub fn handle_update(conn: &Connection, updated: &LicensedArticle, searcher: &Searcher) { + let id = updated.object .object_props .id_string() .expect("Post::handle_update: id error"); let mut post = Post::find_by_ap_url(conn, &id).expect("Post::handle_update: finding error"); - if let Ok(title) = updated.object_props.name_string() { + if let Ok(title) = updated.object.object_props.name_string() { post.slug = title.to_kebab_case(); post.title = title; } - if let Ok(content) = updated.object_props.content_string() { + if let Ok(content) = updated.object.object_props.content_string() { post.content = SafeString::new(&content); } - if let Ok(subtitle) = updated.object_props.summary_string() { + if let Ok(subtitle) = updated.object.object_props.summary_string() { post.subtitle = subtitle; } - if let Ok(ap_url) = updated.object_props.url_string() { + if let Ok(ap_url) = updated.object.object_props.url_string() { post.ap_url = ap_url; } - if let Ok(source) = updated.ap_object_props.source_object::() { + if let Ok(source) = updated.object.ap_object_props.source_object::() { post.source = source.content; } + if let Ok(license) = updated.custom_props.license_string() { + post.license = license; + } + let mut txt_hashtags = md_to_html(&post.source) .2 .into_iter() .map(|s| s.to_camel_case()) .collect::>(); - if let Some(serde_json::Value::Array(mention_tags)) = updated.object_props.tag.clone() { + if let Some(serde_json::Value::Array(mention_tags)) = updated.object.object_props.tag.clone() { let mut mentions = vec![]; let mut tags = vec![]; let mut hashtags = vec![]; @@ -782,8 +791,10 @@ impl Post { } } -impl<'a> FromActivity for Post { - fn from_activity((conn, searcher): &(&'a Connection, &'a Searcher), article: Article, _actor: Id) -> Post { +impl<'a> FromActivity for Post { + fn from_activity((conn, searcher): &(&'a Connection, &'a Searcher), article: LicensedArticle, _actor: Id) -> Post { + let license = article.custom_props.license_string().unwrap_or_default(); + let article = article.object; if let Some(post) = Post::find_by_ap_url( conn, &article.object_props.id_string().unwrap_or_default(), @@ -829,7 +840,7 @@ impl<'a> FromActivity for Post { .expect("Post::from_activity: content error"), ), published: true, - license: String::from("CC-BY-SA"), // TODO + license: license, // FIXME: This is wrong: with this logic, we may use the display URL as the AP ID. We need two different fields ap_url: article.object_props.url_string().unwrap_or_else(|_| article diff --git a/po/de.po b/po/de.po index c08f161..60a80c7 100644 --- a/po/de.po +++ b/po/de.po @@ -609,6 +609,13 @@ msgstr "Administration" msgid "None" msgstr "" +#, fuzzy +msgid "Let it empty reserve all rights" +msgstr "Falls es dies nicht gibt, lass es leer" + +msgid "All rights reserved." +msgstr "" + #~ msgid "Home to" #~ msgstr "Heimat von" diff --git a/po/en.po b/po/en.po index d861db6..3453906 100644 --- a/po/en.po +++ b/po/en.po @@ -593,3 +593,9 @@ msgstr "" msgid "None" msgstr "" + +msgid "Let it empty reserve all rights" +msgstr "" + +msgid "All rights reserved." +msgstr "" diff --git a/po/fr.po b/po/fr.po index 80ab0d9..34291c3 100644 --- a/po/fr.po +++ b/po/fr.po @@ -609,3 +609,10 @@ msgstr "Illustration" msgid "None" msgstr "Aucun" + +#, fuzzy +msgid "Let it empty reserve all rights" +msgstr "Laisser vide s’il n’y en a pas" + +msgid "All rights reserved." +msgstr "" diff --git a/po/gl.po b/po/gl.po index 915acc1..59975cc 100644 --- a/po/gl.po +++ b/po/gl.po @@ -600,6 +600,13 @@ msgstr "Administración" msgid "None" msgstr "" +#, fuzzy +msgid "Let it empty reserve all rights" +msgstr "Deixar baldeiro si non hai ningunha" + +msgid "All rights reserved." +msgstr "" + #~ msgid "Home to" #~ msgstr "Fogar de" diff --git a/po/it.po b/po/it.po index 51fc63e..6c7c8eb 100644 --- a/po/it.po +++ b/po/it.po @@ -603,6 +603,13 @@ msgstr "Amministrazione" msgid "None" msgstr "" +#, fuzzy +msgid "Let it empty reserve all rights" +msgstr "Lascialo vuoto se non è presente nessuno" + +msgid "All rights reserved." +msgstr "" + #~ msgid "Home to" #~ msgstr "Casa di" diff --git a/po/ja.po b/po/ja.po index a85b377..e61733e 100644 --- a/po/ja.po +++ b/po/ja.po @@ -595,6 +595,13 @@ msgstr "図" msgid "None" msgstr "なし" +#, fuzzy +msgid "Let it empty reserve all rights" +msgstr "不要な場合は空にしてください" + +msgid "All rights reserved." +msgstr "" + #~ msgid "Welcome to {{ instance_name | escape }}" #~ msgstr "{{ instance_name | escape }} へようこそ" diff --git a/po/nb.po b/po/nb.po index b3f822e..e81840b 100644 --- a/po/nb.po +++ b/po/nb.po @@ -615,6 +615,12 @@ msgstr "Administrasjon" msgid "None" msgstr "" +msgid "Let it empty reserve all rights" +msgstr "" + +msgid "All rights reserved." +msgstr "" + #~ msgid "Home to" #~ msgstr "Hjem for" diff --git a/po/pl.po b/po/pl.po index f968fa6..898bc17 100644 --- a/po/pl.po +++ b/po/pl.po @@ -609,6 +609,13 @@ msgstr "Ilustracja" msgid "None" msgstr "Brak" +#, fuzzy +msgid "Let it empty reserve all rights" +msgstr "Pozostaw puste, jeżeli niepotrzebne" + +msgid "All rights reserved." +msgstr "" + #~ msgid "Home to" #~ msgstr "Dom dla" diff --git a/po/plume.pot b/po/plume.pot index 28962af..b8218b5 100644 --- a/po/plume.pot +++ b/po/plume.pot @@ -579,3 +579,9 @@ msgstr "" msgid "None" msgstr "" + +msgid "Let it empty reserve all rights" +msgstr "" + +msgid "All rights reserved." +msgstr "" diff --git a/po/ru.po b/po/ru.po index 3930be5..8d977ac 100644 --- a/po/ru.po +++ b/po/ru.po @@ -620,6 +620,13 @@ msgstr "Иллюстрация" msgid "None" msgstr "Нет" +#, fuzzy +msgid "Let it empty reserve all rights" +msgstr "Оставьте пустым если нет" + +msgid "All rights reserved." +msgstr "" + #~ msgid "Home to" #~ msgstr "Дом для" diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 012e5fa..5a58595 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -1,4 +1,3 @@ -use activitypub::object::Article; use chrono::Utc; use heck::{CamelCase, KebabCase}; use rocket::request::LenientForm; @@ -89,7 +88,7 @@ pub fn details_response(blog: String, slug: String, conn: DbConn, user: Option/", rank = 3)] -pub fn activity_details(blog: String, slug: String, conn: DbConn, _ap: ApRequest) -> Result, Option> { +pub fn activity_details(blog: String, slug: String, conn: DbConn, _ap: ApRequest) -> Result, Option> { let blog = Blog::find_by_fqn(&*conn, &blog).ok_or(None)?; let post = Post::find_by_slug(&*conn, &slug, blog.id).ok_or(None)?; if post.published { @@ -123,11 +122,13 @@ pub fn new(blog: String, user: User, conn: DbConn, intl: I18n) -> Option &(&*conn, &intl.catalog, Some(user)), b, false, - &NewPostForm::default(), + &NewPostForm { + license: Instance::get_local(&*conn).map(|i| i.default_license).unwrap_or_else(||String::from("CC-BY-SA")), + ..NewPostForm::default() + }, true, None, ValidationErrors::default(), - Instance::get_local(&*conn).expect("posts::new error: Local instance is null").default_license, medias ))) } @@ -171,7 +172,6 @@ pub fn edit(blog: String, slug: String, user: User, conn: DbConn, intl: I18n) -> !post.published, Some(post), ValidationErrors::default(), - Instance::get_local(&*conn).expect("posts::new error: Local instance is null").default_license, medias ))) } @@ -209,12 +209,6 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien } else { let (content, mentions, hashtags) = utils::md_to_html(form.content.to_string().as_ref()); - let license = if !form.license.is_empty() { - form.license.to_string() - } else { - Instance::get_local(&*conn).map(|i| i.default_license).unwrap_or_else(|| String::from("CC-BY-SA")) - }; - // update publication date if when this article is no longer a draft let newly_published = if !post.published && !form.draft { post.published = true; @@ -229,7 +223,7 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien post.subtitle = form.subtitle.clone(); post.content = SafeString::new(&content); post.source = form.content.clone(); - post.license = license; + post.license = form.license.clone(); post.cover_id = form.cover; post.update(&*conn, &searcher); let post = post.update_ap_url(&*conn); @@ -262,7 +256,7 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien } } else { let medias = Media::for_user(&*conn, user.id); - let temp = render!(posts::new( + let temp = render!(posts::new( &(&*conn, &intl.catalog, Some(user)), b, true, @@ -270,7 +264,6 @@ pub fn update(blog: String, slug: String, user: User, conn: DbConn, form: Lenien form.draft.clone(), Some(post), errors.clone(), - Instance::get_local(&*conn).expect("posts::new error: Local instance is null").default_license, medias.clone() )); Err(Some(temp)) @@ -330,11 +323,7 @@ pub fn create(blog_name: String, form: LenientForm, user: User, con title: form.title.to_string(), content: SafeString::new(&content), published: !form.draft, - license: if !form.license.is_empty() { - form.license.to_string() - } else { - Instance::get_local(&*conn).map(|i| i.default_license).unwrap_or_else(||String::from("CC-BY-SA")) - }, + license: form.license.clone(), ap_url: "".to_string(), creation_date: None, subtitle: form.subtitle.clone(), @@ -390,7 +379,6 @@ pub fn create(blog_name: String, form: LenientForm, user: User, con form.draft, None, errors.clone(), - Instance::get_local(&*conn).expect("posts::new error: Local instance is null").default_license, medias )))) } diff --git a/src/routes/user.rs b/src/routes/user.rs index a64caca..ed02800 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -1,4 +1,4 @@ -use activitypub::{activity::Create, collection::OrderedCollection, object::Article}; +use activitypub::{activity::Create, collection::OrderedCollection}; use atom_syndication::{Entry, FeedBuilder}; use rocket::{ http::{ContentType, Cookies}, @@ -18,7 +18,7 @@ use plume_common::activity_pub::{ }; use plume_common::utils; use plume_models::{ - blogs::Blog, db_conn::DbConn, follows, headers::Headers, instance::Instance, posts::Post, + blogs::Blog, db_conn::DbConn, follows, headers::Headers, instance::Instance, posts::{LicensedArticle, Post}, reshares::Reshare, users::*, }; use routes::Page; @@ -56,7 +56,7 @@ pub fn details( let searcher = searcher.clone(); worker.execute(move || { for create_act in user_clone.fetch_outbox::() { - match create_act.create_props.object_object::
() { + match create_act.create_props.object_object::() { Ok(article) => { Post::from_activity( &(&*fetch_articles_conn, &searcher), diff --git a/templates/posts/details.rs.html b/templates/posts/details.rs.html index 48bc231..c65dcff 100644 --- a/templates/posts/details.rs.html +++ b/templates/posts/details.rs.html @@ -53,7 +53,13 @@ @Html(&article.content)