Add commenting feature #9

This commit is contained in:
Aaron Fischer 2017-04-03 21:21:48 +02:00
parent 4d4f403508
commit 54c2e25b5b
6 changed files with 101 additions and 16 deletions

View file

@ -72,10 +72,11 @@ VALUES (:image_id, :author, :comment)
-- :name get-comments-for-image :? :* -- :name get-comments-for-image :? :*
SELECT * FROM comments SELECT * FROM comments
WHERE image_id = :image_id WHERE image_id = :image_id
ORDER BY created_at DESC ORDER BY created_at ASC
-- :name get-recent-comments :? :* -- :name get-recent-comments :? :*
SELECT * FROM comments SELECT comments.*, images.title FROM comments
LEFT JOIN images ON images.id = comments.image_id
ORDER BY created_at DESC ORDER BY created_at DESC
LIMIT :num-comments LIMIT :num-comments

View file

@ -2,5 +2,31 @@
{% block content %} {% block content %}
<h1>Kommentarstream</h1> <h1>Kommentarstream</h1>
<img src="/img/warning_clojure.png">
<ul class="list-unlisted">
{% for comment in comments %}
<hr/>
<li class="media mb-3">
<img src="https://robohash.org/{{ comment.author }}?size=40x40" class="d-flex mr-3"/>
<div class="media-body">
<i>{{ comment.created_at|parse-date|date:"dd-MM-yyyy HH:mm" }} Uhr</i>
von <strong>{{ comment.author }}</strong>
<br/>
<small><a href="/show/{{ comment.image_id }}#{{ comment.id }}">
{% if comment.title %}
{{ comment.title }}
{% else %}
Zum Bild
{% endif %}
</a>
</small>
<div>
{{ comment.comment|markdown-to-html|safe }}
</div>
</div>
</li>
{% endfor %}
</ul>
{% endblock %} {% endblock %}

View file

@ -65,4 +65,39 @@
</div> </div>
{% endif %} {% endif %}
<ul class="list-unlisted">
{% for comment in comments %}
<li class="media mb-3">
<a name="{{ comment.id }}"></a>
<img src="https://robohash.org/{{ comment.author }}?size=50x50" class="d-flex mr-3"/>
<div class="media-body">
<h5 class="mt-0 mb-1">{{ comment.author }}</h5>
{{ comment.comment|markdown-to-html|safe }}
</div>
</li>
{% endfor %}
</ul>
<form action="/add-comment/{{ image.id }}" method="POST" class="form-horizontal">
{% csrf-field %}
<div class="form-group">
<label for="author">Name</label>
<input type="text" name="author" class="form-control"
value="{{ saved-author }}"
placeholder="Bitte einen Namen wählen (wird beim nächsten Mal gespeichert)">
</div>
<div class="form-group">
<label for="comment">Kommentar</label>
<textarea name="comment" class="form-control" rows="3"
placeholder="Es kann auch Markdown-Syntax verwendet werden"></textarea>
</div>
<button type="submit" class="btn btn-success">
<span class="fa fa-floppy-o"></span>
Kommentar hinzufügen
</button>
</form>
{% endblock %} {% endblock %}

View file

@ -7,6 +7,7 @@
[ring.middleware.format :refer [wrap-restful-format]] [ring.middleware.format :refer [wrap-restful-format]]
[yenu.config :refer [env]] [yenu.config :refer [env]]
[ring.middleware.flash :refer [wrap-flash]] [ring.middleware.flash :refer [wrap-flash]]
[ring.middleware.cookies :refer [wrap-cookies]]
[immutant.web.middleware :refer [wrap-session]] [immutant.web.middleware :refer [wrap-session]]
[ring.middleware.defaults :refer [site-defaults wrap-defaults]] [ring.middleware.defaults :refer [site-defaults wrap-defaults]]
[buddy.auth.middleware :refer [wrap-authentication wrap-authorization]] [buddy.auth.middleware :refer [wrap-authentication wrap-authorization]]
@ -65,13 +66,6 @@
;; since they're not compatible with this middleware ;; since they're not compatible with this middleware
((if (:websocket? request) handler wrapped) request)))) ((if (:websocket? request) handler wrapped) request))))
(defn wrap-auth [handler]
(let [backend (session-backend)]
(-> handler
(wrap-access-rules {:rules rules :on-error on-error})
(wrap-authentication backend)
(wrap-authorization backend))))
(defn on-error [request response] (defn on-error [request response]
(redirect "/login")) (redirect "/login"))
@ -87,11 +81,19 @@
{:pattern #"^/.*" {:pattern #"^/.*"
:handler authenticated?}]) :handler authenticated?}])
(defn wrap-auth [handler]
(let [backend (session-backend)]
(-> handler
(wrap-access-rules {:rules rules :on-error on-error})
(wrap-authentication backend)
(wrap-authorization backend))))
(defn wrap-base [handler] (defn wrap-base [handler]
(-> ((:middleware defaults) handler) (-> ((:middleware defaults) handler)
wrap-webjars wrap-webjars
wrap-flash wrap-flash
(wrap-session {:cookie-attrs {:http-only true}}) (wrap-session {:cookie-attrs {:http-only true}})
wrap-cookies
(wrap-defaults (wrap-defaults
(-> site-defaults (-> site-defaults
(assoc-in [:security :anti-forgery] false) (assoc-in [:security :anti-forgery] false)

View file

@ -41,6 +41,10 @@
(db/delete-image-comments! {:id id}) (db/delete-image-comments! {:id id})
(images/delete-image! (:hash image)))) (images/delete-image! (:hash image))))
(defn comment-stream []
(let [comments (db/get-recent-comments {:num-comments 20})]
(layout/render "comments.html" {:comments comments})))
(defroutes admin-routes (defroutes admin-routes
(GET "/upload" [:as request] (GET "/upload" [:as request]
(-> (layout/render (-> (layout/render
@ -61,4 +65,4 @@
(GET "/statistics" [] (GET "/statistics" []
(layout/render "statistics.html")) (layout/render "statistics.html"))
(GET "/comments" [] (GET "/comments" []
(layout/render "comments.html"))) (comment-stream)))

View file

@ -21,29 +21,46 @@
:pages (range 1 (inc pages)) :pages (range 1 (inc pages))
:flash (:flash request)}))) :flash (:flash request)})))
(defn detail-page [image-id] (defn detail-page [image-id request]
(let [image (db/get-image {:id image-id}) (let [image (db/get-image {:id image-id})
next-img (db/get-next-image {:image-date (:created_at image)}) next-img (db/get-next-image {:image-date (:created_at image)})
prev-img (db/get-prev-image {:image-date (:created_at image)})] prev-img (db/get-prev-image {:image-date (:created_at image)})
saved-author (get-in request [:cookies "author" :value])]
(layout/render "detail.html" (layout/render "detail.html"
{:image image {:image image
:next-image next-img :next-image next-img
:prev-image prev-img :prev-image prev-img
:tags (db/get-tags-for-image {:id (:id image)}) :tags (db/get-tags-for-image {:id (:id image)})
:saved-author saved-author
:comments (db/get-comments-for-image {:image_id (:id image)})}))) :comments (db/get-comments-for-image {:image_id (:id image)})})))
(defn add-comment! [image-id request]
(let [author (:author (:params request))
comment (:comment (:params request))]
(db/create-comment! {:image_id image-id
:author author
:comment comment})
(-> (redirect (str "/show/" image-id))
(assoc-in [:cookies "author"]
{:value author
:path "/"
:max-age (* 60 60 24 30 12)}))))
(defn image-file [type hash ext] (defn image-file [type hash ext]
(let [filename (str hash "." ext)] (let [filename (str hash "." ext)]
(file-response (images/data-path "gallery" type filename)))) (file-response (images/data-path "gallery" type filename))))
(defroutes core-routes (defroutes core-routes
(GET "/" [] (GET "/" []
(redirect "/page/1")) (redirect "/page/1"))
(GET "/page/:page-number" [page-number :as request] (GET "/page/:page-number" [page-number :as request]
(index-page (Integer. page-number) request)) (index-page (Integer. page-number) request))
(GET "/show/:image-id" [image-id] (GET "/show/:image-id" [image-id :as request]
(detail-page image-id)) (detail-page image-id request))
(POST "/add-comment/:image-id" [image-id :as request]
(add-comment! image-id request))
(GET ["/images/:type/:hash.:ext" (GET ["/images/:type/:hash.:ext"
;;:type #"(normal|raw|thumbnails)" ;;:type #"(normal|raw|thumbnails)"
;;:hash #"[0-9]+-[^.]+" ;;:hash #"[0-9]+-[^.]+"