Implement clean URLs feature (Issue #89)

When `clean-urls?` is set in config, emit pages as subdirectories
`prefix/root/page-name/index.html` instead of
`prefix/root/page-name.html`. Links in emitted HTML then point to
`prefix/root/page-name/`. When `clean-urls?` not set, behaves as
before.

Refactor most URI generation into a new `page-uri` function.
`page-uri` replaces most calls* to `path`, all calls to `post-uri`
and all calls to the old `page-uri`.

Introduce function `create-file-recursive`. Function creates
file parent if not exists.

Introduce function `write-html`. When `clean-urls?` is set, spits
emitted HTML into subdirectories as described above; otherwise
behaves like `create-file`. Replaces most* calls to `create-file`
Calls `create-file` or `create-file-recursive`.

* Exceptions made for sitemap XML and RSS feed XML pages
This commit is contained in:
Tom L 2016-02-11 20:04:56 -06:00
parent b3bdba2804
commit c18c3d60f2
2 changed files with 104 additions and 83 deletions

View file

@ -11,7 +11,7 @@
[cryogen-core.sass :as sass] [cryogen-core.sass :as sass]
[cryogen-core.markup :as m] [cryogen-core.markup :as m]
[cryogen-core.io :refer [cryogen-core.io :refer
[get-resource find-assets create-folder create-file wipe-public-folder [get-resource find-assets create-folder create-file-recursive create-file wipe-public-folder
copy-resources copy-resources-from-theme path]] copy-resources copy-resources-from-theme path]]
[cryogen-core.sitemap :as sitemap] [cryogen-core.sitemap :as sitemap]
[cryogen-core.rss :as rss]) [cryogen-core.rss :as rss])
@ -47,15 +47,14 @@
(let [fmt (java.text.SimpleDateFormat. date-fmt)] (let [fmt (java.text.SimpleDateFormat. date-fmt)]
(.parse fmt (.substring file-name 0 10)))) (.parse fmt (.substring file-name 0 10))))
(defn post-uri
"Creates a post uri from the post file name"
[file-name {:keys [blog-prefix post-root-uri]} mu]
(path "/" blog-prefix post-root-uri (s/replace file-name (re-pattern-from-ext (m/ext mu)) ".html")))
(defn page-uri (defn page-uri
"Creates a page uri from the page file name" "Creates a URI from file name. `uri-type` is any of the uri types specified in config, e.g., `:post-root-uri`."
[page-name {:keys [blog-prefix page-root-uri]} mu] ([file-name params]
(path "/" blog-prefix page-root-uri (s/replace page-name (re-pattern-from-ext (m/ext mu)) ".html"))) (page-uri file-name nil params))
([file-name uri-type {:keys [blog-prefix clean-urls?] :as params}]
(let [page-uri (params uri-type)
uri-end (if clean-urls? (s/replace file-name #"(index)?\.html" "/") file-name)]
(path "/" blog-prefix page-uri uri-end))))
(defn read-page-meta (defn read-page-meta
"Returns the clojure map from the top of a markdown page/post" "Returns the clojure map from the top of a markdown page/post"
@ -94,7 +93,7 @@
(let [{:keys [file-name page-meta content]} (page-content page config markup)] (let [{:keys [file-name page-meta content]} (page-content page config markup)]
(merge (merge
(merge-meta-and-content file-name page-meta content) (merge-meta-and-content file-name page-meta content)
{:uri (page-uri file-name config markup) {:uri (page-uri file-name :page-root-uri config)
:page-index (:page-index page-meta)}))) :page-index (:page-index page-meta)})))
(defn parse-post (defn parse-post
@ -111,7 +110,7 @@
{:date date {:date date
:formatted-archive-group formatted-group :formatted-archive-group formatted-group
:parsed-archive-group (.parse archive-fmt formatted-group) :parsed-archive-group (.parse archive-fmt formatted-group)
:uri (post-uri file-name config markup) :uri (page-uri file-name :post-root-uri config)
:tags (set (:tags page-meta))})))) :tags (set (:tags page-meta))}))))
(defn read-posts (defn read-posts
@ -178,9 +177,9 @@
(defn tag-info (defn tag-info
"Returns a map containing the name and uri of the specified tag" "Returns a map containing the name and uri of the specified tag"
[{:keys [blog-prefix tag-root-uri]} tag] [config tag]
{:name (name tag) {:name (name tag)
:uri (path "/" blog-prefix tag-root-uri (str (name tag) ".html"))}) :uri (page-uri (str (name tag) ".html") :tag-root-uri config)})
(defn add-prev-next (defn add-prev-next
"Adds a :prev and :next key to the page/post data containing the title and uri of the prev/next "Adds a :prev and :next key to the page/post data containing the title and uri of the prev/next
@ -199,6 +198,13 @@
sidebar-pages false} (group-by #(boolean (:navbar? %)) pages)] sidebar-pages false} (group-by #(boolean (:navbar? %)) pages)]
(map (partial sort-by :page-index) [navbar-pages sidebar-pages]))) (map (partial sort-by :page-index) [navbar-pages sidebar-pages])))
(defn write-html
"When `clean-urls?` is set, appends `/index.html` before spit; otherwise just spits."
[file-uri {:keys [clean-urls?]} data]
(if clean-urls?
(create-file-recursive (path file-uri "index.html") data)
(create-file file-uri data)))
(defn compile-pages (defn compile-pages
"Compiles all the pages into html and spits them out into the public folder" "Compiles all the pages into html and spits them out into the public folder"
[{:keys [blog-prefix page-root-uri] :as params} pages] [{:keys [blog-prefix page-root-uri] :as params} pages]
@ -207,7 +213,8 @@
(create-folder (path "/" blog-prefix page-root-uri)) (create-folder (path "/" blog-prefix page-root-uri))
(doseq [{:keys [uri] :as page} pages] (doseq [{:keys [uri] :as page} pages]
(println "\t-->" (cyan uri)) (println "\t-->" (cyan uri))
(create-file uri (write-html uri
params
(render-file (str "/html/" (:layout page)) (render-file (str "/html/" (:layout page))
(merge params (merge params
{:active-page "pages" {:active-page "pages"
@ -223,7 +230,8 @@
(create-folder (path "/" blog-prefix post-root-uri)) (create-folder (path "/" blog-prefix post-root-uri))
(doseq [post posts] (doseq [post posts]
(println "\t-->" (cyan (:uri post))) (println "\t-->" (cyan (:uri post)))
(create-file (:uri post) (write-html (:uri post)
params
(render-file (str "/html/" (:layout post)) (render-file (str "/html/" (:layout post))
(merge params (merge params
{:active-page "posts" {:active-page "posts"
@ -241,7 +249,8 @@
(doseq [[tag posts] posts-by-tag] (doseq [[tag posts] posts-by-tag]
(let [{:keys [name uri]} (tag-info params tag)] (let [{:keys [name uri]} (tag-info params tag)]
(println "\t-->" (cyan uri)) (println "\t-->" (cyan uri))
(create-file uri (write-html uri
params
(render-file "/html/tag.html" (render-file "/html/tag.html"
(merge params (merge params
{:active-page "tags" {:active-page "tags"
@ -252,11 +261,13 @@
(defn compile-tags-page [{:keys [blog-prefix] :as params}] (defn compile-tags-page [{:keys [blog-prefix] :as params}]
(println (blue "compiling tags page")) (println (blue "compiling tags page"))
(create-file (path "/" blog-prefix "tags.html") (let [uri (page-uri "tags.html" params)]
(write-html uri
params
(render-file "/html/tags.html" (render-file "/html/tags.html"
(merge params (merge params
{:active-page "tags" {:active-page "tags"
:uri (path "/" blog-prefix "tags.html")})))) :uri uri})))))
(defn content-until-more-marker (defn content-until-more-marker
[^String content] [^String content]
@ -288,11 +299,11 @@
(defn create-preview-links (defn create-preview-links
"Turn each vector of previews into a map with :prev and :next keys that contain the uri of the "Turn each vector of previews into a map with :prev and :next keys that contain the uri of the
prev/next preview page" prev/next preview page"
[previews blog-prefix] [previews params]
(mapv (fn [[prev target next]] (mapv (fn [[prev target next]]
(merge target (merge target
{:prev (if prev (path "/" blog-prefix "p" (str (:index prev) ".html")) nil) {:prev (if prev (page-uri (path "p" (str (:index prev) ".html")) params) nil)
:next (if next (path "/" blog-prefix "p" (str (:index next) ".html")) nil)})) :next (if next (page-uri (path "p" (str (:index next) ".html")) params) nil)}))
(partition 3 1 (flatten [nil previews nil])))) (partition 3 1 (flatten [nil previews nil]))))
(defn compile-preview-pages (defn compile-preview-pages
@ -300,12 +311,13 @@
[{:keys [blog-prefix posts-per-page blocks-per-preview] :as params} posts] [{:keys [blog-prefix posts-per-page blocks-per-preview] :as params} posts]
(when-not (empty? posts) (when-not (empty? posts)
(let [previews (-> (create-previews posts-per-page blocks-per-preview posts) (let [previews (-> (create-previews posts-per-page blocks-per-preview posts)
(create-preview-links blog-prefix)) (create-preview-links params))
previews (if (> (count previews) 1) (assoc-in previews [1 :prev] (path "/" blog-prefix "index.html")) previews)] previews (if (> (count previews) 1) (assoc-in previews [1 :prev] (page-uri "index.html" params)) previews)]
(create-folder (path "/" blog-prefix "p")) (create-folder (path "/" blog-prefix "p"))
(doseq [{:keys [index posts prev next]} previews (doseq [{:keys [index posts prev next]} previews
:let [index-page? (= 1 index)]] :let [index-page? (= 1 index)]]
(create-file (if index-page? (path "/" blog-prefix "index.html") (path "/" blog-prefix "p" (str index ".html"))) (write-html (if index-page? (page-uri "index.html" params) (page-uri (path "p" (str index ".html")) params))
params
(render-file "/html/previews.html" (render-file "/html/previews.html"
(merge params (merge params
{:active-page "preview" {:active-page "preview"
@ -319,26 +331,30 @@
"Compiles the index page into html and spits it out into the public folder" "Compiles the index page into html and spits it out into the public folder"
[{:keys [blog-prefix disqus?] :as params}] [{:keys [blog-prefix disqus?] :as params}]
(println (blue "compiling index")) (println (blue "compiling index"))
(create-file (path "/" blog-prefix "index.html") (let [uri (page-uri "index.html" params)]
(write-html uri
params
(render-file "/html/home.html" (render-file "/html/home.html"
(merge params (merge params
{:active-page "home" {:active-page "home"
:home true :home true
:disqus? disqus? :disqus? disqus?
:post (get-in params [:latest-posts 0]) :post (get-in params [:latest-posts 0])
:uri (path "/" blog-prefix "index.html")})))) :uri uri})))))
(defn compile-archives (defn compile-archives
"Compiles the archives page into html and spits it out into the public folder" "Compiles the archives page into html and spits it out into the public folder"
[{:keys [blog-prefix] :as params} posts] [{:keys [blog-prefix] :as params} posts]
(println (blue "compiling archives")) (println (blue "compiling archives"))
(create-file (path "/" blog-prefix "archives.html") (let [uri (page-uri "archives.html" params)]
(write-html uri
params
(render-file "/html/archives.html" (render-file "/html/archives.html"
(merge params (merge params
{:active-page "archives" {:active-page "archives"
:archives true :archives true
:groups (group-for-archive posts) :groups (group-for-archive posts)
:uri (path "/" blog-prefix "/archives.html")})))) :uri uri})))))
(defn compile-authors (defn compile-authors
"For each author, creates a page with filtered posts." "For each author, creates a page with filtered posts."
@ -347,9 +363,10 @@
(create-folder (path "/" blog-prefix author-root-uri)) (create-folder (path "/" blog-prefix author-root-uri))
;; if the post author is empty defaults to config's :author ;; if the post author is empty defaults to config's :author
(doseq [{:keys [author posts]} (group-for-author posts author)] (doseq [{:keys [author posts]} (group-for-author posts author)]
(let [uri (path "/" blog-prefix author-root-uri (str author ".html"))] (let [uri (page-uri (str author ".html") :author-root-uri params)]
(println "\t-->" (cyan uri)) (println "\t-->" (cyan uri))
(create-file uri (write-html uri
params
(render-file "/html/author.html" (render-file "/html/author.html"
(merge params (merge params
{:author author {:author author
@ -414,9 +431,9 @@
:latest-posts (->> posts (take recent-posts) vec) :latest-posts (->> posts (take recent-posts) vec)
:navbar-pages navbar-pages :navbar-pages navbar-pages
:sidebar-pages sidebar-pages :sidebar-pages sidebar-pages
:archives-uri (path "/" blog-prefix "archives.html") :archives-uri (page-uri "archives.html" config)
:index-uri (path "/" blog-prefix "index.html") :index-uri (page-uri "index.html" config)
:tags-uri (path "/" blog-prefix "tags.html") :tags-uri (page-uri "tags.html" config)
:rss-uri (path "/" blog-prefix rss-name) :rss-uri (path "/" blog-prefix rss-name)
:site-url (if (.endsWith site-url "/") (.substring site-url 0 (dec (count site-url))) site-url) :site-url (if (.endsWith site-url "/") (.substring site-url 0 (dec (count site-url))) site-url)
:theme-path (str "file:resources/templates/themes/" (:theme config))})] :theme-path (str "file:resources/templates/themes/" (:theme config))})]

View file

@ -45,12 +45,16 @@
[])) []))
(defn create-folder [folder] (defn create-folder [folder]
(let [loc (io/file (str public folder))] (let [loc (io/file (path public folder))]
(when-not (.exists loc) (when-not (.exists loc)
(.mkdirs loc)))) (.mkdirs loc))))
(defn create-file [file data] (defn create-file [file data]
(spit (str public file) data)) (spit (path public file) data))
(defn create-file-recursive [file data]
(create-folder (.getParent (io/file file)))
(create-file file data))
(defn wipe-public-folder [keep-files] (defn wipe-public-folder [keep-files]
(let [filenamefilter (reify java.io.FilenameFilter (accept [this _ filename] (not (some #{filename} keep-files))))] (let [filenamefilter (reify java.io.FilenameFilter (accept [this _ filename] (not (some #{filename} keep-files))))]