org_reader.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. """
  2. Org Reader
  3. ==========
  4. Version 1.1.
  5. Relevant Pelican settings:
  6. - ORG_READER_EMACS_LOCATION: Required. Location of Emacs binary.
  7. - ORG_READER_EMACS_SETTINGS: Optional. An absolute path to an Elisp file, to
  8. run per invocation. Useful for initializing the `package` Emacs library if
  9. that's where your Org mode comes from, or any modifications to Org Export-
  10. related variables.
  11. - ORG_READER_BACKEND: Optional. A custom backend to provide to Org. Defaults
  12. to 'html.
  13. To provide metadata to Pelican, the following properties can be defined in
  14. the org file's header:
  15. #+TITLE: The Title Of This BlogPost
  16. #+DATE: 2001-01-01
  17. #+CATEGORY: blog-category
  18. #+AUTHOR: My Name
  19. #+PROPERTY: LANGUAGE en
  20. #+PROPERTY: SUMMARY hello, this is the description
  21. #+PROPERTY: SLUG test_slug
  22. #+PROPERTY: MODIFIED [2015-12-29 Di]
  23. #+PROPERTY: TAGS my, first, tags
  24. #+PROPERTY: SAVE_AS alternative_filename.html
  25. - The TITLE is the only mandatory header property
  26. - Timestamps (DATE and MODIFIED) are optional and can be either a string of
  27. %Y-%m-%d or an org timestamp
  28. - The property names (SUMMARY, SLUG, MODIFIED, TAGS, SAVE_AS) can be either
  29. lower-case or upper-case
  30. - The slug is automatically the filename of the Org file, if not explicitly
  31. specified
  32. - It is not possible to pass an empty property to Pelican. For this plugin,
  33. it makes no difference if a property is present in the Org file and left
  34. empty, or if it is not defined at all.
  35. """
  36. import os
  37. import json
  38. import logging
  39. import subprocess
  40. from pelican import readers
  41. from pelican import signals
  42. ELISP = os.path.join(os.path.dirname(__file__), 'org_reader.el')
  43. LOG = logging.getLogger(__name__)
  44. class OrgReader(readers.BaseReader):
  45. enabled = True
  46. EMACS_ARGS = ["-Q", "--batch"]
  47. ELISP_EXEC = "(org->pelican \"{0}\" {1})"
  48. file_extensions = ['org']
  49. def __init__(self, settings):
  50. super(OrgReader, self).__init__(settings)
  51. assert 'ORG_READER_EMACS_LOCATION' in self.settings, \
  52. "No ORG_READER_EMACS_LOCATION specified in settings"
  53. def read(self, filename):
  54. LOG.info("Reading Org file {0}".format(filename))
  55. cmd = [self.settings['ORG_READER_EMACS_LOCATION']]
  56. cmd.extend(self.EMACS_ARGS)
  57. if 'ORG_READER_EMACS_SETTINGS' in self.settings:
  58. cmd.append('-l')
  59. cmd.append(self.settings['ORG_READER_EMACS_SETTINGS'])
  60. backend = self.settings.get('ORG_READER_BACKEND', "'html")
  61. cmd.append('-l')
  62. cmd.append(ELISP)
  63. cmd.append('--eval')
  64. cmd.append(self.ELISP_EXEC.format(filename, backend))
  65. LOG.debug("OrgReader: running command `{0}`".format(cmd))
  66. json_result = subprocess.check_output(cmd, universal_newlines=True)
  67. json_output = json.loads(json_result)
  68. # get default slug from .org filename
  69. default_slug, _ = os.path.splitext(os.path.basename(filename))
  70. metadata = {'title': json_output['title'] or '',
  71. 'date': json_output['date'] or '',
  72. 'author': json_output['author'] or '',
  73. 'lang': json_output['language'] or '',
  74. 'category': json_output['category'] or '',
  75. 'slug': json_output['slug'] or default_slug,
  76. 'modified': json_output['modified'] or '',
  77. 'tags': json_output['tags'] or '',
  78. 'save_as': json_output['save_as'] or '',
  79. 'summary': json_output['summary'] or ''}
  80. # remove empty strings when necessary
  81. for key in ['save_as', 'modified', 'lang', 'summary']:
  82. if not metadata[key]:
  83. metadata.pop(key)
  84. parsed = {}
  85. for key, value in metadata.items():
  86. parsed[key] = self.process_metadata(key, value)
  87. content = json_output['post']
  88. return content, parsed
  89. def add_reader(readers):
  90. readers.reader_classes['org'] = OrgReader
  91. def register():
  92. signals.readers_init.connect(add_reader)