Thanks! Didn’t even think about using org headings for that.
I have try your snippet code to export org files, but it still has rough edges:
An org file named Foo
:
#+title: Foo
It contains [[id:bar][Bar]]
Another org file named Bar
:
#+title: Bar
* Outline 1
xxxxxxxxxxx
* Outline 2
yyyyyyyyyy[fn:1]
* Footnotes
[fn:1] This is footnote 1.
Then open file Foo
, and call interactive command org-publish-current-file
, then the publish process will be interrupted by error:
Definition not found for footnote 1
I presume the command org-roam-node-file
or org-roam-node-preview
in your snippet can not find the footnote 1, so the emacs throw an error.
I am not so sure, because I am not so familiar with org-roam source code, don’t konw how to fix it.
Maybe there are some remedies to this problem?
After some effort, I remedy this rough edge.
(defun hurricane//collect-backlinks-string (backend)
(when (org-roam-node-at-point)
(let* ((source-node (org-roam-node-at-point))
(source-file (org-roam-node-file source-node))
(nodes-in-file (--filter (s-equals? (org-roam-node-file it) source-file)
(org-roam-node-list)))
(nodes-start-position (-map 'org-roam-node-point nodes-in-file))
;; Nodes don't store the last position, so get the next headline position
;; and subtract one character (or, if no next headline, get point-max)
(nodes-end-position (-map (lambda (nodes-start-position)
(goto-char nodes-start-position)
(if (org-before-first-heading-p) ;; file node
(point-max)
(call-interactively
'org-forward-heading-same-level)
(if (> (point) nodes-start-position)
(- (point) 1) ;; successfully found next
(point-max)))) ;; there was no next
nodes-start-position))
;; sort in order of decreasing end position
(nodes-in-file-sorted (->> (-zip nodes-in-file nodes-end-position)
(--sort (> (cdr it) (cdr other))))))
(dolist (node-and-end nodes-in-file-sorted)
(-when-let* (((node . end-position) node-and-end)
(backlinks (--filter (->> (org-roam-backlink-source-node it)
(org-roam-node-file)
(s-contains? "private/") (not))
(org-roam-backlinks-get node)))
(heading (format "\n\n%s Links to this node\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
(properties-drawer ":PROPERTIES:\n:HTML_CONTAINER_CLASS: references\n:END:\n"))
(goto-char end-position)
(insert heading)
(insert properties-drawer)
(dolist (backlink backlinks)
(let* ((source-node (org-roam-backlink-source-node backlink))
(source-file (org-roam-node-file source-node))
(properties (org-roam-backlink-properties backlink))
(outline (when-let ((outline (plist-get properties :outline)))
(when (> (length outline) 1)
(mapconcat #'org-link-display-format outline " > "))))
(point (org-roam-backlink-point backlink))
(text (s-replace "\n" " " (org-roam-preview-get-contents
source-file
point)))
(reference (format "%s [[id:%s][%s]]\n%s\n%s\n\n"
(s-repeat (+ (org-roam-node-level node) 2) "*")
(org-roam-node-id source-node)
(org-roam-node-title source-node)
(if outline (format "%s (/%s/)"
(s-repeat (+ (org-roam-node-level node) 3) "*") outline) "")
text))
(label-list (with-temp-buffer
(insert text)
(org-element-map (org-element-parse-buffer) 'footnote-reference
(lambda (reference)
(org-element-property :label reference)))))
(footnote-string-list
(with-temp-buffer
(insert-file-contents source-file)
(-map (lambda (label) (buffer-substring-no-properties
(nth 1 (org-footnote-get-definition label))
(nth 2 (org-footnote-get-definition label))))
label-list))))
(-map (lambda (footnote-string) (insert footnote-string)) footnote-string-list)
(insert reference))))))))
(add-hook 'org-export-before-processing-hook 'hurricane//collect-backlinks-string)
How would I modify this code so that ONLY the private
tag is linked and exported. My lisp is so poor. Thanks!
(dolist (node-and-end nodes-in-file-sorted)
(-when-let* (((node . end-position) node-and-end)
(backlinks (--filter (->> (org-roam-backlink-source-node it)
(org-roam-node-file)
(s-contains? "private/") (not))
(org-roam-backlinks-get node)))
(heading (format "\n\n%s Links to this node\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
(properties-drawer ":PROPERTIES:\n:HTML_CONTAINER_CLASS: references\n:END:\n"))
If node has aliases, there will be as many “links to this node” as aliases are.
It seems like most of the methods here won’t work on --batch
mode. See this other topic.
It might be caused by the interactive
calls. Does anyone here have any idea on how to fix this?
Are you using V2 of Org-roam? The other post has this:
(org-roam-sql [:select [file-from] :from file-links :where (= file-to $s1)] file))
If you are on V2, org-roam-sql
does not exist, and the table named file-links
does not, either. Refer to this section of the manual, where this example is mentioned:
(org-roam-db-query [:select * :from nodes])
For correct table and field names, see C-h v
of variable org-roam-db--table-schemata
. I tink you are going for table links
for field dest
.
I’m able to run the functions above if I’m already inside Emacs. However I’m unable to generate the backlinks when I try to run the script in --batch
mode.
I’m trying to run the following code with the command emacs -q --batch -l publish.el
and it does publish my website but it doesn’t generate the backlinks below each note.
(require 'ox-publish)
(require 'org-roam)
(setq org-roam-directory "."
org-id-locations-file ".orgids"
org-id-link-to-org-use-id t
org-id-extra-files (org-roam-list-files)
org-id-track-globally t
org-export-with-broken-links 'mark
;; HTML
org-html-validation-link nil
org-html-home/up-format "<!-- %s --><nav><a href=\"%s\">Index</a></nav>"
org-html-link-home "index.html"
org-html-head-include-scripts nil
org-html-head-include-default-style nil
org-html-head "<link rel=\"stylesheet\" href=\"assets/bamboo.css\">"
publish-directory "html")
(defun collect-backlinks-string (backend)
(when (org-roam-node-at-point)
(let* ((source-node (org-roam-node-at-point))
(source-file (org-roam-node-file source-node))
(nodes-in-file (--filter (s-equals? (org-roam-node-file it) source-file)
(org-roam-node-list)))
(nodes-start-position (-map 'org-roam-node-point nodes-in-file))
;; Nodes don't store the last position, so get the next headline position
;; and subtract one character (or, if no next headline, get point-max)
(nodes-end-position (-map (lambda (nodes-start-position)
(goto-char nodes-start-position)
(if (org-before-first-heading-p) ;; file node
(point-max)
('org-forward-heading-same-level)
(if (> (point) nodes-start-position)
(- (point) 1) ;; successfully found next
(point-max)))) ;; there was no next
nodes-start-position))
;; sort in order of decreasing end position
(nodes-in-file-sorted (->> (-zip nodes-in-file nodes-end-position)
(--sort (> (cdr it) (cdr other))))))
(dolist (node-and-end nodes-in-file-sorted)
(-when-let* (((node . end-position) node-and-end)
(backlinks (--filter (->> (org-roam-backlink-source-node it)
(org-roam-node-file)
(s-contains? "private/") (not))
(org-roam-backlinks-get node)))
(heading (format "\n\n%s Backlinks\n"
(s-repeat (+ (org-roam-node-level node) 1) "*")))
(properties-drawer ":PROPERTIES:\n:HTML_CONTAINER_CLASS: references\n:END:\n"))
(goto-char end-position)
(insert heading)
(insert properties-drawer)
(dolist (backlink backlinks)
(let* ((source-node (org-roam-backlink-source-node backlink))
(source-file (org-roam-node-file source-node))
(properties (org-roam-backlink-properties backlink))
(outline (when-let ((outline (plist-get properties :outline)))
(when (> (length outline) 1)
(mapconcat #'org-link-display-format outline " > "))))
(point (org-roam-backlink-point backlink))
(text (org-roam-preview-get-contents
source-file
point))
(reference (format "\n ----- \n%s [[id:%s][%s]]\n%s\n%s\n\n"
(s-repeat (+ (org-roam-node-level node) 2) "*")
(org-roam-node-id source-node)
(org-roam-node-title source-node)
(if outline (format "%s (/%s/)"
(s-repeat (+ (org-roam-node-level node) 3) "*") outline) "")
text))
(label-list (with-temp-buffer
(insert text)
(org-element-map (org-element-parse-buffer) 'footnote-reference
(lambda (reference)
(org-element-property :label reference)))))
(footnote-string-list
(with-temp-buffer
(insert-file-contents source-file)
(-map (lambda (label) (buffer-substring-no-properties
(nth 1 (org-footnote-get-definition label))
(nth 2 (org-footnote-get-definition label))))
label-list))))
(-map (lambda (footnote-string) (insert footnote-string)) footnote-string-list)
(insert reference))))))))
(add-hook 'org-export-before-processing-hook 'collect-backlinks-string)
(setq org-publish-project-alist
(list
(list "org-site:main"
:recursive nil
:exclude ".*gitignore|.*/private/.*" ;; remove private directory
:base-directory org-roam-directory
:publishing-function 'org-html-publish-to-html
:publishing-directory publish-directory
:with-date t
:with-author nil ;; Don't include author name
:with-creator t ;; Include Emacs and Org versions in footer
:with-toc t ;; Include a table of contents
:section-numbers nil ;; Don't include section numbers
:time-stamp-file nil
:auto-sitemap t
:sitemap-filename "index.org"
:sitemap-sort-files 'alphabetically
:sitemap-ignore-case t
:sitemap-title "personal digital garden")
(list "org-site:static"
:base-directory org-roam-directory
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
:recursive t
:publishing-directory publish-directory
:publishing-function 'org-publish-attachment)))
(org-publish-all t)
The —batch option implies -q (and you explicitly adds it in the command line). I’m guessing you would need to explicitly add load-path for org-roam before you require it.
I do have org-roam
on my load-path
. The problem is that the function needs something interactively so I can’t run it on --batch
I guess.
Thanks for the fast replies, though!
You may well be right about interactive but org-publish is also an interactive function isn’t it? (but you can run it in the batch mode).
I don’t see load-path in your command in terminal or in the script… init.el (or .emacs) won’t be called — I guess you know that.
I also had this issue. Here’s how I fixed it:
TLDR: Check if org-roam-db-location
is correct.
I figured out the variables nodes-in-file-sorted
was nil so (do list
never entered. From there, I discovered (org-roam-at-point)
was returning little information: #s(org-roam-node nil nil nil nil nil 6a6d5036-8acd-46b3-4550-9e737271d8c0 nil 1 nil nil nil nil nil nil nil nil nil nil)
The function needs more data to work.
If you see the documentation of (org-roam-at-point)
,
This function also returns the node if it has yet to be cached in the
database. In this scenario, only expect :id and :point to be
populated.
This is exactly what was happening. I discovered org-roam-db-location
was pointing to the wrong location, because batch mode doesn’t come with my usual configuration.