From 1653a3ac744c35ffa86b2aae95615d84f912bca8 Mon Sep 17 00:00:00 2001 From: Bat Date: Tue, 19 Jun 2018 19:40:20 +0100 Subject: [PATCH 1/5] Check for existing slug before creating a new blog Fix #63 --- src/routes/blogs.rs | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index eecc07fc..28a49472 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -59,21 +59,25 @@ fn create(conn: DbConn, data: Form, user: User) -> Redirect { let form = data.get(); let slug = utils::make_actor_id(form.title.to_string()); - let blog = Blog::insert(&*conn, NewBlog::new_local( - slug.to_string(), - form.title.to_string(), - String::from(""), - Instance::local_id(&*conn) - )); - blog.update_boxes(&*conn); + if Blog::find_local(&*conn, slug.clone()).is_some() { + Redirect::to(uri!(new)) + } else { + let blog = Blog::insert(&*conn, NewBlog::new_local( + slug.to_string(), + form.title.to_string(), + String::from(""), + Instance::local_id(&*conn) + )); + blog.update_boxes(&*conn); - BlogAuthor::insert(&*conn, NewBlogAuthor { - blog_id: blog.id, - author_id: user.id, - is_owner: true - }); - - Redirect::to(format!("/~/{}/", slug)) + BlogAuthor::insert(&*conn, NewBlogAuthor { + blog_id: blog.id, + author_id: user.id, + is_owner: true + }); + + Redirect::to(format!("/~/{}/", slug)) + } } #[get("/~//outbox")] From 07c5ef3fece41cb19019dfa338eb4dc0d4d8d0e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Tue, 19 Jun 2018 21:16:11 +0200 Subject: [PATCH 2/5] i18n: Update Polish translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- po/pl.po | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/po/pl.po b/po/pl.po index a319828f..2254305d 100644 --- a/po/pl.po +++ b/po/pl.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: plume\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2018-06-15 16:33-0700\n" -"PO-Revision-Date: 2018-06-17 20:57+0200\n" +"PO-Revision-Date: 2018-06-19 21:15+0200\n" "Last-Translator: Marcin Mikołajczak \n" "Language-Team: none\n" "Language: pl\n" @@ -12,6 +12,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" +"X-Generator: Poedit 2.0.8\n" msgid "Latest articles" msgstr "Najnowsze artykuły" @@ -117,7 +118,7 @@ msgid "Publish" msgstr "Opublikuj" msgid "Login" -msgstr "" +msgstr "Logowanie" msgid "Username or email" msgstr "Nazwa użytkownika lub adres e-mail" @@ -261,22 +262,22 @@ msgid "By {{ link_1 }}{{ link_2 }}{{ link_3 }}{{ name }}{{ link_4 }}" msgstr "Napisano przez {{ link_1 }}{{ url }}{{ link_2 }}{{ name }}{{ link_3 }}" msgid "{{ data }} reshared your article" -msgstr "" +msgstr "{{ data }} udostępnił Twój artykuł" msgid "{{ data }} started following you" -msgstr "" +msgstr "{{ data }} zaczął Cię obserwować" msgid "{{ data }} liked your article" -msgstr "" +msgstr "{{ data }} polubił Twój artykuł" msgid "{{ data }} commented your article" -msgstr "" +msgstr "{{ data }} skomentował Twój artykuł" msgid "We couldn't find this page." -msgstr "" +msgstr "Nie udało się odnaleźć tej strony." msgid "The link that led you here may be broken." -msgstr "" +msgstr "Odnośnik który Cię tu zaprowadził może być uszkodzony." #~ msgid "Logowanie" #~ msgstr "Zaloguj się" From 857e1f1d6a4024d6ba82b676a61d3fef5b91cbfa Mon Sep 17 00:00:00 2001 From: Bat Date: Tue, 19 Jun 2018 20:16:18 +0100 Subject: [PATCH 3/5] Disallow naming an article 'new', or any already used slug Fix #64 Also fixes a lot of potential bug with articles having the same slugs, but not in the same blog --- src/models/posts.rs | 2 +- src/routes/blogs.rs | 2 +- src/routes/comments.rs | 26 ++++++++------ src/routes/likes.rs | 4 ++- src/routes/posts.rs | 79 ++++++++++++++++++++++-------------------- src/routes/reshares.rs | 4 ++- 6 files changed, 65 insertions(+), 52 deletions(-) diff --git a/src/models/posts.rs b/src/models/posts.rs index 8d12bc8f..b084dd4b 100644 --- a/src/models/posts.rs +++ b/src/models/posts.rs @@ -52,7 +52,7 @@ pub struct NewPost { impl Post { insert!(posts, NewPost); get!(posts); - find_by!(posts, find_by_slug, slug as String); + find_by!(posts, find_by_slug, slug as String, blog_id as i32); find_by!(posts, find_by_ap_url, ap_url as String); pub fn count_local(conn: &PgConnection) -> usize { diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 28a49472..118dac80 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -75,7 +75,7 @@ fn create(conn: DbConn, data: Form, user: User) -> Redirect { author_id: user.id, is_owner: true }); - + Redirect::to(format!("/~/{}/", slug)) } } diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 289dee28..923795a4 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -7,6 +7,7 @@ use rocket_contrib::Template; use activity_pub::{broadcast, IntoId, inbox::Notify}; use db_conn::DbConn; use models::{ + blogs::Blog, comments::*, posts::Post, users::User @@ -15,13 +16,15 @@ use models::{ use utils; use safe_string::SafeString; -#[get("/~/<_blog>//comment")] -fn new(_blog: String, slug: String, user: User, conn: DbConn) -> Template { - may_fail!(Post::find_by_slug(&*conn, slug), "Couldn't find this post", |post| { - Template::render("comments/new", json!({ - "post": post, - "account": user - })) +#[get("/~///comment")] +fn new(blog: String, slug: String, user: User, conn: DbConn) -> Template { + may_fail!(Blog::find_by_fqn(&*conn, blog), "Couldn't find this blog", |blog| { + may_fail!(Post::find_by_slug(&*conn, slug, blog.id), "Couldn't find this post", |post| { + Template::render("comments/new", json!({ + "post": post, + "account": user + })) + }) }) } @@ -40,9 +43,10 @@ struct NewCommentForm { pub content: String } -#[post("/~///comment?", data = "")] -fn create(blog: String, slug: String, query: CommentQuery, data: Form, user: User, conn: DbConn) -> Redirect { - let post = Post::find_by_slug(&*conn, slug.clone()).unwrap(); +#[post("/~///comment?", data = "")] +fn create(blog_name: String, slug: String, query: CommentQuery, data: Form, user: User, conn: DbConn) -> Redirect { + let blog = Blog::find_by_fqn(&*conn, blog_name.clone()).unwrap(); + let post = Post::find_by_slug(&*conn, slug.clone(), blog.id).unwrap(); let form = data.get(); let comment = Comment::insert(&*conn, NewComment { content: SafeString::new(&form.content.clone()), @@ -57,5 +61,5 @@ fn create(blog: String, slug: String, query: CommentQuery, data: Form//like")] fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { - let post = Post::find_by_slug(&*conn, slug.clone()).unwrap(); + let b = Blog::find_by_fqn(&*conn, blog.clone()).unwrap(); + let post = Post::find_by_slug(&*conn, slug.clone(), b.id).unwrap(); if !user.has_liked(&*conn, &post) { let like = likes::Like::insert(&*conn, likes::NewLike { diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 4fd02666..be59f407 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -20,7 +20,7 @@ use safe_string::SafeString; #[get("/~//", rank = 4)] fn details(blog: String, slug: String, conn: DbConn, user: Option) -> Template { may_fail!(Blog::find_by_fqn(&*conn, blog), "Couldn't find this blog", |blog| { - may_fail!(Post::find_by_slug(&*conn, slug), "Couldn't find this post", |post| { + may_fail!(Post::find_by_slug(&*conn, slug, blog.id), "Couldn't find this post", |post| { let comments = Comment::find_by_post(&*conn, post.id); Template::render("posts/details", json!({ @@ -39,10 +39,10 @@ fn details(blog: String, slug: String, conn: DbConn, user: Option) -> Temp }) } -#[get("/~/<_blog>/", rank = 3, format = "application/activity+json")] -fn activity_details(_blog: String, slug: String, conn: DbConn) -> ActivityPub { - // FIXME: posts in different blogs may have the same slug - let post = Post::find_by_slug(&*conn, slug).unwrap(); +#[get("/~//", rank = 3, format = "application/activity+json")] +fn activity_details(blog: String, slug: String, conn: DbConn) -> ActivityPub { + let blog = Blog::find_by_fqn(&*conn, blog).unwrap(); + let post = Post::find_by_slug(&*conn, slug, blog.id).unwrap(); let mut act = post.serialize(&*conn); act["@context"] = context(); @@ -54,8 +54,9 @@ 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)] -fn new(_blog: String, user: User) -> Template { +#[get("/~//new", rank = 1)] +#[allow(unused_variables)] +fn new(blog: String, user: User) -> Template { Template::render("posts/new", json!({ "account": user })) @@ -74,37 +75,41 @@ fn create(blog_name: String, data: Form, user: User, conn: DbConn) 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() - }); + if slug == "new" || Post::find_by_slug(&*conn, slug.clone(), blog.id).is_some() { + Redirect::to(uri!(new: blog = blog_name)) + } else { + 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: SafeString::new(&content), - published: true, - license: form.license.to_string(), - ap_url: "".to_string() - }); - post.update_ap_url(&*conn); - PostAuthor::insert(&*conn, NewPostAuthor { - post_id: post.id, - author_id: user.id - }); + let post = Post::insert(&*conn, NewPost { + blog_id: blog.id, + slug: slug.to_string(), + title: form.title.to_string(), + content: SafeString::new(&content), + published: true, + license: form.license.to_string(), + ap_url: "".to_string() + }); + post.update_ap_url(&*conn); + PostAuthor::insert(&*conn, NewPostAuthor { + post_id: post.id, + author_id: user.id + }); - let act = post.create_activity(&*conn); - broadcast(&*conn, &user, act, user.get_followers(&*conn)); + let act = post.create_activity(&*conn); + broadcast(&*conn, &user, act, user.get_followers(&*conn)); - Redirect::to(format!("/~/{}/{}/", blog_name, slug)) + Redirect::to(format!("/~/{}/{}/", blog_name, slug)) + } } diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs index 5365af48..c243d851 100644 --- a/src/routes/reshares.rs +++ b/src/routes/reshares.rs @@ -3,6 +3,7 @@ use rocket::response::{Redirect, Flash}; use activity_pub::{broadcast, IntoId, inbox::Notify}; use db_conn::DbConn; use models::{ + blogs::Blog, posts::Post, reshares::*, users::User @@ -12,7 +13,8 @@ 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(); + let b = Blog::find_by_fqn(&*conn, blog.clone()).unwrap(); + let post = Post::find_by_slug(&*conn, slug.clone(), b.id).unwrap(); if !user.has_reshared(&*conn, &post) { let reshare = Reshare::insert(&*conn, NewReshare { From db248701b9b5d1f8f1187f421e02e409041bb991 Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Tue, 19 Jun 2018 22:34:59 +0200 Subject: [PATCH 4/5] Disallow blog name which yould result in empty blog name fix #63 --- src/routes/blogs.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 118dac80..880872ad 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -58,7 +58,9 @@ struct NewBlogForm { fn create(conn: DbConn, data: Form, user: User) -> Redirect { let form = data.get(); let slug = utils::make_actor_id(form.title.to_string()); - + if slug.len() == 0 { + return Redirect::to("/blogs/new") + } if Blog::find_local(&*conn, slug.clone()).is_some() { Redirect::to(uri!(new)) } else { From 8ab25b1ca22a58a0abf5fdbbe277057dcbe0da48 Mon Sep 17 00:00:00 2001 From: Bat Date: Tue, 19 Jun 2018 22:20:27 +0100 Subject: [PATCH 5/5] Use uri! as much as possible instead of directly writing URLs --- src/routes/blogs.rs | 10 ++++------ src/routes/comments.rs | 2 +- src/routes/likes.rs | 4 ++-- src/routes/notifications.rs | 2 +- src/routes/posts.rs | 4 ++-- src/routes/reshares.rs | 4 ++-- src/routes/user.rs | 16 ++++++++-------- src/utils.rs | 4 ++-- 8 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 880872ad..ce53a647 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -46,7 +46,7 @@ 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") + utils::requires_login("You need to be logged in order to create a new blog", uri!(new)) } #[derive(FromForm)] @@ -58,10 +58,8 @@ struct NewBlogForm { fn create(conn: DbConn, data: Form, user: User) -> Redirect { let form = data.get(); let slug = utils::make_actor_id(form.title.to_string()); - if slug.len() == 0 { - return Redirect::to("/blogs/new") - } - if Blog::find_local(&*conn, slug.clone()).is_some() { + + if Blog::find_local(&*conn, slug.clone()).is_some() || slug.len() == 0 { Redirect::to(uri!(new)) } else { let blog = Blog::insert(&*conn, NewBlog::new_local( @@ -78,7 +76,7 @@ fn create(conn: DbConn, data: Form, user: User) -> Redirect { is_owner: true }); - Redirect::to(format!("/~/{}/", slug)) + Redirect::to(uri!(details: name = slug)) } } diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 923795a4..49e678be 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -30,7 +30,7 @@ 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)) + utils::requires_login("You need to be logged in order to post a comment", uri!(new: blog = blog, slug = slug)) } #[derive(FromForm)] diff --git a/src/routes/likes.rs b/src/routes/likes.rs index 696210da..a9e4791e 100644 --- a/src/routes/likes.rs +++ b/src/routes/likes.rs @@ -32,10 +32,10 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { broadcast(&*conn, &user, delete_act, user.get_followers(&*conn)); } - Redirect::to(format!("/~/{}/{}/", blog, slug)) + Redirect::to(uri!(super::posts::details: blog = blog, slug = slug)) } #[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)) + utils::requires_login("You need to be logged in order to like a post", uri!(create: blog = blog, slug = slug)) } diff --git a/src/routes/notifications.rs b/src/routes/notifications.rs index 6b096148..9548ad74 100644 --- a/src/routes/notifications.rs +++ b/src/routes/notifications.rs @@ -16,5 +16,5 @@ fn notifications(conn: DbConn, user: User) -> Template { #[get("/notifications", rank = 2)] fn notifications_auth() -> Flash{ - utils::requires_login("You need to be logged in order to see your notifications", "/notifications") + utils::requires_login("You need to be logged in order to see your notifications", uri!(notifications)) } diff --git a/src/routes/posts.rs b/src/routes/posts.rs index be59f407..875db015 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -51,7 +51,7 @@ fn activity_details(blog: String, slug: String, conn: DbConn) -> ActivityPub { #[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)) + utils::requires_login("You need to be logged in order to write a new post", uri!(new: blog = blog)) } #[get("/~//new", rank = 1)] @@ -110,6 +110,6 @@ 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)) + Redirect::to(uri!(details: blog = blog_name, slug = slug)) } } diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs index c243d851..6da07f77 100644 --- a/src/routes/reshares.rs +++ b/src/routes/reshares.rs @@ -32,10 +32,10 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { broadcast(&*conn, &user, delete_act, user.get_followers(&*conn)); } - Redirect::to(format!("/~/{}/{}/", blog, slug)) + Redirect::to(uri!(super::posts::details: blog = blog, slug = slug)) } #[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)) + utils::requires_login("You need to be logged in order to reshare a post", uri!(create: blog = blog, slug = slug)) } diff --git a/src/routes/user.rs b/src/routes/user.rs index 6b925c87..a8d24f80 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -27,8 +27,8 @@ use utils; #[get("/me")] fn me(user: Option) -> Result> { match user { - Some(user) => Ok(Redirect::to(format!("/@/{}/", user.username))), - None => Err(utils::requires_login("", "/me")) + Some(user) => Ok(Redirect::to(uri!(details: name = user.username))), + None => Err(utils::requires_login("", uri!(me))) } } @@ -65,7 +65,7 @@ fn dashboard(user: User, conn: DbConn) -> Template { #[get("/dashboard", rank = 2)] fn dashboard_auth() -> Flash { - utils::requires_login("You need to be logged in order to access your dashboard", "/dashboard") + utils::requires_login("You need to be logged in order to access your dashboard", uri!(dashboard)) } #[get("/@//follow")] @@ -82,12 +82,12 @@ fn follow(name: String, conn: DbConn, user: User) -> Redirect { follows::Follow::notify(&*conn, act.clone(), user.clone().into_id()); broadcast(&*conn, &user, act, vec![target]); - Redirect::to(format!("/@/{}/", name)) + Redirect::to(uri!(details: name = name)) } #[get("/@//follow", rank = 2)] fn follow_auth(name: String) -> Flash { - utils::requires_login("You need to be logged in order to follow someone", &format!("/@/{}/follow", name)) + utils::requires_login("You need to be logged in order to follow someone", uri!(follow: name = name)) } #[get("/@//followers", rank = 2)] @@ -134,7 +134,7 @@ fn edit(name: String, user: User) -> Option