diff --git a/.project.el.gpg b/.project.el.gpg deleted file mode 100644 index c7f837c..0000000 Binary files a/.project.el.gpg and /dev/null differ diff --git a/project.el b/project.el new file mode 100644 index 0000000..39587b6 --- /dev/null +++ b/project.el @@ -0,0 +1,270 @@ +(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 "/")) +(setq publish-assets-dir (concat publish-dir "/")) +(setq rss-dir base-dir) +(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") + +(require 'org) +(require 'ox-publish) +(require 'ox-html) +(require 'org-element) +(require 'ox-rss) + +(setq org-link-file-path-type 'relative) +(setq org-publish-timestamp-directory + (concat (projectile-project-root) "_cache/")) + +(defvar org-blog-head + (concat + "" + "" + "" + "")) + +(defun menu (lst) + "Blog menu" + (concat + "" + (mapconcat 'identity + (append + '("Home" + "Posts" + "About") + 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 get-from-info (info k) + (let ((i (car (plist-get info k)))) + (when (and i (stringp i)) + i))) + +(defun org-blog-preamble (info) + "Pre-amble for whole blog." + (concat + "
" + (menu '("↓ bottom ↓")) + "

" + (format "%s" (car (plist-get info :title))) + "

" + (when-let ((date (plist-get info :date))) + (format "%s" + (format-time-string "%Y-%m-%d" + (org-timestamp-to-time + (car date))))) + (when-let ((subtitle (car (plist-get info :subtitle)))) + (format "

%s

" subtitle)) + "
")) + +(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 + "
" + ;; TODO install a comment system + ;; (let ((url (format "%s%s" domainname (replace-regexp-in-string base-dir "" (plist-get info :input-file))))) + ;; (format "comment" + ;; (url-hexify-string url))) + "" + (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 (concat "@@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: " author-name "\n" + "#+EMAIL: " author-email "\n" + "\n#+begin_archive\n" + (mapconcat (lambda (li) + (format "@@html:
  • @@ %s @@html:
  • @@" (car li))) + (seq-filter #'car (cdr list)) + "\n") + "\n#+end_archive\n")) + +(defun org-blog-publish-to-html (plist filename pub-dir) + "Same as `org-html-publish-to-html' but modifies html before finishing." + (let ((file-path (org-html-publish-to-html plist filename pub-dir))) + (with-current-buffer (find-file-noselect file-path) + (goto-char (point-min)) + (search-forward "") + (insert (mapconcat 'identity + '("" + "" + "" + "" + "" + "
    " + "
    " + "Change theme: " + "" + "(," + " )" + " / " + "" + "()" + "
    " + "
    " + "
    ") + "\n")) + (goto-char (point-max)) + (search-backward "") + (insert "\n
    \n") + (save-buffer) + (kill-buffer)) + file-path)) + +(defun org-blog-publish-attachment (plist filename pub-dir) + "Publish a file with no transformation of any kind. +FILENAME is the filename of the Org file to be published. PLIST +is the property list for the given project. PUB-DIR is the +publishing directory. +Take care of minimizing the pictures using imagemagick. +Return output file name." + (unless (file-directory-p pub-dir) + (make-directory pub-dir t)) + (or (equal (expand-file-name (file-name-directory filename)) + (file-name-as-directory (expand-file-name pub-dir))) + (let ((dst-file (expand-file-name (file-name-nondirectory filename) pub-dir))) + (if (string-match-p ".*\\.\\(png\\|jpg\\|gif\\)$" filename) + (shell-command (format "~/.nix-profile/bin/convert %s -resize 800x800\\> +dither -colors 16 -depth 4 %s" + filename + dst-file)) + (copy-file filename dst-file t))))) + +(setq org-publish-project-alist + `(("orgfiles" + :base-directory ,base-dir + :exclude ".*drafts/.*" + :base-extension "org" + :publishing-directory ,publish-dir + + :recursive t + :publishing-function org-blog-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 ".*" + :exclude ".*\.org$" + :publishing-directory ,publish-assets-dir + :publishing-function org-blog-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 " img { + margin: 0px; +} li { position: relative; display: block; diff --git a/src/index.org b/src/index.org index 374f179..b98e3a6 100644 --- a/src/index.org +++ b/src/index.org @@ -24,12 +24,12 @@ priority: 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. + should be syntax highlighted. 5. *user friendly*; support your preferred light/dark theme by default but you can change it if you want. 6. *rss*; you should be able to get informed when I add a new blog post. -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 +7. *frugal*; try to minimize the resources needed to visit my website; no + javascript, no web-font, not too much CSS magic, not much images or really compressed one. I talk about more details [[file:./posts/new-blog.org][here]]. diff --git a/src/posts/project-el/auto-load-project.el b/src/posts/project-el/auto-load-project.el deleted file mode 100644 index 33458da..0000000 --- a/src/posts/project-el/auto-load-project.el +++ /dev/null @@ -1,71 +0,0 @@ -;;; auto-load-project.el --- Auto load elisp file on project open - -;; Copyright © 2019 Yann Esposito - -;;; Commentary: -;; - -;;; Code: - -(provide :auto-load-project) - -(require 'projectile) - -(defvar y/trusted-gpg-key-fingerprints - '("448E9FEF4F5B86DE79C1669B0000000000000000") - "The list of GPG fingerprint you trust when decrypting a gpg file. -You can retrieve the fingerprints of your own private keys -with: `gpg --list-secret-keys' (take care of removing the -spaces when copy/pasting here)") - -(defun y/get-encryption-key (file) - "Return the fingerprint of they key that encrypted FILE." - (string-trim-right - (shell-command-to-string - (concat - "gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null" - "|grep DECRYPTION_KEY" - "|awk '{print $4}'")))) - -(defun y/trusted-gpg-origin-p (file) - "Return non-nil if the FILE is encrypted with a trusted key." - (member (y/get-encryption-key file) y/trusted-gpg-key-fingerprints)) - - -(defconst y/project-file ".project.el.gpg" - "Project configuration file name.") - -(defun y/init-project-el-auto-load () - "Initialize the autoload of .project.el.gpg for projects." - - (with-eval-after-load 'projectile - - (defvar y/loaded-projects (list) - "Projects that have been loaded by `y/load-project-file'.") - - (defun y/load-project-file () - "Loads the `y/project-file' for a project. This is run once - after the project is loaded signifying project setup." - (interactive) - (when (projectile-project-p) - (lexical-let* ((current-project-root (projectile-project-root)) - (project-init-file (expand-file-name y/project-file current-project-root))) - (when (and (not (member current-project-root y/loaded-projects)) - (file-exists-p project-init-file) - (y/trusted-gpg-origin-p project-init-file)) - (message "Loading project init file for %s" (projectile-project-name)) - (condition-case ex - (progn (load project-init-file) - (add-to-list 'y/loaded-projects current-project-root) - (message "%s loaded successfully" project-init-file)) - ('error - (message - "There was an error loading %s: %s" - project-init-file - (error-message-string ex)))))))) - (add-hook 'find-file-hook #'y/load-project-file t) - (add-hook 'dired-mode-hook #'y/load-project-file t))) - -(provide 'auto-load-project) - -;;; auto-load-project.el ends here diff --git a/src/posts/project-el/index.org b/src/posts/project-el/index.org index 97466fa..a606a4c 100644 --- a/src/posts/project-el/index.org +++ b/src/posts/project-el/index.org @@ -26,38 +26,38 @@ centralised place in your =~/.emacs.d/.init.el= file. The main principle is quite simple. -1. When finding a new file in a project, check for the presence of a - =.project.el.gpg= file. -2. Check the file was encrypted with by a trusted fingerprint -3. load the file +1. When opening a new file in a project, check for the presence of a + =project.el= and =project.el.sig= file at the root directory of the + project. +2. Check the file was signed with by a trusted fingerprint. +3. Load the file. -I found a solution that asked you each time you enter in a project if you trust -it. +Other solutions I found on the internet asked you each time you enter in a +project if you trust the file or not. This was both quite annoying and insecure as it is kind of easy to type 'y' -instead of 'n' and to load an untrusted 3rd party script. +instead of 'n' and to load 3rd party script. -With emacs epa, opening encrypted gpg files is seamless. -But here we not only want to open the gpg file but also check we trust the -person that did it. - -Note that checking who's encrypted a gpg file is not straightforward: +Note that checking who signed a file with an external signature is not as +straightforward as it should be: #+begin_src elisp -(defun y/get-encryption-key (file) - "given a gpg encrypted file, returns the fingerprint of they - key that encrypted it" +(defun auto-load-project/get-sign-key (file) + "Return the fingerprint of they key that signed FILE. + +To sign a file you should used + +`gpg --local-user my@email --output project.el.sig --detach-sign project.el`" (string-trim-right (shell-command-to-string (concat - "gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null" - "|grep DECRYPTION_KEY" - "|awk '{print $4}'")))) + (format "gpg --status-fd 1 --verify %s.sig %s 2>/dev/null " file file) + "|grep VALIDSIG" + "|awk '{print $3}'")))) #+end_src - The `--status-fd` should provide more script friendly output. GPG provide localized output by default which are therefore hard to use in script (for grep for example). -- we do not want to decrypt to a file so we redirect the output to =/dev/null= We use =projectile= to detect the project-root and when we are in a new project. Unfortunately the =projectile-after-switch-project-hooks= doesn't work as I @@ -67,69 +67,38 @@ file. In order not to load the code each time, I need to keep a local state of project already loaded. +So now, each time I modify the =project.el= I sign it with the following +command line: + +#+begin_src bash +gpg --local-user my@email --output project.el.sig --detach-sign project.el +#+end_src + * Solution :PROPERTIES: :CUSTOM_ID: solution :END: -Dowload the code [[file:auto-load-project.el][auto-load-project.el]] +The project is hosted here: https://gitlab.esy.fun/yogsototh/auto-load-project-el + +You can setup the emacs package in spacemacs with: #+begin_src elisp - (defun y/init-project-el-auto-load () - "Initialize the autoload of .project.el.gpg for projects" - - (with-eval-after-load 'projectile - - (defvar y/trusted-gpg-key-fingerprints - '("448E9FEF4F5B86DE79C1669B0000000000000000") - "The list of GPG fingerprint you trust when decrypting a gpg file. - You can retrieve the fingerprints of your own private keys - with: `gpg --list-secret-keys' (take care of removing the - spaces when copy/pasting here)") - - (defun y/get-encryption-key (file) - "given a gpg encrypted file, returns the fingerprint of - they key that encrypted it" - (string-trim-right - (shell-command-to-string - (concat - "gpg --status-fd 1 --decrypt -o /dev/null " file " 2>/dev/null" - "|grep DECRYPTION_KEY" - "|awk '{print $4}'")))) - - (defun y/trusted-gpg-origin-p (file) - "Returns true if the file is encrypted with a trusted key" - (member (y/get-encryption-key file) y/trusted-gpg-key-fingerprints)) - - - (defconst y/project-file ".project.el.gpg" - "Project configuration file name.") - - (defvar y/loaded-projects (list) - "Projects that have been loaded by `y/load-project-file'.") - - (defun y/load-project-file () - "Loads the `y/project-file' for a project. This is run once - after the project is loaded signifying project setup." - (interactive) - (when (projectile-project-p) - (lexical-let* ((current-project-root (projectile-project-root)) - (project-init-file (expand-file-name y/project-file current-project-root))) - (when (and (not (member current-project-root y/loaded-projects)) - (file-exists-p project-init-file) - (y/trusted-gpg-origin-p project-init-file)) - (message "Loading project init file for %s" (projectile-project-name)) - (condition-case ex - (progn (load project-init-file) - (add-to-list 'y/loaded-projects current-project-root) - (message "%s loaded successfully" project-init-file)) - ('error - (message - "There was an error loading %s: %s" - project-init-file - (error-message-string ex)))))))) - (add-hook 'find-file-hook #'y/load-project-file t) - (add-hook 'dired-mode-hook #'y/load-project-file t))) - - (y/init-project-el-auto-load) + ;; ... + dotspacemacs-additional-packages + '((auto-load-project :location + (recipe + :fetcher git + :url "https://gitlab.esy.fun/yogsototh/auto-load-project-el" + :files ("auto-load-project.el")))) + ;; ... + (defun dotspacemacs/user-config () + ;; ... + (require 'auto-load-project) + (setq auto-load-project/trusted-gpg-key-fingerprints + '("0000000000000000000000000000000000000000" ;; figerprint of trusted key 1 + "1111111111111111111111111111111111111111" + "2222222222222222222222222222222222222222" + ))) + ;; ... #+end_src diff --git a/src/posts/troll-2.org b/src/posts/troll-2/index.org similarity index 65% rename from src/posts/troll-2.org rename to src/posts/troll-2/index.org index df754d2..0293f18 100644 --- a/src/posts/troll-2.org +++ b/src/posts/troll-2/index.org @@ -11,30 +11,31 @@ #+STARTUP: showeverything #+begin_notes -I watched what might be the most failed movie of all time and I still -enjoyed greatly the show. +I watched what may be the worse movie of all time and I still enjoyed +greatly the show. #+end_notes -I recently wanted to watch again an horror teen movie I watched when I was -a young boy: Troll. -But I discovered there were a movie called Troll 2. -I guessed that it must be the following one. +I wanted to watch an horror teen movie I saw when I was a kid; Troll. +During my searches, I discovered there is another movie named "Troll 2". +I thought that it is certainly the sequel. -Oh god, I saw the imdb note was, really, really bad. -But who cares, I watched it. - -And here is a resume of my experience: +I took a look to the imdb note, and it was very bad (2.9 / 10). +But, hey, I decided to watch it. +Here is an overview of my watching experience. * The watching :PROPERTIES: :CUSTOM_ID: the-watching :END: -Troll 2 synopsis is a family that goes camping to Nilbog. -The son of the family see his dead grandfather that warn him against -Goblins that make you eat things that transform you and then they eat you. -Of course nobody listen to the warn of the boy, the family goes to Nilbog -which is of course infested by Goblins. +The synopsis of Troll 2 is a family living in a city that exchange its +house to go to the village of Nilbog "to live like farmers"... +The son of the family see his dead grandfather. +The grandpa warn his grandson against Goblins. +Those creatures make you eat green things that transform you and then they +eat you. +Of course, nobody listen to the boy that does not want to go to Nilbog, +which is infested by goblins that can take Human appearance. Troll 2 is bad on most criterion you use to measure the quality of a movie. @@ -51,7 +52,7 @@ So much you can only find them not terrorizing but funny and ridiculous. Soon after that, you realize the acting of all actors is extremely bad. In fact, it is so bad, you might not believe me how bad it is. To give you an idea, the only equal bad acting I ever witnessed was while -looking at amateurs first Youtube movie trying to follow a scenario. +looking at amateurs first Youtube movies trying to follow a scenario. Apparently all actor were amateurs, it was their first and last movie. #+CAPTION: One particularly terrible acting scene @@ -66,7 +67,7 @@ dialog. The scenario is terrible. For example, most of the thing occurring suffer from terrible plot holes or terrible mistakes that make everything hard to believe even if you accept -the premises of a world were Goblin would exists. +the premises of a world were Goblins would exist. For example, the grandfather ghost can stop time for 30 seconds for no reason at all. @@ -78,7 +79,8 @@ I forgot to give a word about the music. It is like the director choose the worst music to go along each scene. The first ending is really, quite surprising. -They beat the monsters with, what I believe was a failed attempt at humor. +They win against the monsters with, what I believe was a failed attempt at +humor. It misses the point so bad, that the irony still make it funny. #+CAPTION: Our hero save the day by urinating on the table. His family is frozen for 30s said grandpa, they were for 70s. @@ -91,8 +93,7 @@ To let you close the experience in awe. But there is a bonus, the cherry on the cake. During all the movie, it is *never* question of a Troll at all. -The monsters are all Goblin, not Troll. -I think the word "troll" is not pronounced once in the movie. +*There is not Troll in Troll 2*. Still, it was quite entertaining. @@ -104,10 +105,12 @@ Still, it was quite entertaining. :END: What is really interesting though is that I really enjoyed the watch. -It was so bad, that in the end, you do not really enter in the movie, but -really just analyze the movie, and you start to see how much the movie is bad. -And this is so fun. -For example, there are some attempts at humor, but almost all of them fail terribly. +It was so bad, that can't enter in the movie. +You analyze the movie, and see all mistakes and notice everything wrong +about it. +And this is really fun. +For example, there are some attempts at humor, but almost all of them fail +terribly. And so, you can laugh at how much it failed. Once going to imdb, I discovered I wasn't alone in loving that terrible @@ -126,13 +129,16 @@ None of them spoke English either. #+begin_quote “The cast had few experienced actors, and was primarily assembled from -residents of nearby towns who responded to an open casting call. George -Hardy was a dentist with no acting experience who showed up for fun, hoping -to be cast as an extra, only to be given one of the film’s largest speaking -roles. Don Packard, who played the store owner, was actually a resident at -a nearby mental hospital, and was cast for—and filmed—his role while on a -day trip; after recovering and being released from the hospital, he -recalled that he had no idea what was happening around him, and that his -disturbed “performance” in the film was not acting” +residents of nearby towns who responded to an open casting call. + +George Hardy was a dentist with no acting experience who showed up for fun, +hoping to be cast as an extra, only to be given one of the film’s largest +speaking roles. + +Don Packard, who played the store owner, was actually a resident at a +nearby mental hospital, and was cast for—and filmed—his role while on a day +trip; after recovering and being released from the hospital, he recalled +that he had no idea what was happening around him, and that his disturbed +“performance” in the film was not acting”. #+end_quote