about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/handlers.rs19
-rw-r--r--src/render.rs18
-rw-r--r--templates/new-thread.html7
-rw-r--r--templates/thread.html6
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">