Federate stickied posts (ref #647)
This commit is contained in:
parent
68bcc26ff6
commit
fb2b1bea32
5 changed files with 121 additions and 12 deletions
|
@ -17,6 +17,7 @@ use crate::{
|
|||
activity::insert_activity,
|
||||
community::{Community, CommunityForm},
|
||||
community_view::{CommunityFollowerView, CommunityModeratorView},
|
||||
post::Post,
|
||||
user::User_,
|
||||
},
|
||||
naive_now,
|
||||
|
@ -29,6 +30,7 @@ use activitystreams::{
|
|||
context,
|
||||
endpoint::EndpointProperties,
|
||||
object::properties::ObjectProperties,
|
||||
primitives::{XsdAnyUri, XsdAnyUriError, XsdString},
|
||||
Activity,
|
||||
Base,
|
||||
BaseBox,
|
||||
|
@ -40,6 +42,7 @@ use diesel::PgConnection;
|
|||
use failure::{Error, _core::fmt::Debug};
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CommunityQuery {
|
||||
|
@ -92,7 +95,12 @@ impl ToApub for Community {
|
|||
.set_endpoints(endpoint_props)?
|
||||
.set_followers(self.get_followers_url())?;
|
||||
|
||||
let group_extension = GroupExtension::new(conn, self.category_id, self.nsfw)?;
|
||||
let group_extension = GroupExtension::new(
|
||||
conn,
|
||||
self.category_id,
|
||||
self.nsfw,
|
||||
self.featured_collection_address()?,
|
||||
)?;
|
||||
|
||||
Ok(Ext3::new(
|
||||
group,
|
||||
|
@ -124,6 +132,14 @@ impl ActorType for Community {
|
|||
self.private_key.to_owned().unwrap()
|
||||
}
|
||||
|
||||
fn send_follow(&self, _follow_actor_id: &str, _conn: &PgConnection) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn send_unfollow(&self, _follow_actor_id: &str, _conn: &PgConnection) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// As a local community, accept the follow request from a remote user.
|
||||
fn send_accept_follow(&self, follow: &Follow, conn: &PgConnection) -> Result<(), Error> {
|
||||
let actor_uri = follow.actor.as_single_xsd_any_uri().unwrap().to_string();
|
||||
|
@ -288,14 +304,6 @@ impl ActorType for Community {
|
|||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn send_follow(&self, _follow_actor_id: &str, _conn: &PgConnection) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn send_unfollow(&self, _follow_actor_id: &str, _conn: &PgConnection) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromApub for CommunityForm {
|
||||
|
@ -371,13 +379,40 @@ pub async fn get_apub_community_followers(
|
|||
let oprops: &mut ObjectProperties = collection.as_mut();
|
||||
oprops
|
||||
.set_context_xsd_any_uri(context())?
|
||||
.set_id(community.actor_id)?;
|
||||
.set_id(community.get_followers_url())?;
|
||||
collection
|
||||
.collection_props
|
||||
.set_total_items(community_followers.len() as u64)?;
|
||||
Ok(create_apub_response(&collection))
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub async fn get_featured_posts(
|
||||
info: Path<CommunityQuery>,
|
||||
db: DbPoolParam,
|
||||
) -> Result<HttpResponse<Body>, Error> {
|
||||
let community = Community::read_from_name(&&db.get()?, &info.community_name)?;
|
||||
|
||||
let conn = db.get()?;
|
||||
|
||||
let featured_posts = Post::list_stickied_for_community(&conn, community.id)?
|
||||
.iter()
|
||||
// TODO: should probably be XsdAnyUri but collection doesnt have a setter for that
|
||||
.map(|p| XsdString::from_str(&p.ap_id))
|
||||
.collect::<Result<Vec<_>, std::convert::Infallible>>()?;
|
||||
|
||||
let mut collection = UnorderedCollection::default();
|
||||
let oprops: &mut ObjectProperties = collection.as_mut();
|
||||
oprops
|
||||
.set_context_xsd_any_uri(context())?
|
||||
.set_id(community.featured_collection_address()?)?;
|
||||
collection
|
||||
.collection_props
|
||||
.set_total_items(featured_posts.len() as u64)?
|
||||
.set_many_items_xsd_strings(featured_posts)?;
|
||||
Ok(create_apub_response(&collection))
|
||||
}
|
||||
|
||||
impl Community {
|
||||
pub fn do_announce<A>(
|
||||
activity: A,
|
||||
|
@ -411,4 +446,7 @@ impl Community {
|
|||
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
||||
fn featured_collection_address(&self) -> Result<XsdAnyUri, XsdAnyUriError> {
|
||||
XsdAnyUri::from_str(&format!("{}{}", self.actor_id, "/collections/featured"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
use crate::db::{category::Category, Crud};
|
||||
use activitystreams::{ext::Extension, Actor};
|
||||
use activitystreams::{ext::Extension, primitives::XsdAnyUri, Actor};
|
||||
use diesel::PgConnection;
|
||||
use failure::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// TODO: need to specify these fields in the context
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GroupExtension {
|
||||
pub category: GroupCategory,
|
||||
pub sensitive: bool,
|
||||
pub featured: XsdAnyUri,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
|
@ -24,6 +27,7 @@ impl GroupExtension {
|
|||
conn: &PgConnection,
|
||||
category_id: i32,
|
||||
sensitive: bool,
|
||||
featured: XsdAnyUri,
|
||||
) -> Result<GroupExtension, Error> {
|
||||
let category = Category::read(conn, category_id)?;
|
||||
let group_category = GroupCategory {
|
||||
|
@ -33,6 +37,7 @@ impl GroupExtension {
|
|||
Ok(GroupExtension {
|
||||
category: group_category,
|
||||
sensitive,
|
||||
featured,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ use crate::{
|
|||
|
||||
use crate::{
|
||||
apub::{
|
||||
extensions::group_extensions::GroupExtension,
|
||||
get_apub_protocol_string,
|
||||
is_apub_id_valid,
|
||||
FromApub,
|
||||
|
@ -38,6 +39,7 @@ use crate::{
|
|||
},
|
||||
db::user_view::UserView,
|
||||
};
|
||||
use activitystreams::{collection::UnorderedCollection, primitives::XsdString};
|
||||
use chrono::NaiveDateTime;
|
||||
|
||||
static ACTOR_REFETCH_INTERVAL_SECONDS: i64 = 24 * 60 * 60;
|
||||
|
@ -215,7 +217,9 @@ pub fn get_or_fetch_and_upsert_remote_community(
|
|||
let group = fetch_remote_object::<GroupExt>(&Url::parse(apub_id)?)?;
|
||||
let mut cf = CommunityForm::from_apub(&group, conn)?;
|
||||
cf.last_refreshed_at = Some(naive_now());
|
||||
Ok(Community::update(&conn, c.id, &cf)?)
|
||||
let community = Community::update(&conn, c.id, &cf)?;
|
||||
fetch_community_stickied_posts(&group.ext_one, community.id, &conn)?;
|
||||
Ok(community)
|
||||
} else {
|
||||
Ok(c)
|
||||
}
|
||||
|
@ -225,6 +229,7 @@ pub fn get_or_fetch_and_upsert_remote_community(
|
|||
let group = fetch_remote_object::<GroupExt>(&Url::parse(apub_id)?)?;
|
||||
let cf = CommunityForm::from_apub(&group, conn)?;
|
||||
let community = Community::create(conn, &cf)?;
|
||||
fetch_community_stickied_posts(&group.ext_one, community.id, &conn)?;
|
||||
|
||||
// Also add the community moderators too
|
||||
let creator_and_moderator_uris = group
|
||||
|
@ -250,6 +255,28 @@ pub fn get_or_fetch_and_upsert_remote_community(
|
|||
}
|
||||
}
|
||||
|
||||
fn fetch_community_stickied_posts(
|
||||
group_ext: &GroupExtension,
|
||||
community_id: i32,
|
||||
conn: &PgConnection,
|
||||
) -> Result<(), Error> {
|
||||
let featured =
|
||||
fetch_remote_object::<UnorderedCollection>(&Url::parse(group_ext.featured.as_str())?)?;
|
||||
let featured_items = featured
|
||||
.collection_props
|
||||
.get_many_items_xsd_strings()
|
||||
.map(|o| o.collect::<Vec<_>>())
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|i: &&XsdString| -> Result<String, Error> {
|
||||
get_or_fetch_and_insert_remote_post(i.as_str(), &conn)?;
|
||||
Ok(i.as_str().to_string())
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
Post::set_stickied_for_community(&conn, community_id, featured_items)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn upsert_post(post_form: &PostForm, conn: &PgConnection) -> Result<Post, Error> {
|
||||
let existing = Post::read_from_apub_id(conn, &post_form.ap_id);
|
||||
match existing {
|
||||
|
|
|
@ -70,6 +70,41 @@ impl Post {
|
|||
.load::<Self>(conn)
|
||||
}
|
||||
|
||||
pub fn list_stickied_for_community(
|
||||
conn: &PgConnection,
|
||||
the_community_id: i32,
|
||||
) -> Result<Vec<Self>, Error> {
|
||||
use crate::schema::post::dsl::*;
|
||||
post
|
||||
.filter(community_id.eq(the_community_id))
|
||||
.filter(stickied.eq(true))
|
||||
.load::<Self>(conn)
|
||||
}
|
||||
|
||||
pub fn set_stickied_for_community(
|
||||
conn: &PgConnection,
|
||||
the_community_id: i32,
|
||||
stickied_items: Vec<String>,
|
||||
) -> Result<(), Error> {
|
||||
use crate::schema::post::dsl::*;
|
||||
// TODO: surely there is a better way to do this
|
||||
diesel::update(post)
|
||||
.filter(community_id.eq(the_community_id))
|
||||
.set(stickied.eq(false))
|
||||
.execute(conn)?;
|
||||
stickied_items
|
||||
.iter()
|
||||
.map(|i| {
|
||||
diesel::update(post)
|
||||
.filter(community_id.eq(the_community_id))
|
||||
.filter(ap_id.eq(i))
|
||||
.set(stickied.eq(true))
|
||||
.execute(conn)
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error> {
|
||||
use crate::schema::post::dsl::*;
|
||||
post.filter(ap_id.eq(object_id)).first::<Self>(conn)
|
||||
|
|
|
@ -28,6 +28,10 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
|||
"/c/{community_name}/followers",
|
||||
web::get().to(get_apub_community_followers),
|
||||
)
|
||||
.route(
|
||||
"/c/{community_name}/collections/featured",
|
||||
web::get().to(get_featured_posts),
|
||||
)
|
||||
// TODO This is only useful for history which we aren't doing right now
|
||||
// .route(
|
||||
// "/c/{community_name}/outbox",
|
||||
|
|
Loading…
Reference in a new issue