From 3f788ee8e2af7c8871d33d3449940879f67f343e Mon Sep 17 00:00:00 2001 From: Nan Zhong Date: Mon, 20 Sep 2021 14:21:59 -0400 Subject: [PATCH 01/10] Add support for org-roam input sources --- weblorg.el | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/weblorg.el b/weblorg.el index 3dd2ac4..18fca5b 100644 --- a/weblorg.el +++ b/weblorg.el @@ -549,6 +549,70 @@ last." +;; ---- Input Source: org-roam ---- + +(defvar weblorg--org-id-updated nil + "Internal state representing if org id mapping have been updated.") + +(defvar weblorg--org-roam-db-synced nil + "Internal state representing if org-roam db has been synced.") + +(defun weblorg--input-source-org-roam-nodes (&optional filter render-org) + "Computes an alist for each org-roam file node that matches FILTER, optionally +rendering the html if RENDER-ORG is non-nil." + ;; Make sure we are starting off with a sync org-roam db. + (unless weblorg--org-roam-db-synced + (org-roam-db-sync) + (setq weblorg--org-roam-db-synced t)) + + (let* ((org-roam-files (sort (mapcar (lambda (file) + `(("file" . ,(nth 0 file)) + ("atime" . ,(nth 1 file)) + ("mtime" . ,(nth 2 file)))) + (org-roam-db-query [:select [file atime mtime] :from files])) + (lambda (a b) + (string< (cdr (assoc "file" a)) + (cdr (assoc "file" b))))))) + + ;; Update id locations based on the org-roam-files. + (unless weblorg--org-id-updated + (org-id-update-id-locations (mapcar (lambda (file) (cdr (assoc "file" file))) org-roam-files)) + (setq weblorg--org-id-update t)) + + (delq nil (mapcar (lambda (file) + (let ((file-path (cdr (assoc "file" file)))) + (org-roam-with-temp-buffer file-path + (let* ((slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory file-path)))) + (node (org-roam-node-at-point)) + (tags (car (org-roam-db-query [:select [tag] :from tags :where (= node-id $s1)] + (org-roam-node-id node)))) + (backlinks (mapcar (lambda (node) + (let* ((source-node (org-roam-backlink-source-node node)) + (source-slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory (org-roam-node-file source-node))))) + (source-title (org-roam-node-title source-node))) + `(("slug" . ,source-slug) + ("title" . ,source-title)))) + (org-roam-backlinks-get node))) + (keywords (if render-org (weblorg--parse-org (buffer-string) file-path) + `(("title" . ,(org-roam-node-title node)))))) + (weblorg--prepend keywords (cons "backlinks" backlinks)) + (weblorg--prepend keywords (cons "tags" tags)) + (weblorg--prepend keywords (cons "slug" slug)) + (if filter (if (funcall filter keywords) keywords + nil) + keywords))))) + org-roam-files)))) + +(defun weblorg-input-source-org-roam-nodes (&optional filter) + "Find all org-roam file nodes that match the optional FILTER" + (mapcar (lambda (node) + `(("node" . ,node))) + (weblorg--input-source-org-roam-nodes filter t))) + +(defun weblorg-input-source-org-roam-nodes-agg (&optional filter) + "Aggregate all org-roam file nodes that match the optional FILTER." + `((("nodes" . ,(weblorg--input-source-org-roam-nodes filter))))) + ;; ---- Input Source: autodoc ---- (defun weblorg-input-source-autodoc (pattern) @@ -782,13 +846,23 @@ default templates." This function also installs an Org-Mode link handler `url_for' that is accessible with the same syntax as the template filter." (let ((site (gethash :site route)) - (env (gethash :template-env route))) + (env (gethash :template-env route)) + (name (gethash :name route))) ;; Install link handlers (org-link-set-parameters "anchor" :export (lambda(path desc _backend) (format "%s" path desc))) (org-link-set-parameters + "id" + :export (lambda (id desc _backend) + (format "%s" + (weblorg--url-for (format "%s,slug=%s" + name + (weblorg--slugify (file-name-sans-extension (file-name-nondirectory (org-id-find-id-file id))))) + site) + desc))) + (org-link-set-parameters "url_for" :export (lambda(path desc _backend) (format "%s" (weblorg--url-for path site) desc))) From 5f3aacb8e5b9573fdcd2d490448af7b3d213b9ad Mon Sep 17 00:00:00 2001 From: Nan Zhong Date: Mon, 20 Sep 2021 14:38:01 -0400 Subject: [PATCH 02/10] Add default-route configuration for site for resolving org id links --- weblorg.el | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/weblorg.el b/weblorg.el index 18fca5b..f3dcd7f 100644 --- a/weblorg.el +++ b/weblorg.el @@ -141,14 +141,17 @@ OPTIONS can contain the following parameters: tries to register two sites with the same ~:base-url~, an error will be raised. + * *:default-route.* Name of the route to use for resolving org id links. + * *:template-vars* Association list of extra variables to be passed down to all templates. - * *:theme* function that returns the base path of a weblorg + * *:theme* Function that returns the base path of a weblorg theme. It defaults to [[anchor:symbol-weblorg-theme-default][weblorg-theme-default]]." (let* ((opt (seq-partition options 2)) (base-url (weblorg--get opt :base-url weblorg-default-url)) + (default-route (weblorg--get opt :default-route)) (theme (weblorg--get opt :theme #'weblorg-theme-default)) (site (weblorg--site-get base-url))) (if (null site) @@ -159,6 +162,7 @@ OPTIONS can contain the following parameters: ;; parameters of the route. (let ((new-site (make-hash-table :size 3))) (puthash :base-url base-url new-site) + (puthash :default-route default-route new-site) (puthash :theme theme new-site) (puthash :template-vars (weblorg--get opt :template-vars nil) new-site) (puthash :routes (make-hash-table :test 'equal) new-site) @@ -845,23 +849,14 @@ default templates." This function also installs an Org-Mode link handler `url_for' that is accessible with the same syntax as the template filter." - (let ((site (gethash :site route)) - (env (gethash :template-env route)) - (name (gethash :name route))) + (let* ((site (gethash :site route)) + (site-default-route (gethash :default-route site)) + (env (gethash :template-env route))) ;; Install link handlers (org-link-set-parameters "anchor" :export (lambda(path desc _backend) (format "%s" path desc))) - (org-link-set-parameters - "id" - :export (lambda (id desc _backend) - (format "%s" - (weblorg--url-for (format "%s,slug=%s" - name - (weblorg--slugify (file-name-sans-extension (file-name-nondirectory (org-id-find-id-file id))))) - site) - desc))) (org-link-set-parameters "url_for" :export (lambda(path desc _backend) @@ -870,6 +865,16 @@ that is accessible with the same syntax as the template filter." "url_for_img" :export (lambda(path desc _backend) (format "\"%s\"" (weblorg--url-for path site) desc))) + (if site-default-route + (org-link-set-parameters + "id" + :export (lambda (id desc _backend) + (format "%s" + (weblorg--url-for (format "%s,slug=%s" + site-default-route + (weblorg--slugify (file-name-sans-extension (file-name-nondirectory (org-id-find-id-file id))))) + site) + desc)))) (templatel-env-add-filter env "url_for" (lambda(route-name &optional vars) From f11687a8c1c58c15ce50b2245085bb3760681219 Mon Sep 17 00:00:00 2001 From: Nan Zhong Date: Mon, 20 Sep 2021 14:43:28 -0400 Subject: [PATCH 03/10] Change input-source to support both list and function forms --- weblorg.el | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/weblorg.el b/weblorg.el index f3dcd7f..75c0b86 100644 --- a/weblorg.el +++ b/weblorg.el @@ -231,10 +231,11 @@ Parameters in ~OPTIONS~: files from the input list. The variables available for the template come from the return of this function. - * *:input-source* List of collections of data to be written - directly to templates. In other words, this parameter - replaces the pipeline ~pattern~ > ~exclude~ > ~filter~ > - ~aggregate~ and will feed data directly into the function that + * *:input-source* List of collections or a function that returns + a list of collections of data to be written directly to templates. + In other words, this parameter replaces the pipeline + ~pattern~ > ~exclude~ > ~filter~ > ~aggregate~ + and will feed data directly into the function that writes down templates. This is useful for generating HTML files off template variables read from whatever source you want. @@ -415,9 +416,9 @@ Parameters in ~OPTIONS~: (let ((input-source (gethash :input-source route))) (weblorg--export-templates route - (if (null input-source) - (weblorg--route-posts route) - input-source)))) + (cond ((null input-source) (weblorg--route-posts route)) + ((functionp input-source) (funcall input-source)) + (t input-source))))) (defun weblorg-export-assets (route) "Export static assets ROUTE." From 49f35dde719e3b88cd0e8762716303fb758962cb Mon Sep 17 00:00:00 2001 From: Nan Zhong Date: Tue, 21 Sep 2021 11:16:44 -0400 Subject: [PATCH 04/10] Use org-id-find instead of internal org-id-find-id-file function --- weblorg.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/weblorg.el b/weblorg.el index 75c0b86..b3e1724 100644 --- a/weblorg.el +++ b/weblorg.el @@ -873,7 +873,13 @@ that is accessible with the same syntax as the template filter." (format "%s" (weblorg--url-for (format "%s,slug=%s" site-default-route - (weblorg--slugify (file-name-sans-extension (file-name-nondirectory (org-id-find-id-file id))))) + (let* ((where (org-id-find id)) + (file (car where)) + (file-slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory file)))) + ;; TODO it might be possible to link to a more specific location based on the file and position. + ;; Perhaps by using the org-element apis or the org-roam db. + (pos (cdr where))) + file-slug)) site) desc)))) (templatel-env-add-filter From 275fcedd204a788d603114255378d3a899722861 Mon Sep 17 00:00:00 2001 From: Nan Zhong Date: Wed, 8 Dec 2021 23:11:32 -0500 Subject: [PATCH 05/10] Separate org-roam related bits into a separate extension --- weblorg-org-roam.el | 70 +++++++++++++++++++++++++++++++++++++++++++++ weblorg.el | 64 ----------------------------------------- 2 files changed, 70 insertions(+), 64 deletions(-) create mode 100644 weblorg-org-roam.el diff --git a/weblorg-org-roam.el b/weblorg-org-roam.el new file mode 100644 index 0000000..cd524f7 --- /dev/null +++ b/weblorg-org-roam.el @@ -0,0 +1,70 @@ +(require 'weblorg) +(require 'org-roam) + +;; ---- Input Source: org-roam ---- + +(defvar weblorg--org-id-updated nil + "Internal state representing if org id mapping have been updated.") + +(defvar weblorg--org-roam-db-synced nil + "Internal state representing if org-roam db has been synced.") + +(defun weblorg--input-source-org-roam-nodes (&optional filter render-org) + "Computes an alist for each org-roam file node that matches FILTER, optionally +rendering the html if RENDER-ORG is non-nil." + ;; Make sure we are starting off with a sync org-roam db. + (unless weblorg--org-roam-db-synced + (org-roam-db-sync) + (setq weblorg--org-roam-db-synced t)) + + (let* ((org-roam-files (sort (mapcar (lambda (file) + `(("file" . ,(nth 0 file)) + ("atime" . ,(nth 1 file)) + ("mtime" . ,(nth 2 file)))) + (org-roam-db-query [:select [file atime mtime] :from files])) + (lambda (a b) + (string< (cdr (assoc "file" a)) + (cdr (assoc "file" b))))))) + + ;; Update id locations based on the org-roam-files. + (unless weblorg--org-id-updated + (org-id-update-id-locations (mapcar (lambda (file) (cdr (assoc "file" file))) org-roam-files)) + (setq weblorg--org-id-updated t)) + + (delq nil (mapcar (lambda (file) + (let ((file-path (cdr (assoc "file" file)))) + (org-roam-with-temp-buffer file-path + '(print (current-buffer))) + (org-roam-with-temp-buffer file-path + (let* ((slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory file-path)))) + (node (org-roam-node-at-point)) + (tags (car (org-roam-db-query [:select [tag] :from tags :where (= node-id $s1)] + (org-roam-node-id node)))) + (backlinks (mapcar (lambda (node) + (let* ((source-node (org-roam-backlink-source-node node)) + (source-slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory (org-roam-node-file source-node))))) + (source-title (org-roam-node-title source-node))) + `(("slug" . ,source-slug) + ("title" . ,source-title)))) + (org-roam-backlinks-get node))) + (keywords (if render-org (weblorg--parse-org (buffer-string) file-path) + `(("title" . ,(org-roam-node-title node)))))) + (weblorg--prepend keywords (cons "backlinks" backlinks)) + (weblorg--prepend keywords (cons "tags" tags)) + (weblorg--prepend keywords (cons "slug" slug)) + (if filter (if (funcall filter keywords) keywords + nil) + keywords))))) + org-roam-files)))) + +(defun weblorg-input-source-org-roam-nodes (&optional filter) + "Find all org-roam file nodes that match the optional FILTER" + (mapcar (lambda (node) + `(("node" . ,node))) + (weblorg--input-source-org-roam-nodes filter t))) + +(defun weblorg-input-source-org-roam-nodes-agg (&optional filter) + "Aggregate all org-roam file nodes that match the optional FILTER." + `((("nodes" . ,(weblorg--input-source-org-roam-nodes filter))))) + +(provide 'weblorg-org-roam) diff --git a/weblorg.el b/weblorg.el index b3e1724..f1ecf73 100644 --- a/weblorg.el +++ b/weblorg.el @@ -554,70 +554,6 @@ last." -;; ---- Input Source: org-roam ---- - -(defvar weblorg--org-id-updated nil - "Internal state representing if org id mapping have been updated.") - -(defvar weblorg--org-roam-db-synced nil - "Internal state representing if org-roam db has been synced.") - -(defun weblorg--input-source-org-roam-nodes (&optional filter render-org) - "Computes an alist for each org-roam file node that matches FILTER, optionally -rendering the html if RENDER-ORG is non-nil." - ;; Make sure we are starting off with a sync org-roam db. - (unless weblorg--org-roam-db-synced - (org-roam-db-sync) - (setq weblorg--org-roam-db-synced t)) - - (let* ((org-roam-files (sort (mapcar (lambda (file) - `(("file" . ,(nth 0 file)) - ("atime" . ,(nth 1 file)) - ("mtime" . ,(nth 2 file)))) - (org-roam-db-query [:select [file atime mtime] :from files])) - (lambda (a b) - (string< (cdr (assoc "file" a)) - (cdr (assoc "file" b))))))) - - ;; Update id locations based on the org-roam-files. - (unless weblorg--org-id-updated - (org-id-update-id-locations (mapcar (lambda (file) (cdr (assoc "file" file))) org-roam-files)) - (setq weblorg--org-id-update t)) - - (delq nil (mapcar (lambda (file) - (let ((file-path (cdr (assoc "file" file)))) - (org-roam-with-temp-buffer file-path - (let* ((slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory file-path)))) - (node (org-roam-node-at-point)) - (tags (car (org-roam-db-query [:select [tag] :from tags :where (= node-id $s1)] - (org-roam-node-id node)))) - (backlinks (mapcar (lambda (node) - (let* ((source-node (org-roam-backlink-source-node node)) - (source-slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory (org-roam-node-file source-node))))) - (source-title (org-roam-node-title source-node))) - `(("slug" . ,source-slug) - ("title" . ,source-title)))) - (org-roam-backlinks-get node))) - (keywords (if render-org (weblorg--parse-org (buffer-string) file-path) - `(("title" . ,(org-roam-node-title node)))))) - (weblorg--prepend keywords (cons "backlinks" backlinks)) - (weblorg--prepend keywords (cons "tags" tags)) - (weblorg--prepend keywords (cons "slug" slug)) - (if filter (if (funcall filter keywords) keywords - nil) - keywords))))) - org-roam-files)))) - -(defun weblorg-input-source-org-roam-nodes (&optional filter) - "Find all org-roam file nodes that match the optional FILTER" - (mapcar (lambda (node) - `(("node" . ,node))) - (weblorg--input-source-org-roam-nodes filter t))) - -(defun weblorg-input-source-org-roam-nodes-agg (&optional filter) - "Aggregate all org-roam file nodes that match the optional FILTER." - `((("nodes" . ,(weblorg--input-source-org-roam-nodes filter))))) - ;; ---- Input Source: autodoc ---- (defun weblorg-input-source-autodoc (pattern) From 69a4388848c517f09ad8497e3c2e57c4235eb56a Mon Sep 17 00:00:00 2001 From: Nan Zhong Date: Fri, 10 Dec 2021 04:19:25 -0500 Subject: [PATCH 06/10] Add support for sorting and limiting org-roam-nodes --- weblorg-org-roam.el | 58 +++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/weblorg-org-roam.el b/weblorg-org-roam.el index cd524f7..2ebde6f 100644 --- a/weblorg-org-roam.el +++ b/weblorg-org-roam.el @@ -9,22 +9,32 @@ (defvar weblorg--org-roam-db-synced nil "Internal state representing if org-roam db has been synced.") -(defun weblorg--input-source-org-roam-nodes (&optional filter render-org) - "Computes an alist for each org-roam file node that matches FILTER, optionally -rendering the html if RENDER-ORG is non-nil." +(defun weblorg--input-source-org-roam-nodes (&optional filter-fn sort-fn limit render-org) + "Computes an alist for each org-roam file node and return the list of alists. + +A FILTER-FN function taking a org-roam-node and returning a bool can be provided +to select which nodes to return. +A SORT-FN function taking two org-roam-nodes and returning a bool can be +provided to order the returned nodes. +A LIMIT integer value can be provided to limit how many results are returned. +A RENDER-ORG bool value can be provided to selectively enable rendering HTML for +each node." ;; Make sure we are starting off with a sync org-roam db. (unless weblorg--org-roam-db-synced (org-roam-db-sync) (setq weblorg--org-roam-db-synced t)) - (let* ((org-roam-files (sort (mapcar (lambda (file) - `(("file" . ,(nth 0 file)) - ("atime" . ,(nth 1 file)) - ("mtime" . ,(nth 2 file)))) - (org-roam-db-query [:select [file atime mtime] :from files])) - (lambda (a b) - (string< (cdr (assoc "file" a)) - (cdr (assoc "file" b))))))) + (let* ((org-roam-files (mapcar (lambda (file) + `(("file" . ,(nth 0 file)) + ("atime" . ,(nth 1 file)) + ("mtime" . ,(nth 2 file)))) + (org-roam-db-query [:select [file atime mtime] :from files]))) + (org-roam-files (sort org-roam-files (or sort-fn + (lambda (a b) + (string< (cdr (assoc "file" a)) + (cdr (assoc "file" b))))))) + (org-roam-files (if limit (seq-take org-roam-files limit) + org-roam-files))) ;; Update id locations based on the org-roam-files. (unless weblorg--org-id-updated @@ -52,19 +62,31 @@ rendering the html if RENDER-ORG is non-nil." (weblorg--prepend keywords (cons "backlinks" backlinks)) (weblorg--prepend keywords (cons "tags" tags)) (weblorg--prepend keywords (cons "slug" slug)) - (if filter (if (funcall filter keywords) keywords + (if filter-fn (if (funcall filter-fn keywords) keywords nil) keywords))))) org-roam-files)))) -(defun weblorg-input-source-org-roam-nodes (&optional filter) - "Find all org-roam file nodes that match the optional FILTER" +(defun weblorg-input-source-org-roam-nodes (&optional filter-fn sort-fn limit) + "Find all org-roam file nodes. + +A FILTER-FN function taking a org-roam-node and returning a bool can be provided +to select which nodes to return. +A SORT-FN function taking two org-roam-nodes and returning a bool can be +provided to order the returned nodes. +A LIMIT integer value can be provided to limit how many results are returned." (mapcar (lambda (node) `(("node" . ,node))) - (weblorg--input-source-org-roam-nodes filter t))) + (weblorg--input-source-org-roam-nodes filter-fn sort-fn limit t))) + +(defun weblorg-input-source-org-roam-nodes-agg (&optional filter-fn sort-fn limit) + "Aggregate all org-roam file nodes. -(defun weblorg-input-source-org-roam-nodes-agg (&optional filter) - "Aggregate all org-roam file nodes that match the optional FILTER." - `((("nodes" . ,(weblorg--input-source-org-roam-nodes filter))))) +A FILTER-FN function taking a org-roam-node and returning a bool can be provided +to select which nodes to return. +A SORT-FN function taking two org-roam-nodes and returning a bool can be +provided to order the returned nodes. +A LIMIT integer value can be provided to limit how many results are returned." + `((("nodes" . ,(weblorg--input-source-org-roam-nodes filter-fn sort-fn limit))))) (provide 'weblorg-org-roam) From 22e3be84a9d24ad52202619206a217cff25ec44b Mon Sep 17 00:00:00 2001 From: Nan Zhong Date: Fri, 10 Dec 2021 05:30:24 -0500 Subject: [PATCH 07/10] Clean up use of intermediate alist representation of org-roam-node --- weblorg-org-roam.el | 77 +++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/weblorg-org-roam.el b/weblorg-org-roam.el index 2ebde6f..c9f1577 100644 --- a/weblorg-org-roam.el +++ b/weblorg-org-roam.el @@ -24,48 +24,49 @@ each node." (org-roam-db-sync) (setq weblorg--org-roam-db-synced t)) - (let* ((org-roam-files (mapcar (lambda (file) - `(("file" . ,(nth 0 file)) - ("atime" . ,(nth 1 file)) - ("mtime" . ,(nth 2 file)))) - (org-roam-db-query [:select [file atime mtime] :from files]))) - (org-roam-files (sort org-roam-files (or sort-fn - (lambda (a b) - (string< (cdr (assoc "file" a)) - (cdr (assoc "file" b))))))) - (org-roam-files (if limit (seq-take org-roam-files limit) - org-roam-files))) - + (let ((org-roam-files (mapcar (lambda (result) + (car result)) + (org-roam-db-query [:select file :from files])))) ;; Update id locations based on the org-roam-files. (unless weblorg--org-id-updated - (org-id-update-id-locations (mapcar (lambda (file) (cdr (assoc "file" file))) org-roam-files)) + (org-id-update-id-locations org-roam-files) (setq weblorg--org-id-updated t)) - (delq nil (mapcar (lambda (file) - (let ((file-path (cdr (assoc "file" file)))) - (org-roam-with-temp-buffer file-path - '(print (current-buffer))) - (org-roam-with-temp-buffer file-path - (let* ((slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory file-path)))) - (node (org-roam-node-at-point)) - (tags (car (org-roam-db-query [:select [tag] :from tags :where (= node-id $s1)] - (org-roam-node-id node)))) - (backlinks (mapcar (lambda (node) - (let* ((source-node (org-roam-backlink-source-node node)) - (source-slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory (org-roam-node-file source-node))))) - (source-title (org-roam-node-title source-node))) - `(("slug" . ,source-slug) - ("title" . ,source-title)))) - (org-roam-backlinks-get node))) - (keywords (if render-org (weblorg--parse-org (buffer-string) file-path) - `(("title" . ,(org-roam-node-title node)))))) - (weblorg--prepend keywords (cons "backlinks" backlinks)) - (weblorg--prepend keywords (cons "tags" tags)) - (weblorg--prepend keywords (cons "slug" slug)) - (if filter-fn (if (funcall filter-fn keywords) keywords - nil) - keywords))))) - org-roam-files)))) + (let* ((org-roam-nodes (mapcar (lambda (file) + (org-roam-with-temp-buffer file + (org-roam-node-at-point))) + org-roam-files)) + (org-roam-nodes (seq-filter (lambda (node) + (if filter-fn (funcall filter-fn node) + t)) + org-roam-nodes)) + (org-roam-nodes (sort org-roam-nodes (or sort-fn + (lambda (a b) + (string< (org-roam-node-title a) + (org-roam-node-title b)))))) + (org-roam-nodes (if limit (seq-take org-roam-nodes limit) + org-roam-nodes))) + (mapcar (lambda (node) + (let ((slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory (org-roam-node-file node))))) + (tags (org-roam-node-tags node)) + (backlinks (mapcar (lambda (node) + (let* ((source-node (org-roam-backlink-source-node node)) + (source-slug (weblorg--slugify (file-name-sans-extension (file-name-nondirectory (org-roam-node-file source-node))))) + (source-title (org-roam-node-title source-node))) + `(("slug" . ,source-slug) + ("title" . ,source-title)))) + (org-roam-backlinks-get node))) + (atime (org-roam-node-file-atime node)) + (mtime (org-roam-node-file-mtime node)) + (keywords (if render-org (weblorg--parse-org-file (org-roam-node-file node)) + `(("title" . ,(org-roam-node-title node)))))) + (weblorg--prepend keywords (cons "backlinks" backlinks)) + (weblorg--prepend keywords (cons "tags" tags)) + (weblorg--prepend keywords (cons "atime" atime)) + (weblorg--prepend keywords (cons "mtime" mtime)) + (weblorg--prepend keywords (cons "slug" slug)) + keywords)) + org-roam-nodes)))) (defun weblorg-input-source-org-roam-nodes (&optional filter-fn sort-fn limit) "Find all org-roam file nodes. From 5048a53debac96dc9b83a6d6cdabd482f6885b77 Mon Sep 17 00:00:00 2001 From: Nan Zhong Date: Sun, 12 Dec 2021 20:23:54 -0500 Subject: [PATCH 08/10] Add header, license, and commentary for org roam extension --- weblorg-org-roam.el | 51 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/weblorg-org-roam.el b/weblorg-org-roam.el index c9f1577..9db7605 100644 --- a/weblorg-org-roam.el +++ b/weblorg-org-roam.el @@ -1,3 +1,51 @@ +;;; weblorg-org-roam.el --- Extension to adding support for org-roam to weblorg.el -*- lexical-binding: t -*- +;; +;; Author: Nan Zhong +;; URL: https://emacs.love/weblorg +;; Version: 0.1.2 +;; Package-Requires: ((emacs "27.2") (templatel "0.1.6") (weblorg "0.1.2") (org-roam "2.1.0")) +;; +;; Copyright (C) 2020-2021 Lincoln Clarete +;; +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. +;; +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . +;; +;;; Commentary: +;; +;; This extension to weblorg introduces a set of input source functions that can +;; be used with weblorg-route to generate static content from org-roam file +;; nodes. +;; +;; 1. Use-case: route for individual org roam nodes: +;; +;; (weblorg-route +;; :name "org-nodes" +;; :input-source #'weblorg-input-source-org-roam-nodes) +;; :template "org-node.html" +;; :output "output/org/{{ slug }}/index.html" +;; :url "/org/{{ slug }}/") +;; +;; 2. Use-case: route for aggregated list of org roam nodes: +;; +;; (weblorg-route +;; :name "org" +;; :input-source #'weblorg-input-source-org-roam-nodes-agg) +;; :template "org.html" +;; :output "output/org/index.html" +;; :url "/org") +;; +;;; Code: + (require 'weblorg) (require 'org-roam) @@ -10,7 +58,7 @@ "Internal state representing if org-roam db has been synced.") (defun weblorg--input-source-org-roam-nodes (&optional filter-fn sort-fn limit render-org) - "Computes an alist for each org-roam file node and return the list of alists. + "Computes returns a list of alists one for each org-roam file node. A FILTER-FN function taking a org-roam-node and returning a bool can be provided to select which nodes to return. @@ -91,3 +139,4 @@ A LIMIT integer value can be provided to limit how many results are returned." `((("nodes" . ,(weblorg--input-source-org-roam-nodes filter-fn sort-fn limit))))) (provide 'weblorg-org-roam) +;;; weblorg-org-roam.el ends here From c938cc3061eacd2fb4a34555a13a0a00238ebb10 Mon Sep 17 00:00:00 2001 From: Nan Zhong Date: Mon, 13 Dec 2021 20:42:15 -0500 Subject: [PATCH 09/10] Add example site using weblorg-org-roam --- .../.emacs/straight/versions/default.el | 15 ++++++ examples/org-roam/.gitignore | 8 +++ examples/org-roam/init.el | 51 +++++++++++++++++++ examples/org-roam/org/private.org | 7 +++ examples/org-roam/org/public_1.org | 7 +++ examples/org-roam/org/public_2.org | 9 ++++ examples/org-roam/output/index.html | 29 +++++++++++ examples/org-roam/output/public-1/index.html | 44 ++++++++++++++++ examples/org-roam/output/public-2/index.html | 37 ++++++++++++++ examples/org-roam/publish.el | 35 +++++++++++++ examples/org-roam/theme/templates/index.html | 15 ++++++ examples/org-roam/theme/templates/layout.html | 15 ++++++ .../org-roam/theme/templates/org-node.html | 31 +++++++++++ 13 files changed, 303 insertions(+) create mode 100644 examples/org-roam/.emacs/straight/versions/default.el create mode 100644 examples/org-roam/.gitignore create mode 100644 examples/org-roam/init.el create mode 100644 examples/org-roam/org/private.org create mode 100644 examples/org-roam/org/public_1.org create mode 100644 examples/org-roam/org/public_2.org create mode 100644 examples/org-roam/output/index.html create mode 100644 examples/org-roam/output/public-1/index.html create mode 100644 examples/org-roam/output/public-2/index.html create mode 100644 examples/org-roam/publish.el create mode 100644 examples/org-roam/theme/templates/index.html create mode 100644 examples/org-roam/theme/templates/layout.html create mode 100644 examples/org-roam/theme/templates/org-node.html diff --git a/examples/org-roam/.emacs/straight/versions/default.el b/examples/org-roam/.emacs/straight/versions/default.el new file mode 100644 index 0000000..e69cfbe --- /dev/null +++ b/examples/org-roam/.emacs/straight/versions/default.el @@ -0,0 +1,15 @@ +(("dash.el" . "da167c51e9fd167a48d06c7c0ee8e3ac7abd9718") + ("el-get" . "960f3fb962c35d3196bab20b2a3f6d6228119277") + ("emacsmirror-mirror" . "482c86b64ed225212475c7e65102b2c860a949cf") + ("emacsql" . "9dca5996168c4963eb67e61c7f17fdcb8228e314") + ("f.el" . "50af874cd19042f17c8686813d52569b1025c76a") + ("gnu-elpa-mirror" . "98cfebcc05fd018121d943916e9d86189125e3d2") + ("magit" . "1eb183e7672bf25fa77ea06d97b3d9c502a698ae") + ("melpa" . "e75a21c91d8aa1a07ba274b56fe8cf96119f22a4") + ("org" . "8908fba11334190c1a344159273320c5b2358b9e") + ("org-roam" . "7068d63e966c0ca8d098ac4f7a90434f4c9b6822") + ("s.el" . "08661efb075d1c6b4fa812184c1e5e90c08795a9") + ("straight.el" . "af5437f2afd00936c883124d6d3098721c2d306c") + ("templatel" . "b52349948b6927f7a5da4e54a89e01c794f2095a") + ("use-package" . "a7422fb8ab1baee19adb2717b5b47b9c3812a84c")) +:beta diff --git a/examples/org-roam/.gitignore b/examples/org-roam/.gitignore new file mode 100644 index 0000000..a15fdb4 --- /dev/null +++ b/examples/org-roam/.gitignore @@ -0,0 +1,8 @@ +org-id-locations +org-roam.db +.emacs/* +!.emacs/straight +.emacs/straight/* +!.emacs/straight/versions +.emacs/straight/versions/* +!.emacs/straight/versions/default.el diff --git a/examples/org-roam/init.el b/examples/org-roam/init.el new file mode 100644 index 0000000..6861bce --- /dev/null +++ b/examples/org-roam/init.el @@ -0,0 +1,51 @@ +;;; init.el --- Initialize emacs environment +;;; Commentary: +;; +;; Installation and configuration and dependent packages. +;; +;;; Code: + +(setq user-emacs-directory (expand-file-name ".emacs")) + +;; Boostrap straight.el +(defvar bootstrap-version) +(let ((bootstrap-file + (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) + (bootstrap-version 5)) + (unless (file-exists-p bootstrap-file) + (with-current-buffer + (url-retrieve-synchronously + "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" + 'silent 'inhibit-cookies) + (goto-char (point-max)) + (eval-print-last-sexp))) + (load bootstrap-file nil 'nomessage)) + +;; Bootstrap use-package. +(straight-use-package 'use-package) + +(use-package org + :straight t + :config + ;; This is not necessary and is only used for this example to avoid polluting + ;; the local user's id locations file with the org files from this example. + (setq org-id-locations-file (expand-file-name "./org-id-locations"))) +(use-package org-roam + :straight t + :after org + :init + (setq org-roam-v2-ack t) + :config + (setq org-roam-directory (expand-file-name "./org") + org-roam-file-extensions '("org") + ;; This is not necessary and is only used for this example to avoid + ;; polluting the local user's org-roam db with the org files from this + ;; example. + org-roam-db-location (expand-file-name "./org-roam.db"))) + +(use-package templatel :straight t) + +(add-to-list 'load-path "../../") +(require 'weblorg) +(require 'weblorg-org-roam) +;;; init.el ends here diff --git a/examples/org-roam/org/private.org b/examples/org-roam/org/private.org new file mode 100644 index 0000000..641b9a0 --- /dev/null +++ b/examples/org-roam/org/private.org @@ -0,0 +1,7 @@ +:PROPERTIES: +:ID: 8de1fa5e-7fed-4893-b7d1-0a2ca1c49840 +:END: +#+title: Private +#+filetags: :private: + +This is a private note that should not be published. diff --git a/examples/org-roam/org/public_1.org b/examples/org-roam/org/public_1.org new file mode 100644 index 0000000..babde5c --- /dev/null +++ b/examples/org-roam/org/public_1.org @@ -0,0 +1,7 @@ +:PROPERTIES: +:ID: ff7ef0a2-8988-4634-b7fa-40664c3da8e4 +:END: +#+title: Public 1 +#+filetags: :public: + +This is public note #1. diff --git a/examples/org-roam/org/public_2.org b/examples/org-roam/org/public_2.org new file mode 100644 index 0000000..def24a5 --- /dev/null +++ b/examples/org-roam/org/public_2.org @@ -0,0 +1,9 @@ +:PROPERTIES: +:ID: a850226a-01fc-4b14-abdd-525e93906090 +:END: +#+title: Public 2 +#+filetags: :public: + +This is public note #2. + +Link to [[id:ff7ef0a2-8988-4634-b7fa-40664c3da8e4][Public 1]]. diff --git a/examples/org-roam/output/index.html b/examples/org-roam/output/index.html new file mode 100644 index 0000000..b627f0b --- /dev/null +++ b/examples/org-roam/output/index.html @@ -0,0 +1,29 @@ + + + + + + + Example org-roam site + + + + +
+ + +

Org Notes

+ + +
+ diff --git a/examples/org-roam/output/public-1/index.html b/examples/org-roam/output/public-1/index.html new file mode 100644 index 0000000..d888ce8 --- /dev/null +++ b/examples/org-roam/output/public-1/index.html @@ -0,0 +1,44 @@ + + + + + + + Public 1 + + + + +
+ +

Public 1

+
+ 2021-12-12 +
    + +
  • public
  • + +
+
+ +

+This is public note #1. +

+ + + + + + + +
+ diff --git a/examples/org-roam/output/public-2/index.html b/examples/org-roam/output/public-2/index.html new file mode 100644 index 0000000..7d8fd34 --- /dev/null +++ b/examples/org-roam/output/public-2/index.html @@ -0,0 +1,37 @@ + + + + + + + Public 2 + + + + +
+ +

Public 2

+
+ 2021-12-12 +
    + +
  • public
  • + +
+
+ +

+This is public note #2. +

+ +

+Link to Public 1. +

+ + + + + +
+ diff --git a/examples/org-roam/publish.el b/examples/org-roam/publish.el new file mode 100644 index 0000000..e77f7df --- /dev/null +++ b/examples/org-roam/publish.el @@ -0,0 +1,35 @@ +;;; publish.el --- Export org roam file nodes +;;; Commentary: +;; +;; Example configuration for generating a static site from org-roam file nodes. +;; +;;; Code: + +(add-to-list 'load-path ".") +(load "init") + +(org-roam-db-sync) + +(let* ((site (weblorg-site + :default-route "org-nodes" + :theme nil)) + (org-roam-nodes-filter (lambda (node) + (member "public" (org-roam-node-tags node))))) + (weblorg-route + :name "index" + :input-source (lambda () (weblorg-input-source-org-roam-nodes-agg org-roam-nodes-filter)) + :template "index.html" + :output "output/index.html" + :url "/" + :site site) + (weblorg-route + :name "org-nodes" + :input-source (lambda () (weblorg-input-source-org-roam-nodes org-roam-nodes-filter)) + :template "org-node.html" + :output "output/{{ slug }}/index.html" + :url "/{{ slug }}/" + :site site) + + (setq debug-on-error t) + (weblorg-export)) +;;; publish.el ends here diff --git a/examples/org-roam/theme/templates/index.html b/examples/org-roam/theme/templates/index.html new file mode 100644 index 0000000..9c27959 --- /dev/null +++ b/examples/org-roam/theme/templates/index.html @@ -0,0 +1,15 @@ +{% extends "layout.html" %} + +{% block title %}Example org-roam site{% endblock %} + +{% block main %} + +

Org Notes

+
    + {% for node in nodes %} +
  • + [{{ node.mtime | strftime("%Y-%m-%d") }}] {{ node.title }} +
  • + {% endfor %} +
+{% endblock %} diff --git a/examples/org-roam/theme/templates/layout.html b/examples/org-roam/theme/templates/layout.html new file mode 100644 index 0000000..4ca241e --- /dev/null +++ b/examples/org-roam/theme/templates/layout.html @@ -0,0 +1,15 @@ + + + {% block head %} + + + + {% block title %}{{ title }}{% endblock %} + + {% endblock %} + + +
+ {% block main %}{% endblock %} +
+ diff --git a/examples/org-roam/theme/templates/org-node.html b/examples/org-roam/theme/templates/org-node.html new file mode 100644 index 0000000..5d4c542 --- /dev/null +++ b/examples/org-roam/theme/templates/org-node.html @@ -0,0 +1,31 @@ +{% extends "layout.html" %} + +{% block title %}{{ node.title }}{% endblock %} + +{% block main %} +

{{ node.title }}

+
+ {{ node.mtime | strftime("%Y-%m-%d") }} +
    + {% for tag in node.tags %} +
  • {{ tag }}
  • + {% endfor %} +
+
+ +{{ node.html | safe }} + +{% if node.backlinks %} + +{% endif %} + +{% endblock %} From 09d9c3d7276f8bba703f2cc1497c304471ba13f8 Mon Sep 17 00:00:00 2001 From: Nan Zhong Date: Thu, 28 Dec 2023 15:50:07 -0500 Subject: [PATCH 10/10] fix: undefer org headline nodes for org 9.7 --- weblorg.el | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/weblorg.el b/weblorg.el index f1ecf73..0a6c1d7 100644 --- a/weblorg.el +++ b/weblorg.el @@ -1039,6 +1039,12 @@ can be found in the ROUTE." (weblorg--prepend keywords (cons "file_slug" file-slug)) keywords)) +(defun weblorg--org-element-properties-resolve (node) + (if (version<= "9.7-pre" org-version) + (with-no-warnings + (org-element-properties-resolve node t)) + node)) + (defun weblorg--parse-org (input-data &optional input-path) "Parse INPUT-DATA as an Org-Mode file & generate its HTML. @@ -1067,6 +1073,7 @@ an INPUT-PATH to resolve relative links and INCLUDES from." (lambda(fn headline contents info) ;; Don't override existing value, so users can still put ;; whatever they want + (setf headline (weblorg--org-element-properties-resolve headline)) (unless (org-element-property :CUSTOM_ID headline) (let ((headline-slug (weblorg--slugify (org-element-property :raw-value headline)))) (org-element-put-property headline :CUSTOM_ID headline-slug))) @@ -1100,8 +1107,9 @@ template filter to display a nicely formatted string. If it's a filetag field, it will return a collection of strings. The user will have to iterate over the collection to get all keywords." - (let ((key (downcase (org-element-property :key keyword))) - (value (org-element-property :value keyword))) + (let* ((keyword (weblorg--org-element-properties-resolve keyword)) + (key (downcase (org-element-property :key keyword))) + (value (org-element-property :value keyword))) (cons key (cond ((string= key "date")