diff --git a/resources/config.edn b/resources/config.edn
index 87d1361..ecb1ea0 100644
--- a/resources/config.edn
+++ b/resources/config.edn
@@ -30,37 +30,35 @@
:content-dir "resources/public/content"
;; where content is served from.
:default-locale "en-GB" ;; default language used for messages
+ :extensions-from :local ;; where to load JavaScript libraries
+ ;; from: options are :local, :remote.
:formatters ;; formatters for processing markdown
;; extensions.
{:backticks {:formatter "smeagol.formatting/process-backticks"
:scripts {}
:styles {}}
:mermaid {:formatter "smeagol.extensions.mermaid/process-mermaid"
- :scripts {:core {:local "vendor/mermaid/dist/mermaid.js"}}
+ :scripts {:core {:local "vendor/mermaid/dist/mermaid.js"
+ :remote "https://cdnjs.cloudflare.com/ajax/libs/mermaid/8.4.6/mermaid.min.js"}}
:styles {}}
:pswp {:formatter "smeagol.extensions.photoswipe/process-photoswipe"
- :scripts {:core {:local "/vendor/node_modules/photoswipe/dist/photoswipe.min.js"
+ :scripts {:core {:local "vendor/node_modules/photoswipe/dist/photoswipe.min.js"
:remote "https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.js"}
- :ui {:local "/vendor/node_modules/photoswipe/dist/photoswipe-ui-default.min.js"
+ :ui {:local "vendor/node_modules/photoswipe/dist/photoswipe-ui-default.min.js"
:remote "https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe-ui-default.min.js"}}
- :styles {:core {:local "/vendor/node_modules/photoswipe/dist/photoswipe.css"
- :remote "/vendor/node_modules/photoswipe/dist/default-skin/default-skin.css"}}}
+ :styles {:core {:local "vendor/node_modules/photoswipe/dist/photoswipe.css"}
+ :skin {:local "vendor/node_modules/photoswipe/dist/default-skin/default-skin.css"}}}
:test {:formatter "smeagol.extensions.test/process-test" }
:vega {:formatter "smeagol.extensions.vega/process-vega"
:scripts {:core {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega/5.9.1/vega.min.js"}
:lite {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega-lite/4.1.1/vega-lite.min.js"}
- :embed {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega-embed/6.2.2/vega-embed.min.js"}
- :styles {}}}
+ :embed {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega-embed/6.2.2/vega-embed.min.js"}}}
:vis {:formatter "smeagol.extensions.vega/process-vega"
:scripts {:core {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega/5.9.1/vega.min.js"}
:lite {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega-lite/4.1.1/vega-lite.min.js"}
- :embed {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega-embed/6.2.2/vega-embed.min.js"}
- :styles {}}}
- }
+ :embed {:remote "https://cdnjs.cloudflare.com/ajax/libs/vega-embed/6.2.2/vega-embed.min.js"}}}}
:log-level :info ;; the minimum logging level; one of
;; :trace :debug :info :warn :error :fatal
- :js-from :cdnjs ;; where to load JavaScript libraries
- ;; from: options are :local, :cdnjs
:passwd "resources/passwd"
;; where the password file is stored
:site-title "Smeagol" ;; overall title of the site, used in
@@ -71,4 +69,10 @@
;; stored in the /small directory
:med 400 ;; maximum dimension of thumbnails
;; stored in the /med directory
- }}
+ ;; you can add as many extra keys and values as
+ ;; you like here for additional sizes of images.
+ ;; Images will only be scaled if their maximum
+ ;; dimension (in pixels) is greater than the value;
+ ;; only JPEG and PNG images will be scaled.
+ }
+ }
diff --git a/resources/templates/wiki.html b/resources/templates/wiki.html
index 7342854..81634ea 100644
--- a/resources/templates/wiki.html
+++ b/resources/templates/wiki.html
@@ -2,11 +2,9 @@
{% block extra-headers %}
{% for script in scripts %}
-
- {% endfor %}
+ {% endfor %}
{% for style in styles %}
-
- {% endfor %}
+ {% endfor %}
{% endblock %}
{% block content %}
diff --git a/src/smeagol/formatting.clj b/src/smeagol/formatting.clj
index 153c904..5891eeb 100644
--- a/src/smeagol/formatting.clj
+++ b/src/smeagol/formatting.clj
@@ -93,6 +93,7 @@
fragments
(cons fragment processed)))
+
(defn deep-merge [v & vs]
"Cripped in its entirety from https://clojuredocs.org/clojure.core/merge."
(letfn [(rec-merge [v1 v2]
@@ -124,18 +125,18 @@
fragments
(cons ident processed))))
-(apply-formatter
- 3
- {:inclusions {}}
- '()
- '()
- "pswp
- ![Frost on a gate, Laurieston](content/uploads/g1.jpg)
- ![Feathered crystals on snow surface, Taliesin](content/uploads/g2.jpg)
- ![Feathered snow on log, Taliesin](content/uploads/g3.jpg)
- ![Crystaline growth on seed head, Taliesin](content/uploads/g4.jpg)"
- "pswp"
- smeagol.extensions.photoswipe/process-photoswipe)
+;; (apply-formatter
+;; 3
+;; {:inclusions {}}
+;; '()
+;; '()
+;; "pswp
+;; ![Frost on a gate, Laurieston](content/uploads/g1.jpg)
+;; ![Feathered crystals on snow surface, Taliesin](content/uploads/g2.jpg)
+;; ![Feathered snow on log, Taliesin](content/uploads/g3.jpg)
+;; ![Crystaline growth on seed head, Taliesin](content/uploads/g4.jpg)"
+;; "pswp"
+;; smeagol.extensions.photoswipe/process-photoswipe)
(defn process-text
"Process this `text`, assumed to be markdown potentially containing both local links
@@ -145,6 +146,8 @@
The map has two top-level keys: `:inclusions`, a map of constructed keywords to
inclusion specifications, and `:text`, an HTML text string with the keywords
present where the corresponding inclusion should be inserted."
+
+ ;; TODO: the inclusion->index bug is in here somewhere.
([^String text]
(process-text 0 {} (cs/split (or text "") #"```") '()))
([index result fragments processed]
@@ -185,19 +188,19 @@
(rest fragments)
(cons ident processed))
{:inclusions {ident (apply formatter (list (subs fragment (count first-token)) index))}
- :extensions (cons kw (:extensions result))}))
+ :extensions (assoc (or (:extensions result) {}) kw (-> config :formatters kw))}))
:else
;; Otherwise process the current fragment as markdown and recurse on
;; down the list
(process-markdown-fragment
index result remarked (rest fragments) processed)))))
-(process-text
- "pswp
- ![Frost on a gate, Laurieston](content/uploads/g1.jpg)
- ![Feathered crystals on snow surface, Taliesin](content/uploads/g2.jpg)
- ![Feathered snow on log, Taliesin](content/uploads/g3.jpg)
- ![Crystaline growth on seed head, Taliesin](content/uploads/g4.jpg)" )
+;; (process-text
+;; "pswp
+;; ![Frost on a gate, Laurieston](content/uploads/g1.jpg)
+;; ![Feathered crystals on snow surface, Taliesin](content/uploads/g2.jpg)
+;; ![Feathered snow on log, Taliesin](content/uploads/g3.jpg)
+;; ![Crystaline growth on seed head, Taliesin](content/uploads/g4.jpg)" )
(defn reintegrate-inclusions
"Given a map of the form produced by `process-text`, return a string of HTML text
@@ -236,3 +239,4 @@
+
diff --git a/src/smeagol/routes/wiki.clj b/src/smeagol/routes/wiki.clj
index 0fa7cac..a3456c1 100644
--- a/src/smeagol/routes/wiki.clj
+++ b/src/smeagol/routes/wiki.clj
@@ -28,7 +28,8 @@
[com.stuartsierra.component :as component]
[smeagol.configuration :refer [config]]
[smeagol.include.resolve-local-file :as resolve]
- [smeagol.include :as include]))
+ [smeagol.include :as include]
+ [smeagol.util :refer [content-dir local-url]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
@@ -118,6 +119,9 @@
(def md-include-system
+ "Allowing Markdown includes. Unfortunately the contributor who contributed
+ this didn't document it, and I haven't yet worked out how it works. TODO:
+ investigate and document."
(component/start
(component/system-map
:resolver (resolve/new-resolver util/content-dir)
@@ -125,6 +129,7 @@
(include/new-includer)
[:resolver]))))
+
(defn preferred-source
"Here, `component` is expected to be a map with two keys, `:local` and
`:remote`. If the value of `:extensions-from` in `config.edn` is remote
@@ -132,26 +137,51 @@
be returned. Otherwise, if the value of `:local` is nil and the value of
`:remote` is non-nil, the value of `:remote` will be returned. By default,
the value of `:local` will be returned."
- [component]
- (let [l (:local component) ;; TODO: look at the trick in Selmer to get relative URL
- r (:remote component)]
- (cond
- (= (:extensions-from config) :remote) (if (empty? r) l r)
- (empty? l) r
- :else l)))
+ [component ks]
+ (try
+ (let [l (:local component)
+ l' (if-not (empty? l) (local-url l) l)
+ r (:remote component)]
+ (cond
+ (= (:extensions-from config) :remote)
+ (if (empty? r) l' r)
+ (empty? l') r
+ :else l'))
+ (catch Exception any
+ (log/error "Failed to find appropriate source for component" ks "because:" any)
+ nil)))
+
+;; (preferred-source {:local "vendor/node_modules/photoswipe/dist/photoswipe.min.js",
+;; :remote "https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.js"} :core)
(defn collect-preferred
- "From extensions referenced in this `processed-text`, extract the preferred
- URLs for this keyword `k`, expected to be either `:scripts` or `:styles`."
- [processed-text k]
- (set
- (remove
- nil?
- (map
- preferred-source
- (apply
- concat
- (map vals (map k (vals (:extensions processed-text)))))))))
+ ([processed-text]
+ (concat
+ (collect-preferred processed-text :scripts)
+ (collect-preferred processed-text :styles)))
+ ([processed-text resource-type]
+ (reduce concat
+ (map
+ (fn [extension-key]
+ (map
+ (fn [requirement]
+ (let [r (preferred-source
+ (-> processed-text :extensions extension-key resource-type requirement)
+ requirement)]
+ (if (empty? r)
+ (log/warn "Found no valid URL for requirement"
+ requirement "of extension" extension-key))
+ r))
+ (keys (-> processed-text :extensions extension-key resource-type))))
+ (keys (:extensions processed-text))))))
+
+(cjio/file content-dir "vendor/node_modules/photoswipe/dist/photoswipe.min.js")
+
+(def processed-text (md->html (slurp "resources/public/content/Simplified example gallery.md" )))
+
+(preferred-source (-> processed-text :extensions :pswp :scripts :core) :pswp)
+
+(collect-preferred processed-text :scripts)
(defn wiki-page
"Render the markdown page specified in this `request`, if any. If none found, redirect to edit-page"
diff --git a/src/smeagol/util.clj b/src/smeagol/util.clj
index 64e0a7c..e92bafe 100644
--- a/src/smeagol/util.clj
+++ b/src/smeagol/util.clj
@@ -51,6 +51,8 @@
(str (cjio/file content-dir "uploads")))
(def local-url-base
+ "Essentially, the slash-terminated absolute path of the `public` resource
+ directory."
(let [a (str (fs/absolute content-dir))]
(subs a 0 (- (count a) (count "content")))))
@@ -59,7 +61,11 @@
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)]
+ (try
+ (let [path (if
+ (cs/starts-with? (str file-path) "/")
+ file-path
+ (cjio/file local-url-base file-path))]
(cond
(cs/includes? file-path "..")
(cs/join " " file-path
@@ -69,31 +75,57 @@
(not (fs/exists? path))
(cs/join " " [path "does not exist"])
(not (fs/readable? path))
- (cs/join " " [path "is not readable"]))))
+ (cs/join " " [path "is not readable"])))
+ (catch Exception any (cs/join " " file-path "is not servable because" (.getMessage any)))))
+
+
+;; (not-servable-reason "/home/simon/workspace/smeagol/resources/public/content/vendor/node_modules/photoswipe/dist/photoswipe.min.js")
+;; (not-servable-reason "/root/froboz")
(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)))
+ (try
+ (if
+ (empty? (not-servable-reason file-path))
+ true
+ (do
+ (log/error
+ "In `smeagol.util/local-url? `" file-path "` is not a servable resource.")
+ false))
+ (catch Exception any
+ (log/error
+ "In `smeagol.util/local-url `" file-path "` is not a servable resource:" any)
+ false)))
(defn local-url
"Return a local URL for this `file-path`, or a deliberate 404 if none
can be safely served."
+ ;; TODO: this actually returns a relative URL relative to local-url-base.
+ ;; That's not quite what we want because in Tomcat contexts the absolute
+ ;; URL may be different. We *ought* to be able to extract the offset from the
+ ;; servlet context, but it may be simpler to jam it in the config.
[file-path]
(try
- (let [path (fs/absolute file-path)
+ (let [path (if
+ (cs/starts-with? file-path local-url-base)
+ (subs file-path (count local-url-base))
+ file-path)
problem (not-servable-reason path)]
(if
(empty? problem)
- (subs (str path) (count local-url-base))
+ path
(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))))
+ (log/error
+ "In `smeagol.util/local-url `" file-path "` is not a servable resource:" any)
+ (str "404-not-found?path=" file-path))))
+
+(local-url? "vendor/node_modules/photoswipe/dist/photoswipe.min.js")
+(local-url? "/home/simon/workspace/smeagol/resources/public/vendor/node_modules/photoswipe/dist/photoswipe.min.js")
(defn standard-params
"Return a map of standard parameters to pass to the template renderer."