diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 7a7eadfb..8338d608 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -218,19 +218,14 @@ Note that, aside from the title, none of this metadata is mandatory: if the date is not specified and DEFAULT_DATE is 'fs', Pelican will rely on the file's "mtime" timestamp, and the category can be determined by the directory in which the file resides. For example, a file located at ``python/foobar/myfoobar.rst`` -will have a category of ``foobar``. +will have a category of ``foobar``. If you would like to organize your files in +other ways where the name of the subfolder would not be a good category name, +you can set the setting ``USE_FOLDER_AS_CATEGORY`` to ``False``. -Note that, aside from the title and date, none of this metadata is mandatory. -If the date is not specified and you have ``DEFAULT_DATE`` set, Pelican will -use that instead, making the ``date`` metadata attribute optional. The category -can be determined by the directory in which the file resides. For example, a -file located at ``python/foobar/myfoobar.rst`` will have a category of -``foobar``. If you would like to organize your files in other ways where the -name of the subfolder would not be a good category name, you can set the -setting ``USE_FOLDER_AS_CATEGORY`` to ``False``. If there is no summary -metadata for a given post, the ``SUMMARY_MAX_LENGTH`` setting can be used to -specify how many words from the beginning of an article are used as the -summary. +If there is no summary metadata for a given post, the ``SUMMARY_MAX_LENGTH`` +setting can be used to specify how many words from the beginning of an article +are used as the summary. Summaries can also be specified inline with the body +using the :ref:`Summary Plugin `. You can also extract any metadata from the filename through a regular expression to be set in the ``FILENAME_METADATA`` setting. diff --git a/docs/plugins.rst b/docs/plugins.rst index 43128176..77b114cf 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -111,6 +111,7 @@ The following plugins are currently included with Pelican: * `HTML tags for reStructuredText`_ ``pelican.plugins.html_rst_directive`` * `Related posts`_ ``pelican.plugins.related_posts`` * `Sitemap`_ ``pelican.plugins.sitemap`` +* `Summary`_ ``pelican.plugins.summary`` Ideas for plugins that haven't been written yet: @@ -371,3 +372,34 @@ Here is an example configuration (it's also the default settings): 'pages': 'monthly' } } + +.. _plugin-summary: + +Summary +------------- + +This plugin allows easy, variable length summaries directly embedded into the +body of your articles. It introduces two new settings: ``SUMMARY_BEGIN_MARKER`` +and ``SUMMARY_END_MARKER``: strings which can be placed directly into an article +to mark the beginning and end of a summary. When found, the standard +``SUMMARY_MAX_LENGTH`` setting will be ignored. The markers themselves will also +be removed from your articles before they are published. The default values +are ```` and ````. +For example:: + + Title: My super title + Date: 2010-12-03 10:20 + Tags: thats, awesome + Category: yeah + Slug: my-super-post + Author: Alexis Metaireau + + This is the content of my super blog post. + + and this content occurs after the summary. + +Here, the summary is taken to be the first line of the post. Because no +beginning marker was found, it starts at the top of the body. It is possible +to leave out the end marker instead, in which case the summary will start at the +beginning marker and continue to the end of the body. + diff --git a/pelican/contents.py b/pelican/contents.py index d7d7e558..8a9be817 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -201,11 +201,11 @@ class Page(object): if it is set, else truncate the content.""" if hasattr(self, '_summary'): return self._summary - else: - if self.settings['SUMMARY_MAX_LENGTH']: - return truncate_html_words(self.content, - self.settings['SUMMARY_MAX_LENGTH']) - return self.content + + if self.settings['SUMMARY_MAX_LENGTH']: + return truncate_html_words(self.content, + self.settings['SUMMARY_MAX_LENGTH']) + return self.content def _set_summary(self, summary): """Dummy function""" diff --git a/pelican/plugins/summary.py b/pelican/plugins/summary.py new file mode 100644 index 00000000..cd433b7c --- /dev/null +++ b/pelican/plugins/summary.py @@ -0,0 +1,62 @@ +import types + +from pelican import signals + +def initialized(pelican): + from pelican.settings import _DEFAULT_CONFIG + _DEFAULT_CONFIG.setdefault('SUMMARY_BEGIN_MARKER', + '') + _DEFAULT_CONFIG.setdefault('SUMMARY_END_MARKER', + '') + if pelican: + pelican.settings.setdefault('SUMMARY_BEGIN_MARKER', + '') + pelican.settings.setdefault('SUMMARY_END_MARKER', + '') + +def content_object_init(PageClass, instance): + # if summary is already specified, use it + if 'summary' in instance.metadata: + return + + try: + content = instance.content + except: + # in some tests, this fails because a context has not been set + return + + # monkey patch a new function around get_content that removes summary + # markers + prev_get_content = instance.get_content + def get_content(self, siteurl): + content = prev_get_content(siteurl) + self.settings['SUMMARY_BEGIN_MARKER'] = '' + self.settings['SUMMARY_END_MARKER'] = '' + if self.settings['SUMMARY_BEGIN_MARKER']: + content = content.replace( + self.settings['SUMMARY_BEGIN_MARKER'], '', 1) + if self.settings['SUMMARY_END_MARKER']: + content = content.replace( + self.settings['SUMMARY_END_MARKER'], '', 1) + return content + instance.get_content = types.MethodType(get_content, instance) + + # extract out our summary + begin_summary = -1 + end_summary = -1 + if instance.settings['SUMMARY_BEGIN_MARKER']: + begin_summary = content.find(instance.settings['SUMMARY_BEGIN_MARKER']) + if instance.settings['SUMMARY_END_MARKER']: + end_summary = content.find(instance.settings['SUMMARY_END_MARKER']) + if begin_summary != -1 or end_summary != -1: + # the beginning position has to take into account the length + # of the marker + begin_summary = (begin_summary + + len(instance.settings['SUMMARY_BEGIN_MARKER']) + if begin_summary != -1 else 0) + end_summary = end_summary if end_summary != -1 else None + instance._summary = content[begin_summary:end_summary] + +def register(): + signals.initialized.connect(initialized) + signals.content_object_init.connect(content_object_init) diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 0a5be921..9d6d43e6 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -4,8 +4,10 @@ import os import tempfile +from pelican.contents import Page from pelican.plugins import gzip_cache +from .test_contents import TEST_CONTENT, TEST_SUMMARY from .support import unittest, temporary_folder class TestGzipCache(unittest.TestCase): @@ -34,3 +36,65 @@ class TestGzipCache(unittest.TestCase): gzip_cache.create_gzip_file(a_html_filename) self.assertTrue(os.path.exists(a_html_filename + '.gz')) +class TestSummary(unittest.TestCase): + def setUp(self): + super(TestSummary, self).setUp() + + from pelican.plugins import summary + + summary.register() + summary.initialized(None) + self.page_kwargs = { + 'content': TEST_CONTENT, + 'context': { + 'localsiteurl': '', + }, + 'metadata': { + 'summary': TEST_SUMMARY, + 'title': 'foo bar', + 'author': 'Blogger', + }, + } + + def _copy_page_kwargs(self): + # make a deep copy of page_kwargs + page_kwargs = dict([(key, self.page_kwargs[key]) for key in + self.page_kwargs]) + for key in page_kwargs: + if not isinstance(page_kwargs[key], dict): + break + page_kwargs[key] = dict([(subkey, page_kwargs[key][subkey]) + for subkey in page_kwargs[key]]) + + return page_kwargs + + def test_end_summary(self): + page_kwargs = self._copy_page_kwargs() + del page_kwargs['metadata']['summary'] + page_kwargs['content'] = ( + TEST_SUMMARY + '' + TEST_CONTENT) + page = Page(**page_kwargs) + # test both the summary and the marker removal + self.assertEqual(page.summary, TEST_SUMMARY) + self.assertEqual(page.content, TEST_SUMMARY + TEST_CONTENT) + + def test_begin_summary(self): + page_kwargs = self._copy_page_kwargs() + del page_kwargs['metadata']['summary'] + page_kwargs['content'] = ( + 'FOOBAR' + TEST_CONTENT) + page = Page(**page_kwargs) + # test both the summary and the marker removal + self.assertEqual(page.summary, TEST_CONTENT) + self.assertEqual(page.content, 'FOOBAR' + TEST_CONTENT) + + def test_begin_end_summary(self): + page_kwargs = self._copy_page_kwargs() + del page_kwargs['metadata']['summary'] + page_kwargs['content'] = ( + 'FOOBAR' + TEST_SUMMARY + + '' + TEST_CONTENT) + page = Page(**page_kwargs) + # test both the summary and the marker removal + self.assertEqual(page.summary, TEST_SUMMARY) + self.assertEqual(page.content, 'FOOBAR' + TEST_SUMMARY + TEST_CONTENT)