diff options
| author | Carsten Dominik | 2007-06-06 15:16:21 +0000 |
|---|---|---|
| committer | Carsten Dominik | 2007-06-06 15:16:21 +0000 |
| commit | 9542795adae087ac1f0854feaf9712e8705d1b46 (patch) | |
| tree | 63c34ad8d3711d1d5456a695e97cbceb0e0c13c2 /lisp | |
| parent | 873f531de6b9759e104ce151e429591d88fd0424 (diff) | |
| download | emacs-9542795adae087ac1f0854feaf9712e8705d1b46.tar.gz emacs-9542795adae087ac1f0854feaf9712e8705d1b46.zip | |
New file.
Diffstat (limited to 'lisp')
| -rw-r--r-- | lisp/textmodes/org-publish.el | 601 |
1 files changed, 601 insertions, 0 deletions
diff --git a/lisp/textmodes/org-publish.el b/lisp/textmodes/org-publish.el new file mode 100644 index 00000000000..8ec27a4431c --- /dev/null +++ b/lisp/textmodes/org-publish.el | |||
| @@ -0,0 +1,601 @@ | |||
| 1 | ;;; org-publish.el --- publish related org-mode files as a website | ||
| 2 | |||
| 3 | ;; Copyright (C) 2006, 2007 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: David O'Toole <dto@gnu.org> | ||
| 6 | ;; Keywords: hypermedia, outlines | ||
| 7 | ;; Version: 1.80 | ||
| 8 | |||
| 9 | ;; $Id: org-publish.el,v 1.80 2007/03/22 02:31:03 dto Exp dto $ | ||
| 10 | |||
| 11 | ;; This file is free software; you can redistribute it and/or modify | ||
| 12 | ;; it under the terms of the GNU General Public License as published by | ||
| 13 | ;; the Free Software Foundation; either version 2, or (at your option) | ||
| 14 | ;; any later version. | ||
| 15 | |||
| 16 | ;; This file is distributed in the hope that it will be useful, | ||
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | ;; GNU General Public License for more details. | ||
| 20 | |||
| 21 | ;; You should have received a copy of the GNU General Public License | ||
| 22 | ;; along with GNU Emacs; see the file COPYING. If not, write to | ||
| 23 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
| 24 | ;; Boston, MA 02110-1301, USA. | ||
| 25 | |||
| 26 | ;; This file is part of GNU Emacs. | ||
| 27 | |||
| 28 | ;;; Commentary: | ||
| 29 | |||
| 30 | ;; Requires at least version 4.27 of org.el | ||
| 31 | ;; | ||
| 32 | ;; The official org-mode website: | ||
| 33 | ;; http://staff.science.uva.nl/~dominik/Tools/org/ | ||
| 34 | ;; | ||
| 35 | ;; Home page for org-publish.el: | ||
| 36 | ;; http://dto.freeshell.org/notebook/OrgMode.html | ||
| 37 | |||
| 38 | ;; This program extends the HTML publishing support of Emacs Org-mode | ||
| 39 | ;; to allow configurable publishing of related sets of files as a | ||
| 40 | ;; complete website. | ||
| 41 | ;; | ||
| 42 | ;; org-publish.el can do the following: | ||
| 43 | ;; | ||
| 44 | ;; + Publish all one's org-files to html | ||
| 45 | ;; + Upload html, images, attachments and other files to a web server | ||
| 46 | ;; + Exclude selected private pages from publishing | ||
| 47 | ;; + Publish a clickable index of pages | ||
| 48 | ;; + Manage local timestamps, for publishing only changed files | ||
| 49 | ;; + Accept plugin functions to extend range of publishable content | ||
| 50 | ;; | ||
| 51 | ;; Special thanks to the org-mode maintainer Carsten Dominik for his | ||
| 52 | ;; ideas, enthusiasm, and cooperation. | ||
| 53 | |||
| 54 | ;;; Installation: | ||
| 55 | |||
| 56 | ;; Put org-publish.el in your load path, byte-compile it, and then add | ||
| 57 | ;; the following lines to your emacs initialization file: | ||
| 58 | |||
| 59 | ;; (autoload 'org-publish "org-publish" nil t) | ||
| 60 | ;; (autoload 'org-publish "org-publish-all" nil t) | ||
| 61 | ;; (autoload 'org-publish "org-publish-current-file" nil t) | ||
| 62 | ;; (autoload 'org-publish "org-publish-current-project" nil t) | ||
| 63 | |||
| 64 | ;; NOTE: When org-publish.el is included with org.el, those forms are | ||
| 65 | ;; already in the file org-install.el, and hence don't need to be put | ||
| 66 | ;; in your emacs initialization file in this case. | ||
| 67 | |||
| 68 | ;;; Usage: | ||
| 69 | ;; | ||
| 70 | ;; The program's main configuration variable is | ||
| 71 | ;; `org-publish-project-alist'. See below for example configurations | ||
| 72 | ;; with commentary. | ||
| 73 | |||
| 74 | ;; The main interactive functions are: | ||
| 75 | ;; | ||
| 76 | ;; M-x org-publish | ||
| 77 | ;; M-x org-publish-all | ||
| 78 | ;; M-x org-publish-current-file | ||
| 79 | ;; M-x org-publish-current-project | ||
| 80 | |||
| 81 | ;;;; Simple example configuration: | ||
| 82 | |||
| 83 | ;; (setq org-publish-project-alist | ||
| 84 | ;; (list | ||
| 85 | ;; '("org" . (:base-directory "~/org/" | ||
| 86 | ;; :base-extension "org" | ||
| 87 | ;; :publishing-directory "~/public_html" | ||
| 88 | ;; :with-section-numbers nil | ||
| 89 | ;; :table-of-contents nil | ||
| 90 | ;; :style "<link rel=stylesheet href=\"../other/mystyle.css\" type=\"text/css\">"))) | ||
| 91 | |||
| 92 | ;;;; More complex example configuration: | ||
| 93 | |||
| 94 | ;; Imagine your *.org files are kept in ~/org, your images in | ||
| 95 | ;; ~/images, and stylesheets in ~/other. Now imagine you want to | ||
| 96 | ;; publish the files through an ssh connection to a remote host, via | ||
| 97 | ;; Tramp-mode. To maintain relative links from *.org files to /images | ||
| 98 | ;; and /other, we should replicate the same directory structure in | ||
| 99 | ;; your web server account's designated html root (in this case, | ||
| 100 | ;; assumed to be ~/html) | ||
| 101 | |||
| 102 | ;; Once you've done created the proper directories, you can adapt the | ||
| 103 | ;; following example configuration to your specific paths, run M-x | ||
| 104 | ;; org-publish-all, and it should publish the files to the correct | ||
| 105 | ;; directories on the web server, transforming the *.org files into | ||
| 106 | ;; HTML, and leaving other files alone. | ||
| 107 | |||
| 108 | ;; (setq org-publish-project-alist | ||
| 109 | ;; (list | ||
| 110 | ;; '("orgfiles" :base-directory "~/org/" | ||
| 111 | ;; :base-extension "org" | ||
| 112 | ;; :publishing-directory "/ssh:user@host:~/html/notebook/" | ||
| 113 | ;; :publishing-function org-publish-org-to-html | ||
| 114 | ;; :exclude "PrivatePage.org" ;; regexp | ||
| 115 | ;; :headline-levels 3 | ||
| 116 | ;; :with-section-numbers nil | ||
| 117 | ;; :table-of-contents nil | ||
| 118 | ;; :style "<link rel=stylesheet href=\"../other/mystyle.css\" type=\"text/css\">" | ||
| 119 | ;; :auto-preamble t | ||
| 120 | ;; :auto-postamble nil) | ||
| 121 | ;; | ||
| 122 | ;; ("images" :base-directory "~/images/" | ||
| 123 | ;; :base-extension "jpg\\|gif\\|png" | ||
| 124 | ;; :publishing-directory "/ssh:user@host:~/html/images/" | ||
| 125 | ;; :publishing-function org-publish-attachment) | ||
| 126 | ;; | ||
| 127 | ;; ("other" :base-directory "~/other/" | ||
| 128 | ;; :base-extension "css" | ||
| 129 | ;; :publishing-directory "/ssh:user@host:~/html/other/" | ||
| 130 | ;; :publishing-function org-publish-attachment) | ||
| 131 | ;; ("website" :components ("orgfiles" "images" "other")))) | ||
| 132 | |||
| 133 | ;; For more information, see the documentation for the variable | ||
| 134 | ;; `org-publish-project-alist'. | ||
| 135 | |||
| 136 | ;; Of course, you don't have to publish to remote directories from | ||
| 137 | ;; within emacs. You can always just publish to local folders, and | ||
| 138 | ;; then use the synchronization/upload tool of your choice. | ||
| 139 | |||
| 140 | |||
| 141 | ;;; List of user-visible changes since version 1.27 | ||
| 142 | |||
| 143 | ;; 1.78: Allow list-valued :publishing-function | ||
| 144 | ;; 1.77: Added :preparation-function, this allows you to use GNU Make etc. | ||
| 145 | ;; 1.65: Remove old "composite projects". They're redundant. | ||
| 146 | ;; 1.64: Allow meta-projects with :components | ||
| 147 | ;; 1.57: Timestamps flag is now called "org-publish-use-timestamps-flag" | ||
| 148 | ;; 1.52: Properly set default for :index-filename | ||
| 149 | ;; 1.48: Composite projects allowed. | ||
| 150 | ;; :include keyword allowed. | ||
| 151 | ;; 1.43: Index no longer includes itself in the index. | ||
| 152 | ;; 1.42: Fix "function definition is void" error | ||
| 153 | ;; when :publishing-function not set in org-publish-current-file. | ||
| 154 | ;; 1.41: Fixed bug where index isn't published on first try. | ||
| 155 | ;; 1.37: Added interactive function "org-publish". Prompts for particular | ||
| 156 | ;; project name to publish. | ||
| 157 | ;; 1.34: Added force-publish option to all interactive functions. | ||
| 158 | ;; 1.32: Fixed "index.org has changed on disk" error during index publishing. | ||
| 159 | ;; 1.30: Fixed startup error caused by (require 'em-unix) | ||
| 160 | |||
| 161 | ;;; Code: | ||
| 162 | |||
| 163 | |||
| 164 | (eval-when-compile | ||
| 165 | (require 'cl)) | ||
| 166 | |||
| 167 | |||
| 168 | (defgroup org-publish nil | ||
| 169 | "Options for publishing a set of Org-mode and related files." | ||
| 170 | :tag "Org Publishing" | ||
| 171 | :group 'org) | ||
| 172 | |||
| 173 | |||
| 174 | (defcustom org-publish-project-alist nil | ||
| 175 | "Association list to control publishing behavior. | ||
| 176 | Each element of the alist is a publishing 'project.' The CAR of | ||
| 177 | each element is a string, uniquely identifying the project. The | ||
| 178 | CDR of each element is in one of the following forms: | ||
| 179 | |||
| 180 | (:property value :property value ... ) | ||
| 181 | |||
| 182 | OR, | ||
| 183 | |||
| 184 | (:components (\"project-1\" \"project-2\" ...)) | ||
| 185 | |||
| 186 | When the CDR of an element of org-publish-project-alist is in | ||
| 187 | this second form, the elements of the list after :components are | ||
| 188 | taken to be components of the project, which group together files | ||
| 189 | requiring different publishing options. When you publish such a | ||
| 190 | project with M-x org-publish, the components all publish. | ||
| 191 | |||
| 192 | When a property is given a value in org-publish-project-alist, its | ||
| 193 | setting overrides the value of the corresponding user variable | ||
| 194 | (if any) during publishing. However, options set within a file | ||
| 195 | override everything. | ||
| 196 | |||
| 197 | Most properties are optional, but some should always be set: | ||
| 198 | |||
| 199 | :base-directory Directory containing publishing source files | ||
| 200 | :base-extension Extension (without the dot!) of source files. | ||
| 201 | This can be a regular expression. | ||
| 202 | :publishing-directory Directory (possibly remote) where output | ||
| 203 | files will be published | ||
| 204 | |||
| 205 | The :exclude property may be used to prevent certain files from | ||
| 206 | being published. Its value may be a string or regexp matching | ||
| 207 | file names you don't want to be published. | ||
| 208 | |||
| 209 | The :include property may be used to include extra files. Its | ||
| 210 | value may be a list of filenames to include. The filenames are | ||
| 211 | considered relative to the publishing directory. | ||
| 212 | |||
| 213 | When both :include and :exclude properties are given values, the | ||
| 214 | exclusion step happens first. | ||
| 215 | |||
| 216 | One special property controls which back-end function to use for | ||
| 217 | publishing files in the project. This can be used to extend the | ||
| 218 | set of file types publishable by org-publish, as well as the set | ||
| 219 | of output formats. | ||
| 220 | |||
| 221 | :publishing-function Function to publish file. The default is | ||
| 222 | org-publish-org-to-html, but other | ||
| 223 | values are possible. May also be a | ||
| 224 | list of functions, in which case | ||
| 225 | each function in the list is invoked | ||
| 226 | in turn. | ||
| 227 | |||
| 228 | Another property allows you to insert code that prepares a | ||
| 229 | project for publishing. For example, you could call GNU Make on a | ||
| 230 | certain makefile, to ensure published files are built up to date. | ||
| 231 | |||
| 232 | :preparation-function Function to be called before publishing | ||
| 233 | this project. | ||
| 234 | |||
| 235 | Some properties control details of the Org publishing process, | ||
| 236 | and are equivalent to the corresponding user variables listed in | ||
| 237 | the right column. See the documentation for those variables to | ||
| 238 | learn more about their use and default values. | ||
| 239 | |||
| 240 | :language org-export-default-language | ||
| 241 | :headline-levels org-export-headline-levels | ||
| 242 | :section-numbers org-export-with-section-numbers | ||
| 243 | :table-of-contents org-export-with-toc | ||
| 244 | :emphasize org-export-with-emphasize | ||
| 245 | :sub-superscript org-export-with-sub-superscripts | ||
| 246 | :TeX-macros org-export-with-TeX-macros | ||
| 247 | :fixed-width org-export-with-fixed-width | ||
| 248 | :tables org-export-with-tables | ||
| 249 | :table-auto-headline org-export-highlight-first-table-line | ||
| 250 | :style org-export-html-style | ||
| 251 | :convert-org-links org-export-html-link-org-files-as-html | ||
| 252 | :inline-images org-export-html-inline-images | ||
| 253 | :expand-quoted-html org-export-html-expand | ||
| 254 | :timestamp org-export-html-with-timestamp | ||
| 255 | :publishing-directory org-export-publishing-directory | ||
| 256 | :preamble org-export-html-preamble | ||
| 257 | :postamble org-export-html-postamble | ||
| 258 | :auto-preamble org-export-html-auto-preamble | ||
| 259 | :auto-postamble org-export-html-auto-postamble | ||
| 260 | :author user-full-name | ||
| 261 | :email user-mail-address | ||
| 262 | |||
| 263 | The following properties may be used to control publishing of an | ||
| 264 | index of files or summary page for a given project. | ||
| 265 | |||
| 266 | :auto-index Whether to publish an index during | ||
| 267 | org-publish-current-project or org-publish-all. | ||
| 268 | :index-filename Filename for output of index. Defaults | ||
| 269 | to 'index.org' (which becomes 'index.html') | ||
| 270 | :index-title Title of index page. Defaults to name of file. | ||
| 271 | :index-function Plugin function to use for generation of index. | ||
| 272 | Defaults to 'org-publish-org-index', which | ||
| 273 | generates a plain list of links to all files | ||
| 274 | in the project. | ||
| 275 | " | ||
| 276 | :group 'org-publish | ||
| 277 | :type 'alist) | ||
| 278 | |||
| 279 | |||
| 280 | (defcustom org-publish-use-timestamps-flag t | ||
| 281 | "When non-nil, use timestamp checking to publish only changed files. | ||
| 282 | When nil, do no timestamp checking and always publish all | ||
| 283 | files." | ||
| 284 | :group 'org-publish | ||
| 285 | :type 'boolean) | ||
| 286 | |||
| 287 | |||
| 288 | (defcustom org-publish-timestamp-directory "~/.org-timestamps/" | ||
| 289 | "Name of directory in which to store publishing timestamps." | ||
| 290 | :group 'org-publish | ||
| 291 | :type 'string) | ||
| 292 | |||
| 293 | |||
| 294 | ;;;; Timestamp-related functions | ||
| 295 | |||
| 296 | |||
| 297 | (defun org-publish-timestamp-filename (filename) | ||
| 298 | "Return path to timestamp file for filename FILENAME." | ||
| 299 | (while (string-match "~\\|/" filename) | ||
| 300 | (setq filename (replace-match "_" nil t filename))) | ||
| 301 | (concat org-publish-timestamp-directory filename ".timestamp")) | ||
| 302 | |||
| 303 | |||
| 304 | (defun org-publish-needed-p (filename) | ||
| 305 | "Check whether file should be published. | ||
| 306 | If org-publish-use-timestamps-flag is set to nil, this function always | ||
| 307 | returns t. Otherwise, check the timestamps folder to determine | ||
| 308 | whether file should be published." | ||
| 309 | (if org-publish-use-timestamps-flag | ||
| 310 | (progn | ||
| 311 | ;; | ||
| 312 | ;; create folder if needed | ||
| 313 | (if (not (file-exists-p org-publish-timestamp-directory)) | ||
| 314 | (make-directory org-publish-timestamp-directory) | ||
| 315 | (if (not (file-directory-p org-publish-timestamp-directory)) | ||
| 316 | (error "org-publish-timestamp-directory must be a directory."))) | ||
| 317 | ;; | ||
| 318 | ;; check timestamp. ok if timestamp file doesn't exist | ||
| 319 | (let* ((timestamp (org-publish-timestamp-filename filename)) | ||
| 320 | (rtn (file-newer-than-file-p filename timestamp))) | ||
| 321 | (if rtn | ||
| 322 | ;; handle new timestamps | ||
| 323 | (if (not (file-exists-p timestamp)) | ||
| 324 | ;; create file | ||
| 325 | (with-temp-buffer | ||
| 326 | (make-directory (file-name-directory timestamp) :parents) | ||
| 327 | (write-file timestamp) | ||
| 328 | (kill-buffer (current-buffer))))) | ||
| 329 | rtn)) | ||
| 330 | t)) | ||
| 331 | |||
| 332 | |||
| 333 | (defun org-publish-update-timestamp (filename) | ||
| 334 | "Update publishing timestamp for file FILENAME." | ||
| 335 | (let ((timestamp (org-publish-timestamp-filename filename))) | ||
| 336 | ;; Emacs 21 doesn't have set-file-times | ||
| 337 | (if (fboundp 'set-file-times) | ||
| 338 | (set-file-times timestamp) | ||
| 339 | (call-process "touch" nil 0 nil timestamp)))) | ||
| 340 | |||
| 341 | |||
| 342 | ;;;; A hash mapping files to project names | ||
| 343 | |||
| 344 | |||
| 345 | (defvar org-publish-files (make-hash-table :test 'equal) "Hash | ||
| 346 | table mapping file names to project names.") | ||
| 347 | |||
| 348 | |||
| 349 | ;;;; Checking filenames against this hash | ||
| 350 | |||
| 351 | |||
| 352 | (defun org-publish-validate-link (link &optional directory) | ||
| 353 | (gethash (file-truename (expand-file-name link directory)) | ||
| 354 | org-publish-files)) | ||
| 355 | |||
| 356 | |||
| 357 | ;;;; Getting project information out of org-publish-project-alist | ||
| 358 | |||
| 359 | |||
| 360 | (defun org-publish-get-plists (&optional project-name) | ||
| 361 | "Return a list of property lists for project PROJECT-NAME. | ||
| 362 | When argument is not given, return all property lists for all projects." | ||
| 363 | (let ((alist (if project-name | ||
| 364 | (list (assoc project-name org-publish-project-alist)) | ||
| 365 | org-publish-project-alist)) | ||
| 366 | (project nil) | ||
| 367 | (plists nil) | ||
| 368 | (single nil) | ||
| 369 | (components nil)) | ||
| 370 | |||
| 371 | ;; | ||
| 372 | ;; | ||
| 373 | (while (setq project (pop alist)) | ||
| 374 | ;; what kind of project is it? | ||
| 375 | (if (setq components (plist-get (cdr project) :components)) | ||
| 376 | ;; meta project. annotate each plist with name of enclosing project | ||
| 377 | (setq single | ||
| 378 | (apply 'append | ||
| 379 | (mapcar 'org-publish-get-plists components))) | ||
| 380 | ;; normal project | ||
| 381 | (setq single (list (cdr project)))) | ||
| 382 | ;; | ||
| 383 | (setq plists (append plists single)) | ||
| 384 | (dolist (p single) | ||
| 385 | (let* ((exclude (plist-get p :exclude)) | ||
| 386 | (files (org-publish-get-base-files p exclude))) | ||
| 387 | (dolist (f files) | ||
| 388 | (puthash (file-truename f) (car project) org-publish-files))))) | ||
| 389 | plists)) | ||
| 390 | |||
| 391 | |||
| 392 | |||
| 393 | (defun org-publish-get-base-files (plist &optional exclude-regexp) | ||
| 394 | "Return a list of all files in project defined by PLIST. | ||
| 395 | If EXCLUDE-REGEXP is set, this will be used to filter out | ||
| 396 | matching filenames." | ||
| 397 | (let* ((dir (file-name-as-directory (plist-get plist :base-directory))) | ||
| 398 | (include-list (plist-get plist :include)) | ||
| 399 | (extension (or (plist-get plist :base-extension) "org")) | ||
| 400 | (regexp (concat "^[^\\.].*\\.\\(" extension "\\)$")) | ||
| 401 | (allfiles (directory-files dir t regexp))) | ||
| 402 | ;; | ||
| 403 | ;; exclude files | ||
| 404 | (setq allfiles | ||
| 405 | (if (not exclude-regexp) | ||
| 406 | allfiles | ||
| 407 | (delq nil | ||
| 408 | (mapcar (lambda (x) | ||
| 409 | (if (string-match exclude-regexp x) nil x)) | ||
| 410 | allfiles)))) | ||
| 411 | ;; | ||
| 412 | ;; include extra files | ||
| 413 | (let ((inc nil)) | ||
| 414 | (while (setq inc (pop include-list)) | ||
| 415 | (setq allfiles (cons (expand-file-name inc dir) allfiles)))) | ||
| 416 | |||
| 417 | allfiles)) | ||
| 418 | |||
| 419 | |||
| 420 | (defun org-publish-get-project-from-filename (filename) | ||
| 421 | "Figure out which project a given FILENAME belongs to, if any. | ||
| 422 | Filename should contain full path. Returns name of project, or | ||
| 423 | nil if not found." | ||
| 424 | (org-publish-get-plists) | ||
| 425 | (gethash (file-truename filename) org-publish-files)) | ||
| 426 | |||
| 427 | |||
| 428 | (defun org-publish-get-plist-from-filename (filename) | ||
| 429 | "Return publishing configuration plist for file FILENAME." | ||
| 430 | (let ((found nil)) | ||
| 431 | (mapcar | ||
| 432 | (lambda (plist) | ||
| 433 | (let ((files (org-publish-get-base-files plist))) | ||
| 434 | (if (member (expand-file-name filename) files) | ||
| 435 | (setq found plist)))) | ||
| 436 | (org-publish-get-plists)) | ||
| 437 | found)) | ||
| 438 | |||
| 439 | |||
| 440 | |||
| 441 | ;;;; Pluggable publishing back-end functions | ||
| 442 | |||
| 443 | |||
| 444 | (defun org-publish-org-to-html (plist filename) | ||
| 445 | "Publish an org file to HTML. | ||
| 446 | PLIST is the property list for the given project. | ||
| 447 | FILENAME is the filename of the org file to be published." | ||
| 448 | (require 'org) | ||
| 449 | (let* ((arg (plist-get plist :headline-levels))) | ||
| 450 | (progn | ||
| 451 | (find-file filename) | ||
| 452 | (org-export-as-html arg nil plist) | ||
| 453 | ;; get rid of HTML buffer | ||
| 454 | (kill-buffer (current-buffer))))) | ||
| 455 | |||
| 456 | |||
| 457 | (defun org-publish-attachment (plist filename) | ||
| 458 | "Publish a file with no transformation of any kind. | ||
| 459 | PLIST is the property list for the given project. | ||
| 460 | FILENAME is the filename of the file to be published." | ||
| 461 | ;; make sure eshell/cp code is loaded | ||
| 462 | (require 'eshell) | ||
| 463 | (require 'esh-maint) | ||
| 464 | (require 'em-unix) | ||
| 465 | (let ((destination (file-name-as-directory (plist-get plist :publishing-directory)))) | ||
| 466 | (eshell/cp filename destination))) | ||
| 467 | |||
| 468 | |||
| 469 | ;;;; Publishing files, sets of files, and indices | ||
| 470 | |||
| 471 | |||
| 472 | (defun org-publish-file (filename) | ||
| 473 | "Publish file FILENAME." | ||
| 474 | (let* ((project-name (org-publish-get-project-from-filename filename)) | ||
| 475 | (plist (org-publish-get-plist-from-filename filename)) | ||
| 476 | (publishing-function (or (plist-get plist :publishing-function) 'org-publish-org-to-html))) | ||
| 477 | (if (not project-name) | ||
| 478 | (error (format "File %s is not part of any known project." filename))) | ||
| 479 | (when (org-publish-needed-p filename) | ||
| 480 | (if (listp publishing-function) | ||
| 481 | ;; allow chain of publishing functions | ||
| 482 | (mapc (lambda (f) | ||
| 483 | (funcall f plist filename)) | ||
| 484 | publishing-function) | ||
| 485 | (funcall publishing-function plist filename)) | ||
| 486 | (org-publish-update-timestamp filename)))) | ||
| 487 | |||
| 488 | |||
| 489 | (defun org-publish-plist (plist) | ||
| 490 | "Publish all files in set defined by PLIST. | ||
| 491 | If :auto-index is set, publish the index too." | ||
| 492 | (let* ((exclude-regexp (plist-get plist :exclude)) | ||
| 493 | (publishing-function (or (plist-get plist :publishing-function) 'org-publish-org-to-html)) | ||
| 494 | (index-p (plist-get plist :auto-index)) | ||
| 495 | (index-filename (or (plist-get plist :index-filename) "index.org")) | ||
| 496 | (index-function (or (plist-get plist :index-function) 'org-publish-org-index)) | ||
| 497 | (preparation-function (plist-get plist :preparation-function)) | ||
| 498 | (f nil)) | ||
| 499 | ;; | ||
| 500 | (when preparation-function | ||
| 501 | (funcall preparation-function)) | ||
| 502 | (if index-p | ||
| 503 | (funcall index-function plist index-filename)) | ||
| 504 | (let ((files (org-publish-get-base-files plist exclude-regexp))) | ||
| 505 | (while (setq f (pop files)) | ||
| 506 | ;; check timestamps | ||
| 507 | (when (org-publish-needed-p f) | ||
| 508 | (if (listp publishing-function) | ||
| 509 | ;; allow chain of publishing functions | ||
| 510 | (mapc (lambda (func) | ||
| 511 | (funcall func plist f)) | ||
| 512 | publishing-function) | ||
| 513 | (funcall publishing-function plist f)) | ||
| 514 | (org-publish-update-timestamp f)))))) | ||
| 515 | |||
| 516 | |||
| 517 | (defun org-publish-org-index (plist &optional index-filename) | ||
| 518 | "Create an index of pages in set defined by PLIST. | ||
| 519 | Optionally set the filename of the index with INDEX-FILENAME; | ||
| 520 | default is 'index.org'." | ||
| 521 | (let* ((dir (file-name-as-directory (plist-get plist :base-directory))) | ||
| 522 | (exclude-regexp (plist-get plist :exclude)) | ||
| 523 | (files (org-publish-get-base-files plist exclude-regexp)) | ||
| 524 | (index-filename (concat dir (or index-filename "index.org"))) | ||
| 525 | (index-buffer (find-buffer-visiting index-filename)) | ||
| 526 | (ifn (file-name-nondirectory index-filename)) | ||
| 527 | (f nil)) | ||
| 528 | ;; | ||
| 529 | ;; if buffer is already open, kill it to prevent error message | ||
| 530 | (if index-buffer | ||
| 531 | (kill-buffer index-buffer)) | ||
| 532 | (with-temp-buffer | ||
| 533 | (while (setq f (pop files)) | ||
| 534 | (let ((fn (file-name-nondirectory f))) | ||
| 535 | (unless (string= fn ifn) ;; index shouldn't index itself | ||
| 536 | (insert (concat " + [[file:" fn "][" | ||
| 537 | (file-name-sans-extension fn) | ||
| 538 | "]]\n"))))) | ||
| 539 | (write-file index-filename) | ||
| 540 | (kill-buffer (current-buffer))))) | ||
| 541 | |||
| 542 | |||
| 543 | ;(defun org-publish-meta-index (meta-plist &optional index-filename) | ||
| 544 | ; "Create an index for a metaproject." | ||
| 545 | ; (let* ((plists ( | ||
| 546 | |||
| 547 | |||
| 548 | ;;;; Interactive publishing functions | ||
| 549 | |||
| 550 | |||
| 551 | ;;;###autoload | ||
| 552 | (defun org-publish (project-name &optional force) | ||
| 553 | "Publish the project PROJECT-NAME." | ||
| 554 | (interactive (list (completing-read "Project name: " org-publish-project-alist | ||
| 555 | nil t) | ||
| 556 | current-prefix-arg)) | ||
| 557 | (save-window-excursion | ||
| 558 | (let ((org-publish-use-timestamps-flag (if force nil t)) | ||
| 559 | (plists (org-publish-get-plists project-name))) | ||
| 560 | (mapcar 'org-publish-plist plists)))) | ||
| 561 | |||
| 562 | |||
| 563 | ;;;###autoload | ||
| 564 | (defun org-publish-current-project (&optional force) | ||
| 565 | "Publish the project associated with the current file. | ||
| 566 | With prefix argument, force publishing all files in project." | ||
| 567 | (interactive "P") | ||
| 568 | (save-window-excursion | ||
| 569 | (let* ((project-name (org-publish-get-project-from-filename (buffer-file-name))) | ||
| 570 | (org-publish-use-timestamps-flag (if force nil t))) | ||
| 571 | (if (not project-name) | ||
| 572 | (error (format "File %s is not part of any known project." (buffer-file-name)))) | ||
| 573 | (org-publish project-name)))) | ||
| 574 | |||
| 575 | |||
| 576 | ;;;###autoload | ||
| 577 | (defun org-publish-current-file (&optional force) | ||
| 578 | "Publish the current file. | ||
| 579 | With prefix argument, force publish the file." | ||
| 580 | (interactive "P") | ||
| 581 | (save-window-excursion | ||
| 582 | (let ((org-publish-use-timestamps-flag | ||
| 583 | (if force nil t))) | ||
| 584 | (org-publish-file (buffer-file-name))))) | ||
| 585 | |||
| 586 | |||
| 587 | ;;;###autoload | ||
| 588 | (defun org-publish-all (&optional force) | ||
| 589 | "Publish all projects. | ||
| 590 | With prefix argument, force publish all files." | ||
| 591 | (interactive "P") | ||
| 592 | (save-window-excursion | ||
| 593 | (let ((org-publish-use-timestamps-flag | ||
| 594 | (if force nil t)) | ||
| 595 | (plists (org-publish-get-plists))) | ||
| 596 | (mapcar 'org-publish-plist plists)))) | ||
| 597 | |||
| 598 | |||
| 599 | |||
| 600 | (provide 'org-publish) | ||
| 601 | ;;; org-publish.el ends here | ||