From mboxrd@z Thu Jan 1 00:00:00 1970 From: Eric Abrahamsen Subject: Templating with Org Date: Thu, 03 Sep 2015 16:49:37 +0800 Message-ID: <87fv2vhopq.fsf@ericabrahamsen.net> Mime-Version: 1.0 Content-Type: text/plain Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:45235) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZXQDY-0001n6-5J for emacs-orgmode@gnu.org; Thu, 03 Sep 2015 04:50:01 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZXQDU-0004uz-4c for emacs-orgmode@gnu.org; Thu, 03 Sep 2015 04:50:00 -0400 Received: from plane.gmane.org ([80.91.229.3]:38562) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZXQDT-0004uY-UI for emacs-orgmode@gnu.org; Thu, 03 Sep 2015 04:49:56 -0400 Received: from list by plane.gmane.org with local (Exim 4.69) (envelope-from ) id 1ZXQDQ-0008WK-PA for emacs-orgmode@gnu.org; Thu, 03 Sep 2015 10:49:53 +0200 Received: from 114.250.134.217 ([114.250.134.217]) by main.gmane.org with esmtp (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Thu, 03 Sep 2015 10:49:52 +0200 Received: from eric by 114.250.134.217 with local (Gmexim 0.1 (Debian)) id 1AlnuQ-0007hv-00 for ; Thu, 03 Sep 2015 10:49:52 +0200 List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org To: emacs-orgmode@gnu.org 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