浏览代码

Merge branch 'master' of git+ssh://repo.or.cz/srv/git/org-mode

Conflicts:

	ChangeLog
Carsten Dominik 17 年之前
父节点
当前提交
84ac2ccab5
共有 5 个文件被更改,包括 348 次插入318 次删除
  1. 28 0
      ChangeLog
  2. 5 3
      org-export-latex.el
  3. 286 297
      org-publish.el
  4. 15 10
      org.el
  5. 14 8
      org.texi

+ 28 - 0
ChangeLog

@@ -1,8 +1,36 @@
 2008-03-03  Carsten Dominik  <dominik@science.uva.nl>
 2008-03-03  Carsten Dominik  <dominik@science.uva.nl>
 
 
+
 	* org.el (org-toggle-region-items): New function.
 	* org.el (org-toggle-region-items): New function.
 	(org-toggle-region-headings): New function.
 	(org-toggle-region-headings): New function.
 
 
+2008-03-03  Bastien Guerry  <bzg@altern.org>
+
+	* org.el (org-load-default-extensions): Don't throw an error when
+	a feature cannot be loaded, send a message instead.
+
+	* org-publish.el (org-publish-org-to, org-publish-org-to-latex)
+	(org-publish-org-to-html): New argument `pub-dir' for temporary
+	publication directory.
+	(org-publish-projects): Renamed from `org-publish-plists'.
+	(org-publish, org-publish-all): Use `org-publish-projects'.
+	(org-publish-all, org-publish-current-file)
+	(org-publish-files-alist): Renamed from `org-publish-files'.
+	(org-publish-initialize-files-alist): New defun.
+	(org-publish-current-project): First initialize the list of
+	publishable files with`org-publish-initialize-files-alist'.
+	(org-publish-get-files): New defun.
+	(org-publish-expand-projects): New defun.
+	(org-publish-expand-components): New defun.
+	(org-publish-get-base-files): Get files recursively.
+	(org-publish-update-timestamp): Create the timestamp file.
+	(org-publish-needed-p): Removed the code about creating/updating
+	the timestamp file.
+	(org-publish-validate-link)
+	(org-publish-get-plists-from-filename, org-publish-get-plists):
+	Deleted.
+	(org-publish-timestamp-directory): Use directory type.
+
 2008-03-01  Bastien Guerry  <bzg@altern.org>
 2008-03-01  Bastien Guerry  <bzg@altern.org>
 
 
 	* org.el (org-dblock-write:columnview, org-columns-capture-view):
 	* org.el (org-dblock-write:columnview, org-columns-capture-view):

+ 5 - 3
org-export-latex.el

@@ -329,7 +329,7 @@ in a window.  A non-interactive call will only retunr the buffer."
 
 
 ;;;###autoload
 ;;;###autoload
 (defun org-export-as-latex (arg &optional hidden ext-plist
 (defun org-export-as-latex (arg &optional hidden ext-plist
-				to-buffer body-only)
+				to-buffer body-only pub-dir)
   "Export current buffer to a LaTeX file.
   "Export current buffer to a LaTeX file.
 If there is an active region, export only the region.  The prefix
 If there is an active region, export only the region.  The prefix
 ARG specifies how many levels of the outline should become
 ARG specifies how many levels of the outline should become
@@ -344,7 +344,8 @@ buffer.  If TO-BUFFER is the symbol `string', don't leave any
 buffer behind but just return the resulting LaTeX as a string.
 buffer behind but just return the resulting LaTeX as a string.
 When BODY-ONLY is set, don't produce the file header and footer,
 When BODY-ONLY is set, don't produce the file header and footer,
 simply return the content of \begin{document}...\end{document},
 simply return the content of \begin{document}...\end{document},
-without even the \begin{document} and \end{document} commands."
+without even the \begin{document} and \end{document} commands.
+when PUB-DIR is set, use this as the publishing directory."
   (interactive "P")
   (interactive "P")
   ;; Make sure we have a file name when we need it.
   ;; Make sure we have a file name when we need it.
   (when (and (not (or to-buffer body-only))
   (when (and (not (or to-buffer body-only))
@@ -375,7 +376,8 @@ without even the \begin{document} and \end{document} commands."
 		    (file-name-sans-extension
 		    (file-name-sans-extension
 		     (file-name-nondirectory buffer-file-name))))
 		     (file-name-nondirectory buffer-file-name))))
 	 (filename (concat (file-name-as-directory
 	 (filename (concat (file-name-as-directory
-			    (org-export-directory :LaTeX ext-plist))
+			    (or pub-dir
+				(org-export-directory :LaTeX ext-plist)))
 			   (file-name-sans-extension
 			   (file-name-sans-extension
 			    (file-name-nondirectory ;sans-extension
 			    (file-name-nondirectory ;sans-extension
 			     buffer-file-name)) ".tex"))
 			     buffer-file-name)) ".tex"))

+ 286 - 297
org-publish.el

@@ -3,8 +3,9 @@
 ;; Copyright (C) 2006, 2007, 2008  Free Software Foundation, Inc.
 ;; Copyright (C) 2006, 2007, 2008  Free Software Foundation, Inc.
 
 
 ;; Author: David O'Toole <dto@gnu.org>
 ;; Author: David O'Toole <dto@gnu.org>
-;; Keywords: hypermedia, outlines
+;; Maintainer: Bastien Guerry <bzg AT altern DOT org>
-;; Version: 1.80c
+;; Keywords: hypermedia, outlines, wp
+;; Version: 1.81
 
 
 ;; This file is part of GNU Emacs.
 ;; This file is part of GNU Emacs.
 ;;
 ;;
@@ -26,24 +27,17 @@
 ;;; Commentary:
 ;;; Commentary:
 
 
 ;; Requires at least version 4.27 of org.el
 ;; Requires at least version 4.27 of org.el
-;;
-;; The official org-mode website:
-;; http://staff.science.uva.nl/~dominik/Tools/org/
-;;
-;; Home page for org-publish.el:
-;; http://dto.freeshell.org/notebook/OrgMode.html
 
 
-;; This program extends the HTML publishing support of Emacs Org-mode
+;; This program allow configurable publishing of related sets of
-;; to allow configurable publishing of related sets of files as a
+;; Org-mode files as a complete website.
-;; complete website.
 ;;
 ;;
 ;; org-publish.el can do the following:
 ;; org-publish.el can do the following:
 ;;
 ;;
-;; + Publish all one's org-files to html
+;; + Publish all one's org-files to HTML or LaTeX
-;; + Upload html, images, attachments and other files to a web server
+;; + Upload HTML, images, attachments and other files to a web server
 ;; + Exclude selected private pages from publishing
 ;; + Exclude selected private pages from publishing
 ;; + Publish a clickable index of pages
 ;; + Publish a clickable index of pages
-;; + Manage local timestamps, for publishing only changed files
+;; + Manage local timestamps for publishing only changed files
 ;; + Accept plugin functions to extend range of publishable content
 ;; + Accept plugin functions to extend range of publishable content
 ;;
 ;;
 ;; Special thanks to the org-mode maintainer Carsten Dominik for his
 ;; Special thanks to the org-mode maintainer Carsten Dominik for his
@@ -85,6 +79,7 @@
 ;; 		     :publishing-directory "~/public_html"
 ;; 		     :publishing-directory "~/public_html"
 ;;                   :with-section-numbers nil
 ;;                   :with-section-numbers nil
 ;; 		     :table-of-contents nil
 ;; 		     :table-of-contents nil
+;;                   :recursive t
 ;; 		     :style "<link rel=stylesheet href=\"../other/mystyle.css\" type=\"text/css\">")))
 ;; 		     :style "<link rel=stylesheet href=\"../other/mystyle.css\" type=\"text/css\">")))
 
 
 ;;;; More complex example configuration:
 ;;;; More complex example configuration:
@@ -116,12 +111,10 @@
 ;; 		       :style "<link rel=stylesheet href=\"../other/mystyle.css\" type=\"text/css\">"
 ;; 		       :style "<link rel=stylesheet href=\"../other/mystyle.css\" type=\"text/css\">"
 ;; 		       :auto-preamble t
 ;; 		       :auto-preamble t
 ;; 		       :auto-postamble nil)
 ;; 		       :auto-postamble nil)
-;;
 ;;         ("images" :base-directory "~/images/"
 ;;         ("images" :base-directory "~/images/"
 ;; 	             :base-extension "jpg\\|gif\\|png"
 ;; 	             :base-extension "jpg\\|gif\\|png"
 ;; 		     :publishing-directory "/ssh:user@host:~/html/images/"
 ;; 		     :publishing-directory "/ssh:user@host:~/html/images/"
 ;; 		     :publishing-function org-publish-attachment)
 ;; 		     :publishing-function org-publish-attachment)
-;;
 ;;         ("other"  :base-directory "~/other/"
 ;;         ("other"  :base-directory "~/other/"
 ;; 	   	     :base-extension "css"
 ;; 	   	     :base-extension "css"
 ;; 		     :publishing-directory "/ssh:user@host:~/html/other/"
 ;; 		     :publishing-directory "/ssh:user@host:~/html/other/"
@@ -135,7 +128,6 @@
 ;; within emacs. You can always just publish to local folders, and
 ;; within emacs. You can always just publish to local folders, and
 ;; then use the synchronization/upload tool of your choice.
 ;; then use the synchronization/upload tool of your choice.
 
 
-
 ;;; List of user-visible changes since version 1.27
 ;;; List of user-visible changes since version 1.27
 
 
 ;; 1.78: Allow list-valued :publishing-function
 ;; 1.78: Allow list-valued :publishing-function
@@ -158,17 +150,16 @@
 
 
 ;;; Code:
 ;;; Code:
 
 
-
 (eval-when-compile
 (eval-when-compile
   (require 'cl))
   (require 'cl))
 
 
+(require 'dired-aux)
 
 
 (defgroup org-publish nil
 (defgroup org-publish nil
 	"Options for publishing a set of Org-mode and related files."
 	"Options for publishing a set of Org-mode and related files."
    :tag "Org Publishing"
    :tag "Org Publishing"
    :group 'org)
    :group 'org)
 
 
-
 (defcustom org-publish-project-alist nil
 (defcustom org-publish-project-alist nil
   "Association list to control publishing behavior.
   "Association list to control publishing behavior.
 Each element of the alist is a publishing 'project.'  The CAR of
 Each element of the alist is a publishing 'project.'  The CAR of
@@ -194,11 +185,11 @@ override everything.
 
 
 Most properties are optional, but some should always be set:
 Most properties are optional, but some should always be set:
 
 
-    :base-directory        Directory containing publishing source files
+  :base-directory        Directory containing publishing source files
-    :base-extension        Extension (without the dot!) of source files.
+  :base-extension        Extension (without the dot!) of source files.
-                             This can be a regular expression.
+                         This can be a regular expression.
-    :publishing-directory  Directory (possibly remote) where output
+  :publishing-directory  Directory (possibly remote) where output
-                             files will be published
+                         files will be published
 
 
 The :exclude property may be used to prevent certain files from
 The :exclude property may be used to prevent certain files from
 being published. Its value may be a string or regexp matching
 being published. Its value may be a string or regexp matching
@@ -216,65 +207,63 @@ publishing files in the project. This can be used to extend the
 set of file types publishable by org-publish, as well as the set
 set of file types publishable by org-publish, as well as the set
 of output formats.
 of output formats.
 
 
-    :publishing-function     Function to publish file. The default is
+  :publishing-function     Function to publish file. The default is
-                             org-publish-org-to-html, but other
+                           org-publish-org-to-html, but other
-                             values are possible. May also be a
+                           values are possible. May also be a
-                             list of functions, in which case 
+                           list of functions, in which case
-                             each function in the list is invoked
+                           each function in the list is invoked
-                             in turn.
+                           in turn.
 
 
 Another property allows you to insert code that prepares a
 Another property allows you to insert code that prepares a
 project for publishing. For example, you could call GNU Make on a
 project for publishing. For example, you could call GNU Make on a
-certain makefile, to ensure published files are built up to date. 
+certain makefile, to ensure published files are built up to date.
 
 
-    :preparation-function   Function to be called before publishing
+  :preparation-function   Function to be called before publishing
-                              this project.
+                          this project.
 
 
 Some properties control details of the Org publishing process,
 Some properties control details of the Org publishing process,
 and are equivalent to the corresponding user variables listed in
 and are equivalent to the corresponding user variables listed in
 the right column. See the documentation for those variables to
 the right column. See the documentation for those variables to
 learn more about their use and default values.
 learn more about their use and default values.
 
 
-    :language              org-export-default-language
+  :language              org-export-default-language
-    :headline-levels       org-export-headline-levels
+  :headline-levels       org-export-headline-levels
-    :section-numbers       org-export-with-section-numbers
+  :section-numbers       org-export-with-section-numbers
-    :table-of-contents     org-export-with-toc
+  :table-of-contents     org-export-with-toc
-    :emphasize             org-export-with-emphasize
+  :emphasize             org-export-with-emphasize
-    :sub-superscript       org-export-with-sub-superscripts
+  :sub-superscript       org-export-with-sub-superscripts
-    :TeX-macros            org-export-with-TeX-macros
+  :TeX-macros            org-export-with-TeX-macros
-    :fixed-width           org-export-with-fixed-width
+  :fixed-width           org-export-with-fixed-width
-    :tables                org-export-with-tables
+  :tables                org-export-with-tables
-    :table-auto-headline   org-export-highlight-first-table-line
+  :table-auto-headline   org-export-highlight-first-table-line
-    :style                 org-export-html-style
+  :style                 org-export-html-style
-    :convert-org-links     org-export-html-link-org-files-as-html
+  :convert-org-links     org-export-html-link-org-files-as-html
-    :inline-images         org-export-html-inline-images
+  :inline-images         org-export-html-inline-images
-    :expand-quoted-html    org-export-html-expand
+  :expand-quoted-html    org-export-html-expand
-    :timestamp             org-export-html-with-timestamp
+  :timestamp             org-export-html-with-timestamp
-    :publishing-directory  org-export-publishing-directory
+  :publishing-directory  org-export-publishing-directory
-    :preamble              org-export-html-preamble
+  :preamble              org-export-html-preamble
-    :postamble             org-export-html-postamble
+  :postamble             org-export-html-postamble
-    :auto-preamble         org-export-html-auto-preamble
+  :auto-preamble         org-export-html-auto-preamble
-    :auto-postamble        org-export-html-auto-postamble
+  :auto-postamble        org-export-html-auto-postamble
-    :author                user-full-name
+  :author                user-full-name
-    :email                 user-mail-address
+  :email                 user-mail-address
 
 
 The following properties may be used to control publishing of an
 The following properties may be used to control publishing of an
 index of files or summary page for a given project.
 index of files or summary page for a given project.
 
 
-    :auto-index            Whether to publish an index during
+  :auto-index            Whether to publish an index during
-                           org-publish-current-project or org-publish-all.
+                         org-publish-current-project or org-publish-all.
-    :index-filename        Filename for output of index. Defaults
+  :index-filename        Filename for output of index. Defaults
-                           to 'index.org' (which becomes 'index.html')
+                         to 'index.org' (which becomes 'index.html')
-    :index-title           Title of index page. Defaults to name of file.
+  :index-title           Title of index page. Defaults to name of file.
-    :index-function        Plugin function to use for generation of index.
+  :index-function        Plugin function to use for generation of index.
-                           Defaults to 'org-publish-org-index', which
+                         Defaults to 'org-publish-org-index', which
-                           generates a plain list of links to all files
+                         generates a plain list of links to all files
-                           in the project.
+                         in the project."
-"
   :group 'org-publish
   :group 'org-publish
   :type 'alist)
   :type 'alist)
 
 
-
 (defcustom org-publish-use-timestamps-flag t
 (defcustom org-publish-use-timestamps-flag t
   "When non-nil, use timestamp checking to publish only changed files.
   "When non-nil, use timestamp checking to publish only changed files.
 When nil, do no timestamp checking and always publish all
 When nil, do no timestamp checking and always publish all
@@ -282,261 +271,261 @@ files."
   :group 'org-publish
   :group 'org-publish
   :type 'boolean)
   :type 'boolean)
 
 
-
 (defcustom org-publish-timestamp-directory "~/.org-timestamps/"
 (defcustom org-publish-timestamp-directory "~/.org-timestamps/"
   "Name of directory in which to store publishing timestamps."
   "Name of directory in which to store publishing timestamps."
   :group 'org-publish
   :group 'org-publish
-  :type 'string)
+  :type 'directory)
 
 
 
 
-;;;; Timestamp-related functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Timestamp-related functions
 
 
 (defun org-publish-timestamp-filename (filename)
 (defun org-publish-timestamp-filename (filename)
   "Return path to timestamp file for filename FILENAME."
   "Return path to timestamp file for filename FILENAME."
-  (while (string-match (if (eq system-type 'windows-nt) "~\\|/\\|:" "~\\|/")
+  (while (string-match 
-		       filename)
+	  (if (eq system-type 'windows-nt) "~\\|/\\|:" "~\\|/") filename)
     (setq filename (replace-match "_" nil t filename)))
     (setq filename (replace-match "_" nil t filename)))
   (concat org-publish-timestamp-directory filename ".timestamp"))
   (concat org-publish-timestamp-directory filename ".timestamp"))
 
 
 (defun org-publish-needed-p (filename)
 (defun org-publish-needed-p (filename)
-  "Check whether file should be published.
+  "Return `t' if FILENAME should be published."
-If org-publish-use-timestamps-flag is set to nil, this function always
-returns t. Otherwise, check the timestamps folder to determine
-whether file should be published."
   (if org-publish-use-timestamps-flag
   (if org-publish-use-timestamps-flag
-      (progn
+      (if (file-exists-p org-publish-timestamp-directory)
-	;;
+	  ;; first handle possible wrong timestamp directory
-	;; create folder if needed
-	(if (not (file-exists-p org-publish-timestamp-directory))
-	    (make-directory org-publish-timestamp-directory)
 	  (if (not (file-directory-p org-publish-timestamp-directory))
 	  (if (not (file-directory-p org-publish-timestamp-directory))
-	      (error "`org-publish-timestamp-directory' must be a directory")))
+	      (error "Org publish timestamp: %s is not a directory"
-	;;
+		     org-publish-timestamp-directory)
-	;; check timestamp. ok if timestamp file doesn't exist
+	    ;; there is a timestamp, check if FILENAME is newer
-	(let* ((timestamp (org-publish-timestamp-filename filename))
+	    (file-newer-than-file-p
-	       (rtn (file-newer-than-file-p filename timestamp)))
+	     filename (org-publish-timestamp-filename filename))))
-	  (if rtn
+    ;; don't use timestamps, always return t
-	      ;; handle new timestamps
-	      (if (not (file-exists-p timestamp))
-		  ;; create file
-		  (with-temp-buffer
-		    (make-directory (file-name-directory timestamp) :parents)
-		    (write-file timestamp)
-		    (kill-buffer (current-buffer)))))
-	  rtn))
     t))
     t))
 
 
-
 (defun org-publish-update-timestamp (filename)
 (defun org-publish-update-timestamp (filename)
-  "Update publishing timestamp for file FILENAME."
+  "Update publishing timestamp for file FILENAME.
-  (let ((timestamp (org-publish-timestamp-filename filename)))
+If there is no timestamp, create one."
-    ;; Emacs 21 doesn't have set-file-times
+  (let ((timestamp-file (org-publish-timestamp-filename filename))
-    (if (fboundp 'set-file-times)
+	newly-created-timestamp)
-        (set-file-times timestamp)
+    (if (not (file-exists-p timestamp-file))
-      (call-process "touch" nil 0 nil timestamp))))
+	;; create timestamp file if needed
-
+	(with-temp-buffer
-
+	  (make-directory (file-name-directory timestamp-file) t)
-;;;; A hash mapping files to project names
+	  (write-file timestamp-file)
-
+	  (setq newly-created-timestamp t)))
-
+    ;; Emacs 21 doesn't have `set-file-times'
-(defvar org-publish-files (make-hash-table :test 'equal) "Hash
+    (if (and (fboundp 'set-file-times)
-table mapping file names to project names.")
+	     (not newly-created-timestamp))
-
+        (set-file-times timestamp-file)
-
+      (call-process "touch" nil 0 nil timestamp-file))))
-;;;; Checking filenames against this hash
+
-
+
-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defun org-publish-validate-link (link &optional directory)
+;;; Mapping files to project names
-  (gethash (file-truename (expand-file-name link directory))
+
-	   org-publish-files))
+(defvar org-publish-files-alist nil
-
+  "Alist of files and their parent project.
-
+Each element of this alist is of the form:
-;;;; Getting project information out of org-publish-project-alist
+
-
+  (file-name . project-name)")
-
+
-(defun org-publish-get-plists (&optional project-name)
+(defun org-publish-initialize-files-alist (&optional refresh)
- "Return a list of property lists for project PROJECT-NAME.
+  "Set `org-publish-files-alist' if it is not set.
-When argument is not given, return all property lists for all projects."
+Also set it if the optional argument REFRESH is non-nil."
- (let ((alist (if project-name
+  (when (or (not org-publish-files-alist) refresh)
-		   (list (assoc project-name org-publish-project-alist))
+    (setq org-publish-files-alist
-		 org-publish-project-alist))
+	  (org-publish-get-files org-publish-project-alist))))
-	(project nil)
+
-	(plists nil)
+
-	(single nil)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-	(components nil))
+;;; Getting project information out of org-publish-project-alist
-
+
-   ;;
+(defun org-publish-get-files (projects-alist &optional no-exclusion)
-   ;;
+  "Return the list of all publishable files for PROJECTS-ALIST.
-   (while (setq project (pop alist))
+If NO-EXCLUSION is non-nil, don't exclude files."
-     ;; what kind of project is it?
+  (let (all-files)
-     (if (setq components (plist-get (cdr project) :components))
+    ;; add all projects
-	  ;; meta project. annotate each plist with name of enclosing project
+    (mapc
-	  (setq single
+     (lambda(p)
-		(apply 'append
+       (let* ((exclude (plist-get p :exclude))
-		       (mapcar 'org-publish-get-plists components)))
+	      (files (org-publish-get-base-files p exclude)))
-	;; normal project
+	 ;; add all files from this project
-	(setq single (list (cdr project))))
+	 (mapc (lambda(f)
-     ;;
+		 (add-to-list 'all-files
-     (setq plists (append plists single))
+			      (cons (file-truename f) (car p))))
-     (dolist (p single)
+	       files)))
-	(let* ((exclude (plist-get p :exclude))
+     (org-publish-expand-projects projects-alist))
-	       (files (org-publish-get-base-files p exclude)))
+    all-files))
-	  (dolist (f files)
+
-	    (puthash (file-truename f) (car project) org-publish-files)))))
+(defun org-publish-expand-projects (projects-alist)
-   plists))
+  "Expand projects contained in PROJECTS-ALIST."
-
+  (let (without-component with-component)
-
+    (mapc (lambda(p) 
-
+	    (add-to-list
-(defun org-publish-get-base-files (plist &optional exclude-regexp)
+	     (if (plist-get (cdr p) :components)
-  "Return a list of all files in project defined by PLIST.
+		 'with-component 'without-component) p))
+	  projects-alist)
+    (delete-dups
+     (append without-component
+	     (car (mapcar (lambda(p) (org-publish-expand-components p))
+			  with-component))))))
+
+(defun org-publish-expand-components (project)
+  "Expand PROJECT into an alist of its components."
+  (let* ((components (plist-get (cdr project) :components)))
+    (delete-dups 
+     (mapcar (lambda(c) (assoc c org-publish-project-alist))
+	     components))))
+
+(defun org-publish-get-base-files (project &optional exclude-regexp)
+  "Return a list of all files in PROJECT.
 If EXCLUDE-REGEXP is set, this will be used to filter out
 If EXCLUDE-REGEXP is set, this will be used to filter out
 matching filenames."
 matching filenames."
-  (let* ((dir (file-name-as-directory (plist-get plist :base-directory)))
+  (let* ((project-plist (cdr project))
-	 (include-list (plist-get plist :include))
+	 (base-dir (file-name-as-directory 
-	 (extension (or (plist-get plist :base-extension) "org"))
+		    (plist-get project-plist :base-directory)))
-	 (regexp (concat "^[^\\.].*\\.\\(" extension "\\)$"))
+ 	 (include-list (plist-get project-plist :include))
-	 (allfiles (directory-files dir t regexp)))
+ 	 (recursive-p (plist-get project-plist :recursive))
-    ;;
+ 	 (extension (or (plist-get project-plist :base-extension) "org"))
-    ;; exclude files
+ 	 (regexp (concat "^[^\\.].*\\.\\(" extension "\\)$"))
-    (setq allfiles
+ 	 alldirs allfiles files dir)
-	  (if (not exclude-regexp)
+    ;; Get all files and directories in base-directory
-	      allfiles
+    (setq files (dired-files-attributes base-dir))
-	    (delq nil
+    ;; Get all subdirectories if recursive-p
-		  (mapcar (lambda (x)
+    (setq alldirs
-			    (if (string-match exclude-regexp x) nil x))
+ 	  (if recursive-p
-			  allfiles))))
+ 	      (delq nil (mapcar (lambda(f) (if (caaddr f) (cadr f))) files))
-    ;;
+ 	    (list base-dir)))
-    ;; include extra files
+    (while (setq dir (pop alldirs))
-    (let ((inc nil))
+      (setq files (directory-files dir t regexp))
-      (while (setq inc (pop include-list))
+      ;; Exclude files
-	(setq allfiles (cons (expand-file-name inc dir) allfiles))))
+      (setq files
-
+ 	    (if (not exclude-regexp)
+ 		files
+ 	      (delq nil
+ 		    (mapcar (lambda (x)
+ 			      (if (string-match exclude-regexp x) nil x))
+ 			    files))))
+      ;; Include extra files
+      (let (inc)
+ 	(while (setq inc (pop include-list))
+ 	  (setq files (cons (expand-file-name inc dir) files))))
+      (setq allfiles (append allfiles files)))
     allfiles))
     allfiles))
 
 
-
 (defun org-publish-get-project-from-filename (filename)
 (defun org-publish-get-project-from-filename (filename)
-  "Figure out which project a given FILENAME belongs to, if any.
+  "Return the project FILENAME belongs."
-Filename should contain full path. Returns name of project, or
+  (let* ((project-name (cdr (assoc (file-truename filename)
-nil if not found."
+				   org-publish-files-alist))))
-  (org-publish-get-plists)
+    (assoc project-name org-publish-project-alist)))
-  (gethash (file-truename filename) org-publish-files))
-
-
-(defun org-publish-get-plist-from-filename (filename)
-  "Return publishing configuration plist for file FILENAME."
-  (let ((found nil))
-    (mapc
-     (lambda (plist)
-       (let ((files (org-publish-get-base-files plist)))
- 	 (if (member (expand-file-name filename) files)
-	     (setq found plist))))
-     (org-publish-get-plists))
-    found))
-
 
 
 
 
-;;;; Pluggable publishing back-end functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Pluggable publishing back-end functions
 
 
-(defun org-publish-org-to-latex (plist filename)
+(defun org-publish-org-to (format plist filename pub-dir)
-  "Publish an org file to LaTeX."
-  (org-publish-org-to "latex" plist filename))
-
-(defun org-publish-org-to-html (plist filename)
-  "Publish an org file to HTML."
-  (org-publish-org-to "html" plist filename))
-
-(defun org-publish-org-to (format plist filename)
   "Publish an org file to FORMAT.
   "Publish an org file to FORMAT.
 PLIST is the property list for the given project.
 PLIST is the property list for the given project.
-FILENAME is the filename of the org file to be published."
+FILENAME is the filename of the org file to be published.
+PUB-DIR is the publishing directory."
   (require 'org)
   (require 'org)
-  (let* ((arg (plist-get plist :headline-levels)))
+  (unless (file-exists-p pub-dir)
-    (progn
+    (make-directory pub-dir t))
-      (find-file filename)
+  (find-file filename)
-      (funcall (intern (concat "org-export-as-" format))
+  (funcall (intern (concat "org-export-as-" format))
-	       arg nil plist)
+	   (plist-get plist :headline-levels)
-      (kill-buffer (current-buffer)))))
+	   nil plist nil nil pub-dir)
-
+  (kill-buffer (current-buffer)))
+
+(defun org-publish-org-to-latex (plist filename pub-dir)
+  "Publish an org file to LaTeX.
+See `org-publish-org-to' to the list of arguments."
+  (org-publish-org-to "latex" plist filename pub-dir))
+
+(defun org-publish-org-to-html (plist filename pub-dir)
+  "Publish an org file to HTML.
+See `org-publish-org-to' to the list of arguments."
+  (org-publish-org-to "html" plist filename pub-dir))
 
 
 (defun org-publish-attachment (plist filename)
 (defun org-publish-attachment (plist filename)
   "Publish a file with no transformation of any kind.
   "Publish a file with no transformation of any kind.
 PLIST is the property list for the given project.
 PLIST is the property list for the given project.
 FILENAME is the filename of the file to be published."
 FILENAME is the filename of the file to be published."
   ;; make sure eshell/cp code is loaded
   ;; make sure eshell/cp code is loaded
-  (eval-and-compile 
+  (eval-and-compile
     (require 'eshell)
     (require 'eshell)
     (require 'esh-maint)
     (require 'esh-maint)
     (require 'em-unix))
     (require 'em-unix))
-  (let ((destination (file-name-as-directory (plist-get plist :publishing-directory))))
+  (let ((destination (file-name-as-directory
+		      (plist-get plist :publishing-directory))))
     (eshell/cp filename destination)))
     (eshell/cp filename destination)))
 
 
 
 
-;;;; Publishing files, sets of files, and indices
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
+;;; Publishing files, sets of files, and indices
-
+
-(defun org-publish-file (filename)
+(defun org-publish-file (filename &optional project)
-  "Publish file FILENAME."
+  "Publish file FILENAME from PROJECT."
-  (let* ((project-name (org-publish-get-project-from-filename filename))
+  (when (org-publish-needed-p filename)
-	 (plist (org-publish-get-plist-from-filename filename))
+    (let* ((project (or project (org-publish-get-project-from-filename filename)))
-	 (publishing-function (or (plist-get plist :publishing-function) 'org-publish-org-to-html)))
+	   (project-plist (cdr project))
-    (if (not project-name)
+	   (publishing-function (or (plist-get project-plist :publishing-function)
-	(error "File %s is not part of any known project" filename))
+				    'org-publish-org-to-html))
-    (when (org-publish-needed-p filename)
+	   (base-dir (file-truename (plist-get project-plist :base-directory)))
+	   (pub-dir (file-truename (plist-get project-plist :publishing-directory)))
+	   tmp-pub-dir)
+      (if (not project) (error "File %s is not part of any known project" filename))
+      (setq tmp-pub-dir
+	    (file-name-directory
+	     (concat pub-dir
+		     (and (string-match (regexp-quote base-dir) filename)
+			  (substring filename (match-end 0))))))
       (if (listp publishing-function)
       (if (listp publishing-function)
 	  ;; allow chain of publishing functions
 	  ;; allow chain of publishing functions
 	  (mapc (lambda (f)
 	  (mapc (lambda (f)
-		  (funcall f plist filename)) 
+		  (funcall f project-plist filename tmp-pub-dir))
 		publishing-function)
 		publishing-function)
-	(funcall publishing-function plist filename))
+	(funcall publishing-function project-plist filename tmp-pub-dir))
       (org-publish-update-timestamp filename))))
       (org-publish-update-timestamp filename))))
 
 
-
+(defun org-publish-projects (projects)
-(defun org-publish-plist (plist)
+  "Publish all files belonging to the PROJECTS alist.
-  "Publish all files in set defined by PLIST.
+If :auto-index is set, publish the index too."
- If :auto-index is set, publish the index too."
+  (mapc 
-  (let* ((exclude-regexp (plist-get plist :exclude))
+   (lambda (project)
-	 (publishing-function (or (plist-get plist :publishing-function) 'org-publish-org-to-html))
+     (let* ((project-plist (cdr project))
-	 (index-p (plist-get plist :auto-index))
+	    (exclude-regexp (plist-get project-plist :exclude))
-         (index-filename (or (plist-get plist :index-filename) "index.org"))
+	    (index-p (plist-get project-plist :auto-index))
-	 (index-function (or (plist-get plist :index-function) 'org-publish-org-index))
+	    (index-filename (or (plist-get project-plist :index-filename) 
-	 (preparation-function (plist-get plist :preparation-function))
+				"index.org"))
-	 (f nil))
+	    (index-function (or (plist-get project-plist :index-function) 
-    ;;
+				'org-publish-org-index))
-    (when preparation-function
+	    (preparation-function (plist-get project-plist :preparation-function))
-      (funcall preparation-function))
+	    (files (org-publish-get-base-files project exclude-regexp)) file)
-    (if index-p
+       (when preparation-function (funcall preparation-function))
-	(funcall index-function plist index-filename))
+       (if index-p (funcall index-function project index-filename))
-    (let ((files (org-publish-get-base-files plist exclude-regexp)))
+       (while (setq file (pop files))
-      (while (setq f (pop files))
+	 (org-publish-file file project))))
-	;; check timestamps
+   (org-publish-expand-projects projects)))
-	(when (org-publish-needed-p f)
+
-	  (if (listp publishing-function)
+(defun org-publish-org-index (project &optional index-filename)
-	      ;; allow chain of publishing functions
+  "Create an index of pages in set defined by PROJECT.
-	      (mapc (lambda (func)
+Optionally set the filename of the index with INDEX-FILENAME.
-		      (funcall func plist f)) 
+Default for INDEX-FILENAME is 'index.org'."
-		    publishing-function)
+  (let* ((project-plist (cdr project))
-	    (funcall publishing-function plist f))
+	 (dir (file-name-as-directory
-	  (org-publish-update-timestamp f))))))
+	       (plist-get project-plist :base-directory)))
-
+	 (exclude-regexp (plist-get project-plist :exclude))
-
+	 (files (org-publish-get-base-files project exclude-regexp))
-(defun org-publish-org-index (plist &optional index-filename)
-  "Create an index of pages in set defined by PLIST.
-Optionally set the filename of the index with INDEX-FILENAME;
-default is 'index.org'."
-  (let* ((dir (file-name-as-directory (plist-get plist :base-directory)))
-	 (exclude-regexp (plist-get plist :exclude))
-	 (files (org-publish-get-base-files plist exclude-regexp))
 	 (index-filename (concat dir (or index-filename "index.org")))
 	 (index-filename (concat dir (or index-filename "index.org")))
 	 (index-buffer (find-buffer-visiting index-filename))
 	 (index-buffer (find-buffer-visiting index-filename))
 	 (ifn (file-name-nondirectory index-filename))
 	 (ifn (file-name-nondirectory index-filename))
-	 (f nil))
+	 file)
-    ;;
     ;; if buffer is already open, kill it to prevent error message
     ;; if buffer is already open, kill it to prevent error message
     (if index-buffer
     (if index-buffer
 	(kill-buffer index-buffer))
 	(kill-buffer index-buffer))
     (with-temp-buffer
     (with-temp-buffer
-      (while (setq f (pop files))
+      (while (setq file (pop files))
-	(let ((fn (file-name-nondirectory f)))
+	(let ((fn (file-name-nondirectory file)))
 	  (unless (string= fn ifn) ;; index shouldn't index itself
 	  (unless (string= fn ifn) ;; index shouldn't index itself
 	    (insert (concat " + [[file:" fn "]["
 	    (insert (concat " + [[file:" fn "]["
 			    (file-name-sans-extension fn)
 			    (file-name-sans-extension fn)
@@ -545,61 +534,61 @@ default is 'index.org'."
       (kill-buffer (current-buffer)))))
       (kill-buffer (current-buffer)))))
 
 
 
 
-;;;; Interactive publishing functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
+;;; Interactive publishing functions
 
 
 ;;;###autoload
 ;;;###autoload
 (defun org-publish (project-name &optional force)
 (defun org-publish (project-name &optional force)
-  "Publish the project PROJECT-NAME."
+  "Publish the project named PROJECT-NAME."
-  (interactive (list (completing-read "Project name: " org-publish-project-alist
+  (interactive 
-				      nil t)
+   (list (progn (completing-read 
-		     current-prefix-arg))
+		 "Project name: " org-publish-project-alist nil t))
+	 current-prefix-arg))
   (save-window-excursion
   (save-window-excursion
     (let ((org-publish-use-timestamps-flag
     (let ((org-publish-use-timestamps-flag
-	   (if force nil org-publish-use-timestamps-flag))
+	   (if force nil org-publish-use-timestamps-flag)))
-	  (plists (org-publish-get-plists project-name)))
+      (org-publish-projects 
-      (mapcar 'org-publish-plist plists))))
+       (list (assoc project-name org-publish-project-alist))))))
-
 
 
 ;;;###autoload
 ;;;###autoload
-(defun org-publish-current-project (&optional force)
+(defun org-publish-all (&optional force)
-  "Publish the project associated with the current file.
+  "Publish all projects.
-With prefix argument, force publishing all files in project."
+With prefix argument, force publish all files."
   (interactive "P")
   (interactive "P")
+  (org-publish-initialize-files-alist)
   (save-window-excursion
   (save-window-excursion
-    (let ((project-name (org-publish-get-project-from-filename (buffer-file-name)))
+    (let ((org-publish-use-timestamps-flag
-	  (org-publish-use-timestamps-flag
 	   (if force nil org-publish-use-timestamps-flag)))
 	   (if force nil org-publish-use-timestamps-flag)))
-      (if (not project-name)
+      (org-publish-projects org-publish-project-alist))))
-	  (error "File %s is not part of any known project" (buffer-file-name)))
-      (org-publish project-name))))
-
 
 
 ;;;###autoload
 ;;;###autoload
 (defun org-publish-current-file (&optional force)
 (defun org-publish-current-file (&optional force)
   "Publish the current file.
   "Publish the current file.
 With prefix argument, force publish the file."
 With prefix argument, force publish the file."
   (interactive "P")
   (interactive "P")
+  (org-publish-initialize-files-alist)
   (save-window-excursion
   (save-window-excursion
     (let ((org-publish-use-timestamps-flag
     (let ((org-publish-use-timestamps-flag
 	   (if force nil org-publish-use-timestamps-flag)))
 	   (if force nil org-publish-use-timestamps-flag)))
       (org-publish-file (buffer-file-name)))))
       (org-publish-file (buffer-file-name)))))
 
 
-
 ;;;###autoload
 ;;;###autoload
-(defun org-publish-all (&optional force)
+(defun org-publish-current-project (&optional force)
-  "Publish all projects.
+  "Publish the project associated with the current file.
-With prefix argument, force publish all files."
+With a prefix argument, force publishing of all files in
+the project."
   (interactive "P")
   (interactive "P")
+  (org-publish-initialize-files-alist)
   (save-window-excursion
   (save-window-excursion
-    (let ((org-publish-use-timestamps-flag
+    (let ((project (org-publish-get-project-from-filename (buffer-file-name)))
-	   (if force nil org-publish-use-timestamps-flag))
+	  (org-publish-use-timestamps-flag
-	  (plists (org-publish-get-plists)))
+	   (if force nil org-publish-use-timestamps-flag)))
-      (mapcar 'org-publish-plist plists))))
+      (if (not project)
-
+	  (error "File %s is not part of any known project" (buffer-file-name)))
-
+      (org-publish project))))
 
 
 (provide 'org-publish)
 (provide 'org-publish)
 
 
+
 ;; arch-tag: 72807f3c-8af0-4a6b-8dca-c3376eb25adb
 ;; arch-tag: 72807f3c-8af0-4a6b-8dca-c3376eb25adb
 ;;; org-publish.el ends here
 ;;; org-publish.el ends here

+ 15 - 10
org.el

@@ -172,8 +172,11 @@ will be autoloaded when needed, preloading is not necessary."
 	(const :tag "    Apple Mail message links under OS X (org-mac-message.el)" org-mac-message)))
 	(const :tag "    Apple Mail message links under OS X (org-mac-message.el)" org-mac-message)))
 
 
 (defun org-load-default-extensions ()
 (defun org-load-default-extensions ()
-  "Load all extensions that are listed in `org-default-extensions'."
+  "Load all extensions listed in `org-default-extensions'."
-  (mapc 'require org-default-extensions))
+  (mapc (lambda (ext) 
+	  (condition-case nil (require ext)
+	    (error (message "Feature `%s' is not known" ext))))
+	org-default-extensions))
 
 
 ;; FIXME: Needs a separate group...
 ;; FIXME: Needs a separate group...
 (defcustom org-completion-fallback-command 'hippie-expand
 (defcustom org-completion-fallback-command 'hippie-expand
@@ -25239,7 +25242,7 @@ in a window.  A non-interactive call will only retunr the buffer."
 
 
 (defvar html-table-tag nil) ; dynamically scoped into this.
 (defvar html-table-tag nil) ; dynamically scoped into this.
 (defun org-export-as-html (arg &optional hidden ext-plist
 (defun org-export-as-html (arg &optional hidden ext-plist
-			       to-buffer body-only)
+			       to-buffer body-only pub-dir)
   "Export the outline as a pretty HTML file.
   "Export the outline as a pretty HTML file.
 If there is an active region, export only the region.  The prefix
 If there is an active region, export only the region.  The prefix
 ARG specifies how many levels of the outline should become
 ARG specifies how many levels of the outline should become
@@ -25248,11 +25251,12 @@ lists.  When HIDDEN is non-nil, don't display the HTML buffer.
 EXT-PLIST is a property list with external parameters overriding
 EXT-PLIST is a property list with external parameters overriding
 org-mode's default settings, but still inferior to file-local
 org-mode's default settings, but still inferior to file-local
 settings.  When TO-BUFFER is non-nil, create a buffer with that
 settings.  When TO-BUFFER is non-nil, create a buffer with that
-name and export to that buffer.  If TO-BUFFER is the symbol `string',
+name and export to that buffer.  If TO-BUFFER is the symbol
-don't leave any buffer behind but just return the resulting HTML as
+`string', don't leave any buffer behind but just return the
-a string.  When BODY-ONLY is set, don't produce the file header and footer,
+resulting HTML as a string.  When BODY-ONLY is set, don't produce
-simply return the content of <body>...</body>, without even
+the file header and footer, simply return the content of
-the body tags themselves."
+<body>...</body>, without even the body tags themselves.  When
+PUB-DIR is set, use this as the publishing directory."
   (interactive "P")
   (interactive "P")
 
 
   ;; Make sure we have a file name when we need it.
   ;; Make sure we have a file name when we need it.
@@ -25287,7 +25291,8 @@ the body tags themselves."
 		   (>= (org-end-of-subtree t t) (region-end))))))
 		   (>= (org-end-of-subtree t t) (region-end))))))
 	 ;; The following two are dynamically scoped into other
 	 ;; The following two are dynamically scoped into other
 	 ;; routines below.
 	 ;; routines below.
-	 (org-current-export-dir (org-export-directory :html opt-plist))
+	 (org-current-export-dir 
+	  (or pub-dir (org-export-directory :html opt-plist)))
 	 (org-current-export-file buffer-file-name)
 	 (org-current-export-file buffer-file-name)
          (level 0) (line "") (origline "") txt todo
          (level 0) (line "") (origline "") txt todo
          (umax nil)
          (umax nil)
@@ -25302,7 +25307,7 @@ the body tags themselves."
 			    (file-name-nondirectory buffer-file-name)))
 			    (file-name-nondirectory buffer-file-name)))
 		       "." html-extension)
 		       "." html-extension)
 		      (file-name-as-directory
 		      (file-name-as-directory
-		       (org-export-directory :html opt-plist)))))
+		       (or pub-dir (org-export-directory :html opt-plist))))))
 	 (current-dir (if buffer-file-name
 	 (current-dir (if buffer-file-name
 			  (file-name-directory buffer-file-name)
 			  (file-name-directory buffer-file-name)
 			default-directory))
 			default-directory))

+ 14 - 8
org.texi

@@ -7024,14 +7024,16 @@ the two following forms:
 
 
 @end lisp
 @end lisp
 
 
-In both cases, projects are configured by specifying property values.
+In both cases, projects are configured by specifying property values.  A
-A project defines the set of files that will be published, as well as
+project defines the set of files that will be published, as well as the
-the publishing configuration to use when publishing those files.  When
+publishing configuration to use when publishing those files.  When a
-a project takes the second form listed above, the individual members
+project takes the second form listed above, the individual members of
-of the ``components'' property are taken to be components of the
+the ``components'' property are taken to be components of the project,
-project, which group together files requiring different publishing
+which group together files requiring different publishing options.  When
-options. When you publish such a ``meta-project'' all the components
+you publish such a ``meta-project'' all the components will also be
-will also publish.
+published.
+
+For now, components are not allowed to have components themselves.
 
 
 @node Sources and destinations, Selecting files, Project alist, Configuration
 @node Sources and destinations, Selecting files, Project alist, Configuration
 @subsection Sources and destinations for files
 @subsection Sources and destinations for files
@@ -7072,6 +7074,10 @@ extension.
 @item @code{:include}
 @item @code{:include}
 @tab List of files to be included regardless of @code{:base-extension}
 @tab List of files to be included regardless of @code{:base-extension}
 and @code{:exclude}.
 and @code{:exclude}.
+
+@item @code{:recursive}
+@tab When non-@code{nil}, the @code{:base-directory} will be recursively
+published.
 @end multitable
 @end multitable
 
 
 @node Publishing action, Publishing options, Selecting files, Configuration
 @node Publishing action, Publishing options, Selecting files, Configuration