feat(handler): Perform basic input validation on new thread view
This commit is contained in:
parent
f46f6f3c42
commit
e7a54a5aff
4 changed files with 39 additions and 11 deletions
|
@ -59,7 +59,7 @@ pub fn forum_thread(state: State<AppState>, thread_id: Path<i32>) -> ConverseRes
|
||||||
|
|
||||||
/// This handler presents the user with the "New Thread" form.
|
/// This handler presents the user with the "New Thread" form.
|
||||||
pub fn new_thread(state: State<AppState>) -> ConverseResponse {
|
pub fn new_thread(state: State<AppState>) -> ConverseResponse {
|
||||||
state.renderer.send(NewThreadPage).flatten()
|
state.renderer.send(NewThreadPage::default()).flatten()
|
||||||
.map(|res| HttpResponse::Ok().content_type(HTML).body(res))
|
.map(|res| HttpResponse::Ok().content_type(HTML).body(res))
|
||||||
.responder()
|
.responder()
|
||||||
}
|
}
|
||||||
|
@ -70,11 +70,26 @@ pub struct NewThreadForm {
|
||||||
pub body: String,
|
pub body: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NEW_THREAD_LENGTH_ERR: &'static str = "Title and body can not be empty!";
|
||||||
|
|
||||||
/// This handler receives a "New thread"-form and redirects the user
|
/// This handler receives a "New thread"-form and redirects the user
|
||||||
/// to the new thread after creation.
|
/// to the new thread after creation.
|
||||||
pub fn submit_thread(state: State<AppState>,
|
pub fn submit_thread(state: State<AppState>,
|
||||||
input: Form<NewThreadForm>,
|
input: Form<NewThreadForm>,
|
||||||
mut req: HttpRequest<AppState>) -> ConverseResponse {
|
mut req: HttpRequest<AppState>) -> ConverseResponse {
|
||||||
|
// Perform simple validation and abort here if it fails:
|
||||||
|
if input.0.title.is_empty() || input.0.body.is_empty() {
|
||||||
|
return state.renderer
|
||||||
|
.send(NewThreadPage {
|
||||||
|
alerts: vec![NEW_THREAD_LENGTH_ERR],
|
||||||
|
title: Some(input.0.title),
|
||||||
|
body: Some(input.0.body),
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.map(|res| HttpResponse::Ok().content_type(HTML).body(res))
|
||||||
|
.responder();
|
||||||
|
}
|
||||||
|
|
||||||
// Author is "unwrapped" because the RequireLogin middleware
|
// Author is "unwrapped" because the RequireLogin middleware
|
||||||
// guarantees it to be present.
|
// guarantees it to be present.
|
||||||
let author: Author = req.session().get(AUTHOR).unwrap().unwrap();
|
let author: Author = req.session().get(AUTHOR).unwrap().unwrap();
|
||||||
|
@ -158,7 +173,7 @@ pub fn callback(state: State<AppState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Middleware used to enforce logins unceremonially.
|
/// Middleware used to enforce logins unceremoniously.
|
||||||
pub struct RequireLogin;
|
pub struct RequireLogin;
|
||||||
|
|
||||||
impl <S> Middleware<S> for RequireLogin {
|
impl <S> Middleware<S> for RequireLogin {
|
||||||
|
|
|
@ -141,7 +141,15 @@ impl Handler<ThreadPage> for Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Message used to render new thread page.
|
/// Message used to render new thread page.
|
||||||
pub struct NewThreadPage;
|
///
|
||||||
|
/// It can optionally contain a vector of warnings to display to the
|
||||||
|
/// user in alert boxes, such as input validation errors.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NewThreadPage {
|
||||||
|
pub alerts: Vec<&'static str>,
|
||||||
|
pub title: Option<String>,
|
||||||
|
pub body: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Message for NewThreadPage {
|
impl Message for NewThreadPage {
|
||||||
type Result = Result<String>;
|
type Result = Result<String>;
|
||||||
|
@ -150,7 +158,11 @@ impl Message for NewThreadPage {
|
||||||
impl Handler<NewThreadPage> for Renderer {
|
impl Handler<NewThreadPage> for Renderer {
|
||||||
type Result = Result<String>;
|
type Result = Result<String>;
|
||||||
|
|
||||||
fn handle(&mut self, _: NewThreadPage, _: &mut Self::Context) -> Self::Result {
|
fn handle(&mut self, msg: NewThreadPage, _: &mut Self::Context) -> Self::Result {
|
||||||
Ok(self.tera.render("new-thread.html", &Context::new())?)
|
let mut ctx = Context::new();
|
||||||
|
ctx.add("alerts", &msg.alerts);
|
||||||
|
ctx.add("title", &msg.title.map(|s| escape_html(&s)));
|
||||||
|
ctx.add("body", &msg.body.map(|s| escape_html(&s)));
|
||||||
|
Ok(self.tera.render("new-thread.html", &ctx)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,9 @@
|
||||||
</header>
|
</header>
|
||||||
<div class="container border rounded">
|
<div class="container border rounded">
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
|
{% for alert in alerts %}
|
||||||
|
<div class="alert alert-warning m-3"><strong>{{ alert }}</strong></div>
|
||||||
|
{% endfor %}
|
||||||
<p class="mt-3">Make <i>your own thread</i> on these here forums!</p>
|
<p class="mt-3">Make <i>your own thread</i> on these here forums!</p>
|
||||||
<p>Remember that you can use <strong>Markdown</strong> when
|
<p>Remember that you can use <strong>Markdown</strong> when
|
||||||
writing your posts.</p>
|
writing your posts.</p>
|
||||||
|
@ -29,7 +32,7 @@
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text" id="title-text">Title:</span>
|
<span class="input-group-text" id="title-text">Title:</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" class="form-control" id="title" name="title" aria-describedby="title-text">
|
<input type="text" class="form-control" id="title" name="title" aria-describedby="title-text" {% if title %}value="{{ title }}"{% endif %}>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -37,7 +40,7 @@
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text" id="body-text">Body:</span>
|
<span class="input-group-text" id="body-text">Body:</span>
|
||||||
</div>
|
</div>
|
||||||
<textarea class="form-control" id="body" name="body" aria-label="thread body"></textarea>
|
<textarea class="form-control" id="body" name="body" aria-label="thread body">{%if body %}{{ body }}{% endif %}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -13,9 +13,7 @@
|
||||||
<a class="navbar-brand" href="/">
|
<a class="navbar-brand" href="/">
|
||||||
<h2>Converse</h2>
|
<h2>Converse</h2>
|
||||||
</a>
|
</a>
|
||||||
<form class="form-inline">
|
<a class="btn btn-outline-secondary my-2" href="/">Back to index</a>
|
||||||
<a class="btn btn-outline-secondary my-2" href="/">Back to index</a>
|
|
||||||
</form>
|
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
@ -59,7 +57,7 @@
|
||||||
<div class="list-group-item flex-column align-items-start">
|
<div class="list-group-item flex-column align-items-start">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<form action="/thread/reply" method="post">
|
<form id="reply-form" action="/thread/reply" method="post">
|
||||||
<input type="hidden" id="thread_id" name="thread_id" value="{{ id }}">
|
<input type="hidden" id="thread_id" name="thread_id" value="{{ id }}">
|
||||||
<label for="body">You can use <strong>Markdown</strong>!</label>
|
<label for="body">You can use <strong>Markdown</strong>!</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
|
Loading…
Reference in a new issue