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
This commit is contained in:
Bat 2018-06-19 20:16:18 +01:00
parent 1653a3ac74
commit 857e1f1d6a
6 changed files with 65 additions and 52 deletions

View File

@ -52,7 +52,7 @@ pub struct NewPost {
impl Post { impl Post {
insert!(posts, NewPost); insert!(posts, NewPost);
get!(posts); 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); find_by!(posts, find_by_ap_url, ap_url as String);
pub fn count_local(conn: &PgConnection) -> usize { pub fn count_local(conn: &PgConnection) -> usize {

View File

@ -75,7 +75,7 @@ fn create(conn: DbConn, data: Form<NewBlogForm>, user: User) -> Redirect {
author_id: user.id, author_id: user.id,
is_owner: true is_owner: true
}); });
Redirect::to(format!("/~/{}/", slug)) Redirect::to(format!("/~/{}/", slug))
} }
} }

View File

@ -7,6 +7,7 @@ use rocket_contrib::Template;
use activity_pub::{broadcast, IntoId, inbox::Notify}; use activity_pub::{broadcast, IntoId, inbox::Notify};
use db_conn::DbConn; use db_conn::DbConn;
use models::{ use models::{
blogs::Blog,
comments::*, comments::*,
posts::Post, posts::Post,
users::User users::User
@ -15,13 +16,15 @@ use models::{
use utils; use utils;
use safe_string::SafeString; use safe_string::SafeString;
#[get("/~/<_blog>/<slug>/comment")] #[get("/~/<blog>/<slug>/comment")]
fn new(_blog: String, slug: String, user: User, conn: DbConn) -> Template { 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| { may_fail!(Blog::find_by_fqn(&*conn, blog), "Couldn't find this blog", |blog| {
Template::render("comments/new", json!({ may_fail!(Post::find_by_slug(&*conn, slug, blog.id), "Couldn't find this post", |post| {
"post": post, Template::render("comments/new", json!({
"account": user "post": post,
})) "account": user
}))
})
}) })
} }
@ -40,9 +43,10 @@ struct NewCommentForm {
pub content: String pub content: String
} }
#[post("/~/<blog>/<slug>/comment?<query>", data = "<data>")] #[post("/~/<blog_name>/<slug>/comment?<query>", data = "<data>")]
fn create(blog: String, slug: String, query: CommentQuery, data: Form<NewCommentForm>, user: User, conn: DbConn) -> Redirect { fn create(blog_name: String, slug: String, query: CommentQuery, data: Form<NewCommentForm>, user: User, conn: DbConn) -> Redirect {
let post = Post::find_by_slug(&*conn, slug.clone()).unwrap(); 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 form = data.get();
let comment = Comment::insert(&*conn, NewComment { let comment = Comment::insert(&*conn, NewComment {
content: SafeString::new(&form.content.clone()), content: SafeString::new(&form.content.clone()),
@ -57,5 +61,5 @@ fn create(blog: String, slug: String, query: CommentQuery, data: Form<NewComment
Comment::notify(&*conn, comment.into_activity(&*conn), user.clone().into_id()); Comment::notify(&*conn, comment.into_activity(&*conn), user.clone().into_id());
broadcast(&*conn, &user, comment.create_activity(&*conn), user.get_followers(&*conn)); broadcast(&*conn, &user, comment.create_activity(&*conn), user.get_followers(&*conn));
Redirect::to(format!("/~/{}/{}/#comment-{}", blog, slug, comment.id)) Redirect::to(format!("/~/{}/{}/#comment-{}", blog_name, slug, comment.id))
} }

View File

@ -3,6 +3,7 @@ use rocket::response::{Redirect, Flash};
use activity_pub::{broadcast, IntoId, inbox::Notify}; use activity_pub::{broadcast, IntoId, inbox::Notify};
use db_conn::DbConn; use db_conn::DbConn;
use models::{ use models::{
blogs::Blog,
likes, likes,
posts::Post, posts::Post,
users::User users::User
@ -12,7 +13,8 @@ use utils;
#[get("/~/<blog>/<slug>/like")] #[get("/~/<blog>/<slug>/like")]
fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { 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) { if !user.has_liked(&*conn, &post) {
let like = likes::Like::insert(&*conn, likes::NewLike { let like = likes::Like::insert(&*conn, likes::NewLike {

View File

@ -20,7 +20,7 @@ use safe_string::SafeString;
#[get("/~/<blog>/<slug>", rank = 4)] #[get("/~/<blog>/<slug>", rank = 4)]
fn details(blog: String, slug: String, conn: DbConn, user: Option<User>) -> Template { fn details(blog: String, slug: String, conn: DbConn, user: Option<User>) -> Template {
may_fail!(Blog::find_by_fqn(&*conn, blog), "Couldn't find this blog", |blog| { 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); let comments = Comment::find_by_post(&*conn, post.id);
Template::render("posts/details", json!({ Template::render("posts/details", json!({
@ -39,10 +39,10 @@ fn details(blog: String, slug: String, conn: DbConn, user: Option<User>) -> Temp
}) })
} }
#[get("/~/<_blog>/<slug>", rank = 3, format = "application/activity+json")] #[get("/~/<blog>/<slug>", rank = 3, format = "application/activity+json")]
fn activity_details(_blog: String, slug: String, conn: DbConn) -> ActivityPub { fn activity_details(blog: String, slug: String, conn: DbConn) -> ActivityPub {
// FIXME: posts in different blogs may have the same slug let blog = Blog::find_by_fqn(&*conn, blog).unwrap();
let post = Post::find_by_slug(&*conn, slug).unwrap(); let post = Post::find_by_slug(&*conn, slug, blog.id).unwrap();
let mut act = post.serialize(&*conn); let mut act = post.serialize(&*conn);
act["@context"] = context(); act["@context"] = context();
@ -54,8 +54,9 @@ fn new_auth(blog: String) -> Flash<Redirect> {
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", &format!("/~/{}/new",blog))
} }
#[get("/~/<_blog>/new", rank = 1)] #[get("/~/<blog>/new", rank = 1)]
fn new(_blog: String, user: User) -> Template { #[allow(unused_variables)]
fn new(blog: String, user: User) -> Template {
Template::render("posts/new", json!({ Template::render("posts/new", json!({
"account": user "account": user
})) }))
@ -74,37 +75,41 @@ fn create(blog_name: String, data: Form<NewPostForm>, user: User, conn: DbConn)
let form = data.get(); let form = data.get();
let slug = form.title.to_string().to_kebab_case(); let slug = form.title.to_string().to_kebab_case();
let content = markdown_to_html(form.content.to_string().as_ref(), &ComrakOptions{ if slug == "new" || Post::find_by_slug(&*conn, slug.clone(), blog.id).is_some() {
smart: true, Redirect::to(uri!(new: blog = blog_name))
safe: true, } else {
ext_strikethrough: true, let content = markdown_to_html(form.content.to_string().as_ref(), &ComrakOptions{
ext_tagfilter: true, smart: true,
ext_table: true, safe: true,
ext_autolink: true, ext_strikethrough: true,
ext_tasklist: true, ext_tagfilter: true,
ext_superscript: true, ext_table: true,
ext_header_ids: Some("title".to_string()), ext_autolink: true,
ext_footnotes: true, ext_tasklist: true,
..ComrakOptions::default() ext_superscript: true,
}); ext_header_ids: Some("title".to_string()),
ext_footnotes: true,
..ComrakOptions::default()
});
let post = Post::insert(&*conn, NewPost { let post = Post::insert(&*conn, NewPost {
blog_id: blog.id, blog_id: blog.id,
slug: slug.to_string(), slug: slug.to_string(),
title: form.title.to_string(), title: form.title.to_string(),
content: SafeString::new(&content), content: SafeString::new(&content),
published: true, published: true,
license: form.license.to_string(), license: form.license.to_string(),
ap_url: "".to_string() ap_url: "".to_string()
}); });
post.update_ap_url(&*conn); post.update_ap_url(&*conn);
PostAuthor::insert(&*conn, NewPostAuthor { PostAuthor::insert(&*conn, NewPostAuthor {
post_id: post.id, post_id: post.id,
author_id: user.id author_id: user.id
}); });
let act = post.create_activity(&*conn); let act = post.create_activity(&*conn);
broadcast(&*conn, &user, act, user.get_followers(&*conn)); broadcast(&*conn, &user, act, user.get_followers(&*conn));
Redirect::to(format!("/~/{}/{}/", blog_name, slug)) Redirect::to(format!("/~/{}/{}/", blog_name, slug))
}
} }

View File

@ -3,6 +3,7 @@ use rocket::response::{Redirect, Flash};
use activity_pub::{broadcast, IntoId, inbox::Notify}; use activity_pub::{broadcast, IntoId, inbox::Notify};
use db_conn::DbConn; use db_conn::DbConn;
use models::{ use models::{
blogs::Blog,
posts::Post, posts::Post,
reshares::*, reshares::*,
users::User users::User
@ -12,7 +13,8 @@ use utils;
#[get("/~/<blog>/<slug>/reshare")] #[get("/~/<blog>/<slug>/reshare")]
fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { 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) { if !user.has_reshared(&*conn, &post) {
let reshare = Reshare::insert(&*conn, NewReshare { let reshare = Reshare::insert(&*conn, NewReshare {