diff --git a/resources/i18n/en-GB.edn b/resources/i18n/en-GB.edn index 6f4d8b2..442f097 100644 --- a/resources/i18n/en-GB.edn +++ b/resources/i18n/en-GB.edn @@ -131,6 +131,8 @@ "Smeagol has been unable to find some of the resources on which it depends, possibly because of misconfiguration or missing environment variables." ;; used in sanity check report + :sortable "You can sort this table by selecting column headers" + ;; used for sortable tables :user-lacks-field "User record in the passwd file lacks a field" ;; used in sanity check report :username-prompt "Username" ;; text of the username widget prompt on edit user page diff --git a/resources/public/content/stylesheet.css b/resources/public/content/stylesheet.css index 81a661c..62690e0 100644 --- a/resources/public/content/stylesheet.css +++ b/resources/public/content/stylesheet.css @@ -24,6 +24,16 @@ ## html elements generally in alphabetic order */ +a { + color: darkgray; + font-weight: bold; +} + +a:hover { + color: darkgray; + background:rgba(200,200,200,0.8); +} + body { margin: 0; padding: 0; @@ -52,14 +62,15 @@ dd { /* footer of the page - not-editable, provided by Smeagol */ footer { - border-top: thin solid gray; + border-top: thin solid silver; + color: gray; + background:rgba(200,200,200,0.8); clear: both; font-size: smaller; text-align: center; - color: gray; - background: rgba(224,224,224,0.95); width: 100%; margin: 0; + min-height: 4px; padding: 0.25em 0; bottom:0; position:fixed; @@ -71,9 +82,14 @@ footer { } footer div { + display: none; padding: 0.1em; } +footer:hover div { + display: block; +} + form { border: thin solid silver; } @@ -83,8 +99,6 @@ header { margin-top: 0; width:100%; max-width: 100%; - background-color: gray; - color: white; } header h1 { @@ -93,7 +107,6 @@ header h1 { header a { font-weight: bold; - color: white; } header a:hover { @@ -131,12 +144,12 @@ ins { label { width: 20%; min-width: 20em; - border-right: thin solid gray; + border-right: thin solid silver; display: inline-block; } table { - border: 2px solid black; + border: thin solid silver; border-collapse: collapse; } @@ -148,7 +161,7 @@ th, td { text-align: left; vertical-align: top; padding: 0.15em 1.5em; - border: 1px solid gray; + border: 1px solid silver; } th { @@ -166,6 +179,7 @@ th { /* left bar for all pages in the Wiki - editable, provided by users. Within main-container */ #side-bar { + display: none; width: 17%; height: 100%; float: left; @@ -173,10 +187,10 @@ th { /* cookies information box, fixed, in right margin, just above footer */ #cookies { - width: 30%; + width: 20%; float: right; position: fixed; - bottom: 3.5em; + bottom: 8px; right: 0; z-index: 175; background: transparent; @@ -190,8 +204,8 @@ th { text-align: right; padding: 0.25em 2em; border-radius: 0.25em; - color: white; - background:rgba(40,40,40,0.8); + color: gray; + background:rgba(200,200,200,0.8); } /* more-about-cookies box, normally hidden */ @@ -199,9 +213,9 @@ th { display: none; padding: 0.5em 2em; border-radius: 0.5em; - color: white; - background:rgba(40,40,40,0.8); - border-bottom: thin solid white; + color: gray; + background:rgba(200,200,200,0.8); + border-bottom: thin solid gray; } /* but magically appears on mouseover */ @@ -242,8 +256,8 @@ th { right: 0; padding: 0.25em 2em; border-radius: 0.25em; - color: white; - background:rgba(40,40,40,0.8); + color: gray; + background:rgba(200,200,200,0.8); font-size: 66%; } @@ -254,7 +268,11 @@ th { .minor-controls a { float: right; padding: 0.25em 2em; - color: white; + color: gray; +} + +.minor-controls a:hover { + color: darkgray; } .pseudo-input { @@ -303,8 +321,7 @@ th { /* content of the current page in the Wiki - editable, provided by users. Within main-container */ #content { border: thin solid silver; - width: 80%; - float: right; + width: 100%; padding-bottom: 5em; } @@ -312,16 +329,29 @@ th { display: none; } + #header { + font-size: smaller; + } + /* top-of-page navigation, not editable, provided by Smeagol */ #nav{ margin: 0; padding: 0; top: 0; - width: 100%; + min-height: 4px; _position: absolute; _top: expression(document.documentElement.scrollTop); z-index: 149; - background:rgba(40,40,40,0.8); + color: gray; + background:rgba(200,200,200,0.8); + } + + #nav #nav-menu { + display: none; + } + + #nav:hover #nav-menu { + display: block; } /* only needed for fly-out menu effect on tablet and phone stylesheets */ @@ -341,14 +371,14 @@ th { } #nav menu li a { - color: white; + color: gray; text-decoration: none; font-weight: bold; padding: 0.1em 0.75em; margin: 0; } - #nav menu li.active a { background: gray;} + #nav menu li.active a { background: gray; color: white;} li.nav-item a:hover { background: rgb( 240, 240, 240) } li.nav-item a:active { background: gray; color: white; } @@ -379,17 +409,15 @@ th { padding: 0; position: fixed; z-index: 149; - color: silver; - background:rgba(40,40,40,0.9); + color: black; + background:rgba(200,200,200,0.9); } #nav a { - color: white; - text-decoration: none; font-weight: bold; } - #nav:hover #nav-menu { + #nav:hover #nav-menu, #nav:hover #phone-side-bar { display: block; list-style-type: none; width: 100%; @@ -455,18 +483,21 @@ th { display: none; } + #header { + display: none; + } + #nav{ margin: 0; padding: 0; position: fixed; z-index: 149; - color: silver; - background:rgba(40,40,40,0.9); + color: black; + background:rgba(200,200,200,0.9); } #nav a { - color: white; - text-decoration: none; + color: black; font-weight: bold; } @@ -491,6 +522,8 @@ th { } #nav menu li a { + color: black; + font-weight: bold; } #nav ul li.active a { background: silver;} diff --git a/resources/templates/base.html b/resources/templates/base.html index 7a31f07..01152af 100644 --- a/resources/templates/base.html +++ b/resources/templates/base.html @@ -46,7 +46,9 @@

{% i18n site-title %}: {{title}}

- {{header|safe}} + {% if message %}

{{message}}

diff --git a/resources/templates/edit-users.html b/resources/templates/edit-users.html index 2621576..06d0d2e 100644 --- a/resources/templates/edit-users.html +++ b/resources/templates/edit-users.html @@ -1,10 +1,18 @@ {% extends "templates/base.html" %} +{% block extra-headers %} + {% script "/vendor/node_modules/tablesort/dist/tablesort.min.js" %} +{% endblock %} {% block content %}
- - - +

+ {% i18n sortable %} +

+
{% i18n edit-col-hdr %}{% i18n del-col-hdr %}
+ + + + {% for user in users %} @@ -13,11 +21,12 @@ {% endfor %} - - - - + +
{% i18n user-title-prefix %}{% i18n edit-col-hdr %}{% i18n del-col-hdr %}
{% i18n del-col-hdr %} {{user}}
{% i18n add-user-label %}
{% i18n add-user-label %}
+ {% endblock %} diff --git a/resources/templates/edit.html b/resources/templates/edit.html index 36426dc..44ee54a 100644 --- a/resources/templates/edit.html +++ b/resources/templates/edit.html @@ -1,11 +1,11 @@ {% extends "templates/base.html" %} {% block extra-headers %} - {% ifequal js-from ":cloudflare" %} + {% ifequal js-from ":cdnjs" %} {% else %} - {% style "/vendor/simplemde/dist/simplemde.min.css" %} - {% script "/vendor/simplemde/dist/simplemde.min.js" %} + {% style "vendor/simplemde/dist/simplemde.min.css" %} + {% script "vendor/simplemde/dist/simplemde.min.js" %} {% endifequal %} {% endblock %} diff --git a/resources/templates/list-uploads.html b/resources/templates/list-uploads.html index e759dac..4289485 100644 --- a/resources/templates/list-uploads.html +++ b/resources/templates/list-uploads.html @@ -1,4 +1,10 @@ {% extends "templates/base.html" %} +{% block extra-headers %} + {% script "/vendor/node_modules/tablesort/dist/tablesort.min.js" %} + {% script "/vendor/node_modules/tablesort/dist/sorts/tablesort.number.min.js" %} + {% script "/vendor/node_modules/tablesort/dist/sorts/tablesort.date.min.js" %} + {% script "/vendor/node_modules/tablesort/dist/sorts/tablesort.monthname.min.js" %} +{% endblock %} {% block content %}
@@ -9,12 +15,15 @@

- - +

+ {% i18n sortable %} +

+
+ - + {% for entry in files %} @@ -26,9 +35,11 @@ - {% endfor %}
Name Uploaded Type thisTo get thisTo get this
{% if entry.is-image %} {{entry.name|capitalize}} {% else %} link {% endif %}
+ {% endblock %} diff --git a/src/smeagol/formatting.clj b/src/smeagol/formatting.clj index bbcaaa7..153c904 100644 --- a/src/smeagol/formatting.clj +++ b/src/smeagol/formatting.clj @@ -235,3 +235,4 @@ (reintegrate-inclusions (process-text md-src))) + diff --git a/src/smeagol/routes/wiki.clj b/src/smeagol/routes/wiki.clj index e4ca7ba..0fa7cac 100644 --- a/src/smeagol/routes/wiki.clj +++ b/src/smeagol/routes/wiki.clj @@ -15,6 +15,7 @@ [noir.util.route :as route] [noir.session :as session] [smeagol.authenticate :as auth] + [smeagol.configuration :refer [config]] [smeagol.diff2html :as d2h] [smeagol.formatting :refer [md->html]] [smeagol.history :as hist] @@ -193,7 +194,8 @@ (log/info (format "Showing history of page '%s'" page)) (layout/render "history.html" (merge (util/standard-params request) - {:title (util/get-message :history-title-prefix request) + {:title (str (util/get-message :history-title-prefix request) + " " page) :page page :history (hist/find-history repo-path file-name)})))) @@ -207,7 +209,7 @@ 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")) + (format-instant unix-time "dd MMMM YYYY")) ([^Long unix-time ^String template] (jt/format (java-time/formatter template) @@ -220,26 +222,26 @@ [request] (let [params (keywordize-keys (:params request)) - data-path (str util/content-dir "/uploads/") - cl (count (io/resource-path)) files - (map - #(zipmap - [:base-name :is-image :modified :name :resource] - [(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 %) - (subs (str (fs/absolute %)) cl)]) - (remove - #(or (cs/starts-with? (fs/name %) ".") - (fs/directory? %)) - (file-seq (clojure.java.io/file data-path))))] + (sort-by + (juxt :name (fn [x] (- 0 (count (:resource x))))) + (map + #(zipmap + [:base-name :is-image :modified :name :resource] + [(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 %) + (util/local-url %)]) + (remove + #(or (cs/starts-with? (fs/name %) ".") + (fs/directory? %)) + (file-seq (clojure.java.io/file util/upload-dir)))))] (log/info (with-out-str (pprint files))) (layout/render "list-uploads.html" @@ -261,6 +263,7 @@ files) })))) + ;;;; end of list-uploads section ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defn upload-page diff --git a/src/smeagol/util.clj b/src/smeagol/util.clj index 9e0a6ba..64e0a7c 100644 --- a/src/smeagol/util.clj +++ b/src/smeagol/util.clj @@ -2,6 +2,7 @@ :author "Simon Brooke"} smeagol.util (:require [clojure.java.io :as cjio] + [clojure.string :as cs] [environ.core :refer [env]] [me.raynes.fs :as fs] [noir.io :as io] @@ -49,6 +50,51 @@ (def upload-dir (str (cjio/file content-dir "uploads"))) +(def local-url-base + (let [a (str (fs/absolute content-dir))] + (subs a 0 (- (count a) (count "content"))))) + +(defn not-servable-reason + "As a string, the reason this `file-path` cannot safely be served, or `nil` + if it is safe to serve. This reason may be logged, but should *not* be + shown to remote users, as it would allow file system probing." + [file-path] + (let [path (fs/absolute file-path)] + (cond + (cs/includes? file-path "..") + (cs/join " " file-path + "Attempts to ascend the file hierarchy are disallowed.") + (not (cs/starts-with? path local-url-base)) + (cs/join " " [path "is not servable"]) + (not (fs/exists? path)) + (cs/join " " [path "does not exist"]) + (not (fs/readable? path)) + (cs/join " " [path "is not readable"])))) + +(defn local-url? + "True if this `file-path` can be served as a local URL, else false." + [file-path] + (empty? (not-servable-reason file-path))) + +(defn local-url + "Return a local URL for this `file-path`, or a deliberate 404 if none + can be safely served." + [file-path] + (try + (let [path (fs/absolute file-path) + problem (not-servable-reason path)] + (if + (empty? problem) + (subs (str path) (count local-url-base)) + (do + (log/error + "In `smeagol.util/local-url `" file-path "` is not a servable resource.") + (str "404-not-found?path=" file-path)))) + (catch Exception any + (log/error + "In `smeagol.util/local-url `" file-path "` is not a servable resource:" any) + (str "404-not-found?path=" file-path)))) + (defn standard-params "Return a map of standard parameters to pass to the template renderer." [request]