Is there anything like backlinks?
michaelsjackson opened this issue · 1 comments
Backlinks known from wikis available in org-wiki?
Not in org-wiki
itself, but, if it helps, I implemented them in the following (admittedly a bit convoluted) way.
I generate my wiki using make
. I keep a more-or-less separate wiki.el
file containing the elisp I use to export my wiki. Basically, I override org-wiki--org-link
to:
- check that the file the current link points at exist (
dest
)1; - and if so, put the name of the file being exported in
_cache/dest.backlinks
After the export is done, I end up with a bunch of files in _cache
. Each of them is named after an existing wiki page, and they contain a list of all the filenames that point to them, one per line.
At that point, my Makefile
calls a make_backlinks
script (and a few others, to spit out a list of files with update date, and some quick-and-dirty stats about the wiki itself), which goes over all those backlinks files, ensures that there are no duplicates, and which calls ed
with a script to edit the HTML to add a backlinks section.
Another possible approach would be to write an elisp function to go over all the pages in your wiki, search for [[wiki:]]
strings, extract the string and put the backlinks in your org-file itself.
Override the link exporter
;; Override `org-wiki--org-link' to add backlinks support
;; The backlinks themselves will have to be added to the relevant files
;; in a second pass, implemented here with a shell & ed script.
(defun org-wiki--org-link (path desc backend)
"Creates an html org-wiki page link when exporting to HTML,
if and only if the linked page already exists.
Example: the hyperlink [[wiki:Linux][Dealing with Linux]]
will be exported to <a href='Linux.html'>Dealing with Linux</a>
if Linux.org exists, or <span class='missing link'>Dealing with
Linux</span> if it does not."
(cl-case backend
(html
(if (file-exists-p (org-wiki--page->file path))
;; If the destination file exists, then write out the path to
;; the current file to its backlinks file.
(let* ((filename (concat "_cache/"
(replace-regexp-in-string "/" "!" path)
".backlinks"))
(filepath (org-wiki--page->file path))
(folder (file-name-directory filepath)))
(if (file-directory-p folder)
(write-region (concat current-file "\n") nil
filename 'append))
(format "<a href='%s.html'>%s</a>"
path (or desc path)))
;; Otherwise, wrap the `desc' of the link in a <span> with
;; the missing class and add the destination to missing
(progn
(write-region (concat path "\n") nil "_cache/missing" 'append)
(format "<span class='missing link'>%s</span>"
(or desc path)))))))
;; Advice `org-html-publish-to-html' to save the bare name - no
;; path, no extension - of the file it is invoked on, so that
;; `org-wiki--org-link' can know which page is linking to which
;; when outputting backlinks.
(advice-add #'org-html-publish-to-html :before
(lambda (plist filename pub-dir)
(setq current-file (file-name-base filename))))
make_backlinks
#!/bin/sh
for filename in _cache/*.backlinks; do
# Remove duplicates and sort by name
sort --unique "$filename" | sponge "$filename"
# Strip the leading "_cache/" and the extension
target="${filename/!//}"
target="${target#_cache/}"
target="${target%.backlinks}"
# Check that we haven't already added the backlinks
if grep -q "_out/${target}.html" -e '<div id="backlinks">'; then
continue
fi
# Add the backlinks to the HTML file
ed -s "_out/${target}.html" >/dev/null <<EOF
# Find the closing main
g/<\\/main>/
# Split it out to its own line
s/<\\/main>/\\
<\\/main>/
# TODO: redundant, could do it with main
# Find the bottom of the file
g/<footer id="postamble"/
# Scroll up a bit so we are inside the main section
-1
# Open the backlinks section
i
<div id="backlinks">
<h2>Backlinks</h2>
.
# Gobble up the results of a sed turning the names into links
.r !sed 's|^.*$|<a href="&.html">&</a>|g' "$filename"
# Close the backlinks section
a
</div>
.
# We are done. Bye!
wq
EOF
done
Dynamic face for wiki:
links
;; Defines a dynamic face for org-wiki links, inheriting from
;; `org-link' but making the text red when the linked page does
;; not exist.
(defun org-wiki--dynamic-face (path)
(let ((org-wiki-file (org-wiki--page->file path)))
(if (not (file-exists-p org-wiki-file))
'(:inherit org-link :foreground "red")
'org-link)))
(org-link-set-parameters "wiki" :face #'org-wiki--dynamic-face)
Footnotes
-
I also use this to make
wiki:
links turn red when pointing at a non-existent page, see the last section. ↩