#+title: Org static blog
#+property: header-args lexical: t :tangle yes :comments yes :results silent :shebang ";; -*- lexical-binding: t -*-"  :tangle-mode (identity #o644)
#+auto_tangle: t

* Vars and setup
  #+begin_src emacs-lisp
    (jao-load-path "org-static-blog")
    (when (> emacs-major-version 26) (use-package htmlize :ensure t))
    (defvar jao-blog-base-dir "~/doc/jao.io")
    (defun jao-blog-dir (p) (expand-file-name p jao-blog-base-dir))

    (setq jao-org-blog-tag-files
          (seq-difference (directory-files (jao-blog-dir "blog") nil "tag-.*")
                          "tag-norss.html")

          jao-org-blog-tags
          (mapcar (lambda (f)
                    (string-match "tag-\\(.+\\)\\.html" f)
                    (format "<a href=\"/blog/%s\">%s</a>"
                            f (match-string 1 f)))
                  jao-org-blog-tag-files)

          jao-org-blog-tag-rss
          (mapcar (lambda (f)
                    (string-match "\\(.+\\)-rss\\.xml" f)
                    (format "<a href=\"/blog/%s\">%s</a>"
                            f (match-string 1 f)))
                  (directory-files (jao-blog-dir "blog") nil ".*-rss.xml"))

          jao-org-blog-tag-names
          (mapcar (lambda (f)
                    (string-match "tag-\\(.+\\)\\.html" f)
                    (match-string 1 f))
                  jao-org-blog-tag-files))
  #+end_src
* HTML headers and footers
*** Header
    #+begin_src emacs-lisp
      (setq org-static-blog-page-header
            (concat
             "<meta name=\"author\" content=\"jao\">\n"
             "<meta name=\"referrer\" content=\"no-referrer\">\n"
             "<link rel=\"stylesheet\" href=\"/static/style.css\""
             "  type=\"text/css\">\n"
             "<link rel=\"apple-touch-icon\" sizes=\"180x180\""
             "  href=\"/static/apple-touch-icon.png\" >\n"
             "<link rel=\"icon\" type=\"image/png\""
             "  sizes=\"32x32\" href=\"/static/favicon-32x32.png\">\n"
             "<link rel=\"icon\" type=\"image/png\""
             "  sizes=\"16x16\" href=\"/static/favicon-16x16.png\">\n"
             "<link rel=\"icon\" href=\"/static/favicon.ico\">\n"
             "<link rel=\"manifest\" href=\"/static/site.webmanifest\">\n")

            org-static-blog-page-preamble
            (concat
             "<div class=\"header\">"
             "  <a href=\"https://jao.io\">programming (and other) musings</a>"
             "  <div class=\"sitelinks\">"
             "    <a href=\"/blog/about.html\">about</a>"
             "    | <a href=\"/blog/hacking.html\">hacking</a>"
             "    | <a href=\"/blog/archive.html\">archive</a>"
             "    | <div class=\"dropdown\">"
             "       <a href=\"/blog/tags.html\" class=\"dropbtn\">tags</a>"
             "       <div class=\"dropdown-content\">"
             (mapconcat #'identity jao-org-blog-tags "")
             "       </div>"
             "      </div>"
             "    | <div class=\"dropdown\">"
             "       <a href=\"/blog/rss.xml\" class=\"dropbtn\">rss</a>"
             "       <div class=\"dropdown-content\">"
             (mapconcat #'identity jao-org-blog-tag-rss "")
             "       </div>"
             "      </div>"
             "  </div>"
             "</div>"))
    #+end_src
*** Footer
      #+begin_src html :tangle ~/.emacs.d/commons.html :comments no :shebang ""
      <center>
        <a rel="license" href="https://creativecommons.org/licenses/by-sa/3.0/">
        <img alt="Creative Commons License" style="border-width:0"
             src="https://i.creativecommons.org/l/by-sa/3.0/88x31.png" />
        </a>
        <br />
        <span xmlns:dct="https://purl.org/dc/terms/"
              href="https://purl.org/dc/dcmitype/Text" property="dct:title"
              rel="dct:type">jao.io</span> by
        <a xmlns:cc="https://creativecommons.org/ns#" href="https://jao.io"
           property="cc:attributionName" rel="cc:attributionURL">jao</a>
        is licensed under a
        <a rel="license" href="https://creativecommons.org/licenses/by-sa/3.0/">
          Creative Commons Attribution-ShareAlike 3.0 Unported License</a>.
      </center>
      #+end_src

      #+begin_src emacs-lisp
        (setq org-static-blog-page-postamble
              (with-temp-buffer
                (insert-file-contents "~/.emacs.d/commons.html")
                (buffer-string)))
      #+end_src
* Package
  #+begin_src emacs-lisp
    (use-package org-static-blog
      :ensure t
      :init
      (setq org-static-blog-use-preview t
            org-static-blog-preview-link-p t
            org-static-blog-preview-start "<!-- preview-start -->"
            org-static-blog-preview-end "<!-- preview-end -->"
            org-static-blog-preview-date-first-p t
            org-static-blog-index-length 30
            org-static-blog-preview-convert-titles t
            org-static-blog-preview-ellipsis "more ..."
            org-static-blog-enable-tags t
            org-static-blog-tags-file "tags.html"
            org-static-blog-rss-file "rss.xml"
            org-static-blog-publish-url "https://jao.io/blog/"
            org-static-blog-publish-title "programming (and other) musings"
            org-static-blog-posts-directory (jao-blog-dir "posts/")
            org-static-blog-drafts-directory (jao-blog-dir "pages/")
            org-static-blog-publish-directory (jao-blog-dir "blog/")
            org-static-blog-rss-extra "" ; "<author>mail@jao.io</author>\n"
            org-static-blog-rss-max-entries 30
            org-static-blog-rss-excluded-tag "norss"
            org-static-blog-enable-tag-rss t
            org-export-with-toc nil
            org-export-with-section-numbers nil)

      :config
      (defun jao-org-static-post-path (pf dt)
        (cond ((string-match-p "pages/.*\\|in-no-particular-order" pf)
               (file-name-nondirectory pf))
              ((string-match-p "drafts/.*" pf) pf)
              ((string-match-p "^[[:digit:]]+-.*" pf) pf)
              (t (concat (format-time-string "%Y-%m-%d-" dt)
                         (file-name-nondirectory pf)))))
      (advice-add 'org-static-blog-generate-post-path :override
                  #'jao-org-static-post-path)

      :bind (("H-s-b" . jao-hydra-org-blog/body)
             :map org-mode-map (("C-c B" . jao-hydra-org-blog/body))))
   #+end_src
* Commands
*** New entries
    #+begin_src emacs-lisp
      (defun jao-org-blog-publish-file (fname)
        (interactive (list (read-file-name "Publish: "
                                           nil
                                           (buffer-file-name)
                                           t
                                           (buffer-file-name))))
        (let ((geiser-active-implementations '(guile))
              (geiser-default-implementation 'guile))
          (org-static-blog-publish-file fname)))

      (defconst jao-org-static-blog--prev-beg
        "#+begin_export html\n    <!-- preview-start -->\n#+end_export ")

      (defconst jao-org-static-blog--prev-end
        "#+begin_export html\n    <!-- preview-end -->\n#+end_export ")

      (defun jao-org-static-blog-create-new-post (&optional draft)
        (interactive)
        (let* ((title (read-string "Title: "))
               (file (replace-regexp-in-string "\s" "-" (downcase title)))
               (tags (completing-read-multiple "Tags: " jao-org-blog-tag-names)))
          (find-file (expand-file-name (concat file ".org")
                                       (if draft
                                           org-static-blog-drafts-directory
                                         org-static-blog-posts-directory)))
          (insert "#+title: " title "\n"
                  "#+date: " (format-time-string "<%Y-%m-%d %H:%M>") "\n"
                  "#+filetags: "
                  (mapconcat #'identity tags " ")
                  "\n\n")
          (when (member "books" tags)
            (insert jao-org-static-blog--prev-beg
                    "\n\n[[https://jao.io/img/" file ".jpg]]\n\n"))
          (save-excursion (insert jao-org-static-blog--prev-end "\n"))))
    #+end_src
*** Drafts
    #+begin_src emacs-lisp
      (defun jao-org-static-blog-update-date ()
        (interactive)
        (when (y-or-n-p "Update date? ")
          (goto-char (point-min))
          (when (re-search-forward "^#\\+date: " nil t)
            (let ((kill-whole-line nil)) (kill-line))
            (insert (format-time-string "<%Y-%m-%d %H:%M>"))
            (save-buffer))))

      (defun jao-org-static-blog-create-new-draft ()
        (interactive)
        (jao-org-static-blog-create-new-post t))

      (defun jao-org-static-blog-publish-draft ()
        (interactive)
        (let* ((from (read-file-name "Post: "
                                     org-static-blog-drafts-directory
                                     nil t))
               (to (expand-file-name (file-name-nondirectory from)
                                     org-static-blog-posts-directory)))
          (rename-file from to)
          (when-let ((b (get-buffer from)))
            (kill-buffer b))
          (find-file to)
          (jao-org-static-blog-update-date)
          (when (y-or-n-p "Generate HTML? ")
            (jao-org-blog-publish))))

      (defun jao-org-static-blog-edit-draft ()
        (interactive)
        (find-file (read-file-name "Edit: "
                                   org-static-blog-drafts-directory
                                   nil
                                   t)))
    #+end_src
*** Publish
    #+begin_src emacs-lisp
      (defun jao-org-blog-publish (&optional force)
          (interactive "P")
          (let ((geiser-active-implementations '(guile))
                (geiser-default-implementation 'guile))
            (org-static-blog-publish force)))

      (defun jao-org-blog-republish ()
        (interactive)
        (jao-org-blog-publish t))
    #+end_src
* Hydras
  #+begin_src emacs-lisp
    (pretty-hydra-define jao-hydra-org-blog (:color blue :quit-key "q")
      ("Insert"
       (("s" (insert jao-org-static-blog--prev-beg) "preview begin")
        ("S" (insert jao-org-static-blog--prev-end) "preview end")
        ("t" jao-org-static-blog-update-date "update date"))
       "Edit"
       (("n" jao-org-static-blog-create-new-post "create post")
        ("d" jao-org-static-blog-create-new-draft "create draft")
        ("e" jao-org-static-blog-edit-draft "edit draft"))
       "Publish"
       (("D" jao-org-static-blog-publish-draft "publish draft")
        ("f" jao-org-blog-publish-file "publish single file")
        ("p" jao-org-blog-publish "publish all")
        ("r" jao-org-blog-republish "republish"))))

    (major-mode-hydra-define+ org-mode nil
      ("Utilities" (("b" jao-hydra-org-blog/body "Blog ops"))))
  #+end_src