about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@gmail.com>2018-04-15T21·09+0200
committerVincent Ambo <tazjin@gmail.com>2018-04-15T21·09+0200
commit705097dab91c57524d2311dd839615840044437c (patch)
tree2b7763180fb25d3e85107a086b8fb1739e0e6688
parent7a557865528d6dba78b4dcb90248623aa100f930 (diff)
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").
-rw-r--r--src/errors.rs3
-rw-r--r--src/handlers.rs35
-rw-r--r--src/main.rs1
-rw-r--r--src/render.rs24
-rw-r--r--templates/thread.html2
5 files changed, 63 insertions, 2 deletions
diff --git a/src/errors.rs b/src/errors.rs
index 9de907430d..8345a9382f 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -54,6 +54,9 @@ pub enum ConverseError {
     #[fail(display = "error occured running 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
     // actix-web-compatible response errors, such as the errors it
     // throws itself.
diff --git a/src/handlers.rs b/src/handlers.rs
index 8bfd3c1511..cbe4e4292b 100644
--- a/src/handlers.rs
+++ b/src/handlers.rs
@@ -189,6 +189,41 @@ pub fn reply_thread(state: State<AppState>,
         .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
 /// displays the results to the user.
 pub fn search_forum(state: State<AppState>,
diff --git a/src/main.rs b/src/main.rs
index 8d81a670e0..55b19b0be7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -181,6 +181,7 @@ fn start_http_server(base_url: String,
             .resource("/thread/submit", |r| r.method(Method::POST).with3(submit_thread))
             .resource("/thread/reply", |r| r.method(Method::POST).with3(reply_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("/oidc/login", |r| r.method(Method::GET).with(login))
             .resource("/oidc/callback", |r| r.method(Method::POST).with3(callback));
diff --git a/src/render.rs b/src/render.rs
index c0a2fe88e6..e02ac31ea9 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -200,7 +200,7 @@ impl Handler<NewThreadPage> for Renderer {
     type Result = Result<String>;
 
     fn handle(&mut self, msg: NewThreadPage, _: &mut Self::Context) -> Self::Result {
-        let ctx: FormContext = FormContext {
+        let ctx = FormContext {
             alerts: msg.alerts,
             title: msg.title,
             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
 #[derive(Serialize)]
 pub struct SearchResultPage {
diff --git a/templates/thread.html b/templates/thread.html
index 6a89135cd2..826046ce15 100644
--- a/templates/thread.html
+++ b/templates/thread.html
@@ -42,7 +42,7 @@
 
             <div class="d-inline-flex flex-row mt-auto ml-auto">
               {%- 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 -%}
               <a href="#quote" class="badge badge-light border m-1 p-1">Quote</a>
             </div>