updated a bit

This commit is contained in:
Yann Esposito (Yogsototh) 2019-08-14 14:54:14 +02:00
parent 2db39c15a4
commit cb24d0bac1
Signed by untrusted user who does not match committer: yogsototh
GPG Key ID: 7B19A4C650D59646
6 changed files with 336 additions and 375 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
_site _site
src/archive.org

Binary file not shown.

View File

@ -1,8 +0,0 @@
#+TITLE: Blog Posts
#+AUTHOR: Yann Esposito
#+EMAIL: yann@esposito.host
#+begin_archive
@@html:<li>@@ @@html:<span class="archive-item"><span class="archive-date">@@ 2019-08-13: @@html:</span>@@ [[file:posts/project-el/index.org][Autoload Script by project]] @@html:</span>@@ @@html:</li>@@
@@html:<li>@@ @@html:<span class="archive-item"><span class="archive-date">@@ 2019-07-15: @@html:</span>@@ [[file:posts/2019-07-04-static-org-publish.org][Static blog with org-mode]] @@html:</span>@@ @@html:</li>@@
#+end_archive

View File

@ -190,17 +190,22 @@ navigation > a {
} }
#preamble { #preamble {
border-bottom: solid 1px; border-bottom: solid 1px;
margin-bottom: 1em;
} }
#preamble h1, #preamble h2 { #preamble h1, #preamble h2 {
margin: 0; margin: 0;
} }
#preamble h2 {
font-weight: normal;
font-style: italic;
}
#postamble { #postamble {
border-top: solid 1px; border-top: solid 1px;
margin-top: 10px; margin-top: 10px;
} }
#content,.content { #content,.content {
max-width: 50em; max-width: 50em;
margin: 0 auto; margin: 0 1em;
padding: 1px; padding: 1px;
} }
#content *:first-child { #content *:first-child {
@ -267,6 +272,13 @@ navigation > a {
--soft-foreground: var(--base01); --soft-foreground: var(--base01);
--border-color: var(--base02); --border-color: var(--base02);
--todo-txt: #000; --todo-txt: #000;
--color-h1: var(--magenta);
--color-h2: var(--violet);
--color-h3: var(--blue);
--color-h4: var(--cyan);
--color-h5: var(--green);
--color-h6: var(--yellow);
--color-link: var(--yellow);
} }
/* org colors */ /* org colors */
@ -290,7 +302,9 @@ navigation > a {
cursor: pointer; cursor: pointer;
font-style: italic; font-style: italic;
} }
input#light, input#dark { display:none; }
body > input { display:none; }
input#light:checked ~ .main { input#light:checked ~ .main {
--main-background: #fefaf0; /* 0.5 lighter than #fdf6e3 */ --main-background: #fefaf0; /* 0.5 lighter than #fdf6e3 */
--main-foreground: var(--base01); --main-foreground: var(--base01);
@ -300,6 +314,11 @@ input#light:checked ~ .main {
--border-color: var(--base2); --border-color: var(--base2);
--todo-txt: #FFF; --todo-txt: #FFF;
} }
input#light:checked ~ #labels {
background: #fefaf0;
color: var(--base00);
}
input#dark:checked ~ .main { input#dark:checked ~ .main {
--main-background: var(--base03); /* 0.5 darker than #002b36; */ --main-background: var(--base03); /* 0.5 darker than #002b36; */
--main-foreground: var(--base1); --main-foreground: var(--base1);
@ -309,14 +328,33 @@ input#dark:checked ~ .main {
--border-color: var(--base02); --border-color: var(--base02);
--todo-txt: #000; --todo-txt: #000;
} }
input#light:checked ~ #labels {
background: #fefaf0;
color: var(--base00);
}
input#dark:checked ~ #labels { input#dark:checked ~ #labels {
background: var(--base03); background: var(--base03);
color: var(--base0); color: var(--base0);
} }
input#raw:checked ~ .main {
--main-background: #fff;
--main-foreground: #000;
--second-foreground: #000;
--reveal-background: #fff;
--soft-foreground: #000;
--border-color: #000;
--todo-txt: #000;
--color-h1: #000;
--color-h2: #000;
--color-h3: #000;
--color-h4: #000;
--color-h5: #000;
--color-h6: #000;
--color-link: #33d;
}
input#raw:checked ~ #labels {
background: #fff;
color: #000;
}
body,.main { body,.main {
background: var(--main-background); background: var(--main-background);
color: var(--main-foreground); color: var(--main-foreground);
@ -337,7 +375,7 @@ pre::after,pre::before,hr:after,
} }
a:hover, a:active, a:focus, a:hover, a:active, a:focus,
.main a:hover,.main a:active,.main a:focus { .main a:hover,.main a:active,.main a:focus {
color: var(--yellow); color: var(--color-link);
} }
navigation a, navigation a:visited, navigation a, navigation a:visited,
.main navigation a,.main navigation a:visited { .main navigation a,.main navigation a:visited {
@ -345,7 +383,7 @@ navigation a, navigation a:visited,
} }
navigation a:focus, navigation a:hover, navigation a:focus, navigation a:hover,
.main navigation a:focus,.main navigation a:hover { .main navigation a:focus,.main navigation a:hover {
color: var(--yellow); color: var(--color-link);
} }
thead, thead,
.main thead { .main thead {
@ -357,22 +395,25 @@ tr:hover,
background-color: var(--reveal-background); background-color: var(--reveal-background);
} }
h1, .main h1 { h1, .main h1 {
color: var(--magenta); color: var(--color-h1);
}
#preamble h2, .main #preamble h2 {
color: var(--color-h4);
} }
h2, .main h2 { h2, .main h2 {
color: var(--violet); color: var(--color-h2);
} }
h3, .main h3 { h3, .main h3 {
color: var(--blue); color: var(--color-h3);
} }
h4, .main h4 { h4, .main h4 {
color: var(--cyan); color: var(--color-h4);
} }
h5, .main h5 { h5, .main h5 {
color: var(--green); color: var(--color-h5);
} }
h6, .main h6 { h6, .main h6 {
color: var(--green); color: var(--color-h6);
} }
table, td, th, table, td, th,
.main table,.main td,.main th { .main table,.main td,.main th {

View File

@ -8,12 +8,24 @@
#+OPTIONS: H:5 #+OPTIONS: H:5
#+STARTUP: showeverything #+STARTUP: showeverything
Welcome! Welcome to my personal website.
#+begin_notes Here I will talk mostly about the life of a software developer.
This website is dark by default, but it support user preferred theme. So programming, functional programming in particular.
For example on Mac OS X > 10.14, if you have selected a light theme, the website
will be light.
#+end_notes
- [[./archive.html][articles]] That website was created with the following constraints in mind by order of
priority:
1. /Respect Privacy/; no tracker of any sort (no google ads, no referrer for all external links,
etc...)
2. /javascript free/; no js at all, yes even the theme changer
3. /disability friendly/; should be easy to read on a text browser so people
with disabilities could easily consume it
4. /nerdy/; should feel mostly like markdown text in a terminal and source code
should be syntax higlighted.
5. /org-mode friendly/; I should write my post in [[https://orgmode.org][org mode]]
6. /user friendly/; support your preferred light/dark theme by default but you
can change it if you want.
7. /frugal/; try to minize the resources needed to visit my website; no
javascript, no webfont, not too much CSS magic, not much images or really
compressed one.

View File

@ -1,406 +1,321 @@
#+TITLE: Static blog with org-mode #+TITLE: Blogging with org-mode
#+SUBTITLE: Meta post about this blog
#+AUTHOR: Yann Esposito #+AUTHOR: Yann Esposito
#+EMAIL: yann@esposito.host #+EMAIL: yann@esposito.host
#+DATE: 2019-07-27 #+DATE: 2019-07-27
#+KEYWORDS: programming, blog, org-mode #+KEYWORDS: programming, blog, org-mode, meta
#+OPTIONS: auto-id:t #+OPTIONS: auto-id:t
#+begin_quote #+begin_quote
/tl;dr/: [[#current-solution][Full code for the impatient↓]]. /tl;dr/: [[#full-solution][Go to the full solution]] for the impatients.
Read the full blog post to have all the details.
#+end_quote #+end_quote
This is the first article using my new blog system. As the first article of my new blogging system, let's have a meta blog post.
Once in a while, I like to change how I blog.
A long time ago, I used PHP for my first website using my personal space at my
university.
And as of today, a clond of my university website still exists:
http://yann.esposito.free.fr It contains all details about my old research
activities and teaching resources for my students.
Once in a while, I create a new personal website.
A long time ago, I used PHP for my first website.
I used include and took care of XHTML pages validation.
Then I used [[http://nanoc.ws][nanoc]], a ruby static website generator. 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 Then I switched to [[https://jaspervdj.be/hakyll/][hakyll]] because I wanted to switch to a Haskell's written
tool. 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? This time 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]].
:PROPERTIES: I used vim a lot, but recently I switched to emacs.
:CUSTOM_ID: why--8eb9 The main reason were:
:END:
Everything started when I was hired in a place where I was given a terrible 1. You use LISP as plugin language while for Vim it is vimscript which is a
keyboard. quite terrible language.
After a few weeks I started to feel a lot of pain in both my wrists. 2. Many comment on the Internet about people telling that [[https://github.com/emacs-evil/evil][evil]] (vim keybdings in
So I started to go from classical IDE to being able to use vim emacs) was really really good. And I can confirm it is.
correctly[fn:vim]. 3. The [[http://spacemacs.org][spacemacs]] project made it easier to handle the emacs configuratin
complexity for a newbie.
Then I started to work in Clojure and I heard that emacs might certainly be a And by doing the switch, I discovered a lot of great emacs packages.
better fit for LISP dialiects. Now I really do not regret the switch.
But, I couldn't switch to an editor without vim keybindings support because they In particular, [[http://orgmode.org][org-mode]] has been a game changer.
are so great once you're used to them. I used to write markdown before.
By chance it was about the same time that [[http://spacemacs.org][spacemacs]] appeared and I switched. But now I use the org format.
Is is really impressive how well the vim keybindings are supported. It is superior for my usage(s).
Even most of the advanced vim features I used to use worked like a charm.
The first benefit of emacs is you can configure emacs with elisp.
Which unlike vimscript looks like a correct language to work with.
One unexpected benefit of emacs was [[http://orgmode.org][org-mode]].
I always heard good things about it but it took me a while to really get it and
to understand why it is so great.
If you don't know anything about org-mode, it is many things.
First imagine a Markdown but more TODO list oriented.
But along with this, emacs has a lot of helper functions to work with those
org-mode files.
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? * How?
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: how--831e :CUSTOM_ID: how
:END: :END:
You can easily follow the org-mode doc to make your own website. You can easily follow the org-mode doc to make your own website.
But I have added a few "goodies". But I have added a few niceties.
First, the code to export is provided in a local file.
I've got a system to auto-load elisp file when entering in a new project.
It is quite safe as it uses PGP and check if I trust the person who encrypted
the file.
See [[file:project-el/index.org][my other post about it]].
- attempt to obfuscate contact email,
** Basic Blog ** Configuration variables
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: basic-blog-a1fc :CUSTOM_ID: configuration-variables
:END: :END:
I put the need minimal code in a =.project.el.gpg= file of my blog repository.
Inspired by this [[https://francismurillo.github.io/2017-02-15-Project-Script-Loader/][blog post]] I enhanced this version to be both more user friendly
and secure (see [[./autoload-emacs-script-by-project.org][Autoload Emacs Script by Project]]).
But even before that, I simply put the code in has an elisp code block of my
=index.org= that I exectued with =C-c C-c=.
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=.
Do not export an org-mode subtree you can simply tag it with =:noexport:=.
=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
A very basic publish rule would be:
#+begin_src org
,* Magic Script :noexport:
,#+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
#+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
** Relative Paths
:PROPERTIES:
:CUSTOM_ID: relative-paths-d145
:END:
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 #+begin_src elisp
(setq base-dir (concat (projectile-project-root) "src")) ;; CONFIGURATION VARIABLES
(setq publish-dir (concat (projectile-project-root) "_site")) (setq domainname "https://her.esy.fun")
(setq org-publish-project-alist (setq base-dir (concat (projectile-project-root) "src"))
`(("orgfiles" (setq publish-dir (concat (projectile-project-root) "_site"))
:base-directory ,base-dir (setq assets-dir (concat base-dir "/"))
:publishing-directory ,publish-dir (setq publish-assets-dir (concat publish-dir "/"))
:recursive t (setq rss-dir base-dir)
:base-extension "org"))) (setq rss-title "Subscribe to articles")
(setq publish-rss-dir publish-dir)
(setq css-path "/css/minimalist.css")
(setq author-name "Yann Esposito")
(setq author-email "yann@esposito.host")
#+end_src #+end_src
That's better. * Full Solution
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.
** Assets
:PROPERTIES:
:CUSTOM_ID: assets-cf7d
:END:
Generally you want a bit more features than that for publishing 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...)
#+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
With that you will copy the assets and export org file to html.
You can do both by selecting `blog` when doing an `org-publish`.
That's better. But for a blog you also generally want to have your own CSS.
* Current Solution
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: current-solution :CUSTOM_ID: full-solution
:END: :END:
#+begin_src elisp #+begin_src elisp
;; Global variables
(setq domainname "https://her.esy.fun")
(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 css-name "minimalist.css")
(setq author-name "Yann Esposito")
(setq author-email "yann@esposito.host")
(require 'org) (require 'org)
(require 'ox-publish) (require 'ox-publish)
(require 'ox-html) (require 'ox-html)
(require 'org-element) (require 'org-element)
(require 'ox-rss) (require 'ox-rss)
(defun org-blog-prepare (project-plist) (setq org-link-file-path-type 'relative)
"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 (defun org-blog-prepare (project-plist)
(concat "With help from `https://github.com/howardabrams/dot-files'.
"<link rel=\"stylesheet\" type=\"text/css\" href=\"/assets/css/" css-name "\"/>" Touch `index.org' to rebuilt it.
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">" Argument `PROJECT-PLIST' contains information about the current project."
"<link rel=\"alternative\" type=\"application/rss+xml\" title=\"Subscribe to articles\" href=\"/archives.xml\" />" (let* ((base-directory (plist-get project-plist :base-directory))
"<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/assets/img/favicon.ico\">")) (buffer (find-file-noselect (expand-file-name "index.org" base-directory) t)))
(with-current-buffer buffer
(set-buffer-modified-p t)
(save-buffer 0))))
(defun menu (lst) (defvar org-blog-head
"Blog menu" (concat
(concat "<link rel=\"stylesheet\" type=\"text/css\" href=\"" css-path "\"/>"
"<navigation class=\"menu\">" "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
(mapconcat 'identity "<link rel=\"alternative\" type=\"application/rss+xml\" title=\"" rss-title "\" href=\"/archives.xml\" />"
(append "<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/favicon.ico\">"))
'("<a href=\"/index.html\">Home</a>"
"<a href=\"/archive.html\">Posts</a>")
lst)
" | ")
"</navigation>"))
(defun str-time-to-year-float (date-str) (defun menu (lst)
(/ (float-time "Blog menu"
(apply 'encode-time (concat
(mapcar (lambda (x) (if (null x) 0 x)) "<navigation>"
(parse-time-string date-str)))) (mapconcat 'identity
(* 365.25 24 60 60))) (append
'("<a href=\"/index.html\">Home</a>"
"<a href=\"/archive.html\">Posts</a>"
"<a href=\"/about-me.html\">About</a>")
lst)
" | ")
"</navigation>"))
(defvar blog-creation-date "2019-07-01")
(defun delta-date (date-str) (defun str-time-to-year-float (date-str)
"Number of year since the begining of this blog" (/ (float-time
(let ((y (- (str-time-to-year-float date-str) (apply 'encode-time
(str-time-to-year-float blog-creation-date)))) (mapcar (lambda (x) (if (null x) 0 x))
(format "∆t=%.2f" y))) (parse-time-string date-str))))
(* 365.25 24 60 60)))
(defun org-blog-preamble (info) (defvar blog-creation-date "2019-07-01")
"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) (defun y-date (date-str)
(let ((i (car (plist-get info k)))) "Number of year since the begining of this blog"
(when (and i (stringp i)) (let ((y (- (str-time-to-year-float date-str)
i))) (str-time-to-year-float blog-creation-date))))
(format "∆t=%.2f" y)))
(defun rand-obfs (c) (defun org-blog-preamble (info)
(let ((r (% (random) 20))) "Pre-amble for whole blog."
(cond ((eq 0 r) (format "%c" c)) (concat
((< 0 r 10) (format "&#%d;" c)) "<div class=\"content\">"
(t (format "&#x%X;" c))))) (menu '("<a href=\"#postamble\">↓ bottom ↓</a>"))
"<h1>"
(format "%s" (car (plist-get info :title)))
"</h1>"
(when-let ((date (get-from-info info :date)))
(format "<span class=\"article-date\">%s</span>" date))
(when-let ((subtitle (car (plist-get info :subtitle))))
(format "<h2>%s</h2>" subtitle))
"</div>"))
(defun obfuscate-html (txt) (defun get-from-info (info k)
(apply 'concat (let ((i (car (plist-get info k))))
(mapcar 'rand-obfs txt))) (when (and i (stringp i))
i)))
(defun org-blog-postamble (info) (defun rand-obfs (c)
"Post-amble for whole blog." (let ((r (% (random) 20)))
(concat (cond ;; ((eq 0 r) (format "%c" c))
"<div class=\"content\">" ((<= 0 r 10) (format "&#%d;" c))
"<footer>" (t (format "&#x%X;" c)))))
(when-let ((author (get-from-info info :author)))
(if-let ((email (plist-get info :email))) (defun obfuscate-html (txt)
(format "<div class=\"author\">Author: <a href=\"%s%s\">%s</a></div>" (apply 'concat
(mapcar 'rand-obfs txt)))
(defun org-blog-postamble (info)
"Post-amble for whole blog."
(concat
"<div class=\"content\">"
;; TODO install a comment system
;; (let ((url (format "%s%s" domainname (replace-regexp-in-string base-dir "" (plist-get info :input-file)))))
;; (format "<a href=\"https://comments.esy.fun/slug/%s\">comment</a>"
;; (url-hexify-string url)))
"<footer>"
(when-let ((author (get-from-info info :author)))
(if-let ((email (plist-get info :email)))
(let* ((obfs-email (obfuscate-html email))
(obfs-author (obfuscate-html author))
(obfs-title (obfuscate-html (get-from-info info :title)))
(full-email (format "%s &lt;%s&gt;" obfs-author obfs-email)))
(format "<div class=\"author\">Author: <a href=\"%s%s%s%s\">%s</a></div>"
(obfuscate-html "mailto:") (obfuscate-html "mailto:")
(obfuscate-html email) full-email
(obfuscate-html author)) (obfuscate-html "?subject=yblog: ")
(format "<div class=\"author\">Author: %s</div>" author))) obfs-title
(when-let ((date (get-from-info info :date))) full-email))
(format "<div class=\"date\">Created: %s (%s)</div>" date (delta-date date))) (format "<div class=\"author\">Author: %s</div>" author)))
(when-let ((keywords (plist-get info :keywords))) (when-let ((date (get-from-info info :date)))
(format "<div class=\"keywords\">Keywords: <code>%s</code></div>" keywords)) (format "<div class=\"date\">Created: %s (%s)</div>" date (y-date date)))
(format "<div class=\"date\">Generated: %s</div>" (when-let ((keywords (plist-get info :keywords)))
(format-time-string "%Y-%m-%d %H:%M:%S")) (format "<div class=\"keywords\">Keywords: <code>%s</code></div>" keywords))
(format (concat "<div class=\"creator\"> Generated with " (format "<div class=\"date\">Generated: %s</div>"
"<a href=\"https://www.gnu.org/software/emacs/\" target=\"_blank\" rel=\"noopener noreferrer\">Emacs %s</a>, " (format-time-string "%Y-%m-%d %H:%M:%S"))
"<a href=\"http://spacemacs.org\" target=\"_blank\" rel=\"noopener noreferrer\">Spacemacs %s</a>, " (format (concat "<div class=\"creator\"> Generated with "
"<a href=\"http://orgmode.org\" target=\"_blank\" rel=\"noopener noreferrer\">Org Mode %s</a>" "<a href=\"https://www.gnu.org/software/emacs/\" target=\"_blank\" rel=\"noopener noreferrer\">Emacs %s</a>, "
"</div>") "<a href=\"http://spacemacs.org\" target=\"_blank\" rel=\"noopener noreferrer\">Spacemacs %s</a>, "
emacs-version spacemacs-version org-version) "<a href=\"http://orgmode.org\" target=\"_blank\" rel=\"noopener noreferrer\">Org Mode %s</a>"
"</footer>" "</div>")
(menu '("<a href=\"#preamble\">↑ Top ↑</a>")) emacs-version spacemacs-version org-version)
"</div>")) "</footer>"
(menu '("<a href=\"#preamble\">↑ Top ↑</a>"))
"</div>"))
(defun org-blog-sitemap-format-entry (entry _style project) (defun org-blog-sitemap-format-entry (entry _style project)
"Return string for each ENTRY in PROJECT." "Return string for each ENTRY in PROJECT."
(when (s-starts-with-p "posts/" entry) (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 "@@html:<span class=\"archive-item\"><span class=\"archive-date\">@@ %s: @@html:</span>@@ [[file:%s][%s]] @@html:</span>@@"
(format-time-string "%Y-%m-%d" (format-time-string "%Y-%m-%d"
(org-publish-find-date entry project)) (org-publish-find-date entry project))
entry entry
(org-publish-find-title entry project)))) (org-publish-find-title entry project))))
(defun org-blog-sitemap-function (title list) (defun org-blog-sitemap-function (title list)
"Return sitemap using TITLE and LIST returned by `org-blog-sitemap-format-entry'." "Return sitemap using TITLE and LIST returned by `org-blog-sitemap-format-entry'."
(concat "#+TITLE: " title "\n" (concat "#+TITLE: " title "\n"
"#+AUTHOR: " author-name "\n" "#+AUTHOR: " author-name "\n"
"#+EMAIL: " author-email "\n" "#+EMAIL: " author-email "\n"
"\n#+begin_archive\n" "\n#+begin_archive\n"
(mapconcat (lambda (li) (mapconcat (lambda (li)
(format "@@html:<li>@@ %s @@html:</li>@@" (car li))) (format "@@html:<li>@@ %s @@html:</li>@@" (car li)))
(seq-filter #'car (cdr list)) (seq-filter #'car (cdr list))
"\n") "\n")
"\n#+end_archive\n")) "\n#+end_archive\n"))
(setq org-publish-project-alist (defun org-blog-publish-to-html (plist filename pub-dir)
`(("orgfiles" "Same as `org-html-publish-to-html' but modifies html before finishing."
:base-directory ,base-dir (let ((file-path (org-html-publish-to-html plist filename pub-dir)))
:exclude ".*drafts/.*" (with-current-buffer (find-file-noselect file-path)
:base-extension "org" (goto-char (point-min))
(search-forward "<body>")
(insert (mapconcat 'identity
'("<input type=\"radio\" id=\"light\" name=\"theme\"/>"
"<input type=\"radio\" id=\"dark\" name=\"theme\"/>"
"<div id=\"labels\">"
"<div class=\"content\">"
"<label for=\"light\">Light Theme</label>"
" / "
"<label for=\"dark\">Dark Theme</label>"
"</div>"
"</div>"
"<div class=\"main\">")
"\n"))
(goto-char (point-max))
(search-backward "</body>")
(insert "\n</div>\n")
(save-buffer)
(kill-buffer))
file-path))
:publishing-directory ,publish-dir (setq org-publish-project-alist
`(("orgfiles"
:base-directory ,base-dir
:exclude ".*drafts/.*"
:base-extension "org"
:publishing-directory ,publish-dir
:recursive t :recursive t
:preparation-function org-blog-prepare :preparation-function org-blog-prepare
:publishing-function org-html-publish-to-html :publishing-function org-blog-publish-to-html
:with-toc nil :with-toc nil
:with-title nil :with-title nil
:with-date t :with-date t
:section-numbers nil :section-numbers nil
:html-doctype "html5" :html-doctype "html5"
:html-html5-fancy t :html-html5-fancy t
:html-head-include-default-style nil :html-head-include-default-style nil
:html-head-include-scripts nil :html-head-include-scripts nil
:htmlized-source t :htmlized-source t
:html-head-extra ,org-blog-head :html-head-extra ,org-blog-head
:html-preamble org-blog-preamble :html-preamble org-blog-preamble
:html-postamble org-blog-postamble :html-postamble org-blog-postamble
:auto-sitemap t :auto-sitemap t
:sitemap-filename "archive.org" :sitemap-filename "archive.org"
:sitemap-title "Blog Posts" :sitemap-title "Blog Posts"
:sitemap-style list :sitemap-style list
:sitemap-sort-files anti-chronologically :sitemap-sort-files anti-chronologically
:sitemap-format-entry org-blog-sitemap-format-entry :sitemap-format-entry org-blog-sitemap-format-entry
:sitemap-function org-blog-sitemap-function) :sitemap-function org-blog-sitemap-function)
("assets" ("assets"
:base-directory ,assets-dir :base-directory ,assets-dir
:base-extension ".*" :base-extension ".*"
:publishing-directory ,publish-assets-dir :exclude ".*\.org$"
:publishing-function org-publish-attachment :publishing-directory ,publish-assets-dir
:recursive t) :publishing-function org-publish-attachment
:recursive t)
("rss" ("rss"
:base-directory ,rss-dir :base-directory ,rss-dir
:base-extension "org" :base-extension "org"
:html-link-home ,domainname :html-link-home ,domainname
:html-link-use-abs-url t :html-link-use-abs-url t
:rss-extension "xml" :rss-extension "xml"
:publishing-directory ,publish-rss-dir :publishing-directory ,publish-rss-dir
:publishing-function (org-rss-publish-to-rss) :publishing-function (org-rss-publish-to-rss)
:exclude ".*" :exclude ".*"
:include ("archive.org") :include ("archive.org")
:section-numbers nil :section-numbers nil
:table-of-contents nil) :table-of-contents nil)
("blog" :components ("orgfiles" "assets" "rss")))) ("blog" :components ("orgfiles" "assets" "rss"))))
;; add target=_blank and rel="noopener noreferrer" to all links by default ;; 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) (defun my-org-export-add-target-blank-to-http-links (text backend info)
"Add target=\"_blank\" to external links." "Add target=\"_blank\" to external links."
(when (and (when (and
(org-export-derived-backend-p backend 'html) (org-export-derived-backend-p backend 'html)
(string-match "href=\"http[^\"]+" text) (string-match "href=\"http[^\"]+" text)
(not (string-match "target=\"" text)) (not (string-match "target=\"" text))
(not (string-match (concat "href=\"" domainname "[^\"]*") text))) (not (string-match (concat "href=\"" domainname "[^\"]*") text)))
(string-match "<a " text) (string-match "<a " text)
(replace-match "<a target=\"_blank\" rel=\"noopener noreferrer\" " nil nil text))) (replace-match "<a target=\"_blank\" rel=\"noopener noreferrer\" " nil nil text)))
(add-to-list 'org-export-filter-link-functions (add-to-list 'org-export-filter-link-functions
'my-org-export-add-target-blank-to-http-links) 'my-org-export-add-target-blank-to-http-links)
#+end_src #+end_src