about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@gmail.com>2018-04-15T19·13+0200
committerVincent Ambo <tazjin@gmail.com>2018-04-15T19·13+0200
commitec712cc4c0e12329f51d10d9bd626d1859a011b8 (patch)
treee5b51d31c4bfb66eaca5c29e9d796b1a7599dee5
parent4c0e6552e80e70de55fa0ab4310dfec0078b18b9 (diff)
refactor(templates/render): Add generic post editing template
Adds a generic template that can be used for submitting, responding to
and editing posts.
-rw-r--r--src/db.rs4
-rw-r--r--src/handlers.rs14
-rw-r--r--src/render.rs49
-rw-r--r--templates/new-thread.html56
-rw-r--r--templates/post.html103
-rw-r--r--templates/thread.html2
6 files changed, 156 insertions, 72 deletions
diff --git a/src/db.rs b/src/db.rs
index 22438d2b8766..ad1c148a7072 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -80,7 +80,7 @@ impl Handler<GetThread> for DbExecutor {
 /// Message used to create a new thread
 pub struct CreateThread {
     pub new_thread: NewThread,
-    pub body: String,
+    pub post: String,
 }
 
 impl Message for CreateThread {
@@ -105,7 +105,7 @@ impl Handler<CreateThread> for DbExecutor {
             // ... then create the first post in the thread.
             let new_post = NewPost {
                 thread_id: thread.id,
-                body: msg.body,
+                body: msg.post,
                 author_name: msg.new_thread.author_name.clone(),
                 author_email: msg.new_thread.author_email.clone(),
             };
diff --git a/src/handlers.rs b/src/handlers.rs
index 1cd596aad1d2..8bfd3c1511e6 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -99,7 +99,7 @@ fn anonymous() -> Author {
 #[derive(Deserialize)]
 pub struct NewThreadForm {
     pub title: String,
-    pub body: String,
+    pub post: String,
 }
 
 const NEW_THREAD_LENGTH_ERR: &'static str = "Title and body can not be empty!";
@@ -112,16 +112,16 @@ pub fn submit_thread(state: State<AppState>,
     // Trim whitespace out of inputs:
     let input = NewThreadForm {
         title: input.title.trim().into(),
-        body: input.body.trim().into(),
+        post: input.post.trim().into(),
     };
 
     // Perform simple validation and abort here if it fails:
-    if input.title.is_empty() || input.body.is_empty() {
+    if input.title.is_empty() || input.post.is_empty() {
         return state.renderer
             .send(NewThreadPage {
                 alerts: vec![NEW_THREAD_LENGTH_ERR],
                 title: Some(input.title),
-                body: Some(input.body),
+                post: Some(input.post),
             })
             .flatten()
             .map(|res| HttpResponse::Ok().content_type(HTML).body(res))
@@ -140,7 +140,7 @@ pub fn submit_thread(state: State<AppState>,
 
     let msg = CreateThread {
         new_thread,
-        body: input.body,
+        post: input.post,
     };
 
     state.db.send(msg)
@@ -158,7 +158,7 @@ pub fn submit_thread(state: State<AppState>,
 #[derive(Deserialize)]
 pub struct NewPostForm {
     pub thread_id: i32,
-    pub body: String,
+    pub post: String,
 }
 
 /// This handler receives a "Reply"-form and redirects the user to the
@@ -172,7 +172,7 @@ pub fn reply_thread(state: State<AppState>,
 
     let new_post = NewPost {
         thread_id: input.thread_id,
-        body: input.body.trim().into(),
+        body: input.post.trim().into(),
         author_name: author.name,
         author_email: author.email,
     };
diff --git a/src/render.rs b/src/render.rs
index 9cc9efd4d10e..d95fb693478b 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -159,6 +159,41 @@ impl Handler<ThreadPage> for Renderer {
     }
 }
 
+/// The different types of editing modes supported by the editing
+/// template:
+#[derive(Debug, Serialize)]
+pub enum EditingMode {
+    NewThread,
+    PostReply,
+    EditPost,
+}
+
+impl Default for EditingMode {
+    fn default() -> EditingMode { EditingMode::NewThread }
+}
+
+/// This struct represents the context submitted to the template used
+/// for rendering the new thread, edit post and reply to thread forms.
+#[derive(Default, Serialize)]
+pub struct FormContext {
+    /// Which editing mode is to be used by the template?
+    pub mode: EditingMode,
+
+    /// Potential alerts to display to the user (e.g. input validation
+    /// results)
+    pub alerts: Vec<&'static str>,
+
+    /// Either the title to be used in the subject field or the title
+    /// of the thread the user is responding to.
+    pub title: Option<String>,
+
+    /// Body of the post being edited, if present.
+    pub post: Option<String>,
+
+    /// ID of the thread being replied to or the post being edited.
+    pub id: Option<i32>,
+}
+
 /// Message used to render new thread page.
 ///
 /// It can optionally contain a vector of warnings to display to the
@@ -167,7 +202,7 @@ impl Handler<ThreadPage> for Renderer {
 pub struct NewThreadPage {
     pub alerts: Vec<&'static str>,
     pub title: Option<String>,
-    pub body: Option<String>,
+    pub post: Option<String>,
 }
 
 impl Message for NewThreadPage {
@@ -178,11 +213,13 @@ impl Handler<NewThreadPage> for Renderer {
     type Result = Result<String>;
 
     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)?)
+        let ctx: FormContext = FormContext {
+            alerts: msg.alerts,
+            title: msg.title,
+            post: msg.post,
+            ..Default::default()
+        };
+        Ok(self.tera.render("post.html", &ctx)?)
     }
 }
 
diff --git a/templates/new-thread.html b/templates/new-thread.html
deleted file mode 100644
index 855626b8e423..000000000000
--- a/templates/new-thread.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!doctype html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-    <meta http-equiv="Content-Security-Policy" content="script-src 'self';">
-    <!-- Bootstrap CSS -->
-    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
-    <title>Converse Index</title>
-  </head>
-  <body>
-    <header>
-      <nav class="navbar navbar-light bg-light justify-content-between mb-3">
-        <a class="navbar-brand" href="/">
-          <h2>Converse: New Thread</h2>
-        </a>
-        <form class="form-inline">
-          <a class="btn btn-outline-secondary my-2" href="/">Back to index</a>
-        </form>
-      </nav>
-    </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>
-        <form action="/thread/submit" method="post">
-          <div class="row">
-            <div class="col-8 input-group m-3">
-              <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" {% if title %}value="{{ title }}"{% endif %}>
-            </div>
-          </div>
-          <div class="row">
-            <div class="col-8 input-group m-3">
-              <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">{%if body %}{{ body }}{% endif %}</textarea>
-            </div>
-          </div>
-          <div class="row">
-            <div class="col-2 m-3">
-              <button class="btn btn-outline-primary" type="submit">Post!</button>
-            </div>
-          </div>
-        </form>
-      </div>
-    </div>
-  </body>
-</html>
diff --git a/templates/post.html b/templates/post.html
new file mode 100644
index 000000000000..74cf03abf307
--- /dev/null
+++ b/templates/post.html
@@ -0,0 +1,103 @@
+<!-- {#
+  This template is shared by the new thread, reply and post-editing pages.
+
+  The main display differences between the different editing styles are the
+  headline of the page ("Submit new thread", "Reply to thread", "Edit post")
+  and whether or not the subject line field is displayed in the input form.
+
+  Every one of these pages can have a variable length list of alerts submitted
+  into the template, which will be rendered as Boostrap alert boxes above the
+  user input form.
+#} -->
+
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <meta http-equiv="Content-Security-Policy" content="script-src 'self';">
+    <!-- Bootstrap CSS -->
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
+    <title>Converse Index</title>
+  </head>
+  <body>
+    <header>
+      <nav class="navbar navbar-light bg-light justify-content-between mb-3">
+        <a class="navbar-brand" href="/">
+          {% if mode == "NewThread" %}
+          <h2>Converse: Submit new thread</h2>
+          {% elif mode == "PostReply" %}
+          <h2>Converse: Reply to thread</h2>
+          {% elif mode == "EditPost" %}
+          <h2>Converse: Edit post</h2>
+          {% endif %}
+        </a>
+        <form class="form-inline">
+          <a class="btn btn-outline-secondary my-2" href="/">Back to index</a>
+        </form>
+      </nav>
+    </header>
+    <div class="container border rounded">
+      <div class="d-flex flex-column mt-3 border-bottom">
+        {%- for alert in alerts %}
+        <div class="alert alert-warning m-3"><strong>{{ alert }}</strong></div>
+        {% endfor -%}
+
+        {%- if mode == "NewThread" %}
+        <h5>Create a new thread</h5>
+        {% elif mode == "PostReply" %}
+        <h5>Respond to thread '{{ title }}'</h5>
+        {% elif mode == "EditPost" %}
+        <h5>Edit your post</h5>
+        {% endif -%}
+      </div>
+      <div class="d-flex flex-column mt-3">
+        {% if mode == "NewThread" %}
+        <form action="/thread/submit" method="post">
+        {% elif mode == "PostReply" %}
+        <form action="/thread/reply" method="post">
+        {% elif mode == "EditPost" %}
+        <form action="/post/edit" method="post">
+        {% endif %}
+          {% if mode == "PostReply" %}
+          <input type="hidden" id="thread_id" name="thread_id" value="{{ id }}">
+          {% elif mode == "EditPost" %}
+          <input type="hidden" id="thread_id" name="post_id" value="{{ id }}">
+          {% endif %}
+
+          {% if mode == "NewThread" %}
+          <div class="input-group mb-3">
+            <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-label="thread title" {% if title %}value="{{ title }}"{% endif %}>
+          </div>
+          {% endif %}
+
+          <div class="d-flex flex-row">
+            <div class="input-group">
+              <div class="input-group-prepend">
+                <span class="input-group-text" id="post-text">Post:</span>
+              </div>
+              <textarea class="form-control" id="post" name="post" rows="15" aria-label="post content">{% if body %}{{ body }}{% endif %}</textarea>
+            </div>
+            <div class="d-flex flex-column flex-wrap-reverse border rounded">
+              <p class="m-2 pb-2 border-bottom border-dark">
+                Remember that you can use <a href="https://daringfireball.net/projects/markdown/basics"><strong>Markdown</strong></a> when
+                writing your posts:
+              </p>
+              <p class="ml-4 m-2"><i>*italic text*</i></p>
+              <p class="ml-4 m-2"><strong>**bold text**</strong></p>
+              <p class="ml-4 m-2"><s>~strikethrough text~</s></p>
+              <p class="ml-4 m-2"><code>[link text](https://some.link.com/)</code></p>
+              <p class="ml-4 m-2"><code>![image text](https://foo.com/thing.jpg)</code></p>
+              <p class="ml-4 m-2">Use <code>*</code> or <code>-</code> to enumerate lists.</p>
+              <p class="ml-4 m-2">See Markdown documentation for more information!</p>
+            </div>
+          </div>
+          <button class="btn btn-primary mt-3 mb-3" type="submit">Post!</button>
+        </form>
+      </div>
+    </div>
+  </body>
+</html>
diff --git a/templates/thread.html b/templates/thread.html
index f11b96b4d7af..6a89135cd223 100644
--- a/templates/thread.html
+++ b/templates/thread.html
@@ -55,7 +55,7 @@
             <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">
-              <textarea class="form-control" id="body" name="body" aria-label="thread response" rows="10"></textarea>
+              <textarea class="form-control" id="post" name="post" aria-label="thread response" rows="10"></textarea>
               <div class="input-group-append">
                 <button class="btn btn-primary" type="submit">Post!</button>
               </div>