add enum containing all successful route returns (#614)
* add enum containing all successful route returns This enum derives `Responder`, so it can be used as route result. We also implement `From`, so it can be converted * disable clippy warning about this enum There's no use trying to chase this warning. The next warning will be about `Redirect`, which itself is 360 byte, according to @fdb-hiroshima (thanks for the research!) So, if anything, we can try to get Rocket to work on that! * refactor routes/posts to only use one level of Result * admin settings error is not an ErrorPage, so dont use Result<> * refactor: use early return to remove indent of main logic This diff is absolutely atrocious. but the resulting readability is worth it * refactor routes/post for early returns & RespondOrRedirect * refactor routes/session for early returns & use RespondOrRedirect * refactor routes/user -- add another field to enum * refactor routes/blogs for early returns & RespondOrRedirect * refactor routes/blogs for early returns & RespondOrRedirect This is a final refactor of `pub fn update()` for readability. We're removing at least one indentation level.
This commit is contained in:
parent
4b205fa995
commit
3d27e283ad
@ -16,7 +16,7 @@ use plume_models::{
|
|||||||
blog_authors::*, blogs::*, instance::Instance, medias::*, posts::Post, safe_string::SafeString,
|
blog_authors::*, blogs::*, instance::Instance, medias::*, posts::Post, safe_string::SafeString,
|
||||||
users::User, Connection, PlumeRocket,
|
users::User, Connection, PlumeRocket,
|
||||||
};
|
};
|
||||||
use routes::{errors::ErrorPage, Page};
|
use routes::{errors::ErrorPage, Page, RespondOrRedirect};
|
||||||
use template_utils::{IntoContext, Ructe};
|
use template_utils::{IntoContext, Ructe};
|
||||||
|
|
||||||
#[get("/~/<name>?<page>", rank = 2)]
|
#[get("/~/<name>?<page>", rank = 2)]
|
||||||
@ -84,10 +84,7 @@ fn valid_slug(title: &str) -> Result<(), ValidationError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[post("/blogs/new", data = "<form>")]
|
#[post("/blogs/new", data = "<form>")]
|
||||||
pub fn create(
|
pub fn create(form: LenientForm<NewBlogForm>, rockets: PlumeRocket) -> RespondOrRedirect {
|
||||||
form: LenientForm<NewBlogForm>,
|
|
||||||
rockets: PlumeRocket,
|
|
||||||
) -> Result<Flash<Redirect>, Ructe> {
|
|
||||||
let slug = utils::make_actor_id(&form.title);
|
let slug = utils::make_actor_id(&form.title);
|
||||||
let conn = &*rockets.conn;
|
let conn = &*rockets.conn;
|
||||||
let intl = &rockets.intl.catalog;
|
let intl = &rockets.intl.catalog;
|
||||||
@ -111,42 +108,43 @@ pub fn create(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
let blog = Blog::insert(
|
return render!(blogs::new(&rockets.to_context(), &*form, errors)).into();
|
||||||
&*conn,
|
|
||||||
NewBlog::new_local(
|
|
||||||
slug.clone(),
|
|
||||||
form.title.to_string(),
|
|
||||||
String::from(""),
|
|
||||||
Instance::get_local()
|
|
||||||
.expect("blog::create: instance error")
|
|
||||||
.id,
|
|
||||||
)
|
|
||||||
.expect("blog::create: new local error"),
|
|
||||||
)
|
|
||||||
.expect("blog::create: error");
|
|
||||||
|
|
||||||
BlogAuthor::insert(
|
|
||||||
&*conn,
|
|
||||||
NewBlogAuthor {
|
|
||||||
blog_id: blog.id,
|
|
||||||
author_id: user.id,
|
|
||||||
is_owner: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.expect("blog::create: author error");
|
|
||||||
|
|
||||||
Ok(Flash::success(
|
|
||||||
Redirect::to(uri!(details: name = slug.clone(), page = _)),
|
|
||||||
&i18n!(intl, "Your blog was successfully created!"),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Err(render!(blogs::new(&rockets.to_context(), &*form, errors)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let blog = Blog::insert(
|
||||||
|
&*conn,
|
||||||
|
NewBlog::new_local(
|
||||||
|
slug.clone(),
|
||||||
|
form.title.to_string(),
|
||||||
|
String::from(""),
|
||||||
|
Instance::get_local()
|
||||||
|
.expect("blog::create: instance error")
|
||||||
|
.id,
|
||||||
|
)
|
||||||
|
.expect("blog::create: new local error"),
|
||||||
|
)
|
||||||
|
.expect("blog::create: error");
|
||||||
|
|
||||||
|
BlogAuthor::insert(
|
||||||
|
&*conn,
|
||||||
|
NewBlogAuthor {
|
||||||
|
blog_id: blog.id,
|
||||||
|
author_id: user.id,
|
||||||
|
is_owner: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("blog::create: author error");
|
||||||
|
|
||||||
|
Flash::success(
|
||||||
|
Redirect::to(uri!(details: name = slug.clone(), page = _)),
|
||||||
|
&i18n!(intl, "Your blog was successfully created!"),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/~/<name>/delete")]
|
#[post("/~/<name>/delete")]
|
||||||
pub fn delete(name: String, rockets: PlumeRocket) -> Result<Flash<Redirect>, Ructe> {
|
pub fn delete(name: String, rockets: PlumeRocket) -> RespondOrRedirect {
|
||||||
let conn = &*rockets.conn;
|
let conn = &*rockets.conn;
|
||||||
let blog = Blog::find_by_fqn(&rockets, &name).expect("blog::delete: blog not found");
|
let blog = Blog::find_by_fqn(&rockets, &name).expect("blog::delete: blog not found");
|
||||||
|
|
||||||
@ -158,19 +156,21 @@ pub fn delete(name: String, rockets: PlumeRocket) -> Result<Flash<Redirect>, Ruc
|
|||||||
{
|
{
|
||||||
blog.delete(&conn, &rockets.searcher)
|
blog.delete(&conn, &rockets.searcher)
|
||||||
.expect("blog::expect: deletion error");
|
.expect("blog::expect: deletion error");
|
||||||
Ok(Flash::success(
|
Flash::success(
|
||||||
Redirect::to(uri!(super::instance::index)),
|
Redirect::to(uri!(super::instance::index)),
|
||||||
i18n!(rockets.intl.catalog, "Your blog was deleted."),
|
i18n!(rockets.intl.catalog, "Your blog was deleted."),
|
||||||
))
|
)
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
// TODO actually return 403 error code
|
// TODO actually return 403 error code
|
||||||
Err(render!(errors::not_authorized(
|
render!(errors::not_authorized(
|
||||||
&rockets.to_context(),
|
&rockets.to_context(),
|
||||||
i18n!(
|
i18n!(
|
||||||
rockets.intl.catalog,
|
rockets.intl.catalog,
|
||||||
"You are not allowed to delete this blog."
|
"You are not allowed to delete this blog."
|
||||||
)
|
)
|
||||||
)))
|
))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,104 +236,107 @@ pub fn update(
|
|||||||
name: String,
|
name: String,
|
||||||
form: LenientForm<EditForm>,
|
form: LenientForm<EditForm>,
|
||||||
rockets: PlumeRocket,
|
rockets: PlumeRocket,
|
||||||
) -> Result<Flash<Redirect>, Ructe> {
|
) -> RespondOrRedirect {
|
||||||
let conn = &*rockets.conn;
|
let conn = &*rockets.conn;
|
||||||
let intl = &rockets.intl.catalog;
|
let intl = &rockets.intl.catalog;
|
||||||
let mut blog = Blog::find_by_fqn(&rockets, &name).expect("blog::update: blog not found");
|
let mut blog = Blog::find_by_fqn(&rockets, &name).expect("blog::update: blog not found");
|
||||||
if rockets
|
if !rockets
|
||||||
.user
|
.user
|
||||||
.clone()
|
.clone()
|
||||||
.and_then(|u| u.is_author_in(&*conn, &blog).ok())
|
.and_then(|u| u.is_author_in(&*conn, &blog).ok())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
let user = rockets
|
|
||||||
.user
|
|
||||||
.clone()
|
|
||||||
.expect("blogs::edit: User was None while it shouldn't");
|
|
||||||
form.validate()
|
|
||||||
.and_then(|_| {
|
|
||||||
if let Some(icon) = form.icon {
|
|
||||||
if !check_media(&*conn, icon, &user) {
|
|
||||||
let mut errors = ValidationErrors::new();
|
|
||||||
errors.add(
|
|
||||||
"",
|
|
||||||
ValidationError {
|
|
||||||
code: Cow::from("icon"),
|
|
||||||
message: Some(Cow::from(i18n!(
|
|
||||||
intl,
|
|
||||||
"You can't use this media as a blog icon."
|
|
||||||
))),
|
|
||||||
params: HashMap::new(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return Err(errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(banner) = form.banner {
|
|
||||||
if !check_media(&*conn, banner, &user) {
|
|
||||||
let mut errors = ValidationErrors::new();
|
|
||||||
errors.add(
|
|
||||||
"",
|
|
||||||
ValidationError {
|
|
||||||
code: Cow::from("banner"),
|
|
||||||
message: Some(Cow::from(i18n!(
|
|
||||||
intl,
|
|
||||||
"You can't use this media as a blog banner."
|
|
||||||
))),
|
|
||||||
params: HashMap::new(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return Err(errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blog.title = form.title.clone();
|
|
||||||
blog.summary = form.summary.clone();
|
|
||||||
blog.summary_html = SafeString::new(
|
|
||||||
&utils::md_to_html(
|
|
||||||
&form.summary,
|
|
||||||
None,
|
|
||||||
true,
|
|
||||||
Some(Media::get_media_processor(
|
|
||||||
&conn,
|
|
||||||
blog.list_authors(&conn)
|
|
||||||
.expect("Couldn't get list of authors")
|
|
||||||
.iter()
|
|
||||||
.collect(),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
.0,
|
|
||||||
);
|
|
||||||
blog.icon_id = form.icon;
|
|
||||||
blog.banner_id = form.banner;
|
|
||||||
blog.save_changes::<Blog>(&*conn)
|
|
||||||
.expect("Couldn't save blog changes");
|
|
||||||
Ok(Flash::success(
|
|
||||||
Redirect::to(uri!(details: name = name, page = _)),
|
|
||||||
i18n!(intl, "Your blog information have been updated."),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.map_err(|err| {
|
|
||||||
let medias = Media::for_user(&*conn, user.id).expect("Couldn't list media");
|
|
||||||
render!(blogs::edit(
|
|
||||||
&rockets.to_context(),
|
|
||||||
&blog,
|
|
||||||
medias,
|
|
||||||
&*form,
|
|
||||||
err
|
|
||||||
))
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// TODO actually return 403 error code
|
// TODO actually return 403 error code
|
||||||
Err(render!(errors::not_authorized(
|
return render!(errors::not_authorized(
|
||||||
&rockets.to_context(),
|
&rockets.to_context(),
|
||||||
i18n!(
|
i18n!(
|
||||||
rockets.intl.catalog,
|
rockets.intl.catalog,
|
||||||
"You are not allowed to edit this blog."
|
"You are not allowed to edit this blog."
|
||||||
)
|
)
|
||||||
)))
|
))
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let user = rockets
|
||||||
|
.user
|
||||||
|
.clone()
|
||||||
|
.expect("blogs::edit: User was None while it shouldn't");
|
||||||
|
form.validate()
|
||||||
|
.and_then(|_| {
|
||||||
|
if let Some(icon) = form.icon {
|
||||||
|
if !check_media(&*conn, icon, &user) {
|
||||||
|
let mut errors = ValidationErrors::new();
|
||||||
|
errors.add(
|
||||||
|
"",
|
||||||
|
ValidationError {
|
||||||
|
code: Cow::from("icon"),
|
||||||
|
message: Some(Cow::from(i18n!(
|
||||||
|
intl,
|
||||||
|
"You can't use this media as a blog icon."
|
||||||
|
))),
|
||||||
|
params: HashMap::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return Err(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(banner) = form.banner {
|
||||||
|
if !check_media(&*conn, banner, &user) {
|
||||||
|
let mut errors = ValidationErrors::new();
|
||||||
|
errors.add(
|
||||||
|
"",
|
||||||
|
ValidationError {
|
||||||
|
code: Cow::from("banner"),
|
||||||
|
message: Some(Cow::from(i18n!(
|
||||||
|
intl,
|
||||||
|
"You can't use this media as a blog banner."
|
||||||
|
))),
|
||||||
|
params: HashMap::new(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return Err(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blog.title = form.title.clone();
|
||||||
|
blog.summary = form.summary.clone();
|
||||||
|
blog.summary_html = SafeString::new(
|
||||||
|
&utils::md_to_html(
|
||||||
|
&form.summary,
|
||||||
|
None,
|
||||||
|
true,
|
||||||
|
Some(Media::get_media_processor(
|
||||||
|
&conn,
|
||||||
|
blog.list_authors(&conn)
|
||||||
|
.expect("Couldn't get list of authors")
|
||||||
|
.iter()
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.0,
|
||||||
|
);
|
||||||
|
blog.icon_id = form.icon;
|
||||||
|
blog.banner_id = form.banner;
|
||||||
|
blog.save_changes::<Blog>(&*conn)
|
||||||
|
.expect("Couldn't save blog changes");
|
||||||
|
Ok(Flash::success(
|
||||||
|
Redirect::to(uri!(details: name = name, page = _)),
|
||||||
|
i18n!(intl, "Your blog information have been updated."),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
let medias = Media::for_user(&*conn, user.id).expect("Couldn't list media");
|
||||||
|
render!(blogs::edit(
|
||||||
|
&rockets.to_context(),
|
||||||
|
&blog,
|
||||||
|
medias,
|
||||||
|
&*form,
|
||||||
|
err
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/~/<name>/outbox")]
|
#[get("/~/<name>/outbox")]
|
||||||
|
@ -13,7 +13,7 @@ use plume_models::{
|
|||||||
admin::Admin, comments::Comment, db_conn::DbConn, headers::Headers, instance::*, posts::Post,
|
admin::Admin, comments::Comment, db_conn::DbConn, headers::Headers, instance::*, posts::Post,
|
||||||
safe_string::SafeString, users::User, Error, PlumeRocket, CONFIG,
|
safe_string::SafeString, users::User, Error, PlumeRocket, CONFIG,
|
||||||
};
|
};
|
||||||
use routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page};
|
use routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page, RespondOrRedirect};
|
||||||
use template_utils::{IntoContext, Ructe};
|
use template_utils::{IntoContext, Ructe};
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
@ -114,36 +114,36 @@ pub fn update_settings(
|
|||||||
_admin: Admin,
|
_admin: Admin,
|
||||||
form: LenientForm<InstanceSettingsForm>,
|
form: LenientForm<InstanceSettingsForm>,
|
||||||
rockets: PlumeRocket,
|
rockets: PlumeRocket,
|
||||||
) -> Result<Flash<Redirect>, Ructe> {
|
) -> RespondOrRedirect {
|
||||||
let conn = &*rockets.conn;
|
let conn = &*rockets.conn;
|
||||||
form.validate()
|
if let Err(e) = form.validate() {
|
||||||
.and_then(|_| {
|
let local_inst =
|
||||||
let instance =
|
Instance::get_local().expect("instance::update_settings: local instance error");
|
||||||
Instance::get_local().expect("instance::update_settings: local instance error");
|
render!(instance::admin(
|
||||||
instance
|
&rockets.to_context(),
|
||||||
.update(
|
local_inst,
|
||||||
conn,
|
form.clone(),
|
||||||
form.name.clone(),
|
e
|
||||||
form.open_registrations,
|
))
|
||||||
form.short_description.clone(),
|
.into()
|
||||||
form.long_description.clone(),
|
} else {
|
||||||
)
|
let instance =
|
||||||
.expect("instance::update_settings: save error");
|
Instance::get_local().expect("instance::update_settings: local instance error");
|
||||||
Ok(Flash::success(
|
instance
|
||||||
Redirect::to(uri!(admin)),
|
.update(
|
||||||
i18n!(rockets.intl.catalog, "Instance settings have been saved."),
|
conn,
|
||||||
))
|
form.name.clone(),
|
||||||
})
|
form.open_registrations,
|
||||||
.or_else(|e| {
|
form.short_description.clone(),
|
||||||
let local_inst =
|
form.long_description.clone(),
|
||||||
Instance::get_local().expect("instance::update_settings: local instance error");
|
)
|
||||||
Err(render!(instance::admin(
|
.expect("instance::update_settings: save error");
|
||||||
&rockets.to_context(),
|
Flash::success(
|
||||||
local_inst,
|
Redirect::to(uri!(admin)),
|
||||||
form.clone(),
|
i18n!(rockets.intl.catalog, "Instance settings have been saved."),
|
||||||
e
|
)
|
||||||
)))
|
.into()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/admin/instances?<page>")]
|
#[get("/admin/instances?<page>")]
|
||||||
|
@ -38,76 +38,75 @@ pub fn upload(
|
|||||||
ct: &ContentType,
|
ct: &ContentType,
|
||||||
conn: DbConn,
|
conn: DbConn,
|
||||||
) -> Result<Redirect, status::BadRequest<&'static str>> {
|
) -> Result<Redirect, status::BadRequest<&'static str>> {
|
||||||
if ct.is_form_data() {
|
if !ct.is_form_data() {
|
||||||
let (_, boundary) = ct
|
return Ok(Redirect::to(uri!(new)));
|
||||||
.params()
|
}
|
||||||
.find(|&(k, _)| k == "boundary")
|
|
||||||
.ok_or_else(|| status::BadRequest(Some("No boundary")))?;
|
|
||||||
|
|
||||||
match Multipart::with_body(data.open(), boundary).save().temp() {
|
let (_, boundary) = ct
|
||||||
SaveResult::Full(entries) => {
|
.params()
|
||||||
let fields = entries.fields;
|
.find(|&(k, _)| k == "boundary")
|
||||||
|
.ok_or_else(|| status::BadRequest(Some("No boundary")))?;
|
||||||
|
|
||||||
let filename = fields
|
if let SaveResult::Full(entries) = Multipart::with_body(data.open(), boundary).save().temp() {
|
||||||
.get("file")
|
let fields = entries.fields;
|
||||||
.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
|
|
||||||
let ext = filename
|
|
||||||
.and_then(|f| {
|
|
||||||
f.rsplit('.')
|
|
||||||
.next()
|
|
||||||
.and_then(|ext| {
|
|
||||||
if ext.chars().any(|c| !c.is_alphanumeric()) {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(ext.to_lowercase())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(|ext| format!(".{}", ext))
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
let dest = format!("static/media/{}{}", GUID::rand().to_string(), ext);
|
|
||||||
|
|
||||||
match fields["file"][0].data {
|
let filename = fields
|
||||||
SavedData::Bytes(ref bytes) => fs::write(&dest, bytes)
|
.get("file")
|
||||||
.map_err(|_| status::BadRequest(Some("Couldn't save upload")))?,
|
.and_then(|v| v.iter().next())
|
||||||
SavedData::File(ref path, _) => {
|
.ok_or_else(|| status::BadRequest(Some("No file uploaded")))?
|
||||||
fs::copy(path, &dest)
|
.headers
|
||||||
.map_err(|_| status::BadRequest(Some("Couldn't copy upload")))?;
|
.filename
|
||||||
}
|
.clone();
|
||||||
_ => {
|
// Remove extension if it contains something else than just letters and numbers
|
||||||
return Ok(Redirect::to(uri!(new)));
|
let ext = filename
|
||||||
}
|
.and_then(|f| {
|
||||||
}
|
f.rsplit('.')
|
||||||
|
.next()
|
||||||
let has_cw = !read(&fields["cw"][0].data)
|
.and_then(|ext| {
|
||||||
.map(|cw| cw.is_empty())
|
if ext.chars().any(|c| !c.is_alphanumeric()) {
|
||||||
.unwrap_or(false);
|
|
||||||
let media = Media::insert(
|
|
||||||
&*conn,
|
|
||||||
NewMedia {
|
|
||||||
file_path: dest,
|
|
||||||
alt_text: read(&fields["alt"][0].data)?,
|
|
||||||
is_remote: false,
|
|
||||||
remote_url: None,
|
|
||||||
sensitive: has_cw,
|
|
||||||
content_warning: if has_cw {
|
|
||||||
Some(read(&fields["cw"][0].data)?)
|
|
||||||
} else {
|
|
||||||
None
|
None
|
||||||
},
|
} else {
|
||||||
owner_id: user.id,
|
Some(ext.to_lowercase())
|
||||||
},
|
}
|
||||||
)
|
})
|
||||||
.map_err(|_| status::BadRequest(Some("Error while saving media")))?;
|
.map(|ext| format!(".{}", ext))
|
||||||
Ok(Redirect::to(uri!(details: id = media.id)))
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
let dest = format!("static/media/{}{}", GUID::rand().to_string(), ext);
|
||||||
|
|
||||||
|
match fields["file"][0].data {
|
||||||
|
SavedData::Bytes(ref bytes) => fs::write(&dest, bytes)
|
||||||
|
.map_err(|_| status::BadRequest(Some("Couldn't save upload")))?,
|
||||||
|
SavedData::File(ref path, _) => {
|
||||||
|
fs::copy(path, &dest)
|
||||||
|
.map_err(|_| status::BadRequest(Some("Couldn't copy upload")))?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Ok(Redirect::to(uri!(new)));
|
||||||
}
|
}
|
||||||
SaveResult::Partial(_, _) | SaveResult::Error(_) => Ok(Redirect::to(uri!(new))),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let has_cw = !read(&fields["cw"][0].data)
|
||||||
|
.map(|cw| cw.is_empty())
|
||||||
|
.unwrap_or(false);
|
||||||
|
let media = Media::insert(
|
||||||
|
&*conn,
|
||||||
|
NewMedia {
|
||||||
|
file_path: dest,
|
||||||
|
alt_text: read(&fields["alt"][0].data)?,
|
||||||
|
is_remote: false,
|
||||||
|
remote_url: None,
|
||||||
|
sensitive: has_cw,
|
||||||
|
content_warning: if has_cw {
|
||||||
|
Some(read(&fields["cw"][0].data)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
owner_id: user.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|_| status::BadRequest(Some("Error while saving media")))?;
|
||||||
|
Ok(Redirect::to(uri!(details: id = media.id)))
|
||||||
} else {
|
} else {
|
||||||
Ok(Redirect::to(uri!(new)))
|
Ok(Redirect::to(uri!(new)))
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,51 @@ use rocket::{
|
|||||||
RawStr, Status,
|
RawStr, Status,
|
||||||
},
|
},
|
||||||
request::{self, FromFormValue, FromRequest, Request},
|
request::{self, FromFormValue, FromRequest, Request},
|
||||||
response::NamedFile,
|
response::{Flash, NamedFile, Redirect},
|
||||||
Outcome,
|
Outcome,
|
||||||
};
|
};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use template_utils::Ructe;
|
||||||
|
|
||||||
use plume_models::{posts::Post, Connection};
|
use plume_models::{posts::Post, Connection};
|
||||||
|
|
||||||
const ITEMS_PER_PAGE: i32 = 12;
|
const ITEMS_PER_PAGE: i32 = 12;
|
||||||
|
|
||||||
|
/// Special return type used for routes that "cannot fail", and instead
|
||||||
|
/// `Redirect`, or `Flash<Redirect>`, when we cannot deliver a `Ructe` Response
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
#[derive(Responder)]
|
||||||
|
pub enum RespondOrRedirect {
|
||||||
|
Response(Ructe),
|
||||||
|
FlashResponse(Flash<Ructe>),
|
||||||
|
Redirect(Redirect),
|
||||||
|
FlashRedirect(Flash<Redirect>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Ructe> for RespondOrRedirect {
|
||||||
|
fn from(response: Ructe) -> Self {
|
||||||
|
RespondOrRedirect::Response(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Flash<Ructe>> for RespondOrRedirect {
|
||||||
|
fn from(response: Flash<Ructe>) -> Self {
|
||||||
|
RespondOrRedirect::FlashResponse(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Redirect> for RespondOrRedirect {
|
||||||
|
fn from(redirect: Redirect) -> Self {
|
||||||
|
RespondOrRedirect::Redirect(redirect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Flash<Redirect>> for RespondOrRedirect {
|
||||||
|
fn from(redirect: Flash<Redirect>) -> Self {
|
||||||
|
RespondOrRedirect::FlashRedirect(redirect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Shrinkwrap, Copy, Clone, UriDisplayQuery)]
|
#[derive(Shrinkwrap, Copy, Clone, UriDisplayQuery)]
|
||||||
pub struct Page(i32);
|
pub struct Page(i32);
|
||||||
|
|
||||||
|
@ -26,7 +26,9 @@ use plume_models::{
|
|||||||
users::User,
|
users::User,
|
||||||
Error, PlumeRocket,
|
Error, PlumeRocket,
|
||||||
};
|
};
|
||||||
use routes::{comments::NewCommentForm, errors::ErrorPage, ContentLen, RemoteForm};
|
use routes::{
|
||||||
|
comments::NewCommentForm, errors::ErrorPage, ContentLen, RemoteForm, RespondOrRedirect,
|
||||||
|
};
|
||||||
use template_utils::{IntoContext, Ructe};
|
use template_utils::{IntoContext, Ructe};
|
||||||
|
|
||||||
#[get("/~/<blog>/<slug>?<responding_to>", rank = 4)]
|
#[get("/~/<blog>/<slug>?<responding_to>", rank = 4)]
|
||||||
@ -40,17 +42,23 @@ pub fn details(
|
|||||||
let user = rockets.user.clone();
|
let user = rockets.user.clone();
|
||||||
let blog = Blog::find_by_fqn(&rockets, &blog)?;
|
let blog = Blog::find_by_fqn(&rockets, &blog)?;
|
||||||
let post = Post::find_by_slug(&*conn, &slug, blog.id)?;
|
let post = Post::find_by_slug(&*conn, &slug, blog.id)?;
|
||||||
if post.published
|
if !(post.published
|
||||||
|| post
|
|| post
|
||||||
.get_authors(&*conn)?
|
.get_authors(&*conn)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0))
|
.any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0)))
|
||||||
{
|
{
|
||||||
let comments = CommentTree::from_post(&*conn, &post, user.as_ref())?;
|
return Ok(render!(errors::not_authorized(
|
||||||
|
&rockets.to_context(),
|
||||||
|
i18n!(rockets.intl.catalog, "This post isn't published yet.")
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
let previous = responding_to.and_then(|r| Comment::get(&*conn, r).ok());
|
let comments = CommentTree::from_post(&*conn, &post, user.as_ref())?;
|
||||||
|
|
||||||
Ok(render!(posts::details(
|
let previous = responding_to.and_then(|r| Comment::get(&*conn, r).ok());
|
||||||
|
|
||||||
|
Ok(render!(posts::details(
|
||||||
&rockets.to_context(),
|
&rockets.to_context(),
|
||||||
post.clone(),
|
post.clone(),
|
||||||
blog,
|
blog,
|
||||||
@ -87,12 +95,6 @@ pub fn details(
|
|||||||
user.and_then(|u| u.is_following(&*conn, post.get_authors(&*conn).ok()?[0].id).ok()).unwrap_or(false),
|
user.and_then(|u| u.is_following(&*conn, post.get_authors(&*conn).ok()?[0].id).ok()).unwrap_or(false),
|
||||||
post.get_authors(&*conn)?[0].clone()
|
post.get_authors(&*conn)?[0].clone()
|
||||||
)))
|
)))
|
||||||
} else {
|
|
||||||
Ok(render!(errors::not_authorized(
|
|
||||||
&rockets.to_context(),
|
|
||||||
i18n!(rockets.intl.catalog, "This post isn't published yet.")
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/~/<blog>/<slug>", rank = 3)]
|
#[get("/~/<blog>/<slug>", rank = 3)]
|
||||||
@ -219,7 +221,7 @@ pub fn update(
|
|||||||
cl: ContentLen,
|
cl: ContentLen,
|
||||||
form: LenientForm<NewPostForm>,
|
form: LenientForm<NewPostForm>,
|
||||||
rockets: PlumeRocket,
|
rockets: PlumeRocket,
|
||||||
) -> Result<Flash<Redirect>, Ructe> {
|
) -> RespondOrRedirect {
|
||||||
let conn = &*rockets.conn;
|
let conn = &*rockets.conn;
|
||||||
let b = Blog::find_by_fqn(&rockets, &blog).expect("post::update: blog error");
|
let b = Blog::find_by_fqn(&rockets, &blog).expect("post::update: blog error");
|
||||||
let mut post =
|
let mut post =
|
||||||
@ -255,10 +257,11 @@ pub fn update(
|
|||||||
.expect("posts::update: is author in error")
|
.expect("posts::update: is author in error")
|
||||||
{
|
{
|
||||||
// actually it's not "Ok"…
|
// actually it's not "Ok"…
|
||||||
Ok(Flash::error(
|
Flash::error(
|
||||||
Redirect::to(uri!(super::blogs::details: name = blog, page = _)),
|
Redirect::to(uri!(super::blogs::details: name = blog, page = _)),
|
||||||
i18n!(&intl, "You are not allowed to publish on this blog."),
|
i18n!(&intl, "You are not allowed to publish on this blog."),
|
||||||
))
|
)
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
let (content, mentions, hashtags) = utils::md_to_html(
|
let (content, mentions, hashtags) = utils::md_to_html(
|
||||||
form.content.to_string().as_ref(),
|
form.content.to_string().as_ref(),
|
||||||
@ -345,14 +348,15 @@ pub fn update(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Flash::success(
|
Flash::success(
|
||||||
Redirect::to(uri!(details: blog = blog, slug = new_slug, responding_to = _)),
|
Redirect::to(uri!(details: blog = blog, slug = new_slug, responding_to = _)),
|
||||||
i18n!(intl, "Your article has been updated."),
|
i18n!(intl, "Your article has been updated."),
|
||||||
))
|
)
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let medias = Media::for_user(&*conn, user.id).expect("posts:update: medias error");
|
let medias = Media::for_user(&*conn, user.id).expect("posts:update: medias error");
|
||||||
Err(render!(posts::new(
|
render!(posts::new(
|
||||||
&rockets.to_context(),
|
&rockets.to_context(),
|
||||||
i18n!(intl, "Edit {0}"; &form.title),
|
i18n!(intl, "Edit {0}"; &form.title),
|
||||||
b,
|
b,
|
||||||
@ -363,7 +367,8 @@ pub fn update(
|
|||||||
errors.clone(),
|
errors.clone(),
|
||||||
medias.clone(),
|
medias.clone(),
|
||||||
cl.0
|
cl.0
|
||||||
)))
|
))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +401,7 @@ pub fn create(
|
|||||||
form: LenientForm<NewPostForm>,
|
form: LenientForm<NewPostForm>,
|
||||||
cl: ContentLen,
|
cl: ContentLen,
|
||||||
rockets: PlumeRocket,
|
rockets: PlumeRocket,
|
||||||
) -> Result<Flash<Redirect>, Result<Ructe, ErrorPage>> {
|
) -> Result<RespondOrRedirect, ErrorPage> {
|
||||||
let conn = &*rockets.conn;
|
let conn = &*rockets.conn;
|
||||||
let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("post::create: blog error");;
|
let blog = Blog::find_by_fqn(&rockets, &blog_name).expect("post::create: blog error");;
|
||||||
let slug = form.title.to_string().to_kebab_case();
|
let slug = form.title.to_string().to_kebab_case();
|
||||||
@ -429,7 +434,8 @@ pub fn create(
|
|||||||
&rockets.intl.catalog,
|
&rockets.intl.catalog,
|
||||||
"You are not allowed to publish on this blog."
|
"You are not allowed to publish on this blog."
|
||||||
),
|
),
|
||||||
));
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let (content, mentions, hashtags) = utils::md_to_html(
|
let (content, mentions, hashtags) = utils::md_to_html(
|
||||||
@ -530,10 +536,11 @@ pub fn create(
|
|||||||
Ok(Flash::success(
|
Ok(Flash::success(
|
||||||
Redirect::to(uri!(details: blog = blog_name, slug = slug, responding_to = _)),
|
Redirect::to(uri!(details: blog = blog_name, slug = slug, responding_to = _)),
|
||||||
i18n!(&rockets.intl.catalog, "Your article has been saved."),
|
i18n!(&rockets.intl.catalog, "Your article has been saved."),
|
||||||
))
|
)
|
||||||
|
.into())
|
||||||
} else {
|
} else {
|
||||||
let medias = Media::for_user(&*conn, user.id).expect("posts::create: medias error");
|
let medias = Media::for_user(&*conn, user.id).expect("posts::create: medias error");
|
||||||
Err(Ok(render!(posts::new(
|
Ok(render!(posts::new(
|
||||||
&rockets.to_context(),
|
&rockets.to_context(),
|
||||||
i18n!(rockets.intl.catalog, "New article"),
|
i18n!(rockets.intl.catalog, "New article"),
|
||||||
blog,
|
blog,
|
||||||
@ -544,7 +551,8 @@ pub fn create(
|
|||||||
errors.clone(),
|
errors.clone(),
|
||||||
medias,
|
medias,
|
||||||
cl.0
|
cl.0
|
||||||
))))
|
))
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,14 +635,14 @@ pub fn remote_interact_post(
|
|||||||
blog_name: String,
|
blog_name: String,
|
||||||
slug: String,
|
slug: String,
|
||||||
remote: LenientForm<RemoteForm>,
|
remote: LenientForm<RemoteForm>,
|
||||||
) -> Result<Result<Ructe, Redirect>, ErrorPage> {
|
) -> Result<RespondOrRedirect, ErrorPage> {
|
||||||
let target = Blog::find_by_fqn(&rockets, &blog_name)
|
let target = Blog::find_by_fqn(&rockets, &blog_name)
|
||||||
.and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?;
|
.and_then(|blog| Post::find_by_slug(&rockets.conn, &slug, blog.id))?;
|
||||||
if let Some(uri) = User::fetch_remote_interact_uri(&remote.remote)
|
if let Some(uri) = User::fetch_remote_interact_uri(&remote.remote)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|uri| rt_format!(uri, uri = target.ap_url).ok())
|
.and_then(|uri| rt_format!(uri, uri = target.ap_url).ok())
|
||||||
{
|
{
|
||||||
Ok(Err(Redirect::to(uri)))
|
Ok(Redirect::to(uri).into())
|
||||||
} else {
|
} else {
|
||||||
let mut errs = ValidationErrors::new();
|
let mut errs = ValidationErrors::new();
|
||||||
errs.add("remote", ValidationError {
|
errs.add("remote", ValidationError {
|
||||||
@ -643,13 +651,14 @@ pub fn remote_interact_post(
|
|||||||
params: HashMap::new(),
|
params: HashMap::new(),
|
||||||
});
|
});
|
||||||
//could not get your remote url?
|
//could not get your remote url?
|
||||||
Ok(Ok(render!(posts::remote_interact(
|
Ok(render!(posts::remote_interact(
|
||||||
&rockets.to_context(),
|
&rockets.to_context(),
|
||||||
target,
|
target,
|
||||||
super::session::LoginForm::default(),
|
super::session::LoginForm::default(),
|
||||||
ValidationErrors::default(),
|
ValidationErrors::default(),
|
||||||
remote.clone(),
|
remote.clone(),
|
||||||
errs
|
errs
|
||||||
))))
|
))
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ use rocket::{
|
|||||||
State,
|
State,
|
||||||
};
|
};
|
||||||
use rocket_i18n::I18n;
|
use rocket_i18n::I18n;
|
||||||
|
use routes::RespondOrRedirect;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
@ -45,7 +46,7 @@ pub fn create(
|
|||||||
form: LenientForm<LoginForm>,
|
form: LenientForm<LoginForm>,
|
||||||
mut cookies: Cookies,
|
mut cookies: Cookies,
|
||||||
rockets: PlumeRocket,
|
rockets: PlumeRocket,
|
||||||
) -> Result<Flash<Redirect>, Ructe> {
|
) -> RespondOrRedirect {
|
||||||
let conn = &*rockets.conn;
|
let conn = &*rockets.conn;
|
||||||
let user = User::find_by_email(&*conn, &form.email_or_name)
|
let user = User::find_by_email(&*conn, &form.email_or_name)
|
||||||
.or_else(|_| User::find_by_fqn(&rockets, &form.email_or_name));
|
.or_else(|_| User::find_by_fqn(&rockets, &form.email_or_name));
|
||||||
@ -76,48 +77,43 @@ pub fn create(
|
|||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
if errors.is_empty() {
|
if !errors.is_empty() {
|
||||||
cookies.add_private(
|
return render!(session::login(&rockets.to_context(), None, &*form, errors)).into();
|
||||||
Cookie::build(AUTH_COOKIE, user_id)
|
}
|
||||||
.same_site(SameSite::Lax)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
let destination = rockets
|
|
||||||
.flash_msg
|
|
||||||
.clone()
|
|
||||||
.and_then(
|
|
||||||
|(name, msg)| {
|
|
||||||
if name == "callback" {
|
|
||||||
Some(msg)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|| "/".to_owned());
|
|
||||||
|
|
||||||
let uri = Uri::parse(&destination)
|
cookies.add_private(
|
||||||
.map(IntoOwned::into_owned)
|
Cookie::build(AUTH_COOKIE, user_id)
|
||||||
.map_err(|_| {
|
.same_site(SameSite::Lax)
|
||||||
render!(session::login(
|
.finish(),
|
||||||
&(conn, &rockets.intl.catalog, None, None),
|
);
|
||||||
None,
|
let destination = rockets
|
||||||
&*form,
|
.flash_msg
|
||||||
errors
|
.clone()
|
||||||
))
|
.and_then(
|
||||||
})?;
|
|(name, msg)| {
|
||||||
|
if name == "callback" {
|
||||||
|
Some(msg)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|| "/".to_owned());
|
||||||
|
|
||||||
Ok(Flash::success(
|
if let Ok(uri) = Uri::parse(&destination).map(IntoOwned::into_owned) {
|
||||||
|
Flash::success(
|
||||||
Redirect::to(uri),
|
Redirect::to(uri),
|
||||||
i18n!(&rockets.intl.catalog, "You are now connected."),
|
i18n!(&rockets.intl.catalog, "You are now connected."),
|
||||||
))
|
)
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
Err(render!(session::login(
|
render!(session::login(
|
||||||
&rockets.to_context(),
|
&(conn, &rockets.intl.catalog, None, None),
|
||||||
None,
|
None,
|
||||||
&*form,
|
&*form,
|
||||||
errors
|
errors
|
||||||
)))
|
))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,14 +25,14 @@ use plume_models::{
|
|||||||
users::*,
|
users::*,
|
||||||
Error, PlumeRocket,
|
Error, PlumeRocket,
|
||||||
};
|
};
|
||||||
use routes::{errors::ErrorPage, Page, RemoteForm};
|
use routes::{errors::ErrorPage, Page, RemoteForm, RespondOrRedirect};
|
||||||
use template_utils::{IntoContext, Ructe};
|
use template_utils::{IntoContext, Ructe};
|
||||||
|
|
||||||
#[get("/me")]
|
#[get("/me")]
|
||||||
pub fn me(user: Option<User>) -> Result<Redirect, Flash<Redirect>> {
|
pub fn me(user: Option<User>) -> RespondOrRedirect {
|
||||||
match user {
|
match user {
|
||||||
Some(user) => Ok(Redirect::to(uri!(details: name = user.username))),
|
Some(user) => Redirect::to(uri!(details: name = user.username)).into(),
|
||||||
None => Err(utils::requires_login("", uri!(me))),
|
None => utils::requires_login("", uri!(me)).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ pub fn follow_not_connected(
|
|||||||
name: String,
|
name: String,
|
||||||
remote_form: Option<LenientForm<RemoteForm>>,
|
remote_form: Option<LenientForm<RemoteForm>>,
|
||||||
i18n: I18n,
|
i18n: I18n,
|
||||||
) -> Result<Result<Flash<Ructe>, Redirect>, ErrorPage> {
|
) -> Result<RespondOrRedirect, ErrorPage> {
|
||||||
let target = User::find_by_fqn(&rockets, &name)?;
|
let target = User::find_by_fqn(&rockets, &name)?;
|
||||||
if let Some(remote_form) = remote_form {
|
if let Some(remote_form) = remote_form {
|
||||||
if let Some(uri) = User::fetch_remote_interact_uri(&remote_form)
|
if let Some(uri) = User::fetch_remote_interact_uri(&remote_form)
|
||||||
@ -207,7 +207,7 @@ pub fn follow_not_connected(
|
|||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
Ok(Err(Redirect::to(uri)))
|
Ok(Redirect::to(uri).into())
|
||||||
} else {
|
} else {
|
||||||
let mut err = ValidationErrors::default();
|
let mut err = ValidationErrors::default();
|
||||||
err.add("remote",
|
err.add("remote",
|
||||||
@ -217,7 +217,7 @@ pub fn follow_not_connected(
|
|||||||
params: HashMap::new(),
|
params: HashMap::new(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
Ok(Ok(Flash::new(
|
Ok(Flash::new(
|
||||||
render!(users::follow_remote(
|
render!(users::follow_remote(
|
||||||
&rockets.to_context(),
|
&rockets.to_context(),
|
||||||
target,
|
target,
|
||||||
@ -228,10 +228,11 @@ pub fn follow_not_connected(
|
|||||||
)),
|
)),
|
||||||
"callback",
|
"callback",
|
||||||
uri!(follow: name = name).to_string(),
|
uri!(follow: name = name).to_string(),
|
||||||
)))
|
)
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(Ok(Flash::new(
|
Ok(Flash::new(
|
||||||
render!(users::follow_remote(
|
render!(users::follow_remote(
|
||||||
&rockets.to_context(),
|
&rockets.to_context(),
|
||||||
target,
|
target,
|
||||||
@ -243,7 +244,8 @@ pub fn follow_not_connected(
|
|||||||
)),
|
)),
|
||||||
"callback",
|
"callback",
|
||||||
uri!(follow: name = name).to_string(),
|
uri!(follow: name = name).to_string(),
|
||||||
)))
|
)
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user