main
  1<!DOCTYPE html>
  2<html lang="en">
  3<head>
  4<!-- Oct 07, 2022 -->
  5<meta charset="utf-8" />
  6<meta name="viewport" content="width=device-width, initial-scale=1" />
  7<title>meta: publishing this website</title>
  8<meta name="author" content="Vincent Demeester" />
  9<meta name="generator" content="Org Mode" />
 10<link rel='icon' type='image/x-icon' href='/images/favicon.ico'/>
 11<meta name='viewport' content='width=device-width, initial-scale=1'>
 12<link rel='stylesheet' href='/css/new.css' type='text/css'/>
 13<link rel='stylesheet' href='/css/syntax.css' type='text/css'/>
 14<link href='/index.xml' rel='alternate' type='application/rss+xml' title='Vincent Demeester' />
 15</head>
 16<body>
 17<main id="content" class="content">
 18<header>
 19<h1 class="title">meta: publishing this website</h1>
 20</header><p>
 21This is a <b>always up-to-date</b> version of my initial <a href="https://vincent.demeester.fr/posts/2020-03-22-org-mode-website.html">post</a> about publishing this website
 22using <a href="org_mode.html">org-mode</a>. This uses the <a href="https://orgmode.org/manual/Extracting-Source-Code.html">tangle</a> feature of <code>org-mode</code> and will span from the
 23<code>Makefile</code> of the <a href="https://git.sr.ht/~vdemeester/www">repository</a> to any <a href="elisp.html">emacs-lisp</a> code required.
 24</p>
 25
 26<p>
 27This is part of the <a href="meta_meta.html">Meta</a> entry.
 28</p>
 29
 30<nav id="table-of-contents" role="doc-toc">
 31<h2>Table of Contents</h2>
 32<div id="text-table-of-contents" role="doc-toc">
 33<ul>
 34<li><a href="#Parts%20of%20the%20website">Parts of the website</a></li>
 35<li><a href="#Publishing">Publishing</a>
 36<ul>
 37<li><a href="#%2FMake%2F%20it%20happen"><i>Make</i> it happen</a>
 38<ul>
 39<li><a href="#Building%20%3Dpublic%2F%3D%20and%20publishing%20it">Building <code>public/</code> and publishing it</a></li>
 40<li><a href="#Local%20server">Local server</a></li>
 41<li><a href="#Final%20nits%20of%20the%20%3DMakefile%3D">Final nits of the <code>Makefile</code></a></li>
 42</ul>
 43</li>
 44<li><a href="#The%20%2Fpublish%2F%20scrits">The <i>publish</i> scrits</a>
 45<ul>
 46<li><a href="#%3Dpublish.el%3D"><code>publish.el</code></a></li>
 47<li><a href="#%3Dpublish-common.el%3D"><code>publish-common.el</code></a></li>
 48</ul>
 49</li>
 50</ul>
 51</li>
 52</ul>
 53</div>
 54</nav>
 55
 56<section id="outline-container-Parts%20of%20the%20website" class="outline-2">
 57<h2 id="Parts%20of%20the%20website">Parts of the website</h2>
 58<div class="outline-text-2" id="text-Parts%20of%20the%20website">
 59<p>
 60Let&rsquo;s look at the different part of the website and for each where I do get the
 61information I want to publish.
 62</p>
 63
 64<dl class="org-dl">
 65<dt><code>/</code></dt><dd>this is just a file (<code>index.org</code>) that I maintain manually.</dd>
 66<dt><code>/posts</code></dt><dd>this is what we call <i>blog</i> these days : short to medium article that are valid
 67at a point of time, as may contain <i>deprecated</i> content, or content that do not reflect
 68my views at a later point in time.</dd>
 69<dt><code>/articles</code></dt><dd>medium to long article about a topic. Those should be up-to-date or
 70explicitly mark as deprecated or invalid. This is my <b>ready for the public</b> knowledge
 71database, a bit like <a href="https://braindump.jethro.dev/">Jethro&rsquo;s Braindump</a>. It is managed using <a href="org_roam.html">org-roam</a> and I just need to
 72get the latests somewhere to publish it.</dd>
 73<dt><code>/configurations</code></dt><dd>medium to long article about my configurations. Those are base
 74sources for my <code>home</code> <i>configuration</i> mono-repository, and usually follow literate
 75programming principles. The are managed using <a href="org_roam.html">org-roam</a> with the <code>config:</code> prefix.</dd>
 76<dt><code>/files</code></dt><dd>a dump of random files, it is actually on another domain name, completely
 77unmanaged by this.</dd>
 78<dt><code>/about</code></dt><dd>an about page about the author of the website (aka <a href="https://vincent.demeester.fr">me</a>), linking external
 79contributions (GitHub/Gitlab/… profiles, Talks, …).</dd>
 80</dl>
 81
 82<p>
 83In a nutshell, the folder hierarchy is something like :
 84</p>
 85
 86<div class="org-src-container">
 87<pre class="src src-shell">src/www
 88|-- about           <span class="org-comment-delimiter"># </span><span class="org-comment">the about folder (with one index.org)</span>
 89|-- articles        <span class="org-comment-delimiter"># </span><span class="org-comment">&lt;- comes from ~/desktop/org/notes~</span>
 90|-- css             <span class="org-comment-delimiter"># </span><span class="org-comment">the css</span>
 91|-- images          <span class="org-comment-delimiter"># </span><span class="org-comment">the images</span>
 92|-- index.org       <span class="org-comment-delimiter"># </span><span class="org-comment">the index &#127881;</span>
 93|-- posts           <span class="org-comment-delimiter"># </span><span class="org-comment">the posts (format YYYY-MM-DD-{title}.org)</span>
 94<span class="org-sh-quoted-exec">`-- public          # the output that get deployed later on</span>
 95</pre>
 96</div>
 97</div>
 98</section>
 99
100<section id="outline-container-Publishing" class="outline-2">
101<h2 id="Publishing">Publishing</h2>
102<div class="outline-text-2" id="text-Publishing">
103</div>
104<div id="outline-container-%2FMake%2F%20it%20happen" class="outline-3">
105<h3 id="%2FMake%2F%20it%20happen"><i>Make</i> it happen</h3>
106<div class="outline-text-3" id="text-%2FMake%2F%20it%20happen">
107<p>
108In order to publish this website, I am using <a href="make.html">make</a>. In a nutshell, I am going to define a
109few target to get the content from my notes, export org files into html and copy more or
110less everything to the <code>public</code> folder. I will also define a clean and a publish target.
111</p>
112
113<p>
114The first part of my <code>Makefile</code> will be to define some constants that I want to use later
115on. Those are mainly to easily change where to look for the notes or where the emacs
116configuration is.
117</p>
118
119<div class="org-src-container">
120<pre class="src src-makefile"><span class="org-variable-name">EMACS</span> =
121ifndef EMACS
122<span class="org-variable-name">EMACS</span> = <span class="org-string">"emacs"</span>
123endif
124
125<span class="org-variable-name">DOTEMACS</span> =
126ifndef DOTEMACS
127<span class="org-variable-name">DOTEMACS</span> = <span class="org-string">"~/.config/emacs"</span>
128endif
129
130<span class="org-variable-name">PUBLISH</span> =
131ifndef PUBLISH
132<span class="org-variable-name">PUBLISH</span> = vincent.demeester.fr
133endif
134
135<span class="org-variable-name">NOTES</span> = ~/desktop/org/notes
136</pre>
137</div>
138
139<p>
140The default target will be name <code>build</code>.
141</p>
142
143<div class="org-src-container">
144<pre class="src src-makefile"><span class="org-makefile-targets">all</span>: build
145</pre>
146</div>
147</div>
148
149<div id="outline-container-Building%20%3Dpublic%2F%3D%20and%20publishing%20it" class="outline-4">
150<h4 id="Building%20%3Dpublic%2F%3D%20and%20publishing%20it">Building <code>public/</code> and publishing it</h4>
151<div class="outline-text-4" id="text-Building%20%3Dpublic%2F%3D%20and%20publishing%20it">
152<p>
153To build the website, we will be using <a href="emacs.html">Emacs</a> in batch mode, with some shared library <b>and</b>
154the actual <a href="#The%20%2Fpublish%2F%20scrits">publish</a> script.
155</p>
156
157<div class="org-src-container">
158<pre class="src src-makefile"><span class="org-makefile-targets">.PHONY</span>: build
159<span class="org-makefile-targets">build</span>: publish.el publish-common.el build-articles
160        <span class="org-type">@</span><span class="org-makefile-shell">echo </span><span class="org-string"><span class="org-makefile-shell">"Publishing... with current Emacs configurations."</span></span>
161        ${<span class="org-variable-name">EMACS</span>} --debug-init --batch --directory $(<span class="org-variable-name">DOTEMACS</span>)/lisp/ --directory $(<span class="org-variable-name">DOTEMACS</span>)/lisp/vorg/ \
162                --load publish-common.el --load publish.el \
163                --funcall org-publish-all
164
165<span class="org-makefile-targets">.PHONY</span>: build-articles
166<span class="org-makefile-targets">build-articles</span>: $(<span class="org-variable-name">NOTES</span>)
167        rsync -arv --delete --copy-links --exclude=<span class="org-string">'*.private.org'</span> --exclude=<span class="org-string">'*.db'</span> $(<span class="org-variable-name">NOTES</span>)/ articles/
168
169<span class="org-makefile-targets">$(</span><span class="org-variable-name"><span class="org-makefile-targets">NOTES</span></span><span class="org-makefile-targets">)</span>:
170        $(<span class="org-variable-name">error</span> $(<span class="org-variable-name">NOTES</span>) doesn<span class="org-string">'t exists&#8230;)</span>
171</pre>
172</div>
173
174<p>
175The <code>publish</code> target is gonna be really simple: I just need to copy the content to
176<code>~/desktop/sites</code> on the current machine, and the rest is automated.
177</p>
178
179<div class="org-src-container">
180<pre class="src src-makefile"><span class="org-comment-delimiter">#</span><span class="org-comment">rsync -a --progress --copy-links --delete public/assets/.fancyindex/ ~/desktop/sites/dl.sbr.pm/.fancyindex/</span>
181<span class="org-comment-delimiter">#</span><span class="org-comment">rsync -a --progress --copy-links --delete public/ ~/desktop/sites/${</span><span class="org-comment"><span class="org-variable-name">PUBLISH</span></span><span class="org-comment">}/</span>
182<span class="org-makefile-targets">.PHONY</span>: publish
183<span class="org-makefile-targets">publish</span>: build
184        <span class="org-makefile-targets">rsync -ave ssh --progress --copy-links --delete public/assets/.fancyindex/ kerkouane.vpn</span>:/var/www/dl.sbr.pm/.fancyindex/
185        <span class="org-makefile-targets">rsync -ave ssh --progress --copy-links --delete public/ kerkouane.vpn</span>:/var/www/${<span class="org-variable-name">PUBLISH</span>}/
186</pre>
187</div>
188</div>
189</div>
190
191<div id="outline-container-Local%20server" class="outline-4">
192<h4 id="Local%20server">Local server</h4>
193<div class="outline-text-4" id="text-Local%20server">
194<p>
195Let&rsquo;s use <code>miniserve</code> (using <a href="nix.html">Nix</a> with <code>nix-shell</code>) to serve the static website locally to
196validate my changes.
197</p>
198
199<div class="org-src-container">
200<pre class="src src-makefile"><span class="org-makefile-targets">.PHONY</span>: serve
201<span class="org-makefile-targets">serve</span>:
202        nix-shell -p miniserve --command <span class="org-string">"miniserve --port=8181 --index=index.html public/"</span>
203</pre>
204</div>
205</div>
206</div>
207
208<div id="outline-container-Final%20nits%20of%20the%20%3DMakefile%3D" class="outline-4">
209<h4 id="Final%20nits%20of%20the%20%3DMakefile%3D">Final nits of the <code>Makefile</code></h4>
210<div class="outline-text-4" id="text-Final%20nits%20of%20the%20%3DMakefile%3D">
211<p>
212One of the final step is to install the git hooks if any. I tend to have this target in all my
213personal <code>Makefile</code> at least. Let&rsquo;s also define a <code>pre-commit</code> target that will hold
214anything we need to do at <code>pre-commit</code>.
215</p>
216
217<div class="org-src-container">
218<pre class="src src-makefile"><span class="org-makefile-targets">.PHONY</span>: install-hooks
219<span class="org-makefile-targets">install-hooks</span>:
220        if [ -e .git ]; then nix-shell -p git --run <span class="org-string">'git config core.hooksPath .githooks'</span>; fi
221
222<span class="org-makefile-targets">.PHONY</span>: pre-commit
223<span class="org-makefile-targets">pre-commit</span>: README.md
224</pre>
225</div>
226
227<p>
228And the final target is the <code>clean</code> one. This will remove any compile emacs-lisp file
229(<code>*.elc</code>), the <code>public</code> folder, and some org-mode metadata.
230</p>
231
232<div class="org-src-container">
233<pre class="src src-makefile"><span class="org-makefile-targets">.PHONY</span>: clean
234<span class="org-makefile-targets">clean</span>:
235        <span class="org-type">@</span><span class="org-makefile-shell">echo </span><span class="org-string"><span class="org-makefile-shell">"Cleaning up.."</span></span>
236        <span class="org-type">@-</span><span class="org-makefile-shell">rm -rvf *.elc</span>
237        <span class="org-type">@-</span><span class="org-makefile-shell">rm -rvf public</span>
238        <span class="org-type">@-</span><span class="org-makefile-shell">rm -rv ~/.org-timestamps/*</span>
239</pre>
240</div>
241</div>
242</div>
243</div>
244
245<div id="outline-container-The%20%2Fpublish%2F%20scrits" class="outline-3">
246<h3 id="The%20%2Fpublish%2F%20scrits">The <i>publish</i> scrits</h3>
247<div class="outline-text-3" id="text-The%20%2Fpublish%2F%20scrits">
248<p>
249I&rsquo;ve imported the script directly in here, I&rsquo;ll slowly split this and document it.
250</p>
251</div>
252
253<div id="outline-container-%3Dpublish.el%3D" class="outline-4">
254<h4 id="%3Dpublish.el%3D"><code>publish.el</code></h4>
255<div class="outline-text-4" id="text-%3Dpublish.el%3D">
256<div class="org-src-container">
257<pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;;; </span><span class="org-comment">publish.el --- Publish www project -*- lexical-binding: t; -*-</span>
258<span class="org-comment-delimiter">;; </span><span class="org-comment">Author: Vincent Demeester <a href="mailto:vincent%40sbr.pm">&lt;vincent@sbr.pm&gt;</a></span>
259
260<span class="org-comment-delimiter">;;; </span><span class="org-comment">Commentary:</span>
261<span class="org-comment-delimiter">;; </span><span class="org-comment">This script will convert the org-mode files in this directory into</span>
262<span class="org-comment-delimiter">;; </span><span class="org-comment">html.</span>
263
264<span class="org-comment-delimiter">;;; </span><span class="org-comment">Code:</span>
265(<span class="org-keyword">require</span> '<span class="org-constant">package</span>)
266(<span class="org-keyword">require</span> '<span class="org-constant">publish-common</span>)
267
268(<span class="org-keyword">setq</span> org-publish-project-alist
269      `((<span class="org-string">"posts"</span>
270         <span class="org-builtin">:base-directory</span> <span class="org-string">"posts"</span>
271         <span class="org-builtin">:base-extension</span> <span class="org-string">"org"</span>
272         <span class="org-builtin">:recursive</span> t
273         <span class="org-builtin">:publishing-function</span> org-html-publish-to-html
274         <span class="org-builtin">:publishing-directory</span> <span class="org-string">"./public/posts"</span>
275         <span class="org-builtin">:exclude</span> ,(regexp-opt '(<span class="org-string">"README.org"</span> <span class="org-string">"draft"</span>))
276         <span class="org-builtin">:auto-sitemap</span> t
277         <span class="org-builtin">:with-footnotes</span> t
278         <span class="org-builtin">:with-toc</span> nil
279         <span class="org-builtin">:with-drawers</span> t
280         <span class="org-builtin">:sitemap-filename</span> <span class="org-string">"index.org"</span>
281         <span class="org-builtin">:sitemap-title</span> <span class="org-string">"Posts"</span>
282         <span class="org-builtin">:sitemap-format-entry</span> sbr/org-sitemap-format-entry
283         <span class="org-builtin">:sitemap-style</span> list
284         <span class="org-builtin">:sitemap-sort-files</span> anti-chronologically
285         <span class="org-builtin">:sitemap-function</span> sbr/org-publish-sitemap
286         <span class="org-builtin">:html-head-include-scripts</span> nil
287         <span class="org-builtin">:html-head-include-default-style</span> nil
288         <span class="org-builtin">:html-head</span> ,sbr-website-html-head
289         <span class="org-builtin">:html-preamble</span> sbr-website-html-preamble
290         <span class="org-builtin">:html-postamble</span> ,sbr-website-html-postamble)
291        (<span class="org-string">"posts-rss"</span>
292         <span class="org-builtin">:base-directory</span> <span class="org-string">"posts"</span>
293         <span class="org-builtin">:base-extension</span> <span class="org-string">"org"</span>
294         <span class="org-builtin">:recursive</span> t
295         <span class="org-builtin">:html-link-home</span> <span class="org-string">"https://vincent.demeester.fr/"</span>
296         <span class="org-builtin">:rss-link-home</span> <span class="org-string">"https://vincent.demeester.fr/posts/"</span>
297         <span class="org-builtin">:html-link-use-abs-url</span> t
298         <span class="org-builtin">:rss-extension</span> <span class="org-string">"xml"</span>
299         <span class="org-builtin">:publishing-directory</span> <span class="org-string">"./public"</span>
300         <span class="org-builtin">:publishing-function</span> (sbr/org-rss-publish-to-rss)
301         <span class="org-builtin">:section-number</span> nil
302         <span class="org-builtin">:exclude</span> <span class="org-string">".*"</span>
303         <span class="org-builtin">:include</span> (<span class="org-string">"index.org"</span>))
304        (<span class="org-string">"articles"</span>
305         <span class="org-builtin">:base-directory</span> <span class="org-string">"articles"</span>
306         <span class="org-builtin">:base-extension</span> <span class="org-string">"org"</span>
307         <span class="org-builtin">:recursive</span> t
308         <span class="org-builtin">:publishing-function</span> org-html-publish-to-html
309         <span class="org-builtin">:publishing-directory</span> <span class="org-string">"./public/articles"</span>
310         <span class="org-builtin">:exclude</span> ,(regexp-opt '(<span class="org-string">"README.org"</span> <span class="org-string">"draft"</span>))
311         <span class="org-builtin">:auto-sitemap</span> t
312         <span class="org-builtin">:with-footnotes</span> t
313         <span class="org-builtin">:with-toc</span> nil
314         <span class="org-builtin">:with-drawers</span> t
315         <span class="org-builtin">:sitemap-filename</span> <span class="org-string">"sitemap.org"</span>
316         <span class="org-builtin">:sitemap-title</span> <span class="org-string">"Articles"</span>
317         <span class="org-builtin">:sitemap-style</span> tree
318         <span class="org-builtin">:sitemap-sort-files</span> anti-chronologically
319         <span class="org-comment-delimiter">;;</span><span class="org-comment">:sitemap-format-entry sbr/org-sitemap-format-entry</span>
320         <span class="org-comment-delimiter">;;</span><span class="org-comment">:sitemap-function sbr/org-publish-sitemap</span>
321         <span class="org-builtin">:html-head-include-scripts</span> nil
322         <span class="org-builtin">:html-head-include-default-style</span> nil
323         <span class="org-builtin">:html-head</span> ,sbr-website-html-head
324         <span class="org-builtin">:html-preamble</span> sbr-website-html-preamble
325         <span class="org-builtin">:html-postamble</span> ,sbr-website-html-postamble)
326        (<span class="org-string">"articles-assets"</span>
327         <span class="org-builtin">:exclude</span> ,(regexp-opt '(<span class="org-string">"*.org"</span>))
328         <span class="org-builtin">:base-directory</span> <span class="org-string">"articles"</span>
329         <span class="org-builtin">:base-extension</span> ,site-attachments
330         <span class="org-builtin">:publishing-directory</span> <span class="org-string">"./public/articles"</span>
331         <span class="org-builtin">:publishing-function</span> org-publish-attachment
332         <span class="org-builtin">:recursive</span> t)
333        (<span class="org-string">"about"</span>
334         <span class="org-builtin">:base-directory</span> <span class="org-string">"about"</span>
335         <span class="org-builtin">:base-extension</span> <span class="org-string">"org"</span>
336         <span class="org-builtin">:exclude</span> ,(regexp-opt '(<span class="org-string">"README.org"</span> <span class="org-string">"draft"</span>))
337         <span class="org-builtin">:index-filename</span> <span class="org-string">"index.org"</span>
338         <span class="org-builtin">:recursive</span> nil
339         <span class="org-builtin">:with-footnotes</span> t
340         <span class="org-builtin">:with-toc</span> nil
341         <span class="org-builtin">:with-drawers</span> t
342         <span class="org-builtin">:publishing-function</span> org-html-publish-to-html
343         <span class="org-builtin">:publishing-directory</span> <span class="org-string">"./public/about"</span>
344         <span class="org-builtin">:html-head-include-scripts</span> nil
345         <span class="org-builtin">:html-head-include-default-style</span> nil
346         <span class="org-builtin">:html-head</span> ,sbr-website-html-head
347         <span class="org-builtin">:html-preamble</span> sbr-website-html-preamble
348         <span class="org-builtin">:html-postamble</span> ,sbr-website-html-postamble)
349        (<span class="org-string">"index"</span>
350         <span class="org-builtin">:base-directory</span> <span class="org-string">""</span>
351         <span class="org-builtin">:base-extension</span> <span class="org-string">"org"</span>
352         <span class="org-builtin">:exclude</span> ,(regexp-opt '(<span class="org-string">"README.org"</span> <span class="org-string">"draft"</span>))
353         <span class="org-builtin">:index-filename</span> <span class="org-string">"index.org"</span>
354         <span class="org-builtin">:recursive</span> nil
355         <span class="org-builtin">:with-footnotes</span> t
356         <span class="org-builtin">:with-toc</span> nil
357         <span class="org-builtin">:with-drawers</span> t
358         <span class="org-builtin">:with-title</span> nil
359         <span class="org-builtin">:publishing-function</span> org-html-publish-to-html
360         <span class="org-builtin">:publishing-directory</span> <span class="org-string">"./public"</span>
361         <span class="org-builtin">:html-head-include-scripts</span> nil
362         <span class="org-builtin">:html-head-include-default-style</span> nil
363         <span class="org-builtin">:html-head</span> ,sbr-website-html-head
364         <span class="org-builtin">:html-preamble</span> sbr-website-html-preamble
365         <span class="org-builtin">:html-postamble</span> ,sbr-website-html-postamble)
366        (<span class="org-string">"css"</span>
367         <span class="org-builtin">:base-directory</span> <span class="org-string">"./css"</span>
368         <span class="org-builtin">:base-extension</span> ,site-attachments
369         <span class="org-builtin">:recursive</span> t
370         <span class="org-builtin">:publishing-directory</span> <span class="org-string">"./public/css"</span>
371         <span class="org-builtin">:publishing-function</span> org-publish-attachment
372         <span class="org-builtin">:recursive</span> t)
373        (<span class="org-string">"images"</span>
374         <span class="org-builtin">:base-directory</span> <span class="org-string">"./images"</span>
375         <span class="org-builtin">:base-extension</span> ,site-attachments
376         <span class="org-builtin">:publishing-directory</span> <span class="org-string">"./public/images"</span>
377         <span class="org-builtin">:publishing-function</span> org-publish-attachment
378         <span class="org-builtin">:recursive</span> t)
379        (<span class="org-string">"assets"</span>
380         <span class="org-builtin">:base-directory</span> <span class="org-string">"./assets"</span>
381         <span class="org-builtin">:base-extension</span> ,site-attachments
382         <span class="org-builtin">:publishing-directory</span> <span class="org-string">"./public/assets"</span>
383         <span class="org-builtin">:publishing-function</span> org-publish-attachment
384         <span class="org-builtin">:recursive</span> t)
385        (<span class="org-string">"legacy"</span>
386         <span class="org-builtin">:base-directory</span> <span class="org-string">"./legacy"</span>
387         <span class="org-builtin">:base-extension</span> ,site-attachments
388         <span class="org-builtin">:publishing-directory</span> <span class="org-string">"./public/"</span>
389         <span class="org-builtin">:publishing-function</span> org-publish-attachment
390         <span class="org-builtin">:recursive</span> t)
391        (<span class="org-string">"all"</span> <span class="org-builtin">:components</span> (<span class="org-string">"posts"</span> <span class="org-string">"about"</span> <span class="org-string">"index"</span> <span class="org-string">"articles"</span> <span class="org-string">"articles-assets"</span> <span class="org-string">"css"</span> <span class="org-string">"images"</span> <span class="org-string">"assets"</span> <span class="org-string">"legacy"</span> <span class="org-string">"posts-rss"</span>))))
392
393(<span class="org-keyword">provide</span> '<span class="org-constant">publish</span>)
394<span class="org-comment-delimiter">;;; </span><span class="org-comment">publish.el ends here</span>
395</pre>
396</div>
397</div>
398</div>
399
400<div id="outline-container-%3Dpublish-common.el%3D" class="outline-4">
401<h4 id="%3Dpublish-common.el%3D"><code>publish-common.el</code></h4>
402<div class="outline-text-4" id="text-%3Dpublish-common.el%3D">
403<div class="org-src-container">
404<pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;;; </span><span class="org-comment">publish-common.el --- Commons code for www publishing projects -*- lexical-binding: t; -*-</span>
405<span class="org-comment-delimiter">;; </span><span class="org-comment">Author: Vincent Demeester <a href="mailto:vincent%40sbr.pm">&lt;vincent@sbr.pm&gt;</a></span>
406
407<span class="org-comment-delimiter">;;; </span><span class="org-comment">Commentary:</span>
408<span class="org-comment-delimiter">;;</span>
409<span class="org-comment-delimiter">;;; </span><span class="org-comment">Code:</span>
410<span class="org-comment-delimiter">;; </span><span class="org-comment">load org</span>
411(<span class="org-keyword">require</span> '<span class="org-constant">org</span>)
412(<span class="org-keyword">require</span> '<span class="org-constant">dash</span>)
413<span class="org-comment-delimiter">;; </span><span class="org-comment">load org export functions</span>
414(<span class="org-keyword">require</span> '<span class="org-constant">ox-publish</span>)
415(<span class="org-keyword">require</span> '<span class="org-constant">ox-rss</span>)
416(<span class="org-keyword">require</span> '<span class="org-constant">ox-html</span>)
417<span class="org-comment-delimiter">;; </span><span class="org-comment">load org link functions</span>
418(<span class="org-keyword">require</span> '<span class="org-constant">ol-man</span>)
419(<span class="org-keyword">require</span> '<span class="org-constant">ol-git-link</span>)
420<span class="org-comment-delimiter">;; </span><span class="org-comment">Those are mine</span>
421(<span class="org-keyword">require</span> '<span class="org-constant">ol-github</span>)
422(<span class="org-keyword">require</span> '<span class="org-constant">ol-gitlab</span>)
423(<span class="org-keyword">require</span> '<span class="org-constant">org-attach</span>)
424<span class="org-comment-delimiter">;; </span><span class="org-comment">load additional libraries</span>
425(<span class="org-keyword">require</span> '<span class="org-constant">go-mode</span>)
426(<span class="org-keyword">require</span> '<span class="org-constant">css-mode</span>)
427(<span class="org-keyword">require</span> '<span class="org-constant">yaml-mode</span>)
428(<span class="org-keyword">require</span> '<span class="org-constant">nix-mode</span>)
429
430(<span class="org-keyword">require</span> '<span class="org-constant">s</span>)
431
432(<span class="org-keyword">setq</span> org-export-use-babel nil)
433(<span class="org-keyword">setq</span> org-link-abbrev-alist '((<span class="org-string">"att"</span> . org-attach-expand-link)))
434
435<span class="org-comment-delimiter">;; </span><span class="org-comment">setting to nil, avoids "Author: x" at the bottom</span>
436(<span class="org-keyword">setq</span> org-export-with-section-numbers nil
437      org-export-with-smart-quotes t
438      org-export-with-toc nil)
439
440(<span class="org-keyword">defvar</span> <span class="org-variable-name">sbr-date-format</span> <span class="org-string">"%b %d, %Y"</span>)
441
442(<span class="org-keyword">setq</span> org-html-divs '((preamble <span class="org-string">"header"</span> <span class="org-string">"top"</span>)
443                      (content <span class="org-string">"main"</span> <span class="org-string">"content"</span>)
444                      (postamble <span class="org-string">"footer"</span> <span class="org-string">"postamble"</span>))
445      org-html-container-element <span class="org-string">"section"</span>
446      org-html-metadata-timestamp-format sbr-date-format
447      org-html-checkbox-type 'unicode
448      org-html-html5-fancy t
449      org-html-doctype <span class="org-string">"html5"</span>
450      org-html-htmlize-output-type 'css
451      org-html-htmlize-font-prefix <span class="org-string">"org-"</span>
452      org-src-fontify-natively t
453      org-html-coding-system 'utf-8-unix)
454
455(<span class="org-keyword">defun</span> <span class="org-function-name">sbr/org-export-format-drawer</span> (name content)
456  <span class="org-doc">"HTML export of drawer with NAME and CONTENT.</span>
457<span class="org-doc">name is the name of the drawer, that will be used as class.</span>
458<span class="org-doc">content is the content of the drawer"</span>
459  (format <span class="org-string">"&lt;div class='drawer %s'&gt;\n&lt;h6&gt;%s&lt;/h6&gt;\n%s&lt;/div&gt;"</span>
460          (downcase name)
461          (capitalize name)
462          content))
463(<span class="org-keyword">setq</span> org-html-format-drawer-function 'sbr/org-export-format-drawer)
464
465(<span class="org-keyword">defun</span> <span class="org-function-name">read-file</span> (filePath)
466  <span class="org-doc">"Return FILEPATH's file content."</span>
467  (<span class="org-keyword">with-temp-buffer</span>
468    (insert-file-contents filePath)
469    (buffer-string)))
470
471(<span class="org-keyword">defvar</span> <span class="org-variable-name">sbr-website-html-head</span>
472  <span class="org-string">"&lt;link rel='</span><span class="org-string"><span class="org-constant">icon</span></span><span class="org-string">' type='</span><span class="org-string"><span class="org-constant">image/x-icon</span></span><span class="org-string">' href='</span><span class="org-string"><span class="org-constant">/images/favicon.ico</span></span><span class="org-string">'/&gt;</span>
473<span class="org-string">&lt;meta name='</span><span class="org-string"><span class="org-constant">viewport</span></span><span class="org-string">' content='width=device-width, initial-scale=1'&gt;</span>
474<span class="org-string">&lt;link rel='</span><span class="org-string"><span class="org-constant">stylesheet</span></span><span class="org-string">' href='</span><span class="org-string"><span class="org-constant">/css/new.css</span></span><span class="org-string">' type='</span><span class="org-string"><span class="org-constant">text/css</span></span><span class="org-string">'/&gt;</span>
475<span class="org-string">&lt;link rel='</span><span class="org-string"><span class="org-constant">stylesheet</span></span><span class="org-string">' href='</span><span class="org-string"><span class="org-constant">/css/syntax.css</span></span><span class="org-string">' type='</span><span class="org-string"><span class="org-constant">text/css</span></span><span class="org-string">'/&gt;</span>
476<span class="org-string">&lt;link href='</span><span class="org-string"><span class="org-constant">/index.xml</span></span><span class="org-string">' rel='</span><span class="org-string"><span class="org-constant">alternate</span></span><span class="org-string">' type='</span><span class="org-string"><span class="org-constant">application/rss+xml</span></span><span class="org-string">' title='Vincent Demeester' /&gt;"</span>)
477
478(<span class="org-keyword">defun</span> <span class="org-function-name">sbr-website-html-preamble</span> (plist)
479  <span class="org-doc">"PLIST: An entry."</span>
480  <span class="org-comment-delimiter">;; </span><span class="org-comment">Skip adding subtitle to the post if :KEYWORDS don't have '</span><span class="org-comment"><span class="org-constant">post</span></span><span class="org-comment">' has a</span>
481  <span class="org-comment-delimiter">;; </span><span class="org-comment">keyword</span>
482  (<span class="org-keyword">when</span> (string-match-p <span class="org-string">"post"</span> (format <span class="org-string">"%s"</span> (plist-get plist <span class="org-builtin">:keywords</span>)))
483    (plist-put plist
484               <span class="org-builtin">:subtitle</span> (format <span class="org-string">"Published on %s by %s."</span>
485                                 (org-export-get-date plist sbr-date-format)
486                                 (car (plist-get plist <span class="org-builtin">:author</span>)))))
487
488  <span class="org-comment-delimiter">;; </span><span class="org-comment">Below content will be added anyways</span>
489  <span class="org-string">"&lt;nav&gt;</span>
490<span class="org-string">&lt;img src=\"/images/favicon.ico\" id=\"sitelogo\"/&gt; &lt;a href='</span><span class="org-string"><span class="org-constant">/</span></span><span class="org-string">'&gt;home&lt;/a&gt; /</span>
491<span class="org-string">&lt;a href='</span><span class="org-string"><span class="org-constant">/posts/</span></span><span class="org-string">'&gt;posts&lt;/a&gt; (&lt;a href='</span><span class="org-string"><span class="org-constant">/index.xml</span></span><span class="org-string">'&gt;rss&lt;/a&gt;) /</span>
492<span class="org-string">&lt;a href='</span><span class="org-string"><span class="org-constant">/articles/</span></span><span class="org-string">'&gt;articles&lt;/a&gt; /</span>
493<span class="org-string">&lt;a href='</span><span class="org-string"><span class="org-constant">https://dl.sbr.pm/</span></span><span class="org-string">'&gt;files&lt;/a&gt; /</span>
494<span class="org-string">&lt;a href='</span><span class="org-string"><span class="org-constant">/about/</span></span><span class="org-string">'&gt;about&lt;/a&gt;&lt;/li&gt;</span>
495<span class="org-string">&lt;/nav&gt;"</span>)
496
497(<span class="org-keyword">defvar</span> <span class="org-variable-name">sbr-website-html-postamble</span>
498  <span class="org-string">"&lt;footer&gt;</span>
499<span class="org-string">     &lt;span class='</span><span class="org-string"><span class="org-constant">questions</span></span><span class="org-string">'&gt;Questions, comments ? Please use my &lt;a href=\"https://lists.sr.ht/~vdemeester/public-inbox\"&gt;public inbox&lt;/a&gt; by sending a plain-text email to &lt;a href=\"mailto:~vdemeester/public-inbox@lists.sr.ht\"&gt;~vdemeester/public-inbox@lists.sr.ht&lt;/a&gt;.&lt;/span&gt;</span>
500<span class="org-string">     &lt;span class='</span><span class="org-string"><span class="org-constant">opinions</span></span><span class="org-string">'&gt;Opinions stated here are my own and do not express the views of my employer, spouse, children, pets, neighbors, secret crushes, favorite authors, or anyone else who is not me. And maybe not even me, depending on how old this is.&lt;/span&gt;</span>
501<span class="org-string">     &lt;span class='</span><span class="org-string"><span class="org-constant">copyright</span></span><span class="org-string">'&gt;</span>
502<span class="org-string">      Content and design by Vincent Demeester</span>
503<span class="org-string">      (&lt;a rel='</span><span class="org-string"><span class="org-constant">licence</span></span><span class="org-string">' href='</span><span class="org-string"><span class="org-constant">http://creativecommons.org/licenses/by-nc-sa/3.0/</span></span><span class="org-string">'&gt;Some rights reserved&lt;/a&gt;)</span>
504<span class="org-string">    &lt;/span&gt;&lt;br /&gt;</span>
505<span class="org-string">    &lt;span class='</span><span class="org-string"><span class="org-constant">engine</span></span><span class="org-string">'&gt;</span>
506<span class="org-string">      Powered by &lt;a href='</span><span class="org-string"><span class="org-constant">https://www.gnu.org/software/emacs/</span></span><span class="org-string">'&gt;Gnu Emacs&lt;/a&gt; and &lt;a href='</span><span class="org-string"><span class="org-constant">https://orgmode.org</span></span><span class="org-string">'&gt;orgmode&lt;/a&gt;</span>
507<span class="org-string">    &lt;/span&gt;</span>
508<span class="org-string">&lt;/footer&gt;"</span>)
509(<span class="org-keyword">defvar</span> <span class="org-variable-name">site-attachments</span>
510  (regexp-opt '(<span class="org-string">"jpg"</span> <span class="org-string">"jpeg"</span> <span class="org-string">"gif"</span> <span class="org-string">"png"</span> <span class="org-string">"svg"</span>
511                <span class="org-string">"ico"</span> <span class="org-string">"cur"</span> <span class="org-string">"css"</span> <span class="org-string">"js"</span> <span class="org-string">"woff"</span> <span class="org-string">"html"</span> <span class="org-string">"pdf"</span> <span class="org-string">"otf"</span>))
512  <span class="org-doc">"File types that are published as static files."</span>)
513
514(<span class="org-keyword">defun</span> <span class="org-function-name">sbr/org-sitemap-format-entry</span> (entry style project)
515  <span class="org-doc">"Format posts with author and published data in the index page.</span>
516
517<span class="org-doc">ENTRY: file-name</span>
518<span class="org-doc">STYLE:</span>
519<span class="org-doc">PROJECT: `posts in this case."</span>
520  (<span class="org-keyword">cond</span> ((not (directory-name-p entry))
521         (format <span class="org-string">"%s &#8212; [[file:%s][%s]]</span>
522<span class="org-string">                 :PROPERTIES:</span>
523<span class="org-string">                 :PUBDATE: [%s]</span>
524<span class="org-string">                 :END:"</span>
525                 (format-time-string <span class="org-string">"%Y-%m-%d"</span>
526                                     (org-publish-find-date entry project))
527                 entry
528                 (org-publish-find-title entry project)
529                 (format-time-string <span class="org-string">"%Y-%m-%d"</span>
530                                     (org-publish-find-date entry project))))
531        ((eq style 'tree) (file-name-nondirectory (directory-file-name entry)))
532        (t entry)))
533
534(<span class="org-keyword">defun</span> <span class="org-function-name">sbr/org-publish-sitemap</span> (title list)
535  <span class="org-doc">""</span>
536  (concat <span class="org-string">"#+TITLE: "</span> title <span class="org-string">"\n\n"</span>
537          (org-list-to-subtree list)))
538
539(<span class="org-keyword">defun</span> <span class="org-function-name">sbr/org-get-first-paragraph</span> (file)
540  <span class="org-doc">"Get string content of first paragraph of file."</span>
541  (<span class="org-keyword">ignore-errors</span>
542    (<span class="org-keyword">with-temp-buffer</span>
543    (insert-file-contents file)
544    (goto-char (point-min))
545    (show-all)
546    (<span class="org-keyword">let</span> ((first-begin (<span class="org-keyword">progn</span>
547                         (org-forward-heading-same-level 1)
548                         (next-line)
549                         (point)))
550          (first-end (<span class="org-keyword">progn</span>
551                       (org-next-visible-heading 1)
552                       (point))))
553      (buffer-substring first-begin first-end)))))
554
555(<span class="org-keyword">defun</span> <span class="org-function-name">sbr/org-rss-publish-to-rss</span> (plist filename pub-dir)
556  <span class="org-doc">"Prepare rss.org file before exporting."</span>
557  (<span class="org-keyword">let*</span> ((postsdir (plist-get plist <span class="org-builtin">:base-directory</span>)))
558    (<span class="org-keyword">with-current-buffer</span> (find-file filename)
559      (erase-buffer)
560      (insert <span class="org-string">"#+TITLE: Posts\n"</span>)
561      (insert <span class="org-string">"#+AUTHOR: Vincent Demeester\n"</span>)
562      (insert <span class="org-string">"#+OPTIONS: toc:nil\n"</span>)
563      (<span class="org-keyword">let*</span> ((files-all
564              (reverse (directory-files <span class="org-string">"."</span> nil
565                                        <span class="org-string">"[0-9-]+.*\\.org$"</span>)))
566             (files (seq-subseq files-all 0 (min (length files-all) 30))))
567        (message (format <span class="org-string">"foo: %s"</span> filename))
568        (<span class="org-keyword">dolist</span> (post files)
569          (<span class="org-keyword">let*</span> ((post-file post)
570                 (post-title (org-publish-find-title post-file plist))
571                 (preview-str (sbr/org-get-first-paragraph post-file))
572                 (date (replace-regexp-in-string
573                        <span class="org-string">"</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">[0-9]\\{</span><span class="org-string"><span class="org-variable-name">4\\</span></span><span class="org-string">}-[0-9]\\{</span><span class="org-string"><span class="org-variable-name">2\\</span></span><span class="org-string">}-[0-9]\\{</span><span class="org-string"><span class="org-variable-name">2\\</span></span><span class="org-string">}</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">-.*"</span>
574                        <span class="org-string">"\\1"</span> post)))
575            (insert (concat <span class="org-string">"* [[file:"</span> postsdir <span class="org-string">"/"</span> post <span class="org-string">"]["</span> post-title <span class="org-string">"]]\n\n"</span>))
576            (org-set-property <span class="org-string">"ID"</span> post)
577            (org-set-property <span class="org-string">"RSS_TITLE"</span> post-title)
578            <span class="org-comment-delimiter">;; </span><span class="org-comment">ox-rss prepends html-link-home to permalink</span>
579            (org-set-property <span class="org-string">"RSS_PERMALINK"</span>
580                              (concat postsdir <span class="org-string">"/"</span>
581                                      (file-name-sans-extension post)
582                                      <span class="org-string">".html"</span>))
583            (org-set-property
584             <span class="org-string">"PUBDATE"</span>
585             (format-time-string
586              <span class="org-string">"&lt;%Y-%m-%d %a %H:%M&gt;"</span>
587              (org-time-string-to-time
588               (replace-regexp-in-string
589                <span class="org-string">"</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">[0-9]\\{</span><span class="org-string"><span class="org-variable-name">4\\</span></span><span class="org-string">}-[0-9]\\{</span><span class="org-string"><span class="org-variable-name">2\\</span></span><span class="org-string">}-[0-9]\\{</span><span class="org-string"><span class="org-variable-name">2\\</span></span><span class="org-string">}</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">-.*"</span>
590                <span class="org-string">"\\1"</span> post))))
591            (insert preview-str)
592            (newline 1)
593            (insert (concat <span class="org-string">"[[file:"</span> postsdir <span class="org-string">"/"</span> post <span class="org-string">"][(Read more)]]\n\n"</span>))))
594        (save-buffer))))
595  (<span class="org-keyword">let</span> ((user-mail-address <span class="org-string">"t"</span>)
596        (org-export-with-broken-links t)
597        (org-rss-use-entry-url-as-guid nil))
598    (org-rss-publish-to-rss plist filename pub-dir)))
599
600(advice-add #'org-export-get-reference <span class="org-builtin">:override</span> #'unpackaged/org-export-get-reference)
601
602(<span class="org-keyword">defun</span> <span class="org-function-name">unpackaged/org-export-get-reference</span> (datum info)
603  <span class="org-doc">"Like `</span><span class="org-doc"><span class="org-constant">org-export-get-reference</span></span><span class="org-doc">', except uses heading titles instead of random numbers."</span>
604  (<span class="org-keyword">let</span> ((cache (plist-get info <span class="org-builtin">:internal-references</span>)))
605    (<span class="org-keyword">or</span> (car (rassq datum cache))
606        (<span class="org-keyword">let*</span> ((crossrefs (plist-get info <span class="org-builtin">:crossrefs</span>))
607               (cells (org-export-search-cells datum))
608               <span class="org-comment-delimiter">;; </span><span class="org-comment">Preserve any pre-existing association between</span>
609               <span class="org-comment-delimiter">;; </span><span class="org-comment">a search cell and a reference, i.e., when some</span>
610               <span class="org-comment-delimiter">;; </span><span class="org-comment">previously published document referenced a location</span>
611               <span class="org-comment-delimiter">;; </span><span class="org-comment">within current file (see</span>
612               <span class="org-comment-delimiter">;; </span><span class="org-comment">`</span><span class="org-comment"><span class="org-constant">org-publish-resolve-external-link</span></span><span class="org-comment">').</span>
613               <span class="org-comment-delimiter">;;</span>
614               <span class="org-comment-delimiter">;; </span><span class="org-comment">However, there is no guarantee that search cells are</span>
615               <span class="org-comment-delimiter">;; </span><span class="org-comment">unique, e.g., there might be duplicate custom ID or</span>
616               <span class="org-comment-delimiter">;; </span><span class="org-comment">two headings with the same title in the file.</span>
617               <span class="org-comment-delimiter">;;</span>
618               <span class="org-comment-delimiter">;; </span><span class="org-comment">As a consequence, before re-using any reference to</span>
619               <span class="org-comment-delimiter">;; </span><span class="org-comment">an element or object, we check that it doesn't refer</span>
620               <span class="org-comment-delimiter">;; </span><span class="org-comment">to a previous element or object.</span>
621               (new (<span class="org-keyword">or</span> (cl-some
622                         (<span class="org-keyword">lambda</span> (cell)
623                           (<span class="org-keyword">let</span> ((stored (cdr (assoc cell crossrefs))))
624                             (<span class="org-keyword">when</span> stored
625                               (<span class="org-keyword">let</span> ((old (org-export-format-reference stored)))
626                                 (<span class="org-keyword">and</span> (not (assoc old cache)) stored)))))
627                         cells)
628                        (<span class="org-keyword">when</span> (org-element-property <span class="org-builtin">:raw-value</span> datum)
629                          <span class="org-comment-delimiter">;; </span><span class="org-comment">Heading with a title</span>
630                          (unpackaged/org-export-new-title-reference datum cache))
631                        <span class="org-comment-delimiter">;; </span><span class="org-comment">NOTE: This probably breaks some Org Export</span>
632                        <span class="org-comment-delimiter">;; </span><span class="org-comment">feature, but if it does what I need, fine.</span>
633                        (org-export-format-reference
634                         (org-export-new-reference cache))))
635               (reference-string new))
636          <span class="org-comment-delimiter">;; </span><span class="org-comment">Cache contains both data already associated to</span>
637          <span class="org-comment-delimiter">;; </span><span class="org-comment">a reference and in-use internal references, so as to make</span>
638          <span class="org-comment-delimiter">;; </span><span class="org-comment">unique references.</span>
639          (<span class="org-keyword">dolist</span> (cell cells) (<span class="org-keyword">push</span> (cons cell new) cache))
640          <span class="org-comment-delimiter">;; </span><span class="org-comment">Retain a direct association between reference string and</span>
641          <span class="org-comment-delimiter">;; </span><span class="org-comment">DATUM since (1) not every object or element can be given</span>
642          <span class="org-comment-delimiter">;; </span><span class="org-comment">a search cell (2) it permits quick lookup.</span>
643          (<span class="org-keyword">push</span> (cons reference-string datum) cache)
644          (plist-put info <span class="org-builtin">:internal-references</span> cache)
645          reference-string))))
646
647(<span class="org-keyword">defun</span> <span class="org-function-name">unpackaged/org-export-new-title-reference</span> (datum cache)
648  <span class="org-doc">"Return new reference for DATUM that is unique in CACHE."</span>
649  (<span class="org-keyword">cl-macrolet</span> ((inc-suffixf (place)
650                             `(<span class="org-keyword">progn</span>
651                                (string-match (<span class="org-keyword">rx</span> bos
652                                                  (minimal-match (group (1+ anything)))
653                                                  (optional <span class="org-string">"--"</span> (group (1+ digit)))
654                                                  eos)
655                                              ,place)
656                                <span class="org-comment-delimiter">;; </span><span class="org-comment">HACK: `</span><span class="org-comment"><span class="org-constant">s1</span></span><span class="org-comment">' instead of a gensym.</span>
657                                (<span class="org-keyword">-let*</span> (((s1 suffix) (list (match-string 1 ,place)
658                                                           (match-string 2 ,place)))
659                                        (suffix (<span class="org-keyword">if</span> suffix
660                                                    (string-to-number suffix)
661                                                  0)))
662                                  (<span class="org-keyword">setf</span> ,place (format <span class="org-string">"%s--%s"</span> s1 (<span class="org-keyword">cl-incf</span> suffix)))))))
663    (<span class="org-keyword">let*</span> ((title (org-element-property <span class="org-builtin">:raw-value</span> datum))
664           (ref (url-hexify-string (substring-no-properties title)))
665           (parent (org-element-property <span class="org-builtin">:parent</span> datum)))
666      (<span class="org-keyword">while</span> (<span class="org-keyword">--any</span> (equal ref (car it))
667                    cache)
668        <span class="org-comment-delimiter">;; </span><span class="org-comment">Title not unique: make it so.</span>
669        (<span class="org-keyword">if</span> parent
670            <span class="org-comment-delimiter">;; </span><span class="org-comment">Append ancestor title.</span>
671            (<span class="org-keyword">setf</span> title (concat (org-element-property <span class="org-builtin">:raw-value</span> parent)
672                                <span class="org-string">"--"</span> title)
673                  ref (url-hexify-string (substring-no-properties title))
674                  parent (org-element-property <span class="org-builtin">:parent</span> parent))
675          <span class="org-comment-delimiter">;; </span><span class="org-comment">No more ancestors: add and increment a number.</span>
676          (inc-suffixf ref)))
677      ref)))
678
679(<span class="org-keyword">provide</span> '<span class="org-constant">publish-common</span>)
680<span class="org-comment-delimiter">;;; </span><span class="org-comment">publish-common.el ends here</span>
681</pre>
682</div>
683
684<div class="org-src-container">
685<pre class="src src-bash"><span class="org-comment-delimiter"># </span><span class="org-comment">-*- mode: org; eval: (add-hook 'after-save-hook (lambda () (org-babel-tangle)) nil t) -*-</span>
686</pre>
687</div>
688</div>
689</div>
690</div>
691</section>
692</main>
693<footer id="postamble" class="status">
694<footer>
695     <small><a href="/" rel="history">Index</a><a href="/sitemap.html">Sitemap</a><a href="https://dl.sbr.pm/">Files</a></small><br/>
696     <small class='questions'>Questions, comments ? Please use my <a href="https://lists.sr.ht/~vdemeester/public-inbox">public inbox</a> by sending a plain-text email to <a href="mailto:~vdemeester/public-inbox@lists.sr.ht">~vdemeester/public-inbox@lists.sr.ht</a>.</small><br/>
697     <small class='copyright'>
698      Content and design by Vincent Demeester
699      (<a rel='licence' href='http://creativecommons.org/licenses/by-nc-sa/3.0/'>Some rights reserved</a>)
700    </small><br />
701</footer>
702</footer>
703</body>
704</html>