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 %} +
+
+ {% csrf-field %} +

+ + +

+
+ + + + + + + + {% for entry in files %} + + + + + + + + {% endfor %} +
NameUploadedType thisTo get this
{{entry.base-name}}{{entry.modified}} + {% if entry.is-image %} ![{{entry.name|capitalize}}](uploads/{{entry.base-name}}) {% else %} [{{entry.name|capitalize}}](uploads/{{entry.base-name}}) {% endif %} + + {% if entry.is-image %} {{entry.name|capitalize}} {% else %} link {% endif %} +
+
+{% 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)))