From 0ac3cb4c0f41a68d5ccb33949da6c63992037909 Mon Sep 17 00:00:00 2001 From: Bat Date: Sat, 19 May 2018 10:50:56 +0100 Subject: [PATCH 01/33] Fix AP link serialization --- src/activity_pub/mod.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/activity_pub/mod.rs b/src/activity_pub/mod.rs index 35e5ee5e..f544d15e 100644 --- a/src/activity_pub/mod.rs +++ b/src/activity_pub/mod.rs @@ -103,16 +103,11 @@ pub fn broadcast>(id: T) -> Id { - Id { - id: id.into() - } + Id(id.into()) } } From 63eb1a7e9810208199781b2f3b05fe597861e652 Mon Sep 17 00:00:00 2001 From: Bat Date: Sat, 19 May 2018 10:51:10 +0100 Subject: [PATCH 02/33] Add a route to reshare posts --- src/main.rs | 2 ++ src/models/reshares.rs | 66 +++++++++++++++++++++++++++++++++++++----- src/models/users.rs | 11 +++++++ src/routes/mod.rs | 1 + src/routes/reshares.rs | 31 ++++++++++++++++++++ 5 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 src/routes/reshares.rs diff --git a/src/main.rs b/src/main.rs index cbd27b47..9663b379 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,6 +90,8 @@ fn main() { routes::posts::new_auth, routes::posts::create, + routes::reshares::create, + routes::session::new, routes::session::create, routes::session::delete, diff --git a/src/models/reshares.rs b/src/models/reshares.rs index 0ad8f464..8d50ff5b 100644 --- a/src/models/reshares.rs +++ b/src/models/reshares.rs @@ -1,23 +1,26 @@ +use activitystreams_types::activity; use chrono::NaiveDateTime; use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; +use activity_pub::{IntoId, actor::Actor, object::Object}; +use models::{posts::Post, users::User}; use schema::reshares; #[derive(Serialize, Deserialize, Queryable, Identifiable)] pub struct Reshare { - id: i32, - user_id: i32, - post_id: i32, - ap_url: String, - creation_date: NaiveDateTime + pub id: i32, + pub user_id: i32, + pub post_id: i32, + pub ap_url: String, + pub creation_date: NaiveDateTime } #[derive(Insertable)] #[table_name = "reshares"] pub struct NewReshare { - user_id: i32, - post_id: i32, - ap_url: String + pub user_id: i32, + pub post_id: i32, + pub ap_url: String } impl Reshare { @@ -35,4 +38,51 @@ impl Reshare { .expect("Could'nt load reshare") .into_iter().nth(0) } + + pub fn update_ap_url(&self, conn: &PgConnection) { + if self.ap_url.len() == 0 { + diesel::update(self) + .set(reshares::ap_url.eq(format!( + "{}/reshare/{}", + User::get(conn, self.user_id).unwrap().compute_id(conn), + Post::get(conn, self.post_id).unwrap().compute_id(conn) + ))) + .get_result::(conn).expect("Couldn't update AP URL"); + } + } + + pub fn find_by_ap_url(conn: &PgConnection, ap_url: String) -> Option { + reshares::table.filter(reshares::ap_url.eq(ap_url)) + .limit(1) + .load::(conn) + .expect("Error loading reshare by AP URL") + .into_iter().nth(0) + } + + pub fn find_by_user_on_post(conn: &PgConnection, user: &User, post: &Post) -> Option { + reshares::table.filter(reshares::post_id.eq(post.id)) + .filter(reshares::user_id.eq(user.id)) + .limit(1) + .load::(conn) + .expect("Error loading reshare for user and post") + .into_iter().nth(0) + } + + pub fn delete(&self, conn: &PgConnection) -> activity::Undo { + diesel::delete(self).execute(conn).unwrap(); + + let mut act = activity::Undo::default(); + act.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); + act.set_object_object(self.into_activity(conn)).unwrap(); + act + } + + pub fn into_activity(&self, conn: &PgConnection) -> activity::Announce { + let mut act = activity::Announce::default(); + act.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); + act.set_object_link(Post::get(conn, self.post_id).unwrap().into_id()).unwrap(); + act.object_props.set_id_string(self.ap_url.clone()).unwrap(); + + act + } } diff --git a/src/models/users.rs b/src/models/users.rs index 92b85715..3a38de9c 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -287,6 +287,17 @@ impl User { .len() > 0 } + pub fn has_reshared(&self, conn: &PgConnection, post: &Post) -> bool { + use schema::reshares; + use models::reshares::Reshare; + reshares::table + .filter(reshares::post_id.eq(post.id)) + .filter(reshares::user_id.eq(self.id)) + .load::(conn) + .expect("Couldn't load reshares") + .len() > 0 + } + pub fn get_keypair(&self) -> PKey { PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap() } diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 60946b8a..501c774c 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -7,6 +7,7 @@ pub mod instance; pub mod likes; pub mod notifications; pub mod posts; +pub mod reshares; pub mod session; pub mod user; pub mod well_known; diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs new file mode 100644 index 00000000..7cf74d9d --- /dev/null +++ b/src/routes/reshares.rs @@ -0,0 +1,31 @@ +use rocket::response::Redirect; + +use activity_pub::broadcast; +use db_conn::DbConn; +use models::{ + posts::Post, + reshares::*, + users::User +}; + +#[get("/~///reshare")] +fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { + let post = Post::find_by_slug(&*conn, slug.clone()).unwrap(); + + if !user.has_reshared(&*conn, &post) { + let reshare = Reshare::insert(&*conn, NewReshare { + post_id: post.id, + user_id: user.id, + ap_url: "".to_string() + }); + reshare.update_ap_url(&*conn); + + broadcast(&*conn, &user, reshare.into_activity(&*conn), user.get_followers(&*conn)); + } else { + let reshare = Reshare::find_by_user_on_post(&*conn, &user, &post).unwrap(); + let delete_act = reshare.delete(&*conn); + broadcast(&*conn, &user, delete_act, user.get_followers(&*conn)); + } + + Redirect::to(format!("/~/{}/{}/", blog, slug).as_ref()) +} From 9b98a45f2e94835dce13a79c93603a43253546a8 Mon Sep 17 00:00:00 2001 From: Bat Date: Sat, 19 May 2018 10:57:39 +0100 Subject: [PATCH 03/33] Add a button to reshare --- src/models/posts.rs | 8 ++++++++ src/routes/posts.rs | 2 ++ templates/posts/details.tera | 11 +++++++++++ 3 files changed, 21 insertions(+) diff --git a/src/models/posts.rs b/src/models/posts.rs index dcb94837..cb3b4e3c 100644 --- a/src/models/posts.rs +++ b/src/models/posts.rs @@ -17,6 +17,7 @@ use models::{ blogs::Blog, likes::Like, post_authors::PostAuthor, + reshares::Reshare, users::User }; use schema::posts; @@ -127,6 +128,13 @@ impl Post { .expect("Couldn't load likes associted to post") } + pub fn get_reshares(&self, conn: &PgConnection) -> Vec { + use schema::reshares; + reshares::table.filter(reshares::post_id.eq(self.id)) + .load::(conn) + .expect("Couldn't load reshares associted to post") + } + pub fn update_ap_url(&self, conn: &PgConnection) { if self.ap_url.len() == 0 { diesel::update(self) diff --git a/src/routes/posts.rs b/src/routes/posts.rs index e213c869..53edda5c 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -39,6 +39,8 @@ fn details(blog: String, slug: String, conn: DbConn, user: Option) -> Temp }).collect::>(), "n_likes": post.get_likes(&*conn).len(), "has_liked": user.clone().map(|u| u.has_liked(&*conn, &post)).unwrap_or(false), + "n_reshares": post.get_reshares(&*conn).len(), + "has_reshared": user.clone().map(|u| u.has_reshared(&*conn, &post)).unwrap_or(false), "account": user, "date": &post.creation_date.timestamp() })) diff --git a/templates/posts/details.tera b/templates/posts/details.tera index eeacafb9..5c658d37 100644 --- a/templates/posts/details.tera +++ b/templates/posts/details.tera @@ -33,6 +33,17 @@ Add yours {% endif %} + +

+ {{ n_reshares }} reshare{{ n_reshares | pluralize }} +

+ + {% if has_reshared %} + I don't want to reshare this anymore + {% else %} + Reshare + {% endif %} +

Comments

From a8aeb40b957b9fa22aaf53070d29abe67a401232 Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Tue, 22 May 2018 17:35:16 +0200 Subject: [PATCH 04/33] add trailing slashes to links where they were missing Signed-off-by: Trinity Pointard --- src/models/posts.rs | 2 +- src/routes/blogs.rs | 2 +- src/routes/posts.rs | 2 +- src/routes/user.rs | 6 +++--- templates/notifications/index.tera | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/models/posts.rs b/src/models/posts.rs index dcb94837..35f08b30 100644 --- a/src/models/posts.rs +++ b/src/models/posts.rs @@ -183,7 +183,7 @@ impl IntoId for Post { impl Object for Post { fn compute_id(&self, conn: &PgConnection) -> String { - ap_url(format!("{}/~/{}/{}", BASE_URL.as_str(), self.get_blog(conn).actor_id, self.slug)) + ap_url(format!("{}/~/{}/{}/", BASE_URL.as_str(), self.get_blog(conn).actor_id, self.slug)) } fn serialize(&self, conn: &PgConnection) -> serde_json::Value { diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 08fea83b..2df26cf5 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -77,7 +77,7 @@ fn create(conn: DbConn, data: Form, user: User) -> Redirect { is_owner: true }); - Redirect::to(format!("/~/{}", slug).as_str()) + Redirect::to(format!("/~/{}/", slug).as_str()) } #[get("/~//outbox")] diff --git a/src/routes/posts.rs b/src/routes/posts.rs index e213c869..61a917f4 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -96,5 +96,5 @@ fn create(blog_name: String, data: Form, user: User, conn: DbConn) let act = post.create_activity(&*conn); broadcast(&*conn, &user, act, user.get_followers(&*conn)); - Redirect::to(format!("/~/{}/{}", blog_name, slug).as_str()) + Redirect::to(format!("/~/{}/{}/", blog_name, slug).as_str()) } diff --git a/src/routes/user.rs b/src/routes/user.rs index 807168a8..6f4bb615 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -21,7 +21,7 @@ use models::{ #[get("/me")] fn me(user: User) -> Redirect { - Redirect::to(format!("/@/{}", user.username).as_ref()) + Redirect::to(format!("/@/{}/", user.username).as_ref()) } #[get("/@/", rank = 2)] @@ -64,7 +64,7 @@ fn follow(name: String, conn: DbConn, user: User) -> Redirect { act.set_object_object(user.into_activity(&*conn)).unwrap(); act.object_props.set_id_string(format!("{}/follow/{}", user.ap_url, target.ap_url)).unwrap(); broadcast(&*conn, &user, act, vec![target]); - Redirect::to(format!("/@/{}", name).as_ref()) + Redirect::to(format!("/@/{}/", name).as_ref()) } #[get("/@//followers", rank = 2)] @@ -155,7 +155,7 @@ fn create(conn: DbConn, data: Form) -> Result { User::hash_pass(form.password.to_string()), inst.id )).update_boxes(&*conn); - Ok(Redirect::to(format!("/@/{}", data.get().username).as_str())) + Ok(Redirect::to(format!("/@/{}/", data.get().username).as_str())) } else { Err(String::from("Passwords don't match")) } diff --git a/templates/notifications/index.tera b/templates/notifications/index.tera index 3af0de19..d25dedb0 100644 --- a/templates/notifications/index.tera +++ b/templates/notifications/index.tera @@ -9,7 +9,7 @@ Notifications
{% for notification in notifications %}
-

{{ notification.title }}

+

{{ notification.title }}

{% if notification.content %}

{{ notification.content }}

{% endif %} From c9b231fae188c38e075eafd96e036bdb2757c359 Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Tue, 22 May 2018 17:52:53 +0200 Subject: [PATCH 05/33] add trailing / to followers url Signed-off-by: Trinity Pointard --- templates/users/followers.tera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/users/followers.tera b/templates/users/followers.tera index 932be28c..ed6a8244 100644 --- a/templates/users/followers.tera +++ b/templates/users/followers.tera @@ -22,7 +22,7 @@
{% for follower in followers %}
-

{{ follower.display_name }} — @{{ follower.fqn }}

+

{{ follower.display_name }} — @{{ follower.fqn }}

{{ follower.summary }}

{% endfor %} From c0d1a914c497ebb89986ba72b4c4fc528d7ae620 Mon Sep 17 00:00:00 2001 From: Bat Date: Wed, 23 May 2018 18:09:59 +0100 Subject: [PATCH 06/33] Handle reshares from AP --- src/activity_pub/inbox.rs | 15 ++++++++++++++- src/models/users.rs | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/activity_pub/inbox.rs b/src/activity_pub/inbox.rs index e6629119..6b2c624a 100644 --- a/src/activity_pub/inbox.rs +++ b/src/activity_pub/inbox.rs @@ -1,7 +1,7 @@ use activitystreams_traits::Actor; use activitystreams_types::{ actor::Person, - activity::{Accept, Create, Follow, Like, Undo}, + activity::{Accept, Announce, Create, Follow, Like, Undo}, object::{Article, Note} }; use diesel::PgConnection; @@ -19,6 +19,7 @@ use models::{ follows, likes, posts::*, + reshares::*, users::User }; @@ -94,10 +95,22 @@ pub trait Inbox { Ok(()) } + fn announce(&self, conn: &PgConnection, announce: Announce) -> Result<(), Error> { + let user = User::from_url(conn, announce.actor.as_str().unwrap().to_string()); + let post = Post::find_by_ap_url(conn, announce.object.as_str().unwrap().to_string()); + Reshare::insert(conn, NewReshare { + post_id: post.unwrap().id, + user_id: user.unwrap().id, + ap_url: announce.object_props.id_string()? + }); + Ok(()) + } + fn save(&self, conn: &PgConnection, act: serde_json::Value) -> Result<(), Error> { match act["type"].as_str() { Some(t) => { match t { + "Announce" => self.announce(conn, serde_json::from_value(act.clone())?), "Create" => { let act: Create = serde_json::from_value(act.clone())?; match act.object["type"].as_str().unwrap() { diff --git a/src/models/users.rs b/src/models/users.rs index 3a38de9c..dcfe2e9f 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -438,7 +438,9 @@ impl WithInbox for User { impl Inbox for User { fn received(&self, conn: &PgConnection, act: serde_json::Value) { - self.save(conn, act.clone()).unwrap(); + if let Err(err) = self.save(conn, act.clone()) { + println!("Inbox error:\n{}\n{}", err.cause(), err.backtrace()); + } // Notifications match act["type"].as_str().unwrap() { From e9cd48eccae7af7d847561a3b2c83a2fd857a956 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 24 May 2018 10:45:36 +0100 Subject: [PATCH 07/33] Display reshares on profile page --- src/models/reshares.rs | 12 ++++++++++++ src/routes/user.rs | 16 ++++++++++++++++ templates/users/details.tera | 7 +++++++ 3 files changed, 35 insertions(+) diff --git a/src/models/reshares.rs b/src/models/reshares.rs index 8d50ff5b..6d8c138e 100644 --- a/src/models/reshares.rs +++ b/src/models/reshares.rs @@ -68,6 +68,18 @@ impl Reshare { .into_iter().nth(0) } + pub fn get_recents_for_author(conn: &PgConnection, user: &User, limit: i64) -> Vec { + reshares::table.filter(reshares::user_id.eq(user.id)) + .order(reshares::creation_date.desc()) + .limit(limit) + .load::(conn) + .expect("Error loading recent reshares for user") + } + + pub fn get_post(&self, conn: &PgConnection) -> Option { + Post::get(conn, self.post_id) + } + pub fn delete(&self, conn: &PgConnection) -> activity::Undo { diesel::delete(self).execute(conn).unwrap(); diff --git a/src/routes/user.rs b/src/routes/user.rs index 6f4bb615..606b0cf5 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -16,6 +16,7 @@ use models::{ follows, instance::Instance, posts::Post, + reshares::Reshare, users::* }; @@ -28,6 +29,7 @@ fn me(user: User) -> Redirect { fn details(name: String, conn: DbConn, account: Option) -> Template { let user = User::find_by_fqn(&*conn, name).unwrap(); let recents = Post::get_recents_for_author(&*conn, &user, 5); + let reshares = Reshare::get_recents_for_author(&*conn, &user, 5); let user_id = user.id.clone(); let n_followers = user.get_followers(&*conn).len(); @@ -47,6 +49,20 @@ fn details(name: String, conn: DbConn, account: Option) -> Template { "date": p.creation_date.timestamp() }) }).collect::>(), + "reshares": reshares.into_iter().map(|r| { + let p = r.get_post(&*conn).unwrap(); + json!({ + "post": p, + "author": ({ + let author = &p.get_authors(&*conn)[0]; + let mut json = serde_json::to_value(author).unwrap(); + json["fqn"] = serde_json::Value::String(author.get_fqn(&*conn)); + json + }), + "url": format!("/~/{}/{}/", p.get_blog(&*conn).actor_id, p.slug), + "date": p.creation_date.timestamp() + }) + }).collect::>(), "is_self": account.map(|a| a.id == user_id).unwrap_or(false), "n_followers": n_followers })) diff --git a/templates/users/details.tera b/templates/users/details.tera index c78fe935..33982214 100644 --- a/templates/users/details.tera +++ b/templates/users/details.tera @@ -37,4 +37,11 @@ {{ macros::post_card(article=article) }} {% endfor %}
+ +

Recently reshared

+
+ {% for article in reshares %} + {{ macros::post_card(article=article) }} + {% endfor %} +
{% endblock content %} From 963d4138472e9b5b5bb1b8d386be91ae7695bc94 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 24 May 2018 10:49:02 +0100 Subject: [PATCH 08/33] Don't show reshares or posts on profile if there are none --- templates/users/details.tera | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/templates/users/details.tera b/templates/users/details.tera index 33982214..d225a300 100644 --- a/templates/users/details.tera +++ b/templates/users/details.tera @@ -31,17 +31,21 @@ {{ user.summary | safe }}
-

Latest articles

-
- {% for article in recents %} - {{ macros::post_card(article=article) }} - {% endfor %} -
+ {% if recents | length != 0 %} +

Latest articles

+
+ {% for article in recents %} + {{ macros::post_card(article=article) }} + {% endfor %} +
+ {% endif %} -

Recently reshared

-
- {% for article in reshares %} - {{ macros::post_card(article=article) }} - {% endfor %} -
+ {% if reshares | length != 0 %} +

Recently reshared

+
+ {% for article in reshares %} + {{ macros::post_card(article=article) }} + {% endfor %} +
+ {% endif %} {% endblock content %} From 0b098e539ff08526bcc0eec4fa6cf1a84d689484 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 24 May 2018 10:55:41 +0100 Subject: [PATCH 09/33] Remove HTML tags from preview Fixes #24 --- templates/macros.tera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/macros.tera b/templates/macros.tera index 04860487..c090fe41 100644 --- a/templates/macros.tera +++ b/templates/macros.tera @@ -1,7 +1,7 @@ {% macro post_card(article) %}

{{ article.post.title }}

-

{{ article.post.content | escape | truncate(length=200) }}…

+

{{ article.post.content | striptags | truncate(length=200) }}…

By {{ article.author.display_name }} ⋅ {{ article.date | date(format="%B %e") }}

{% endmacro post_card %} From daf9120fbabf8901231cc2592a673a10d05f97b6 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 24 May 2018 11:03:37 +0100 Subject: [PATCH 10/33] Send a notification when one of your article is reshared Fixes #14 Fixes #19 --- src/models/users.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/models/users.rs b/src/models/users.rs index dcfe2e9f..79c37ad9 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -444,6 +444,16 @@ impl Inbox for User { // Notifications match act["type"].as_str().unwrap() { + "Announce" => { + let actor = User::from_url(conn, act["actor"].as_str().unwrap().to_string()).unwrap(); + let post = Post::find_by_ap_url(conn, act["object"].as_str().unwrap().to_string()).unwrap(); + Notification::insert(conn, NewNotification { + title: format!("{} reshared your article", actor.display_name.clone()), + content: Some(post.title), + link: Some(post.ap_url), + user_id: self.id + }); + }, "Follow" => { let follower = User::from_url(conn, act["actor"].as_str().unwrap().to_string()).unwrap(); Notification::insert(conn, NewNotification { From a0b4a6eacb30891f0df865fff82471c67e58e056 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 24 May 2018 11:12:27 +0100 Subject: [PATCH 11/33] Order notifications by creation date --- .../down.sql | 2 ++ .../2018-05-24-100613_add_notifications_creation_date/up.sql | 2 ++ src/models/notifications.rs | 5 ++++- src/schema.rs | 1 + 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 migrations/2018-05-24-100613_add_notifications_creation_date/down.sql create mode 100644 migrations/2018-05-24-100613_add_notifications_creation_date/up.sql diff --git a/migrations/2018-05-24-100613_add_notifications_creation_date/down.sql b/migrations/2018-05-24-100613_add_notifications_creation_date/down.sql new file mode 100644 index 00000000..ca04e11e --- /dev/null +++ b/migrations/2018-05-24-100613_add_notifications_creation_date/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE notifications DROP COLUMN creation_date; diff --git a/migrations/2018-05-24-100613_add_notifications_creation_date/up.sql b/migrations/2018-05-24-100613_add_notifications_creation_date/up.sql new file mode 100644 index 00000000..fddcc387 --- /dev/null +++ b/migrations/2018-05-24-100613_add_notifications_creation_date/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE notifications ADD COLUMN creation_date TIMESTAMP NOT NULL DEFAULT now(); diff --git a/src/models/notifications.rs b/src/models/notifications.rs index 70ffde89..1087d5cb 100644 --- a/src/models/notifications.rs +++ b/src/models/notifications.rs @@ -1,3 +1,4 @@ +use chrono::NaiveDateTime; use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods}; use models::users::User; @@ -9,7 +10,8 @@ pub struct Notification { pub title: String, pub content: Option, pub link: Option, - pub user_id: i32 + pub user_id: i32, + pub creation_date: NaiveDateTime } #[derive(Insertable)] @@ -39,6 +41,7 @@ impl Notification { pub fn find_for_user(conn: &PgConnection, user: &User) -> Vec { notifications::table.filter(notifications::user_id.eq(user.id)) + .order_by(notifications::creation_date.desc()) .load::(conn) .expect("Couldn't load user notifications") } diff --git a/src/schema.rs b/src/schema.rs index 07b6688a..9c85dc6f 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -73,6 +73,7 @@ table! { content -> Nullable, link -> Nullable, user_id -> Int4, + creation_date -> Timestamp, } } From 93eb89bc77538d0c9021bdaba34a3f38e97e7bd1 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 24 May 2018 11:42:45 +0100 Subject: [PATCH 12/33] Markdown! Fixes #18 --- Cargo.lock | 144 ++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 1 + src/routes/posts.rs | 18 +++++- 4 files changed, 163 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 3c603262..5f5db7af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,6 +54,14 @@ dependencies = [ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "antidote" version = "1.0.0" @@ -72,6 +80,16 @@ dependencies = [ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "atty" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "backtrace" version = "0.3.6" @@ -196,6 +214,36 @@ dependencies = [ "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "clap" +version = "2.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "comrak" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", + "entities 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_derive 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "typed-arena 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cookie" version = "0.9.2" @@ -350,6 +398,11 @@ dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "entities" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "error-chain" version = "0.10.0" @@ -824,6 +877,21 @@ name = "pest" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pest" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pest_derive" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "phf" version = "0.7.22" @@ -876,6 +944,7 @@ dependencies = [ "base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "bcrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "comrak 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -995,6 +1064,14 @@ name = "redox_syscall" version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex" version = "0.2.10" @@ -1256,6 +1333,11 @@ name = "state" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "strsim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "syn" version = "0.11.11" @@ -1343,6 +1425,24 @@ dependencies = [ "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "0.3.5" @@ -1524,11 +1624,24 @@ name = "traitobject" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typeable" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "typed-arena" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "typenum" version = "1.10.0" @@ -1573,6 +1686,11 @@ name = "unicode-segmentation" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.0.4" @@ -1583,6 +1701,11 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unidecode" version = "0.3.0" @@ -1629,6 +1752,11 @@ name = "vcpkg" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "version_check" version = "0.1.3" @@ -1689,9 +1817,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum activitystreams-types 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aff9aa0c3412fe4da72a1f6e4b1c2e9792bfdf1308b709389192f17aa8e2b3cd" "checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45" "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" "checksum array_tool 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" "checksum backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe525f66f42d207968308ee86bc2dd60aa5fab535b22e616323a173d097d8e" "checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" @@ -1708,6 +1838,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8b9d2900f78631a5876dc5d6c9033ede027253efcd33dd36b1309fc6cab97ee0" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" +"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" +"checksum comrak 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "053b26c8ce23b4c505a9479beace98f95899e0bf5c5255cf0219e9b0f48cf6ea" "checksum cookie 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "477eb650753e319be2ae77ec368a58c638f9f0c4d941c39bad95e950fb1d1d0d" "checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" "checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" @@ -1724,6 +1856,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a70de3c590ce18df70743cace1cf12565637a0b26fd8b04ef10c7d33fdc66cdc" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d" +"checksum entities 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" "checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" @@ -1782,6 +1915,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pear_codegen 0.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ca34109829349aeefe22772916da5404b3f5cd0e63a72c5d91209fc809342265" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pest 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e823a5967bb4cdc6d3e46f47baaf4ecfeae44413a642b74ad44e59e49c7f6" +"checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc" +"checksum pest_derive 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ab94faafeb93f4c5e3ce81ca0e5a779529a602ad5d09ae6d21996bfb8b6a52bf" "checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2" "checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b" "checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998" @@ -1799,6 +1934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a77c51c07654ddd93f6cb543c7a849863b03abc7e82591afda6dc8ad4ac3ac4a" "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb" "checksum regex-syntax 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bd90079345f4a4c3409214734ae220fd773c6f2e8a543d07370c6c1c369cfbfb" "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" @@ -1828,6 +1964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum smallvec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4f357e8cd37bf8822e1b964e96fd39e2cb5a0424f8aaa284ccaccc2162411c" "checksum state 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5562ac59585fe3d9a1ccf6b4e298ce773f5063db80d59f783776b410c1714c2" +"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5" "checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59" @@ -1837,6 +1974,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tera 0.10.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d706c3bec8103f346fc7b8a3887a2ff4195cf704bdbc6307069f32ea8a2b3af5" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" "checksum tokio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "be15ef40f675c9fe66e354d74c73f3ed012ca1aa14d65846a33ee48f1ae8d922" @@ -1853,7 +1992,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "137bda266504893ac4774e0ec4c2108f7ccdbcb7ac8dced6305fe9e4e0b5041a" "checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" +"checksum twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" +"checksum typed-arena 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5934776c3ac1bea4a9d56620d6bf2d483b20d394e49581db40f187e1118ff667" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" @@ -1861,8 +2002,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" "checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" "checksum unidecode 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "402bb19d8e03f1d1a7450e2bd613980869438e0666331be3e073089124aa1adc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" @@ -1870,6 +2013,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22" "checksum vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ed0f6789c8a85ca41bbc1c9d175422116a9869bd1cf31bb08e1493ecce60380" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" diff --git a/Cargo.toml b/Cargo.toml index d5b416c9..41241766 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ activitystreams-types = "0.1" array_tool = "1.0" base64 = "0.9" bcrypt = "0.2" +comrak = "0.2" dotenv = "*" failure = "0.1" failure_derive = "0.1" diff --git a/src/main.rs b/src/main.rs index 9663b379..7b6b803b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ extern crate array_tool; extern crate base64; extern crate bcrypt; extern crate chrono; +extern crate comrak; extern crate failure; #[macro_use] extern crate failure_derive; diff --git a/src/routes/posts.rs b/src/routes/posts.rs index fdc0d53a..60be113b 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -1,3 +1,4 @@ +use comrak::{markdown_to_html, ComrakOptions}; use heck::KebabCase; use rocket::request::Form; use rocket::response::Redirect; @@ -80,11 +81,26 @@ fn create(blog_name: String, data: Form, user: User, conn: DbConn) let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).unwrap(); let form = data.get(); let slug = form.title.to_string().to_kebab_case(); + + let content = markdown_to_html(form.content.to_string().as_ref(), &ComrakOptions{ + smart: true, + safe: true, + ext_strikethrough: true, + ext_tagfilter: true, + ext_table: true, + ext_autolink: true, + ext_tasklist: true, + ext_superscript: true, + ext_header_ids: Some("title".to_string()), + ext_footnotes: true, + ..ComrakOptions::default() + }); + let post = Post::insert(&*conn, NewPost { blog_id: blog.id, slug: slug.to_string(), title: form.title.to_string(), - content: form.content.to_string(), + content: content, published: true, license: form.license.to_string(), ap_url: "".to_string() From ef815a436bf662d77ff85b2302ae99b085863316 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 24 May 2018 11:43:16 +0100 Subject: [PATCH 13/33] =?UTF-8?q?Tera=20already=20adds=20"=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/macros.tera | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/macros.tera b/templates/macros.tera index c090fe41..49432656 100644 --- a/templates/macros.tera +++ b/templates/macros.tera @@ -1,7 +1,7 @@ {% macro post_card(article) %}

{{ article.post.title }}

-

{{ article.post.content | striptags | truncate(length=200) }}…

+

{{ article.post.content | striptags | truncate(length=200) }}

By {{ article.author.display_name }} ⋅ {{ article.date | date(format="%B %e") }}

{% endmacro post_card %} From d604b629174284adfeafb3ccd1f067432057eb70 Mon Sep 17 00:00:00 2001 From: Bat Date: Thu, 24 May 2018 12:20:11 +0100 Subject: [PATCH 14/33] Make it easier to debug inbox errors --- src/activity_pub/inbox.rs | 4 ++-- src/models/users.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/activity_pub/inbox.rs b/src/activity_pub/inbox.rs index 6b2c624a..1b45b442 100644 --- a/src/activity_pub/inbox.rs +++ b/src/activity_pub/inbox.rs @@ -114,8 +114,8 @@ pub trait Inbox { "Create" => { let act: Create = serde_json::from_value(act.clone())?; match act.object["type"].as_str().unwrap() { - "Article" => self.new_article(conn, act.object_object().unwrap()), - "Note" => self.new_comment(conn, act.object_object().unwrap(), act.actor_object::()?.object_props.id_string()?), + "Article" => self.new_article(conn, act.object_object()?), + "Note" => self.new_comment(conn, act.object_object()?, act.actor_object::()?.object_props.id_string()?), _ => Err(InboxError::InvalidType)? } }, diff --git a/src/models/users.rs b/src/models/users.rs index 79c37ad9..55b3f181 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -439,7 +439,7 @@ impl WithInbox for User { impl Inbox for User { fn received(&self, conn: &PgConnection, act: serde_json::Value) { if let Err(err) = self.save(conn, act.clone()) { - println!("Inbox error:\n{}\n{}", err.cause(), err.backtrace()); + println!("Inbox error:\n{}\n{}\n\nActivity was: {}", err.cause(), err.backtrace(), act.to_string()); } // Notifications From 2fde47d909a1a2aa01f8ccfb36c559dcc5c15d52 Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Mon, 4 Jun 2018 20:21:43 +0200 Subject: [PATCH 15/33] add optional login message and callback --- src/main.rs | 1 + src/routes/session.rs | 24 ++++++++++++++++++++---- templates/session/login.tera | 3 +++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7b6b803b..2c50b408 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,6 +94,7 @@ fn main() { routes::reshares::create, routes::session::new, + routes::session::new_message, routes::session::create, routes::session::delete, diff --git a/src/routes/session.rs b/src/routes/session.rs index f8a1ed94..bf7d5e6b 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -1,7 +1,7 @@ use rocket::{ http::{Cookie, Cookies}, response::{Redirect, status::NotFound}, - request::Form + request::{Form,FlashMessage} }; use rocket_contrib::Template; @@ -15,6 +15,20 @@ fn new(user: Option) -> Template { })) } +#[derive(FromForm)] +struct Message { + m: String +} + +#[get("/login?")] +fn new_message(user: Option, message: Message) -> Template { + Template::render("session/login", json!({ + "account": user, + "message": message.m + })) +} + + #[derive(FromForm)] struct LoginForm { email_or_name: String, @@ -22,7 +36,7 @@ struct LoginForm { } #[post("/login", data = "")] -fn create(conn: DbConn, data: Form, mut cookies: Cookies) -> Result> { +fn create(conn: DbConn, data: Form, flash: Option, mut cookies: Cookies) -> Result> { let form = data.get(); let user = match User::find_by_email(&*conn, form.email_or_name.to_string()) { Some(usr) => Ok(usr), @@ -31,12 +45,14 @@ fn create(conn: DbConn, data: Form, mut cookies: Cookies) -> Result Err("Invalid username or password") } }; - match user { Ok(usr) => { if usr.auth(form.password.to_string()) { cookies.add_private(Cookie::new(AUTH_COOKIE, usr.id.to_string())); - Ok(Redirect::to("/")) + Ok(Redirect::to(&flash + .and_then(|f| if f.name()=="callback" { Some(f.msg().to_owned()) } else { None }) + .unwrap_or("/".to_owned())) + ) } else { Err(NotFound(String::from("Invalid username or password"))) } diff --git a/templates/session/login.tera b/templates/session/login.tera index eb2cebe8..1099b5e8 100644 --- a/templates/session/login.tera +++ b/templates/session/login.tera @@ -6,6 +6,9 @@ Login {% block content %}

Login

+{% if message %} +

{{ message | escape }}

+{% endif %}
From 8158f19b858fccdd2e9817a45c3343a3938625e4 Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Mon, 4 Jun 2018 21:57:03 +0200 Subject: [PATCH 16/33] add fallback to url generating 404 --- src/main.rs | 5 +++++ src/routes/blogs.rs | 7 ++++++- src/routes/comments.rs | 12 +++++++++++- src/routes/likes.rs | 9 ++++++++- src/routes/notifications.rs | 8 ++++++++ src/routes/posts.rs | 8 ++++---- src/routes/reshares.rs | 9 ++++++++- src/utils.rs | 6 +++--- 8 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index 2c50b408..04403e25 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,9 +71,11 @@ fn main() { routes::blogs::activity_details, routes::blogs::outbox, routes::blogs::new, + routes::blogs::new_auth, routes::blogs::create, routes::comments::new, + routes::comments::new_auth, routes::comments::create, routes::instance::index, @@ -82,8 +84,10 @@ fn main() { routes::instance::shared_inbox, routes::likes::create, + routes::likes::create_auth, routes::notifications::notifications, + routes::notifications::notifications_auth, routes::posts::details, routes::posts::activity_details, @@ -92,6 +96,7 @@ fn main() { routes::posts::create, routes::reshares::create, + routes::reshares::create_auth, routes::session::new, routes::session::new_message, diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 2df26cf5..863f60a2 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -1,7 +1,7 @@ use activitystreams_types::collection::OrderedCollection; use rocket::{ request::Form, - response::Redirect + response::{Redirect, Flash} }; use rocket_contrib::Template; use serde_json; @@ -53,6 +53,11 @@ fn new(user: User) -> Template { })) } +#[get("/blogs/new", rank = 2)] +fn new_auth() -> Flash{ + utils::requires_login("You need to be logged in order to create a new blog", "/blogs/new") +} + #[derive(FromForm)] struct NewBlogForm { pub title: String diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 2a47b87f..57f5a642 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -1,4 +1,7 @@ -use rocket::{ request::Form, response::Redirect}; +use rocket::{ + request::Form, + response::{Redirect, Flash} +}; use rocket_contrib::Template; use activity_pub::broadcast; @@ -9,6 +12,8 @@ use models::{ users::User }; +use utils; + #[get("/~/<_blog>//comment")] fn new(_blog: String, slug: String, user: User, conn: DbConn) -> Template { let post = Post::find_by_slug(&*conn, slug).unwrap(); @@ -18,6 +23,11 @@ fn new(_blog: String, slug: String, user: User, conn: DbConn) -> Template { })) } +#[get("/~///comment", rank=2)] +fn new_auth(blog: String, slug: String) -> Flash{ + utils::requires_login("You need to be logged in order to post a comment", &format!("~/{}/{}/comment", blog, slug)) +} + #[derive(FromForm)] struct CommentQuery { responding_to: Option diff --git a/src/routes/likes.rs b/src/routes/likes.rs index 11c578bb..55dce944 100644 --- a/src/routes/likes.rs +++ b/src/routes/likes.rs @@ -1,4 +1,4 @@ -use rocket::response::Redirect; +use rocket::response::{Redirect, Flash}; use activity_pub::broadcast; use db_conn::DbConn; @@ -8,6 +8,8 @@ use models::{ users::User }; +use utils; + #[get("/~///like")] fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { let post = Post::find_by_slug(&*conn, slug.clone()).unwrap(); @@ -29,3 +31,8 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { Redirect::to(format!("/~/{}/{}/", blog, slug).as_ref()) } + +#[get("/~///like", rank = 2)] +fn create_auth(blog: String, slug: String) -> Flash{ + utils::requires_login("You need to be logged in order to like a post", &format!("/~/{}/{}/like", blog, slug)) +} diff --git a/src/routes/notifications.rs b/src/routes/notifications.rs index ae8c0d0b..6b096148 100644 --- a/src/routes/notifications.rs +++ b/src/routes/notifications.rs @@ -1,8 +1,11 @@ +use rocket::response::{Redirect, Flash}; use rocket_contrib::Template; use db_conn::DbConn; use models::{notifications::Notification, users::User}; +use utils; + #[get("/notifications")] fn notifications(conn: DbConn, user: User) -> Template { Template::render("notifications/index", json!({ @@ -10,3 +13,8 @@ fn notifications(conn: DbConn, user: User) -> Template { "notifications": Notification::find_for_user(&*conn, &user) })) } + +#[get("/notifications", rank = 2)] +fn notifications_auth() -> Flash{ + utils::requires_login("You need to be logged in order to see your notifications", "/notifications") +} diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 60be113b..675159b7 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -1,7 +1,7 @@ use comrak::{markdown_to_html, ComrakOptions}; use heck::KebabCase; use rocket::request::Form; -use rocket::response::Redirect; +use rocket::response::{Redirect, Flash}; use rocket_contrib::Template; use serde_json; @@ -57,9 +57,9 @@ fn activity_details(_blog: String, slug: String, conn: DbConn) -> ActivityPub { activity_pub(act) } -#[get("/~/<_blog>/new", rank = 2)] -fn new_auth(_blog: String) -> Redirect { - utils::requires_login() +#[get("/~//new", rank = 2)] +fn new_auth(blog: String) -> Flash { + utils::requires_login("You need to be logged in order to write a new post", &format!("/~/{}/new",blog)) } #[get("/~/<_blog>/new", rank = 1)] diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs index 7cf74d9d..19f69332 100644 --- a/src/routes/reshares.rs +++ b/src/routes/reshares.rs @@ -1,4 +1,4 @@ -use rocket::response::Redirect; +use rocket::response::{Redirect, Flash}; use activity_pub::broadcast; use db_conn::DbConn; @@ -8,6 +8,8 @@ use models::{ users::User }; +use utils; + #[get("/~///reshare")] fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { let post = Post::find_by_slug(&*conn, slug.clone()).unwrap(); @@ -29,3 +31,8 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { Redirect::to(format!("/~/{}/{}/", blog, slug).as_ref()) } + +#[get("/~///reshare", rank=1)] +fn create_auth(blog: String, slug: String) -> Flash { + utils::requires_login("You need to be logged in order to reshare a post", &format!("/~/{}/{}/reshare",blog, slug)) +} diff --git a/src/utils.rs b/src/utils.rs index 8e3b9bbf..eb6d1100 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,5 @@ use heck::CamelCase; -use rocket::response::Redirect; +use rocket::response::{Redirect, Flash}; /// Remove non alphanumeric characters and CamelCase a string pub fn make_actor_id(name: String) -> String { @@ -11,6 +11,6 @@ pub fn make_actor_id(name: String) -> String { .collect() } -pub fn requires_login() -> Redirect { - Redirect::to("/login") +pub fn requires_login(message: &str, url: &str) -> Flash { + Flash::new(Redirect::to(&format!("/login?m={}", message)), "callback", url) } From 3dcd78f205d06e138f5a5d2b50f7bece14446912 Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Thu, 7 Jun 2018 10:39:22 +0200 Subject: [PATCH 17/33] fix redirection to login in src/routes/user.rs --- src/main.rs | 2 ++ src/routes/user.rs | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 04403e25..3e6d3a03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -109,8 +109,10 @@ fn main() { routes::user::details, routes::user::followers, routes::user::edit, + routes::user::edit_auth, routes::user::update, routes::user::follow, + routes::user::follow_auth, routes::user::activity_details, routes::user::outbox, routes::user::inbox, diff --git a/src/routes/user.rs b/src/routes/user.rs index 606b0cf5..35192b09 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -2,7 +2,9 @@ use activitystreams_types::{ activity::Follow, collection::OrderedCollection }; -use rocket::{request::Form, response::Redirect}; +use rocket::{request::Form, + response::{Redirect, Flash} +}; use rocket_contrib::Template; use serde_json; @@ -19,10 +21,14 @@ use models::{ reshares::Reshare, users::* }; +use utils; #[get("/me")] -fn me(user: User) -> Redirect { - Redirect::to(format!("/@/{}/", user.username).as_ref()) +fn me(user: Option) -> Result> { + match user { + Some(user) => Ok(Redirect::to(format!("/@/{}/", user.username).as_ref())), + None => Err(utils::requires_login("", "/me")) + } } #[get("/@/", rank = 2)] @@ -83,6 +89,11 @@ fn follow(name: String, conn: DbConn, user: User) -> Redirect { Redirect::to(format!("/@/{}/", name).as_ref()) } +#[get("/@//follow", rank = 2)] +fn follow_auth(name: String) -> Flash { + utils::requires_login("You need to belogged in order to follow someone", &format!("/@/{}/follow", name)) +} + #[get("/@//followers", rank = 2)] fn followers(name: String, conn: DbConn, account: Option) -> Template { let user = User::find_by_fqn(&*conn, name.clone()).unwrap(); @@ -125,6 +136,11 @@ fn edit(name: String, user: User) -> Option