diff options
author | Vincent Ambo <tazjin@gmail.com> | 2018-04-11T23·07+0200 |
---|---|---|
committer | Vincent Ambo <tazjin@gmail.com> | 2018-04-11T23·07+0200 |
commit | e7a54a5affd40f92f788f46ab64033d14860959a (patch) | |
tree | 34b89cbad6e6f71aedad65edcdd47c137eb79dc7 | |
parent | f46f6f3c4296a16e3040398df2774756dec29e93 (diff) |
feat(handler): Perform basic input validation on new thread view
-rw-r--r-- | src/handlers.rs | 19 | ||||
-rw-r--r-- | src/render.rs | 18 | ||||
-rw-r--r-- | templates/new-thread.html | 7 | ||||
-rw-r--r-- | templates/thread.html | 6 |
4 files changed, 39 insertions, 11 deletions
diff --git a/src/handlers.rs b/src/handlers.rs index 43e45d925463..0848740bc10b 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -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. 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)) .responder() } @@ -70,11 +70,26 @@ pub struct NewThreadForm { 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 /// to the new thread after creation. pub fn submit_thread(state: State<AppState>, input: Form<NewThreadForm>, 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 // guarantees it to be present. 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; impl <S> Middleware<S> for RequireLogin { diff --git a/src/render.rs b/src/render.rs index 527cc404481d..37f1c4b7fe60 100644 --- a/src/render.rs +++ b/src/render.rs @@ -141,7 +141,15 @@ impl Handler<ThreadPage> for Renderer { } /// 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 { type Result = Result<String>; @@ -150,7 +158,11 @@ impl Message for NewThreadPage { impl Handler<NewThreadPage> for Renderer { type Result = Result<String>; - fn handle(&mut self, _: NewThreadPage, _: &mut Self::Context) -> Self::Result { - Ok(self.tera.render("new-thread.html", &Context::new())?) + fn handle(&mut self, msg: NewThreadPage, _: &mut Self::Context) -> Self::Result { + 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)?) } } diff --git a/templates/new-thread.html b/templates/new-thread.html index 90c724b016be..fec26fb5aa0b 100644 --- a/templates/new-thread.html +++ b/templates/new-thread.html @@ -20,6 +20,9 @@ </header> <div class="container border rounded"> <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>Remember that you can use <strong>Markdown</strong> when writing your posts.</p> @@ -29,7 +32,7 @@ <div class="input-group-prepend"> <span class="input-group-text" id="title-text">Title:</span> </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 class="row"> @@ -37,7 +40,7 @@ <div class="input-group-prepend"> <span class="input-group-text" id="body-text">Body:</span> </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 class="row"> diff --git a/templates/thread.html b/templates/thread.html index 1b484637331c..4909365a8005 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -13,9 +13,7 @@ <a class="navbar-brand" href="/"> <h2>Converse</h2> </a> - <form class="form-inline"> - <a class="btn btn-outline-secondary my-2" href="/">Back to index</a> - </form> + <a class="btn btn-outline-secondary my-2" href="/">Back to index</a> </nav> </header> @@ -59,7 +57,7 @@ <div class="list-group-item flex-column align-items-start"> <div class="row"> <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 }}"> <label for="body">You can use <strong>Markdown</strong>!</label> <div class="input-group"> |