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 8253aa79bd
.
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
This commit is contained in:
parent
6a3f210dfc
commit
847d6f7fac
@ -1,5 +1,4 @@
|
|||||||
use activitypub::collection::{OrderedCollection, OrderedCollectionPage};
|
use activitypub::collection::{OrderedCollection, OrderedCollectionPage};
|
||||||
use atom_syndication::{Entry, FeedBuilder};
|
|
||||||
use diesel::SaveChangesDsl;
|
use diesel::SaveChangesDsl;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
http::ContentType,
|
http::ContentType,
|
||||||
@ -361,20 +360,13 @@ pub fn outbox_page(
|
|||||||
pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option<Content<String>> {
|
pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option<Content<String>> {
|
||||||
let blog = Blog::find_by_fqn(&rockets, &name).ok()?;
|
let blog = Blog::find_by_fqn(&rockets, &name).ok()?;
|
||||||
let conn = &*rockets.conn;
|
let conn = &*rockets.conn;
|
||||||
let feed = FeedBuilder::default()
|
let entries = Post::get_recents_for_blog(&*conn, &blog, 15).ok()?;
|
||||||
.title(blog.title.clone())
|
let uri = Instance::get_local()
|
||||||
.id(Instance::get_local()
|
|
||||||
.ok()?
|
.ok()?
|
||||||
.compute_box("~", &name, "atom.xml"))
|
.compute_box("~", &name, "atom.xml");
|
||||||
.entries(
|
let title = &blog.title;
|
||||||
Post::get_recents_for_blog(&*conn, &blog, 15)
|
let default_updated = &blog.creation_date;
|
||||||
.ok()?
|
let feed = super::build_atom_feed(entries, &uri, title, default_updated, conn);
|
||||||
.into_iter()
|
|
||||||
.map(|p| super::post_to_atom(p, &*conn))
|
|
||||||
.collect::<Vec<Entry>>(),
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
.ok()?;
|
|
||||||
Some(Content(
|
Some(Content(
|
||||||
ContentType::new("application", "atom+xml"),
|
ContentType::new("application", "atom+xml"),
|
||||||
feed.to_string(),
|
feed.to_string(),
|
||||||
|
47
src/routes/mod.rs
Normal file → Executable file
47
src/routes/mod.rs
Normal file → Executable file
@ -1,6 +1,9 @@
|
|||||||
#![warn(clippy::too_many_arguments)]
|
#![warn(clippy::too_many_arguments)]
|
||||||
use crate::template_utils::Ructe;
|
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 plume_models::{posts::Post, Connection, CONFIG, ITEMS_PER_PAGE};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
http::{
|
http::{
|
||||||
@ -115,13 +118,46 @@ pub struct RemoteForm {
|
|||||||
pub remote: String,
|
pub remote: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_to_atom(post: Post, conn: &Connection) -> Entry {
|
pub fn build_atom_feed(
|
||||||
|
entries: Vec<Post>,
|
||||||
|
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::<Vec<Entry>>(),
|
||||||
|
)
|
||||||
|
.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()
|
EntryBuilder::default()
|
||||||
.title(format!("<![CDATA[{}]]>", post.title))
|
.title(format!("<![CDATA[{}]]>", post.title))
|
||||||
.content(
|
.content(
|
||||||
ContentBuilder::default()
|
ContentBuilder::default()
|
||||||
.value(format!("<![CDATA[{}]]>", *post.content.get()))
|
.value(format!("<![CDATA[{}]]>", *post.content.get()))
|
||||||
.src(post.ap_url.clone())
|
|
||||||
.content_type("html".to_string())
|
.content_type("html".to_string())
|
||||||
.build()
|
.build()
|
||||||
.expect("Atom feed: content error"),
|
.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
|
// 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)
|
// 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())
|
.published(formatted_creation_date.clone())
|
||||||
.id(post.id.to_string())
|
.updated(formatted_creation_date)
|
||||||
|
.id(post.ap_url.clone())
|
||||||
.links(vec![LinkBuilder::default()
|
.links(vec![LinkBuilder::default()
|
||||||
.href(post.ap_url)
|
.href(post.ap_url)
|
||||||
.build()
|
.build()
|
||||||
|
@ -2,7 +2,6 @@ use activitypub::{
|
|||||||
activity::Create,
|
activity::Create,
|
||||||
collection::{OrderedCollection, OrderedCollectionPage},
|
collection::{OrderedCollection, OrderedCollectionPage},
|
||||||
};
|
};
|
||||||
use atom_syndication::{Entry, FeedBuilder};
|
|
||||||
use diesel::SaveChangesDsl;
|
use diesel::SaveChangesDsl;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
http::{ContentType, Cookies},
|
http::{ContentType, Cookies},
|
||||||
@ -619,20 +618,13 @@ pub fn ap_followers(
|
|||||||
pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option<Content<String>> {
|
pub fn atom_feed(name: String, rockets: PlumeRocket) -> Option<Content<String>> {
|
||||||
let conn = &*rockets.conn;
|
let conn = &*rockets.conn;
|
||||||
let author = User::find_by_fqn(&rockets, &name).ok()?;
|
let author = User::find_by_fqn(&rockets, &name).ok()?;
|
||||||
let feed = FeedBuilder::default()
|
let entries = Post::get_recents_for_author(conn, &author, 15).ok()?;
|
||||||
.title(author.display_name.clone())
|
let uri = Instance::get_local()
|
||||||
.id(Instance::get_local()
|
|
||||||
.unwrap()
|
|
||||||
.compute_box("@", &name, "atom.xml"))
|
|
||||||
.entries(
|
|
||||||
Post::get_recents_for_author(conn, &author, 15)
|
|
||||||
.ok()?
|
.ok()?
|
||||||
.into_iter()
|
.compute_box("@", &name, "atom.xml");
|
||||||
.map(|p| super::post_to_atom(p, conn))
|
let title = &author.display_name;
|
||||||
.collect::<Vec<Entry>>(),
|
let default_updated = &author.creation_date;
|
||||||
)
|
let feed = super::build_atom_feed(entries, &uri, title, default_updated, conn);
|
||||||
.build()
|
|
||||||
.expect("user::atom_feed: Error building Atom feed");
|
|
||||||
Some(Content(
|
Some(Content(
|
||||||
ContentType::new("application", "atom+xml"),
|
ContentType::new("application", "atom+xml"),
|
||||||
feed.to_string(),
|
feed.to_string(),
|
||||||
|
Loading…
Reference in New Issue
Block a user