Цифровой садик - приветственная

Цифровой садик - приветственная | Полный список всего, что тут есть | RSS | Подписаться через follow.it

26.03.2024

org-static-blog

Генератор статического блога, который я сейчас использую для своего публичного цифрового сада.

Плюсы вполне очевидны:

Из минусов:

И конечно, жаль, что не умеет вычищать ссылки на то, чего на сайте нет. Вероятно, это плохо совместимо с простотой и скоростью.

Забавно, что ссылку на список всех постов оказалось удобно засунуть в org-static-blog-post-comments. Для комментариев я всё равно ничего не использую, а если класть в org-static-blog-page-postamble, то она во многих местах дублирует уже существующую. Тоже ничего страшного, но зачем, если можно обойтись без этого.

Мои настройки, доработки и наблюдения

  • пробел после даты мешает сортировке по дате

Временами почему-то жалуется на то, что нет запрошенного бэкенда для экспорта

Так и не поняла, в каких случаях, но нашла лекарство:

(org-export-define-derived-backend 'org-static-blog-post-bare 'html
  :translate-alist '((template . (lambda (contents info) contents))))

Выполнить вот этот фрагмент из тамошнего же кода. Скопировала к себе поближе, иногда применяю.

Брать только файлы с расширением .publ.org

Собственно, каким образом я выбираю, какие файлы публиковать. Внутри файла уже работают механизмы самого org-mode.

Про drafts - реально смысла не имеет, но типа чтобы было.

(defun my-org-static-blog-get-post-filenames ()
     "Returns a list of all posts."
     (directory-files-recursively
      org-static-blog-posts-directory ".*\\.publ\\.org$"))
(advice-add  'org-static-blog-get-post-filenames :override  #'my-org-static-blog-get-post-filenames)

(defun my-org-static-blog-get-draft-filenames ()
     "Returns a list of all drafts."
     (directory-files-recursively
      org-static-blog-drafts-directory ".*\\.publ\\.org$"))
(advice-add 'org-static-blog-get-draft-filenames :override  #'my-org-static-blog-get-draft-filenames)

А потом ликвидировать следы этого самого publ

Добавила второе file-name-sans-extension в стратегические места

В org-static-blog

(defun my-org-static-blog-get-relative-path (post-filename)
  "Removes absolute directory path from POST-FILENAME and changes file extension
from `.publ.org` to `.html`. Returns filepath to HTML file relative to posts or drafts directories.
Works with both posts and drafts directories.
For example, when `org-static-blog-posts-directory` is set to '~/blog/posts'
and `post-filename` is passed as '~/blog/posts/my-life-update.publ.org' then the function
will return 'my-life-update.html'."
  (concat (file-name-sans-extension (file-name-sans-extension (file-relative-name post-filename org-static-blog-posts-directory)))
          ".html")
  )

(advice-add 'org-static-blog-get-relative-path :override  #'my-org-static-blog-get-relative-path)

В ox-html

(defun my-org-html-link (link desc info)
  "Transcode a LINK object from Org to HTML.
DESC is the description part of the link, or the empty string.
INFO is a plist holding contextual information.  See
`org-export-data'."
  (let* ((html-ext (plist-get info :html-extension))
         (dot (when (> (length html-ext) 0) "."))
         (link-org-files-as-html-maybe
          (lambda (raw-path info)
            ;; Treat links to `file.org' as links to `file.html', if
            ;; needed.  See `org-html-link-org-files-as-html'.
            (cond
             ((and (plist-get info :html-link-org-files-as-html)
                   (string= ".org"
                            (downcase (file-name-extension raw-path "."))))
              (concat (file-name-sans-extension (file-name-sans-extension raw-path)) dot html-ext))
             (t raw-path))))
         (type (org-element-property :type link))
         (raw-path (org-element-property :path link))
         ;; Ensure DESC really exists, or set it to nil.
         (desc (org-string-nw-p desc))
         (path
          (cond
           ((member type '("http" "https" "ftp" "mailto" "news"))
            (url-encode-url (concat type ":" raw-path)))
           ((string= "file" type)
            ;; During publishing, turn absolute file names belonging
            ;; to base directory into relative file names.  Otherwise,
            ;; append "file" protocol to absolute file name.
            (setq raw-path
                  (org-export-file-uri
                   (org-publish-file-relative-name raw-path info)))
            ;; Possibly append `:html-link-home' to relative file
            ;; name.
            (let ((home (and (plist-get info :html-link-home)
                             (org-trim (plist-get info :html-link-home)))))
              (when (and home
                         (plist-get info :html-link-use-abs-url)
                         (file-name-absolute-p raw-path))
                (setq raw-path (concat (file-name-as-directory home) raw-path))))
            ;; Maybe turn ".org" into ".html".
            (setq raw-path (funcall link-org-files-as-html-maybe raw-path info))
            ;; Add search option, if any.  A search option can be
            ;; relative to a custom-id, a headline title, a name or
            ;; a target.
            (let ((option (org-element-property :search-option link)))
              (if (not option) raw-path
                (let ((path (org-element-property :path link)))
                  (concat raw-path
                          "#"
                          (org-publish-resolve-external-link option path t))))))
           (t raw-path)))
         (attributes-plist
          (org-combine-plists
           ;; Extract attributes from parent's paragraph.  HACK: Only
           ;; do this for the first link in parent (inner image link
           ;; for inline images).  This is needed as long as
           ;; attributes cannot be set on a per link basis.
           (let* ((parent (org-export-get-parent-element link))
                  (link (let ((container (org-export-get-parent link)))
                          (if (and (eq 'link (org-element-type container))
                                   (org-html-inline-image-p link info))
                              container
                            link))))
             (and (eq link (org-element-map parent 'link #'identity info t))
                  (org-export-read-attribute :attr_html parent)))
           ;; Also add attributes from link itself.  Currently, those
           ;; need to be added programmatically before `org-html-link'
           ;; is invoked, for example, by backends building upon HTML
           ;; export.
           (org-export-read-attribute :attr_html link)))
         (attributes
          (let ((attr (org-html--make-attribute-string attributes-plist)))
            (if (org-string-nw-p attr) (concat " " attr) ""))))
    (cond
     ;; Link type is handled by a special function.
     ((org-export-custom-protocol-maybe link desc 'html info))
     ;; Image file.
     ((and (plist-get info :html-inline-images)
           (org-export-inline-image-p
            link (plist-get info :html-inline-image-rules)))
      (org-html--format-image path attributes-plist info))
     ;; Radio target: Transcode target's contents and use them as
     ;; link's description.
     ((string= type "radio")
      (let ((destination (org-export-resolve-radio-link link info)))
        (if (not destination) desc
          (format "<a href=\"#%s\"%s>%s</a>"
                  (org-export-get-reference destination info)
                  attributes
                  desc))))
     ;; Links pointing to a headline: Find destination and build
     ;; appropriate referencing command.
     ((member type '("custom-id" "fuzzy" "id"))
      (let ((destination (if (string= type "fuzzy")
                             (org-export-resolve-fuzzy-link link info)
                           (org-export-resolve-id-link link info))))
        (pcase (org-element-type destination)
          ;; ID link points to an external file.
          (`plain-text
           (let ((fragment (concat "ID-" path))
                 ;; Treat links to ".org" files as ".html", if needed.
                 (path (funcall link-org-files-as-html-maybe
                                destination info)))
             (format "<a href=\"%s#%s\"%s>%s</a>"
                     path fragment attributes (or desc destination))))
          ;; Fuzzy link points nowhere.
          (`nil
           (format "<i>%s</i>"
                   (or desc
                       (org-export-data
                        (org-element-property :raw-link link) info))))
          ;; Link points to a headline.
          (`headline
           (let ((href (org-html--reference destination info))
                 ;; What description to use?
                 (desc
                  ;; Case 1: Headline is numbered and LINK has no
                  ;; description.  Display section number.
                  (if (and (org-export-numbered-headline-p destination info)
                           (not desc))
                      (mapconcat #'number-to-string
                                 (org-export-get-headline-number
                                  destination info) ".")
                    ;; Case 2: Either the headline is un-numbered or
                    ;; LINK has a custom description.  Display LINK's
                    ;; description or headline's title.
                    (or desc
                        (org-export-data
                         (org-element-property :title destination) info)))))
             (format "<a href=\"#%s\"%s>%s</a>" href attributes desc)))
          ;; Fuzzy link points to a target or an element.
          (_
           (if (and destination
                    (memq (plist-get info :with-latex) '(mathjax t))
                    (eq 'latex-environment (org-element-type destination))
                    (eq 'math (org-latex--environment-type destination)))
               ;; Caption and labels are introduced within LaTeX
               ;; environment.  Use "ref" or "eqref" macro, depending on user
               ;; preference to refer to those in the document.
               (format (plist-get info :html-equation-reference-format)
                       (org-html--reference destination info))
             (let* ((ref (org-html--reference destination info))
                    (org-html-standalone-image-predicate
                     #'org-html--has-caption-p)
                    (counter-predicate
                     (if (eq 'latex-environment (org-element-type destination))
                         #'org-html--math-environment-p
                       #'org-html--has-caption-p))
                    (number
                     (cond
                      (desc nil)
                      ((org-html-standalone-image-p destination info)
                       (org-export-get-ordinal
                        (org-element-map destination 'link #'identity info t)
                        info 'link 'org-html-standalone-image-p))
                      (t (org-export-get-ordinal
                          destination info nil counter-predicate))))
                    (desc
                     (cond (desc)
                           ((not number) "No description for this link")
                           ((numberp number) (number-to-string number))
                           (t (mapconcat #'number-to-string number ".")))))
               (format "<a href=\"#%s\"%s>%s</a>" ref attributes desc)))))))
     ;; Coderef: replace link with the reference name or the
     ;; equivalent line number.
     ((string= type "coderef")
      (let ((fragment (concat "coderef-" (org-html-encode-plain-text path))))
        (format "<a href=\"#%s\" %s%s>%s</a>"
                fragment
                (format "class=\"coderef\" onmouseover=\"CodeHighlightOn(this, \
'%s');\" onmouseout=\"CodeHighlightOff(this, '%s');\""
                        fragment fragment)
                attributes
                (format (org-export-get-coderef-format path desc)
                        (org-export-resolve-coderef path info)))))
     ;; External link with a description part.
     ((and path desc)
      (format "<a href=\"%s\"%s>%s</a>"
              (org-html-encode-plain-text path)
              attributes
              desc))
     ;; External link without a description part.
     (path
      (let ((path (org-html-encode-plain-text path)))
        (format "<a href=\"%s\"%s>%s</a>" path attributes path)))
     ;; No path, only description.  Try to do something useful.
     (t
      (format "<i>%s</i>" desc)))))
(advice-add  'org-html-link :override  #'my-org-html-link)

Вариант сортировки по дате создания

Сделала, попробовала и работает, но убрала, потому что решила, что пусть отображается по последней правке.

Если выполняется это, то посты в блоге отображают как дату и сортируются по created, а не по date, то есть, по времени типа-создания, а не изменения.

(defun my-org-static-blog-get-date (post-filename)
"Extract the `#+created:` from POST-FILENAME as date-time."
(let ((case-fold-search t))
  (with-temp-buffer
    (insert-file-contents post-filename)
    (goto-char (point-min))
    (if (search-forward-regexp "^\\#\\+created:[ ]*<\\([^]>]+\\)>$" nil t)
        (date-to-time (match-string 1))
      (time-since 0)))))
(advice-add 'org-static-blog-get-date :override  #'my-org-static-blog-get-date)

Настройки, кроме путей, названий и ещё некоторых мелочей

(setq org-static-blog-enable-tags t)
(setq org-static-blog-preview-link-p t)
(setq org-static-blog-use-preview t)
(setq org-export-with-toc t)
(setq org-export-with-section-numbers nil)
(setq org-static-blog-langcode "ru")
(setq org-static-blog-preview-date-first-p nil)
(setq org-static-blog-index-length 30)

Как определяем границы preview

(setq org-static-blog-preview-start "<div class=\"preview\"")
(setq org-static-blog-preview-end "</div>")

Соответствующий див задаётся блоком #+begin_preview #+end_preview

Rss

(setq org-static-blog-rss-extra
   "<atom:link href=\"http://agnessa.pp.ru/rss.xml\" rel=\"self\" type=\"application/rss+xml\" />")
(setq org-static-blog-rss-max-entries 10)

Даты в локали С, у rss замороченней заголовок.

(defun my-org-static-blog--write-rss (items &optional tag)
  "Generates an RSS file for the given TAG, or for all tags is TAG is nil."
  (let ((title (format "%s%s"
                       org-static-blog-publish-title
                       (if tag (concat " - " tag) "")))
        (url (format "%s%s"
                     org-static-blog-publish-url
                     (if tag (concat "/tag-" (downcase tag) ".html") "")))
        (items (sort items (lambda (x y) (time-less-p (car y) (car x))))))
    (org-static-blog-with-find-file
     (org-static-blog--rss-filename tag)
     (concat "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
             "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">\n"
             "<channel>\n"
             "<title><![CDATA[" title "]]></title>\n"
             "<description><![CDATA[" title "]]></description>\n"
             "<link>" url "</link>\n"
             "<lastBuildDate>" (let ((system-time-locale "C")) ; force dates to render as per RSS spec
                                 (format-time-string "%a, %d %b %Y %H:%M:%S %z" (current-time)))
             "</lastBuildDate>\n"
             org-static-blog-rss-extra
             (apply 'concat (mapcar 'cdr (org-static-blog--prune-items items)))
             "</channel>\n"
             "</rss>\n"))))

  (advice-add 'org-static-blog--write-rss :override  #'my-org-static-blog--write-rss)

Урленкодить ссылки, добавить guid, локаль C.

      (defun my-org-static-blog-get-rss-item (post-filename)
  "Assemble RSS item from post-filename.
The HTML content is taken from the rendered HTML post."
  (concat
   "<item>\n"
   "  <title><![CDATA[" (org-static-blog-get-title post-filename) "]]></title>\n"
   "  <description><![CDATA["
   (org-static-blog-get-body post-filename t) ; exclude headline!
   "\n\n]]></description>\n"
   (let ((categories ""))
     (when (and (org-static-blog-get-tags post-filename) org-static-blog-enable-tags)
       (dolist (tag (org-static-blog-get-tags post-filename))
         (setq categories (concat categories
                                  "  <category><![CDATA[" tag "]]></category>\n"))))
     categories)
   "  <link>"
   (url-encode-url (org-static-blog-get-post-url post-filename))
   "</link>\n"
   "  <guid>"
   (url-encode-url (org-static-blog-get-post-url post-filename))
   "</guid>\n"
   "  <pubDate>"
   (let ((system-time-locale "C")) ; force dates to render as per RSS spec
     (format-time-string "%a, %d %b %Y %H:%M:%S %z" (org-static-blog-get-date post-filename)))
   "</pubDate>\n"
   "</item>\n"))

  (advice-add 'org-static-blog-get-rss-item :override #'my-org-static-blog-get-rss-item)

Задаю всякий html, примерно

в head каждой страницы

Автор, стили, иконка. Потом, вероятно, надо будет сюда часть микроразметки по заветам indieweb.

(setq org-static-blog-page-header
"<meta name=\"author\" content=\"Agnessa\">
<meta name=\"follow_it-verification-code\" content=\"code\"/>
<link href= \"https://agnessa.pp.ru/static/style.css\" rel=\"stylesheet\" type=\"text/css\" />
<link rel=\"icon\"  type=\"image/png\" sizes=\"150x150\" href=\"https://agnessa.pp.ru/static/bonsai-small.png\">
")

follow.it - это сервис, через который можно подписаться на обновления.

Шапка страницы, оно же начало body каждой страницы

(setq org-static-blog-page-preamble
"<div class=\"header\">
 <p style=\"float:right\"><a href=\"https://agnessa.pp.ru\"><img src=\"https://agnessa.pp.ru/static/bonsai-small.png\" alt=\"Цифровой садик - приветственная\"/></a></p>
<p><a href=\"https://agnessa.pp.ru\">Цифровой садик - приветственная</a> |
   <a href=\"https://agnessa.pp.ru/archive.html\">Полный список всего, что тут есть</a> |
   <a href=\"https://forms.yandex.ru/u/61057e8e2f1b2d9ca96ab333/\">Отправить сообщение через Яндекс.Форму</a> | <a href=\"https://agnessa.pp.ru/rss.xml\">RSS</a> | <a href=\"https://follow.it/m2wkzr?action=followPub\">Подписаться  через follow.it</a></p>
</div>")

Подвал каждой страницы

(setq org-static-blog-page-postamble
  "<hr/>
<p>Если у вас есть мысли, комментарии, предложения или отклики по поводу этой страницы или этого цифрового сада в целом, <a href=\"https://forms.yandex.ru/u/61057e8e2f1b2d9ca96ab333/\">напишите мне сообщение через Яндекс.Форму</a>. Мне ооочень интересно!</p>
")

Под постами

(setq org-static-blog-post-comments "<div id=\"archive\">
    <a href=\"https://agnessa.pp.ru/archive.html\">Все посты</a>
  </div>")

Начало приветственной страницы

  (setq org-static-blog-index-front-matter
  "<h1>Привет!</h1>\n
<div class=\"epigraph\">
<p>Я несу свою пургу,<br/>
Потому что я — могу!<br/>
 — Я
  </p>

 <p>Нередко мы считаем свои заблуждения как раз находками, почему же ими не поделиться?..<br/>
  — Виктор Кротов</p>

<p>Мнение автора может не совпадать с его точкой зрения.<br/>
  — Дисклеймер</p>

<p>...надо всё-таки возделывать свой сад.<br/>
  — Вольтер «Кандид»
</div>

<p>Тут мой маленький <a href=\"https://agnessa.pp.ru/0-20210912/20210721053232-digital_garden.html\">цифровой садик</a>. Наполняю содержанием. Даже не столько создаю, сколько выбираю, что из существующего беспорядка мне ок показывать наружу. :) Делаю это медленно, по мере постоянного текущего разгребания базы.</p>

  <p><a href=\"https://agnessa.pp.ru/archive.html\">Полный список всего, что тут есть</a>, ссылка также внизу примерно на каждой странице. Там же возможность <a href=\"https://forms.yandex.ru/u/61057e8e2f1b2d9ca96ab333/\">написать мне в яндекс-форму</a>. Мне интересно, что вы думаете, и отклики сильно улучшают сад. Если хотите написать о конкретной странице — скопируйте ссылку на эту страницу в форму, пожалуйста. Форма не скажет мне сама, с какой страницы вы пришли.</p>

<p>На существующих страничках есть сколько-то ссылок на страницы, которые я пока не решилась или не добралась выложить. К сожалению, пока не знаю ни как убрать, не убирая рабочие, ни как отмечать, что они ведут в никуда. Давайте думать, что это хорошо для читателя, потому что даёт возможность подглядеть в закрытую часть сада :)</p>

<p>Страницы расположены в хронологическом порядке, раньше — глубже. Дата — последняя правка соответствующего файла. И блоговость тут — иллюзия. Это не окончательные тексты по каким-то поводам, которые не предполагают меняться, быв раз написаны, это «растения», они растут, увядают и меняются прочими способами.</p>

<p>Некоторые «входные точки»:</p>
<ul>
  <li><a href=\"https://agnessa.pp.ru/texts/20210721093953-рецензия.html\">Как писать рецензию</a></li>
  <li><a href=\"https://agnessa.pp.ru/0-20210912/20200712142832-тг_каналы.html\">Телеграм-каналы и чаты</a></li>
  <li><a href=\"https://agnessa.pp.ru/time/20210704083219-улучшающие%20и%20поддерживающие%20дела.html\">Улучшающие и поддерживающие дела</a></li>
  <li><a href=\"https://agnessa.pp.ru/computer/emacs/20200820235900-emacs.html\">Еmacs</a></li>
  <li><a href=\"https://agnessa.pp.ru/mine/20210826225955-мое.html\">мои стихи и что там ещё</a></li>
  <li><a href=\"https://agnessa.pp.ru/selfrelations/20200912193056-%D0%BE%D0%B1%D0%BE_%D0%BC%D0%BD%D0%B5.html\">обо мне</a></li>
  <li><a href=\"https://agnessa.pp.ru/me/20210820030406-%D0%B2%D0%B8%D1%88%D0%BB%D0%B8%D1%81%D1%82.html\">вишлист</a></li>
</ul>")

Публикация

(defun my-auto-org-static-blog-publish()
"Отправляет на место."
  (interactive)
  (org-static-blog-publish)
  (shell-command-to-string "syncgarden")
)

Требует скриптика syncgarden, который живёт отдельно в папочке, добавленной в PATH. Там куда перейти и команда для rsync.

Hy, и

(add-hook 'midnight-hook 'my-auto-org-static-blog-publish)

чтоб временами автосрабатывало. https://www.emacswiki.org/emacs/MidnightMode

Потому что чем меньше рутины требует человеческого внимания, тем лучше. :)


Если у вас есть мысли, комментарии, предложения или отклики по поводу этой страницы или этого цифрового сада в целом, напишите мне сообщение на agnessa@agnessa.pp.ru. Мне ооочень интересно!

Задонатить.


An IndieWeb Webring 🕸💍