Is there a way to convince org-mode to export nested blocks as nested <blockquote> elements?

This would be really cool to handle nested quotes in html emails with mu4e.

#+BEGIN_QUOTE
hey ho
#+BEGIN_QUOTE
 hey hey
#+END_QUOTE
ho ho
#+END_QUOTE

Currently the exporter outputs:

<blockquote>
    <p>
        hey ho
    </p>
    <p>
        #+BEGIN<sub>QUOTE</sub>
        hey hey
    </p>
</blockquote>
<p>
    ho ho
</p>
<p>
    #+END<sub>QUOTE</sub>
</p>

While I would expect it to output:

<blockquote>
    <p>
        hey ho
    </p>
    <blockquote>
        <p>
            hey hey
        </p>
    </blockquote>
    <p>
        ho ho
    </p>
</blockquote>
  • 1
    It is (now) unclear to me what you want. Before the last edit by @Unihedron it sounded like you wanted something like <blockquote>foo<blockquote>bar</blockquote>baz</blockquote>. Please edit your question and state exactly (a) what the output currently looks like and (b) what the output produced by the exporter should look like. – itsjeyd Oct 22 '14 at 11:40
  • @itsjeyd true, edited. – Gambhiro Oct 22 '14 at 14:08
up vote 4 down vote accepted

Yes, org-mode can be coerced into exporting nested <blockquote> elements.

1. Create inner quote inside org code block. Add :results html header to code block.

        #+NAME: inner-quote-org
        #+BEGIN_SRC org  :results html replace 

           ,#+BEGIN_QUOTE
           hey hey
           ,#+END_QUOTE

        #+END_SRC

2. Create outer quote inside org code block and put a language specific variable, e.g. ruby syntax, as place holder for inner quote.

        #+NAME: outer-quote-org
        #+BEGIN_SRC org :results html replace

        ,#+BEGIN_QUOTE
        hey ho
        #{inner_quote}
        ho ho
        ,#+END_QUOTE

        #+END_SRC

3. Create new code block, e.g. ruby, with a matching:var header, e.g :var inner_quote="Hello World!". Then add :noweb yes header to insert outer quote block. Add :results header so output will rendered as html.

        #+NAME: nested-quotes-html 
        #+BEGIN_SRC ruby :var inner_quote="Hello World!" :results html replace 
          output = <<-ORGSRC
          <<outer-quote-org()>>
          ORGSRC
        #+END_SRC

Finished code blocks should look similar to example below:

        #+NAME: inner-quote-org
        #+BEGIN_SRC org  :results html replace 

           ,#+BEGIN_QUOTE
           hey hey
           ,#+END_QUOTE

        #+END_SRC

        #+NAME: outer-quote-org
        #+BEGIN_SRC org :results html replace

        ,#+BEGIN_QUOTE
        hey ho
        #{inner_quote}
        ho ho
        ,#+END_QUOTE

        #+END_SRC

        #+NAME: nested-quotes-html 
        #+BEGIN_SRC ruby :var inner_quote="Hello World!" :results html replace 
          output = <<-ORGSRC
          <<outer-quote-org()>>
          ORGSRC
        #+END_SRC

To export nested <blockquote> as HTML, use #+CALL: with :results html.

        #+CALL: nested-quotes-html(inner_quote=inner-quote-org()) :results html replace

Notice: inner_quote=inner-quote-org() is how we dynamically set value of inner quote after outer quote was rendered as html.


Result exported as HTML using C-c C-e h H

        <blockquote>
        <p>
        hey ho
        <blockquote>
        <p>
        hey hey
        </p>
        </blockquote>

        ho ho
        </p>
        </blockquote>

Hope that helped!

I use a similar technique to create dynamic nested code templates using org code blocks that send output to a drawer. It works really well but I'm always looking for a better method.


Code tested using

GNU Emacs 24.4.1 (x86_64-apple-darwin14.0.0, NS apple-appkit-1343.14)

Org-mode version 8.3.1 (8.3.1-87-g4ef2c0-elpa)

You get the correct and expected output. Per the Org Syntax, (Greater) Elements cannot contain (Greater) Elements of the same type. Please refer to the Org Syntax. You are free to install violations/hacks of the syntax via filters but I think it won't be easy in this case.

The Syntax makes sense. I only know your expected output of the given input because you told me! There is not a unique way to match the #+BEGIN_QUOTE and #+END_QUOTE in your example and naturally the first match is assumed to be the matching ending.

Alternatively, you can use another special block within your quote block and fix it with a filter ex post.

(defun rasmus/org-html-myquote-to-quote (block backend info)
  (when (org-export-derived-backend-p backend 'html)
    (when (string-match "\\`<div class=\"MYQUOTE\">" block)
      (setq block (replace-match "<blockquote>" t nil block))
      (string-match "</div>\n\\'" block)
      (setq block (replace-match "</blockquote>\n" t nil block))
      block)))

(let ((org-export-filter-special-block-functions
      '(rasmus/org-html-myquote-to-quote)))
    (with-temp-buffer
      (insert "
#+BEGIN_QUOTE
hey ho
#+BEGIN_MYQUOTE
 hey hey
#+END_MYQUOTE
#+BEGIN_ANOTHER_SPECIAL_BLOCK
is preserved as is
#+END_ANOTHER_SPECIAL_BLOCK
ho ho
#+END_QUOTE")
      (org-html-export-as-html nil nil nil t)))

=>

<blockquote>
<p>
hey ho
</p>
<blockquote>
<p>
hey hey
</p>

</blockquote>
<div class="ANOTHER_SPECIAL_BLOCK">
<p>
is preserved as is
</p>

</div>
<p>
ho ho
</p>
</blockquote>

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.