From 847d6f7fac57e18b9b3bf7ac0f64bc461883841d Mon Sep 17 00:00:00 2001 From: KITAITI Makoto Date: Mon, 4 May 2020 22:28:52 +0900 Subject: [PATCH] Fix Atom feed (#764) * Percent-encode URI segments in ap_url * Fix invalid Atoms * Remove redundant clone according to cargo clippy * Revert "Percent-encode URI segments in ap_url" This reverts commit 8253aa79bd6efd120c1f2d3c2872deda9609ff3a. ActivityPub(JSON-LD) accepts URI. See https://github.com/Plume-org/Plume/pull/764#issuecomment-623105734 * Use absolute IRI for IDs in Atom * Reuse formatted string * Use parent element's creation date for Atom updated if there's no post * Remove uncecessary UTC conversion * Extract routes::build_atom_feed function --- src/routes/blogs.rs | 22 +++++++-------------- src/routes/mod.rs | 47 ++++++++++++++++++++++++++++++++++++++++----- src/routes/user.rs | 22 +++++++-------------- 3 files changed, 56 insertions(+), 35 deletions(-) mode change 100644 => 100755 src/routes/mod.rs diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index bba2b402..0c08c536 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -1,5 +1,4 @@ use activitypub::collection::{OrderedCollection, OrderedCollectionPage}; -use atom_syndication::{Entry, FeedBuilder}; use diesel::SaveChangesDsl; use rocket::{ http::ContentType, @@ -361,20 +360,13 @@ pub fn outbox_page( pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option> { let blog = Blog::find_by_fqn(&rockets, &name).ok()?; let conn = &*rockets.conn; - let feed = FeedBuilder::default() - .title(blog.title.clone()) - .id(Instance::get_local() - .ok()? - .compute_box("~", &name, "atom.xml")) - .entries( - Post::get_recents_for_blog(&*conn, &blog, 15) - .ok()? - .into_iter() - .map(|p| super::post_to_atom(p, &*conn)) - .collect::>(), - ) - .build() - .ok()?; + let entries = Post::get_recents_for_blog(&*conn, &blog, 15).ok()?; + let uri = Instance::get_local() + .ok()? + .compute_box("~", &name, "atom.xml"); + let title = &blog.title; + let default_updated = &blog.creation_date; + let feed = super::build_atom_feed(entries, &uri, title, default_updated, conn); Some(Content( ContentType::new("application", "atom+xml"), feed.to_string(), diff --git a/src/routes/mod.rs b/src/routes/mod.rs old mode 100644 new mode 100755 index 88da993d..8f4e928c --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,6 +1,9 @@ #![warn(clippy::too_many_arguments)] use crate::template_utils::Ructe; -use atom_syndication::{ContentBuilder, Entry, EntryBuilder, LinkBuilder, Person, PersonBuilder}; +use atom_syndication::{ + ContentBuilder, Entry, EntryBuilder, Feed, FeedBuilder, LinkBuilder, Person, PersonBuilder, +}; +use chrono::naive::NaiveDateTime; use plume_models::{posts::Post, Connection, CONFIG, ITEMS_PER_PAGE}; use rocket::{ http::{ @@ -115,13 +118,46 @@ pub struct RemoteForm { pub remote: String, } -pub fn post_to_atom(post: Post, conn: &Connection) -> Entry { +pub fn build_atom_feed( + entries: Vec, + uri: &str, + title: &str, + default_updated: &NaiveDateTime, + conn: &Connection, +) -> Feed { + let updated = if entries.is_empty() { + default_updated + } else { + &entries[0].creation_date + }; + + FeedBuilder::default() + .title(title) + .id(uri) + .updated(updated.format("%Y-%m-%d %H:%M:%SZ").to_string()) + .entries( + entries + .into_iter() + .map(|p| post_to_atom(p, conn)) + .collect::>(), + ) + .links(vec![LinkBuilder::default() + .href(uri) + .rel("self") + .mime_type("application/atom+xml".to_string()) + .build() + .expect("Atom feed: link error")]) + .build() + .expect("user::atom_feed: Error building Atom feed") +} + +fn post_to_atom(post: Post, conn: &Connection) -> Entry { + let formatted_creation_date = post.creation_date.format("%Y-%m-%dT%H:%M:%SZ").to_string(); EntryBuilder::default() .title(format!("", post.title)) .content( ContentBuilder::default() .value(format!("", *post.content.get())) - .src(post.ap_url.clone()) .content_type("html".to_string()) .build() .expect("Atom feed: content error"), @@ -141,8 +177,9 @@ pub fn post_to_atom(post: Post, conn: &Connection) -> Entry { ) // Using RFC 4287 format, see https://tools.ietf.org/html/rfc4287#section-3.3 for dates // eg: 2003-12-13T18:30:02Z (Z is here because there is no timezone support with the NaiveDateTime crate) - .published(post.creation_date.format("%Y-%m-%dT%H:%M:%SZ").to_string()) - .id(post.id.to_string()) + .published(formatted_creation_date.clone()) + .updated(formatted_creation_date) + .id(post.ap_url.clone()) .links(vec![LinkBuilder::default() .href(post.ap_url) .build() diff --git a/src/routes/user.rs b/src/routes/user.rs index 2a952d1d..e0bfd783 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -2,7 +2,6 @@ use activitypub::{ activity::Create, collection::{OrderedCollection, OrderedCollectionPage}, }; -use atom_syndication::{Entry, FeedBuilder}; use diesel::SaveChangesDsl; use rocket::{ http::{ContentType, Cookies}, @@ -619,20 +618,13 @@ pub fn ap_followers( pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option> { let conn = &*rockets.conn; let author = User::find_by_fqn(&rockets, &name).ok()?; - let feed = FeedBuilder::default() - .title(author.display_name.clone()) - .id(Instance::get_local() - .unwrap() - .compute_box("@", &name, "atom.xml")) - .entries( - Post::get_recents_for_author(conn, &author, 15) - .ok()? - .into_iter() - .map(|p| super::post_to_atom(p, conn)) - .collect::>(), - ) - .build() - .expect("user::atom_feed: Error building Atom feed"); + let entries = Post::get_recents_for_author(conn, &author, 15).ok()?; + let uri = Instance::get_local() + .ok()? + .compute_box("@", &name, "atom.xml"); + let title = &author.display_name; + let default_updated = &author.creation_date; + let feed = super::build_atom_feed(entries, &uri, title, default_updated, conn); Some(Content( ContentType::new("application", "atom+xml"), feed.to_string(),