Refactor with the help of Clippy (#462)
We add clippy as our build — also rectifying the missing `plume-cli` build! In the next step we follow clippy's advise and fix some of the "simple" mistakes in our code, such as style or map usage. Finally, we refactor some hard bits that need extraction of new types, or refactoring of function call-types, especially those that thread thru macros, and, of course functions with ~15 parameters should probably be rethought.
This commit is contained in:
committed by
Baptiste Gelez
parent
570d7fe2d0
commit
732f514da7
@@ -1,3 +1,4 @@
|
||||
#![warn(clippy::too_many_arguments)]
|
||||
use rocket::{response::{self, Responder}, request::{Form, Request}};
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json;
|
||||
|
||||
+6
-5
@@ -1,3 +1,4 @@
|
||||
#![warn(clippy::too_many_arguments)]
|
||||
use activitypub::{
|
||||
activity::{
|
||||
Announce,
|
||||
@@ -14,7 +15,7 @@ use failure::Error;
|
||||
use rocket::{
|
||||
data::*,
|
||||
http::Status,
|
||||
Outcome::{self, *},
|
||||
Outcome::*,
|
||||
Request,
|
||||
};
|
||||
use rocket_contrib::json::*;
|
||||
@@ -129,7 +130,7 @@ pub trait Inbox {
|
||||
_ => Err(InboxError::CantUndo)?,
|
||||
}
|
||||
} else {
|
||||
let link = act.undo_props.object.as_str().expect("Inbox::received: undo don't contain type and isn't Link");
|
||||
let link = act.undo_props.object.as_str().expect("Inbox::received: undo doesn't contain a type and isn't Link");
|
||||
if let Ok(like) = likes::Like::find_by_ap_url(conn, link) {
|
||||
likes::Like::delete_id(&like.ap_url, actor_id.as_ref(), conn).expect("Inbox::received: delete Like error");
|
||||
Ok(())
|
||||
@@ -146,7 +147,7 @@ pub trait Inbox {
|
||||
}
|
||||
"Update" => {
|
||||
let act: Update = serde_json::from_value(act.clone())?;
|
||||
Post::handle_update(conn, &act.update_props.object_object()?, searcher).expect("Inbox::received: post update error");;
|
||||
Post::handle_update(conn, &act.update_props.object_object()?, searcher).expect("Inbox::received: post update error");
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(InboxError::InvalidType)?,
|
||||
@@ -168,7 +169,7 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for SignedJson<T> {
|
||||
type Owned = String;
|
||||
type Borrowed = str;
|
||||
|
||||
fn transform(r: &Request, d: Data) -> Transform<Outcome<Self::Owned, (Status, Self::Error), Data>> {
|
||||
fn transform(r: &Request, d: Data) -> Transform<rocket::data::Outcome<Self::Owned, Self::Error>> {
|
||||
let size_limit = r.limits().get("json").unwrap_or(JSON_LIMIT);
|
||||
let mut s = String::with_capacity(512);
|
||||
match d.open().take(size_limit).read_to_string(&mut s) {
|
||||
@@ -177,7 +178,7 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for SignedJson<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_data(_: &Request, o: Transformed<'a, Self>) -> Outcome<Self, (Status, Self::Error), Data> {
|
||||
fn from_data(_: &Request, o: Transformed<'a, Self>) -> rocket::data::Outcome<Self, Self::Error> {
|
||||
let string = o.borrowed()?;
|
||||
match serde_json::from_str(&string) {
|
||||
Ok(v) => Success(SignedJson(Digest::from_body(&string),Json(v))),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#![warn(clippy::too_many_arguments)]
|
||||
use lettre_email::Email;
|
||||
use std::env;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
#![feature(decl_macro, proc_macro_hygiene)]
|
||||
|
||||
extern crate activitypub;
|
||||
@@ -83,6 +84,8 @@ fn init_pool() -> Option<DbPool> {
|
||||
fn main() {
|
||||
let dbpool = init_pool().expect("main: database pool initialization error");
|
||||
let workpool = ScheduledThreadPool::with_name("worker {}", num_cpus::get());
|
||||
// we want a fast exit here, so
|
||||
#[allow(clippy::match_wild_err_arm)]
|
||||
let searcher = match UnmanagedSearcher::open(&"search_index") {
|
||||
Err(Error::Search(e)) => match e {
|
||||
SearcherError::WriteLockAcquisitionError => panic!(
|
||||
|
||||
+20
-7
@@ -17,19 +17,20 @@ use plume_models::{
|
||||
db_conn::DbConn,
|
||||
instance::Instance,
|
||||
posts::Post,
|
||||
users::User
|
||||
};
|
||||
use routes::{Page, errors::ErrorPage};
|
||||
use routes::{Page, PlumeRocket, errors::ErrorPage};
|
||||
use template_utils::Ructe;
|
||||
use Searcher;
|
||||
|
||||
#[get("/~/<name>?<page>", rank = 2)]
|
||||
pub fn details(intl: I18n, name: String, conn: DbConn, user: Option<User>, page: Option<Page>) -> Result<Ructe, ErrorPage> {
|
||||
pub fn details(name: String, page: Option<Page>, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let page = page.unwrap_or_default();
|
||||
let conn = rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&*conn, &name)?;
|
||||
let posts = Post::blog_page(&*conn, &blog, page.limits())?;
|
||||
let articles_count = Post::count_for_blog(&*conn, &blog)?;
|
||||
let authors = &blog.list_authors(&*conn)?;
|
||||
let user = rockets.user;
|
||||
let intl = rockets.intl;
|
||||
|
||||
Ok(render!(blogs::details(
|
||||
&(&*conn, &intl.catalog, user.clone()),
|
||||
@@ -50,7 +51,11 @@ pub fn activity_details(name: String, conn: DbConn, _ap: ApRequest) -> Option<Ac
|
||||
}
|
||||
|
||||
#[get("/blogs/new")]
|
||||
pub fn new(user: User, conn: DbConn, intl: I18n) -> Ructe {
|
||||
pub fn new(rockets: PlumeRocket) -> Ructe {
|
||||
let user = rockets.user.unwrap();
|
||||
let intl = rockets.intl;
|
||||
let conn = rockets.conn;
|
||||
|
||||
render!(blogs::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
&NewBlogForm::default(),
|
||||
@@ -82,8 +87,11 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> {
|
||||
}
|
||||
|
||||
#[post("/blogs/new", data = "<form>")]
|
||||
pub fn create(conn: DbConn, form: LenientForm<NewBlogForm>, user: User, intl: I18n) -> Result<Redirect, Ructe> {
|
||||
pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> Result<Redirect, Ructe> {
|
||||
let slug = utils::make_actor_id(&form.title);
|
||||
let conn = rockets.conn;
|
||||
let intl = rockets.intl;
|
||||
let user = rockets.user.unwrap();
|
||||
|
||||
let mut errors = match form.validate() {
|
||||
Ok(_) => ValidationErrors::new(),
|
||||
@@ -122,8 +130,13 @@ pub fn create(conn: DbConn, form: LenientForm<NewBlogForm>, user: User, intl: I1
|
||||
}
|
||||
|
||||
#[post("/~/<name>/delete")]
|
||||
pub fn delete(conn: DbConn, name: String, user: Option<User>, intl: I18n, searcher: Searcher) -> Result<Redirect, Ructe>{
|
||||
pub fn delete(name: String, rockets: PlumeRocket) -> Result<Redirect, Ructe>{
|
||||
let conn = rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&*conn, &name).expect("blog::delete: blog not found");
|
||||
let user = rockets.user;
|
||||
let intl = rockets.intl;
|
||||
let searcher = rockets.searcher;
|
||||
|
||||
if user.clone().and_then(|u| u.is_author_in(&*conn, &blog).ok()).unwrap_or(false) {
|
||||
blog.delete(&conn, &searcher).expect("blog::expect: deletion error");
|
||||
Ok(Redirect::to(uri!(super::instance::index)))
|
||||
|
||||
@@ -35,7 +35,7 @@ pub fn upload(user: User, data: Data, ct: &ContentType, conn: DbConn) -> Result<
|
||||
SaveResult::Full(entries) => {
|
||||
let fields = entries.fields;
|
||||
|
||||
let filename = fields.get("file").and_then(|v| v.into_iter().next())
|
||||
let filename = fields.get("file").and_then(|v| v.iter().next())
|
||||
.ok_or_else(|| status::BadRequest(Some("No file uploaded")))?.headers
|
||||
.filename.clone();
|
||||
// Remove extension if it contains something else than just letters and numbers
|
||||
|
||||
+39
-2
@@ -1,3 +1,4 @@
|
||||
#![warn(clippy::too_many_arguments)]
|
||||
use atom_syndication::{ContentBuilder, Entry, EntryBuilder, LinkBuilder, Person, PersonBuilder};
|
||||
use rocket::{
|
||||
http::{
|
||||
@@ -8,9 +9,45 @@ use rocket::{
|
||||
request::{self, FromFormValue, FromRequest, Request},
|
||||
response::NamedFile,
|
||||
};
|
||||
use rocket_i18n::I18n;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use plume_models::{Connection, posts::Post};
|
||||
use plume_models::{
|
||||
Connection,
|
||||
users::User,
|
||||
posts::Post,
|
||||
db_conn::DbConn,
|
||||
};
|
||||
|
||||
use Worker;
|
||||
use Searcher;
|
||||
|
||||
pub struct PlumeRocket<'a> {
|
||||
conn: DbConn,
|
||||
intl: I18n,
|
||||
user: Option<User>,
|
||||
searcher: Searcher<'a>,
|
||||
worker: Worker<'a>,
|
||||
}
|
||||
|
||||
impl<'a, 'r> FromRequest<'a, 'r> for PlumeRocket<'a> {
|
||||
type Error = ();
|
||||
|
||||
fn from_request(request: &'a Request<'r>) -> request::Outcome<PlumeRocket<'a>, ()> {
|
||||
let conn = request.guard::<DbConn>()?;
|
||||
let intl = request.guard::<I18n>()?;
|
||||
let user = request.guard::<User>().succeeded();
|
||||
let worker = request.guard::<Worker>()?;
|
||||
let searcher = request.guard::<Searcher>()?;
|
||||
rocket::Outcome::Success(PlumeRocket {
|
||||
conn,
|
||||
intl,
|
||||
user,
|
||||
worker,
|
||||
searcher,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const ITEMS_PER_PAGE: i32 = 12;
|
||||
|
||||
@@ -45,7 +82,7 @@ impl Page {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn limits(&self) -> (i32, i32) {
|
||||
pub fn limits(self) -> (i32, i32) {
|
||||
((self.0 - 1) * ITEMS_PER_PAGE, self.0 * ITEMS_PER_PAGE)
|
||||
}
|
||||
}
|
||||
|
||||
+155
-134
@@ -24,10 +24,8 @@ use plume_models::{
|
||||
tags::*,
|
||||
users::User
|
||||
};
|
||||
use routes::{errors::ErrorPage, comments::NewCommentForm, ContentLen};
|
||||
use routes::{PlumeRocket, errors::ErrorPage, comments::NewCommentForm, ContentLen};
|
||||
use template_utils::Ructe;
|
||||
use Worker;
|
||||
use Searcher;
|
||||
|
||||
#[get("/~/<blog>/<slug>?<responding_to>", rank = 4)]
|
||||
pub fn details(blog: String, slug: String, conn: DbConn, user: Option<User>, responding_to: Option<i32>, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
@@ -103,86 +101,96 @@ pub fn new_auth(blog: String, i18n: I18n) -> Flash<Redirect> {
|
||||
}
|
||||
|
||||
#[get("/~/<blog>/new", rank = 1)]
|
||||
pub fn new(blog: String, user: User, cl: ContentLen, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
pub fn new(blog: String, cl: ContentLen, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let conn = rockets.conn;
|
||||
let b = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let user = rockets.user.unwrap();
|
||||
let intl = rockets.intl;
|
||||
|
||||
if !user.is_author_in(&*conn, &b)? {
|
||||
// TODO actually return 403 error code
|
||||
Ok(render!(errors::not_authorized(
|
||||
return Ok(render!(errors::not_authorized(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
i18n!(intl.catalog, "You are not author in this blog.")
|
||||
)))
|
||||
} else {
|
||||
let medias = Media::for_user(&*conn, user.id)?;
|
||||
Ok(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
i18n!(intl.catalog, "New post"),
|
||||
b,
|
||||
false,
|
||||
&NewPostForm {
|
||||
license: Instance::get_local(&*conn)?.default_license,
|
||||
..NewPostForm::default()
|
||||
},
|
||||
true,
|
||||
None,
|
||||
ValidationErrors::default(),
|
||||
medias,
|
||||
cl.0
|
||||
)))
|
||||
}
|
||||
|
||||
let medias = Media::for_user(&*conn, user.id)?;
|
||||
Ok(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
i18n!(intl.catalog, "New post"),
|
||||
b,
|
||||
false,
|
||||
&NewPostForm {
|
||||
license: Instance::get_local(&*conn)?.default_license,
|
||||
..NewPostForm::default()
|
||||
},
|
||||
true,
|
||||
None,
|
||||
ValidationErrors::default(),
|
||||
medias,
|
||||
cl.0
|
||||
)))
|
||||
}
|
||||
|
||||
#[get("/~/<blog>/<slug>/edit")]
|
||||
pub fn edit(blog: String, slug: String, user: User, cl: ContentLen, conn: DbConn, intl: I18n) -> Result<Ructe, ErrorPage> {
|
||||
pub fn edit(blog: String, slug: String, cl: ContentLen, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
||||
let conn = rockets.conn;
|
||||
let intl = rockets.intl;
|
||||
let b = Blog::find_by_fqn(&*conn, &blog)?;
|
||||
let post = Post::find_by_slug(&*conn, &slug, b.id)?;
|
||||
let user = rockets.user.unwrap();
|
||||
|
||||
if !user.is_author_in(&*conn, &b)? {
|
||||
Ok(render!(errors::not_authorized(
|
||||
return Ok(render!(errors::not_authorized(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
i18n!(intl.catalog, "You are not author in this blog.")
|
||||
)))
|
||||
} else {
|
||||
let source = if !post.source.is_empty() {
|
||||
post.source.clone()
|
||||
} else {
|
||||
post.content.get().clone() // fallback to HTML if the markdown was not stored
|
||||
};
|
||||
|
||||
let medias = Media::for_user(&*conn, user.id)?;
|
||||
let title = post.title.clone();
|
||||
Ok(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
i18n!(intl.catalog, "Edit {0}"; &title),
|
||||
b,
|
||||
true,
|
||||
&NewPostForm {
|
||||
title: post.title.clone(),
|
||||
subtitle: post.subtitle.clone(),
|
||||
content: source,
|
||||
tags: Tag::for_post(&*conn, post.id)?
|
||||
.into_iter()
|
||||
.filter_map(|t| if !t.is_hashtag {Some(t.tag)} else {None})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
license: post.license.clone(),
|
||||
draft: true,
|
||||
cover: post.cover_id,
|
||||
},
|
||||
!post.published,
|
||||
Some(post),
|
||||
ValidationErrors::default(),
|
||||
medias,
|
||||
cl.0
|
||||
)))
|
||||
}
|
||||
|
||||
|
||||
let source = if !post.source.is_empty() {
|
||||
post.source.clone()
|
||||
} else {
|
||||
post.content.get().clone() // fallback to HTML if the markdown was not stored
|
||||
};
|
||||
|
||||
let medias = Media::for_user(&*conn, user.id)?;
|
||||
let title = post.title.clone();
|
||||
Ok(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
i18n!(intl.catalog, "Edit {0}"; &title),
|
||||
b,
|
||||
true,
|
||||
&NewPostForm {
|
||||
title: post.title.clone(),
|
||||
subtitle: post.subtitle.clone(),
|
||||
content: source,
|
||||
tags: Tag::for_post(&*conn, post.id)?
|
||||
.into_iter()
|
||||
.filter_map(|t| if !t.is_hashtag {Some(t.tag)} else {None})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
license: post.license.clone(),
|
||||
draft: true,
|
||||
cover: post.cover_id,
|
||||
},
|
||||
!post.published,
|
||||
Some(post),
|
||||
ValidationErrors::default(),
|
||||
medias,
|
||||
cl.0
|
||||
)))
|
||||
}
|
||||
|
||||
#[post("/~/<blog>/<slug>/edit", data = "<form>")]
|
||||
pub fn update(blog: String, slug: String, user: User, cl: ContentLen, form: LenientForm<NewPostForm>, worker: Worker, conn: DbConn, intl: I18n, searcher: Searcher)
|
||||
pub fn update(blog: String, slug: String, cl: ContentLen, form: LenientForm<NewPostForm>, rockets: PlumeRocket)
|
||||
-> Result<Redirect, Ructe> {
|
||||
let conn = rockets.conn;
|
||||
let b = Blog::find_by_fqn(&*conn, &blog).expect("post::update: blog error");
|
||||
let mut post = Post::find_by_slug(&*conn, &slug, b.id).expect("post::update: find by slug error");
|
||||
let user = rockets.user.unwrap();
|
||||
let intl = rockets.intl;
|
||||
|
||||
let new_slug = if !post.published {
|
||||
form.title.to_string().to_kebab_case()
|
||||
@@ -219,6 +227,8 @@ pub fn update(blog: String, slug: String, user: User, cl: ContentLen, form: Leni
|
||||
false
|
||||
};
|
||||
|
||||
let searcher = rockets.searcher;
|
||||
let worker = rockets.worker;
|
||||
post.slug = new_slug.clone();
|
||||
post.title = form.title.clone();
|
||||
post.subtitle = form.subtitle.clone();
|
||||
@@ -263,7 +273,7 @@ pub fn update(blog: String, slug: String, user: User, cl: ContentLen, form: Leni
|
||||
b,
|
||||
true,
|
||||
&*form,
|
||||
form.draft.clone(),
|
||||
form.draft,
|
||||
Some(post),
|
||||
errors.clone(),
|
||||
medias.clone(),
|
||||
@@ -296,9 +306,11 @@ pub fn valid_slug(title: &str) -> Result<(), ValidationError> {
|
||||
}
|
||||
|
||||
#[post("/~/<blog_name>/new", data = "<form>")]
|
||||
pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, cl: ContentLen, conn: DbConn, worker: Worker, intl: I18n, searcher: Searcher) -> Result<Redirect, Result<Ructe, ErrorPage>> {
|
||||
pub fn create(blog_name: String, form: LenientForm<NewPostForm>, cl: ContentLen, rockets: PlumeRocket) -> Result<Redirect, Result<Ructe, ErrorPage>> {
|
||||
let conn = rockets.conn;
|
||||
let blog = Blog::find_by_fqn(&*conn, &blog_name).expect("post::create: blog error");;
|
||||
let slug = form.title.to_string().to_kebab_case();
|
||||
let user = rockets.user.unwrap();
|
||||
|
||||
let mut errors = match form.validate() {
|
||||
Ok(_) => ValidationErrors::new(),
|
||||
@@ -315,73 +327,76 @@ pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, cl:
|
||||
if errors.is_empty() {
|
||||
if !user.is_author_in(&*conn, &blog).expect("post::create: is author in error") {
|
||||
// actually it's not "Ok"…
|
||||
Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _)))
|
||||
} else {
|
||||
let (content, mentions, hashtags) = utils::md_to_html(
|
||||
form.content.to_string().as_ref(),
|
||||
&Instance::get_local(&conn).expect("post::create: local instance error").public_domain
|
||||
);
|
||||
|
||||
let post = Post::insert(&*conn, NewPost {
|
||||
blog_id: blog.id,
|
||||
slug: slug.to_string(),
|
||||
title: form.title.to_string(),
|
||||
content: SafeString::new(&content),
|
||||
published: !form.draft,
|
||||
license: form.license.clone(),
|
||||
ap_url: "".to_string(),
|
||||
creation_date: None,
|
||||
subtitle: form.subtitle.clone(),
|
||||
source: form.content.clone(),
|
||||
cover_id: form.cover,
|
||||
},
|
||||
&searcher,
|
||||
).expect("post::create: post save error");
|
||||
|
||||
PostAuthor::insert(&*conn, NewPostAuthor {
|
||||
post_id: post.id,
|
||||
author_id: user.id
|
||||
}).expect("post::create: author save error");
|
||||
|
||||
let tags = form.tags.split(',')
|
||||
.map(|t| t.trim().to_camel_case())
|
||||
.filter(|t| !t.is_empty())
|
||||
.collect::<HashSet<_>>();
|
||||
for tag in tags {
|
||||
Tag::insert(&*conn, NewTag {
|
||||
tag,
|
||||
is_hashtag: false,
|
||||
post_id: post.id
|
||||
}).expect("post::create: tags save error");
|
||||
}
|
||||
for hashtag in hashtags {
|
||||
Tag::insert(&*conn, NewTag {
|
||||
tag: hashtag.to_camel_case(),
|
||||
is_hashtag: true,
|
||||
post_id: post.id
|
||||
}).expect("post::create: hashtags save error");
|
||||
}
|
||||
|
||||
if post.published {
|
||||
for m in mentions {
|
||||
Mention::from_activity(
|
||||
&*conn,
|
||||
&Mention::build_activity(&*conn, &m).expect("post::create: mention build error"),
|
||||
post.id,
|
||||
true,
|
||||
true
|
||||
).expect("post::create: mention save error");
|
||||
}
|
||||
|
||||
let act = post.create_activity(&*conn).expect("posts::create: activity error");
|
||||
let dest = User::one_by_instance(&*conn).expect("posts::create: dest error");
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
}
|
||||
|
||||
Ok(Redirect::to(uri!(details: blog = blog_name, slug = slug, responding_to = _)))
|
||||
return Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _)))
|
||||
}
|
||||
|
||||
let (content, mentions, hashtags) = utils::md_to_html(
|
||||
form.content.to_string().as_ref(),
|
||||
&Instance::get_local(&conn).expect("post::create: local instance error").public_domain
|
||||
);
|
||||
|
||||
let searcher = rockets.searcher;
|
||||
let post = Post::insert(&*conn, NewPost {
|
||||
blog_id: blog.id,
|
||||
slug: slug.to_string(),
|
||||
title: form.title.to_string(),
|
||||
content: SafeString::new(&content),
|
||||
published: !form.draft,
|
||||
license: form.license.clone(),
|
||||
ap_url: "".to_string(),
|
||||
creation_date: None,
|
||||
subtitle: form.subtitle.clone(),
|
||||
source: form.content.clone(),
|
||||
cover_id: form.cover,
|
||||
},
|
||||
&searcher,
|
||||
).expect("post::create: post save error");
|
||||
|
||||
PostAuthor::insert(&*conn, NewPostAuthor {
|
||||
post_id: post.id,
|
||||
author_id: user.id
|
||||
}).expect("post::create: author save error");
|
||||
|
||||
let tags = form.tags.split(',')
|
||||
.map(|t| t.trim().to_camel_case())
|
||||
.filter(|t| !t.is_empty())
|
||||
.collect::<HashSet<_>>();
|
||||
for tag in tags {
|
||||
Tag::insert(&*conn, NewTag {
|
||||
tag,
|
||||
is_hashtag: false,
|
||||
post_id: post.id
|
||||
}).expect("post::create: tags save error");
|
||||
}
|
||||
for hashtag in hashtags {
|
||||
Tag::insert(&*conn, NewTag {
|
||||
tag: hashtag.to_camel_case(),
|
||||
is_hashtag: true,
|
||||
post_id: post.id
|
||||
}).expect("post::create: hashtags save error");
|
||||
}
|
||||
|
||||
if post.published {
|
||||
for m in mentions {
|
||||
Mention::from_activity(
|
||||
&*conn,
|
||||
&Mention::build_activity(&*conn, &m).expect("post::create: mention build error"),
|
||||
post.id,
|
||||
true,
|
||||
true
|
||||
).expect("post::create: mention save error");
|
||||
}
|
||||
|
||||
let act = post.create_activity(&*conn).expect("posts::create: activity error");
|
||||
let dest = User::one_by_instance(&*conn).expect("posts::create: dest error");
|
||||
let worker = rockets.worker;
|
||||
worker.execute(move || broadcast(&user, act, dest));
|
||||
}
|
||||
|
||||
Ok(Redirect::to(uri!(details: blog = blog_name, slug = slug, responding_to = _)))
|
||||
} else {
|
||||
let medias = Media::for_user(&*conn, user.id).expect("posts::create: medias error");
|
||||
let intl = rockets.intl;
|
||||
Err(Ok(render!(posts::new(
|
||||
&(&*conn, &intl.catalog, Some(user)),
|
||||
i18n!(intl.catalog, "New post"),
|
||||
@@ -398,22 +413,28 @@ pub fn create(blog_name: String, form: LenientForm<NewPostForm>, user: User, cl:
|
||||
}
|
||||
|
||||
#[post("/~/<blog_name>/<slug>/delete")]
|
||||
pub fn delete(blog_name: String, slug: String, conn: DbConn, user: User, worker: Worker, searcher: Searcher) -> Result<Redirect, ErrorPage> {
|
||||
pub fn delete(blog_name: String, slug: String, rockets: PlumeRocket) -> Result<Redirect, ErrorPage> {
|
||||
let conn = rockets.conn;
|
||||
let user = rockets.user.unwrap();
|
||||
let post = Blog::find_by_fqn(&*conn, &blog_name)
|
||||
.and_then(|blog| Post::find_by_slug(&*conn, &slug, blog.id));
|
||||
|
||||
if let Ok(post) = post {
|
||||
if !post.get_authors(&*conn)?.into_iter().any(|a| a.id == user.id) {
|
||||
Ok(Redirect::to(uri!(details: blog = blog_name.clone(), slug = slug.clone(), responding_to = _)))
|
||||
} else {
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let delete_activity = post.delete(&(&conn, &searcher))?;
|
||||
let user_c = user.clone();
|
||||
worker.execute(move || broadcast(&user_c, delete_activity, dest));
|
||||
worker.execute_after(Duration::from_secs(10*60), move || {user.rotate_keypair(&conn).expect("Failed to rotate keypair");});
|
||||
|
||||
Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _)))
|
||||
return Ok(Redirect::to(uri!(details: blog = blog_name.clone(), slug = slug.clone(), responding_to = _)))
|
||||
}
|
||||
|
||||
let searcher = rockets.searcher;
|
||||
let worker = rockets.worker;
|
||||
|
||||
let dest = User::one_by_instance(&*conn)?;
|
||||
let delete_activity = post.delete(&(&conn, &searcher))?;
|
||||
let user_c = user.clone();
|
||||
|
||||
worker.execute(move || broadcast(&user_c, delete_activity, dest));
|
||||
worker.execute_after(Duration::from_secs(10*60), move || {user.rotate_keypair(&conn).expect("Failed to rotate keypair");});
|
||||
|
||||
Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _)))
|
||||
} else {
|
||||
Ok(Redirect::to(uri!(super::blogs::details: name = blog_name, page = _)))
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use plume_models::{
|
||||
use routes::Page;
|
||||
use template_utils::Ructe;
|
||||
use Searcher;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Default, FromForm)]
|
||||
pub struct SearchQuery {
|
||||
@@ -57,7 +58,7 @@ macro_rules! param_to_query {
|
||||
pub fn search(query: Option<Form<SearchQuery>>, conn: DbConn, searcher: Searcher, user: Option<User>, intl: I18n) -> Ructe {
|
||||
let query = query.map(|f| f.into_inner()).unwrap_or_default();
|
||||
let page = query.page.unwrap_or_default();
|
||||
let mut parsed_query = Query::from_str(&query.q.as_ref().map(|q| q.as_str()).unwrap_or_default());
|
||||
let mut parsed_query = Query::from_str(&query.q.as_ref().map(|q| q.as_str()).unwrap_or_default()).unwrap_or_default();
|
||||
|
||||
param_to_query!(query, parsed_query; normal: title, subtitle, content, tag,
|
||||
instance, author, blog, lang, license;
|
||||
|
||||
@@ -161,10 +161,10 @@ pub fn password_reset_request(
|
||||
i18n!(intl.catalog, "Password reset"),
|
||||
i18n!(intl.catalog, "Here is the link to reset your password: {0}"; link)
|
||||
) {
|
||||
match *mail.lock().unwrap() {
|
||||
Some(ref mut mail) => { mail.send(message.into()).map_err(|_| eprintln!("Couldn't send password reset mail")).ok(); }
|
||||
None => {}
|
||||
}
|
||||
if let Some(ref mut mail) = *mail.lock().unwrap() {
|
||||
mail
|
||||
.send(message.into())
|
||||
.map_err(|_| eprintln!("Couldn't send password reset mail")).ok(); }
|
||||
}
|
||||
}
|
||||
render!(session::password_reset_request_ok(
|
||||
@@ -214,7 +214,7 @@ pub fn password_reset(
|
||||
form.validate()
|
||||
.and_then(|_| {
|
||||
let mut requests = requests.lock().unwrap();
|
||||
let req = requests.iter().find(|x| x.id == token.clone()).ok_or(to_validation(0))?.clone();
|
||||
let req = requests.iter().find(|x| x.id == token.clone()).ok_or_else(|| to_validation(0))?.clone();
|
||||
if req.creation_date.elapsed().as_secs() < 60 * 60 * 2 { // Reset link is only valid for 2 hours
|
||||
requests.retain(|r| *r != req);
|
||||
let user = User::find_by_email(&*conn, &req.mail).map_err(to_validation)?;
|
||||
|
||||
+7
-6
@@ -23,7 +23,7 @@ use plume_models::{
|
||||
blogs::Blog, db_conn::DbConn, follows, headers::Headers, instance::Instance, posts::{LicensedArticle, Post},
|
||||
reshares::Reshare, users::*,
|
||||
};
|
||||
use routes::{Page, errors::ErrorPage};
|
||||
use routes::{Page, PlumeRocket, errors::ErrorPage};
|
||||
use template_utils::Ructe;
|
||||
use Worker;
|
||||
use Searcher;
|
||||
@@ -39,18 +39,17 @@ pub fn me(user: Option<User>) -> Result<Redirect, Flash<Redirect>> {
|
||||
#[get("/@/<name>", rank = 2)]
|
||||
pub fn details(
|
||||
name: String,
|
||||
conn: DbConn,
|
||||
account: Option<User>,
|
||||
worker: Worker,
|
||||
rockets: PlumeRocket,
|
||||
fetch_articles_conn: DbConn,
|
||||
fetch_followers_conn: DbConn,
|
||||
update_conn: DbConn,
|
||||
intl: I18n,
|
||||
searcher: Searcher,
|
||||
) -> Result<Ructe, ErrorPage> {
|
||||
let conn = rockets.conn;
|
||||
let user = User::find_by_fqn(&*conn, &name)?;
|
||||
let recents = Post::get_recents_for_author(&*conn, &user, 6)?;
|
||||
let reshares = Reshare::get_recents_for_author(&*conn, &user, 6)?;
|
||||
let searcher = rockets.searcher;
|
||||
let worker = rockets.worker;
|
||||
|
||||
if !user.get_instance(&*conn)?.local {
|
||||
// Fetch new articles
|
||||
@@ -99,6 +98,8 @@ pub fn details(
|
||||
}
|
||||
}
|
||||
|
||||
let account = rockets.user;
|
||||
let intl = rockets.intl;
|
||||
Ok(render!(users::details(
|
||||
&(&*conn, &intl.catalog, account.clone()),
|
||||
user.clone(),
|
||||
|
||||
@@ -27,7 +27,7 @@ impl<'r> Responder<'r> for Ructe {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
hasher.write(&self.0);
|
||||
let etag = format!("{:x}", hasher.finish());
|
||||
if r.headers().get("If-None-Match").any(|s| &s[1..s.len()-1] == etag) {
|
||||
if r.headers().get("If-None-Match").any(|s| s[1..s.len()-1] == etag) {
|
||||
Response::build()
|
||||
.status(Status::NotModified)
|
||||
.header(ETag(EntityTag::strong(etag)))
|
||||
|
||||
Reference in New Issue
Block a user