summaryrefslogtreecommitdiff
path: root/lisp/ox-publish.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/ox-publish.el')
-rw-r--r--lisp/ox-publish.el243
1 files changed, 165 insertions, 78 deletions
diff --git a/lisp/ox-publish.el b/lisp/ox-publish.el
index efc70d2..9f49f24 100644
--- a/lisp/ox-publish.el
+++ b/lisp/ox-publish.el
@@ -1,5 +1,5 @@
;;; ox-publish.el --- Publish Related Org Mode Files as a Website
-;; Copyright (C) 2006-2014 Free Software Foundation, Inc.
+;; Copyright (C) 2006-2015 Free Software Foundation, Inc.
;; Author: David O'Toole <dto@gnu.org>
;; Maintainer: Carsten Dominik <carsten DOT dominik AT gmail DOT com>
@@ -54,6 +54,12 @@
"This will cache timestamps and titles for files in publishing projects.
Blocks could hash sha1 values here.")
+(defvar org-publish-after-publishing-hook nil
+ "Hook run each time a file is published.
+Every function in this hook will be called with two arguments:
+the name of the original file and the name of the file
+produced.")
+
(defgroup org-publish nil
"Options for publishing a set of Org-mode and related files."
:tag "Org Publishing"
@@ -169,7 +175,9 @@ included. See the back-end documentation for more information.
:with-footnotes `org-export-with-footnotes'
:with-inlinetasks `org-export-with-inlinetasks'
:with-latex `org-export-with-latex'
+ :with-planning `org-export-with-planning'
:with-priority `org-export-with-priority'
+ :with-properties `org-export-with-properties'
:with-smart-quotes `org-export-with-smart-quotes'
:with-special-strings `org-export-with-special-strings'
:with-statistics-cookies' `org-export-with-statistics-cookies'
@@ -179,7 +187,7 @@ included. See the back-end documentation for more information.
:with-tags `org-export-with-tags'
:with-tasks `org-export-with-tasks'
:with-timestamps `org-export-with-timestamps'
- :with-planning `org-export-with-planning'
+ :with-title `org-export-with-title'
:with-todo-keywords `org-export-with-todo-keywords'
The following properties may be used to control publishing of
@@ -228,7 +236,7 @@ If you create a site-map file, adjust the sorting like this:
`:sitemap-sort-files'
The site map is normally sorted alphabetically. You can
- change this behaviour setting this to `anti-chronologically',
+ change this behavior setting this to `anti-chronologically',
`chronologically', or nil.
`:sitemap-ignore-case'
@@ -575,7 +583,7 @@ Return output file name."
(body-p (plist-get plist :body-only)))
(org-export-to-file backend output-file
nil nil nil body-p
- ;; Add `org-publish-collect-numbering' and
+ ;; Add `org-publish--collect-references' and
;; `org-publish-collect-index' to final output
;; filters. The latter isn't dependent on
;; `:makeindex', since we want to keep it up-to-date
@@ -583,7 +591,7 @@ Return output file name."
(org-combine-plists
plist
`(:filter-final-output
- ,(cons 'org-publish-collect-numbering
+ ,(cons 'org-publish--collect-references
(cons 'org-publish-collect-index
(plist-get plist :filter-final-output))))))))
;; Remove opened buffer in the process.
@@ -599,11 +607,12 @@ publishing directory.
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)))
- (copy-file filename
- (expand-file-name (file-name-nondirectory filename) pub-dir)
- t)))
+ (let ((output (expand-file-name (file-name-nondirectory filename) pub-dir)))
+ (or (equal (expand-file-name (file-name-directory filename))
+ (file-name-as-directory (expand-file-name pub-dir)))
+ (copy-file filename output t))
+ ;; Return file name.
+ output))
@@ -624,8 +633,10 @@ See `org-publish-projects'."
(project-plist (cdr project))
(ftname (expand-file-name filename))
(publishing-function
- (or (plist-get project-plist :publishing-function)
- (error "No publishing function chosen")))
+ (let ((fun (plist-get project-plist :publishing-function)))
+ (cond ((null fun) (error "No publishing function chosen"))
+ ((listp fun) fun)
+ (t (list fun)))))
(base-dir
(file-name-as-directory
(expand-file-name
@@ -647,19 +658,14 @@ See `org-publish-projects'."
(concat pub-dir
(and (string-match (regexp-quote base-dir) ftname)
(substring ftname (match-end 0))))))
- (if (listp publishing-function)
- ;; allow chain of publishing functions
- (mapc (lambda (f)
- (when (org-publish-needed-p
- filename pub-dir f tmp-pub-dir base-dir)
- (funcall f project-plist filename tmp-pub-dir)
- (org-publish-update-timestamp filename pub-dir f base-dir)))
- publishing-function)
- (when (org-publish-needed-p
- filename pub-dir publishing-function tmp-pub-dir base-dir)
- (funcall publishing-function project-plist filename tmp-pub-dir)
- (org-publish-update-timestamp
- filename pub-dir publishing-function base-dir)))
+ ;; Allow chain of publishing functions.
+ (dolist (f publishing-function)
+ (when (org-publish-needed-p filename pub-dir f tmp-pub-dir base-dir)
+ (let ((output (funcall f project-plist filename tmp-pub-dir)))
+ (org-publish-update-timestamp filename pub-dir f base-dir)
+ (run-hook-with-args 'org-publish-after-publishing-hook
+ filename
+ output))))
(unless no-cache (org-publish-write-cache-file))))
(defun org-publish-projects (projects)
@@ -833,17 +839,15 @@ time in `current-time' format."
(date (plist-get
(with-current-buffer file-buf
(if visiting
- (org-export-with-buffer-copy (org-export-get-environment))
+ (org-export-with-buffer-copy
+ (org-export-get-environment))
(org-export-get-environment)))
:date)))
(unless visiting (kill-buffer file-buf))
- ;; DATE is either a timestamp object or a secondary string. If it
- ;; is a timestamp or if the secondary string contains a timestamp,
+ ;; DATE is a secondary string. If it contains a timestamp,
;; convert it to internal format. Otherwise, use FILE
;; modification time.
- (cond ((eq (org-element-type date) 'timestamp)
- (org-time-string-to-time (org-element-interpret-data date)))
- ((let ((ts (and (consp date) (assq 'timestamp date))))
+ (cond ((let ((ts (and (consp date) (assq 'timestamp date))))
(and ts
(let ((value (org-element-interpret-data ts)))
(and (org-string-nw-p value)
@@ -870,25 +874,28 @@ When optional argument FORCE is non-nil, force publishing all
files in PROJECT. With a non-nil optional argument ASYNC,
publishing will be done asynchronously, in another process."
(interactive
- (list
- (assoc (org-icompleting-read
- "Publish project: "
- org-publish-project-alist nil t)
- org-publish-project-alist)
- current-prefix-arg))
- (let ((project-alist (if (not (stringp project)) (list project)
- ;; If this function is called in batch mode,
- ;; project is still a string here.
- (list (assoc project org-publish-project-alist)))))
- (if async
- (org-export-async-start (lambda (results) nil)
- `(let ((org-publish-use-timestamps-flag
- (if ',force nil ,org-publish-use-timestamps-flag)))
- (org-publish-projects ',project-alist)))
- (save-window-excursion
- (let* ((org-publish-use-timestamps-flag
- (if force nil org-publish-use-timestamps-flag)))
- (org-publish-projects project-alist))))))
+ (list (assoc (org-icompleting-read "Publish project: "
+ org-publish-project-alist nil t)
+ org-publish-project-alist)
+ current-prefix-arg))
+ (let ((project (if (not (stringp project)) project
+ ;; If this function is called in batch mode,
+ ;; PROJECT is still a string here.
+ (assoc project org-publish-project-alist))))
+ (cond
+ ((not project))
+ (async
+ (org-export-async-start (lambda (results) nil)
+ `(let ((org-publish-use-timestamps-flag
+ ,(and (not force) org-publish-use-timestamps-flag)))
+ ;; Expand components right now as external process may not
+ ;; be aware of complete `org-publish-project-alist'.
+ (org-publish-projects
+ ',(org-publish-expand-projects (list project))))))
+ (t (save-window-excursion
+ (let ((org-publish-use-timestamps-flag
+ (and (not force) org-publish-use-timestamps-flag)))
+ (org-publish-projects (list project))))))))
;;;###autoload
(defun org-publish-all (&optional force async)
@@ -1061,31 +1068,103 @@ publishing directory."
;; This part implements tools to resolve [[file.org::*Some headline]]
;; links, where "file.org" belongs to the current project.
-(defun org-publish-collect-numbering (output backend info)
+(defun org-publish--collect-references (output backend info)
+ "Store headlines references for current published file.
+
+OUPUT is the produced output, as a string. BACKEND is the export
+back-end used, as a symbol. INFO is the final export state, as
+a plist.
+
+References are stored as an alist ((TYPE SEARCH) . VALUE) where
+
+ TYPE is a symbol among `headline', `custom-id', `target' and
+ `other'.
+
+ SEARCH is the string a link is expected to match. It is
+
+ - headline's title, as a string, with all whitespace
+ characters and statistics cookies removed, if TYPE is
+ `headline'.
+
+ - CUSTOM_ID value if TYPE is `custom-id'.
+
+ - target's or radio-target's name if TYPE is `target'.
+
+ - NAME affiliated keyword is TYPE is `other'.
+
+ VALUE is an internal reference used in the document, as
+ a string.
+
+This function is meant to be used as a final out filter. See
+`org-publish-org-to'."
(org-publish-cache-set-file-property
- (plist-get info :input-file) :numbering
- (mapcar (lambda (entry)
- (cons (org-split-string
- (replace-regexp-in-string
- "\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" ""
- (org-element-property :raw-value (car entry))))
- (cdr entry)))
- (plist-get info :headline-numbering)))
+ (plist-get info :input-file) :references
+ (let (refs)
+ (when (hash-table-p (plist-get info :internal-references))
+ (maphash
+ (lambda (k v)
+ (case (org-element-type k)
+ ((headline inlinetask)
+ (push (cons
+ (cons 'headline
+ (org-split-string
+ (replace-regexp-in-string
+ "\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" ""
+ (org-element-property :raw-value k))))
+ v)
+ refs)
+ (let ((custom-id (org-element-property :CUSTOM_ID k)))
+ (when custom-id
+ (push (cons (cons 'custom-id custom-id) v) refs))))
+ ((radio-target target)
+ (push
+ (cons (cons 'target
+ (org-split-string (org-element-property :value k)))
+ v)
+ refs))
+ ((org-element-property :name k)
+ (push
+ (cons
+ (cons 'other (org-split-string (org-element-property :name k)))
+ v)
+ refs)))
+ refs)
+ (plist-get info :internal-references)))
+ refs))
;; Return output unchanged.
output)
-(defun org-publish-resolve-external-fuzzy-link (file fuzzy)
- "Return numbering for headline matching FUZZY search in FILE.
-
-Return value is a list of numbers, or nil. This function allows
-to resolve external fuzzy links like:
-
- [[file.org::*fuzzy][description]]"
- (when org-publish-cache
- (cdr (assoc (org-split-string
- (if (eq (aref fuzzy 0) ?*) (substring fuzzy 1) fuzzy))
- (org-publish-cache-get-file-property
- (expand-file-name file) :numbering nil t)))))
+(defun org-publish-resolve-external-link (search file)
+ "Return reference for elements or objects matching SEARCH in FILE.
+
+Return value is an internal reference, as a string.
+
+This function allows to resolve external links like:
+
+ [[file.org::*fuzzy][description]]
+ [[file.org::#custom-id][description]]
+ [[file.org::fuzzy][description]]"
+ (if (not org-publish-cache)
+ (progn
+ (message "Reference \"%s\" in file \"%s\" cannot be resolved without \
+publishing"
+ search
+ file)
+ "MissingReference")
+ (let ((references (org-publish-cache-get-file-property
+ (expand-file-name file) :references nil t)))
+ (cond
+ ((cdr (case (aref search 0)
+ (?* (assoc (cons 'headline (org-split-string (substring search 1)))
+ references))
+ (?# (assoc (cons 'custom-id (substring search 1)) references))
+ (t
+ (let ((s (org-split-string search)))
+ (or (assoc (cons 'target s) references)
+ (assoc (cons 'other s) references)
+ (assoc (cons 'headline s) references)))))))
+ (t (message "Unknown cross-reference \"%s\" in file \"%s\"" search file)
+ "MissingReference")))))
@@ -1164,22 +1243,30 @@ the file including them will be republished as well."
(org-inhibit-startup t)
(visiting (find-buffer-visiting filename))
included-files-ctime buf)
-
(when (equal (file-name-extension filename) "org")
(setq buf (find-file (expand-file-name filename)))
(with-current-buffer buf
(goto-char (point-min))
- (while (re-search-forward
- "^#\\+INCLUDE:[ \t]+\"\\([^\t\n\r\"]*\\)\"[ \t]*.*$" nil t)
- (let* ((included-file (expand-file-name (match-string 1))))
- (add-to-list 'included-files-ctime
- (org-publish-cache-ctime-of-src included-file) t))))
+ (while (re-search-forward "^[ \t]*#\\+INCLUDE:" nil t)
+ (let* ((element (org-element-at-point))
+ (included-file
+ (and (eq (org-element-type element) 'keyword)
+ (let ((value (org-element-property :value element)))
+ (and value
+ (string-match "^\\(\".+?\"\\|\\S-+\\)" value)
+ (org-remove-double-quotes
+ (match-string 1 value)))))))
+ (when included-file
+ (add-to-list 'included-files-ctime
+ (org-publish-cache-ctime-of-src
+ (expand-file-name included-file))
+ t)))))
(unless visiting (kill-buffer buf)))
(if (null pstamp) t
(let ((ctime (org-publish-cache-ctime-of-src filename)))
(or (< pstamp ctime)
(when included-files-ctime
- (not (null (delq nil (mapcar (lambda(ct) (< ctime ct))
+ (not (null (delq nil (mapcar (lambda (ct) (< ctime ct))
included-files-ctime))))))))))
(defun org-publish-cache-set-file-property