emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
* [RFC] Draft mode
@ 2015-09-27  9:38 Nicolas Goaziou
  2015-09-27 11:37 ` Nicolas Goaziou
  2015-09-27 18:39 ` Rasmus
  0 siblings, 2 replies; 19+ messages in thread
From: Nicolas Goaziou @ 2015-09-27  9:38 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 664 bytes --]

Hello,

The following patch implements a draft mode for export. When in draft
mode, invalid macros and links do not throw an error. It can be toggled
with `org-export-as-draft' variable, or using C-d in export dispatch.

It introduces a backward incompatible change since it modifies signature
from `org-export-as' and alike.

This patch is incomplete as it is missing some documentation, an entry
in ORG-NEWS and some tests. Also, export back-end in contrib/ directory
are not updated yet. In any case, feedback welcome.

It is meant to be applied on top of master branch.


Regards,

-- 
Nicolas Goaziou                                                0x80A93738

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Implement-draft-mode.patch --]
[-- Type: text/x-diff, Size: 66261 bytes --]

From 4ad64f60eb12e39a9140feb4e0c7c046e014ec6c Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <mail@nicolasgoaziou.fr>
Date: Sun, 27 Sep 2015 10:59:20 +0200
Subject: [PATCH] ox: Implement draft mode

* lisp/ox.el (org-export-as-draft): New variable.
(org-export-dispatch-use-expert-ui): Document new export option.
(org-export-data): Ignore user errors when in draft mode.
(org-export-as): Add new optional argument to toggle draft mode.
(org-export-string-as):
(org-export-to-buffer):
(org-export-to-file): Handle signature change.
(org-export-dispatch):
(org-export--dispatch-ui):
(org-export--dispatch-action): Provide a mean to toggle draft mode.

* lisp/ox-ascii.el (ascii):
(org-ascii-export-as-ascii):
(org-ascii-export-to-ascii):

* lisp/ox-beamer.el (beamer):
(org-beamer-export-as-latex):
(org-beamer-export-to-latex):
(org-beamer-export-to-pdf):

* lisp/ox-html.el (html):
(org-html-export-as-html):

* lisp/ox-icalendar.el (icalendar):

* lisp/ox-latex.el (latex):
(org-latex-export-as-latex):
(org-latex-export-to-latex):
(org-latex-export-to-pdf):

* lisp/ox-man.el (man):
(org-man-export-to-man):
(org-man-export-to-pdf):

* lisp/ox-md.el (md):
(org-md-export-as-markdown):
(org-md-export-to-markdown):

* lisp/ox-odt.el (odt):
(org-odt-export-to-odt):

* lisp/ox-org.el (org):
(org-org-export-to-org):

* lisp/ox-publish.el (org-publish-org-to):

* lisp/ox-texinfo.el (texinfo):
(org-texinfo-export-to-texinfo):
(org-texinfo-export-to-info): Handle signature change.

* testing/lisp/test-ox.el (test-org-export/handle-options):
(test-org-export/with-timestamps):
(test-org-export/uninterpreted):
(test-org-export/expand-macro):
(test-org-export/footnote-first-reference-p):
(test-org-export/handle-inlinetasks): Update tests.
---
 lisp/ox-ascii.el        |  39 ++++----
 lisp/ox-beamer.el       |  21 +++--
 lisp/ox-html.el         |  11 ++-
 lisp/ox-icalendar.el    |   4 +-
 lisp/ox-latex.el        |  26 ++++--
 lisp/ox-man.el          |  16 +++-
 lisp/ox-md.el           |  25 +++--
 lisp/ox-odt.el          |  21 +++--
 lisp/ox-org.el          |  13 ++-
 lisp/ox-publish.el      |   2 +-
 lisp/ox-texinfo.el      |  20 ++--
 lisp/ox.el              | 242 +++++++++++++++++++++++++++---------------------
 testing/lisp/test-ox.el | 123 ++++++++++++------------
 13 files changed, 329 insertions(+), 234 deletions(-)

diff --git a/lisp/ox-ascii.el b/lisp/ox-ascii.el
index 5cc70bd..b50cebb 100644
--- a/lisp/ox-ascii.el
+++ b/lisp/ox-ascii.el
@@ -97,23 +97,23 @@
   :menu-entry
   '(?t "Export to Plain Text"
        ((?A "As ASCII buffer"
-	    (lambda (a s v b)
-	      (org-ascii-export-as-ascii a s v b '(:ascii-charset ascii))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-as-ascii a s v b d '(:ascii-charset ascii))))
 	(?a "As ASCII file"
-	    (lambda (a s v b)
-	      (org-ascii-export-to-ascii a s v b '(:ascii-charset ascii))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-to-ascii a s v b d '(:ascii-charset ascii))))
 	(?L "As Latin1 buffer"
-	    (lambda (a s v b)
-	      (org-ascii-export-as-ascii a s v b '(:ascii-charset latin1))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-as-ascii a s v b d '(:ascii-charset latin1))))
 	(?l "As Latin1 file"
-	    (lambda (a s v b)
-	      (org-ascii-export-to-ascii a s v b '(:ascii-charset latin1))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-to-ascii a s v b d '(:ascii-charset latin1))))
 	(?U "As UTF-8 buffer"
-	    (lambda (a s v b)
-	      (org-ascii-export-as-ascii a s v b '(:ascii-charset utf-8))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-as-ascii a s v b d '(:ascii-charset utf-8))))
 	(?u "As UTF-8 file"
-	    (lambda (a s v b)
-	      (org-ascii-export-to-ascii a s v b '(:ascii-charset utf-8))))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-to-ascii a s v b d '(:ascii-charset utf-8))))))
   :filters-alist '((:filter-headline . org-ascii-filter-headline-blank-lines)
 		   (:filter-parse-tree org-ascii-filter-paragraph-spacing
 				       org-ascii-filter-comment-spacing)
@@ -2023,7 +2023,7 @@ a communication channel."
 
 ;;;###autoload
 (defun org-ascii-export-as-ascii
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to a text buffer.
 
 If narrowing is active in the current buffer, only export its
@@ -2045,6 +2045,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, strip title and
 table of contents from output.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -2054,11 +2057,12 @@ will be displayed when `org-export-show-temporary-export-buffer'
 is non-nil."
   (interactive)
   (org-export-to-buffer 'ascii "*Org ASCII Export*"
-    async subtreep visible-only body-only ext-plist (lambda () (text-mode))))
+    async subtreep visible-only body-only draft ext-plist
+    (lambda () (text-mode))))
 
 ;;;###autoload
 (defun org-ascii-export-to-ascii
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to a text file.
 
 If narrowing is active in the current buffer, only export its
@@ -2080,6 +2084,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, strip title and
 table of contents from output.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -2088,7 +2095,7 @@ Return output file's name."
   (interactive)
   (let ((file (org-export-output-file-name ".txt" subtreep)))
     (org-export-to-file 'ascii file
-      async subtreep visible-only body-only ext-plist)))
+      async subtreep visible-only body-only draft ext-plist)))
 
 ;;;###autoload
 (defun org-ascii-publish-to-ascii (plist filename pub-dir)
diff --git a/lisp/ox-beamer.el b/lisp/ox-beamer.el
index 3119bd4..f99182a 100644
--- a/lisp/ox-beamer.el
+++ b/lisp/ox-beamer.el
@@ -238,9 +238,9 @@ Return overlay specification, as a string, or nil."
 	(?b "As LaTeX file (Beamer)" org-beamer-export-to-latex)
 	(?P "As PDF file (Beamer)" org-beamer-export-to-pdf)
 	(?O "As PDF file and open (Beamer)"
-	    (lambda (a s v b)
-	      (if a (org-beamer-export-to-pdf t s v b)
-		(org-open-file (org-beamer-export-to-pdf nil s v b)))))))
+	    (lambda (a s v b d)
+	      (if a (org-beamer-export-to-pdf t s v b d)
+		(org-open-file (org-beamer-export-to-pdf nil s v b d)))))))
   :options-alist
   '((:headline-levels nil "H" org-beamer-frame-level)
     (:latex-class "LATEX_CLASS" nil "beamer" t)
@@ -999,7 +999,7 @@ value."
 
 ;;;###autoload
 (defun org-beamer-export-as-latex
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer as a Beamer buffer.
 
 If narrowing is active in the current buffer, only export its
@@ -1021,6 +1021,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -1034,7 +1037,7 @@ is non-nil."
 
 ;;;###autoload
 (defun org-beamer-export-to-latex
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer as a Beamer presentation (tex).
 
 If narrowing is active in the current buffer, only export its
@@ -1056,6 +1059,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -1068,7 +1074,7 @@ Return output file's name."
 
 ;;;###autoload
 (defun org-beamer-export-to-pdf
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer as a Beamer presentation (PDF).
 
 If narrowing is active in the current buffer, only export its
@@ -1090,6 +1096,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
diff --git a/lisp/ox-html.el b/lisp/ox-html.el
index 960bee8..fa5a982 100644
--- a/lisp/ox-html.el
+++ b/lisp/ox-html.el
@@ -104,9 +104,9 @@
        ((?H "As HTML buffer" org-html-export-as-html)
 	(?h "As HTML file" org-html-export-to-html)
 	(?o "As HTML file and open"
-	    (lambda (a s v b)
-	      (if a (org-html-export-to-html t s v b)
-		(org-open-file (org-html-export-to-html nil s v b)))))))
+	    (lambda (a s v b d)
+	      (if a (org-html-export-to-html t s v b d)
+		(org-open-file (org-html-export-to-html nil s v b d)))))))
   :options-alist
   '((:html-doctype "HTML_DOCTYPE" nil org-html-doctype)
     (:html-container "HTML_CONTAINER" nil org-html-container-element)
@@ -3505,7 +3505,7 @@ contextual information."
 
 ;;;###autoload
 (defun org-html-export-as-html
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to an HTML buffer.
 
 If narrowing is active in the current buffer, only export its
@@ -3527,6 +3527,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"<body>\" and \"</body>\" tags.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el
index aefddf8..d5fc97e 100644
--- a/lisp/ox-icalendar.el
+++ b/lisp/ox-icalendar.el
@@ -276,9 +276,9 @@ re-read the iCalendar file.")
   '(?c "Export to iCalendar"
        ((?f "Current file" org-icalendar-export-to-ics)
 	(?a "All agenda files"
-	    (lambda (a s v b) (org-icalendar-export-agenda-files a)))
+	    (lambda (a &rest _) (org-icalendar-export-agenda-files a)))
 	(?c "Combine all agenda files"
-	    (lambda (a s v b) (org-icalendar-combine-agenda-files a))))))
+	    (lambda (a &rest _) (org-icalendar-combine-agenda-files a))))))
 
 
 \f
diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el
index 1840a54..73968a0 100644
--- a/lisp/ox-latex.el
+++ b/lisp/ox-latex.el
@@ -97,9 +97,9 @@
 	(?l "As LaTeX file" org-latex-export-to-latex)
 	(?p "As PDF file" org-latex-export-to-pdf)
 	(?o "As PDF file and open"
-	    (lambda (a s v b)
-	      (if a (org-latex-export-to-pdf t s v b)
-		(org-open-file (org-latex-export-to-pdf nil s v b)))))))
+	    (lambda (a s v b d)
+	      (if a (org-latex-export-to-pdf t s v b d)
+		(org-open-file (org-latex-export-to-pdf nil s v b d)))))))
   :filters-alist '((:filter-options . org-latex-math-block-options-filter)
 		   (:filter-parse-tree org-latex-math-block-tree-filter
 				       org-latex-matrices-tree-filter))
@@ -3310,7 +3310,7 @@ contextual information."
 
 ;;;###autoload
 (defun org-latex-export-as-latex
-  (&optional async subtreep visible-only body-only ext-plist)
+    (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer as a LaTeX buffer.
 
 If narrowing is active in the current buffer, only export its
@@ -3332,6 +3332,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -3341,7 +3344,8 @@ will be displayed when `org-export-show-temporary-export-buffer'
 is non-nil."
   (interactive)
   (org-export-to-buffer 'latex "*Org LATEX Export*"
-    async subtreep visible-only body-only ext-plist (lambda () (LaTeX-mode))))
+    async subtreep visible-only body-only draft ext-plist
+    (lambda () (LaTeX-mode))))
 
 ;;;###autoload
 (defun org-latex-convert-region-to-latex ()
@@ -3354,7 +3358,7 @@ command to convert it."
 
 ;;;###autoload
 (defun org-latex-export-to-latex
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to a LaTeX file.
 
 If narrowing is active in the current buffer, only export its
@@ -3376,17 +3380,20 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings."
   (interactive)
   (let ((outfile (org-export-output-file-name ".tex" subtreep)))
     (org-export-to-file 'latex outfile
-      async subtreep visible-only body-only ext-plist)))
+      async subtreep visible-only body-only draft ext-plist)))
 
 ;;;###autoload
 (defun org-latex-export-to-pdf
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to LaTeX then process through to PDF.
 
 If narrowing is active in the current buffer, only export its
@@ -3408,6 +3415,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
diff --git a/lisp/ox-man.el b/lisp/ox-man.el
index a5d0d49..edb4e15 100644
--- a/lisp/ox-man.el
+++ b/lisp/ox-man.el
@@ -104,9 +104,9 @@
        ((?m "As MAN file" org-man-export-to-man)
 	(?p "As PDF file" org-man-export-to-pdf)
 	(?o "As PDF file and open"
-	    (lambda (a s v b)
-	      (if a (org-man-export-to-pdf t s v b)
-		(org-open-file (org-man-export-to-pdf nil s v b)))))))
+	    (lambda (a s v b d)
+	      (if a (org-man-export-to-pdf t s v b d)
+		(org-open-file (org-man-export-to-pdf nil s v b d)))))))
   :options-alist
   '((:man-class "MAN_CLASS" nil nil t)
     (:man-class-options "MAN_CLASS_OPTIONS" nil nil t)
@@ -1115,7 +1115,7 @@ contextual information."
 ;;; Interactive functions
 
 (defun org-man-export-to-man
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to a Man file.
 
 If narrowing is active in the current buffer, only export its
@@ -1137,6 +1137,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only the body
 without any markers.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -1148,7 +1151,7 @@ Return output file's name."
       async subtreep visible-only body-only ext-plist)))
 
 (defun org-man-export-to-pdf
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to Groff then process through to PDF.
 
 If narrowing is active in the current buffer, only export its
@@ -1170,6 +1173,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write between
 markers.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
diff --git a/lisp/ox-md.el b/lisp/ox-md.el
index 602c149..502a15b 100644
--- a/lisp/ox-md.el
+++ b/lisp/ox-md.el
@@ -60,12 +60,13 @@ This variable can be set to either `atx' or `setext'."
   :menu-entry
   '(?m "Export to Markdown"
        ((?M "To temporary buffer"
-	    (lambda (a s v b) (org-md-export-as-markdown a s v)))
-	(?m "To file" (lambda (a s v b) (org-md-export-to-markdown a s v)))
+	    (lambda (a s v b d) (org-md-export-as-markdown a s v nil d)))
+	(?m "To file"
+	    (lambda (a s v b d) (org-md-export-to-markdown a s v nil d)))
 	(?o "To file and open"
-	    (lambda (a s v b)
-	      (if a (org-md-export-to-markdown t s v)
-		(org-open-file (org-md-export-to-markdown nil s v)))))))
+	    (lambda (a s v b d)
+	      (if a (org-md-export-to-markdown t s v nil d)
+		(org-open-file (org-md-export-to-markdown nil s v nil d)))))))
   :translate-alist '((bold . org-md-bold)
 		     (code . org-md-verbatim)
 		     (example-block . org-md-example-block)
@@ -487,7 +488,7 @@ as a communication channel."
 ;;; Interactive function
 
 ;;;###autoload
-(defun org-md-export-as-markdown (&optional async subtreep visible-only)
+(defun org-md-export-as-markdown (&optional async subtreep visible-only draft)
   "Export current buffer to a Markdown buffer.
 
 If narrowing is active in the current buffer, only export its
@@ -506,12 +507,15 @@ first.
 When optional argument VISIBLE-ONLY is non-nil, don't export
 contents of hidden elements.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 Export is done in a buffer named \"*Org MD Export*\", which will
 be displayed when `org-export-show-temporary-export-buffer' is
 non-nil."
   (interactive)
   (org-export-to-buffer 'md "*Org MD Export*"
-    async subtreep visible-only nil nil (lambda () (text-mode))))
+    async subtreep visible-only nil nil nil (lambda () (text-mode))))
 
 ;;;###autoload
 (defun org-md-convert-region-to-md ()
@@ -524,7 +528,7 @@ this command to convert it."
 
 
 ;;;###autoload
-(defun org-md-export-to-markdown (&optional async subtreep visible-only)
+(defun org-md-export-to-markdown (&optional async subtreep visible-only draft)
   "Export current buffer to a Markdown file.
 
 If narrowing is active in the current buffer, only export its
@@ -543,10 +547,13 @@ first.
 When optional argument VISIBLE-ONLY is non-nil, don't export
 contents of hidden elements.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 Return output file's name."
   (interactive)
   (let ((outfile (org-export-output-file-name ".md" subtreep)))
-    (org-export-to-file 'md outfile async subtreep visible-only)))
+    (org-export-to-file 'md outfile async subtreep visible-only nil draft)))
 
 ;;;###autoload
 (defun org-md-publish-to-md (plist filename pub-dir)
diff --git a/lisp/ox-odt.el b/lisp/ox-odt.el
index 75353a0..76f105e 100644
--- a/lisp/ox-odt.el
+++ b/lisp/ox-odt.el
@@ -92,9 +92,10 @@
   '(?o "Export to ODT"
        ((?o "As ODT file" org-odt-export-to-odt)
 	(?O "As ODT file and open"
-	    (lambda (a s v b)
-	      (if a (org-odt-export-to-odt t s v)
-		(org-open-file (org-odt-export-to-odt nil s v) 'system))))))
+	    (lambda (a s v b d)
+	      (if a (org-odt-export-to-odt t s v nil d)
+		(org-open-file
+		 (org-odt-export-to-odt nil s v nil d) 'system))))))
   :options-alist
   '((:odt-styles-file "ODT_STYLES_FILE" nil nil t)
     (:description "DESCRIPTION" nil nil newline)
@@ -4210,7 +4211,8 @@ formula file."
 ;;;; Export to OpenDocument Text
 
 ;;;###autoload
-(defun org-odt-export-to-odt (&optional async subtreep visible-only ext-plist)
+(defun org-odt-export-to-odt
+    (&optional async subtreep visible-only draft ext-plist)
   "Export current buffer to a ODT file.
 
 If narrowing is active in the current buffer, only export its
@@ -4229,6 +4231,9 @@ first.
 When optional argument VISIBLE-ONLY is non-nil, don't export
 contents of hidden elements.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -4256,8 +4261,9 @@ Return output file's name."
 			(let ((nxml-auto-insert-xml-declaration-flag nil))
 			  (find-file-noselect
 			   (concat org-odt-zip-dir "content.xml") t))))
-		     (output (org-export-as
-			      'odt ,subtreep ,visible-only nil ,ext-plist)))
+		     (output
+		      (org-export-as
+		       'odt ,subtreep ,visible-only nil ,draft ,ext-plist)))
 		 (with-current-buffer out-buf
 		   (erase-buffer)
 		   (insert output)))))))
@@ -4271,7 +4277,8 @@ Return output file's name."
 	      ;; styles.
 	      (hfy-user-sheet-assoc nil))
 	 ;; Initialize content.xml and kick-off the export process.
-	 (let ((output (org-export-as 'odt subtreep visible-only nil ext-plist))
+	 (let ((output
+		(org-export-as 'odt subtreep visible-only nil draft ext-plist))
 	       (out-buf (progn
 			  (require 'nxml-mode)
 			  (let ((nxml-auto-insert-xml-declaration-flag nil))
diff --git a/lisp/ox-org.el b/lisp/ox-org.el
index b395577..c19147b 100644
--- a/lisp/ox-org.el
+++ b/lisp/ox-org.el
@@ -105,9 +105,9 @@ setting of `org-html-htmlize-output-type' is `css'."
        ((?O "As Org buffer" org-org-export-as-org)
 	(?o "As Org file" org-org-export-to-org)
 	(?v "As Org file and open"
-	    (lambda (a s v b)
-	      (if a (org-org-export-to-org t s v b)
-		(org-open-file (org-org-export-to-org nil s v b))))))))
+	    (lambda (a s v b d)
+	      (if a (org-org-export-to-org t s v b d)
+		(org-open-file (org-org-export-to-org nil s v b d))))))))
 
 (defun org-org-identity (blob contents info)
   "Transcode BLOB element or object back into Org syntax.
@@ -244,7 +244,7 @@ non-nil."
 
 ;;;###autoload
 (defun org-org-export-to-org
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to an org file.
 
 If narrowing is active in the current buffer, only export its
@@ -266,6 +266,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, strip document
 keywords from output.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -274,7 +277,7 @@ Return output file name."
   (interactive)
   (let ((outfile (org-export-output-file-name ".org" subtreep)))
     (org-export-to-file 'org outfile
-      async subtreep visible-only body-only ext-plist)))
+      async subtreep visible-only body-only draft ext-plist)))
 
 ;;;###autoload
 (defun org-org-publish-to-org (plist filename pub-dir)
diff --git a/lisp/ox-publish.el b/lisp/ox-publish.el
index 20cacf9..79fffc9 100644
--- a/lisp/ox-publish.el
+++ b/lisp/ox-publish.el
@@ -582,7 +582,7 @@ Return output file name."
 		    (org-export-output-file-name extension nil pub-dir))
 		   (body-p (plist-get plist :body-only)))
 	       (org-export-to-file backend output-file
-		 nil nil nil body-p
+		 nil nil nil body-p nil
 		 ;; Add `org-publish--collect-references' and
 		 ;; `org-publish-collect-index' to final output
 		 ;; filters.  The latter isn't dependent on
diff --git a/lisp/ox-texinfo.el b/lisp/ox-texinfo.el
index c8c6554..0526eeb 100644
--- a/lisp/ox-texinfo.el
+++ b/lisp/ox-texinfo.el
@@ -88,9 +88,9 @@
        ((?t "As TEXI file" org-texinfo-export-to-texinfo)
 	(?i "As INFO file" org-texinfo-export-to-info)
 	(?o "As INFO file and open"
-	    (lambda (a s v b)
-	      (if a (org-texinfo-export-to-info t s v b)
-		(org-open-file (org-texinfo-export-to-info nil s v b)))))))
+	    (lambda (a s v b d)
+	      (if a (org-texinfo-export-to-info t s v b d)
+		(org-open-file (org-texinfo-export-to-info nil s v b d)))))))
   :options-alist
   '((:texinfo-filename "TEXINFO_FILENAME" nil nil t)
     (:texinfo-class "TEXINFO_CLASS" nil org-texinfo-default-class t)
@@ -1483,7 +1483,7 @@ contextual information."
 ;;; Interactive functions
 
 (defun org-texinfo-export-to-texinfo
-  (&optional async subtreep visible-only body-only ext-plist)
+    (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to a Texinfo file.
 
 If narrowing is active in the current buffer, only export its
@@ -1505,6 +1505,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -1514,10 +1517,10 @@ Return output file's name."
   (let ((outfile (org-export-output-file-name ".texi" subtreep))
 	(org-export-coding-system org-texinfo-coding-system))
     (org-export-to-file 'texinfo outfile
-      async subtreep visible-only body-only ext-plist)))
+      async subtreep visible-only body-only draft ext-plist)))
 
 (defun org-texinfo-export-to-info
-  (&optional async subtreep visible-only body-only ext-plist)
+    (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to Texinfo then process through to INFO.
 
 If narrowing is active in the current buffer, only export its
@@ -1539,6 +1542,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -1551,7 +1557,7 @@ Return INFO file's name."
   (let ((outfile (org-export-output-file-name ".texi" subtreep))
 	(org-export-coding-system org-texinfo-coding-system))
     (org-export-to-file 'texinfo outfile
-      async subtreep visible-only body-only ext-plist
+      async subtreep visible-only body-only draft ext-plist
       (lambda (file) (org-texinfo-compile file)))))
 
 ;;;###autoload
diff --git a/lisp/ox.el b/lisp/ox.el
index 0d6711c..7ef36ea 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -857,6 +857,16 @@ automatically.  But you can retrieve them with \\[org-export-stack]."
   :package-version '(Org . "8.0")
   :type 'boolean)
 
+(defcustom org-export-as-draft nil
+  "Non-nil means export is allowed to produce a draft.
+When in draft mode, invalid links and macros do not throw an
+error and stop export process.  Instead, they are marked as
+invalid in the produced output."
+  :group 'org-export-general
+  :version "25.1"
+  :package-version '(Org . "8.4")
+  :type 'boolean)
+
 (defcustom org-export-async-init-file nil
   "File used to initialize external export process.
 
@@ -882,9 +892,9 @@ for current export scope is added to the prompt (\"b\" when
 output is restricted to body only, \"s\" when it is restricted to
 the current subtree, \"v\" when only visible elements are
 considered for export, \"f\" when publishing functions should be
-passed the FORCE argument and \"a\" when the export should be
-asynchronous).  Also, [?] allows to switch back to standard
-mode."
+passed the FORCE argument, \"a\" when the export should be
+asynchronous and \"d\" when document is to be exported as
+a draft).  Also, [?] allows to switch back to standard mode."
   :group 'org-export-general
   :version "24.4"
   :package-version '(Org . "8.0")
@@ -1853,68 +1863,76 @@ Return a string."
   (or (gethash data (plist-get info :exported-data))
       (let* ((type (org-element-type data))
 	     (results
-	      (cond
-	       ;; Ignored element/object.
-	       ((memq data (plist-get info :ignore-list)) nil)
-	       ;; Plain text.
-	       ((eq type 'plain-text)
-		(org-export-filter-apply-functions
-		 (plist-get info :filter-plain-text)
-		 (let ((transcoder (org-export-transcoder data info)))
-		   (if transcoder (funcall transcoder data info) data))
-		 info))
-	       ;; Secondary string.
-	       ((not type)
-		(mapconcat (lambda (obj) (org-export-data obj info)) data ""))
-	       ;; Element/Object without contents or, as a special
-	       ;; case, headline with archive tag and archived trees
-	       ;; restricted to title only.
-	       ((or (not (org-element-contents data))
-		    (and (eq type 'headline)
-			 (eq (plist-get info :with-archived-trees) 'headline)
-			 (org-element-property :archivedp data)))
-		(let ((transcoder (org-export-transcoder data info)))
-		  (or (and (functionp transcoder)
-			   (funcall transcoder data nil info))
-		      ;; Export snippets never return a nil value so
-		      ;; that white spaces following them are never
-		      ;; ignored.
-		      (and (eq type 'export-snippet) ""))))
-	       ;; Element/Object with contents.
-	       (t
-		(let ((transcoder (org-export-transcoder data info)))
-		  (when transcoder
-		    (let* ((greaterp (memq type org-element-greater-elements))
-			   (objectp
-			    (and (not greaterp)
-				 (memq type org-element-recursive-objects)))
-			   (contents
-			    (mapconcat
-			     (lambda (element) (org-export-data element info))
-			     (org-element-contents
-			      (if (or greaterp objectp) data
-				;; Elements directly containing
-				;; objects must have their indentation
-				;; normalized first.
-				(org-element-normalize-contents
-				 data
-				 ;; When normalizing contents of the
-				 ;; first paragraph in an item or
-				 ;; a footnote definition, ignore
-				 ;; first line's indentation: there is
-				 ;; none and it might be misleading.
-				 (when (eq type 'paragraph)
-				   (let ((parent (org-export-get-parent data)))
-				     (and
-				      (eq (car (org-element-contents parent))
-					  data)
-				      (memq (org-element-type parent)
-					    '(footnote-definition item))))))))
-			     "")))
-		      (funcall transcoder data
-			       (if (not greaterp) contents
-				 (org-element-normalize-string contents))
-			       info))))))))
+	      (condition-case err
+		  (cond
+		   ;; Ignored element/object.
+		   ((memq data (plist-get info :ignore-list)) nil)
+		   ;; Plain text.
+		   ((eq type 'plain-text)
+		    (org-export-filter-apply-functions
+		     (plist-get info :filter-plain-text)
+		     (let ((transcoder (org-export-transcoder data info)))
+		       (if transcoder (funcall transcoder data info) data))
+		     info))
+		   ;; Secondary string.
+		   ((not type)
+		    (mapconcat (lambda (o) (org-export-data o info)) data ""))
+		   ;; Element/Object without contents or, as a special
+		   ;; case, headline with archive tag and archived
+		   ;; trees restricted to title only.
+		   ((or (not (org-element-contents data))
+			(and (eq type 'headline)
+			     (eq (plist-get info :with-archived-trees)
+				 'headline)
+			     (org-element-property :archivedp data)))
+		    (let ((transcoder (org-export-transcoder data info)))
+		      (or (and (functionp transcoder)
+			       (funcall transcoder data nil info))
+			  ;; Export snippets never return a nil value
+			  ;; so that white spaces following them are
+			  ;; never ignored.
+			  (and (eq type 'export-snippet) ""))))
+		   ;; Element/Object with contents.
+		   (t
+		    (let ((transcoder (org-export-transcoder data info)))
+		      (when transcoder
+			(let* ((greaterp
+				(memq type org-element-greater-elements))
+			       (objectp
+				(and (not greaterp)
+				     (memq type org-element-recursive-objects)))
+			       (contents
+				(mapconcat
+				 (lambda (e) (org-export-data e info))
+				 (org-element-contents
+				  (if (or greaterp objectp) data
+				    ;; Elements directly containing
+				    ;; objects must have their
+				    ;; indentation normalized first.
+				    (org-element-normalize-contents
+				     data
+				     ;; When normalizing contents of
+				     ;; the first paragraph in an item
+				     ;; or a footnote definition,
+				     ;; ignore first line's
+				     ;; indentation: there is none and
+				     ;; it might be misleading.
+				     (when (eq type 'paragraph)
+				       (let ((p (org-export-get-parent data)))
+					 (and (eq (car (org-element-contents p))
+						  data)
+					      (memq (org-element-type p)
+						    '(footnote-definition
+						      item))))))))
+				 "")))
+			  (funcall transcoder data
+				   (if (not greaterp) contents
+				     (org-element-normalize-string contents))
+				   info))))))
+		(user-error (if (memq 'draft (plist-get info :export-options))
+				(format "[INVALID %s]"
+					(upcase (symbol-name type)))
+			      (user-error (nth 1 err)))))))
 	;; Final result will be memoized before being returned.
 	(puthash
 	 data
@@ -2866,7 +2884,7 @@ not, are considered."
 
 ;;;###autoload
 (defun org-export-as
-    (backend &optional subtreep visible-only body-only ext-plist)
+    (backend &optional subtreep visible-only body-only draft ext-plist)
   "Transcode current Org buffer into BACKEND code.
 
 BACKEND is either an export back-end, as returned by, e.g.,
@@ -2888,6 +2906,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only return body
 code, without surrounding template.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 Optional argument EXT-PLIST, when provided, is a property list
 with external parameters overriding Org default settings, but
 still inferior to file-local settings.
@@ -2915,7 +2936,8 @@ Return code as a string."
 			  (delq nil
 				(list (and subtreep 'subtree)
 				      (and visible-only 'visible-only)
-				      (and body-only 'body-only))))
+				      (and body-only 'body-only)
+				      (and draft 'draft))))
 		    (org-export--get-buffer-attributes)))
 	     (parsed-keywords
 	      (delq nil
@@ -2986,7 +3008,7 @@ Return code as a string."
 	   (cons "email" (org-element-interpret-data (plist-get info :email)))
 	   (cons "title" (org-element-interpret-data (plist-get info :title)))
 	   (cons "results" "$1"))
-	  'finalize
+	  (and (not draft) 'finalize)
 	  parsed-keywords)
 	 ;; Parse buffer.
 	 (setq tree (org-element-parse-buffer nil visible-only))
@@ -3030,7 +3052,7 @@ Return code as a string."
 	     info))))))))
 
 ;;;###autoload
-(defun org-export-string-as (string backend &optional body-only ext-plist)
+(defun org-export-string-as (string backend &optional body-only draft ext-plist)
   "Transcode STRING into BACKEND code.
 
 BACKEND is either an export back-end, as returned by, e.g.,
@@ -3040,6 +3062,9 @@ a registered back-end.
 When optional argument BODY-ONLY is non-nil, only return body
 code, without preamble nor postamble.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 Optional argument EXT-PLIST, when provided, is a property list
 with external parameters overriding Org default settings, but
 still inferior to file-local settings.
@@ -3048,7 +3073,7 @@ Return code as a string."
   (with-temp-buffer
     (insert string)
     (let ((org-inhibit-startup t)) (org-mode))
-    (org-export-as backend nil nil body-only ext-plist)))
+    (org-export-as backend nil nil body-only draft ext-plist)))
 
 ;;;###autoload
 (defun org-export-replace-region-by (backend)
@@ -5754,9 +5779,9 @@ and `org-export-to-file' for more specialized functions."
 
 ;;;###autoload
 (defun org-export-to-buffer
-  (backend buffer
-	   &optional async subtreep visible-only body-only ext-plist
-	   post-process)
+    (backend buffer
+	     &optional async subtreep visible-only body-only draft
+	     ext-plist post-process)
   "Call `org-export-as' with output to a specified buffer.
 
 BACKEND is either an export back-end, as returned by, e.g.,
@@ -5772,7 +5797,7 @@ through the `org-export-stack' interface.  When ASYNC is nil, the
 buffer is displayed if `org-export-show-temporary-export-buffer'
 is non-nil.
 
-Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY and
+Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY, DRAFT and
 EXT-PLIST are similar to those used in `org-export-as', which
 see.
 
@@ -5782,10 +5807,11 @@ from BUFFER, with point at its beginning.  Export back-ends can
 use it to set a major mode there, e.g,
 
   (defun org-latex-export-as-latex
-    (&optional async subtreep visible-only body-only ext-plist)
+    (&optional async subtreep visible-only body-only draft ext-plist)
     (interactive)
     (org-export-to-buffer \\='latex \"*Org LATEX Export*\"
-      async subtreep visible-only body-only ext-plist (lambda () (LaTeX-mode))))
+      async subtreep visible-only body-only draft ext-plist
+      (lambda () (LaTeX-mode))))
 
 This function returns BUFFER."
   (declare (indent 2))
@@ -5800,9 +5826,10 @@ This function returns BUFFER."
 	       (org-export-add-to-stack (current-buffer) ',backend)
 	       (ignore-errors (funcall ,post-process))))
 	`(org-export-as
-	  ',backend ,subtreep ,visible-only ,body-only ',ext-plist))
+	  ',backend ,subtreep ,visible-only ,body-only ,draft ',ext-plist))
     (let ((output
-	   (org-export-as backend subtreep visible-only body-only ext-plist))
+	   (org-export-as
+	    backend subtreep visible-only body-only draft ext-plist))
 	  (buffer (get-buffer-create buffer))
 	  (encoding buffer-file-coding-system))
       (when (and (org-string-nw-p output) (org-export--copy-to-kill-ring-p))
@@ -5819,8 +5846,8 @@ This function returns BUFFER."
 
 ;;;###autoload
 (defun org-export-to-file
-  (backend file &optional async subtreep visible-only body-only ext-plist
-	   post-process)
+    (backend file &optional async subtreep visible-only body-only draft
+	     ext-plist post-process)
   "Call `org-export-as' with output to a specified file.
 
 BACKEND is either an export back-end, as returned by, e.g.,
@@ -5832,7 +5859,7 @@ A non-nil optional argument ASYNC means the process should happen
 asynchronously.  The resulting buffer will then be accessible
 through the `org-export-stack' interface.
 
-Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY and
+Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY, DRAFT and
 EXT-PLIST are similar to those used in `org-export-as', which
 see.
 
@@ -5842,11 +5869,11 @@ has to return a file name, or nil.  Export back-ends can use this
 to send the output file through additional processing, e.g,
 
   (defun org-latex-export-to-latex
-    (&optional async subtreep visible-only body-only ext-plist)
+    (&optional async subtreep visible-only body-only draft ext-plist)
     (interactive)
     (let ((outfile (org-export-output-file-name \".tex\" subtreep)))
       (org-export-to-file \\='latex outfile
-        async subtreep visible-only body-only ext-plist
+        async subtreep visible-only body-only draft ext-plist
         (lambda (file) (org-latex-compile file)))
 
 The function returns either a file name returned by POST-PROCESS,
@@ -5861,15 +5888,16 @@ or FILE."
 		 (org-export-add-to-stack (expand-file-name file) ',backend))
 	    `(let ((output
 		    (org-export-as
-		     ',backend ,subtreep ,visible-only ,body-only
+		     ',backend ,subtreep ,visible-only ,body-only ,draft
 		     ',ext-plist)))
 	       (with-temp-buffer
 		 (insert output)
 		 (let ((coding-system-for-write ',encoding))
 		   (write-file ,file)))
 	       (or (ignore-errors (funcall ',post-process ,file)) ,file)))
-        (let ((output (org-export-as
-                       backend subtreep visible-only body-only ext-plist)))
+        (let ((output
+	       (org-export-as
+		backend subtreep visible-only body-only draft ext-plist)))
           (with-temp-buffer
             (insert output)
             (let ((coding-system-for-write encoding))
@@ -6117,7 +6145,8 @@ When ARG is \\[universal-argument] \\[universal-argument], display the asynchron
 			   (setq org-export-dispatch-last-action
 				 (org-export--dispatch-ui
 				  (list org-export-initial-scope
-					(and org-export-in-background 'async))
+					(and org-export-in-background 'async)
+					(and org-export-as-draft 'draft))
 				  nil
 				  org-export-dispatch-use-expert-ui)))
 		       (and (get-buffer "*Org Export Dispatcher*")
@@ -6158,7 +6187,8 @@ When ARG is \\[universal-argument] \\[universal-argument], display the asynchron
 		  (and (memq 'async optns) t)
 		  (and (memq 'subtree optns) t)
 		  (and (memq 'visible optns) t)
-		  (and (memq 'body optns) t)))))))
+		  (and (memq 'body optns) t)
+		  (and (memq 'draft optns) t)))))))
 
 (defun org-export--dispatch-ui (options first-key expertp)
   "Handle interface for `org-export-dispatch'.
@@ -6170,6 +6200,7 @@ export.  It can contain any of the following symbols:
 `visible' restricts export to visible part of buffer.
 `force'   force publishing files.
 `async'   use asynchronous export process
+`draft'   export using a more permissive mode
 
 FIRST-KEY is the key pressed to select the first level menu.  It
 is nil when this menu hasn't been selected yet.
@@ -6208,13 +6239,13 @@ back to standard interface."
 		'car-less-than-car))
 	 ;; Compute a list of allowed keys based on the first key
 	 ;; pressed, if any.  Some keys
-	 ;; (?^B, ?^V, ?^S, ?^F, ?^A, ?&, ?# and ?q) are always
+	 ;; (?^B, ?^V, ?^S, ?^F, ?^A, ?^D, ?&, ?# and ?q) are always
 	 ;; available.
 	 (allowed-keys
-	  (nconc (list 2 22 19 6 1)
-		 (if (not first-key) (org-uniquify (mapcar 'car entries))
+	  (nconc (list 2 22 19 6 1 4)
+		 (if (not first-key) (org-uniquify (mapcar #'car entries))
 		   (let (sub-menu)
-		     (dolist (entry entries (sort (mapcar 'car sub-menu) '<))
+		     (dolist (entry entries (sort (mapcar #'car sub-menu) #'<))
 		       (when (eq (car entry) first-key)
 			 (setq sub-menu (append (nth 2 entry) sub-menu))))))
 		 (cond ((eq first-key ?P) (list ?f ?p ?x ?a))
@@ -6229,7 +6260,7 @@ back to standard interface."
 	     ;; Options are hard-coded.
 	     (format "[%s] Body only:    %s           [%s] Visible only:     %s
 \[%s] Export scope: %s       [%s] Force publishing: %s
-\[%s] Async export: %s\n\n"
+\[%s] Async export: %s           [%s] Draft mode :      %s\n\n"
 		     (funcall fontify-key "C-b" t)
 		     (funcall fontify-value
 			      (if (memq 'body options) "On " "Off"))
@@ -6244,7 +6275,10 @@ back to standard interface."
 			      (if (memq 'force options) "On " "Off"))
 		     (funcall fontify-key "C-a" t)
 		     (funcall fontify-value
-			      (if (memq 'async options) "On " "Off")))
+			      (if (memq 'async options) "On " "Off"))
+		     (funcall fontify-key "C-d" t)
+		     (funcall fontify-value
+			      (if (memq 'draft options) "On " "Off")))
 	     ;; Display registered back-end entries.  When a key
 	     ;; appears for the second time, do not create another
 	     ;; entry, but append its sub-menu to existing menu.
@@ -6296,12 +6330,13 @@ back to standard interface."
 	 (expert-prompt
 	  (when expertp
 	    (format
-	     "Export command (C-%s%s%s%s%s) [%s]: "
+	     "Export command (C-%s%s%s%s%s%s) [%s]: "
 	     (if (memq 'body options) (funcall fontify-key "b" t) "b")
 	     (if (memq 'visible options) (funcall fontify-key "v" t) "v")
 	     (if (memq 'subtree options) (funcall fontify-key "s" t) "s")
 	     (if (memq 'force options) (funcall fontify-key "f" t) "f")
 	     (if (memq 'async options) (funcall fontify-key "a" t) "a")
+	     (if (memq 'draft options) (funcall fontify-key "d" t) "d")
 	     (mapconcat (lambda (k)
 			  ;; Strip control characters.
 			  (unless (< k 27) (char-to-string k)))
@@ -6346,8 +6381,8 @@ EXPERTP are the same as defined in `org-export--dispatch-ui',
 which see.
 
 Toggle export options when required.  Otherwise, return value is
-a list with action as CAR and a list of interactive export
-options as CDR."
+a list with action as car and a list of interactive export
+options as cdr."
   (let (key)
     ;; Scrolling: when in non-expert mode, act on motion keys (C-n,
     ;; C-p, SPC, DEL).
@@ -6388,11 +6423,11 @@ options as CDR."
      ((eq key ?#) (cons 'template (memq 'subtree options)))
      ;; Switch to asynchronous export stack.
      ((eq key ?&) '(stack))
-     ;; Toggle options: C-b (2) C-v (22) C-s (19) C-f (6) C-a (1).
-     ((memq key '(2 22 19 6 1))
+     ;; Options: C-b (2) C-v (22) C-s (19) C-f (6) C-a (1) C-d (4).
+     ((memq key '(2 22 19 6 1 4))
       (org-export--dispatch-ui
        (let ((option (cl-case key (2 'body) (22 'visible) (19 'subtree)
-			      (6 'force) (1 'async))))
+			      (6 'force) (1 'async) (4 'draft))))
 	 (if (memq option options) (remq option options)
 	   (cons option options)))
        first-key expertp))
@@ -6413,10 +6448,9 @@ options as CDR."
 	     ;; path. Indeed, derived backends can share the same
 	     ;; FIRST-KEY.
 	     (t (catch 'found
-		  (mapc (lambda (entry)
-			  (let ((match (assq key (nth 2 entry))))
-			    (when match (throw 'found (nth 2 match)))))
-			(member (assq first-key entries) entries)))))
+		  (dolist (entry (member (assq first-key entries) entries))
+		    (let ((match (assq key (nth 2 entry))))
+		      (when match (throw 'found (nth 2 match))))))))
 	    options))
      ;; Otherwise, enter sub-menu.
      (t (org-export--dispatch-ui options key expertp)))))
diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
index c5e2218..6cccace 100644
--- a/testing/lisp/test-ox.el
+++ b/testing/lisp/test-ox.el
@@ -401,14 +401,14 @@ Paragraph"
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text "* Head1 :noexp:"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:exclude-tags ("noexp")))))))
+			     nil nil nil nil '(:exclude-tags ("noexp")))))))
   ;; Test include tags for headlines and inlinetasks.
   (should
    (equal "* H2\n** Sub :exp:\n*** Sub Sub\n"
 	  (org-test-with-temp-text "* H1\n* H2\n** Sub :exp:\n*** Sub Sub\n* H3"
 	    (let ((org-tags-column 0))
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:select-tags ("exp")))))))
+			     nil nil nil nil '(:select-tags ("exp")))))))
   ;; If there is an include tag, ignore the section before the first
   ;; headline, if any.
   (should
@@ -416,13 +416,13 @@ Paragraph"
 	  (org-test-with-temp-text "First section\n* H1 :exp:\nBody"
 	    (let ((org-tags-column 0))
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:select-tags ("exp")))))))
+			     nil nil nil nil '(:select-tags ("exp")))))))
   (should-not
    (equal "* H1 :exp:\n"
 	  (org-test-with-temp-text "* H1 :exp:\nBody"
 	    (let ((org-tags-column 0))
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:select-tags ("exp")))))))
+			     nil nil nil nil '(:select-tags ("exp")))))))
   ;; Test mixing include tags and exclude tags.
   (should
    (string-match
@@ -433,7 +433,7 @@ Paragraph"
 ** Sub-Head2
 * Head2 :noexport:
 ** Sub-Head1 :export:"
-      (org-export-as (org-test-default-backend) nil nil nil
+      (org-export-as (org-test-default-backend) nil nil nil nil
 		     '(:select-tags ("export") :exclude-tags ("noexport"))))))
   ;; Ignore tasks.
   (should
@@ -443,13 +443,13 @@ Paragraph"
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text "* TODO Head1"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:with-tasks nil))))))
+			     nil nil nil nil '(:with-tasks nil))))))
   (should
    (equal "* TODO Head1\n"
 	  (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
 	    (org-test-with-temp-text "* TODO Head1"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:with-tasks t))))))
+			     nil nil nil nil '(:with-tasks t))))))
   ;; Archived tree.
   (should
    (equal ""
@@ -458,13 +458,13 @@ Paragraph"
 	    (org-test-with-temp-text "* Head1 :archive:"
 	      (let ((org-archive-tag "archive"))
 		(org-export-as (org-test-default-backend)
-			       nil nil nil '(:with-archived-trees nil)))))))
+			       nil nil nil nil '(:with-archived-trees nil)))))))
   (should
    (string-match
     "\\* Head1[ \t]+:archive:"
     (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2"
       (let ((org-archive-tag "archive"))
-	(org-export-as (org-test-default-backend) nil nil nil
+	(org-export-as (org-test-default-backend) nil nil nil nil
 		       '(:with-archived-trees headline))))))
   (should
    (string-match
@@ -472,20 +472,20 @@ Paragraph"
     (org-test-with-temp-text "* Head1 :archive:"
       (let ((org-archive-tag "archive"))
 	(org-export-as (org-test-default-backend)
-		       nil nil nil '(:with-archived-trees t))))))
+		       nil nil nil nil '(:with-archived-trees t))))))
   ;; Clocks.
   (should
    (string-match "CLOCK: \\[2012-04-29 .* 10:45\\]"
 		 (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
 		   (org-export-as (org-test-default-backend)
-				  nil nil nil '(:with-clocks t)))))
+				  nil nil nil nil '(:with-clocks t)))))
   (should
    (equal ""
 	  (let (org-export-filter-body-functions
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:with-clocks nil))))))
+			     nil nil nil nil '(:with-clocks nil))))))
   ;; Drawers.
   (should
    (equal ""
@@ -493,48 +493,50 @@ Paragraph"
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text ":TEST:\ncontents\n:END:"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:with-drawers nil))))))
+			     nil nil nil nil '(:with-drawers nil))))))
   (should
    (equal ":TEST:\ncontents\n:END:\n"
 	  (org-test-with-temp-text ":TEST:\ncontents\n:END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-drawers t)))))
+			   nil nil nil nil '(:with-drawers t)))))
   (should
    (equal ":FOO:\nkeep\n:END:\n"
 	  (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-drawers ("FOO"))))))
+			   nil nil nil nil '(:with-drawers ("FOO"))))))
   (should
    (equal ":FOO:\nkeep\n:END:\n"
 	  (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-drawers (not "BAR"))))))
+			   nil nil nil nil '(:with-drawers (not "BAR"))))))
   ;; Fixed-width.
   (should
    (equal ": A\n"
 	  (org-test-with-temp-text ": A"
-	    (org-export-as (org-test-default-backend) nil nil nil
+	    (org-export-as (org-test-default-backend) nil nil nil nil
 			   '(:with-fixed-width t)))))
   (should
    (equal ""
 	  (let (org-export-filter-body-functions
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text ": A"
-	      (org-export-as (org-test-default-backend) nil nil nil
+	      (org-export-as (org-test-default-backend) nil nil nil nil
 			     '(:with-fixed-width nil))))))
   ;; Footnotes.
   (should
    (equal "Footnote?"
 	  (let ((org-footnote-section nil))
 	    (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def"
-	      (org-trim (org-export-as (org-test-default-backend)
-				       nil nil nil '(:with-footnotes nil)))))))
+	      (org-trim
+	       (org-export-as (org-test-default-backend)
+			      nil nil nil nil '(:with-footnotes nil)))))))
   (should
    (equal "Footnote?[fn:1]\n\n[fn:1] Def"
 	  (let ((org-footnote-section nil))
 	    (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def"
-	      (org-trim (org-export-as (org-test-default-backend)
-				       nil nil nil '(:with-footnotes t)))))))
+	      (org-trim
+	       (org-export-as (org-test-default-backend)
+			      nil nil nil nil '(:with-footnotes t)))))))
   ;; Inlinetasks.
   (when (featurep 'org-inlinetask)
     (should
@@ -545,7 +547,7 @@ Paragraph"
 	    org-export-filter-final-output-functions)
 	(org-test-with-temp-text "*************** Task"
 	  (org-export-as (org-test-default-backend)
-			 nil nil nil '(:with-inlinetasks nil))))))
+			 nil nil nil nil '(:with-inlinetasks nil))))))
     (should
      (equal
       ""
@@ -555,7 +557,7 @@ Paragraph"
 	(org-test-with-temp-text
 	    "*************** Task\nContents\n*************** END"
 	  (org-export-as (org-test-default-backend)
-			 nil nil nil '(:with-inlinetasks nil)))))))
+			 nil nil nil nil '(:with-inlinetasks nil)))))))
   ;; Plannings.
   (should
    (string-match
@@ -563,32 +565,32 @@ Paragraph"
     (let ((org-closed-string "CLOSED:"))
       (org-test-with-temp-text "* H\nCLOSED: [2012-04-29 sun. 10:45]"
 	(org-export-as (org-test-default-backend)
-		       nil nil nil '(:with-planning t))))))
+		       nil nil nil nil '(:with-planning t))))))
   (should
    (equal "* H\n"
 	  (let ((org-closed-string "CLOSED:"))
 	    (org-test-with-temp-text "* H\nCLOSED: [2012-04-29 sun. 10:45]"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:with-planning nil))))))
+			     nil nil nil nil '(:with-planning nil))))))
   ;; Property Drawers.
   (should
    (equal "* H1\n"
 	  (org-test-with-temp-text
 	      "* H1\n  :PROPERTIES:\n  :PROP: value\n  :END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-properties nil)))))
+			   nil nil nil nil '(:with-properties nil)))))
   (should
    (equal "* H1\n:PROPERTIES:\n:PROP:     value\n:END:\n"
 	  (org-test-with-temp-text
 	      "* H1\n  :PROPERTIES:\n  :PROP: value\n  :END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-properties t)))))
+			   nil nil nil nil '(:with-properties t)))))
   (should
    (equal "* H1\n:PROPERTIES:\n:B:        2\n:END:\n"
 	  (org-test-with-temp-text
 	      "* H1\n  :PROPERTIES:\n  :A: 1\n  :B: 2\n:END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-properties ("B"))))))
+			   nil nil nil nil '(:with-properties ("B"))))))
   ;; Statistics cookies.
   (should
    (equal "* Stats"
@@ -596,20 +598,21 @@ Paragraph"
 		org-export-filter-final-output-functions)
 	    (org-trim
 	     (org-test-with-temp-text "* Stats [0/0]"
-	       (org-export-as (org-test-default-backend)
-			      nil nil nil '(:with-statistics-cookies nil)))))))
+	       (org-export-as
+		(org-test-default-backend)
+		nil nil nil nil '(:with-statistics-cookies nil)))))))
   ;; Tables.
   (should
    (equal "| A |\n"
 	  (org-test-with-temp-text "| A |"
-	    (org-export-as (org-test-default-backend) nil nil nil
+	    (org-export-as (org-test-default-backend) nil nil nil nil
 			   '(:with-tables t)))))
   (should
    (equal ""
 	  (let (org-export-filter-body-functions
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text "| A |"
-	      (org-export-as (org-test-default-backend) nil nil nil
+	      (org-export-as (org-test-default-backend) nil nil nil nil
 			     '(:with-tables nil)))))))
 
 (ert-deftest test-org-export/with-timestamps ()
@@ -620,7 +623,7 @@ Paragraph"
     "\\[2012-04-29 .*? 10:45\\]<2012-04-29 .*? 10:45>"
     (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>"
       (org-export-as (org-test-default-backend)
-		     nil nil nil '(:with-timestamps t)))))
+		     nil nil nil nil '(:with-timestamps t)))))
   ;; nil value.
   (should
    (equal
@@ -630,7 +633,7 @@ Paragraph"
       (org-trim
        (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>"
 	 (org-export-as (org-test-default-backend)
-			nil nil nil '(:with-timestamps nil)))))))
+			nil nil nil nil '(:with-timestamps nil)))))))
   ;; `active' value.
   (should
    (string-match
@@ -640,7 +643,7 @@ Paragraph"
 
 Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
       (org-export-as (org-test-default-backend)
-		     nil nil nil '(:with-timestamps active)))))
+		     nil nil nil nil '(:with-timestamps active)))))
   ;; `inactive' value.
   (should
    (string-match
@@ -650,7 +653,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 
 Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
       (org-export-as (org-test-default-backend)
-		     nil nil nil '(:with-timestamps inactive))))))
+		     nil nil nil nil '(:with-timestamps inactive))))))
 
 (ert-deftest test-org-export/comment-tree ()
   "Test if export process ignores commented trees."
@@ -672,7 +675,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((entity . (lambda (e c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-entities t)))))
+	     nil nil nil nil '(:with-entities t)))))
   (should
    (equal "\\alpha\n"
 	  (org-test-with-temp-text "\\alpha"
@@ -681,7 +684,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((entity . (lambda (e c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-entities nil)))))
+	     nil nil nil nil '(:with-entities nil)))))
   ;; Emphasis.
   (should
    (equal "dummy\n"
@@ -691,7 +694,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((bold . (lambda (b c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-emphasize t)))))
+	     nil nil nil nil '(:with-emphasize t)))))
   (should
    (equal "*bold*\n"
 	  (org-test-with-temp-text "*bold*"
@@ -700,7 +703,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((bold . (lambda (b c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-emphasize nil)))))
+	     nil nil nil nil '(:with-emphasize nil)))))
   ;; LaTeX environment.
   (should
    (equal "dummy\n"
@@ -709,7 +712,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	     (org-export-create-backend
 	      :transcoders '((latex-environment . (lambda (l c i) "dummy"))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-latex t)))))
+	     nil nil nil nil '(:with-latex t)))))
   (should
    (equal "\\begin{equation}\n1+1=2\n\\end{equation}\n"
 	  (org-test-with-temp-text "\\begin{equation}\n1+1=2\n\\end{equation}"
@@ -717,7 +720,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	     (org-export-create-backend
 	      :transcoders '((latex-environment . (lambda (l c i) "dummy"))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-latex verbatim)))))
+	     nil nil nil nil '(:with-latex verbatim)))))
   ;; LaTeX fragment.
   (should
    (equal "dummy\n"
@@ -727,7 +730,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((latex-fragment . (lambda (l c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-latex t)))))
+	     nil nil nil nil '(:with-latex t)))))
   (should
    (equal "$1$\n"
 	  (org-test-with-temp-text "$1$"
@@ -736,7 +739,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((latex-fragment . (lambda (l c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-latex verbatim)))))
+	     nil nil nil nil '(:with-latex verbatim)))))
   ;; Sub/superscript.
   (should
    (equal "adummy\n"
@@ -746,7 +749,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((subscript . (lambda (s c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript t)))))
+	     nil nil nil nil '(:with-sub-superscript t)))))
   (should
    (equal "a_b\n"
 	  (org-test-with-temp-text "a_b"
@@ -755,7 +758,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((subscript . (lambda (s c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript nil)))))
+	     nil nil nil nil '(:with-sub-superscript nil)))))
   (should
    (equal "a_b\n"
 	  (org-test-with-temp-text "a_b"
@@ -764,7 +767,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((subscript . (lambda (s c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript {})))))
+	     nil nil nil nil '(:with-sub-superscript {})))))
   (should
    (equal "adummy\n"
 	  (org-test-with-temp-text "a_{b}"
@@ -773,7 +776,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((subscript . (lambda (s c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript {})))))
+	     nil nil nil nil '(:with-sub-superscript {})))))
   ;; Also handle uninterpreted objects in title.
   (should
    (equal "a_b"
@@ -785,7 +788,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 		(template . (lambda (c i) (org-export-data
 				      (plist-get i :title) i)))
 		(section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript nil)))))
+	     nil nil nil nil '(:with-sub-superscript nil)))))
   ;; Handle uninterpreted objects in captions.
   (should
    (equal "adummy\n"
@@ -797,7 +800,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 		(paragraph . (lambda (p c i)
 			       (org-export-data (org-export-get-caption p) i)))
 		(section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript t)))))
+	     nil nil nil nil '(:with-sub-superscript t)))))
   (should
    (equal "a_b\n"
 	  (org-test-with-temp-text "#+CAPTION: a_b\nParagraph"
@@ -808,7 +811,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 		(paragraph . (lambda (p c i)
 			       (org-export-data (org-export-get-caption p) i)))
 		(section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript nil)))))
+	     nil nil nil nil '(:with-sub-superscript nil)))))
   ;; Special case: multiples uninterpreted objects in a row.
   (should
    (equal "a_b_c_d\n"
@@ -818,7 +821,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((subscript . (lambda (s c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript {}))))))
+	     nil nil nil nil '(:with-sub-superscript {}))))))
 
 (ert-deftest test-org-export/export-scope ()
   "Test all export scopes."
@@ -1175,7 +1178,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 		    :options '((:k "K" nil nil parse)))))
       (org-test-with-temp-text
 	  "#+MACRO: m v\n* H\n:PROPERTIES:\n:EXPORT_K: {{{m}}}\n:END:"
-	(org-export-as backend nil nil nil '(:with-properties t))))))
+	(org-export-as backend nil nil nil nil '(:with-properties t))))))
   ;; Expand specific macros.
   (should
    (equal "me 2012-03-29 me@here Title\n"
@@ -1671,7 +1674,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 		     ""))
 	    (section . (lambda (s c i) c))
 	    (paragraph . (lambda (p c i) c))))
-	 nil nil nil '(:with-footnotes t))
+	 nil nil nil nil '(:with-footnotes t))
 	(nreverse result)))))
   ;; Limit check to DATA, when non-nil.
   (should
@@ -1719,7 +1722,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	    (footnote-definition . (lambda (d c i) c))
 	    (section . (lambda (s c i) c))
 	    (paragraph . (lambda (p c i) c))))
-	 nil nil nil '(:with-footnotes t))
+	 nil nil nil nil '(:with-footnotes t))
 	(nreverse result)))))
   (should
    (equal
@@ -1740,7 +1743,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	    (footnote-definition . (lambda (d c i) c))
 	    (section . (lambda (s c i) c))
 	    (paragraph . (lambda (p c i) c))))
-	 nil nil nil '(:with-footnotes t))
+	 nil nil nil nil '(:with-footnotes t))
 	(nreverse result))))))
 
 (ert-deftest test-org-export/get-footnote-number ()
@@ -2254,7 +2257,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	    org-export-filter-final-output-functions)
 	(org-test-with-temp-text "*** Inlinetask :noexp:\nContents\n*** end"
 	  (org-export-as (org-test-default-backend)
-			 nil nil nil '(:exclude-tags ("noexp")))))))
+			 nil nil nil nil '(:exclude-tags ("noexp")))))))
     ;; Inlinetask with an include tag.
     (should
      (equal
@@ -2263,7 +2266,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	    (org-tags-column 0))
 	(org-test-with-temp-text "* H1\n* H2\n*** Inline :exp:"
 	  (org-export-as (org-test-default-backend)
-			 nil nil nil '(:select-tags ("exp")))))))
+			 nil nil nil nil '(:select-tags ("exp")))))))
     ;; Ignore inlinetask with a TODO keyword and tasks excluded.
     (should
      (equal ""
@@ -2273,7 +2276,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 		  org-export-filter-final-output-functions)
 	      (org-test-with-temp-text "*** TODO Inline"
 		(org-export-as (org-test-default-backend)
-			       nil nil nil '(:with-tasks nil))))))))
+			       nil nil nil nil '(:with-tasks nil))))))))
 
 
 \f
-- 
2.5.3


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-09-27  9:38 [RFC] Draft mode Nicolas Goaziou
@ 2015-09-27 11:37 ` Nicolas Goaziou
  2015-09-27 16:31   ` Charles C. Berry
  2015-09-27 18:39 ` Rasmus
  1 sibling, 1 reply; 19+ messages in thread
From: Nicolas Goaziou @ 2015-09-27 11:37 UTC (permalink / raw)
  To: Org Mode List

[-- Attachment #1: Type: text/plain, Size: 667 bytes --]

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> The following patch implements a draft mode for export. When in draft
> mode, invalid macros and links do not throw an error. It can be toggled
> with `org-export-as-draft' variable, or using C-d in export dispatch.
>
> It introduces a backward incompatible change since it modifies signature
> from `org-export-as' and alike.
>
> This patch is incomplete as it is missing some documentation, an entry
> in ORG-NEWS and some tests. Also, export back-end in contrib/ directory
> are not updated yet. In any case, feedback welcome.
>
> It is meant to be applied on top of master branch.

And, here's a first update.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Implement-draft-mode.patch --]
[-- Type: text/x-diff, Size: 68833 bytes --]

From bae307895b934328f10f003758446b11cbc13beb Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <mail@nicolasgoaziou.fr>
Date: Sun, 27 Sep 2015 10:59:20 +0200
Subject: [PATCH] ox: Implement draft mode

* lisp/ox.el (org-export-as-draft): New variable.
(org-export-dispatch-use-expert-ui): Document new export option.
(org-export-data): Ignore user errors when in draft mode.
(org-export-as): Add new optional argument to toggle draft mode. Ignore
unknown macros.
(org-export-string-as):
(org-export-to-buffer):
(org-export-to-file): Handle signature change.
(org-export-dispatch):
(org-export--dispatch-ui):
(org-export--dispatch-action): Provide a mean to toggle draft mode.

* lisp/ox-ascii.el (ascii):
(org-ascii-export-as-ascii):
(org-ascii-export-to-ascii):

* lisp/ox-beamer.el (beamer):
(org-beamer-export-as-latex):
(org-beamer-export-to-latex):
(org-beamer-export-to-pdf):

* lisp/ox-html.el (html):
(org-html-export-as-html):

* lisp/ox-icalendar.el (icalendar):

* lisp/ox-latex.el (latex):
(org-latex-export-as-latex):
(org-latex-export-to-latex):
(org-latex-export-to-pdf):

* lisp/ox-man.el (man):
(org-man-export-to-man):
(org-man-export-to-pdf):

* lisp/ox-md.el (md):
(org-md-export-as-markdown):
(org-md-export-to-markdown):

* lisp/ox-odt.el (odt):
(org-odt-export-to-odt):

* lisp/ox-org.el (org):
(org-org-export-to-org):

* lisp/ox-publish.el (org-publish-org-to):

* lisp/ox-texinfo.el (texinfo):
(org-texinfo-export-to-texinfo):
(org-texinfo-export-to-info): Handle signature change.

* testing/lisp/test-ox.el (test-org-export/handle-options):
(test-org-export/with-timestamps):
(test-org-export/uninterpreted):
(test-org-export/expand-macro):
(test-org-export/footnote-first-reference-p):
(test-org-export/handle-inlinetasks): Update tests.
---
 lisp/ox-ascii.el        |  39 +++---
 lisp/ox-beamer.el       |  21 +++-
 lisp/ox-html.el         |  11 +-
 lisp/ox-icalendar.el    |   4 +-
 lisp/ox-latex.el        |  26 ++--
 lisp/ox-man.el          |  16 ++-
 lisp/ox-md.el           |  25 ++--
 lisp/ox-odt.el          |  21 ++--
 lisp/ox-org.el          |  13 +-
 lisp/ox-publish.el      |   2 +-
 lisp/ox-texinfo.el      |  20 ++--
 lisp/ox.el              | 311 ++++++++++++++++++++++++++++--------------------
 testing/lisp/test-ox.el | 123 +++++++++----------
 13 files changed, 376 insertions(+), 256 deletions(-)

diff --git a/lisp/ox-ascii.el b/lisp/ox-ascii.el
index 5cc70bd..b50cebb 100644
--- a/lisp/ox-ascii.el
+++ b/lisp/ox-ascii.el
@@ -97,23 +97,23 @@
   :menu-entry
   '(?t "Export to Plain Text"
        ((?A "As ASCII buffer"
-	    (lambda (a s v b)
-	      (org-ascii-export-as-ascii a s v b '(:ascii-charset ascii))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-as-ascii a s v b d '(:ascii-charset ascii))))
 	(?a "As ASCII file"
-	    (lambda (a s v b)
-	      (org-ascii-export-to-ascii a s v b '(:ascii-charset ascii))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-to-ascii a s v b d '(:ascii-charset ascii))))
 	(?L "As Latin1 buffer"
-	    (lambda (a s v b)
-	      (org-ascii-export-as-ascii a s v b '(:ascii-charset latin1))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-as-ascii a s v b d '(:ascii-charset latin1))))
 	(?l "As Latin1 file"
-	    (lambda (a s v b)
-	      (org-ascii-export-to-ascii a s v b '(:ascii-charset latin1))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-to-ascii a s v b d '(:ascii-charset latin1))))
 	(?U "As UTF-8 buffer"
-	    (lambda (a s v b)
-	      (org-ascii-export-as-ascii a s v b '(:ascii-charset utf-8))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-as-ascii a s v b d '(:ascii-charset utf-8))))
 	(?u "As UTF-8 file"
-	    (lambda (a s v b)
-	      (org-ascii-export-to-ascii a s v b '(:ascii-charset utf-8))))))
+	    (lambda (a s v b d)
+	      (org-ascii-export-to-ascii a s v b d '(:ascii-charset utf-8))))))
   :filters-alist '((:filter-headline . org-ascii-filter-headline-blank-lines)
 		   (:filter-parse-tree org-ascii-filter-paragraph-spacing
 				       org-ascii-filter-comment-spacing)
@@ -2023,7 +2023,7 @@ a communication channel."
 
 ;;;###autoload
 (defun org-ascii-export-as-ascii
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to a text buffer.
 
 If narrowing is active in the current buffer, only export its
@@ -2045,6 +2045,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, strip title and
 table of contents from output.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -2054,11 +2057,12 @@ will be displayed when `org-export-show-temporary-export-buffer'
 is non-nil."
   (interactive)
   (org-export-to-buffer 'ascii "*Org ASCII Export*"
-    async subtreep visible-only body-only ext-plist (lambda () (text-mode))))
+    async subtreep visible-only body-only draft ext-plist
+    (lambda () (text-mode))))
 
 ;;;###autoload
 (defun org-ascii-export-to-ascii
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to a text file.
 
 If narrowing is active in the current buffer, only export its
@@ -2080,6 +2084,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, strip title and
 table of contents from output.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -2088,7 +2095,7 @@ Return output file's name."
   (interactive)
   (let ((file (org-export-output-file-name ".txt" subtreep)))
     (org-export-to-file 'ascii file
-      async subtreep visible-only body-only ext-plist)))
+      async subtreep visible-only body-only draft ext-plist)))
 
 ;;;###autoload
 (defun org-ascii-publish-to-ascii (plist filename pub-dir)
diff --git a/lisp/ox-beamer.el b/lisp/ox-beamer.el
index 3119bd4..f99182a 100644
--- a/lisp/ox-beamer.el
+++ b/lisp/ox-beamer.el
@@ -238,9 +238,9 @@ Return overlay specification, as a string, or nil."
 	(?b "As LaTeX file (Beamer)" org-beamer-export-to-latex)
 	(?P "As PDF file (Beamer)" org-beamer-export-to-pdf)
 	(?O "As PDF file and open (Beamer)"
-	    (lambda (a s v b)
-	      (if a (org-beamer-export-to-pdf t s v b)
-		(org-open-file (org-beamer-export-to-pdf nil s v b)))))))
+	    (lambda (a s v b d)
+	      (if a (org-beamer-export-to-pdf t s v b d)
+		(org-open-file (org-beamer-export-to-pdf nil s v b d)))))))
   :options-alist
   '((:headline-levels nil "H" org-beamer-frame-level)
     (:latex-class "LATEX_CLASS" nil "beamer" t)
@@ -999,7 +999,7 @@ value."
 
 ;;;###autoload
 (defun org-beamer-export-as-latex
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer as a Beamer buffer.
 
 If narrowing is active in the current buffer, only export its
@@ -1021,6 +1021,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -1034,7 +1037,7 @@ is non-nil."
 
 ;;;###autoload
 (defun org-beamer-export-to-latex
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer as a Beamer presentation (tex).
 
 If narrowing is active in the current buffer, only export its
@@ -1056,6 +1059,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -1068,7 +1074,7 @@ Return output file's name."
 
 ;;;###autoload
 (defun org-beamer-export-to-pdf
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer as a Beamer presentation (PDF).
 
 If narrowing is active in the current buffer, only export its
@@ -1090,6 +1096,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
diff --git a/lisp/ox-html.el b/lisp/ox-html.el
index 960bee8..fa5a982 100644
--- a/lisp/ox-html.el
+++ b/lisp/ox-html.el
@@ -104,9 +104,9 @@
        ((?H "As HTML buffer" org-html-export-as-html)
 	(?h "As HTML file" org-html-export-to-html)
 	(?o "As HTML file and open"
-	    (lambda (a s v b)
-	      (if a (org-html-export-to-html t s v b)
-		(org-open-file (org-html-export-to-html nil s v b)))))))
+	    (lambda (a s v b d)
+	      (if a (org-html-export-to-html t s v b d)
+		(org-open-file (org-html-export-to-html nil s v b d)))))))
   :options-alist
   '((:html-doctype "HTML_DOCTYPE" nil org-html-doctype)
     (:html-container "HTML_CONTAINER" nil org-html-container-element)
@@ -3505,7 +3505,7 @@ contextual information."
 
 ;;;###autoload
 (defun org-html-export-as-html
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to an HTML buffer.
 
 If narrowing is active in the current buffer, only export its
@@ -3527,6 +3527,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"<body>\" and \"</body>\" tags.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el
index aefddf8..d5fc97e 100644
--- a/lisp/ox-icalendar.el
+++ b/lisp/ox-icalendar.el
@@ -276,9 +276,9 @@ re-read the iCalendar file.")
   '(?c "Export to iCalendar"
        ((?f "Current file" org-icalendar-export-to-ics)
 	(?a "All agenda files"
-	    (lambda (a s v b) (org-icalendar-export-agenda-files a)))
+	    (lambda (a &rest _) (org-icalendar-export-agenda-files a)))
 	(?c "Combine all agenda files"
-	    (lambda (a s v b) (org-icalendar-combine-agenda-files a))))))
+	    (lambda (a &rest _) (org-icalendar-combine-agenda-files a))))))
 
 
 \f
diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el
index 1840a54..73968a0 100644
--- a/lisp/ox-latex.el
+++ b/lisp/ox-latex.el
@@ -97,9 +97,9 @@
 	(?l "As LaTeX file" org-latex-export-to-latex)
 	(?p "As PDF file" org-latex-export-to-pdf)
 	(?o "As PDF file and open"
-	    (lambda (a s v b)
-	      (if a (org-latex-export-to-pdf t s v b)
-		(org-open-file (org-latex-export-to-pdf nil s v b)))))))
+	    (lambda (a s v b d)
+	      (if a (org-latex-export-to-pdf t s v b d)
+		(org-open-file (org-latex-export-to-pdf nil s v b d)))))))
   :filters-alist '((:filter-options . org-latex-math-block-options-filter)
 		   (:filter-parse-tree org-latex-math-block-tree-filter
 				       org-latex-matrices-tree-filter))
@@ -3310,7 +3310,7 @@ contextual information."
 
 ;;;###autoload
 (defun org-latex-export-as-latex
-  (&optional async subtreep visible-only body-only ext-plist)
+    (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer as a LaTeX buffer.
 
 If narrowing is active in the current buffer, only export its
@@ -3332,6 +3332,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -3341,7 +3344,8 @@ will be displayed when `org-export-show-temporary-export-buffer'
 is non-nil."
   (interactive)
   (org-export-to-buffer 'latex "*Org LATEX Export*"
-    async subtreep visible-only body-only ext-plist (lambda () (LaTeX-mode))))
+    async subtreep visible-only body-only draft ext-plist
+    (lambda () (LaTeX-mode))))
 
 ;;;###autoload
 (defun org-latex-convert-region-to-latex ()
@@ -3354,7 +3358,7 @@ command to convert it."
 
 ;;;###autoload
 (defun org-latex-export-to-latex
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to a LaTeX file.
 
 If narrowing is active in the current buffer, only export its
@@ -3376,17 +3380,20 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings."
   (interactive)
   (let ((outfile (org-export-output-file-name ".tex" subtreep)))
     (org-export-to-file 'latex outfile
-      async subtreep visible-only body-only ext-plist)))
+      async subtreep visible-only body-only draft ext-plist)))
 
 ;;;###autoload
 (defun org-latex-export-to-pdf
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to LaTeX then process through to PDF.
 
 If narrowing is active in the current buffer, only export its
@@ -3408,6 +3415,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
diff --git a/lisp/ox-man.el b/lisp/ox-man.el
index a5d0d49..edb4e15 100644
--- a/lisp/ox-man.el
+++ b/lisp/ox-man.el
@@ -104,9 +104,9 @@
        ((?m "As MAN file" org-man-export-to-man)
 	(?p "As PDF file" org-man-export-to-pdf)
 	(?o "As PDF file and open"
-	    (lambda (a s v b)
-	      (if a (org-man-export-to-pdf t s v b)
-		(org-open-file (org-man-export-to-pdf nil s v b)))))))
+	    (lambda (a s v b d)
+	      (if a (org-man-export-to-pdf t s v b d)
+		(org-open-file (org-man-export-to-pdf nil s v b d)))))))
   :options-alist
   '((:man-class "MAN_CLASS" nil nil t)
     (:man-class-options "MAN_CLASS_OPTIONS" nil nil t)
@@ -1115,7 +1115,7 @@ contextual information."
 ;;; Interactive functions
 
 (defun org-man-export-to-man
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to a Man file.
 
 If narrowing is active in the current buffer, only export its
@@ -1137,6 +1137,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only the body
 without any markers.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -1148,7 +1151,7 @@ Return output file's name."
       async subtreep visible-only body-only ext-plist)))
 
 (defun org-man-export-to-pdf
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to Groff then process through to PDF.
 
 If narrowing is active in the current buffer, only export its
@@ -1170,6 +1173,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write between
 markers.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
diff --git a/lisp/ox-md.el b/lisp/ox-md.el
index 602c149..502a15b 100644
--- a/lisp/ox-md.el
+++ b/lisp/ox-md.el
@@ -60,12 +60,13 @@ This variable can be set to either `atx' or `setext'."
   :menu-entry
   '(?m "Export to Markdown"
        ((?M "To temporary buffer"
-	    (lambda (a s v b) (org-md-export-as-markdown a s v)))
-	(?m "To file" (lambda (a s v b) (org-md-export-to-markdown a s v)))
+	    (lambda (a s v b d) (org-md-export-as-markdown a s v nil d)))
+	(?m "To file"
+	    (lambda (a s v b d) (org-md-export-to-markdown a s v nil d)))
 	(?o "To file and open"
-	    (lambda (a s v b)
-	      (if a (org-md-export-to-markdown t s v)
-		(org-open-file (org-md-export-to-markdown nil s v)))))))
+	    (lambda (a s v b d)
+	      (if a (org-md-export-to-markdown t s v nil d)
+		(org-open-file (org-md-export-to-markdown nil s v nil d)))))))
   :translate-alist '((bold . org-md-bold)
 		     (code . org-md-verbatim)
 		     (example-block . org-md-example-block)
@@ -487,7 +488,7 @@ as a communication channel."
 ;;; Interactive function
 
 ;;;###autoload
-(defun org-md-export-as-markdown (&optional async subtreep visible-only)
+(defun org-md-export-as-markdown (&optional async subtreep visible-only draft)
   "Export current buffer to a Markdown buffer.
 
 If narrowing is active in the current buffer, only export its
@@ -506,12 +507,15 @@ first.
 When optional argument VISIBLE-ONLY is non-nil, don't export
 contents of hidden elements.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 Export is done in a buffer named \"*Org MD Export*\", which will
 be displayed when `org-export-show-temporary-export-buffer' is
 non-nil."
   (interactive)
   (org-export-to-buffer 'md "*Org MD Export*"
-    async subtreep visible-only nil nil (lambda () (text-mode))))
+    async subtreep visible-only nil nil nil (lambda () (text-mode))))
 
 ;;;###autoload
 (defun org-md-convert-region-to-md ()
@@ -524,7 +528,7 @@ this command to convert it."
 
 
 ;;;###autoload
-(defun org-md-export-to-markdown (&optional async subtreep visible-only)
+(defun org-md-export-to-markdown (&optional async subtreep visible-only draft)
   "Export current buffer to a Markdown file.
 
 If narrowing is active in the current buffer, only export its
@@ -543,10 +547,13 @@ first.
 When optional argument VISIBLE-ONLY is non-nil, don't export
 contents of hidden elements.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 Return output file's name."
   (interactive)
   (let ((outfile (org-export-output-file-name ".md" subtreep)))
-    (org-export-to-file 'md outfile async subtreep visible-only)))
+    (org-export-to-file 'md outfile async subtreep visible-only nil draft)))
 
 ;;;###autoload
 (defun org-md-publish-to-md (plist filename pub-dir)
diff --git a/lisp/ox-odt.el b/lisp/ox-odt.el
index 75353a0..76f105e 100644
--- a/lisp/ox-odt.el
+++ b/lisp/ox-odt.el
@@ -92,9 +92,10 @@
   '(?o "Export to ODT"
        ((?o "As ODT file" org-odt-export-to-odt)
 	(?O "As ODT file and open"
-	    (lambda (a s v b)
-	      (if a (org-odt-export-to-odt t s v)
-		(org-open-file (org-odt-export-to-odt nil s v) 'system))))))
+	    (lambda (a s v b d)
+	      (if a (org-odt-export-to-odt t s v nil d)
+		(org-open-file
+		 (org-odt-export-to-odt nil s v nil d) 'system))))))
   :options-alist
   '((:odt-styles-file "ODT_STYLES_FILE" nil nil t)
     (:description "DESCRIPTION" nil nil newline)
@@ -4210,7 +4211,8 @@ formula file."
 ;;;; Export to OpenDocument Text
 
 ;;;###autoload
-(defun org-odt-export-to-odt (&optional async subtreep visible-only ext-plist)
+(defun org-odt-export-to-odt
+    (&optional async subtreep visible-only draft ext-plist)
   "Export current buffer to a ODT file.
 
 If narrowing is active in the current buffer, only export its
@@ -4229,6 +4231,9 @@ first.
 When optional argument VISIBLE-ONLY is non-nil, don't export
 contents of hidden elements.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -4256,8 +4261,9 @@ Return output file's name."
 			(let ((nxml-auto-insert-xml-declaration-flag nil))
 			  (find-file-noselect
 			   (concat org-odt-zip-dir "content.xml") t))))
-		     (output (org-export-as
-			      'odt ,subtreep ,visible-only nil ,ext-plist)))
+		     (output
+		      (org-export-as
+		       'odt ,subtreep ,visible-only nil ,draft ,ext-plist)))
 		 (with-current-buffer out-buf
 		   (erase-buffer)
 		   (insert output)))))))
@@ -4271,7 +4277,8 @@ Return output file's name."
 	      ;; styles.
 	      (hfy-user-sheet-assoc nil))
 	 ;; Initialize content.xml and kick-off the export process.
-	 (let ((output (org-export-as 'odt subtreep visible-only nil ext-plist))
+	 (let ((output
+		(org-export-as 'odt subtreep visible-only nil draft ext-plist))
 	       (out-buf (progn
 			  (require 'nxml-mode)
 			  (let ((nxml-auto-insert-xml-declaration-flag nil))
diff --git a/lisp/ox-org.el b/lisp/ox-org.el
index b395577..c19147b 100644
--- a/lisp/ox-org.el
+++ b/lisp/ox-org.el
@@ -105,9 +105,9 @@ setting of `org-html-htmlize-output-type' is `css'."
        ((?O "As Org buffer" org-org-export-as-org)
 	(?o "As Org file" org-org-export-to-org)
 	(?v "As Org file and open"
-	    (lambda (a s v b)
-	      (if a (org-org-export-to-org t s v b)
-		(org-open-file (org-org-export-to-org nil s v b))))))))
+	    (lambda (a s v b d)
+	      (if a (org-org-export-to-org t s v b d)
+		(org-open-file (org-org-export-to-org nil s v b d))))))))
 
 (defun org-org-identity (blob contents info)
   "Transcode BLOB element or object back into Org syntax.
@@ -244,7 +244,7 @@ non-nil."
 
 ;;;###autoload
 (defun org-org-export-to-org
-  (&optional async subtreep visible-only body-only ext-plist)
+  (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to an org file.
 
 If narrowing is active in the current buffer, only export its
@@ -266,6 +266,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, strip document
 keywords from output.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -274,7 +277,7 @@ Return output file name."
   (interactive)
   (let ((outfile (org-export-output-file-name ".org" subtreep)))
     (org-export-to-file 'org outfile
-      async subtreep visible-only body-only ext-plist)))
+      async subtreep visible-only body-only draft ext-plist)))
 
 ;;;###autoload
 (defun org-org-publish-to-org (plist filename pub-dir)
diff --git a/lisp/ox-publish.el b/lisp/ox-publish.el
index 20cacf9..79fffc9 100644
--- a/lisp/ox-publish.el
+++ b/lisp/ox-publish.el
@@ -582,7 +582,7 @@ Return output file name."
 		    (org-export-output-file-name extension nil pub-dir))
 		   (body-p (plist-get plist :body-only)))
 	       (org-export-to-file backend output-file
-		 nil nil nil body-p
+		 nil nil nil body-p nil
 		 ;; Add `org-publish--collect-references' and
 		 ;; `org-publish-collect-index' to final output
 		 ;; filters.  The latter isn't dependent on
diff --git a/lisp/ox-texinfo.el b/lisp/ox-texinfo.el
index c8c6554..0526eeb 100644
--- a/lisp/ox-texinfo.el
+++ b/lisp/ox-texinfo.el
@@ -88,9 +88,9 @@
        ((?t "As TEXI file" org-texinfo-export-to-texinfo)
 	(?i "As INFO file" org-texinfo-export-to-info)
 	(?o "As INFO file and open"
-	    (lambda (a s v b)
-	      (if a (org-texinfo-export-to-info t s v b)
-		(org-open-file (org-texinfo-export-to-info nil s v b)))))))
+	    (lambda (a s v b d)
+	      (if a (org-texinfo-export-to-info t s v b d)
+		(org-open-file (org-texinfo-export-to-info nil s v b d)))))))
   :options-alist
   '((:texinfo-filename "TEXINFO_FILENAME" nil nil t)
     (:texinfo-class "TEXINFO_CLASS" nil org-texinfo-default-class t)
@@ -1483,7 +1483,7 @@ contextual information."
 ;;; Interactive functions
 
 (defun org-texinfo-export-to-texinfo
-  (&optional async subtreep visible-only body-only ext-plist)
+    (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to a Texinfo file.
 
 If narrowing is active in the current buffer, only export its
@@ -1505,6 +1505,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -1514,10 +1517,10 @@ Return output file's name."
   (let ((outfile (org-export-output-file-name ".texi" subtreep))
 	(org-export-coding-system org-texinfo-coding-system))
     (org-export-to-file 'texinfo outfile
-      async subtreep visible-only body-only ext-plist)))
+      async subtreep visible-only body-only draft ext-plist)))
 
 (defun org-texinfo-export-to-info
-  (&optional async subtreep visible-only body-only ext-plist)
+    (&optional async subtreep visible-only body-only draft ext-plist)
   "Export current buffer to Texinfo then process through to INFO.
 
 If narrowing is active in the current buffer, only export its
@@ -1539,6 +1542,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only write code
 between \"\\begin{document}\" and \"\\end{document}\".
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 EXT-PLIST, when provided, is a property list with external
 parameters overriding Org default settings, but still inferior to
 file-local settings.
@@ -1551,7 +1557,7 @@ Return INFO file's name."
   (let ((outfile (org-export-output-file-name ".texi" subtreep))
 	(org-export-coding-system org-texinfo-coding-system))
     (org-export-to-file 'texinfo outfile
-      async subtreep visible-only body-only ext-plist
+      async subtreep visible-only body-only draft ext-plist
       (lambda (file) (org-texinfo-compile file)))))
 
 ;;;###autoload
diff --git a/lisp/ox.el b/lisp/ox.el
index 0d6711c..c3d54cd 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -857,6 +857,16 @@ automatically.  But you can retrieve them with \\[org-export-stack]."
   :package-version '(Org . "8.0")
   :type 'boolean)
 
+(defcustom org-export-as-draft nil
+  "Non-nil means export is allowed to produce a draft.
+When in draft mode, invalid links and macros do not throw an
+error and stop export process.  Instead, they are marked as
+invalid in the produced output."
+  :group 'org-export-general
+  :version "25.1"
+  :package-version '(Org . "8.4")
+  :type 'boolean)
+
 (defcustom org-export-async-init-file nil
   "File used to initialize external export process.
 
@@ -882,9 +892,9 @@ for current export scope is added to the prompt (\"b\" when
 output is restricted to body only, \"s\" when it is restricted to
 the current subtree, \"v\" when only visible elements are
 considered for export, \"f\" when publishing functions should be
-passed the FORCE argument and \"a\" when the export should be
-asynchronous).  Also, [?] allows to switch back to standard
-mode."
+passed the FORCE argument, \"a\" when the export should be
+asynchronous and \"d\" when document is to be exported as
+a draft).  Also, [?] allows to switch back to standard mode."
   :group 'org-export-general
   :version "24.4"
   :package-version '(Org . "8.0")
@@ -1851,91 +1861,107 @@ string.  INFO is a plist holding export options.
 
 Return a string."
   (or (gethash data (plist-get info :exported-data))
-      (let* ((type (org-element-type data))
-	     (results
-	      (cond
-	       ;; Ignored element/object.
-	       ((memq data (plist-get info :ignore-list)) nil)
-	       ;; Plain text.
-	       ((eq type 'plain-text)
-		(org-export-filter-apply-functions
-		 (plist-get info :filter-plain-text)
-		 (let ((transcoder (org-export-transcoder data info)))
-		   (if transcoder (funcall transcoder data info) data))
-		 info))
-	       ;; Secondary string.
-	       ((not type)
-		(mapconcat (lambda (obj) (org-export-data obj info)) data ""))
-	       ;; Element/Object without contents or, as a special
-	       ;; case, headline with archive tag and archived trees
-	       ;; restricted to title only.
-	       ((or (not (org-element-contents data))
-		    (and (eq type 'headline)
-			 (eq (plist-get info :with-archived-trees) 'headline)
-			 (org-element-property :archivedp data)))
-		(let ((transcoder (org-export-transcoder data info)))
-		  (or (and (functionp transcoder)
-			   (funcall transcoder data nil info))
-		      ;; Export snippets never return a nil value so
-		      ;; that white spaces following them are never
-		      ;; ignored.
-		      (and (eq type 'export-snippet) ""))))
-	       ;; Element/Object with contents.
-	       (t
-		(let ((transcoder (org-export-transcoder data info)))
-		  (when transcoder
-		    (let* ((greaterp (memq type org-element-greater-elements))
-			   (objectp
-			    (and (not greaterp)
-				 (memq type org-element-recursive-objects)))
-			   (contents
-			    (mapconcat
-			     (lambda (element) (org-export-data element info))
-			     (org-element-contents
-			      (if (or greaterp objectp) data
-				;; Elements directly containing
-				;; objects must have their indentation
-				;; normalized first.
-				(org-element-normalize-contents
-				 data
-				 ;; When normalizing contents of the
-				 ;; first paragraph in an item or
-				 ;; a footnote definition, ignore
-				 ;; first line's indentation: there is
-				 ;; none and it might be misleading.
-				 (when (eq type 'paragraph)
-				   (let ((parent (org-export-get-parent data)))
-				     (and
-				      (eq (car (org-element-contents parent))
-					  data)
-				      (memq (org-element-type parent)
-					    '(footnote-definition item))))))))
-			     "")))
-		      (funcall transcoder data
-			       (if (not greaterp) contents
-				 (org-element-normalize-string contents))
-			       info))))))))
-	;; Final result will be memoized before being returned.
-	(puthash
-	 data
-	 (cond
-	  ((not results) "")
-	  ((memq type '(org-data plain-text nil)) results)
-	  ;; Append the same white space between elements or objects
-	  ;; as in the original buffer, and call appropriate filters.
-	  (t
-	   (let ((results
+      (cl-macrolet ((ignore-errors-in-draft
+		     (&rest body)
+		     `(condition-case err
+			  (progn ,@body)
+			(user-error
+			 (if (memq 'draft (plist-get info :export-options))
+			     (org-export-data
+			      (format "[INVALID %s]"
+				      (upcase (symbol-name type)))
+			      info)
+			   (user-error (nth 1 err)))))))
+	(let* ((type (org-element-type data))
+	       (results
+		(cond
+		 ;; Ignored element/object.
+		 ((memq data (plist-get info :ignore-list)) nil)
+		 ;; Plain text.
+		 ((eq type 'plain-text)
 		  (org-export-filter-apply-functions
-		   (plist-get info (intern (format ":filter-%s" type)))
-		   (let ((post-blank (or (org-element-property :post-blank data)
-					 0)))
-		     (if (memq type org-element-all-elements)
-			 (concat (org-element-normalize-string results)
-				 (make-string post-blank ?\n))
-		       (concat results (make-string post-blank ?\s))))
-		   info)))
-	     results)))
-	 (plist-get info :exported-data)))))
+		   (plist-get info :filter-plain-text)
+		   (let ((transcoder (org-export-transcoder data info)))
+		     (if transcoder (funcall transcoder data info) data))
+		   info))
+		 ;; Secondary string.
+		 ((not type)
+		  (mapconcat (lambda (o) (org-export-data o info)) data ""))
+		 ;; Element/Object without contents or, as a special
+		 ;; case, headline with archive tag and archived trees
+		 ;; restricted to title only.
+		 ((or (not (org-element-contents data))
+		      (and (eq type 'headline)
+			   (eq (plist-get info :with-archived-trees)
+			       'headline)
+			   (org-element-property :archivedp data)))
+		  (let ((transcoder (org-export-transcoder data info)))
+		    (or (and (functionp transcoder)
+			     (ignore-errors-in-draft
+			      (funcall transcoder data nil info)))
+			;; Export snippets never return a nil value so
+			;; that white spaces following them are never
+			;; ignored.
+			(and (eq type 'export-snippet) ""))))
+		 ;; Element/Object with contents.
+		 (t
+		  (let ((transcoder (org-export-transcoder data info)))
+		    (when transcoder
+		      (let* ((greaterp
+			      (memq type org-element-greater-elements))
+			     (objectp
+			      (and (not greaterp)
+				   (memq type org-element-recursive-objects)))
+			     (contents
+			      (mapconcat
+			       (lambda (e) (org-export-data e info))
+			       (org-element-contents
+				(if (or greaterp objectp) data
+				  ;; Elements directly containing
+				  ;; objects must have their
+				  ;; indentation normalized first.
+				  (org-element-normalize-contents
+				   data
+				   ;; When normalizing contents of the
+				   ;; first paragraph in an item or
+				   ;; a footnote definition, ignore
+				   ;; first line's indentation: there
+				   ;; is none and it might be
+				   ;; misleading.
+				   (when (eq type 'paragraph)
+				     (let ((p (org-export-get-parent data)))
+				       (and (eq (car (org-element-contents p))
+						data)
+					    (memq (org-element-type p)
+						  '(footnote-definition
+						    item))))))))
+			       "")))
+			(ignore-errors-in-draft
+			 (funcall transcoder data
+				  (if (not greaterp) contents
+				    (org-element-normalize-string contents))
+				  info)))))))))
+	  ;; Final result will be memoized before being returned.
+	  (puthash
+	   data
+	   (cond
+	    ((not results) "")
+	    ((memq type '(org-data plain-text nil)) results)
+	    ;; Append the same white space between elements or objects
+	    ;; as in the original buffer, and call appropriate filters.
+	    (t
+	     (let ((results
+		    (org-export-filter-apply-functions
+		     (plist-get info (intern (format ":filter-%s" type)))
+		     (let ((post-blank
+			    (or (org-element-property :post-blank data) 0)))
+		       (if (memq type org-element-all-elements)
+			   (concat (org-element-normalize-string results)
+				   (make-string post-blank ?\n))
+			 (concat results (make-string post-blank ?\s))))
+		     info)))
+	       results)))
+	   (plist-get info :exported-data))))))
 
 (defun org-export-data-with-backend (data backend info)
   "Convert DATA into BACKEND format.
@@ -2866,7 +2892,7 @@ not, are considered."
 
 ;;;###autoload
 (defun org-export-as
-    (backend &optional subtreep visible-only body-only ext-plist)
+    (backend &optional subtreep visible-only body-only draft ext-plist)
   "Transcode current Org buffer into BACKEND code.
 
 BACKEND is either an export back-end, as returned by, e.g.,
@@ -2888,6 +2914,9 @@ contents of hidden elements.
 When optional argument BODY-ONLY is non-nil, only return body
 code, without surrounding template.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 Optional argument EXT-PLIST, when provided, is a property list
 with external parameters overriding Org default settings, but
 still inferior to file-local settings.
@@ -2915,7 +2944,8 @@ Return code as a string."
 			  (delq nil
 				(list (and subtreep 'subtree)
 				      (and visible-only 'visible-only)
-				      (and body-only 'body-only))))
+				      (and body-only 'body-only)
+				      (and draft 'draft))))
 		    (org-export--get-buffer-attributes)))
 	     (parsed-keywords
 	      (delq nil
@@ -2986,8 +3016,25 @@ Return code as a string."
 	   (cons "email" (org-element-interpret-data (plist-get info :email)))
 	   (cons "title" (org-element-interpret-data (plist-get info :title)))
 	   (cons "results" "$1"))
-	  'finalize
+	  (and (not draft) 'finalize)
 	  parsed-keywords)
+	 ;; In draft mode, macros are left as-is in the buffer.
+	 ;; However, no export back-end handles them so they will end
+	 ;; up being silently ignored.  As a consequence, replace them
+	 ;; with more prominent markup.
+	 (when draft
+	   (save-excursion
+	     (goto-char (point-min))
+	     (while (search-forward "{{{" nil t)
+	       (let ((object (org-element-context)))
+		 (when (eq (org-element-type object) 'macro)
+		   (delete-region
+		    (org-element-property :begin object)
+		    (progn (goto-char (org-element-property :end object))
+			   (skip-chars-backward " \t")
+			   (point)))
+		   (insert (format "[INVALID MACRO: %s]"
+				   (org-element-property :key object))))))))
 	 ;; Parse buffer.
 	 (setq tree (org-element-parse-buffer nil visible-only))
 	 ;; Merge footnote definitions outside scope into parse tree.
@@ -3030,7 +3077,7 @@ Return code as a string."
 	     info))))))))
 
 ;;;###autoload
-(defun org-export-string-as (string backend &optional body-only ext-plist)
+(defun org-export-string-as (string backend &optional body-only draft ext-plist)
   "Transcode STRING into BACKEND code.
 
 BACKEND is either an export back-end, as returned by, e.g.,
@@ -3040,6 +3087,9 @@ a registered back-end.
 When optional argument BODY-ONLY is non-nil, only return body
 code, without preamble nor postamble.
 
+When optional argument DRAFT is non-nil, ignore user
+errors (e.g., invalid links) during the process.
+
 Optional argument EXT-PLIST, when provided, is a property list
 with external parameters overriding Org default settings, but
 still inferior to file-local settings.
@@ -3048,7 +3098,7 @@ Return code as a string."
   (with-temp-buffer
     (insert string)
     (let ((org-inhibit-startup t)) (org-mode))
-    (org-export-as backend nil nil body-only ext-plist)))
+    (org-export-as backend nil nil body-only draft ext-plist)))
 
 ;;;###autoload
 (defun org-export-replace-region-by (backend)
@@ -5754,9 +5804,9 @@ and `org-export-to-file' for more specialized functions."
 
 ;;;###autoload
 (defun org-export-to-buffer
-  (backend buffer
-	   &optional async subtreep visible-only body-only ext-plist
-	   post-process)
+    (backend buffer
+	     &optional async subtreep visible-only body-only draft
+	     ext-plist post-process)
   "Call `org-export-as' with output to a specified buffer.
 
 BACKEND is either an export back-end, as returned by, e.g.,
@@ -5772,7 +5822,7 @@ through the `org-export-stack' interface.  When ASYNC is nil, the
 buffer is displayed if `org-export-show-temporary-export-buffer'
 is non-nil.
 
-Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY and
+Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY, DRAFT and
 EXT-PLIST are similar to those used in `org-export-as', which
 see.
 
@@ -5782,10 +5832,11 @@ from BUFFER, with point at its beginning.  Export back-ends can
 use it to set a major mode there, e.g,
 
   (defun org-latex-export-as-latex
-    (&optional async subtreep visible-only body-only ext-plist)
+    (&optional async subtreep visible-only body-only draft ext-plist)
     (interactive)
     (org-export-to-buffer \\='latex \"*Org LATEX Export*\"
-      async subtreep visible-only body-only ext-plist (lambda () (LaTeX-mode))))
+      async subtreep visible-only body-only draft ext-plist
+      (lambda () (LaTeX-mode))))
 
 This function returns BUFFER."
   (declare (indent 2))
@@ -5800,9 +5851,10 @@ This function returns BUFFER."
 	       (org-export-add-to-stack (current-buffer) ',backend)
 	       (ignore-errors (funcall ,post-process))))
 	`(org-export-as
-	  ',backend ,subtreep ,visible-only ,body-only ',ext-plist))
+	  ',backend ,subtreep ,visible-only ,body-only ,draft ',ext-plist))
     (let ((output
-	   (org-export-as backend subtreep visible-only body-only ext-plist))
+	   (org-export-as
+	    backend subtreep visible-only body-only draft ext-plist))
 	  (buffer (get-buffer-create buffer))
 	  (encoding buffer-file-coding-system))
       (when (and (org-string-nw-p output) (org-export--copy-to-kill-ring-p))
@@ -5819,8 +5871,8 @@ This function returns BUFFER."
 
 ;;;###autoload
 (defun org-export-to-file
-  (backend file &optional async subtreep visible-only body-only ext-plist
-	   post-process)
+    (backend file &optional async subtreep visible-only body-only draft
+	     ext-plist post-process)
   "Call `org-export-as' with output to a specified file.
 
 BACKEND is either an export back-end, as returned by, e.g.,
@@ -5832,7 +5884,7 @@ A non-nil optional argument ASYNC means the process should happen
 asynchronously.  The resulting buffer will then be accessible
 through the `org-export-stack' interface.
 
-Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY and
+Optional arguments SUBTREEP, VISIBLE-ONLY, BODY-ONLY, DRAFT and
 EXT-PLIST are similar to those used in `org-export-as', which
 see.
 
@@ -5842,11 +5894,11 @@ has to return a file name, or nil.  Export back-ends can use this
 to send the output file through additional processing, e.g,
 
   (defun org-latex-export-to-latex
-    (&optional async subtreep visible-only body-only ext-plist)
+    (&optional async subtreep visible-only body-only draft ext-plist)
     (interactive)
     (let ((outfile (org-export-output-file-name \".tex\" subtreep)))
       (org-export-to-file \\='latex outfile
-        async subtreep visible-only body-only ext-plist
+        async subtreep visible-only body-only draft ext-plist
         (lambda (file) (org-latex-compile file)))
 
 The function returns either a file name returned by POST-PROCESS,
@@ -5861,15 +5913,16 @@ or FILE."
 		 (org-export-add-to-stack (expand-file-name file) ',backend))
 	    `(let ((output
 		    (org-export-as
-		     ',backend ,subtreep ,visible-only ,body-only
+		     ',backend ,subtreep ,visible-only ,body-only ,draft
 		     ',ext-plist)))
 	       (with-temp-buffer
 		 (insert output)
 		 (let ((coding-system-for-write ',encoding))
 		   (write-file ,file)))
 	       (or (ignore-errors (funcall ',post-process ,file)) ,file)))
-        (let ((output (org-export-as
-                       backend subtreep visible-only body-only ext-plist)))
+        (let ((output
+	       (org-export-as
+		backend subtreep visible-only body-only draft ext-plist)))
           (with-temp-buffer
             (insert output)
             (let ((coding-system-for-write encoding))
@@ -6117,7 +6170,8 @@ When ARG is \\[universal-argument] \\[universal-argument], display the asynchron
 			   (setq org-export-dispatch-last-action
 				 (org-export--dispatch-ui
 				  (list org-export-initial-scope
-					(and org-export-in-background 'async))
+					(and org-export-in-background 'async)
+					(and org-export-as-draft 'draft))
 				  nil
 				  org-export-dispatch-use-expert-ui)))
 		       (and (get-buffer "*Org Export Dispatcher*")
@@ -6158,7 +6212,8 @@ When ARG is \\[universal-argument] \\[universal-argument], display the asynchron
 		  (and (memq 'async optns) t)
 		  (and (memq 'subtree optns) t)
 		  (and (memq 'visible optns) t)
-		  (and (memq 'body optns) t)))))))
+		  (and (memq 'body optns) t)
+		  (and (memq 'draft optns) t)))))))
 
 (defun org-export--dispatch-ui (options first-key expertp)
   "Handle interface for `org-export-dispatch'.
@@ -6170,6 +6225,7 @@ export.  It can contain any of the following symbols:
 `visible' restricts export to visible part of buffer.
 `force'   force publishing files.
 `async'   use asynchronous export process
+`draft'   export using a more permissive mode
 
 FIRST-KEY is the key pressed to select the first level menu.  It
 is nil when this menu hasn't been selected yet.
@@ -6208,13 +6264,13 @@ back to standard interface."
 		'car-less-than-car))
 	 ;; Compute a list of allowed keys based on the first key
 	 ;; pressed, if any.  Some keys
-	 ;; (?^B, ?^V, ?^S, ?^F, ?^A, ?&, ?# and ?q) are always
+	 ;; (?^B, ?^V, ?^S, ?^F, ?^A, ?^D, ?&, ?# and ?q) are always
 	 ;; available.
 	 (allowed-keys
-	  (nconc (list 2 22 19 6 1)
-		 (if (not first-key) (org-uniquify (mapcar 'car entries))
+	  (nconc (list 2 22 19 6 1 4)
+		 (if (not first-key) (org-uniquify (mapcar #'car entries))
 		   (let (sub-menu)
-		     (dolist (entry entries (sort (mapcar 'car sub-menu) '<))
+		     (dolist (entry entries (sort (mapcar #'car sub-menu) #'<))
 		       (when (eq (car entry) first-key)
 			 (setq sub-menu (append (nth 2 entry) sub-menu))))))
 		 (cond ((eq first-key ?P) (list ?f ?p ?x ?a))
@@ -6229,7 +6285,7 @@ back to standard interface."
 	     ;; Options are hard-coded.
 	     (format "[%s] Body only:    %s           [%s] Visible only:     %s
 \[%s] Export scope: %s       [%s] Force publishing: %s
-\[%s] Async export: %s\n\n"
+\[%s] Async export: %s           [%s] Draft mode :      %s\n\n"
 		     (funcall fontify-key "C-b" t)
 		     (funcall fontify-value
 			      (if (memq 'body options) "On " "Off"))
@@ -6244,7 +6300,10 @@ back to standard interface."
 			      (if (memq 'force options) "On " "Off"))
 		     (funcall fontify-key "C-a" t)
 		     (funcall fontify-value
-			      (if (memq 'async options) "On " "Off")))
+			      (if (memq 'async options) "On " "Off"))
+		     (funcall fontify-key "C-d" t)
+		     (funcall fontify-value
+			      (if (memq 'draft options) "On " "Off")))
 	     ;; Display registered back-end entries.  When a key
 	     ;; appears for the second time, do not create another
 	     ;; entry, but append its sub-menu to existing menu.
@@ -6296,12 +6355,13 @@ back to standard interface."
 	 (expert-prompt
 	  (when expertp
 	    (format
-	     "Export command (C-%s%s%s%s%s) [%s]: "
+	     "Export command (C-%s%s%s%s%s%s) [%s]: "
 	     (if (memq 'body options) (funcall fontify-key "b" t) "b")
 	     (if (memq 'visible options) (funcall fontify-key "v" t) "v")
 	     (if (memq 'subtree options) (funcall fontify-key "s" t) "s")
 	     (if (memq 'force options) (funcall fontify-key "f" t) "f")
 	     (if (memq 'async options) (funcall fontify-key "a" t) "a")
+	     (if (memq 'draft options) (funcall fontify-key "d" t) "d")
 	     (mapconcat (lambda (k)
 			  ;; Strip control characters.
 			  (unless (< k 27) (char-to-string k)))
@@ -6346,8 +6406,8 @@ EXPERTP are the same as defined in `org-export--dispatch-ui',
 which see.
 
 Toggle export options when required.  Otherwise, return value is
-a list with action as CAR and a list of interactive export
-options as CDR."
+a list with action as car and a list of interactive export
+options as cdr."
   (let (key)
     ;; Scrolling: when in non-expert mode, act on motion keys (C-n,
     ;; C-p, SPC, DEL).
@@ -6388,11 +6448,11 @@ options as CDR."
      ((eq key ?#) (cons 'template (memq 'subtree options)))
      ;; Switch to asynchronous export stack.
      ((eq key ?&) '(stack))
-     ;; Toggle options: C-b (2) C-v (22) C-s (19) C-f (6) C-a (1).
-     ((memq key '(2 22 19 6 1))
+     ;; Options: C-b (2) C-v (22) C-s (19) C-f (6) C-a (1) C-d (4).
+     ((memq key '(2 22 19 6 1 4))
       (org-export--dispatch-ui
        (let ((option (cl-case key (2 'body) (22 'visible) (19 'subtree)
-			      (6 'force) (1 'async))))
+			      (6 'force) (1 'async) (4 'draft))))
 	 (if (memq option options) (remq option options)
 	   (cons option options)))
        first-key expertp))
@@ -6413,10 +6473,9 @@ options as CDR."
 	     ;; path. Indeed, derived backends can share the same
 	     ;; FIRST-KEY.
 	     (t (catch 'found
-		  (mapc (lambda (entry)
-			  (let ((match (assq key (nth 2 entry))))
-			    (when match (throw 'found (nth 2 match)))))
-			(member (assq first-key entries) entries)))))
+		  (dolist (entry (member (assq first-key entries) entries))
+		    (let ((match (assq key (nth 2 entry))))
+		      (when match (throw 'found (nth 2 match))))))))
 	    options))
      ;; Otherwise, enter sub-menu.
      (t (org-export--dispatch-ui options key expertp)))))
diff --git a/testing/lisp/test-ox.el b/testing/lisp/test-ox.el
index c5e2218..6cccace 100644
--- a/testing/lisp/test-ox.el
+++ b/testing/lisp/test-ox.el
@@ -401,14 +401,14 @@ Paragraph"
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text "* Head1 :noexp:"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:exclude-tags ("noexp")))))))
+			     nil nil nil nil '(:exclude-tags ("noexp")))))))
   ;; Test include tags for headlines and inlinetasks.
   (should
    (equal "* H2\n** Sub :exp:\n*** Sub Sub\n"
 	  (org-test-with-temp-text "* H1\n* H2\n** Sub :exp:\n*** Sub Sub\n* H3"
 	    (let ((org-tags-column 0))
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:select-tags ("exp")))))))
+			     nil nil nil nil '(:select-tags ("exp")))))))
   ;; If there is an include tag, ignore the section before the first
   ;; headline, if any.
   (should
@@ -416,13 +416,13 @@ Paragraph"
 	  (org-test-with-temp-text "First section\n* H1 :exp:\nBody"
 	    (let ((org-tags-column 0))
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:select-tags ("exp")))))))
+			     nil nil nil nil '(:select-tags ("exp")))))))
   (should-not
    (equal "* H1 :exp:\n"
 	  (org-test-with-temp-text "* H1 :exp:\nBody"
 	    (let ((org-tags-column 0))
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:select-tags ("exp")))))))
+			     nil nil nil nil '(:select-tags ("exp")))))))
   ;; Test mixing include tags and exclude tags.
   (should
    (string-match
@@ -433,7 +433,7 @@ Paragraph"
 ** Sub-Head2
 * Head2 :noexport:
 ** Sub-Head1 :export:"
-      (org-export-as (org-test-default-backend) nil nil nil
+      (org-export-as (org-test-default-backend) nil nil nil nil
 		     '(:select-tags ("export") :exclude-tags ("noexport"))))))
   ;; Ignore tasks.
   (should
@@ -443,13 +443,13 @@ Paragraph"
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text "* TODO Head1"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:with-tasks nil))))))
+			     nil nil nil nil '(:with-tasks nil))))))
   (should
    (equal "* TODO Head1\n"
 	  (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
 	    (org-test-with-temp-text "* TODO Head1"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:with-tasks t))))))
+			     nil nil nil nil '(:with-tasks t))))))
   ;; Archived tree.
   (should
    (equal ""
@@ -458,13 +458,13 @@ Paragraph"
 	    (org-test-with-temp-text "* Head1 :archive:"
 	      (let ((org-archive-tag "archive"))
 		(org-export-as (org-test-default-backend)
-			       nil nil nil '(:with-archived-trees nil)))))))
+			       nil nil nil nil '(:with-archived-trees nil)))))))
   (should
    (string-match
     "\\* Head1[ \t]+:archive:"
     (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2"
       (let ((org-archive-tag "archive"))
-	(org-export-as (org-test-default-backend) nil nil nil
+	(org-export-as (org-test-default-backend) nil nil nil nil
 		       '(:with-archived-trees headline))))))
   (should
    (string-match
@@ -472,20 +472,20 @@ Paragraph"
     (org-test-with-temp-text "* Head1 :archive:"
       (let ((org-archive-tag "archive"))
 	(org-export-as (org-test-default-backend)
-		       nil nil nil '(:with-archived-trees t))))))
+		       nil nil nil nil '(:with-archived-trees t))))))
   ;; Clocks.
   (should
    (string-match "CLOCK: \\[2012-04-29 .* 10:45\\]"
 		 (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
 		   (org-export-as (org-test-default-backend)
-				  nil nil nil '(:with-clocks t)))))
+				  nil nil nil nil '(:with-clocks t)))))
   (should
    (equal ""
 	  (let (org-export-filter-body-functions
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:with-clocks nil))))))
+			     nil nil nil nil '(:with-clocks nil))))))
   ;; Drawers.
   (should
    (equal ""
@@ -493,48 +493,50 @@ Paragraph"
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text ":TEST:\ncontents\n:END:"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:with-drawers nil))))))
+			     nil nil nil nil '(:with-drawers nil))))))
   (should
    (equal ":TEST:\ncontents\n:END:\n"
 	  (org-test-with-temp-text ":TEST:\ncontents\n:END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-drawers t)))))
+			   nil nil nil nil '(:with-drawers t)))))
   (should
    (equal ":FOO:\nkeep\n:END:\n"
 	  (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-drawers ("FOO"))))))
+			   nil nil nil nil '(:with-drawers ("FOO"))))))
   (should
    (equal ":FOO:\nkeep\n:END:\n"
 	  (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-drawers (not "BAR"))))))
+			   nil nil nil nil '(:with-drawers (not "BAR"))))))
   ;; Fixed-width.
   (should
    (equal ": A\n"
 	  (org-test-with-temp-text ": A"
-	    (org-export-as (org-test-default-backend) nil nil nil
+	    (org-export-as (org-test-default-backend) nil nil nil nil
 			   '(:with-fixed-width t)))))
   (should
    (equal ""
 	  (let (org-export-filter-body-functions
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text ": A"
-	      (org-export-as (org-test-default-backend) nil nil nil
+	      (org-export-as (org-test-default-backend) nil nil nil nil
 			     '(:with-fixed-width nil))))))
   ;; Footnotes.
   (should
    (equal "Footnote?"
 	  (let ((org-footnote-section nil))
 	    (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def"
-	      (org-trim (org-export-as (org-test-default-backend)
-				       nil nil nil '(:with-footnotes nil)))))))
+	      (org-trim
+	       (org-export-as (org-test-default-backend)
+			      nil nil nil nil '(:with-footnotes nil)))))))
   (should
    (equal "Footnote?[fn:1]\n\n[fn:1] Def"
 	  (let ((org-footnote-section nil))
 	    (org-test-with-temp-text "Footnote?[fn:1]\n\n[fn:1] Def"
-	      (org-trim (org-export-as (org-test-default-backend)
-				       nil nil nil '(:with-footnotes t)))))))
+	      (org-trim
+	       (org-export-as (org-test-default-backend)
+			      nil nil nil nil '(:with-footnotes t)))))))
   ;; Inlinetasks.
   (when (featurep 'org-inlinetask)
     (should
@@ -545,7 +547,7 @@ Paragraph"
 	    org-export-filter-final-output-functions)
 	(org-test-with-temp-text "*************** Task"
 	  (org-export-as (org-test-default-backend)
-			 nil nil nil '(:with-inlinetasks nil))))))
+			 nil nil nil nil '(:with-inlinetasks nil))))))
     (should
      (equal
       ""
@@ -555,7 +557,7 @@ Paragraph"
 	(org-test-with-temp-text
 	    "*************** Task\nContents\n*************** END"
 	  (org-export-as (org-test-default-backend)
-			 nil nil nil '(:with-inlinetasks nil)))))))
+			 nil nil nil nil '(:with-inlinetasks nil)))))))
   ;; Plannings.
   (should
    (string-match
@@ -563,32 +565,32 @@ Paragraph"
     (let ((org-closed-string "CLOSED:"))
       (org-test-with-temp-text "* H\nCLOSED: [2012-04-29 sun. 10:45]"
 	(org-export-as (org-test-default-backend)
-		       nil nil nil '(:with-planning t))))))
+		       nil nil nil nil '(:with-planning t))))))
   (should
    (equal "* H\n"
 	  (let ((org-closed-string "CLOSED:"))
 	    (org-test-with-temp-text "* H\nCLOSED: [2012-04-29 sun. 10:45]"
 	      (org-export-as (org-test-default-backend)
-			     nil nil nil '(:with-planning nil))))))
+			     nil nil nil nil '(:with-planning nil))))))
   ;; Property Drawers.
   (should
    (equal "* H1\n"
 	  (org-test-with-temp-text
 	      "* H1\n  :PROPERTIES:\n  :PROP: value\n  :END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-properties nil)))))
+			   nil nil nil nil '(:with-properties nil)))))
   (should
    (equal "* H1\n:PROPERTIES:\n:PROP:     value\n:END:\n"
 	  (org-test-with-temp-text
 	      "* H1\n  :PROPERTIES:\n  :PROP: value\n  :END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-properties t)))))
+			   nil nil nil nil '(:with-properties t)))))
   (should
    (equal "* H1\n:PROPERTIES:\n:B:        2\n:END:\n"
 	  (org-test-with-temp-text
 	      "* H1\n  :PROPERTIES:\n  :A: 1\n  :B: 2\n:END:"
 	    (org-export-as (org-test-default-backend)
-			   nil nil nil '(:with-properties ("B"))))))
+			   nil nil nil nil '(:with-properties ("B"))))))
   ;; Statistics cookies.
   (should
    (equal "* Stats"
@@ -596,20 +598,21 @@ Paragraph"
 		org-export-filter-final-output-functions)
 	    (org-trim
 	     (org-test-with-temp-text "* Stats [0/0]"
-	       (org-export-as (org-test-default-backend)
-			      nil nil nil '(:with-statistics-cookies nil)))))))
+	       (org-export-as
+		(org-test-default-backend)
+		nil nil nil nil '(:with-statistics-cookies nil)))))))
   ;; Tables.
   (should
    (equal "| A |\n"
 	  (org-test-with-temp-text "| A |"
-	    (org-export-as (org-test-default-backend) nil nil nil
+	    (org-export-as (org-test-default-backend) nil nil nil nil
 			   '(:with-tables t)))))
   (should
    (equal ""
 	  (let (org-export-filter-body-functions
 		org-export-filter-final-output-functions)
 	    (org-test-with-temp-text "| A |"
-	      (org-export-as (org-test-default-backend) nil nil nil
+	      (org-export-as (org-test-default-backend) nil nil nil nil
 			     '(:with-tables nil)))))))
 
 (ert-deftest test-org-export/with-timestamps ()
@@ -620,7 +623,7 @@ Paragraph"
     "\\[2012-04-29 .*? 10:45\\]<2012-04-29 .*? 10:45>"
     (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>"
       (org-export-as (org-test-default-backend)
-		     nil nil nil '(:with-timestamps t)))))
+		     nil nil nil nil '(:with-timestamps t)))))
   ;; nil value.
   (should
    (equal
@@ -630,7 +633,7 @@ Paragraph"
       (org-trim
        (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>"
 	 (org-export-as (org-test-default-backend)
-			nil nil nil '(:with-timestamps nil)))))))
+			nil nil nil nil '(:with-timestamps nil)))))))
   ;; `active' value.
   (should
    (string-match
@@ -640,7 +643,7 @@ Paragraph"
 
 Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
       (org-export-as (org-test-default-backend)
-		     nil nil nil '(:with-timestamps active)))))
+		     nil nil nil nil '(:with-timestamps active)))))
   ;; `inactive' value.
   (should
    (string-match
@@ -650,7 +653,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 
 Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
       (org-export-as (org-test-default-backend)
-		     nil nil nil '(:with-timestamps inactive))))))
+		     nil nil nil nil '(:with-timestamps inactive))))))
 
 (ert-deftest test-org-export/comment-tree ()
   "Test if export process ignores commented trees."
@@ -672,7 +675,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((entity . (lambda (e c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-entities t)))))
+	     nil nil nil nil '(:with-entities t)))))
   (should
    (equal "\\alpha\n"
 	  (org-test-with-temp-text "\\alpha"
@@ -681,7 +684,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((entity . (lambda (e c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-entities nil)))))
+	     nil nil nil nil '(:with-entities nil)))))
   ;; Emphasis.
   (should
    (equal "dummy\n"
@@ -691,7 +694,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((bold . (lambda (b c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-emphasize t)))))
+	     nil nil nil nil '(:with-emphasize t)))))
   (should
    (equal "*bold*\n"
 	  (org-test-with-temp-text "*bold*"
@@ -700,7 +703,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((bold . (lambda (b c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-emphasize nil)))))
+	     nil nil nil nil '(:with-emphasize nil)))))
   ;; LaTeX environment.
   (should
    (equal "dummy\n"
@@ -709,7 +712,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	     (org-export-create-backend
 	      :transcoders '((latex-environment . (lambda (l c i) "dummy"))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-latex t)))))
+	     nil nil nil nil '(:with-latex t)))))
   (should
    (equal "\\begin{equation}\n1+1=2\n\\end{equation}\n"
 	  (org-test-with-temp-text "\\begin{equation}\n1+1=2\n\\end{equation}"
@@ -717,7 +720,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	     (org-export-create-backend
 	      :transcoders '((latex-environment . (lambda (l c i) "dummy"))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-latex verbatim)))))
+	     nil nil nil nil '(:with-latex verbatim)))))
   ;; LaTeX fragment.
   (should
    (equal "dummy\n"
@@ -727,7 +730,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((latex-fragment . (lambda (l c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-latex t)))))
+	     nil nil nil nil '(:with-latex t)))))
   (should
    (equal "$1$\n"
 	  (org-test-with-temp-text "$1$"
@@ -736,7 +739,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((latex-fragment . (lambda (l c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-latex verbatim)))))
+	     nil nil nil nil '(:with-latex verbatim)))))
   ;; Sub/superscript.
   (should
    (equal "adummy\n"
@@ -746,7 +749,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((subscript . (lambda (s c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript t)))))
+	     nil nil nil nil '(:with-sub-superscript t)))))
   (should
    (equal "a_b\n"
 	  (org-test-with-temp-text "a_b"
@@ -755,7 +758,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((subscript . (lambda (s c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript nil)))))
+	     nil nil nil nil '(:with-sub-superscript nil)))))
   (should
    (equal "a_b\n"
 	  (org-test-with-temp-text "a_b"
@@ -764,7 +767,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((subscript . (lambda (s c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript {})))))
+	     nil nil nil nil '(:with-sub-superscript {})))))
   (should
    (equal "adummy\n"
 	  (org-test-with-temp-text "a_{b}"
@@ -773,7 +776,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((subscript . (lambda (s c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript {})))))
+	     nil nil nil nil '(:with-sub-superscript {})))))
   ;; Also handle uninterpreted objects in title.
   (should
    (equal "a_b"
@@ -785,7 +788,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 		(template . (lambda (c i) (org-export-data
 				      (plist-get i :title) i)))
 		(section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript nil)))))
+	     nil nil nil nil '(:with-sub-superscript nil)))))
   ;; Handle uninterpreted objects in captions.
   (should
    (equal "adummy\n"
@@ -797,7 +800,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 		(paragraph . (lambda (p c i)
 			       (org-export-data (org-export-get-caption p) i)))
 		(section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript t)))))
+	     nil nil nil nil '(:with-sub-superscript t)))))
   (should
    (equal "a_b\n"
 	  (org-test-with-temp-text "#+CAPTION: a_b\nParagraph"
@@ -808,7 +811,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 		(paragraph . (lambda (p c i)
 			       (org-export-data (org-export-get-caption p) i)))
 		(section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript nil)))))
+	     nil nil nil nil '(:with-sub-superscript nil)))))
   ;; Special case: multiples uninterpreted objects in a row.
   (should
    (equal "a_b_c_d\n"
@@ -818,7 +821,7 @@ Paragraph <2012-03-29 Thu>[2012-03-29 Thu]"
 	      :transcoders '((subscript . (lambda (s c i) "dummy"))
 			     (paragraph . (lambda (p c i) c))
 			     (section . (lambda (s c i) c))))
-	     nil nil nil '(:with-sub-superscript {}))))))
+	     nil nil nil nil '(:with-sub-superscript {}))))))
 
 (ert-deftest test-org-export/export-scope ()
   "Test all export scopes."
@@ -1175,7 +1178,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 		    :options '((:k "K" nil nil parse)))))
       (org-test-with-temp-text
 	  "#+MACRO: m v\n* H\n:PROPERTIES:\n:EXPORT_K: {{{m}}}\n:END:"
-	(org-export-as backend nil nil nil '(:with-properties t))))))
+	(org-export-as backend nil nil nil nil '(:with-properties t))))))
   ;; Expand specific macros.
   (should
    (equal "me 2012-03-29 me@here Title\n"
@@ -1671,7 +1674,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 		     ""))
 	    (section . (lambda (s c i) c))
 	    (paragraph . (lambda (p c i) c))))
-	 nil nil nil '(:with-footnotes t))
+	 nil nil nil nil '(:with-footnotes t))
 	(nreverse result)))))
   ;; Limit check to DATA, when non-nil.
   (should
@@ -1719,7 +1722,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	    (footnote-definition . (lambda (d c i) c))
 	    (section . (lambda (s c i) c))
 	    (paragraph . (lambda (p c i) c))))
-	 nil nil nil '(:with-footnotes t))
+	 nil nil nil nil '(:with-footnotes t))
 	(nreverse result)))))
   (should
    (equal
@@ -1740,7 +1743,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	    (footnote-definition . (lambda (d c i) c))
 	    (section . (lambda (s c i) c))
 	    (paragraph . (lambda (p c i) c))))
-	 nil nil nil '(:with-footnotes t))
+	 nil nil nil nil '(:with-footnotes t))
 	(nreverse result))))))
 
 (ert-deftest test-org-export/get-footnote-number ()
@@ -2254,7 +2257,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	    org-export-filter-final-output-functions)
 	(org-test-with-temp-text "*** Inlinetask :noexp:\nContents\n*** end"
 	  (org-export-as (org-test-default-backend)
-			 nil nil nil '(:exclude-tags ("noexp")))))))
+			 nil nil nil nil '(:exclude-tags ("noexp")))))))
     ;; Inlinetask with an include tag.
     (should
      (equal
@@ -2263,7 +2266,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 	    (org-tags-column 0))
 	(org-test-with-temp-text "* H1\n* H2\n*** Inline :exp:"
 	  (org-export-as (org-test-default-backend)
-			 nil nil nil '(:select-tags ("exp")))))))
+			 nil nil nil nil '(:select-tags ("exp")))))))
     ;; Ignore inlinetask with a TODO keyword and tasks excluded.
     (should
      (equal ""
@@ -2273,7 +2276,7 @@ Footnotes[fn:2], foot[fn:test], digit only[3], and [fn:inline:anonymous footnote
 		  org-export-filter-final-output-functions)
 	      (org-test-with-temp-text "*** TODO Inline"
 		(org-export-as (org-test-default-backend)
-			       nil nil nil '(:with-tasks nil))))))))
+			       nil nil nil nil '(:with-tasks nil))))))))
 
 
 \f
-- 
2.5.3


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-09-27 11:37 ` Nicolas Goaziou
@ 2015-09-27 16:31   ` Charles C. Berry
  2015-09-28 20:56     ` Aaron Ecay
  0 siblings, 1 reply; 19+ messages in thread
From: Charles C. Berry @ 2015-09-27 16:31 UTC (permalink / raw)
  To: Nicolas Goaziou; +Cc: Org Mode List

On Sun, 27 Sep 2015, Nicolas Goaziou wrote:

> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>
>> The following patch implements a draft mode for export. When in draft
>> mode, invalid macros and links do not throw an error. It can be toggled
>> with `org-export-as-draft' variable, or using C-d in export dispatch.
>>
>> It introduces a backward incompatible change since it modifies signature
>> from `org-export-as' and alike.
>>

I don't get it.

Why not add the `draft' arg to the end of the `&optional' args? Won't that 
preserve backward compatibility?

I maintain derived backends that use the `post-process' arg and are 
distributed on github. So now I will need to make them version aware 
and/or provide version specific branches so as not to break when used with 
older org-mode versions, won't I?

Chuck

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-09-27  9:38 [RFC] Draft mode Nicolas Goaziou
  2015-09-27 11:37 ` Nicolas Goaziou
@ 2015-09-27 18:39 ` Rasmus
  2015-09-27 20:01   ` Marcin Borkowski
  2015-10-07 20:12   ` Nicolas Goaziou
  1 sibling, 2 replies; 19+ messages in thread
From: Rasmus @ 2015-09-27 18:39 UTC (permalink / raw)
  To: emacs-orgmode

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> The following patch implements a draft mode for export. When in draft
> mode, invalid macros and links do not throw an error. It can be toggled
> with `org-export-as-draft' variable, or using C-d in export dispatch.

Can this just be a variable set by BIND or publish?  The interest of such
functionality seems to be limited to people who maintain
broken-but-soon-to-be-fixed documents...  It does not seem to warrant
prime real estate in the exporter IMO. Further, I don't think 'draft' is
the correct word for this, but that's of course minor.

> It introduces a backward incompatible change since it modifies signature
> from `org-export-as' and alike.

Doesn't seem worth it.

> This patch is incomplete as it is missing some documentation, an entry
> in ORG-NEWS and some tests. Also, export back-end in contrib/ directory
> are not updated yet. In any case, feedback welcome.

I did not test it at this point.

Rasmus

-- 
Sådan en god dansk lagereddike kan man slet ikke bruge mere

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-09-27 18:39 ` Rasmus
@ 2015-09-27 20:01   ` Marcin Borkowski
  2015-10-07 20:12   ` Nicolas Goaziou
  1 sibling, 0 replies; 19+ messages in thread
From: Marcin Borkowski @ 2015-09-27 20:01 UTC (permalink / raw)
  To: emacs-orgmode


On 2015-09-27, at 20:39, Rasmus <rasmus@gmx.us> wrote:

> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>
>> The following patch implements a draft mode for export. When in draft
>> mode, invalid macros and links do not throw an error. It can be toggled
>> with `org-export-as-draft' variable, or using C-d in export dispatch.

I would welcome this feature...

> Can this just be a variable set by BIND or publish?  The interest of such
> functionality seems to be limited to people who maintain
> broken-but-soon-to-be-fixed documents...  It does not seem to warrant
> prime real estate in the exporter IMO. Further, I don't think 'draft' is
> the correct word for this, but that's of course minor.
>
>> It introduces a backward incompatible change since it modifies signature
>> from `org-export-as' and alike.
>
> Doesn't seem worth it.

...but I agree with the above.

Also, wouldn't it be even better if there were some buffer - like
(La)TeX's log file - accumulating all the errors/warnings from the
exporting process?  Then, the above behavior could be the default, but
it would produce warnings.  Or it might depend on some setting.

> Rasmus

Best,

-- 
Marcin Borkowski
http://octd.wmi.amu.edu.pl/en/Marcin_Borkowski
Faculty of Mathematics and Computer Science
Adam Mickiewicz University

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-09-27 16:31   ` Charles C. Berry
@ 2015-09-28 20:56     ` Aaron Ecay
  2015-10-07 20:22       ` Nicolas Goaziou
  0 siblings, 1 reply; 19+ messages in thread
From: Aaron Ecay @ 2015-09-28 20:56 UTC (permalink / raw)
  To: Charles C. Berry, Nicolas Goaziou; +Cc: Org Mode List

Hi Nicolas,

This looks like a useful addition.

2015ko irailak 27an, "Charles C. Berry"-ek idatzi zuen:
> 
> On Sun, 27 Sep 2015, Nicolas Goaziou wrote:
> 
>> Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:
>> 
>>> The following patch implements a draft mode for export. When in draft
>>> mode, invalid macros and links do not throw an error. It can be toggled
>>> with `org-export-as-draft' variable, or using C-d in export dispatch.
>>> 
>>> It introduces a backward incompatible change since it modifies signature
>>> from `org-export-as' and alike.
>>> 
> 
> I don't get it.
> 
> Why not add the `draft' arg to the end of the `&optional' args? Won't that 
> preserve backward compatibility?
> 
> I maintain derived backends that use the `post-process' arg and are 
> distributed on github. So now I will need to make them version aware 
> and/or provide version specific branches so as not to break when used with 
> older org-mode versions, won't I?

This is a very good point.  OTOH, if backwards compatibility is going to
be broken, wouldn’t it be better to move to a keyword argument system?
IOW, the signature of ‘org-export-as’ and friends would change from:

(backend &optional subtreep visible-only body-only ext-plist)

to (using cl-defun):

(backend &key subtreep visible-only body-only ext-plist draft)

This would allow future changes to the API of this function without
breaking backwards compatibility.  It would also clean up messy
constructs like:

(lambda (a s v b d)
  (if a (org-org-export-to-org t s v b d)
    (org-open-file (org-org-export-to-org nil s v b d))))

which could become:

(lambda (&rest kwargs)
  (if (plist-get kwargs :async)
      (apply #'org-org-export-to-org kwargs)
    (org-open-file (apply #'org-org-export-to-org kwargs))))

and with further simplification the following, which is vastly clearer
than the original:

(lambda (&rest kwargs)
  (let ((result (apply #'org-org-export-to-org kwargs)))
    (unless (plist-get kwargs :async) (org-open-file result))))

Since this stanza recurs in the code for most (all?) backends, there’s
some value that org core can provide to backend authors by making it as
simple as possible.

With a non-trivial effort, the old calling convention could still be
supported (if the argument after ‘backend’ is not a :keyword, then
org-export-as knows that the old convention is in use, and it should
map the arguments 2 through 5 to the relevant keywords).  It could
trigger a deprecation warning and be phased out with the next major
version (or whenever).

On a different note, does this scheme make it possible for a backend
to see whether draft mode is enabled?  Latex provides a document-level
draft option, which enables certain optimizations that speed up compile
times (for example, graphics are not read in, but are replaced with a
placeholder).  It would be nice if ox-latex could emit this in the latex
code when draft mode is enabled from the org side.

Thanks,

-- 
Aaron Ecay

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-09-27 18:39 ` Rasmus
  2015-09-27 20:01   ` Marcin Borkowski
@ 2015-10-07 20:12   ` Nicolas Goaziou
  2015-10-08 14:23     ` Rasmus
  1 sibling, 1 reply; 19+ messages in thread
From: Nicolas Goaziou @ 2015-10-07 20:12 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode

Hello,

Rasmus <rasmus@gmx.us> writes:

> Can this just be a variable set by BIND or publish?

It could. If we keep throwing errors on unknown macros, I think we can
even have an OPTIONS item.

> The interest of such functionality seems to be limited to people who
> maintain broken-but-soon-to-be-fixed documents... It does not seem to
> warrant prime real estate in the exporter IMO. Further, I don't think
> 'draft' is the correct word for this, but that's of course minor.

I couldn't find anything better. Suggestions welcome.

Thank you for the feedback.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-09-28 20:56     ` Aaron Ecay
@ 2015-10-07 20:22       ` Nicolas Goaziou
  0 siblings, 0 replies; 19+ messages in thread
From: Nicolas Goaziou @ 2015-10-07 20:22 UTC (permalink / raw)
  To: Charles C. Berry; +Cc: Org Mode List

Hello,

Aaron Ecay <aaronecay@gmail.com> writes:

> This is a very good point.  OTOH, if backwards compatibility is going to
> be broken, wouldn’t it be better to move to a keyword argument system?

I'd like to avoid defun* as much as possible, because it may be harder
to read. This is also the reason why it wasn't included in core Elisp.

> On a different note, does this scheme make it possible for a backend
> to see whether draft mode is enabled?

It is possible with

  (memq 'draft (plist-get info :export-options))

However, Rasmus suggested to give up the export option and use a global
variable, instead.

> Latex provides a document-level draft option, which enables certain
> optimizations that speed up compile times (for example, graphics are
> not read in, but are replaced with a placeholder). It would be nice if
> ox-latex could emit this in the latex code when draft mode is enabled
> from the org side.

It was also suggested that "draft" wasn't the most accurate description
for this option. So, ultimately, it might not be related to LaTeX option
"draft".

Thank you for the feedback.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-07 20:12   ` Nicolas Goaziou
@ 2015-10-08 14:23     ` Rasmus
  2015-10-08 16:36       ` Thomas S. Dye
  0 siblings, 1 reply; 19+ messages in thread
From: Rasmus @ 2015-10-08 14:23 UTC (permalink / raw)
  To: emacs-orgmode

Hi,

>> Can this just be a variable set by BIND or publish?
>
> It could. If we keep throwing errors on unknown macros, I think we can
> even have an OPTIONS item.

I would be happier with this, I think.

>> The interest of such functionality seems to be limited to people who
>> maintain broken-but-soon-to-be-fixed documents... It does not seem to
>> warrant prime real estate in the exporter IMO. Further, I don't think
>> 'draft' is the correct word for this, but that's of course minor.
>
> I couldn't find anything better. Suggestions welcome.

I agree.  We already have the org-latex-classes, which a very different
from latex-proper classes.  This seems to cause a lot of confusion.
Perhaps we can limit such overlap in the future.

It’s a "relaxed" mode.  I’m not sure what a good term for this is.

Rasmus

-- 
Governments should be afraid of their people

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-08 14:23     ` Rasmus
@ 2015-10-08 16:36       ` Thomas S. Dye
  2015-10-08 16:54         ` Rasmus
                           ` (2 more replies)
  0 siblings, 3 replies; 19+ messages in thread
From: Thomas S. Dye @ 2015-10-08 16:36 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode


Rasmus <rasmus@gmx.us> writes:

>>> The interest of such functionality seems to be limited to people who
>>> maintain broken-but-soon-to-be-fixed documents... It does not seem to
>>> warrant prime real estate in the exporter IMO. Further, I don't think
>>> 'draft' is the correct word for this, but that's of course minor.
>>
>> I couldn't find anything better. Suggestions welcome.
>
> I agree.  We already have the org-latex-classes, which a very different
> from latex-proper classes.  This seems to cause a lot of confusion.
> Perhaps we can limit such overlap in the future.
>
> It’s a "relaxed" mode.  I’m not sure what a good term for this is.

Trial mode?

hth,
Tom

-- 
Thomas S. Dye
http://www.tsdye.com

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-08 16:36       ` Thomas S. Dye
@ 2015-10-08 16:54         ` Rasmus
  2015-10-08 16:57         ` Eric Abrahamsen
  2015-10-08 17:00         ` Nicolas Goaziou
  2 siblings, 0 replies; 19+ messages in thread
From: Rasmus @ 2015-10-08 16:54 UTC (permalink / raw)
  To: emacs-orgmode

Thomas S. Dye <tsd@tsdye.com> writes:

>>>> The interest of such functionality seems to be limited to people who
>>>> maintain broken-but-soon-to-be-fixed documents... It does not seem to
>>>> warrant prime real estate in the exporter IMO. Further, I don't think
>>>> 'draft' is the correct word for this, but that's of course minor.
>>>
>>> I couldn't find anything better. Suggestions welcome.
>>
>> I agree.  We already have the org-latex-classes, which a very different
>> from latex-proper classes.  This seems to cause a lot of confusion.
>> Perhaps we can limit such overlap in the future.
>>
>> It’s a "relaxed" mode.  I’m not sure what a good term for this is.
>
> Trial mode?

The latex flag would be "-interaction=nonstopmode".  Probably a similar
flag exits in GCC.
The Knitr option is simply called error ∈ {TRUE, FALSE}.

I like nonstopmode, but I don’t care particularly for the "interaction"
classification.

Rasmus

-- 
Governments should be afraid of their people

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-08 16:36       ` Thomas S. Dye
  2015-10-08 16:54         ` Rasmus
@ 2015-10-08 16:57         ` Eric Abrahamsen
  2015-10-08 17:00         ` Nicolas Goaziou
  2 siblings, 0 replies; 19+ messages in thread
From: Eric Abrahamsen @ 2015-10-08 16:57 UTC (permalink / raw)
  To: emacs-orgmode

Thomas S. Dye <tsd@tsdye.com> writes:

> Rasmus <rasmus@gmx.us> writes:
>
>>>> The interest of such functionality seems to be limited to people who
>>>> maintain broken-but-soon-to-be-fixed documents... It does not seem to
>>>> warrant prime real estate in the exporter IMO. Further, I don't think
>>>> 'draft' is the correct word for this, but that's of course minor.
>>>
>>> I couldn't find anything better. Suggestions welcome.
>>
>> I agree.  We already have the org-latex-classes, which a very different
>> from latex-proper classes.  This seems to cause a lot of confusion.
>> Perhaps we can limit such overlap in the future.
>>
>> It’s a "relaxed" mode.  I’m not sure what a good term for this is.
>
> Trial mode?

Dry run?

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-08 16:36       ` Thomas S. Dye
  2015-10-08 16:54         ` Rasmus
  2015-10-08 16:57         ` Eric Abrahamsen
@ 2015-10-08 17:00         ` Nicolas Goaziou
  2015-10-08 19:38           ` Eric S Fraga
  2 siblings, 1 reply; 19+ messages in thread
From: Nicolas Goaziou @ 2015-10-08 17:00 UTC (permalink / raw)
  To: Thomas S. Dye; +Cc: emacs-orgmode, Rasmus

Thomas S. Dye <tsd@tsdye.com> writes:

> Rasmus <rasmus@gmx.us> writes:
>
>>>> The interest of such functionality seems to be limited to people who
>>>> maintain broken-but-soon-to-be-fixed documents... It does not seem to
>>>> warrant prime real estate in the exporter IMO. Further, I don't think
>>>> 'draft' is the correct word for this, but that's of course minor.
>>>
>>> I couldn't find anything better. Suggestions welcome.
>>
>> I agree.  We already have the org-latex-classes, which a very different
>> from latex-proper classes.  This seems to cause a lot of confusion.
>> Perhaps we can limit such overlap in the future.
>>
>> It’s a "relaxed" mode.  I’m not sure what a good term for this is.
>
> Trial mode?

I think I will go with the straight to the point
`org-export-barf-on-invalid-link' variable.

However, not sure about the OPTIONS item.

  #+OPTIONS: ???:t

Regards,

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-08 17:00         ` Nicolas Goaziou
@ 2015-10-08 19:38           ` Eric S Fraga
  2015-10-08 20:59             ` Nick Dokos
  0 siblings, 1 reply; 19+ messages in thread
From: Eric S Fraga @ 2015-10-08 19:38 UTC (permalink / raw)
  To: emacs-orgmode

On Thursday,  8 Oct 2015 at 19:00, Nicolas Goaziou wrote:

[...]

> I think I will go with the straight to the point
> `org-export-barf-on-invalid-link' variable.
>
> However, not sure about the OPTIONS item.
>
>   #+OPTIONS: ???:t

#+options: barf:t

;-)

More seriously, I'm a big fan of longer is better if more clear so how
about "invalidlinksok:t" or "allowdanglinglinks:t" or some variation
thereof?

-- 
: Eric S Fraga (0xFFFCF67D), Emacs 25.0.50.2, Org release_8.3.2-161-gd2ac25

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-08 19:38           ` Eric S Fraga
@ 2015-10-08 20:59             ` Nick Dokos
  2015-10-09  8:07               ` Rasmus
  0 siblings, 1 reply; 19+ messages in thread
From: Nick Dokos @ 2015-10-08 20:59 UTC (permalink / raw)
  To: emacs-orgmode

Eric S Fraga <e.fraga@ucl.ac.uk> writes:

> On Thursday,  8 Oct 2015 at 19:00, Nicolas Goaziou wrote:
>
> [...]
>
>> I think I will go with the straight to the point
>> `org-export-barf-on-invalid-link' variable.
>>
>> However, not sure about the OPTIONS item.
>>
>>   #+OPTIONS: ???:t
>
> #+options: barf:t
>
> ;-)
>
> More seriously, I'm a big fan of longer is better if more clear so how
> about "invalidlinksok:t" or "allowdanglinglinks:t" or some variation
> thereof?

Can option names contain dashes?
If so, combine these two ideas to make a name as similar as possible
to the variable name:

#+options: barf-on-invalid-link:t

But I suspect that dashes are not allowed.

--
Nick

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-08 20:59             ` Nick Dokos
@ 2015-10-09  8:07               ` Rasmus
  2015-10-10 14:21                 ` Nicolas Goaziou
  0 siblings, 1 reply; 19+ messages in thread
From: Rasmus @ 2015-10-09  8:07 UTC (permalink / raw)
  To: emacs-orgmode

Nick Dokos <ndokos@gmail.com> writes:

> Eric S Fraga <e.fraga@ucl.ac.uk> writes:
>
>> On Thursday,  8 Oct 2015 at 19:00, Nicolas Goaziou wrote:
>>
>> [...]
>>
>>> I think I will go with the straight to the point
>>> `org-export-barf-on-invalid-link' variable.
>>>
>>> However, not sure about the OPTIONS item.
>>>
>>>   #+OPTIONS: ???:t
>>
>> #+options: barf:t
>>
>> ;-)
>>
>> More seriously, I'm a big fan of longer is better if more clear so how
>> about "invalidlinksok:t" or "allowdanglinglinks:t" or some variation
>> thereof?
>
> Can option names contain dashes?
> If so, combine these two ideas to make a name as similar as possible
> to the variable name:
>
> #+options: barf-on-invalid-link:t

While I agree that options such as 't:·', '^:·', 'h:·' are bad, I think
'barf:·' is nice.  It's short and precise, much like 'num:·'.

Something like 'barf-on-invalid-link' is very easy to mistype or forget.
And yes, I mostly type options by hand.

Rasmus

-- 
Got mashed potatoes. Ain't got no T-Bone. No T-Bone

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-09  8:07               ` Rasmus
@ 2015-10-10 14:21                 ` Nicolas Goaziou
  2015-10-11 12:34                   ` Rasmus
  0 siblings, 1 reply; 19+ messages in thread
From: Nicolas Goaziou @ 2015-10-10 14:21 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode

[-- Attachment #1: Type: text/plain, Size: 669 bytes --]

Rasmus <rasmus@gmx.us> writes:

> While I agree that options such as 't:·', '^:·', 'h:·' are bad, I think
> 'barf:·' is nice.  It's short and precise, much like 'num:·'.
>
> Something like 'barf-on-invalid-link' is very easy to mistype or forget.
> And yes, I mostly type options by hand.

Here's another take on this, which is quite different from the original
draft mode. Now, behaviour on broken links is controlled with
`org-export-with-broken-links' or its OPTIONS counterpart
"broken-links".

It is possible to either error on a broken link, ignore it, or mark it
with an obnoxious string in the output.


Feedback welcome.


Regards,


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-Add-an-option-to-ignore-broken-links.patch --]
[-- Type: text/x-diff, Size: 11552 bytes --]

From 0508461b2d57629e1c391c57a7326093f61b07e6 Mon Sep 17 00:00:00 2001
From: Nicolas Goaziou <mail@nicolasgoaziou.fr>
Date: Sat, 10 Oct 2015 16:03:05 +0200
Subject: [PATCH] ox: Add an option to ignore broken links

* lisp/ox.el (org-export-with-broken-links): New variable.
(org-export-options-alist): Add new OPTIONS item.
(broken-link): New error type.
(org-export-resolve-coderef):
(org-export-resolve-fuzzy-link):
(org-export-resolve-id-link): Raise appropriate error symbol when a link
cannot be resolved.
(org-export-data): Handle new error type.
---
 lisp/ox.el | 221 ++++++++++++++++++++++++++++++++++++-------------------------
 1 file changed, 132 insertions(+), 89 deletions(-)

diff --git a/lisp/ox.el b/lisp/ox.el
index d140f17..d74f48b 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -112,6 +112,7 @@
     (:time-stamp-file nil "timestamp" org-export-time-stamp-file)
     (:with-archived-trees nil "arch" org-export-with-archived-trees)
     (:with-author nil "author" org-export-with-author)
+    (:with-broken-links nil "broken-links" org-export-with-broken-links)
     (:with-clocks nil "c" org-export-with-clocks)
     (:with-creator nil "creator" org-export-with-creator)
     (:with-date nil "date" org-export-with-date)
@@ -797,6 +798,27 @@ is nil.  You can also allow them through local buffer variables."
   :package-version '(Org . "8.0")
   :type 'boolean)
 
+(defcustom org-export-with-broken-links nil
+  "Non-nil means do not raise an error on broken links.
+
+When this variable is non-nil, broken links are ignored, without
+stopping the export process.  If it is set to `mark', broken
+links are marked as such in the output, with a string like
+
+  [BROKEN LINK: path]
+
+where PATH is the un-resolvable reference.
+
+This option can also be set with the OPTIONS keyword, e.g.,
+\"broken-links:mark\"."
+  :group 'org-export-general
+  :version "25.1"
+  :package-version '(Org . "8.4")
+  :type '(choice
+	  (const :tag "Ignore broken links" t)
+	  (const :tag "Mark broken links in output" mark)
+	  (const :tag "Raise an error" nil)))
+
 (defcustom org-export-snippet-translation-alist nil
   "Alist between export snippets back-ends and exporter back-ends.
 
@@ -1851,91 +1873,106 @@ string.  INFO is a plist holding export options.
 
 Return a string."
   (or (gethash data (plist-get info :exported-data))
-      (let* ((type (org-element-type data))
-	     (results
-	      (cond
-	       ;; Ignored element/object.
-	       ((memq data (plist-get info :ignore-list)) nil)
-	       ;; Plain text.
-	       ((eq type 'plain-text)
-		(org-export-filter-apply-functions
-		 (plist-get info :filter-plain-text)
-		 (let ((transcoder (org-export-transcoder data info)))
-		   (if transcoder (funcall transcoder data info) data))
-		 info))
-	       ;; Secondary string.
-	       ((not type)
-		(mapconcat (lambda (obj) (org-export-data obj info)) data ""))
-	       ;; Element/Object without contents or, as a special
-	       ;; case, headline with archive tag and archived trees
-	       ;; restricted to title only.
-	       ((or (not (org-element-contents data))
-		    (and (eq type 'headline)
-			 (eq (plist-get info :with-archived-trees) 'headline)
-			 (org-element-property :archivedp data)))
-		(let ((transcoder (org-export-transcoder data info)))
-		  (or (and (functionp transcoder)
-			   (funcall transcoder data nil info))
-		      ;; Export snippets never return a nil value so
-		      ;; that white spaces following them are never
-		      ;; ignored.
-		      (and (eq type 'export-snippet) ""))))
-	       ;; Element/Object with contents.
-	       (t
-		(let ((transcoder (org-export-transcoder data info)))
-		  (when transcoder
-		    (let* ((greaterp (memq type org-element-greater-elements))
-			   (objectp
-			    (and (not greaterp)
-				 (memq type org-element-recursive-objects)))
-			   (contents
-			    (mapconcat
-			     (lambda (element) (org-export-data element info))
-			     (org-element-contents
-			      (if (or greaterp objectp) data
-				;; Elements directly containing
-				;; objects must have their indentation
-				;; normalized first.
-				(org-element-normalize-contents
-				 data
-				 ;; When normalizing contents of the
-				 ;; first paragraph in an item or
-				 ;; a footnote definition, ignore
-				 ;; first line's indentation: there is
-				 ;; none and it might be misleading.
-				 (when (eq type 'paragraph)
-				   (let ((parent (org-export-get-parent data)))
-				     (and
-				      (eq (car (org-element-contents parent))
-					  data)
-				      (memq (org-element-type parent)
-					    '(footnote-definition item))))))))
-			     "")))
-		      (funcall transcoder data
-			       (if (not greaterp) contents
-				 (org-element-normalize-string contents))
-			       info))))))))
-	;; Final result will be memoized before being returned.
-	(puthash
-	 data
-	 (cond
-	  ((not results) "")
-	  ((memq type '(org-data plain-text nil)) results)
-	  ;; Append the same white space between elements or objects
-	  ;; as in the original buffer, and call appropriate filters.
-	  (t
-	   (let ((results
+      ;; Handle broken links according to
+      ;; `org-export-with-broken-links'.
+      (cl-macrolet
+	  ((broken-link-handler
+	    (&rest body)
+	    `(condition-case err
+		 (progn ,@body)
+	       (broken-link
+		(pcase (plist-get info :with-broken-links)
+		  (`nil (user-error "Unable to resolve link: %S" (nth 1 err)))
+		  (`mark (org-export-data
+			  (format "[BROKEN LINK: %s]" (nth 1 err)) info))
+		  (_ nil))))))
+	(let* ((type (org-element-type data))
+	       (results
+		(cond
+		 ;; Ignored element/object.
+		 ((memq data (plist-get info :ignore-list)) nil)
+		 ;; Plain text.
+		 ((eq type 'plain-text)
 		  (org-export-filter-apply-functions
-		   (plist-get info (intern (format ":filter-%s" type)))
-		   (let ((post-blank (or (org-element-property :post-blank data)
-					 0)))
-		     (if (memq type org-element-all-elements)
-			 (concat (org-element-normalize-string results)
-				 (make-string post-blank ?\n))
-		       (concat results (make-string post-blank ?\s))))
-		   info)))
-	     results)))
-	 (plist-get info :exported-data)))))
+		   (plist-get info :filter-plain-text)
+		   (let ((transcoder (org-export-transcoder data info)))
+		     (if transcoder (funcall transcoder data info) data))
+		   info))
+		 ;; Secondary string.
+		 ((not type)
+		  (mapconcat (lambda (obj) (org-export-data obj info)) data ""))
+		 ;; Element/Object without contents or, as a special
+		 ;; case, headline with archive tag and archived trees
+		 ;; restricted to title only.
+		 ((or (not (org-element-contents data))
+		      (and (eq type 'headline)
+			   (eq (plist-get info :with-archived-trees) 'headline)
+			   (org-element-property :archivedp data)))
+		  (let ((transcoder (org-export-transcoder data info)))
+		    (or (and (functionp transcoder)
+			     (broken-link-handler
+			      (funcall transcoder data nil info)))
+			;; Export snippets never return a nil value so
+			;; that white spaces following them are never
+			;; ignored.
+			(and (eq type 'export-snippet) ""))))
+		 ;; Element/Object with contents.
+		 (t
+		  (let ((transcoder (org-export-transcoder data info)))
+		    (when transcoder
+		      (let* ((greaterp (memq type org-element-greater-elements))
+			     (objectp
+			      (and (not greaterp)
+				   (memq type org-element-recursive-objects)))
+			     (contents
+			      (mapconcat
+			       (lambda (element) (org-export-data element info))
+			       (org-element-contents
+				(if (or greaterp objectp) data
+				  ;; Elements directly containing
+				  ;; objects must have their indentation
+				  ;; normalized first.
+				  (org-element-normalize-contents
+				   data
+				   ;; When normalizing contents of the
+				   ;; first paragraph in an item or
+				   ;; a footnote definition, ignore
+				   ;; first line's indentation: there is
+				   ;; none and it might be misleading.
+				   (when (eq type 'paragraph)
+				     (let ((parent (org-export-get-parent data)))
+				       (and
+					(eq (car (org-element-contents parent))
+					    data)
+					(memq (org-element-type parent)
+					      '(footnote-definition item))))))))
+			       "")))
+			(broken-link-handler
+			 (funcall transcoder data
+				  (if (not greaterp) contents
+				    (org-element-normalize-string contents))
+				  info)))))))))
+	  ;; Final result will be memoized before being returned.
+	  (puthash
+	   data
+	   (cond
+	    ((not results) "")
+	    ((memq type '(org-data plain-text nil)) results)
+	    ;; Append the same white space between elements or objects
+	    ;; as in the original buffer, and call appropriate filters.
+	    (t
+	     (let ((results
+		    (org-export-filter-apply-functions
+		     (plist-get info (intern (format ":filter-%s" type)))
+		     (let ((post-blank (or (org-element-property :post-blank data)
+					   0)))
+		       (if (memq type org-element-all-elements)
+			   (concat (org-element-normalize-string results)
+				   (make-string post-blank ?\n))
+			 (concat results (make-string post-blank ?\s))))
+		     info)))
+	       results)))
+	   (plist-get info :exported-data))))))
 
 (defun org-export-data-with-backend (data backend info)
   "Convert DATA into BACKEND format.
@@ -3990,11 +4027,11 @@ meant to be translated with `org-export-data' or alike."
 ;;
 ;; `org-export-resolve-fuzzy-link' searches destination of fuzzy links
 ;; (i.e. links with "fuzzy" as type) within the parsed tree, and
-;; returns an appropriate unique identifier when found, or nil.
+;; returns an appropriate unique identifier.
 ;;
 ;; `org-export-resolve-id-link' returns the first headline with
 ;; specified id or custom-id in parse tree, the path to the external
-;; file with the id or nil when neither was found.
+;; file with the id.
 ;;
 ;; `org-export-resolve-coderef' associates a reference to a line
 ;; number in the element it belongs, or returns the reference itself
@@ -4002,6 +4039,12 @@ meant to be translated with `org-export-data' or alike."
 ;;
 ;; `org-export-file-uri' expands a filename as stored in :path value
 ;;  of a "file" link into a file URI.
+;;
+;; Broken links raise a `broken-link' error, which is caught by
+;; `org-export-data' for further processing, depending on
+;; `org-export-with-broken-links' value.
+
+(define-error 'broken-link "Unable to resolve link; aborting")
 
 (defun org-export-custom-protocol-maybe (link desc backend)
   "Try exporting LINK with a dedicated function.
@@ -4083,7 +4126,7 @@ error if no block contains REF."
 		  (+ (org-export-get-loc el info) (line-number-at-pos)))
 		 (t (line-number-at-pos)))))))
 	info 'first-match)
-      (user-error "Unable to resolve code reference: %s" ref)))
+      (signal 'broken-link (list ref))))
 
 (defun org-export-resolve-fuzzy-link (link info)
   "Return LINK destination.
@@ -4151,7 +4194,7 @@ significant."
 			   path)
 		    h))
 	     info 'first-match))
-	  (t (user-error "Unable to resolve link \"%s\"" raw-path)))
+	  (t (signal 'broken-link (list raw-path))))
 	 link-cache)))))
 
 (defun org-export-resolve-id-link (link info)
@@ -4172,7 +4215,7 @@ tree or a file name.  Assume LINK type is either \"id\" or
 	  info 'first-match)
 	;; Otherwise, look for external files.
 	(cdr (assoc id (plist-get info :id-alist)))
-	(user-error "Unable to resolve ID \"%s\"" id))))
+	(signal 'broken-link (list id)))))
 
 (defun org-export-resolve-radio-link (link info)
   "Return radio-target object referenced as LINK destination.
-- 
2.6.1


^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-10 14:21                 ` Nicolas Goaziou
@ 2015-10-11 12:34                   ` Rasmus
  2015-10-17 12:54                     ` Nicolas Goaziou
  0 siblings, 1 reply; 19+ messages in thread
From: Rasmus @ 2015-10-11 12:34 UTC (permalink / raw)
  To: emacs-orgmode

Nicolas Goaziou <mail@nicolasgoaziou.fr> writes:

> Here's another take on this, which is quite different from the original
> draft mode. Now, behaviour on broken links is controlled with
> `org-export-with-broken-links' or its OPTIONS counterpart
> "broken-links".
>
> It is possible to either error on a broken link, ignore it, or mark it
> with an obnoxious string in the output.
>
>
> Feedback welcome.

That sounds awesome, though I haven't had time to test it yet.

Thanks,
Rasmus

-- 
Hvor meget poesi tror De kommer ud af et glas isvand?

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] Draft mode
  2015-10-11 12:34                   ` Rasmus
@ 2015-10-17 12:54                     ` Nicolas Goaziou
  0 siblings, 0 replies; 19+ messages in thread
From: Nicolas Goaziou @ 2015-10-17 12:54 UTC (permalink / raw)
  To: Rasmus; +Cc: emacs-orgmode

Hello,

Rasmus <rasmus@gmx.us> writes:

> That sounds awesome, though I haven't had time to test it yet.

Patch pushed to master, along with documentation and tests.


Regards,

-- 
Nicolas Goaziou

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2015-10-17 12:52 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-27  9:38 [RFC] Draft mode Nicolas Goaziou
2015-09-27 11:37 ` Nicolas Goaziou
2015-09-27 16:31   ` Charles C. Berry
2015-09-28 20:56     ` Aaron Ecay
2015-10-07 20:22       ` Nicolas Goaziou
2015-09-27 18:39 ` Rasmus
2015-09-27 20:01   ` Marcin Borkowski
2015-10-07 20:12   ` Nicolas Goaziou
2015-10-08 14:23     ` Rasmus
2015-10-08 16:36       ` Thomas S. Dye
2015-10-08 16:54         ` Rasmus
2015-10-08 16:57         ` Eric Abrahamsen
2015-10-08 17:00         ` Nicolas Goaziou
2015-10-08 19:38           ` Eric S Fraga
2015-10-08 20:59             ` Nick Dokos
2015-10-09  8:07               ` Rasmus
2015-10-10 14:21                 ` Nicolas Goaziou
2015-10-11 12:34                   ` Rasmus
2015-10-17 12:54                     ` Nicolas Goaziou

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).