Integrating my publishing system using org-publish

Seems like I may need to purge my web site source, or maybe use a
less-intense system.
This commit is contained in:
Howard Abrams 2021-11-11 20:59:22 -08:00
parent 465fb840c1
commit 0afe98086d
4 changed files with 619 additions and 2 deletions

View file

@ -19,6 +19,7 @@ This creates [[file:~/.emacs.d/init.el][~/.emacs.d/init.el]] that starts the pro
- [[file:ha-org-word-processor.org][ha-org-word-processor.org]] :: attempts to make Org files /visually/ look like what one might see in a word processor, including turning off the colors for headers, and instead increasing their size.
- [[file:ha-org-clipboard.org][ha-org-clipboard.org]] :: automatically converting HTML from a clipboard into Org-formatted content.
- [[file:ha-org-journaling.org][ha-org-journaling.org]] :: for writing journal entries and tasks.
- [[file:ha-org-publishing.org][ha-org-publishing.org]] :: code for publishing my website, [[http://howardism.org][www.howardism.org]].
- [[file:ha-org-sprint.org][ha-org-sprint.org]] :: functions for working with the my Org-focused sprint files.
- [[file:ha-remoting.org][ha-remoting.org]] :: my interface to systems using SSH and Vterm.
- [[file:ha-feed-reader.org][ha-feed-reader.org]] :: configuration of elfeed as well as my RSS feeds.

View file

@ -49,7 +49,8 @@ Let's get the Straight project working with =use-package=:
While that enables the =:straight t= extension to =use-package=, let's just have that be the default:
#+BEGIN_SRC emacs-lisp
(use-package straight
:custom (straight-use-package-by-default t))
:custom (straight-use-package-by-default t
straight-default-vc 'git))
#+END_SRC
See the details in [[https://dev.to/jkreeftmeijer/emacs-package-management-with-straight-el-and-use-package-3oc8][this essay]].
** Basic Libraries
@ -113,7 +114,7 @@ The following loads the rest of my org-mode literate files. I add them as they a
"ha-org-word-processor.org"
"ha-org-clipboard.org"
"ha-org-journaling.org"
;; "org-publishing.org"
"ha-org-publishing.org"
"ha-org-sprint.org"
"ha-capturing-notes.org"
"ha-programming.org"

414
elisp/ox-rss.el Normal file
View file

@ -0,0 +1,414 @@
;;; ox-rss.el --- RSS 2.0 Back-End for Org Export Engine
;; Copyright (C) 2013-2015 Bastien Guerry
;; Author: Bastien Guerry <bzg@gnu.org>
;; Keywords: org, wp, blog, feed, rss
;; This file is not yet part of GNU Emacs.
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This library implements a RSS 2.0 back-end for Org exporter, based on
;; the `html' back-end.
;;
;; It requires Emacs 24.1 at least.
;;
;; It provides two commands for export, depending on the desired output:
;; `org-rss-export-as-rss' (temporary buffer) and `org-rss-export-to-rss'
;; (as a ".xml" file).
;;
;; This backend understands two new option keywords:
;;
;; #+RSS_EXTENSION: xml
;; #+RSS_IMAGE_URL: http://myblog.org/mypicture.jpg
;;
;; It uses #+HTML_LINK_HOME: to set the base url of the feed.
;;
;; Exporting an Org file to RSS modifies each top-level entry by adding a
;; PUBDATE property. If `org-rss-use-entry-url-as-guid', it will also add
;; an ID property, later used as the guid for the feed's item.
;;
;; The top-level headline is used as the title of each RSS item unless
;; an RSS_TITLE property is set on the headline.
;;
;; You typically want to use it within a publishing project like this:
;;
;; (add-to-list
;; 'org-publish-project-alist
;; '("homepage_rss"
;; :base-directory "~/myhomepage/"
;; :base-extension "org"
;; :rss-image-url "http://lumiere.ens.fr/~guerry/images/faces/15.png"
;; :html-link-home "http://lumiere.ens.fr/~guerry/"
;; :html-link-use-abs-url t
;; :rss-extension "xml"
;; :publishing-directory "/home/guerry/public_html/"
;; :publishing-function (org-rss-publish-to-rss)
;; :section-numbers nil
;; :exclude ".*" ;; To exclude all files...
;; :include ("index.org") ;; ... except index.org.
;; :table-of-contents nil))
;;
;; ... then rsync /home/guerry/public_html/ with your server.
;;
;; By default, the permalink for a blog entry points to the headline.
;; You can specify a different one by using the :RSS_PERMALINK:
;; property within an entry.
;;; Code:
(require 'ox-html)
(declare-function url-encode-url "url-util" (url))
;;; Variables and options
(defgroup org-export-rss nil
"Options specific to RSS export back-end."
:tag "Org RSS"
:group 'org-export
:version "24.4"
:package-version '(Org . "8.0"))
(defcustom org-rss-image-url "http://orgmode.org/img/org-mode-unicorn-logo.png"
"The URL of the an image for the RSS feed."
:group 'org-export-rss
:type 'string)
(defcustom org-rss-extension "xml"
"File extension for the RSS 2.0 feed."
:group 'org-export-rss
:type 'string)
(defcustom org-rss-categories 'from-tags
"Where to extract items category information from.
The default is to extract categories from the tags of the
headlines. When set to another value, extract the category
from the :CATEGORY: property of the entry."
:group 'org-export-rss
:type '(choice
(const :tag "From tags" from-tags)
(const :tag "From the category property" from-category)))
(defcustom org-rss-use-entry-url-as-guid t
"Use the URL for the <guid> metatag?
When nil, Org will create ids using `org-icalendar-create-uid'."
:group 'org-export-rss
:type 'boolean)
;;; Define backend
(org-export-define-derived-backend 'rss 'html
:menu-entry
'(?r "Export to RSS"
((?R "As RSS buffer"
(lambda (a s v b) (org-rss-export-as-rss a s v)))
(?r "As RSS file" (lambda (a s v b) (org-rss-export-to-rss a s v)))
(?o "As RSS file and open"
(lambda (a s v b)
(if a (org-rss-export-to-rss t s v)
(org-open-file (org-rss-export-to-rss nil s v)))))))
:options-alist
'((:description "DESCRIPTION" nil nil newline)
(:keywords "KEYWORDS" nil nil space)
(:with-toc nil nil nil) ;; Never include HTML's toc
(:rss-extension "RSS_EXTENSION" nil org-rss-extension)
(:rss-image-url "RSS_IMAGE_URL" nil org-rss-image-url)
(:rss-categories nil nil org-rss-categories))
:filters-alist '((:filter-final-output . org-rss-final-function))
:translate-alist '((headline . org-rss-headline)
(comment . (lambda (&rest args) ""))
(comment-block . (lambda (&rest args) ""))
(timestamp . (lambda (&rest args) ""))
(plain-text . org-rss-plain-text)
(section . org-rss-section)
(template . org-rss-template)))
;;; Export functions
;;;###autoload
(defun org-rss-export-as-rss (&optional async subtreep visible-only)
"Export current buffer to a RSS buffer.
If narrowing is active in the current buffer, only export its
narrowed part.
If a region is active, export that region.
A non-nil optional argument ASYNC means the process should happen
asynchronously. The resulting buffer should be accessible
through the `org-export-stack' interface.
When optional argument SUBTREEP is non-nil, export the sub-tree
at point, extracting information from the headline properties
first.
When optional argument VISIBLE-ONLY is non-nil, don't export
contents of hidden elements.
Export is done in a buffer named \"*Org RSS Export*\", which will
be displayed when `org-export-show-temporary-export-buffer' is
non-nil."
(interactive)
(let ((file (buffer-file-name (buffer-base-buffer))))
(org-icalendar-create-uid file 'warn-user)
(org-rss-add-pubdate-property))
(org-export-to-buffer 'rss "*Org RSS Export*"
async subtreep visible-only nil nil (lambda () (text-mode))))
;;;###autoload
(defun org-rss-export-to-rss (&optional async subtreep visible-only)
"Export current buffer to a RSS file.
If narrowing is active in the current buffer, only export its
narrowed part.
If a region is active, export that region.
A non-nil optional argument ASYNC means the process should happen
asynchronously. The resulting file should be accessible through
the `org-export-stack' interface.
When optional argument SUBTREEP is non-nil, export the sub-tree
at point, extracting information from the headline properties
first.
When optional argument VISIBLE-ONLY is non-nil, don't export
contents of hidden elements.
Return output file's name."
(interactive)
(let ((file (buffer-file-name (buffer-base-buffer))))
(org-icalendar-create-uid file 'warn-user)
(org-rss-add-pubdate-property))
(let ((outfile (org-export-output-file-name
(concat "." org-rss-extension) subtreep)))
(org-export-to-file 'rss outfile async subtreep visible-only)))
;;;###autoload
(defun org-rss-publish-to-rss (plist filename pub-dir)
"Publish an org file to RSS.
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.
Return output file name."
(let ((bf (get-file-buffer filename)))
(if bf
(with-current-buffer bf
(org-icalendar-create-uid filename 'warn-user)
(org-rss-add-pubdate-property)
(write-file filename))
(find-file filename)
(org-icalendar-create-uid filename 'warn-user)
(org-rss-add-pubdate-property)
(write-file filename) (kill-buffer)))
(org-publish-org-to
'rss filename (concat "." org-rss-extension) plist pub-dir))
;;; Main transcoding functions
(defun org-rss-headline (headline contents info)
"Transcode HEADLINE element into RSS format.
CONTENTS is the headline contents. INFO is a plist used as a
communication channel."
(unless (or (org-element-property :footnote-section-p headline)
;; Only consider first-level headlines
(> (org-export-get-relative-level headline info) 1))
(let* ((author (and (plist-get info :with-author)
(let ((auth (plist-get info :author)))
(and auth (org-export-data auth info)))))
(htmlext (plist-get info :html-extension))
(hl-number (org-export-get-headline-number headline info))
(hl-home (file-name-as-directory (plist-get info :html-link-home)))
(hl-pdir (plist-get info :publishing-directory))
(hl-perm (org-element-property :RSS_PERMALINK headline))
(anchor (org-export-get-reference headline info))
(category (org-rss-plain-text
(or (org-element-property :CATEGORY headline) "") info))
(pubdate0 (org-element-property :PUBDATE headline))
(pubdate (let ((system-time-locale "C"))
(if pubdate0
(format-time-string
"%a, %d %b %Y %H:%M:%S %z"
(org-time-string-to-time pubdate0)))))
(title (or (org-element-property :RSS_TITLE headline)
(replace-regexp-in-string
org-bracket-link-regexp
(lambda (m) (or (match-string 3 m)
(match-string 1 m)))
(org-element-property :raw-value headline))))
(publink
(or (and hl-perm (concat (or hl-home hl-pdir) hl-perm))
(concat
(or hl-home hl-pdir)
(file-name-nondirectory
(file-name-sans-extension
(plist-get info :input-file))) "." htmlext "#" anchor)))
(guid (if org-rss-use-entry-url-as-guid
publink
(org-rss-plain-text
(or (org-element-property :ID headline)
(org-element-property :CUSTOM_ID headline)
publink)
info))))
(if (not pubdate0) "" ;; Skip entries with no PUBDATE prop
(format
(concat
"<item>\n"
"<title>%s</title>\n"
"<link>%s</link>\n"
"<author>%s</author>\n"
"<guid isPermaLink=\"false\">%s</guid>\n"
"<pubDate>%s</pubDate>\n"
(org-rss-build-categories headline info) "\n"
"<description><![CDATA[%s]]></description>\n"
"</item>\n")
title publink author guid pubdate contents)))))
(defun org-rss-build-categories (headline info)
"Build categories for the RSS item."
(if (eq (plist-get info :rss-categories) 'from-tags)
(mapconcat
(lambda (c) (format "<category><![CDATA[%s]]></category>" c))
(org-element-property :tags headline)
"\n")
(let ((c (org-element-property :CATEGORY headline)))
(format "<category><![CDATA[%s]]></category>" c))))
(defun org-rss-template (contents info)
"Return complete document string after RSS conversion.
CONTENTS is the transcoded contents string. INFO is a plist used
as a communication channel."
(concat
(format "<?xml version=\"1.0\" encoding=\"%s\"?>"
(symbol-name org-html-coding-system))
"\n<rss version=\"2.0\"
xmlns:content=\"http://purl.org/rss/1.0/modules/content/\"
xmlns:wfw=\"http://wellformedweb.org/CommentAPI/\"
xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
xmlns:atom=\"http://www.w3.org/2005/Atom\"
xmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"
xmlns:slash=\"http://purl.org/rss/1.0/modules/slash/\"
xmlns:georss=\"http://www.georss.org/georss\"
xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\"
xmlns:media=\"http://search.yahoo.com/mrss/\">"
"<channel>"
(org-rss-build-channel-info info) "\n"
contents
"</channel>\n"
"</rss>"))
(defun org-rss-build-channel-info (info)
"Build the RSS channel information."
(let* ((system-time-locale "C")
(title (plist-get info :title))
(email (org-export-data (plist-get info :email) info))
(author (and (plist-get info :with-author)
(let ((auth (plist-get info :author)))
(and auth (org-export-data auth info)))))
(date (format-time-string "%a, %d %b %Y %H:%M:%S %z")) ;; RFC 882
(description (org-export-data (plist-get info :description) info))
(lang (plist-get info :language))
(keywords (plist-get info :keywords))
(rssext (plist-get info :rss-extension))
(blogurl (or (plist-get info :html-link-home)
(plist-get info :publishing-directory)))
(image (url-encode-url (plist-get info :rss-image-url)))
(ifile (plist-get info :input-file))
(publink
(concat (file-name-as-directory blogurl)
(file-name-nondirectory
(file-name-sans-extension ifile))
"." rssext)))
(format
"\n<title>%s</title>
<atom:link href=\"%s\" rel=\"self\" type=\"application/rss+xml\" />
<link>%s</link>
<description><![CDATA[%s]]></description>
<language>%s</language>
<pubDate>%s</pubDate>
<lastBuildDate>%s</lastBuildDate>
<generator>%s</generator>
<webMaster>%s (%s)</webMaster>
<image>
<url>%s</url>
<title>%s</title>
<link>%s</link>
</image>
"
title publink blogurl description lang date date
(concat (format "Emacs %d.%d"
emacs-major-version
emacs-minor-version)
" Org-mode " (org-version))
email author image title blogurl)))
(defun org-rss-section (section contents info)
"Transcode SECTION element into RSS format.
CONTENTS is the section contents. INFO is a plist used as
a communication channel."
contents)
(defun org-rss-timestamp (timestamp contents info)
"Transcode a TIMESTAMP object from Org to RSS.
CONTENTS is nil. INFO is a plist holding contextual
information."
(org-html-encode-plain-text
(org-timestamp-translate timestamp)))
(defun org-rss-plain-text (contents info)
"Convert plain text into RSS encoded text."
(let (output)
(setq output (org-html-encode-plain-text contents)
output (org-export-activate-smart-quotes
output :html info))))
;;; Filters
(defun org-rss-final-function (contents backend info)
"Prettify the RSS output."
(with-temp-buffer
(xml-mode)
(insert contents)
(indent-region (point-min) (point-max))
(buffer-substring-no-properties (point-min) (point-max))))
;;; Miscellaneous
(defun org-rss-add-pubdate-property ()
"Set the PUBDATE property for top-level headlines."
(let (msg)
(org-map-entries
(lambda ()
(let* ((entry (org-element-at-point))
(level (org-element-property :level entry)))
(when (= level 1)
(unless (org-entry-get (point) "PUBDATE")
(setq msg t)
(org-set-property
"PUBDATE" (format-time-string
(cdr org-time-stamp-formats)))))))
nil nil 'comment 'archive)
(when msg
(message "Property PUBDATE added to top-level entries in %s"
(buffer-file-name))
(sit-for 2))))
(provide 'ox-rss)
;;; ox-rss.el ends here

201
ha-org-publishing.org Normal file
View file

@ -0,0 +1,201 @@
#+TITLE: Publishing my Website with Org
#+AUTHOR: Howard X. Abrams
#+EMAIL: howard.abrams@gmail.com
#+DATE: 2020-12-22
#+FILETAGS: :emacs:
A literate programming file for publishing my website using org.
#+BEGIN_SRC emacs-lisp :exports none
;;; org-publishing.el --- Publishing my website using org. -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2020 Howard X. Abrams
;;
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
;; Maintainer: Howard X. Abrams <howard.abrams@gmail.com>
;; Created: December 22, 2020
;;
;; This file is not part of GNU Emacs.
;;
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
;; ~/other/hamacs/org-publishing.org
;; And tangle the file to recreate this one.
;;
;;; Code:
#+END_SRC
* Introduction
While the Emacs community have a plethora of options for generating a static website from org-formatted files, I keep my pretty simple, and just use the standard =org-publish= feature.
While the following packages come with Emacs, they aren't necessarily loaded:
#+BEGIN_SRC emacs-lisp :results silent
(require 'ox-rss)
(require 'ox-publish)
#+END_SRC
Variable settings:
#+BEGIN_SRC emacs-lisp
(setq org-export-with-broken-links t
org-mode-websrc-directory "/Volumes/Personal/dropbox/website"
org-mode-publishing-directory (concat (getenv "HOME") "/website-public/"))
#+END_SRC
* The Projects
I separate my /website/ into distinct projects separately built:
- =blog-content= :: The bulk of rendering my =website= org files into HTML
- =blog-static= :: All of the assets, like images are easily copied in place
- =blog-rss= :: Regenerate the feeder files
- =org-notes= :: Optionally render a non-web site collection of notes.
#+BEGIN_SRC emacs-lisp
(setq org-publish-project-alist
`(("all"
:components ("blog-content" "blog-static" "org-notes" "blog-rss"))
("blog-content"
:base-directory ,org-mode-websrc-directory
:base-extension "org"
:publishing-directory ,org-mode-publishing-directory
:recursive t
:publishing-function org-html-publish-to-html
:preparation-function org-mode-blog-prepare
:export-with-tags nil
:headline-levels 4
:auto-preamble t
:auto-postamble nil
:auto-sitemap t
:sitemap-title "Howardisms"
:section-numbers nil
:table-of-contents nil
:with-toc nil
:with-author nil
:with-creator nil
:with-tags nil
:with-smart-quotes t
:html-doctype "html5"
:html-html5-fancy t
;; :html-preamble org-mode-blog-preamble
;; :html-postamble org-mode-blog-postamble
;; :html-postamble "<hr><div id='comments'></div>"
:html-head "<meta http-equiv=\"X-Clacks-Overhead\" content=\"GNU Terry Pratchett\" />
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Source+Serif+Pro:400,700&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Source+Code+Pro:400,700' rel='stylesheet' type='text/css'>
<link rel=\"stylesheet\" href=\"/css/styles.css\" type=\"text/css\"/>\n"
:html-head-extra "<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js\"></script>
<script src=\"/js/magic.js\"></script>
<link rel=\"icon\" href=\"/img/dragon.svg\">
<link rel=\"shortcut icon\" href=\"/img/dragon-head.png\">
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />"
:html-head-include-default-style nil)
("blog-static"
:base-directory ,org-mode-websrc-directory
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|svg"
:publishing-directory ,org-mode-publishing-directory
:recursive t
:publishing-function org-publish-attachment)
("blog-rss"
:base-directory ,org-mode-websrc-directory
:base-extension "org"
:rss-image-url "http://howardism.org/img/dragon-head.png"
:publishing-directory ,org-mode-publishing-directory
:publishing-function (org-rss-publish-to-rss)
:html-link-home "http://www.howardism.org/"
:html-link-use-abs-url t
:with-toc nil
:exclude ".*"
:include ("index.org"))
("org-notes"
:base-directory "~/technical/"
:base-extension "org"
:publishing-directory ,(concat org-mode-publishing-directory "/notes/")
:recursive t
:publishing-function org-html-publish-to-html
:headline-levels 4 ; Just the default for this project.
:auto-preamble t
:auto-sitemap t ; Generate sitemap.org automagically...
:makeindex t
:section-numbers nil
:style "<link rel=\"stylesheet\" href=\"../css/styles.css\" type=\"text/css\"/> <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js\" type=\"text/javascript\"></script> <link href=\"http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/smoothness/jquery-ui.css\" type=\"text/css\" rel=\"stylesheet\" /> <script src=\"https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js\" type=\"text/javascript\"></script> <script =\"text/javascript\" src=\"js/magic.js\"></script>"
:table-of-contents nil
:with-author nil
:with-creator nil
:with-tags nil)
("org-notes-static"
:base-directory "~/technical/"
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
:publishing-directory ,(concat org-mode-publishing-directory "/other/")
:recursive t
:publishing-function org-publish-attachment)))
#+END_SRC
* Including Sections
In the project definitions, I reference a =pre-= and =postamble= that allow me to inject some standard HTML file headers and footers:
#+BEGIN_SRC emacs-lisp
(defun org-mode-blog-preamble (options)
"The function that creates the preamble top section for the blog.
OPTIONS contains the property list from the org-mode export."
(message "Preamble options: %s" (princ options))
(let ((base-directory (plist-get options :base-directory)))
(org-babel-with-temp-filebuffer (expand-file-name "top-bar.html" base-directory) (buffer-string))))
(defun org-mode-blog-postamble (options)
"The function that creates the postamble, or bottom section for the blog.
OPTIONS contains the property list from the org-mode export."
(let ((base-directory (plist-get options :base-directory)))
(org-babel-with-temp-filebuffer (expand-file-name "bottom.html" base-directory) (buffer-string))))
#+END_SRC
Another helper function for the content of website is to make sure to update =index.org=, so that the RSS gets generated.
#+BEGIN_SRC emacs-lisp
(defun org-mode-blog-prepare (&optional options)
"`index.org' should always be exported so touch the file before publishing."
(let* ((base-directory (plist-get options :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))
(kill-buffer buffer)))
#+END_SRC
* Keybindings
Make it easy to publish all or just some of my website:
#+BEGIN_SRC emacs-lisp
(general-evil-define-key 'normal org-mode-map
:prefix "SPC m"
"p" '(:ignore t :which-key "publishing")
"p a" '("all" . org-publish-all)
"p p" '("project" . org-publish-project))
#+END_SRC
And let's put a /leader key/ sequence for my favorite file on my website:
#+BEGIN_SRC emacs-lisp
(ha-leader
"f h" '(:ignore t :which-key "howards")
"f h w" '("website index" . (lambda ()
(find-file (expand-file-name "index.org" "~/website")))))
#+END_SRC
* Technical Artifacts :noexport:
Let's =provide= a name so we can =require= it:
#+BEGIN_SRC emacs-lisp :exports none
(provide 'ha-org-publishing)
;;; ha-org-publishing.el ends here
#+END_SRC
Before you can build this on a new system, make sure that you put the cursor over any of these properties, and hit: ~C-c C-c~
#+DESCRIPTION: A literate programming version for publishing my website using org.
#+PROPERTY: header-args:sh :tangle no
#+PROPERTY: header-args:emacs-lisp :tangle yes
#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes
#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil
#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js