diff options
Diffstat (limited to 'lisp/ox-publish.el')
-rw-r--r-- | lisp/ox-publish.el | 243 |
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 |