Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there anything like backlinks? #25

Open
michaelsjackson opened this issue Mar 2, 2019 · 1 comment
Open

Is there anything like backlinks? #25

michaelsjackson opened this issue Mar 2, 2019 · 1 comment

Comments

@michaelsjackson
Copy link

Backlinks known from wikis available in org-wiki?

@mftrhu
Copy link

mftrhu commented Mar 13, 2019

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:

  1. check that the file the current link points at exist (dest)1;
  2. 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

  1. I also use this to make wiki: links turn red when pointing at a non-existent page, see the last section.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants