From da3bde16d0885beafe292057259808f69714778a Mon Sep 17 00:00:00 2001
From: Simon Brooke
Date: Fri, 7 Feb 2020 19:01:30 +0000
Subject: [PATCH] Added 'list uploaded files' page, only accessible if logged
in.
---
.gitignore | 2 +
project.clj | 2 +
resources/i18n/en-GB.edn | 7 +-
resources/public/content/_edit-side-bar.md | 4 +-
resources/templates/list-uploads.html | 34 +++++++
resources/templates/upload.html | 3 +
src/smeagol/routes/wiki.clj | 102 +++++++++++++++++----
src/smeagol/uploads.clj | 6 +-
8 files changed, 138 insertions(+), 22 deletions(-)
create mode 100644 resources/templates/list-uploads.html
diff --git a/.gitignore b/.gitignore
index 2a76665..95c58ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,5 @@ smeagol.log*
/node_modules/
.DS_Store
+
+resources/public/content/uploads/
diff --git a/project.clj b/project.clj
index a015883..d9b5cda 100644
--- a/project.clj
+++ b/project.clj
@@ -5,6 +5,7 @@
:url "https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"}
:dependencies [[clj-jgit "0.8.10"]
[clj-yaml "0.4.0"]
+ [clojure.java-time "0.3.2"]
[com.cemerick/url "0.1.1"]
[com.fzakaria/slf4j-timbre "0.3.7"]
[com.stuartsierra/component "0.4.0"]
@@ -17,6 +18,7 @@
[im.chit/cronj "1.4.4"]
[lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]]
[markdown-clj "0.9.99" :exclusions [com.keminglabs/cljx]]
+ [me.raynes/fs "1.4.6"]
[noir-exception "0.2.5"]
[org.clojars.simon_brooke/internationalisation "1.0.3"]
[org.clojure/clojure "1.8.0"]
diff --git a/resources/i18n/en-GB.edn b/resources/i18n/en-GB.edn
index 64c164c..6f4d8b2 100644
--- a/resources/i18n/en-GB.edn
+++ b/resources/i18n/en-GB.edn
@@ -83,6 +83,8 @@
:file-upload-title "Upload a file" ;; title for the file upload page
:is-admin-prompt "Is administrator?"
:here "here" ;; used in sanity check report
+ :history-link "History" ;; text of the history link on the content frame
+ :history-title-prefix "History of" ;; prefix of the title on the history page
:home-link "Home" ;; text of the home link on the menu
:is-not-directory "is not a directory"
;; (of a file or directory) used in sanity check report
@@ -90,6 +92,8 @@
;; (of a file or directory) used in sanity check report
:is-not-writable "is not writable"
;; (of a file or directory) used in sanity check report
+ :list-files "List uploaded files"
+ ;; title of the 'List uploaded Files' page
:login-label "Log in!" ;; text of the login widget on the login page
:login-link "Log in" ;; text of the login link on the menu
:login-prompt "To edit this wiki"
@@ -98,8 +102,7 @@
:logout-link "Log out" ;; text of the logout link on the menu
:logged-in-as "You are logged in as"
;; text of the 'logged in as' label on the menu
- :history-link "History" ;; text of the history link on the content frame
- :history-title-prefix "History of" ;; prefix of the title on the history page
+ :matching "matching" ;; 'matching' in e.g. 'list files matching fred'
:new-pass-prompt "New password" ;; text of the new password widget prompt on the change
;; password and edit user pages
:no-admin-users "There are no users in the 'passwd' file with administrative privileges"
diff --git a/resources/public/content/_edit-side-bar.md b/resources/public/content/_edit-side-bar.md
index bbd61ca..ccec0c0 100644
--- a/resources/public/content/_edit-side-bar.md
+++ b/resources/public/content/_edit-side-bar.md
@@ -10,4 +10,6 @@
+ \*\***bold**\*\*
+ \__italic_\_
-More documentation [here](http://daringfireball.net/projects/markdown/syntax)
\ No newline at end of file
+More documentation [here](http://daringfireball.net/projects/markdown/syntax)
+
+Your uploaded files are listed here.
diff --git a/resources/templates/list-uploads.html b/resources/templates/list-uploads.html
new file mode 100644
index 0000000..761e4a8
--- /dev/null
+++ b/resources/templates/list-uploads.html
@@ -0,0 +1,34 @@
+{% extends "templates/base.html" %}
+
+{% block content %}
+
+
+
+
+ | Name |
+ Uploaded |
+ Type this |
+ To get this |
+
+ {% for entry in files %}
+
+ | {{entry.base-name}} |
+ {{entry.modified}} |
+
+ {% if entry.is-image %}  {% else %} [{{entry.name|capitalize}}](uploads/{{entry.base-name}}) {% endif %}
+ |
+
+ {% if entry.is-image %} {% else %} link {% endif %}
+ |
+
+
+ {% endfor %}
+
+
+{% endblock %}
diff --git a/resources/templates/upload.html b/resources/templates/upload.html
index 53284ec..7c0c373 100644
--- a/resources/templates/upload.html
+++ b/resources/templates/upload.html
@@ -34,5 +34,8 @@
{% endif %}
+
+ Your uploaded files are listed here.
+
{% endblock %}
diff --git a/src/smeagol/routes/wiki.clj b/src/smeagol/routes/wiki.clj
index 72df1af..5457fa1 100644
--- a/src/smeagol/routes/wiki.clj
+++ b/src/smeagol/routes/wiki.clj
@@ -7,6 +7,8 @@
[clojure.string :as cs]
[clojure.walk :refer :all]
[compojure.core :refer :all]
+ [java-time :as jt]
+ [me.raynes.fs :as fs]
[noir.io :as io]
[noir.response :as response]
[noir.util.route :as route]
@@ -161,6 +163,75 @@
:page page
:history (hist/find-history repo-path file-name)}))))
+(def image-extns #{".gif" ".jpg" ".jpeg" ".png"})
+
+(defn format-instant
+ "Format this `unix-time`, expected to be a Long, into something human readable.
+ If `template` is supplied, use that as the formatting template as specified for
+ java.time.Formatter. Assumes system default timezone. Returns a string."
+ ([^Long unix-time]
+ (format-instant unix-time "EEEE, dd MMMM YYYY"))
+ ([^Long unix-time ^String template]
+ (jt/format
+ (java-time/formatter template)
+ (java.time.LocalDateTime/ofInstant
+ (java-time/instant unix-time)
+ (java.time.ZoneOffset/systemDefault)))))
+
+(defn list-uploads-page
+ "Render a list of all uploaded files"
+ [request]
+ (let
+ [params (keywordize-keys (:params request))
+ data-path (str util/content-dir "/uploads/")
+ files
+ (map
+ #(zipmap
+ [:base-name :is-image :modified :name]
+ [(fs/base-name %)
+ (if
+ (and (fs/extension %)
+ (image-extns (cs/lower-case (fs/extension %))))
+ true false)
+ (if
+ (fs/mod-time %)
+ (format-instant (fs/mod-time %)))
+ (fs/name %)])
+ (remove
+ #(or (cs/starts-with? (fs/name %) ".")
+ (fs/directory? %))
+ (file-seq (clojure.java.io/file data-path))))]
+ (layout/render
+ "list-uploads.html"
+ (merge (util/standard-params request)
+ {:title (str
+ (util/get-message :list-files request)
+ (if
+ (:search params)
+ (str " " (util/get-message :matching request))))
+ :search (:search params)
+ :files (if
+ (:search params)
+ (try
+ (let [pattern (re-pattern (:search params))]
+ (filter
+ #(re-find pattern (:base-name %))
+ files))
+ (catch Exception _ files))
+ files)
+ }))))
+
+(map
+ #(zipmap
+ [:base-name :is-image :modified :name]
+ [(fs/base-name %)
+ (if
+ (and (fs/extension %) (image-extns (cs/lower-case (fs/extension %))))
+ true false)
+ (fs/mod-time %)
+ (fs/name %)])
+ (file-seq (clojure.java.io/file "resources/public/content/uploads")))
+
(defn upload-page
"Render a form to allow the upload of a file."
[request]
@@ -174,23 +245,17 @@
(if
uploaded
(do
- (git/git-add git-repo uploaded)
+ (git/git-add git-repo (str data-path (fs/name uploaded)))
(git/git-commit git-repo summary {:name user :email (auth/get-email user)})))
(layout/render "upload.html"
(merge (util/standard-params request)
{:title (util/get-message :file-upload-title request)
- :uploaded uploaded
- :is-image (and
+ :uploaded (if uploaded (fs/base-name uploaded))
+ :is-image (if
uploaded
- (or
- (cs/ends-with? uploaded ".gif")
- (cs/ends-with? uploaded ".jpg")
- (cs/ends-with? uploaded ".jpeg")
- (cs/ends-with? uploaded ".png")
- (cs/ends-with? uploaded ".GIF")
- (cs/ends-with? uploaded ".JPG")
- (cs/ends-with? uploaded ".PNG")))}))))
-
+ (image-extns
+ (cs/lower-case
+ (fs/extension uploaded))))}))))
(defn version-page
"Render a specific historical version of a page"
@@ -286,8 +351,10 @@
(defroutes wiki-routes
- (GET "/wiki" request (wiki-page request))
(GET "/" request (wiki-page request))
+ (GET "/auth" request (auth-page request))
+ (POST "/auth" request (auth-page request))
+ (GET "/changes" request (diff-page request))
(GET "/delete-user" request (route/restricted (admin/delete-user request)))
(GET "/edit" request (route/restricted (edit-page request)))
(POST "/edit" request (route/restricted (edit-page request)))
@@ -297,11 +364,12 @@
(GET "/edit-user" request (route/restricted (admin/edit-user request)))
(POST "/edit-user" request (route/restricted (admin/edit-user request)))
(GET "/history" request (history-page request))
+ (GET "/list-uploads" request (route/restricted (list-uploads-page request)))
+ (POST "/list-uploads" request (route/restricted (list-uploads-page request)))
(GET "/version" request (version-page request))
- (GET "/changes" request (diff-page request))
- (GET "/auth" request (auth-page request))
- (POST "/auth" request (auth-page request))
(GET "/passwd" request (passwd-page request))
(POST "/passwd" request (passwd-page request))
(GET "/upload" request (route/restricted (upload-page request)))
- (POST "/upload" request (route/restricted (upload-page request))))
+ (POST "/upload" request (route/restricted (upload-page request)))
+ (GET "/wiki" request (wiki-page request))
+ )
diff --git a/src/smeagol/uploads.clj b/src/smeagol/uploads.clj
index 7fd8c18..7cadaea 100644
--- a/src/smeagol/uploads.clj
+++ b/src/smeagol/uploads.clj
@@ -49,7 +49,9 @@
"Store an upload both to the file system and to the database.
The issue with storing an upload is moving it into place.
If `params` are passed as a map, it is expected that this is a map from
- an HTTP POST operation of a form with type `multipart/form-data`."
+ an HTTP POST operation of a form with type `multipart/form-data`.
+
+ On success, returns the file object uploaded."
[params path]
(let [upload (:upload params)
tmp-file (:tempfile upload)
@@ -63,7 +65,7 @@
(do
(.renameTo tmp-file
(File. (str path filename)))
- filename)
+ (File. (str path filename)))
(catch Exception x
(timbre/error (str "Failed to move " tmp-file " to " path filename "; " (type x) ": " (.getMessage x)))
(throw x)))