Skip to content

Commit

Permalink
Edit blogs, and add blog icons and banners (Plume-org#460)
Browse files Browse the repository at this point in the history
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 Plume-org#453
Fixes Plume-org#454
  • Loading branch information
elegaanz authored Mar 22, 2019
1 parent 6cd9c8a commit bdfad84
Show file tree
Hide file tree
Showing 41 changed files with 1,371 additions and 436 deletions.
4 changes: 4 additions & 0 deletions migrations/postgres/2019-03-06-115158_blog_images/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- This file should undo anything in `up.sql`
ALTER TABLE blogs DROP COLUMN summary_html;
ALTER TABLE blogs DROP COLUMN icon_id;
ALTER TABLE blogs DROP COLUMN banner_id;
4 changes: 4 additions & 0 deletions migrations/postgres/2019-03-06-115158_blog_images/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Your SQL goes here
ALTER TABLE blogs ADD COLUMN summary_html TEXT NOT NULL DEFAULT '';
ALTER TABLE blogs ADD COLUMN icon_id INTEGER REFERENCES medias(id) ON DELETE SET NULL DEFAULT NULL;
ALTER TABLE blogs ADD COLUMN banner_id INTEGER REFERENCES medias(id) ON DELETE SET NULL DEFAULT NULL;
33 changes: 33 additions & 0 deletions migrations/sqlite/2019-03-19-191712_blog_images/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-- This file should undo anything in `up.sql

CREATE TABLE blogs2 (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
actor_id VARCHAR NOT NULL,
title VARCHAR NOT NULL,
summary TEXT NOT NULL DEFAULT '',
outbox_url VARCHAR NOT NULL UNIQUE,
inbox_url VARCHAR NOT NULL UNIQUE,
instance_id INTEGER REFERENCES instances(id) ON DELETE CASCADE NOT NULL,
creation_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
ap_url text not null default '' UNIQUE,
private_key TEXT,
public_key TEXT NOT NULL DEFAULT '',
fqn TEXT NOT NULL DEFAULT '',
CONSTRAINT blog_unique UNIQUE (actor_id, instance_id)
);
INSERT INTO blogs2 SELECT
id,
actor_id,
title,
summary,
outbox_url,
inbox_url,
instance_id,
creation_date,
ap_url,
private_key,
public_key,
fqn
FROM blogs;
DROP TABLE blogs;
ALTER TABLE blogs2 RENAME TO blogs;
4 changes: 4 additions & 0 deletions migrations/sqlite/2019-03-19-191712_blog_images/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Your SQL goes here
ALTER TABLE blogs ADD COLUMN summary_html TEXT NOT NULL DEFAULT '';
ALTER TABLE blogs ADD COLUMN icon_id INTEGER REFERENCES medias(id) ON DELETE SET NULL DEFAULT NULL;
ALTER TABLE blogs ADD COLUMN banner_id INTEGER REFERENCES medias(id) ON DELETE SET NULL DEFAULT NULL;
4 changes: 3 additions & 1 deletion plume-cli/src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ pub fn run<'a>(args: &ArgMatches<'a>, conn: &Connection) {
}

fn init<'a>(args: &ArgMatches<'a>, conn: &Connection) {
let path = args.value_of("path").map(|p| Path::new(p).join("search_index"))
let path = args
.value_of("path")
.map(|p| Path::new(p).join("search_index"))
.unwrap_or_else(|| Path::new(&CONFIG.search_index).to_path_buf());
let force = args.is_present("force");

Expand Down
52 changes: 49 additions & 3 deletions plume-common/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,22 @@ enum State {
Ready,
}

fn to_inline(tag: Tag) -> Tag {
match tag {
Tag::Header(_) | Tag::Table(_) | Tag::TableHead | Tag::TableRow | Tag::TableCell => {
Tag::Paragraph
}
Tag::Image(url, title) => Tag::Link(url, title),
t => t,
}
}

/// Returns (HTML, mentions, hashtags)
pub fn md_to_html(md: &str, base_url: &str) -> (String, HashSet<String>, HashSet<String>) {
pub fn md_to_html(
md: &str,
base_url: &str,
inline: bool,
) -> (String, HashSet<String>, HashSet<String>) {
let parser = Parser::new_ext(md, Options::all());

let (parser, mentions, hashtags): (Vec<Event>, Vec<String>, Vec<String>) = parser
Expand All @@ -69,6 +83,26 @@ pub fn md_to_html(md: &str, base_url: &str) -> (String, HashSet<String>, HashSet
Some(res)
})
.flat_map(|v| v.into_iter())
// Ignore headings, images, and tables if inline = true
.scan(vec![], |state: &mut Vec<Tag>, evt| {
if inline {
let new_evt = match evt {
Event::Start(t) => {
let tag = to_inline(t);
state.push(tag.clone());
Event::Start(tag)
}
Event::End(t) => match state.pop() {
Some(other) => Event::End(other),
None => Event::End(t),
},
e => e,
};
Some(new_evt)
} else {
Some(evt)
}
})
.map(|evt| match evt {
Event::Text(txt) => {
let (evts, _, _, _, new_mentions, new_hashtags) = txt.chars().fold(
Expand Down Expand Up @@ -239,7 +273,7 @@ mod tests {

for (md, mentions) in tests {
assert_eq!(
md_to_html(md, "").1,
md_to_html(md, "", false).1,
mentions
.into_iter()
.map(|s| s.to_string())
Expand All @@ -264,12 +298,24 @@ mod tests {

for (md, mentions) in tests {
assert_eq!(
md_to_html(md, "").2,
md_to_html(md, "", false).2,
mentions
.into_iter()
.map(|s| s.to_string())
.collect::<HashSet<String>>()
);
}
}

#[test]
fn test_inline() {
assert_eq!(
md_to_html("# Hello", "", false).0,
String::from("<h1>Hello</h1>\n")
);
assert_eq!(
md_to_html("# Hello", "", true).0,
String::from("<p>Hello</p>\n")
);
}
}
104 changes: 100 additions & 4 deletions plume-models/src/blogs.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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;
Expand All @@ -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,
Expand All @@ -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)]
Expand All @@ -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 = "~";
Expand Down Expand Up @@ -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 {
Expand All @@ -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())?;
Expand All @@ -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();
Expand Down Expand Up @@ -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))?;
Expand Down
1 change: 1 addition & 0 deletions plume-models/src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ impl Comment {
let (html, mentions, _hashtags) = utils::md_to_html(
self.content.get().as_ref(),
&Instance::get_local(conn)?.public_domain,
true,
);

let author = User::get(conn, self.author_id)?;
Expand Down
Loading

0 comments on commit bdfad84

Please sign in to comment.