This commit is contained in:
Yann Esposito (Yogsototh) 2019-07-21 18:03:26 +02:00
parent f92f5d1da7
commit fa0aa00b8f
Signed by untrusted user who does not match committer: yogsototh
GPG Key ID: 7B19A4C650D59646
1 changed files with 317 additions and 0 deletions

View File

@ -4,6 +4,8 @@
#+DATE: 2019-07-04
#+KEYWORDS: programming, blog, org-mode
tl;dr: [[#solution][For the impatient]]
This is the first article using my new blog system.
Once in a while, I create a new personal website.
@ -13,6 +15,8 @@ Then I used [[http://nanoc.ws][nanoc]], a ruby static website generator.
Then I switched to [[https://jaspervdj.be/hakyll/][hakyll]] because I wanted to switch to a Haskell's written
tool.
Now I'll try to use [[http://orgmode.org][org-mode]] directly with [[https://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.html][org-publish]].
I am became a quite extensive emacs user now. So using org-mode and org-publish
is a bit like not having to install any 3rd party software.
* Why?
@ -46,3 +50,316 @@ One real game changer is ~org-capture~.
You can add a task quite easily while doing other work in emacs.
[fn:vim] I wrote this article to help people use vim: [[http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/][learn vim progressively]]
* How?
I have a code block in my =index.org= page that I do not export containing
the elisp code I use to load all the information needed by =org-publish=.
#+begin_src elisp :results none
...#+begin_src elisp :results none
...#+end_src
#+end_src
=org-publish= suppose that you'll export one html page per org file.
You need to provide a minimum set of information:
- base directory that should contain your org files
- publish directory, where you will export the html from the org files
#+begin_src elisp :results none
(setq org-publish-project-alist
'(("orgfiles"
:base-directory "/users/me/blog/src"
:publishing-directory "/users/me/blog/_site"
:recursive t)))
#+end_src
So now, if you put org files in the base directory it will copy
recursively the tree in that base directory and will copy it to
the publishing directory exporting all org files to html files.
Nice.
If you are not familiar with emacs or orgmode, orgpublish.
This block of code set the variable named ~org-publish-project-alist~.
The ~alist~ in the end is for "associative list".
#+begin_quote
/☞/ So when you'll execute the command =org-publish= in emacs.
You will get prompted to enter the name of what to publish.
You should see a single proposition named =orgfiles=.
Once selected the export and publish will occurs.
Notice, org-publish does what you expect and do not re-export all files
each time but only the one that have changed.
#+end_quote
A first issue with this is that I don't want to put that in my emacs
preferences.
I would like to be able to publish my website for anyone that could clone my git
repository.
So I will use emacs projectile to find the root directory of the current
project.
#+begin_src elisp
(setq base-dir (concat (projectile-project-root) "src"))
(setq publish-dir (concat (projectile-project-root) "_site"))
(setq org-publish-project-alist
`(("orgfiles"
:base-directory ,base-dir
:publishing-directory ,publish-dir
:recursive t
:base-extension "org")))
#+end_src
That's better.
Now anyone can clone the repository.
Open my =index.org= file, and execute the code in it by =C-e C-e= and then will
be able to publish the website locally in the =_site= directory.
Until there this is nice
But generally you want a bit more features than that for a blog.
Typically you would like to have a common template, an header, some CSS.
For that you need to work a bit more.
First, important thing, add an org publish section to publish your assets (CSS,
images, etc...)
asdf
#+begin_src elisp
(setq base-dir (concat (projectile-project-root) "src"))
(setq publish-dir (concat (projectile-project-root) "_site"))
(setq assets-dir (concat base-dir "/assets"))
(setq publish-assets-dir (concat publish-dir "/assets"))
(setq org-publish-project-alist
`(("orgfiles"
:base-directory ,base-dir
:publishing-directory ,publish-dir
:recursive t
:base-extension "org"
:exclude ".*drafts/.*")
("assets"
:base-directory ,assets-dir
:base-extension ".*"
:publishing-directory ,publish-assets-dir
:publishing-function org-publish-attachment
:recursive t)
("blog" :components ("orgfiles" "assets"))))
#+end_src
* Current Solution
:PROPERTIES:
:CUSTOM_ID: solution
:END:
#+begin_src elisp :results none
(require 'org)
(require 'ox-publish)
(require 'ox-html)
(require 'org-element)
(require 'ox-rss)
(defun org-blog-prepare (project-plist)
"With help from `https://github.com/howardabrams/dot-files'.
Touch `index.org' to rebuilt it.
Argument `PROJECT-PLIST' contains information about the current project."
(let* ((base-directory (plist-get project-plist :base-directory))
(buffer (find-file-noselect (expand-file-name "index.org" base-directory) t)))
(with-current-buffer buffer
(set-buffer-modified-p t)
(save-buffer 0))))
(defvar org-blog-head
(concat
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/assets/css/minimalist.css\"/>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<link rel=\"alternative\" type=\"application/rss+xml\" title=\"Subscribe to articles\" href=\"/archives.xml\" />"
"<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/favicon.ico\">"))
(defun menu (lst)
"Blog menu"
(concat
"<navigation class=\"menu\">"
(mapconcat 'identity
(append
'("<a href=\"/index.html\">Home</a>"
"<a href=\"/archive.html\">Posts</a>")
lst)
" | ")
"</navigation>"))
(defun str-time-to-year-float (date-str)
(/ (float-time
(apply 'encode-time
(mapcar (lambda (x) (if (null x) 0 x))
(parse-time-string date-str))))
(* 365.25 24 60 60)))
(defvar blog-creation-date "2019-07-01")
(defun y-date (date-str)
"Number of year since the begining of this blog"
(let ((y (- (str-time-to-year-float date-str)
(str-time-to-year-float blog-creation-date))))
(format "∆t=%.2f" y)))
(defun org-blog-preamble (info)
"Pre-amble for whole blog."
(concat
"<div class=\"content\">"
(menu '("<a href=\"#postamble\">↓ bottom ↓</a>"))
"<h1>"
(format "%s" (plist-get info :title))
(when-let ((date (get-from-info info :date)))
(format " - <span class=\"article-date\">%s</span>" date))
"</h1>"
"</div>"))
(defun get-from-info (info k)
(let ((i (car (plist-get info k))))
(when (and i (stringp i))
i)))
(defun rand-obfs (c)
(let ((r (% (random) 20)))
(cond ((eq 0 r) (format "%c" c))
((< 0 r 10) (format "&#%d;" c))
(t (format "&#x%X;" c)))))
(defun obfuscate-html (txt)
(apply 'concat
(mapcar 'rand-obfs txt)))
(defun org-blog-postamble (info)
"Post-amble for whole blog."
(concat
"<div class=\"content\">"
"<footer>"
(when-let ((author (get-from-info info :author)))
(if-let ((email (plist-get info :email)))
(format "<div class=\"author\">Author: <a href=\"%s%s\">%s</a></div>"
(obfuscate-html "mailto:")
(obfuscate-html email)
(obfuscate-html author))
(format "<div class=\"author\">Author: %s</div>" author)))
(when-let ((date (get-from-info info :date)))
(format "<div class=\"date\">Created: %s (%s)</div>" date (y-date date)))
(when-let ((keywords (plist-get info :keywords)))
(format "<div class=\"keywords\">Keywords: <code>%s</code></div>" keywords))
(format "<div class=\"date\">Generated: %s</div>"
(format-time-string "%Y-%m-%d %H:%M:%S"))
(format (concat "<div class=\"creator\"> Generated with "
"<a href=\"https://www.gnu.org/software/emacs/\" target=\"_blank\" rel=\"noopener noreferrer\">Emacs %s</a>, "
"<a href=\"http://spacemacs.org\" target=\"_blank\" rel=\"noopener noreferrer\">Spacemacs %s</a>, "
"<a href=\"http://orgmode.org\" target=\"_blank\" rel=\"noopener noreferrer\">Org Mode %s</a>"
"</div>")
emacs-version spacemacs-version org-version)
"</footer>"
(menu '("<a href=\"#preamble\">↑ Top ↑</a>"))
"</div>"))
(defun org-blog-sitemap-format-entry (entry _style project)
"Return string for each ENTRY in PROJECT."
(when (s-starts-with-p "posts/" entry)
(format "@@html:<span class=\"archive-item\"><span class=\"archive-date\">@@ %s: @@html:</span>@@ [[file:%s][%s]] @@html:</span>@@"
(format-time-string "%Y-%m-%d"
(org-publish-find-date entry project))
entry
(org-publish-find-title entry project))))
(defun org-blog-sitemap-function (title list)
"Return sitemap using TITLE and LIST returned by `org-blog-sitemap-format-entry'."
(concat "#+TITLE: " title "\n"
"#+AUTHOR: Yann Esposito\n"
"#+EMAIL: yann.esposito@gmail.com\n"
"\n#+begin_archive\n"
(mapconcat (lambda (li)
(format "@@html:<li>@@ %s @@html:</li>@@" (car li)))
(seq-filter #'car (cdr list))
"\n")
"\n#+end_archive\n"))
(setq base-dir (concat (projectile-project-root) "src"))
(setq publish-dir (concat (projectile-project-root) "_site"))
(setq assets-dir (concat base-dir "/assets"))
(setq publish-assets-dir (concat publish-dir "/assets"))
(setq rss-dir base-dir)
(setq publish-rss-dir publish-dir)
(setq domainname "https://her.esy.fun")
(setq org-publish-project-alist
`(("orgfiles"
:base-directory ,base-dir
:exclude ".*drafts/.*"
:base-extension "org"
:publishing-directory ,publish-dir
:recursive t
:preparation-function org-blog-prepare
:publishing-function org-html-publish-to-html
:with-toc nil
:with-title nil
:with-date t
:section-numbers nil
:html-doctype "html5"
:html-html5-fancy t
:html-head-include-default-style nil
:html-head-include-scripts nil
:htmlized-source t
:html-head-extra ,org-blog-head
:html-preamble org-blog-preamble
:html-postamble org-blog-postamble
:auto-sitemap t
:sitemap-filename "archive.org"
:sitemap-title "Blog Posts"
:sitemap-style list
:sitemap-sort-files anti-chronologically
:sitemap-format-entry org-blog-sitemap-format-entry
:sitemap-function org-blog-sitemap-function)
("assets"
:base-directory ,assets-dir
:base-extension ".*"
:publishing-directory ,publish-assets-dir
:publishing-function org-publish-attachment
:recursive t)
("rss"
:base-directory ,rss-dir
:base-extension "org"
:html-link-home ,domainname
:html-link-use-abs-url t
:rss-extension "xml"
:publishing-directory ,publish-rss-dir
:publishing-function (org-rss-publish-to-rss)
:exclude ".*"
:include ("archive.org")
:section-numbers nil
:table-of-contents nil)
("blog" :components ("orgfiles" "assets" "rss"))))
;; add target=_blank and rel="noopener noreferrer" to all links by default
(defun my-org-export-add-target-blank-to-http-links (text backend info)
"Add target=\"_blank\" to external links."
(when (and
(org-export-derived-backend-p backend 'html)
(string-match "href=\"http[^\"]+" text)
(not (string-match "target=\"" text))
(not (string-match (concat "href=\"" domainname "[^\"]*") text)))
(string-match "<a " text)
(replace-match "<a target=\"_blank\" rel=\"noopener noreferrer\" " nil nil text)))
(add-to-list 'org-export-filter-link-functions
'my-org-export-add-target-blank-to-http-links)
#+end_src