emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Eric Abrahamsen <eric@ericabrahamsen.net>
To: emacs-orgmode@gnu.org
Subject: Templating with Org
Date: Thu, 03 Sep 2015 16:49:37 +0800	[thread overview]
Message-ID: <87fv2vhopq.fsf@ericabrahamsen.net> (raw)

A while ago I asked about creating a system that repeats body text for
many Org headings -- essentially writing an export template that is
re-used for many headings. Search through Gmane is tedious and strangely
inaccurate, so I'm not bothering to find the original thread.

I've come up with a solution that others might find useful -- I'm also
hoping for some tips on improving it. I suppose I can add it to Worg
later.

I'll paste the function at the bottom, but what I'm talking about is
this:

#+BEGIN_SOURCE org
Templating Test
:PROPERTIES:
:EXPORT_BODY_TEMPLATE: template-heading
:END:
** Bob Jones
:PROPERTIES:
:SALUTATION: Dear Mr Jones
:MARMOTS:  390
:END:
** Janet Smith
:PROPERTIES:
:SALUTATION: Dear Prof. Smith
:MARMOTS:  9
:END:
** Unknown
:PROPERTIES:
:SALUTATION: Dear Sir/Madame
:MARMOTS:  42
:END:
Also, would you at your earliest convenience please remind us of your
name?
* Template body                                                    :noexport:
:PROPERTIES:
:ID:       template-heading
:END:
{{{property(SALUTATION)}}},

This is an automatically-generated form letter, informing you that you
have {{{property(MARMOTS)}}} marmots remaining in your account at the
United Bank of Marmots.

@@body@@

Yours truly,\\
Director of the United Bank of Marmots
#+END_SOURCE

When this is exported, the body of the "Template body" heading is
switched in for the bodies of each of the children of "Templating test".
That lets you use property macros to fill in the template context. As a
special case, the string "@@body@@" will be replaced with the original
body of the exported heading, if there is one.

The function below works fine, though I'd sure like to know if anyone
has any suggestions for cleaning it up, particular the bits about
finding and extracting heading bodies.

More unfortunately, it doesn't work if you restrict the export scope to
the "Templating Test" subtree, because the "EXPORT_BODY_TEMPLATE"
property isn't seen. I'd sure like to find a way to fix that.

I suppose one solution would be to explicitly add EXPORT_BODY_TEMPLATE
to the list of inherited properties, and then remove the inner
`org-map-entries'. But one of the things I like about the current
arrangement is that it is limited to direct children of the heading with
that property set.

Anyway, comments and improvements welcome!

Eric


#+BEGIN_SOURCE emacs-lisp
(defun org-template-replace (backend)
  "Do template body replacement in the exported region.

For any heading that has the EXPORT_BODY_TEMPLATE property set,
treat the value of that property as an org heading id, find that
heading, get its body text, and replace the body text of each of
this heading's children with that text.

This can be used to write a single export template which is then
used for many headings."
  (let ((org-use-property-inheritance nil))
    (org-map-entries
     (lambda ()
       (let ((targ (org-entry-get (point) "EXPORT_BODY_TEMPLATE"))
	     (level (org-current-level))
	     original-body template-body)
	 (when targ
	   (setq template-body
		 (save-excursion
		   (org-id-goto targ)
		   (org-end-of-meta-data t)
		   (when (or (looking-at org-heading-regexp)
			     (= (point) (point-max)))
		     (user-error "Template heading has no body text."))
		   (buffer-substring-no-properties
		    (point)
		    (progn (org-next-visible-heading 1)
			   (point)))))
	   (save-restriction
	     (org-narrow-to-subtree)
	     (org-map-entries
	      (lambda ()
		(org-end-of-meta-data t)
		(cond
		 ((looking-at org-heading-regexp)
		  (open-line 1))
		 ((= (point) (point-max))
		  (newline))
		 (t (setq original-body
			  (delete-and-extract-region
			   (point)
			   (progn
			     (save-excursion
			       (org-next-visible-heading 1)
			       (point)))))))
		
		(insert
		 (replace-regexp-in-string
		  (if original-body
		      "@@body@@"
		    "@@body@@\n\n") (or original-body "") template-body))
		(setq org-map-continue-from (1- (point))))
	      (format "LEVEL=%d" (1+ level)))))))
     "EXPORT_BODY_TEMPLATE<>\"\"")))

(add-hook 'org-export-before-processing-hook #'org-template-replace)

#+END_SOURCE

                 reply	other threads:[~2015-09-03  8:50 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87fv2vhopq.fsf@ericabrahamsen.net \
    --to=eric@ericabrahamsen.net \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).