feat(handlers/render): Display edit form for user's own posts

Displays an edit form for posts that are owned by a user (which is
currently defined as "email addresses match").
This commit is contained in:
Vincent Ambo 2018-04-15 23:09:44 +02:00
parent 7a55786552
commit 705097dab9
5 changed files with 63 additions and 2 deletions

View file

@ -54,6 +54,9 @@ pub enum ConverseError {
#[fail(display = "error occured running timer: {}", error)] #[fail(display = "error occured running timer: {}", error)]
Timer { error: tokio_timer::Error }, Timer { error: tokio_timer::Error },
#[fail(display = "user does not have permission to edit post {}", id)]
PostEditForbidden { id: i32 },
// This variant is used as a catch-all for wrapping // This variant is used as a catch-all for wrapping
// actix-web-compatible response errors, such as the errors it // actix-web-compatible response errors, such as the errors it
// throws itself. // throws itself.

View file

@ -189,6 +189,41 @@ pub fn reply_thread(state: State<AppState>,
.responder() .responder()
} }
/// This handler presents the user with the form to edit a post. If
/// the user attempts to edit a post that they do not have access to,
/// they are currently ungracefully redirected back to the post
/// itself.
pub fn edit_form(state: State<AppState>,
mut req: HttpRequest<AppState>,
query: Path<GetPost>) -> ConverseResponse {
let author: Option<Author> = req.session().get(AUTHOR)
.unwrap_or_else(|_| None);
state.db.send(query.into_inner())
.flatten()
.from_err()
.and_then(move |post| {
if let Some(author) = author {
if author.email.eq(&post.author_email) {
return Ok(post);
}
}
Err(ConverseError::PostEditForbidden { id: post.id })
})
.and_then(move |post| {
let edit_msg = EditPostPage {
id: post.id,
post: post.body,
};
state.renderer.send(edit_msg).from_err()
})
.flatten()
.map(|page| HttpResponse::Ok().content_type(HTML).body(page))
.responder()
}
/// This handler executes a full-text search on the forum database and /// This handler executes a full-text search on the forum database and
/// displays the results to the user. /// displays the results to the user.
pub fn search_forum(state: State<AppState>, pub fn search_forum(state: State<AppState>,

View file

@ -181,6 +181,7 @@ fn start_http_server(base_url: String,
.resource("/thread/submit", |r| r.method(Method::POST).with3(submit_thread)) .resource("/thread/submit", |r| r.method(Method::POST).with3(submit_thread))
.resource("/thread/reply", |r| r.method(Method::POST).with3(reply_thread)) .resource("/thread/reply", |r| r.method(Method::POST).with3(reply_thread))
.resource("/thread/{id}", |r| r.method(Method::GET).with3(forum_thread)) .resource("/thread/{id}", |r| r.method(Method::GET).with3(forum_thread))
.resource("/post/{id}/edit", |r| r.method(Method::GET).with3(edit_form))
.resource("/search", |r| r.method(Method::GET).with2(search_forum)) .resource("/search", |r| r.method(Method::GET).with2(search_forum))
.resource("/oidc/login", |r| r.method(Method::GET).with(login)) .resource("/oidc/login", |r| r.method(Method::GET).with(login))
.resource("/oidc/callback", |r| r.method(Method::POST).with3(callback)); .resource("/oidc/callback", |r| r.method(Method::POST).with3(callback));

View file

@ -200,7 +200,7 @@ impl Handler<NewThreadPage> for Renderer {
type Result = Result<String>; type Result = Result<String>;
fn handle(&mut self, msg: NewThreadPage, _: &mut Self::Context) -> Self::Result { fn handle(&mut self, msg: NewThreadPage, _: &mut Self::Context) -> Self::Result {
let ctx: FormContext = FormContext { let ctx = FormContext {
alerts: msg.alerts, alerts: msg.alerts,
title: msg.title, title: msg.title,
post: msg.post, post: msg.post,
@ -210,6 +210,28 @@ impl Handler<NewThreadPage> for Renderer {
} }
} }
/// Message used to render post editing page.
pub struct EditPostPage {
pub id: i32,
pub post: String,
}
message!(EditPostPage, Result<String>);
impl Handler<EditPostPage> for Renderer {
type Result = Result<String>;
fn handle(&mut self, msg: EditPostPage, _: &mut Self::Context) -> Self::Result {
let ctx = FormContext {
mode: EditingMode::EditPost,
id: Some(msg.id),
post: Some(msg.post),
..Default::default()
};
Ok(self.tera.render("post.html", &ctx)?)
}
}
/// Message used to render search results /// Message used to render search results
#[derive(Serialize)] #[derive(Serialize)]
pub struct SearchResultPage { pub struct SearchResultPage {

View file

@ -42,7 +42,7 @@
<div class="d-inline-flex flex-row mt-auto ml-auto"> <div class="d-inline-flex flex-row mt-auto ml-auto">
{%- if post.editable %} {%- if post.editable %}
<a href="#edit" class="badge badge-light border m-1 p-1">Edit</a> <a href="/post/{{ post.id }}/edit" class="badge badge-light border m-1 p-1">Edit</a>
{% endif -%} {% endif -%}
<a href="#quote" class="badge badge-light border m-1 p-1">Quote</a> <a href="#quote" class="badge badge-light border m-1 p-1">Quote</a>
</div> </div>