diff --git a/src/posts/2019-07-04-static-org-publish.org b/src/posts/2019-07-04-static-org-publish.org index a948807..cf9717a 100644 --- a/src/posts/2019-07-04-static-org-publish.org +++ b/src/posts/2019-07-04-static-org-publish.org @@ -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 + "" + "" + "" + "")) + + (defun menu (lst) + "Blog menu" + (concat + "" + (mapconcat 'identity + (append + '("Home" + "Posts") + lst) + " | ") + "")) + + + (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 + "
" + (menu '("↓ bottom ↓")) + "

" + (format "%s" (plist-get info :title)) + (when-let ((date (get-from-info info :date))) + (format " - %s" date)) + "

" + "
")) + + (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 + "
" + "" + (menu '("↑ Top ↑")) + "
")) + + (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:@@ %s: @@html:@@ [[file:%s][%s]] @@html:@@" + (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:
  • @@ %s @@html:
  • @@" (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 "