>From c8ab9a3bf0f4f4fdf086ea3c085a327cc582cd94 Mon Sep 17 00:00:00 2001 From: Rasmus Date: Fri, 27 May 2016 16:55:37 +0200 Subject: [PATCH 4/6] ox-publish: More flexible sitemap formatting * lisp/ox-publish.el (org-publish-project-alist): Document changes. (org-publish-sitemap-sort-ignore-case): New default value. (org-publish-sitemap-file-entry-format): New defcustom. (org-publish-org-sitemap): Refactored to use new functions. (org-publish-org-sitemap-as-list): New function. (org-publish-org-sitemap-as-tree): New function. (org-publish-format-file-entry): Refactored to be more flexible. --- lisp/ox-publish.el | 988 +++++++++++++++++++++++++++++------------------------ 1 file changed, 545 insertions(+), 443 deletions(-) diff --git a/lisp/ox-publish.el b/lisp/ox-publish.el index 8a72349..d94ca04 100644 --- a/lisp/ox-publish.el +++ b/lisp/ox-publish.el @@ -217,10 +217,15 @@ a site-map of files or summary page for a given project. `:sitemap-style' - Can be `list' (site-map is just an itemized list of the - titles of the files involved) or `tree' (the directory - structure of the source files is reflected in the site-map). - Defaults to `tree'. + By default `list' (site-map is a list of files) or + `tree' (the directory structure of the source files is + reflected in the site-map). Defaults to `tree'. Files are + formatted according to `:sitemap-file-entry-format', + directories according to `:sitemap-dir-entry-format'. To add + new styles STYLE define a new function + `org-publish-org-sitemap-as-STYLE' that takes a list of files + and project-plist as arguments (assuming `:sitemap-function' + is `org-publish-org-sitemap'). `:sitemap-sans-extension' @@ -228,6 +233,12 @@ a site-map of files or summary page for a given project. cool URIs (see http://www.w3.org/Provider/Style/URI). Defaults to nil. + `:sitemap-file-entry-format' + `:sitemap-dir-entry-format' + + Format of filenames and directories included in the sitemap. + See `org-publish-sitemap-file-entry-format' for details. + If you create a site-map file, adjust the sorting like this: `:sitemap-sort-folders' @@ -322,15 +333,82 @@ See `format-time-string' for allowed formatters." :group 'org-export-publish :type 'string) -(defcustom org-publish-sitemap-file-entry-format "%t" +(defcustom org-publish-sitemap-file-entry-format "%- [[file:%link][%TITLE]]" "Format string for site-map file entry. -You could use brackets to delimit on what part the link will be. -%t is the title. -%a is the author. -%d is the date formatted using `org-publish-sitemap-date-format'." +Most keywords can be accessed using a the name of the keyword +prefixed with \"%\", e.g. \"%TITLE\" or \"%KEYWORDS\". In +addition, the following keywords are defined. + + %fullpath + The full path of the file. + + %relpath + The relative path of the file. + + %filename + %f + The filename. + + %link + %l + The link. + + %* + Leveled headline relative to the base directory. + + %* + Leveled item relative to the base directory. + + %author + The author of the document, the project, or `user-full-name'. + + %date + Date formatted using `org-publish-sitemap-date-format'. + +See also `org-publish-sitemap-dir-entry-format'." :group 'org-export-publish - :type 'string) + :type 'string + :version "25.2" + :package-version '(Org . "9.0")) + +(defcustom org-publish-sitemap-dir-entry-format "%- %f" + "Format string for site-map file entry. + +The following keywords are defined. + + %fullpath + The full path of the file. + + %relpath + The relative path of the file. + + %filename + %f + The filename. + + %link + %l + The link. + + %* + Leveled headline relative to the base directory. + + %* + Leveled item relative to the base directory. + + %author + The author of the document, the project, or `user-full-name'. + + %date + Date formatted using `org-publish-sitemap-date-format'. + +See also `org-publish-sitemap-file-entry-format'." + :group 'org-export-publish + :type 'string + :version "25.2" + :package-version '(Org . "9.0")) + @@ -340,7 +418,7 @@ You could use brackets to delimit on what part the link will be. (defun org-publish-timestamp-filename (filename &optional pub-dir pub-func) "Return path to timestamp file for filename FILENAME." (setq filename (concat filename "::" (or pub-dir "") "::" - (format "%s" (or pub-func "")))) + (format "%s" (or pub-func "")))) (concat "X" (if (fboundp 'sha1) (sha1 filename) (md5 filename)))) (defun org-publish-needed-p @@ -353,11 +431,11 @@ Right now we cannot do this, because we do not know under what file name the file will be stored - the publishing function can still decide about that independently." (let ((rtn (if (not org-publish-use-timestamps-flag) t - (org-publish-cache-file-needs-publishing - filename pub-dir pub-func base-dir)))) + (org-publish-cache-file-needs-publishing + filename pub-dir pub-func base-dir)))) (if rtn (message "Publishing file %s using `%s'" filename pub-func) (when org-publish-list-skipped-files - (message "Skipping unmodified file %s" filename))) + (message "Skipping unmodified file %s" filename))) rtn)) (defun org-publish-update-timestamp @@ -365,7 +443,7 @@ still decide about that independently." "Update publishing timestamp for file FILENAME. If there is no timestamp, create one." (let ((key (org-publish-timestamp-filename filename pub-dir pub-func)) - (stamp (org-publish-cache-ctime-of-src filename))) + (stamp (org-publish-cache-ctime-of-src filename))) (org-publish-cache-set key stamp))) (defun org-publish-remove-all-timestamps () @@ -386,11 +464,11 @@ This splices all the components into the list." (let ((rest projects-alist) rtn p components) (while (setq p (pop rest)) (if (setq components (plist-get (cdr p) :components)) - (setq rest (append - (mapcar (lambda (x) (assoc x org-publish-project-alist)) - components) - rest)) - (push p rtn))) + (setq rest (append + (mapcar (lambda (x) (assoc x org-publish-project-alist)) + components) + rest)) + (push p rtn))) (nreverse (delete-dups (delq nil rtn))))) (defvar org-publish-sitemap-sort-files) @@ -405,35 +483,35 @@ This splices all the components into the list." (when (or org-publish-sitemap-sort-files org-publish-sitemap-sort-folders) ;; First we sort files: (when org-publish-sitemap-sort-files - (pcase org-publish-sitemap-sort-files - (`alphabetically - (let* ((adir (file-directory-p a)) - (aorg (and (string-match "\\.org$" a) (not adir))) - (bdir (file-directory-p b)) - (borg (and (string-match "\\.org$" b) (not bdir))) - (A (if aorg (concat (file-name-directory a) - (org-publish-find-title a)) a)) - (B (if borg (concat (file-name-directory b) - (org-publish-find-title b)) b))) - (setq retval (if org-publish-sitemap-ignore-case - (not (string-lessp (upcase B) (upcase A))) - (not (string-lessp B A)))))) - ((or `anti-chronologically `chronologically) - (let* ((adate (org-publish-find-date a)) - (bdate (org-publish-find-date b)) - (A (+ (lsh (car adate) 16) (cadr adate))) - (B (+ (lsh (car bdate) 16) (cadr bdate)))) - (setq retval - (if (eq org-publish-sitemap-sort-files 'chronologically) - (<= A B) - (>= A B))))))) + (pcase org-publish-sitemap-sort-files + (`alphabetically + (let* ((adir (file-directory-p a)) + (aorg (and (string-match "\\.org$" a) (not adir))) + (bdir (file-directory-p b)) + (borg (and (string-match "\\.org$" b) (not bdir))) + (A (if aorg (concat (file-name-directory a) + (org-publish-find-title a)) a)) + (B (if borg (concat (file-name-directory b) + (org-publish-find-title b)) b))) + (setq retval (if org-publish-sitemap-ignore-case + (not (string-lessp (upcase B) (upcase A))) + (not (string-lessp B A)))))) + ((or `anti-chronologically `chronologically) + (let* ((adate (org-publish-find-date a)) + (bdate (org-publish-find-date b)) + (A (+ (lsh (car adate) 16) (cadr adate))) + (B (+ (lsh (car bdate) 16) (cadr bdate)))) + (setq retval + (if (eq org-publish-sitemap-sort-files 'chronologically) + (<= A B) + (>= A B))))))) ;; Directory-wise wins: (when org-publish-sitemap-sort-folders ;; a is directory, b not: (cond ((and (file-directory-p a) (not (file-directory-p b))) (setq retval (equal org-publish-sitemap-sort-folders 'first))) - ;; a is not a directory, but b is: + ;; a is not a directory, but b is: ((and (not (file-directory-p a)) (file-directory-p b)) (setq retval (equal org-publish-sitemap-sort-folders 'last)))))) retval)) @@ -448,65 +526,65 @@ SKIP-FILE. If SKIP-DIR is non-nil, don't check directories matching the regexp SKIP-DIR when recursing through BASE-DIR. If INCLUDE-DIRS is non-nil include directories" (let ((all-files (if (not recurse) (directory-files base-dir t match) - ;; If RECURSE is non-nil, we want all files - ;; matching MATCH and sub-directories. - (cl-remove-if-not - (lambda (file) - (or (file-directory-p file) - (and match (string-match-p match file)))) - (directory-files base-dir t))))) + ;; If RECURSE is non-nil, we want all files + ;; matching MATCH and sub-directories. + (cl-remove-if-not + (lambda (file) + (or (file-directory-p file) + (and match (string-match-p match file)))) + (directory-files base-dir t))))) (dolist (f (if (not org-publish-sitemap-requested) all-files - (sort all-files #'org-publish-compare-directory-files))) + (sort all-files #'org-publish-compare-directory-files))) (let ((fd-p (file-directory-p f)) - (fnd (file-name-nondirectory f))) - (if (and fd-p recurse - (not (string-match "^\\.+$" fnd)) - (if skip-dir (not (string-match skip-dir fnd)) t)) - (progn - (and include-dirs - (cl-pushnew f org-publish-temp-files :test #'file-equal-p)) - (org-publish-get-base-files-1 - f recurse match skip-file skip-dir include-dirs)) - (unless (or fd-p ; This is a directory. - (and skip-file (string-match skip-file fnd)) - (not (file-exists-p (file-truename f))) - (not (string-match match fnd))) - (cl-pushnew f org-publish-temp-files :test #'file-equal-p))))))) + (fnd (file-name-nondirectory f))) + (if (and fd-p recurse + (not (string-match "^\\.+$" fnd)) + (if skip-dir (not (string-match skip-dir fnd)) t)) + (progn + (and include-dirs + (cl-pushnew f org-publish-temp-files :test #'file-equal-p)) + (org-publish-get-base-files-1 + f recurse match skip-file skip-dir include-dirs)) + (unless (or fd-p ; This is a directory. + (and skip-file (string-match skip-file fnd)) + (not (file-exists-p (file-truename f))) + (not (string-match match fnd))) + (cl-pushnew f org-publish-temp-files :test #'file-equal-p))))))) (defun org-publish-get-base-files (project &optional exclude-regexp include-dirs) "Return a list of all files in PROJECT. If EXCLUDE-REGEXP is set, this will be used to filter out matching filenames." (let* ((project-plist (cdr project)) - (base-dir (file-name-as-directory - (plist-get project-plist :base-directory))) - (include-list (plist-get project-plist :include)) - (recurse (plist-get project-plist :recursive)) - (extension (or (plist-get project-plist :base-extension) "org")) - ;; sitemap-... variables are dynamically scoped for - ;; org-publish-compare-directory-files: - (org-publish-sitemap-requested - (plist-get project-plist :auto-sitemap)) - (sitemap-filename - (or (plist-get project-plist :sitemap-filename) "sitemap.org")) - (org-publish-sitemap-sort-folders - (if (plist-member project-plist :sitemap-sort-folders) - (plist-get project-plist :sitemap-sort-folders) - org-publish-sitemap-sort-folders)) - (org-publish-sitemap-sort-files - (cond ((plist-member project-plist :sitemap-sort-files) - (plist-get project-plist :sitemap-sort-files)) - ;; For backward compatibility: - ((plist-member project-plist :sitemap-alphabetically) - (if (plist-get project-plist :sitemap-alphabetically) - 'alphabetically nil)) - (t org-publish-sitemap-sort-files))) - (org-publish-sitemap-ignore-case - (if (plist-member project-plist :sitemap-ignore-case) - (plist-get project-plist :sitemap-ignore-case) - org-publish-sitemap-sort-ignore-case)) - (match (if (eq extension 'any) "^[^\\.]" - (concat "^[^\\.].*\\.\\(" extension "\\)$")))) + (base-dir (file-name-as-directory + (plist-get project-plist :base-directory))) + (include-list (plist-get project-plist :include)) + (recurse (plist-get project-plist :recursive)) + (extension (or (plist-get project-plist :base-extension) "org")) + ;; sitemap-... variables are dynamically scoped for + ;; org-publish-compare-directory-files: + (org-publish-sitemap-requested + (plist-get project-plist :auto-sitemap)) + (sitemap-filename + (or (plist-get project-plist :sitemap-filename) "sitemap.org")) + (org-publish-sitemap-sort-folders + (if (plist-member project-plist :sitemap-sort-folders) + (plist-get project-plist :sitemap-sort-folders) + org-publish-sitemap-sort-folders)) + (org-publish-sitemap-sort-files + (cond ((plist-member project-plist :sitemap-sort-files) + (plist-get project-plist :sitemap-sort-files)) + ;; For backward compatibility: + ((plist-member project-plist :sitemap-alphabetically) + (if (plist-get project-plist :sitemap-alphabetically) + 'alphabetically nil)) + (t org-publish-sitemap-sort-files))) + (org-publish-sitemap-ignore-case + (if (plist-member project-plist :sitemap-ignore-case) + (plist-get project-plist :sitemap-ignore-case) + org-publish-sitemap-sort-ignore-case)) + (match (if (eq extension 'any) "^[^\\.]" + (concat "^[^\\.].*\\.\\(" extension "\\)$")))) ;; Make sure `org-publish-sitemap-sort-folders' has an accepted ;; value. (unless (memq org-publish-sitemap-sort-folders '(first last)) @@ -515,14 +593,14 @@ matching filenames." (setq org-publish-temp-files nil) (when org-publish-sitemap-requested (cl-pushnew (expand-file-name (concat base-dir sitemap-filename)) - org-publish-temp-files :test #'file-equal-p)) + org-publish-temp-files :test #'file-equal-p)) (org-publish-get-base-files-1 base-dir recurse match - ;; FIXME distinguish exclude regexp - ;; for skip-file and skip-dir? - exclude-regexp exclude-regexp include-dirs) + ;; FIXME distinguish exclude regexp + ;; for skip-file and skip-dir? + exclude-regexp exclude-regexp include-dirs) (dolist (f include-list org-publish-temp-files) (cl-pushnew (expand-file-name (concat base-dir f)) - org-publish-temp-files :test #'file-equal-p)) + org-publish-temp-files :test #'file-equal-p)) ;; Remove directories from `org-publish-temp-files' that do not ;; hold project files. (when include-dirs @@ -537,31 +615,31 @@ matching filenames." (defun org-publish-get-project-from-filename (filename &optional up) "Return the project that FILENAME belongs to." (let* ((filename (expand-file-name filename)) - project-name) + project-name) (catch 'p-found (dolist (prj org-publish-project-alist) - (unless (plist-get (cdr prj) :components) - ;; [[info:org:Selecting%20files]] shows how this is supposed to work: - (let* ((r (plist-get (cdr prj) :recursive)) - (b (expand-file-name (file-name-as-directory - (plist-get (cdr prj) :base-directory)))) - (x (or (plist-get (cdr prj) :base-extension) "org")) - (e (plist-get (cdr prj) :exclude)) - (i (plist-get (cdr prj) :include)) - (xm (concat "^" b (if r ".+" "[^/]+") "\\.\\(" x "\\)$"))) - (when - (or (and i - (member filename - (dolist (file i) (expand-file-name file b)))) - (and (not (and e (string-match e filename))) - (string-match xm filename))) - (setq project-name (car prj)) - (throw 'p-found project-name)))))) + (unless (plist-get (cdr prj) :components) + ;; [[info:org:Selecting%20files]] shows how this is supposed to work: + (let* ((r (plist-get (cdr prj) :recursive)) + (b (expand-file-name (file-name-as-directory + (plist-get (cdr prj) :base-directory)))) + (x (or (plist-get (cdr prj) :base-extension) "org")) + (e (plist-get (cdr prj) :exclude)) + (i (plist-get (cdr prj) :include)) + (xm (concat "^" b (if r ".+" "[^/]+") "\\.\\(" x "\\)$"))) + (when + (or (and i + (member filename + (dolist (file i) (expand-file-name file b)))) + (and (not (and e (string-match e filename))) + (string-match xm filename))) + (setq project-name (car prj)) + (throw 'p-found project-name)))))) (when up (dolist (prj org-publish-project-alist) - (if (member project-name (plist-get (cdr prj) :components)) - (setq project-name (car prj))))) + (if (member project-name (plist-get (cdr prj) :components)) + (setq project-name (car prj))))) (assoc project-name org-publish-project-alist))) @@ -585,26 +663,26 @@ Return output file name." (unless (or (not pub-dir) (file-exists-p pub-dir)) (make-directory pub-dir t)) ;; Check if a buffer visiting FILENAME is already open. (let* ((org-inhibit-startup t) - (visiting (find-buffer-visiting filename)) - (work-buffer (or visiting (find-file-noselect filename)))) + (visiting (find-buffer-visiting filename)) + (work-buffer (or visiting (find-file-noselect filename)))) (unwind-protect - (with-current-buffer work-buffer - (let ((output (org-export-output-file-name extension nil pub-dir))) - (org-export-to-file backend output - nil nil nil (plist-get plist :body-only) - ;; Add `org-publish--store-crossrefs' 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 in cache anyway. - (org-combine-plists - plist - `(:crossrefs - ,(org-publish-cache-get-file-property - (expand-file-name filename) :crossrefs nil t) - :filter-final-output - (org-publish--store-crossrefs - org-publish-collect-index - ,@(plist-get plist :filter-final-output))))))) + (with-current-buffer work-buffer + (let ((output (org-export-output-file-name extension nil pub-dir))) + (org-export-to-file backend output + nil nil nil (plist-get plist :body-only) + ;; Add `org-publish--store-crossrefs' 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 in cache anyway. + (org-combine-plists + plist + `(:crossrefs + ,(org-publish-cache-get-file-property + (expand-file-name filename) :crossrefs nil t) + :filter-final-output + (org-publish--store-crossrefs + org-publish-collect-index + ,@(plist-get plist :filter-final-output))))))) ;; Remove opened buffer in the process. (unless visiting (kill-buffer work-buffer))))) @@ -620,8 +698,8 @@ Return output file name." (make-directory 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)) + (file-name-as-directory (expand-file-name pub-dir))) + (copy-file filename output t)) ;; Return file name. output)) @@ -637,46 +715,46 @@ This is needed, since this function is used to publish single files, when entire projects are published (see `org-publish-projects')." (let* ((project - (or project - (or (org-publish-get-project-from-filename filename) - (error "File %s not part of any known project" - (abbreviate-file-name filename))))) - (project-plist (cdr project)) - (ftname (expand-file-name filename)) - (publishing-function - (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 - (or (plist-get project-plist :base-directory) - (error "Project %s does not have :base-directory defined" - (car project)))))) - (pub-dir - (file-name-as-directory - (file-truename - (or (eval (plist-get project-plist :publishing-directory)) - (error "Project %s does not have :publishing-directory defined" - (car project)))))) - tmp-pub-dir) + (or project + (or (org-publish-get-project-from-filename filename) + (error "File %s not part of any known project" + (abbreviate-file-name filename))))) + (project-plist (cdr project)) + (ftname (expand-file-name filename)) + (publishing-function + (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 + (or (plist-get project-plist :base-directory) + (error "Project %s does not have :base-directory defined" + (car project)))))) + (pub-dir + (file-name-as-directory + (file-truename + (or (eval (plist-get project-plist :publishing-directory)) + (error "Project %s does not have :publishing-directory defined" + (car project)))))) + tmp-pub-dir) (unless no-cache (org-publish-initialize-cache (car project))) (setq tmp-pub-dir - (file-name-directory - (concat pub-dir - (and (string-match (regexp-quote base-dir) ftname) - (substring ftname (match-end 0)))))) + (file-name-directory + (concat pub-dir + (and (string-match (regexp-quote base-dir) ftname) + (substring ftname (match-end 0)))))) ;; 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)))) + (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)))) ;; Make sure to write cache to file after successfully publishing ;; a file, so as to minimize impact of a publishing failure. (org-publish-write-cache-file))) @@ -688,42 +766,45 @@ If `:auto-sitemap' is set, publish the sitemap too. If (dolist (project (org-publish-expand-projects projects)) (let ((project-plist (cdr project))) (let ((f (plist-get project-plist :preparation-function))) - (cond ((consp f) (mapc #'funcall f)) - ((functionp f) (funcall f)))) + (cond ((consp f) (mapc #'funcall f)) + ((functionp f) (funcall f)))) ;; Each project uses its own cache file. (org-publish-initialize-cache (car project)) (when (plist-get project-plist :auto-sitemap) - (let ((sitemap-filename - (or (plist-get project-plist :sitemap-filename) - "sitemap.org")) - (sitemap-function - (or (plist-get project-plist :sitemap-function) - #'org-publish-org-sitemap)) - (org-publish-sitemap-date-format - (or (plist-get project-plist :sitemap-date-format) - org-publish-sitemap-date-format)) - (org-publish-sitemap-file-entry-format - (or (plist-get project-plist :sitemap-file-entry-format) - org-publish-sitemap-file-entry-format))) - (funcall sitemap-function project sitemap-filename))) + (let ((sitemap-filename + (or (plist-get project-plist :sitemap-filename) + "sitemap.org")) + (sitemap-function + (or (plist-get project-plist :sitemap-function) + #'org-publish-org-sitemap)) + (org-publish-sitemap-date-format + (or (plist-get project-plist :sitemap-date-format) + org-publish-sitemap-date-format)) + (org-publish-sitemap-file-entry-format + (or (plist-get project-plist :sitemap-file-entry-format) + org-publish-sitemap-file-entry-format)) + (org-publish-sitemap-dir-entry-format + (or (plist-get project-plist :sitemap-dir-entry-format) + org-publish-sitemap-dir-entry-format)) + (funcall sitemap-function project sitemap-filename))) ;; Publish all files from PROJECT excepted "theindex.org". Its ;; publishing will be deferred until "theindex.inc" is ;; populated. (let ((theindex - (expand-file-name "theindex.org" - (plist-get project-plist :base-directory))) - (exclude-regexp (plist-get project-plist :exclude))) - (dolist (file (org-publish-get-base-files project exclude-regexp)) - (unless (equal file theindex) (org-publish-file file project t))) - ;; Populate "theindex.inc", if needed, and publish - ;; "theindex.org". - (when (plist-get project-plist :makeindex) - (org-publish-index-generate-theindex - project (plist-get project-plist :base-directory)) - (org-publish-file theindex project t))) + (expand-file-name "theindex.org" + (plist-get project-plist :base-directory))) + (exclude-regexp (plist-get project-plist :exclude))) + (dolist (file (org-publish-get-base-files project exclude-regexp)) + (unless (equal file theindex) (org-publish-file file project t))) + ;; Populate "theindex.inc", if needed, and publish + ;; "theindex.org". + (when (plist-get project-plist :makeindex) + (org-publish-index-generate-theindex + project (plist-get project-plist :base-directory)) + (org-publish-file theindex project t))) (let ((f (plist-get project-plist :completion-function))) - (cond ((consp f) (mapc #'funcall f)) - ((functionp f) (funcall f)))) + (cond ((consp f) (mapc #'funcall f)) + ((functionp f) (funcall f)))) (org-publish-write-cache-file)))) (defun org-publish-org-sitemap (project &optional sitemap-filename) @@ -731,88 +812,109 @@ If `:auto-sitemap' is set, publish the sitemap too. If Optionally set the filename of the sitemap with SITEMAP-FILENAME. Default for SITEMAP-FILENAME is `sitemap.org'." (let* ((project-plist (cdr project)) - (dir (file-name-as-directory - (plist-get project-plist :base-directory))) - (localdir (file-name-directory dir)) - (indent-str (make-string 2 ?\ )) - (exclude-regexp (plist-get project-plist :exclude)) - (files (nreverse - (org-publish-get-base-files project exclude-regexp))) - (sitemap-filename (concat dir (or sitemap-filename "sitemap.org"))) - (sitemap-title (or (plist-get project-plist :sitemap-title) - (concat "Sitemap for project " (car project)))) - (sitemap-style (or (plist-get project-plist :sitemap-style) - 'tree)) - (sitemap-sans-extension - (plist-get project-plist :sitemap-sans-extension)) - (visiting (find-buffer-visiting sitemap-filename)) - file sitemap-buffer) - (with-current-buffer - (let ((org-inhibit-startup t)) - (setq sitemap-buffer - (or visiting (find-file sitemap-filename)))) - (erase-buffer) - (insert (concat "#+TITLE: " sitemap-title "\n\n")) - (while (setq file (pop files)) - (let ((link (file-relative-name file dir)) - (oldlocal localdir)) - (when sitemap-sans-extension - (setq link (file-name-sans-extension link))) - ;; sitemap shouldn't list itself - (unless (equal (file-truename sitemap-filename) - (file-truename file)) - (if (eq sitemap-style 'list) - (message "Generating list-style sitemap for %s" sitemap-title) - (message "Generating tree-style sitemap for %s" sitemap-title) - (setq localdir (concat (file-name-as-directory dir) - (file-name-directory link))) - (unless (string= localdir oldlocal) - (if (string= localdir dir) - (setq indent-str (make-string 2 ?\ )) - (let ((subdirs - (split-string - (directory-file-name - (file-name-directory - (file-relative-name localdir dir))) "/")) - (subdir "") - (old-subdirs (split-string - (file-relative-name oldlocal dir) "/"))) - (setq indent-str (make-string 2 ?\ )) - (while (string= (car old-subdirs) (car subdirs)) - (setq indent-str (concat indent-str (make-string 2 ?\ ))) - (pop old-subdirs) - (pop subdirs)) - (dolist (d subdirs) - (setq subdir (concat subdir d "/")) - (insert (concat indent-str " + " d "\n")) - (setq indent-str (make-string - (+ (length indent-str) 2) ?\ ))))))) - ;; This is common to 'flat and 'tree - (let ((entry - (org-publish-format-file-entry - org-publish-sitemap-file-entry-format file project-plist)) - (regexp "\\(.*\\)\\[\\([^][]+\\)\\]\\(.*\\)")) - (cond ((string-match-p regexp entry) - (string-match regexp entry) - (insert (concat indent-str " + " (match-string 1 entry) - "[[file:" link "][" - (match-string 2 entry) - "]]" (match-string 3 entry) "\n"))) - (t - (insert (concat indent-str " + [[file:" link "][" - entry - "]]\n")))))))) - (save-buffer)) - (or visiting (kill-buffer sitemap-buffer)))) + (dir (file-name-as-directory + (plist-get project-plist :base-directory))) + (sitemap-filename (concat dir (or sitemap-filename "sitemap.org"))) + (files (nreverse + ;; Sitemap shouldn't list itself. + (cl-remove sitemap-filename + (org-publish-get-base-files + project + (plist-get project-plist :exclude) t) + :test #'equal))) + (sitemap-title (or (plist-get project-plist :sitemap-title) + (concat "Sitemap for project " (car project))))) + (with-temp-file sitemap-filename + (insert + (mapconcat + 'identity + (list + (format "#+TITLE: %s" sitemap-title) + ;; Call function to build sitemap based on files and the project-plist. + (let* ((style (or (plist-get project-plist :sitemap-style) 'tree)) + (fun (intern (format "org-publish-org-sitemap-as-%s" style)))) + (unless (functionp fun) + (error "Unknown function %s for :sitemap-style %s." + fun style)) + (funcall fun files project-plist))) "\n"))))) + +(defun org-publish-org-sitemap-as-list (files project-plist) + "Insert FILES as simple list separated by newlines. +PROJECT-PLIST holds the project information." + (mapconcat + (lambda (file) + (org-publish-format-file-entry + org-publish-sitemap-file-entry-format file project-plist)) + (cl-delete-if 'file-directory-p files) "\n")) + +(defun org-publish-org-sitemap-as-tree (files project-plist) + "Insert FILES as a tree. +PROJECT-PLIST holds the project information." + (mapconcat (lambda (elm) + (org-publish-format-file-entry + (cond + ((file-directory-p elm) org-publish-sitemap-dir-entry-format) + (t org-publish-sitemap-file-entry-format)) + elm project-plist)) + (cl-remove (plist-get project-plist :base-directory) + files :test #'file-equal-p) + "\n")) (defun org-publish-format-file-entry (fmt file project-plist) - (format-spec - fmt - `((?t . ,(org-publish-find-title file t)) - (?d . ,(format-time-string org-publish-sitemap-date-format - (org-publish-find-date file))) - (?a . ,(or (plist-get project-plist :author) user-full-name))))) - + "Format FILE according to the format-string FMT. +PROJECT-PLIST is a plist holding project options. +See also `org-publish-sitemap-file-entry-format'. +" + (let* ((basedir (file-truename (plist-get project-plist :base-directory))) + (filename (file-relative-name file basedir)) + (dirname (file-name-directory filename)) + (dirp (file-directory-p file)) + (depth (if (or (eq 'list (plist-get project-plist :sitemap-style)) + (not dirname)) + 1 + (+ (if (not (directory-name-p filename)) 1 0) + (length (split-string (file-name-directory filename) "/" t))))) + (link (funcall (if (plist-get project-plist :sitemap-sans-extension) + #'file-name-sans-extension + #'identity) + filename)) + (relfilepath (if (file-equal-p file basedir) "" + (directory-file-name + (if dirp + (file-relative-name + filename (file-name-directory (directory-file-name filename))) + (file-relative-name filename dirname))))) + (date (unless dirp + (format-time-string + (or (plist-get project-plist :sitemap-date-format) + org-publish-sitemap-date-format) + (org-publish-find-date file)))) + (info (list (list "fullpath" file) + (list "relpath" filename) + (list "filename" relfilepath) + (list "f" relfilepath) + (list "l" link) + (list "link" link) + (list "*" (concat (make-string depth ?*))) + (list "-" (concat (make-string (* 2 depth) ?\s) "-")) + (list "date" (format-time-string + (or (plist-get project-plist :sitemap-date-format) + org-publish-sitemap-date-format) + (org-publish-find-date file))) + (list "author" (or (unless dirp (org-publish-find-property file :author t)) + (plist-get project-plist :author) + user-full-name)) + ;; TODO: Insert legacy values here... + ))) + (with-temp-buffer + (save-excursion (insert fmt)) + (while (re-search-forward "%\\([a-zA-Z-*]+\\>?\\)" nil t) + (save-excursion + (replace-match (or (cadr (assoc-string (match-string 1) info t)) + (save-match-data (org-publish-find-property file (match-string 1) t)) + (error "Could not expand \"%s\" while formatting sitemap entry for \"%s\"" + (match-string 1) file))))) + (buffer-string)))) (defun org-publish-find-property (file property &optional reset) "Find the PROPERTY of FILE in project. @@ -858,12 +960,12 @@ time in `current-time' format." ;; convert it to internal format. Otherwise, use FILE ;; modification time. (cond ((let ((ts (and (consp date) (assq 'timestamp date)))) - (and ts - (let ((value (org-element-interpret-data ts))) - (and (org-string-nw-p value) - (org-time-string-to-time value)))))) - ((file-exists-p file) (nth 5 (file-attributes file))) - (t (error "No such file: \"%s\"" file)))))) + (and ts + (let ((value (org-element-interpret-data ts))) + (and (org-string-nw-p value) + (org-time-string-to-time value)))))) + ((file-exists-p file) (nth 5 (file-attributes file))) + (t (error "No such file: \"%s\"" file)))))) @@ -885,27 +987,27 @@ files in PROJECT. With a non-nil optional argument ASYNC, publishing will be done asynchronously, in another process." (interactive (list (assoc (completing-read "Publish project: " - org-publish-project-alist nil t) - org-publish-project-alist) - current-prefix-arg)) + 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)))) + ;; 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 (_) 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)))))) + `(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)))))))) + (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) @@ -917,16 +1019,16 @@ in another process." (interactive "P") (if async (org-export-async-start (lambda (_) nil) - `(progn - (when ',force (org-publish-remove-all-timestamps)) - (let ((org-publish-use-timestamps-flag - (if ',force nil ,org-publish-use-timestamps-flag))) - (org-publish-projects ',org-publish-project-alist)))) + `(progn + (when ',force (org-publish-remove-all-timestamps)) + (let ((org-publish-use-timestamps-flag + (if ',force nil ,org-publish-use-timestamps-flag))) + (org-publish-projects ',org-publish-project-alist)))) (when force (org-publish-remove-all-timestamps)) (save-window-excursion (let ((org-publish-use-timestamps-flag - (if force nil org-publish-use-timestamps-flag))) - (org-publish-projects org-publish-project-alist))))) + (if force nil org-publish-use-timestamps-flag))) + (org-publish-projects org-publish-project-alist))))) ;;;###autoload @@ -938,14 +1040,14 @@ asynchronously, in another process." (interactive "P") (let ((file (buffer-file-name (buffer-base-buffer)))) (if async - (org-export-async-start (lambda (_) nil) - `(let ((org-publish-use-timestamps-flag - (if ',force nil ,org-publish-use-timestamps-flag))) - (org-publish-file ,file))) + (org-export-async-start (lambda (_) nil) + `(let ((org-publish-use-timestamps-flag + (if ',force nil ,org-publish-use-timestamps-flag))) + (org-publish-file ,file))) (save-window-excursion - (let ((org-publish-use-timestamps-flag - (if force nil org-publish-use-timestamps-flag))) - (org-publish-file file)))))) + (let ((org-publish-use-timestamps-flag + (if force nil org-publish-use-timestamps-flag))) + (org-publish-file file)))))) ;;;###autoload (defun org-publish-current-project (&optional force async) @@ -955,10 +1057,10 @@ the project." (interactive "P") (save-window-excursion (let ((project (org-publish-get-project-from-filename - (buffer-file-name (buffer-base-buffer)) 'up))) + (buffer-file-name (buffer-base-buffer)) 'up))) (if project (org-publish project force async) - (error "File %s is not part of any known project" - (buffer-file-name (buffer-base-buffer))))))) + (error "File %s is not part of any known project" + (buffer-file-name (buffer-base-buffer))))))) @@ -984,23 +1086,23 @@ its CDR is a string." file :index (delete-dups (org-element-map (plist-get info :parse-tree) 'keyword - (lambda (k) - (when (equal (org-element-property :key k) "INDEX") - (let ((parent (org-export-get-parent-headline k))) - (list (org-element-property :value k) - file - (cond - ((not parent) nil) - ((let ((id (org-element-property :ID parent))) - (and id (cons 'id id)))) - ((let ((id (org-element-property :CUSTOM_ID parent))) - (and id (cons 'custom-id id)))) - (t (cons 'name - ;; Remove statistics cookie. - (replace-regexp-in-string - "\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" "" - (org-element-property :raw-value parent))))))))) - info)))) + (lambda (k) + (when (equal (org-element-property :key k) "INDEX") + (let ((parent (org-export-get-parent-headline k))) + (list (org-element-property :value k) + file + (cond + ((not parent) nil) + ((let ((id (org-element-property :ID parent))) + (and id (cons 'id id)))) + ((let ((id (org-element-property :CUSTOM_ID parent))) + (and id (cons 'custom-id id)))) + (t (cons 'name + ;; Remove statistics cookie. + (replace-regexp-in-string + "\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" "" + (org-element-property :raw-value parent))))))))) + info)))) ;; Return output unchanged. output) @@ -1009,68 +1111,68 @@ its CDR is a string." PROJECT is the project the index relates to. DIRECTORY is the publishing directory." (let ((all-files (org-publish-get-base-files - project (plist-get (cdr project) :exclude))) - full-index) + project (plist-get (cdr project) :exclude))) + full-index) ;; Compile full index and sort it alphabetically. (dolist (file all-files - (setq full-index - (sort (nreverse full-index) - (lambda (a b) (string< (downcase (car a)) - (downcase (car b))))))) + (setq full-index + (sort (nreverse full-index) + (lambda (a b) (string< (downcase (car a)) + (downcase (car b))))))) (let ((index (org-publish-cache-get-file-property file :index))) - (dolist (term index) - (unless (member term full-index) (push term full-index))))) + (dolist (term index) + (unless (member term full-index) (push term full-index))))) ;; Write "theindex.inc" in DIRECTORY. (with-temp-file (expand-file-name "theindex.inc" directory) (let ((current-letter nil) (last-entry nil)) - (dolist (idx full-index) - (let* ((entry (org-split-string (car idx) "!")) - (letter (upcase (substring (car entry) 0 1))) - ;; Transform file into a path relative to publishing - ;; directory. - (file (file-relative-name - (nth 1 idx) - (plist-get (cdr project) :base-directory)))) - ;; Check if another letter has to be inserted. - (unless (string= letter current-letter) - (insert (format "* %s\n" letter))) - ;; Compute the first difference between last entry and - ;; current one: it tells the level at which new items - ;; should be added. - (let* ((rank - (if (equal entry last-entry) (1- (length entry)) - (cl-loop for n from 0 to (length entry) - unless (equal (nth n entry) (nth n last-entry)) - return n))) - (len (length (nthcdr rank entry)))) - ;; For each term after the first difference, create - ;; a new sub-list with the term as body. Moreover, - ;; linkify the last term. - (dotimes (n len) - (insert - (concat - (make-string (* (+ rank n) 2) ? ) " - " - (if (not (= (1- len) n)) (nth (+ rank n) entry) - ;; Last term: Link it to TARGET, if possible. - (let ((target (nth 2 idx))) - (format - "[[%s][%s]]" - ;; Destination. - (pcase (car target) - (`nil (format "file:%s" file)) - (`id (format "id:%s" (cdr target))) - (`custom-id (format "file:%s::#%s" file (cdr target))) - (_ (format "file:%s::*%s" file (cdr target)))) - ;; Description. - (car (last entry))))) - "\n")))) - (setq current-letter letter last-entry entry)))) + (dolist (idx full-index) + (let* ((entry (org-split-string (car idx) "!")) + (letter (upcase (substring (car entry) 0 1))) + ;; Transform file into a path relative to publishing + ;; directory. + (file (file-relative-name + (nth 1 idx) + (plist-get (cdr project) :base-directory)))) + ;; Check if another letter has to be inserted. + (unless (string= letter current-letter) + (insert (format "* %s\n" letter))) + ;; Compute the first difference between last entry and + ;; current one: it tells the level at which new items + ;; should be added. + (let* ((rank + (if (equal entry last-entry) (1- (length entry)) + (cl-loop for n from 0 to (length entry) + unless (equal (nth n entry) (nth n last-entry)) + return n))) + (len (length (nthcdr rank entry)))) + ;; For each term after the first difference, create + ;; a new sub-list with the term as body. Moreover, + ;; linkify the last term. + (dotimes (n len) + (insert + (concat + (make-string (* (+ rank n) 2) ? ) " - " + (if (not (= (1- len) n)) (nth (+ rank n) entry) + ;; Last term: Link it to TARGET, if possible. + (let ((target (nth 2 idx))) + (format + "[[%s][%s]]" + ;; Destination. + (pcase (car target) + (`nil (format "file:%s" file)) + (`id (format "id:%s" (cdr target))) + (`custom-id (format "file:%s::#%s" file (cdr target))) + (_ (format "file:%s::*%s" file (cdr target)))) + ;; Description. + (car (last entry))))) + "\n")))) + (setq current-letter letter last-entry entry)))) ;; Create "theindex.org", if it doesn't exist yet, and provide ;; a default index file. (let ((index.org (expand-file-name "theindex.org" directory))) - (unless (file-exists-p index.org) - (with-temp-file index.org - (insert "#+TITLE: Index\n\n#+INCLUDE: \"theindex.inc\"\n\n"))))))) + (unless (file-exists-p index.org) + (with-temp-file index.org + (insert "#+TITLE: Index\n\n#+INCLUDE: \"theindex.inc\"\n\n"))))))) @@ -1095,7 +1197,7 @@ This function is meant to be used as a final output filter. See ;; `:internal-references', with references as strings removed. See ;; `org-export-get-reference' for details. (cl-remove-if (lambda (pair) (stringp (car pair))) - (plist-get info :internal-references))) + (plist-get info :internal-references))) ;; Return output unchanged. output) @@ -1115,27 +1217,27 @@ It only makes sense to use this if export back-end builds references with `org-export-get-reference'." (if (not org-publish-cache) (progn - (message "Reference %S in file %S cannot be resolved without publishing" - search - file) - "MissingReference") + (message "Reference %S in file %S cannot be resolved without publishing" + search + file) + "MissingReference") (let* ((filename (expand-file-name file)) - (crossrefs - (org-publish-cache-get-file-property filename :crossrefs nil t)) - (cells (org-export-string-to-search-cell search))) + (crossrefs + (org-publish-cache-get-file-property filename :crossrefs nil t)) + (cells (org-export-string-to-search-cell search))) (or ;; Look for reference associated to search cells triggered by ;; LINK. It can match when targeted file has been published ;; already. (let ((known (cdr (cl-some (lambda (c) (assoc c crossrefs)) cells)))) - (and known (org-export-format-reference known))) + (and known (org-export-format-reference known))) ;; Search cell is unknown so far. Generate a new internal ;; reference that will be used when the targeted file will be ;; published. (let ((new (org-export-new-reference crossrefs))) - (dolist (cell cells) (push (cons cell new) crossrefs)) - (org-publish-cache-set-file-property filename :crossrefs crossrefs) - (org-export-format-reference new)))))) + (dolist (cell cells) (push (cons cell new) crossrefs)) + (org-publish-cache-set-file-property filename :crossrefs crossrefs) + (org-export-format-reference new)))))) @@ -1152,13 +1254,13 @@ If FREE-CACHE, empty the cache." (error "Cannot find cache-file name in `org-publish-write-cache-file'")) (with-temp-file cache-file (let (print-level print-length) - (insert "(setq org-publish-cache \ + (insert "(setq org-publish-cache \ \(make-hash-table :test 'equal :weakness nil :size 100))\n") - (maphash (lambda (k v) - (insert - (format "(puthash %S %s%S org-publish-cache)\n" - k (if (or (listp v) (symbolp v)) "'" "") v))) - org-publish-cache))) + (maphash (lambda (k v) + (insert + (format "(puthash %S %s%S org-publish-cache)\n" + k (if (or (listp v) (symbolp v)) "'" "") v))) + org-publish-cache))) (when free-cache (org-publish-reset-cache)))) (defun org-publish-initialize-cache (project-name) @@ -1172,23 +1274,23 @@ If FREE-CACHE, empty the cache." (make-directory org-publish-timestamp-directory t)) (unless (file-directory-p org-publish-timestamp-directory) (error "Org publish timestamp: %s is not a directory" - org-publish-timestamp-directory)) + org-publish-timestamp-directory)) (unless (and org-publish-cache - (string= (org-publish-cache-get ":project:") project-name)) + (string= (org-publish-cache-get ":project:") project-name)) (let* ((cache-file - (concat - (expand-file-name org-publish-timestamp-directory) - project-name ".cache")) - (cexists (file-exists-p cache-file))) + (concat + (expand-file-name org-publish-timestamp-directory) + project-name ".cache")) + (cexists (file-exists-p cache-file))) (when org-publish-cache (org-publish-reset-cache)) (if cexists (load-file cache-file) - (setq org-publish-cache - (make-hash-table :test #'equal :weakness nil :size 100)) - (org-publish-cache-set ":project:" project-name) - (org-publish-cache-set ":cache-file:" cache-file)) + (setq org-publish-cache + (make-hash-table :test #'equal :weakness nil :size 100)) + (org-publish-cache-set ":project:" project-name) + (org-publish-cache-set ":cache-file:" cache-file)) (unless cexists (org-publish-write-cache-file nil)))) org-publish-cache) @@ -1209,39 +1311,39 @@ the file including them will be republished as well." (error "`org-publish-cache-file-needs-publishing' called, but no cache present")) (let* ((case-fold-search t) - (key (org-publish-timestamp-filename filename pub-dir pub-func)) - (pstamp (org-publish-cache-get key)) - (org-inhibit-startup t) - (visiting (find-buffer-visiting filename)) - included-files-ctime buf) + (key (org-publish-timestamp-filename filename pub-dir pub-func)) + (pstamp (org-publish-cache-get key)) + (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 "^[ \t]*#\\+INCLUDE:" nil t) - (let* ((element (org-element-at-point)) - (included-file + (goto-char (point-min)) + (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-+\\)\\(?:\\s-+\\|$\\)" - value) + "\\`\\(\".+?\"\\|\\S-+\\)\\(?:\\s-+\\|$\\)" + value) (let ((m (match-string 1 value))) (org-remove-double-quotes - ;; Ignore search suffix. + ;; Ignore search suffix. (if (string-match "\\(::\\(.*?\\)\\)\"?\\'" m) (substring m 0 (match-beginning 0)) m)))))))) - (when included-file - (push (org-publish-cache-ctime-of-src - (expand-file-name included-file)) - included-files-ctime))))) + (when included-file + (push (org-publish-cache-ctime-of-src + (expand-file-name included-file)) + included-files-ctime))))) (unless visiting (kill-buffer buf))) (or (null pstamp) - (let ((ctime (org-publish-cache-ctime-of-src filename))) - (or (< pstamp ctime) - (cl-some (lambda (ct) (< ctime ct)) included-files-ctime)))))) + (let ((ctime (org-publish-cache-ctime-of-src filename))) + (or (< pstamp ctime) + (cl-some (lambda (ct) (< ctime ct)) included-files-ctime)))))) (defun org-publish-cache-set-file-property (filename property value &optional project-name) @@ -1265,12 +1367,12 @@ be created, unless NO-CREATE is not nil." (if project-name (org-publish-initialize-cache project-name)) (let ((pl (org-publish-cache-get filename)) retval) (if pl - (if (plist-member pl property) - (setq retval (plist-get pl property)) - (setq retval default)) + (if (plist-member pl property) + (setq retval (plist-get pl property)) + (setq retval default)) ;; no pl yet: (unless no-create - (org-publish-cache-set filename (list property default))) + (org-publish-cache-set filename (list property default))) (setq retval default)) retval)) @@ -1292,11 +1394,11 @@ Returns value on success, else nil." (defun org-publish-cache-ctime-of-src (file) "Get the ctime of FILE as an integer." (let ((attr (file-attributes - (expand-file-name (or (file-symlink-p file) file) - (file-name-directory file))))) + (expand-file-name (or (file-symlink-p file) file) + (file-name-directory file))))) (if (not attr) (error "No such file: \"%s\"" file) (+ (lsh (car (nth 5 attr)) 16) - (cadr (nth 5 attr)))))) + (cadr (nth 5 attr)))))) (provide 'ox-publish) -- 2.8.3