Edit blogs, and add blog icons and banners (#460)

Also adds a parameter to `md_to_html` to only render inline elements (so that we don't have titles or images in blog descriptions). And moves the delete button for the blog on the edition page.

I still have to update the SQLite migration once others PRs with migrations will be merged.

Also, there will be a problem when you edit a blog while not owning its banner or icon: when validating they will be reset to their default values… I don't see a good solution to this until we have a better way to handle uploads with Rocket (the same is probably happening for articles btw).

And the icon/banner are not federated yet, I don't know if I should add it to this PR or if it can come after?

![image](https://user-images.githubusercontent.com/16254623/53894510-7d853300-4030-11e9-8a2c-f5c0b0c7f512.png)
![image](https://user-images.githubusercontent.com/16254623/53894539-8b3ab880-4030-11e9-8113-685a27be8d7c.png)

Fixes #453
Fixes #454
This commit is contained in:
Baptiste Gelez
2019-03-22 19:51:36 +01:00
committed by GitHub
parent 6cd9c8a01a
commit bdfad844d7
41 changed files with 1391 additions and 456 deletions
+100 -4
View File
@@ -1,4 +1,4 @@
use activitypub::{actor::Group, collection::OrderedCollection, CustomObject};
use activitypub::{actor::Group, collection::OrderedCollection, object::Image, CustomObject};
use chrono::NaiveDateTime;
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl};
use openssl::{
@@ -16,10 +16,11 @@ use url::Url;
use webfinger::*;
use instance::*;
use medias::Media;
use plume_common::activity_pub::{
ap_accept_header,
inbox::{Deletable, WithInbox},
sign, ActivityStream, ApSignature, Id, IntoId, PublicKey,
sign, ActivityStream, ApSignature, Id, IntoId, PublicKey, Source,
};
use posts::Post;
use safe_string::SafeString;
@@ -31,6 +32,7 @@ use {Connection, Error, Result, CONFIG};
pub type CustomGroup = CustomObject<ApSignature, Group>;
#[derive(Queryable, Identifiable, Clone, AsChangeset)]
#[changeset_options(treat_none_as_null = "true")]
pub struct Blog {
pub id: i32,
pub actor_id: String,
@@ -44,6 +46,9 @@ pub struct Blog {
pub private_key: Option<String>,
pub public_key: String,
pub fqn: String,
pub summary_html: SafeString,
pub icon_id: Option<i32>,
pub banner_id: Option<i32>,
}
#[derive(Default, Insertable)]
@@ -58,6 +63,9 @@ pub struct NewBlog {
pub ap_url: String,
pub private_key: Option<String>,
pub public_key: String,
pub summary_html: SafeString,
pub icon_id: Option<i32>,
pub banner_id: Option<i32>,
}
const BLOG_PREFIX: &str = "~";
@@ -189,6 +197,39 @@ impl Blog {
},
)
})?;
let icon_id = acct
.object
.object_props
.icon_image()
.ok()
.and_then(|icon| {
let owner: String = icon.object_props.attributed_to_link::<Id>().ok()?.into();
Media::save_remote(
conn,
icon.object_props.url_string().ok()?,
&User::from_url(conn, &owner).ok()?,
)
.ok()
})
.map(|m| m.id);
let banner_id = acct
.object
.object_props
.image_image()
.ok()
.and_then(|banner| {
let owner: String = banner.object_props.attributed_to_link::<Id>().ok()?.into();
Media::save_remote(
conn,
banner.object_props.url_string().ok()?,
&User::from_url(conn, &owner).ok()?,
)
.ok()
})
.map(|m| m.id);
Blog::insert(
conn,
NewBlog {
@@ -204,11 +245,14 @@ impl Blog {
.public_key_publickey()?
.public_key_pem_string()?,
private_key: None,
banner_id,
icon_id,
summary_html: SafeString::new(&acct.object.object_props.summary_string()?),
},
)
}
pub fn to_activity(&self, _conn: &Connection) -> Result<CustomGroup> {
pub fn to_activity(&self, conn: &Connection) -> Result<CustomGroup> {
let mut blog = Group::default();
blog.ap_actor_props
.set_preferred_username_string(self.actor_id.clone())?;
@@ -217,7 +261,47 @@ impl Blog {
.set_outbox_string(self.outbox_url.clone())?;
blog.ap_actor_props
.set_inbox_string(self.inbox_url.clone())?;
blog.object_props.set_summary_string(self.summary.clone())?;
blog.object_props
.set_summary_string(self.summary_html.to_string())?;
blog.ap_object_props.set_source_object(Source {
content: self.summary.clone(),
media_type: String::from("text/markdown"),
})?;
let mut icon = Image::default();
icon.object_props.set_url_string(
self.icon_id
.and_then(|id| Media::get(conn, id).and_then(|m| m.url(conn)).ok())
.unwrap_or_default(),
)?;
icon.object_props.set_attributed_to_link(
self.icon_id
.and_then(|id| {
Media::get(conn, id)
.and_then(|m| Ok(User::get(conn, m.owner_id)?.into_id()))
.ok()
})
.unwrap_or_else(|| Id::new(String::new())),
)?;
blog.object_props.set_icon_object(icon)?;
let mut banner = Image::default();
banner.object_props.set_url_string(
self.banner_id
.and_then(|id| Media::get(conn, id).and_then(|m| m.url(conn)).ok())
.unwrap_or_default(),
)?;
banner.object_props.set_attributed_to_link(
self.banner_id
.and_then(|id| {
Media::get(conn, id)
.and_then(|m| Ok(User::get(conn, m.owner_id)?.into_id()))
.ok()
})
.unwrap_or_else(|| Id::new(String::new())),
)?;
blog.object_props.set_image_object(banner)?;
blog.object_props.set_id_string(self.ap_url.clone())?;
let mut public_key = PublicKey::default();
@@ -296,6 +380,18 @@ impl Blog {
})
}
pub fn icon_url(&self, conn: &Connection) -> String {
self.icon_id
.and_then(|id| Media::get(conn, id).and_then(|m| m.url(conn)).ok())
.unwrap_or_else(|| "/static/default-avatar.png".to_string())
}
pub fn banner_url(&self, conn: &Connection) -> Option<String> {
self.banner_id
.and_then(|i| Media::get(conn, i).ok())
.and_then(|c| c.url(conn).ok())
}
pub fn delete(&self, conn: &Connection, searcher: &Searcher) -> Result<()> {
for post in Post::get_for_blog(conn, &self)? {
post.delete(&(conn, searcher))?;