Skip to content

Commit

Permalink
Feat/rss (#14)
Browse files Browse the repository at this point in the history
* Feat: add clean anchor and internal link handler. (#13)

Add: empty footnote ref/def css classes.
Add: add sluggified id's to headlines.

Fix: add code child to get-headline-helper.

* Clean: project.clj and cli defaults.

* Clean: cljfmt-fix.
  • Loading branch information
teesloane authored May 29, 2020
1 parent 17a7cbb commit 01be814
Show file tree
Hide file tree
Showing 18 changed files with 145 additions and 87 deletions.
5 changes: 3 additions & 2 deletions clojure/project.clj
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
(defproject firn "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:description "Static Site Generator for Org Mode"
:url "https://github.com/theiceshelf/firn"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[borkdude/sci "0.0.13-alpha.19"]
[cheshire "5.10.0"]
[clj-rss "0.2.5"]
[hiccup "1.0.5"]
[http-kit "2.3.0"]
;; [juxt/dirwatch "0.2.5"] ;; vendored
Expand Down
6 changes: 6 additions & 0 deletions clojure/reflection.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
[
{
"name" : "java.nio.file.StandardWatchEventKinds$StdWatchEventKind[]"
},
{
"name": "com.sun.xml.internal.stream.XMLOutputFactoryImpl",
"allPublicConstructors": true,
"allPublicFields": true,
"allPublicMethods": true
}
]
9 changes: 7 additions & 2 deletions clojure/resources/firn/_firn_starter/config.edn
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
{:dir-data "data" ; org attachments/files to get copied into _site.
:ignored-dirs ["priv"]} ; Directories to ignore org files in.
{
:dir-data "data" ; org attachments/files to get copied into _site.
:ignored-dirs ["priv"] ; Directories to ignore org files in.
:site-url "" ; Root level url
:site-title "" ; Used for RSS.
:site-desc "" ; Used for RSS.
:enable-rss? true} ; defaults to true; creating feed.xml in your _site.
40 changes: 23 additions & 17 deletions clojure/src/firn/build.clj
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
(set! *warn-on-reflection* true)

(def default-files
["layouts/default.clj"
"partials/head.clj"
"config.edn"
"static/css/main.css"])
["layouts/default.clj"
"partials/head.clj"
"config.edn"
"static/css/main.css"])

(defn new-site
"Creates the folders needed for a new site in your wiki directory.
Expand All @@ -31,14 +31,16 @@
(io/make-parents (:out-name f))
(spit (:out-name f) (:contents f)))))))



(defn setup
"Creates folders for output, slurps in layouts and partials.
NOTE: should slurp/mkdir/copy-dir be wrapped in try-catches? if-err handling?"
[{:keys [dir-site dir-files dir-site-data
dir-data dir-site-static dir-static] :as config}]
[{:keys [dir-site
dir-files
dir-site-data
dir-data
dir-site-static
dir-static] :as config}]
(when-not (fs/exists? (config :dir-firn)) (new-site config))
(fs/mkdir dir-site) ;; make _site

Expand All @@ -49,10 +51,11 @@
(fs/delete-dir dir-site-static)
(fs/copy-dir dir-static dir-site-static)

(assoc config
:org-files (u/find-files-by-ext dir-files "org")
:layouts (file/read-clj :layouts config)
:partials (file/read-clj :partials config)))
(let [org-files (u/find-files-by-ext dir-files "org")
layouts (file/read-clj :layouts config)
partials (file/read-clj :partials config)]

(assoc config :org-files org-files :layouts layouts :partials partials)))

(defn write-files
"Takes a config, of which we can presume has :processed-files.
Expand All @@ -62,12 +65,15 @@
(let [out-file-name (str (config :dir-site) (f :path-web) ".html")]
(when-not (file/is-private? config f)
(io/make-parents out-file-name)
(spit out-file-name (f :as-html))))))
(spit out-file-name (f :as-html)))))
config)

(defn all-files
"Processes all files in the org-directory"
[{:keys [dir]}]
(let [config (setup (config/prepare dir))]
(->> config
file/process-all
write-files)))
(let [config (setup (config/prepare dir))
{:keys [enable-rss?]} config]
(cond->> config
true file/process-all
enable-rss? file/write-rss-file!
true write-files)))
28 changes: 14 additions & 14 deletions clojure/src/firn/config.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@
[sci.core :as sci]))

(def starting-config
{:dir-data "data" ; org attachments/files to get copied into _site.
:dir-files nil ; where org content lives.
:dir-layouts "" ; where layouts are stored.
:dir-partials "" ; where partials are stored.
:dir-site "" ; the root dir of the compiled firn site.
:dirname-files nil ; the name of directory where firn is run.
:ignored-dirs ["priv"] ; Directories to ignore org files in.
:layouts {} ; layouts loaded into memory
:partials {} ; partials loaded into memory
{:dir-data "data" ; org attachments/files to get copied into _site.
:dir-files nil ; where org content lives.
:dir-layouts "" ; where layouts are stored.
:dir-partials "" ; where partials are stored.
:dir-site "" ; the root dir of the compiled firn site.
:dirname-files nil ; the name of directory where firn is run.
:site-url "" ; Root level url
:site-title "" ; Used for RSS.
:site-desc "" ; Used for RSS.
:enable-rss? true ; If true, creates a feed.xml in _site.
:ignored-dirs ["priv"] ; Directories to ignore org files in.
:layouts {} ; layouts loaded into memory
:partials {} ; partials loaded into memory
:org-files []}) ; a list of org files, fetched when running setup.


;; -- Default Config -----------------------------------------------------------


(defn make-dir-firn
"make the _firn directory path."
[dir-files]
Expand All @@ -32,7 +32,7 @@

([dir-files external-config]
(let [base-config (merge starting-config external-config)]
(merge starting-config
(merge base-config
{:dir-firn (make-dir-firn dir-files)
:dir-data (str dir-files "/" (base-config :dir-data))
:dir-files dir-files
Expand Down
3 changes: 1 addition & 2 deletions clojure/src/firn/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
[]
;; An option with a required argument
[["-p" "--port PORT" "Port number"
:default 3333
:default 4000
:parse-fn #(Integer/parseInt %)
:validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]]
;; A boolean option defaulting to nil
Expand Down Expand Up @@ -91,4 +91,3 @@
"serve" (server/serve options)
"build" (build/all-files options)
"new" (build/new-site {}))))))

22 changes: 11 additions & 11 deletions clojure/src/firn/dirwatch.clj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
(ns ^{:doc "Directory watcher"
:author "Malcolm Sparks"
:requires "JDK7"}
firn.dirwatch
firn.dirwatch
(:import (java.io File)
(java.nio.file FileSystems Path StandardWatchEventKinds WatchEvent WatchKey WatchService)
(java.util.concurrent Executors ThreadFactory TimeUnit)))
Expand All @@ -23,11 +23,11 @@

(defonce pool
(Executors/newCachedThreadPool
(reify ThreadFactory
(newThread [_ runnable]
(doto (Thread. runnable)
(.setName (str "dirwatch-pool-" (swap! pool-counter inc)))
(.setDaemon true))))))
(reify ThreadFactory
(newThread [_ runnable]
(doto (Thread. runnable)
(.setName (str "dirwatch-pool-" (swap! pool-counter inc)))
(.setDaemon true))))))

(defn ^:private register-path
"Register a watch service with a filesystem path.
Expand All @@ -41,9 +41,9 @@
StandardWatchEventKinds/ENTRY_MODIFY]))
(doseq [^File dir (.. path toAbsolutePath toFile listFiles)]
(when (. dir isDirectory)
(register-path ws (. dir toPath) event-atom))
(register-path ws (. dir toPath) event-atom))
(when event-atom
(swap! event-atom conj {:file dir, :count 1, :action :create}))))
(swap! event-atom conj {:file dir, :count 1, :action :create}))))

(defn ^:private wait-for-events [^WatchService ws f]
(when ws ;; nil when this watcher is closed
Expand All @@ -53,7 +53,7 @@
after the watcher is closed."))]
(when (and k (.isValid k))
(doseq [^WatchEvent ev (.pollEvents k) :when (not= (.kind ev)
StandardWatchEventKinds/OVERFLOW)]
StandardWatchEventKinds/OVERFLOW)]
(let [file (.toFile (.resolve ^Path (cast Path (.watchable k)) ^Path (cast Path (.context ev))))]
(f {:file file
:count (.count ev)
Expand Down Expand Up @@ -110,5 +110,5 @@
[watcher]
{:pre [(::watcher (meta watcher))]}
(send-via pool watcher (fn [^WatchService w]
(when w (.close w))
nil)))
(when w (.close w))
nil)))
40 changes: 34 additions & 6 deletions clojure/src/firn/file.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
You can view the file data-structure as it is made by the `make` function."
(:require [cheshire.core :as json]
[clj-rss.core :as rss]
[clojure.java.io :as io]
[clojure.string :as s]
[firn.layout :as layout]
Expand Down Expand Up @@ -119,17 +120,17 @@
new-res [(+ acc-hours hour) (+ acc-minutes min)]]
new-res))]
(->> logbook
(reduce reduce-fn hours-minutes)
(u/timevec->time-str))))
(reduce reduce-fn hours-minutes)
(u/timevec->time-str))))

(defn- sort-logbook
"Loops over all logbooks, adds start and end unix timestamps."
[logbook file]
(let [mf #(org/parsed-org-date->unix-time %1 file)]
(->> logbook
;; Filter out timestamps if they don't have a start or end.
;; Filter out timestamps if they don't have a start or end.
(filter #(and (% :start) (% :end) (% :duration)))
;; adds a unix timestamp for the :start and :end time so that's sortable.
;; adds a unix timestamp for the :start and :end time so that's sortable.
(map #(assoc % :start-ts (mf (:start %)) :end-ts (mf (:end %))))
(sort-by :start-ts #(> %1 %2)))))

Expand Down Expand Up @@ -223,25 +224,52 @@
final (assoc config-with-data :processed-files with-html)]
final)

;; Otherwise continue...
(let [next-file (first org-files)
processed-file (process-one config next-file)
is-private (is-private? config processed-file)
org-files (rest org-files)
output (assoc output (processed-file :path-web) processed-file)
output (if is-private
output
(assoc output (processed-file :path-web) processed-file))
keyword-map (keywords->map processed-file)
new-site-map (merge keyword-map {:path (processed-file :path-web)})]

;; add to sitemap when file is not private.
(when-not (is-private? config processed-file)
(when-not is-private
(swap! site-map conj new-site-map)
(swap! site-links concat @site-links (-> processed-file :meta :links))
(swap! site-logs concat @site-logs (-> processed-file :meta :links)))
;; add links and logs to site wide data.
(recur org-files output))))))

(defn write-rss-file!
"Build an rss file. It sorts files by file:meta:date-created, writes to feed.xml"
[{:keys [processed-files site-url site-title dir-site site-desc] :as config}]
(println "Building rss file...")
(let [feed-file (str dir-site "feed.xml")
first-entry {:title site-title :link site-url :description site-desc}
make-rss (fn [[_ f]]
(hash-map :title (-> f :meta :title)
:link (str site-url "/" (-> f :path-web))
:pubDate (u/org-date->java-date (-> f :meta :date-created))
:description (str (f :as-html))))]
(io/make-parents feed-file)
(->> processed-files
(filter (fn [[_ f]] (-> f :meta :date-created)))
(map make-rss)
(sort-by :pubDate)
(reverse)
(u/prepend-vec first-entry) ; first entry must be about the site
(apply rss/channel-xml)
(spit feed-file)))
config)

(defn reload-requested-file
"Take a request to a file, pulls the file out of memory
grabs the path of the original file, reslurps it and reprocesses"
[file config]
(let [re-slurped (-> file :path io/file)
re-processed (process-one config re-slurped)]
re-processed))

3 changes: 2 additions & 1 deletion clojure/src/firn/layout.clj
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@


;; render a headline title.


(and is-headline? (= opts :title))
(let [hl (org/get-headline org-tree action)]
(-> hl :children first markup/to-html))
Expand Down Expand Up @@ -104,7 +106,6 @@
:date-updated (-> file :meta :date-updated)
:date-created (-> file :meta :date-created)})


(defn apply-layout
"If a file has a template, render the file with it, or use the default layout"
[config file layout]
Expand Down
15 changes: 7 additions & 8 deletions clojure/src/firn/markup.clj
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@
"converts `::*My Heading` => #my-heading"
[anchor]
(str "#" (-> anchor
(s/replace #"::\*" "")
(s/replace #" " "-")
(s/lower-case))))
(s/replace #"::\*" "")
(s/replace #" " "-")
(s/lower-case))))

(defn internal-link-handler
"Takes an org link and converts it into an html path."
Expand All @@ -63,8 +63,8 @@
img-http-regex #"(http:\/\/|https:\/\/)(.*)\.(jpg|JPG|gif|GIF|png)"
img-rel-regex #"(\.(.*))\.(jpg|JPG|gif|GIF|png)"
img-make-url #(->> (re-matches img-file-regex link-href)
(take-last 2)
(s/join "."))
(take-last 2)
(s/join "."))
;; file regexs / ctor fns
org-file-regex #"(file:)(.*)\.(org)(\:\:\*.+)?"
http-link-regex #"https?:\/\/(?![^\" ]*(?:jpg|png|gif))[^\" ]+"]
Expand Down Expand Up @@ -107,8 +107,7 @@
parent {:type "headline" :level level :children [v]}
heading-priority (u/str->keywrd "span.firn-headline-priority.firn-headline-priority__" priority)
heading-keyword (u/str->keywrd "span.firn-headline-keyword.firn-headline-keyword__" keywrd)
heading-anchor (-> parent org/get-headline-helper clean-anchor)
;; _ (prn "heading anchor is " heading-anchor)
heading-anchor (-> parent org/get-headline-helper clean-anchor)
heading-id+class #(u/str->keywrd "h" % heading-anchor ".firn-headline.firn-headline-" %)
h-level (case level
1 (heading-id+class 1)
Expand Down Expand Up @@ -155,7 +154,7 @@
(defn to-html
"Recursively Parses the org-edn into hiccup.
Some values don't get parsed (drawers) - yet. They return empty strings.
Don't destructure! - it can create uneven maps from possible nil vals on `V`"
Don't destructure! - with recursion, it can create uneven maps from possible nil vals on `v`"
[v]
(let [type (get v :type)
children (get v :children)
Expand Down
6 changes: 3 additions & 3 deletions clojure/src/firn/org.clj
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@
"Fetches a headline from an org-mode tree."
[tree name]
(->> (tree-seq map? :children tree)
(filter #(and (= "headline" (:type %))
(= name (get-headline-helper %))))
(first)))
(filter #(and (= "headline" (:type %))
(= name (get-headline-helper %))))
(first)))

(defn get-headline-content
"Same as get-headline, but removes the first child :title)."
Expand Down
Loading

0 comments on commit 01be814

Please sign in to comment.