We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Setting default summary length = 0
I want to disable the default article summary, and only show one if defined in the article metadata. I assumed this would be done with:

```
SUMMARY_MAX_LENGTH = 0
```

But this has the same effect as `SUMMARY_MAX_LENGTH = None`, meaning all content is shown. It does work however by setting a negative value:

```
SUMMARY_MAX_LENGTH = -1
```

I don't know if this is a bug or a feature. If it's a feature, it's probably good to document it. If it's a bug, setting to `0` would in my opinion the most logical way to disable default summaries.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pelican/contents.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 from __future__ import unicode_literals, print_function
3 import six
4 
5 import copy
6 import locale
7 import logging
8 import functools
9 import os
10 import re
11 import sys
12 
13 from datetime import datetime
14 
15 
16 from pelican import signals
17 from pelican.settings import _DEFAULT_CONFIG
18 from pelican.utils import (slugify, truncate_html_words, memoized, strftime,
19                            python_2_unicode_compatible, deprecated_attribute,
20                            path_to_url)
21 
22 # Import these so that they're avalaible when you import from pelican.contents.
23 from pelican.urlwrappers import (URLWrapper, Author, Category, Tag)  # NOQA
24 
25 logger = logging.getLogger(__name__)
26 
27 
28 class Content(object):
29     """Represents a content.
30 
31     :param content: the string to parse, containing the original content.
32     :param metadata: the metadata associated to this page (optional).
33     :param settings: the settings dictionary (optional).
34     :param source_path: The location of the source of this content (if any).
35     :param context: The shared context between generators.
36 
37     """
38     @deprecated_attribute(old='filename', new='source_path', since=(3, 2, 0))
39     def filename():
40         return None
41 
42     def __init__(self, content, metadata=None, settings=None,
43                  source_path=None, context=None):
44         if metadata is None:
45             metadata = {}
46         if settings is None:
47             settings = copy.deepcopy(_DEFAULT_CONFIG)
48 
49         self.settings = settings
50         self._content = content
51         self._context = context
52         self.translations = []
53 
54         local_metadata = dict(settings.get('DEFAULT_METADATA', ()))
55         local_metadata.update(metadata)
56 
57         # set metadata as attributes
58         for key, value in local_metadata.items():
59             if key in ('save_as', 'url'):
60                 key = 'override_' + key
61             setattr(self, key.lower(), value)
62 
63         # also keep track of the metadata attributes available
64         self.metadata = local_metadata
65 
66         #default template if it's not defined in page
67         self.template = self._get_template()
68 
69         # default author to the one in settings if not defined
70         if not hasattr(self, 'author'):
71             if 'AUTHOR' in settings:
72                 self.author = Author(settings['AUTHOR'], settings)
73 
74         # XXX Split all the following code into pieces, there is too much here.
75 
76         # manage languages
77         self.in_default_lang = True
78         if 'DEFAULT_LANG' in settings:
79             default_lang = settings['DEFAULT_LANG'].lower()
80             if not hasattr(self, 'lang'):
81                 self.lang = default_lang
82 
83             self.in_default_lang = (self.lang == default_lang)
84 
85         # create the slug if not existing, from the title
86         if not hasattr(self, 'slug') and hasattr(self, 'title'):
87             self.slug = slugify(self.title)
88 
89         self.source_path = source_path
90 
91         # manage the date format
92         if not hasattr(self, 'date_format'):
93             if hasattr(self, 'lang') and self.lang in settings['DATE_FORMATS']:
94                 self.date_format = settings['DATE_FORMATS'][self.lang]
95             else:
96                 self.date_format = settings['DEFAULT_DATE_FORMAT']
97 
98         if isinstance(self.date_format, tuple):
99             locale_string = self.date_format[0]
100             if sys.version_info < (3, ) and isinstance(locale_string,
101                                                        six.text_type):
102                 locale_string = locale_string.encode('ascii')
103             locale.setlocale(locale.LC_ALL, locale_string)
104             self.date_format = self.date_format[1]
105 
106         if hasattr(self, 'date'):
107             self.locale_date = strftime(self.date, self.date_format)
108 
109         # manage status
110         if not hasattr(self, 'status'):
111             self.status = settings['DEFAULT_STATUS']
112             if not settings['WITH_FUTURE_DATES']:
113                 if hasattr(self, 'date') and self.date > datetime.now():
114                     self.status = 'draft'
115 
116         # store the summary metadata if it is set
117         if 'summary' in metadata:
118             self._summary = metadata['summary']
119 
120         signals.content_object_init.send(self)
121 
122     def __str__(self):
123         if self.source_path is None:
124             return repr(self)
125         elif six.PY3:
126             return self.source_path or repr(self)
127         else:
128             return str(self.source_path.encode('utf-8', 'replace'))
129 
130     def check_properties(self):
131         """Test mandatory properties are set."""
132         for prop in self.mandatory_properties:
133             if not hasattr(self, prop):
134                 raise NameError(prop)
135 
136     @property
137     def url_format(self):
138         """Returns the URL, formatted with the proper values"""
139         metadata = copy.copy(self.metadata)
140         path = self.metadata.get('path', self.get_relative_source_path())
141         metadata.update({
142             'path': path_to_url(path),
143             'slug': getattr(self, 'slug', ''),
144             'lang': getattr(self, 'lang', 'en'),
145             'date': getattr(self, 'date', datetime.now()),
146             'author': getattr(self, 'author', ''),
147             'category': getattr(self, 'category',
148                 self.settings['DEFAULT_CATEGORY']),
149             })
150         return metadata
151 
152     def _expand_settings(self, key):
153         fq_key = ('%s_%s' % (self.__class__.__name__, key)).upper()
154         return self.settings[fq_key].format(**self.url_format)
155 
156     def get_url_setting(self, key):
157         if hasattr(self, 'override_' + key):
158             return getattr(self, 'override_' + key)
159         key = key if self.in_default_lang else 'lang_%s' % key
160         return self._expand_settings(key)
161 
162     def _update_content(self, content, siteurl):
163         """Update the content attribute.
164 
165         Change all the relative paths of the content to relative paths
166         suitable for the ouput content.
167 
168         :param content: content resource that will be passed to the templates.
169         :param siteurl: siteurl which is locally generated by the writer in
170                         case of RELATIVE_URLS.
171         """
172         hrefs = re.compile(r"""
173             (?P<markup><\s*[^\>]*  # match tag with src and href attr
174                 (?:href|src)\s*=)
175 
176             (?P<quote>["\'])      # require value to be quoted
177             (?P<path>\|(?P<what>.*?)\|(?P<value>.*?))  # the url value
178             \2""", re.X)
179 
180         def replacer(m):
181             what = m.group('what')
182             value = m.group('value')
183             origin = m.group('path')
184 
185             # we support only filename for now. the plan is to support
186             # categories, tags, etc. in the future, but let's keep things
187             # simple for now.
188 
189             # XXX Put this in a different location.
190             if what == 'filename':
191                 if value.startswith('/'):
192                     value = value[1:]
193                 else:
194                     # relative to the source path of this content
195                     value = self.get_relative_source_path(
196                         os.path.join(self.relative_dir, value)
197                     )
198 
199                 if value in self._context['filenames']:
200                     origin = '/'.join((siteurl,
201                              self._context['filenames'][value].url))
202                     origin = origin.replace('\\', '/')  # Fow windows paths.
203                 else:
204                     logger.warning("Unable to find {fn}, skipping url"
205                                     " replacement".format(fn=value))
206 
207             return ''.join((m.group('markup'), m.group('quote'), origin,
208                             m.group('quote')))
209 
210         return hrefs.sub(replacer, content)
211 
212     @memoized
213     def get_content(self, siteurl):
214 
215         if hasattr(self, '_get_content'):
216             content = self._get_content()
217         else:
218             content = self._content
219         return self._update_content(content, siteurl)
220 
221     @property
222     def content(self):
223         return self.get_content(self._context['localsiteurl'])
224 
225     def _get_summary(self):
226         """Returns the summary of an article.
227 
228         This is based on the summary metadata if set, otherwise truncate the
229         content.
230         """
231         if hasattr(self, '_summary'):
232             return self._summary
233 
234         if self.settings['SUMMARY_MAX_LENGTH']:
235             return truncate_html_words(self.content,
236                     self.settings['SUMMARY_MAX_LENGTH'])
237         return self.content
238 
239     def _set_summary(self, summary):
240         """Dummy function"""
241         pass
242 
243     summary = property(_get_summary, _set_summary, "Summary of the article."
244                        "Based on the content. Can't be set")
245     url = property(functools.partial(get_url_setting, key='url'))
246     save_as = property(functools.partial(get_url_setting, key='save_as'))
247 
248     def _get_template(self):
249         if hasattr(self, 'template') and self.template is not None:
250             return self.template
251         else:
252             return self.default_template
253 
254     def get_relative_source_path(self, source_path=None):
255         """Return the relative path (from the content path) to the given
256         source_path.
257 
258         If no source path is specified, use the source path of this
259         content object.
260         """
261         if not source_path:
262             source_path = self.source_path
263         if source_path is None:
264             return None
265 
266         return os.path.relpath(
267             os.path.abspath(os.path.join(self.settings['PATH'], source_path)),
268             os.path.abspath(self.settings['PATH'])
269         )
270 
271     @property
272     def relative_dir(self):
273         return os.path.dirname(os.path.relpath(
274             os.path.abspath(self.source_path),
275             os.path.abspath(self.settings['PATH']))
276         )
277 
278 
279 class Page(Content):
280     mandatory_properties = ('title',)
281     default_template = 'page'
282 
283 
284 class Article(Page):
285     mandatory_properties = ('title', 'date', 'category')
286     default_template = 'article'
287 
288 
289 class Quote(Page):
290     base_properties = ('author', 'date')
291 
292 
293 @python_2_unicode_compatible
294 class Static(Page):
295     @deprecated_attribute(old='filepath', new='source_path', since=(3, 2, 0))
296     def filepath():
297         return None
298 
299     @deprecated_attribute(old='src', new='source_path', since=(3, 2, 0))
300     def src():
301         return None
302 
303     @deprecated_attribute(old='dst', new='save_as', since=(3, 2, 0))
304     def dst():
305         return None
306 
307 
308 def is_valid_content(content, f):
309     try:
310         content.check_properties()
311         return True
312     except NameError as e:
313         logger.error("Skipping %s: could not find information about "
314                       "'%s'" % (f, e))
315         return False
316
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pelican/contents.py b/pelican/contents.py
--- a/pelican/contents.py
+++ b/pelican/contents.py
@@ -231,10 +231,11 @@
         if hasattr(self, '_summary'):
             return self._summary
 
-        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'] is None:
+            return self.content
+
+        return truncate_html_words(self.content,
+                self.settings['SUMMARY_MAX_LENGTH'])
 
     def _set_summary(self, summary):
         """Dummy function"""

{"golden_diff": "diff --git a/pelican/contents.py b/pelican/contents.py\n--- a/pelican/contents.py\n+++ b/pelican/contents.py\n@@ -231,10 +231,11 @@\n         if hasattr(self, '_summary'):\n             return self._summary\n \n-        if self.settings['SUMMARY_MAX_LENGTH']:\n-            return truncate_html_words(self.content,\n-                    self.settings['SUMMARY_MAX_LENGTH'])\n-        return self.content\n+        if self.settings['SUMMARY_MAX_LENGTH'] is None:\n+            return self.content\n+\n+        return truncate_html_words(self.content,\n+                self.settings['SUMMARY_MAX_LENGTH'])\n \n     def _set_summary(self, summary):\n         \"\"\"Dummy function\"\"\"\n", "issue": "Setting default summary length = 0\nI want to disable the default article summary, and only show one if defined in the article metadata. I assumed this would be done with:\n\n```\nSUMMARY_MAX_LENGTH = 0\n```\n\nBut this has the same effect as `SUMMARY_MAX_LENGTH = None`, meaning all content is shown. It does work however by setting a negative value:\n\n```\nSUMMARY_MAX_LENGTH = -1\n```\n\nI don't know if this is a bug or a feature. If it's a feature, it's probably good to document it. If it's a bug, setting to `0` would in my opinion the most logical way to disable default summaries.\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals, print_function\nimport six\n\nimport copy\nimport locale\nimport logging\nimport functools\nimport os\nimport re\nimport sys\n\nfrom datetime import datetime\n\n\nfrom pelican import signals\nfrom pelican.settings import _DEFAULT_CONFIG\nfrom pelican.utils import (slugify, truncate_html_words, memoized, strftime,\n                           python_2_unicode_compatible, deprecated_attribute,\n                           path_to_url)\n\n# Import these so that they're avalaible when you import from pelican.contents.\nfrom pelican.urlwrappers import (URLWrapper, Author, Category, Tag)  # NOQA\n\nlogger = logging.getLogger(__name__)\n\n\nclass Content(object):\n    \"\"\"Represents a content.\n\n    :param content: the string to parse, containing the original content.\n    :param metadata: the metadata associated to this page (optional).\n    :param settings: the settings dictionary (optional).\n    :param source_path: The location of the source of this content (if any).\n    :param context: The shared context between generators.\n\n    \"\"\"\n    @deprecated_attribute(old='filename', new='source_path', since=(3, 2, 0))\n    def filename():\n        return None\n\n    def __init__(self, content, metadata=None, settings=None,\n                 source_path=None, context=None):\n        if metadata is None:\n            metadata = {}\n        if settings is None:\n            settings = copy.deepcopy(_DEFAULT_CONFIG)\n\n        self.settings = settings\n        self._content = content\n        self._context = context\n        self.translations = []\n\n        local_metadata = dict(settings.get('DEFAULT_METADATA', ()))\n        local_metadata.update(metadata)\n\n        # set metadata as attributes\n        for key, value in local_metadata.items():\n            if key in ('save_as', 'url'):\n                key = 'override_' + key\n            setattr(self, key.lower(), value)\n\n        # also keep track of the metadata attributes available\n        self.metadata = local_metadata\n\n        #default template if it's not defined in page\n        self.template = self._get_template()\n\n        # default author to the one in settings if not defined\n        if not hasattr(self, 'author'):\n            if 'AUTHOR' in settings:\n                self.author = Author(settings['AUTHOR'], settings)\n\n        # XXX Split all the following code into pieces, there is too much here.\n\n        # manage languages\n        self.in_default_lang = True\n        if 'DEFAULT_LANG' in settings:\n            default_lang = settings['DEFAULT_LANG'].lower()\n            if not hasattr(self, 'lang'):\n                self.lang = default_lang\n\n            self.in_default_lang = (self.lang == default_lang)\n\n        # create the slug if not existing, from the title\n        if not hasattr(self, 'slug') and hasattr(self, 'title'):\n            self.slug = slugify(self.title)\n\n        self.source_path = source_path\n\n        # manage the date format\n        if not hasattr(self, 'date_format'):\n            if hasattr(self, 'lang') and self.lang in settings['DATE_FORMATS']:\n                self.date_format = settings['DATE_FORMATS'][self.lang]\n            else:\n                self.date_format = settings['DEFAULT_DATE_FORMAT']\n\n        if isinstance(self.date_format, tuple):\n            locale_string = self.date_format[0]\n            if sys.version_info < (3, ) and isinstance(locale_string,\n                                                       six.text_type):\n                locale_string = locale_string.encode('ascii')\n            locale.setlocale(locale.LC_ALL, locale_string)\n            self.date_format = self.date_format[1]\n\n        if hasattr(self, 'date'):\n            self.locale_date = strftime(self.date, self.date_format)\n\n        # manage status\n        if not hasattr(self, 'status'):\n            self.status = settings['DEFAULT_STATUS']\n            if not settings['WITH_FUTURE_DATES']:\n                if hasattr(self, 'date') and self.date > datetime.now():\n                    self.status = 'draft'\n\n        # store the summary metadata if it is set\n        if 'summary' in metadata:\n            self._summary = metadata['summary']\n\n        signals.content_object_init.send(self)\n\n    def __str__(self):\n        if self.source_path is None:\n            return repr(self)\n        elif six.PY3:\n            return self.source_path or repr(self)\n        else:\n            return str(self.source_path.encode('utf-8', 'replace'))\n\n    def check_properties(self):\n        \"\"\"Test mandatory properties are set.\"\"\"\n        for prop in self.mandatory_properties:\n            if not hasattr(self, prop):\n                raise NameError(prop)\n\n    @property\n    def url_format(self):\n        \"\"\"Returns the URL, formatted with the proper values\"\"\"\n        metadata = copy.copy(self.metadata)\n        path = self.metadata.get('path', self.get_relative_source_path())\n        metadata.update({\n            'path': path_to_url(path),\n            'slug': getattr(self, 'slug', ''),\n            'lang': getattr(self, 'lang', 'en'),\n            'date': getattr(self, 'date', datetime.now()),\n            'author': getattr(self, 'author', ''),\n            'category': getattr(self, 'category',\n                self.settings['DEFAULT_CATEGORY']),\n            })\n        return metadata\n\n    def _expand_settings(self, key):\n        fq_key = ('%s_%s' % (self.__class__.__name__, key)).upper()\n        return self.settings[fq_key].format(**self.url_format)\n\n    def get_url_setting(self, key):\n        if hasattr(self, 'override_' + key):\n            return getattr(self, 'override_' + key)\n        key = key if self.in_default_lang else 'lang_%s' % key\n        return self._expand_settings(key)\n\n    def _update_content(self, content, siteurl):\n        \"\"\"Update the content attribute.\n\n        Change all the relative paths of the content to relative paths\n        suitable for the ouput content.\n\n        :param content: content resource that will be passed to the templates.\n        :param siteurl: siteurl which is locally generated by the writer in\n                        case of RELATIVE_URLS.\n        \"\"\"\n        hrefs = re.compile(r\"\"\"\n            (?P<markup><\\s*[^\\>]*  # match tag with src and href attr\n                (?:href|src)\\s*=)\n\n            (?P<quote>[\"\\'])      # require value to be quoted\n            (?P<path>\\|(?P<what>.*?)\\|(?P<value>.*?))  # the url value\n            \\2\"\"\", re.X)\n\n        def replacer(m):\n            what = m.group('what')\n            value = m.group('value')\n            origin = m.group('path')\n\n            # we support only filename for now. the plan is to support\n            # categories, tags, etc. in the future, but let's keep things\n            # simple for now.\n\n            # XXX Put this in a different location.\n            if what == 'filename':\n                if value.startswith('/'):\n                    value = value[1:]\n                else:\n                    # relative to the source path of this content\n                    value = self.get_relative_source_path(\n                        os.path.join(self.relative_dir, value)\n                    )\n\n                if value in self._context['filenames']:\n                    origin = '/'.join((siteurl,\n                             self._context['filenames'][value].url))\n                    origin = origin.replace('\\\\', '/')  # Fow windows paths.\n                else:\n                    logger.warning(\"Unable to find {fn}, skipping url\"\n                                    \" replacement\".format(fn=value))\n\n            return ''.join((m.group('markup'), m.group('quote'), origin,\n                            m.group('quote')))\n\n        return hrefs.sub(replacer, content)\n\n    @memoized\n    def get_content(self, siteurl):\n\n        if hasattr(self, '_get_content'):\n            content = self._get_content()\n        else:\n            content = self._content\n        return self._update_content(content, siteurl)\n\n    @property\n    def content(self):\n        return self.get_content(self._context['localsiteurl'])\n\n    def _get_summary(self):\n        \"\"\"Returns the summary of an article.\n\n        This is based on the summary metadata if set, otherwise truncate the\n        content.\n        \"\"\"\n        if hasattr(self, '_summary'):\n            return self._summary\n\n        if self.settings['SUMMARY_MAX_LENGTH']:\n            return truncate_html_words(self.content,\n                    self.settings['SUMMARY_MAX_LENGTH'])\n        return self.content\n\n    def _set_summary(self, summary):\n        \"\"\"Dummy function\"\"\"\n        pass\n\n    summary = property(_get_summary, _set_summary, \"Summary of the article.\"\n                       \"Based on the content. Can't be set\")\n    url = property(functools.partial(get_url_setting, key='url'))\n    save_as = property(functools.partial(get_url_setting, key='save_as'))\n\n    def _get_template(self):\n        if hasattr(self, 'template') and self.template is not None:\n            return self.template\n        else:\n            return self.default_template\n\n    def get_relative_source_path(self, source_path=None):\n        \"\"\"Return the relative path (from the content path) to the given\n        source_path.\n\n        If no source path is specified, use the source path of this\n        content object.\n        \"\"\"\n        if not source_path:\n            source_path = self.source_path\n        if source_path is None:\n            return None\n\n        return os.path.relpath(\n            os.path.abspath(os.path.join(self.settings['PATH'], source_path)),\n            os.path.abspath(self.settings['PATH'])\n        )\n\n    @property\n    def relative_dir(self):\n        return os.path.dirname(os.path.relpath(\n            os.path.abspath(self.source_path),\n            os.path.abspath(self.settings['PATH']))\n        )\n\n\nclass Page(Content):\n    mandatory_properties = ('title',)\n    default_template = 'page'\n\n\nclass Article(Page):\n    mandatory_properties = ('title', 'date', 'category')\n    default_template = 'article'\n\n\nclass Quote(Page):\n    base_properties = ('author', 'date')\n\n\n@python_2_unicode_compatible\nclass Static(Page):\n    @deprecated_attribute(old='filepath', new='source_path', since=(3, 2, 0))\n    def filepath():\n        return None\n\n    @deprecated_attribute(old='src', new='source_path', since=(3, 2, 0))\n    def src():\n        return None\n\n    @deprecated_attribute(old='dst', new='save_as', since=(3, 2, 0))\n    def dst():\n        return None\n\n\ndef is_valid_content(content, f):\n    try:\n        content.check_properties()\n        return True\n    except NameError as e:\n        logger.error(\"Skipping %s: could not find information about \"\n                      \"'%s'\" % (f, e))\n        return False\n", "path": "pelican/contents.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals, print_function\nimport six\n\nimport copy\nimport locale\nimport logging\nimport functools\nimport os\nimport re\nimport sys\n\nfrom datetime import datetime\n\n\nfrom pelican import signals\nfrom pelican.settings import _DEFAULT_CONFIG\nfrom pelican.utils import (slugify, truncate_html_words, memoized, strftime,\n                           python_2_unicode_compatible, deprecated_attribute,\n                           path_to_url)\n\n# Import these so that they're avalaible when you import from pelican.contents.\nfrom pelican.urlwrappers import (URLWrapper, Author, Category, Tag)  # NOQA\n\nlogger = logging.getLogger(__name__)\n\n\nclass Content(object):\n    \"\"\"Represents a content.\n\n    :param content: the string to parse, containing the original content.\n    :param metadata: the metadata associated to this page (optional).\n    :param settings: the settings dictionary (optional).\n    :param source_path: The location of the source of this content (if any).\n    :param context: The shared context between generators.\n\n    \"\"\"\n    @deprecated_attribute(old='filename', new='source_path', since=(3, 2, 0))\n    def filename():\n        return None\n\n    def __init__(self, content, metadata=None, settings=None,\n                 source_path=None, context=None):\n        if metadata is None:\n            metadata = {}\n        if settings is None:\n            settings = copy.deepcopy(_DEFAULT_CONFIG)\n\n        self.settings = settings\n        self._content = content\n        self._context = context\n        self.translations = []\n\n        local_metadata = dict(settings.get('DEFAULT_METADATA', ()))\n        local_metadata.update(metadata)\n\n        # set metadata as attributes\n        for key, value in local_metadata.items():\n            if key in ('save_as', 'url'):\n                key = 'override_' + key\n            setattr(self, key.lower(), value)\n\n        # also keep track of the metadata attributes available\n        self.metadata = local_metadata\n\n        #default template if it's not defined in page\n        self.template = self._get_template()\n\n        # default author to the one in settings if not defined\n        if not hasattr(self, 'author'):\n            if 'AUTHOR' in settings:\n                self.author = Author(settings['AUTHOR'], settings)\n\n        # XXX Split all the following code into pieces, there is too much here.\n\n        # manage languages\n        self.in_default_lang = True\n        if 'DEFAULT_LANG' in settings:\n            default_lang = settings['DEFAULT_LANG'].lower()\n            if not hasattr(self, 'lang'):\n                self.lang = default_lang\n\n            self.in_default_lang = (self.lang == default_lang)\n\n        # create the slug if not existing, from the title\n        if not hasattr(self, 'slug') and hasattr(self, 'title'):\n            self.slug = slugify(self.title)\n\n        self.source_path = source_path\n\n        # manage the date format\n        if not hasattr(self, 'date_format'):\n            if hasattr(self, 'lang') and self.lang in settings['DATE_FORMATS']:\n                self.date_format = settings['DATE_FORMATS'][self.lang]\n            else:\n                self.date_format = settings['DEFAULT_DATE_FORMAT']\n\n        if isinstance(self.date_format, tuple):\n            locale_string = self.date_format[0]\n            if sys.version_info < (3, ) and isinstance(locale_string,\n                                                       six.text_type):\n                locale_string = locale_string.encode('ascii')\n            locale.setlocale(locale.LC_ALL, locale_string)\n            self.date_format = self.date_format[1]\n\n        if hasattr(self, 'date'):\n            self.locale_date = strftime(self.date, self.date_format)\n\n        # manage status\n        if not hasattr(self, 'status'):\n            self.status = settings['DEFAULT_STATUS']\n            if not settings['WITH_FUTURE_DATES']:\n                if hasattr(self, 'date') and self.date > datetime.now():\n                    self.status = 'draft'\n\n        # store the summary metadata if it is set\n        if 'summary' in metadata:\n            self._summary = metadata['summary']\n\n        signals.content_object_init.send(self)\n\n    def __str__(self):\n        if self.source_path is None:\n            return repr(self)\n        elif six.PY3:\n            return self.source_path or repr(self)\n        else:\n            return str(self.source_path.encode('utf-8', 'replace'))\n\n    def check_properties(self):\n        \"\"\"Test mandatory properties are set.\"\"\"\n        for prop in self.mandatory_properties:\n            if not hasattr(self, prop):\n                raise NameError(prop)\n\n    @property\n    def url_format(self):\n        \"\"\"Returns the URL, formatted with the proper values\"\"\"\n        metadata = copy.copy(self.metadata)\n        path = self.metadata.get('path', self.get_relative_source_path())\n        metadata.update({\n            'path': path_to_url(path),\n            'slug': getattr(self, 'slug', ''),\n            'lang': getattr(self, 'lang', 'en'),\n            'date': getattr(self, 'date', datetime.now()),\n            'author': getattr(self, 'author', ''),\n            'category': getattr(self, 'category',\n                self.settings['DEFAULT_CATEGORY']),\n            })\n        return metadata\n\n    def _expand_settings(self, key):\n        fq_key = ('%s_%s' % (self.__class__.__name__, key)).upper()\n        return self.settings[fq_key].format(**self.url_format)\n\n    def get_url_setting(self, key):\n        if hasattr(self, 'override_' + key):\n            return getattr(self, 'override_' + key)\n        key = key if self.in_default_lang else 'lang_%s' % key\n        return self._expand_settings(key)\n\n    def _update_content(self, content, siteurl):\n        \"\"\"Update the content attribute.\n\n        Change all the relative paths of the content to relative paths\n        suitable for the ouput content.\n\n        :param content: content resource that will be passed to the templates.\n        :param siteurl: siteurl which is locally generated by the writer in\n                        case of RELATIVE_URLS.\n        \"\"\"\n        hrefs = re.compile(r\"\"\"\n            (?P<markup><\\s*[^\\>]*  # match tag with src and href attr\n                (?:href|src)\\s*=)\n\n            (?P<quote>[\"\\'])      # require value to be quoted\n            (?P<path>\\|(?P<what>.*?)\\|(?P<value>.*?))  # the url value\n            \\2\"\"\", re.X)\n\n        def replacer(m):\n            what = m.group('what')\n            value = m.group('value')\n            origin = m.group('path')\n\n            # we support only filename for now. the plan is to support\n            # categories, tags, etc. in the future, but let's keep things\n            # simple for now.\n\n            # XXX Put this in a different location.\n            if what == 'filename':\n                if value.startswith('/'):\n                    value = value[1:]\n                else:\n                    # relative to the source path of this content\n                    value = self.get_relative_source_path(\n                        os.path.join(self.relative_dir, value)\n                    )\n\n                if value in self._context['filenames']:\n                    origin = '/'.join((siteurl,\n                             self._context['filenames'][value].url))\n                    origin = origin.replace('\\\\', '/')  # Fow windows paths.\n                else:\n                    logger.warning(\"Unable to find {fn}, skipping url\"\n                                    \" replacement\".format(fn=value))\n\n            return ''.join((m.group('markup'), m.group('quote'), origin,\n                            m.group('quote')))\n\n        return hrefs.sub(replacer, content)\n\n    @memoized\n    def get_content(self, siteurl):\n\n        if hasattr(self, '_get_content'):\n            content = self._get_content()\n        else:\n            content = self._content\n        return self._update_content(content, siteurl)\n\n    @property\n    def content(self):\n        return self.get_content(self._context['localsiteurl'])\n\n    def _get_summary(self):\n        \"\"\"Returns the summary of an article.\n\n        This is based on the summary metadata if set, otherwise truncate the\n        content.\n        \"\"\"\n        if hasattr(self, '_summary'):\n            return self._summary\n\n        if self.settings['SUMMARY_MAX_LENGTH'] is None:\n            return self.content\n\n        return truncate_html_words(self.content,\n                self.settings['SUMMARY_MAX_LENGTH'])\n\n    def _set_summary(self, summary):\n        \"\"\"Dummy function\"\"\"\n        pass\n\n    summary = property(_get_summary, _set_summary, \"Summary of the article.\"\n                       \"Based on the content. Can't be set\")\n    url = property(functools.partial(get_url_setting, key='url'))\n    save_as = property(functools.partial(get_url_setting, key='save_as'))\n\n    def _get_template(self):\n        if hasattr(self, 'template') and self.template is not None:\n            return self.template\n        else:\n            return self.default_template\n\n    def get_relative_source_path(self, source_path=None):\n        \"\"\"Return the relative path (from the content path) to the given\n        source_path.\n\n        If no source path is specified, use the source path of this\n        content object.\n        \"\"\"\n        if not source_path:\n            source_path = self.source_path\n        if source_path is None:\n            return None\n\n        return os.path.relpath(\n            os.path.abspath(os.path.join(self.settings['PATH'], source_path)),\n            os.path.abspath(self.settings['PATH'])\n        )\n\n    @property\n    def relative_dir(self):\n        return os.path.dirname(os.path.relpath(\n            os.path.abspath(self.source_path),\n            os.path.abspath(self.settings['PATH']))\n        )\n\n\nclass Page(Content):\n    mandatory_properties = ('title',)\n    default_template = 'page'\n\n\nclass Article(Page):\n    mandatory_properties = ('title', 'date', 'category')\n    default_template = 'article'\n\n\nclass Quote(Page):\n    base_properties = ('author', 'date')\n\n\n@python_2_unicode_compatible\nclass Static(Page):\n    @deprecated_attribute(old='filepath', new='source_path', since=(3, 2, 0))\n    def filepath():\n        return None\n\n    @deprecated_attribute(old='src', new='source_path', since=(3, 2, 0))\n    def src():\n        return None\n\n    @deprecated_attribute(old='dst', new='save_as', since=(3, 2, 0))\n    def dst():\n        return None\n\n\ndef is_valid_content(content, f):\n    try:\n        content.check_properties()\n        return True\n    except NameError as e:\n        logger.error(\"Skipping %s: could not find information about \"\n                      \"'%s'\" % (f, e))\n        return False\n", "path": "pelican/contents.py"}]}