Repository: getpelican/pelican

Issue:
Add support for Google feedburner
Hi all,

Do you have any plan to add support for [Google feedburner](http://feedburner.google.com/)? It is very useful to track feed subscribers, provide a HTMLized feed page and so on. To use this, Pelican need to show a feedburner's address to visitors while providing a feed to the feedburner as before.

Regards.



Discussion:
It seems really simple to do: add a setting in your settings file and update the theme to use it if defined. Do you want to give it a try? I would be glad to merge it

Any news ?

In thinking about potential ways of implementing this capability, it occurred to me that it probably can and should be accomplished in a way that's not specific to FeedBurner. For example, let's say you want to host your feed(s) on a sub-domain, such as http://feeds.example.com/ -- as it stands now, the existing Jinja templates assume you always want to use the same fully-qualified domain name (FQDN) as your main site (e.g., www.example.com).

Even for those who are planning to use FeedBurner, there are [good reasons](http://www.scottw.com/feedburner-cname) to use your own sub-domain for this purpose.

To that end, I have added the following entries to my settings file:

```
FEED_DOMAIN = "http://feeds.example.com"  # default -> SITEURL
FEED_MAIN_URL = "main"                    # default -> FEED
FEED_RSS_MAIN_URL = "main-rss"            # default -> FEED_RSS
```

I refer to the latter two as "main" feed URLs in order to distinguish the `all.atom.xml` and `all.rss.xml` feeds from the other tag, category, and language feed types. So in this particular case, the goal is for the main Atom feed to be available at: http://feeds.example.com/main

The relevant `<head>` links in the `base.html` template look like:

```
<link href="{{ FEED_DOMAIN }}/{{ FEED_MAIN_URL }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} Atom Feed" />
{% if FEED_RSS %}
<link href="{{ FEED_DOMAIN }}/{{ FEED_RSS_MAIN_URL }}" type="application/atom+xml" rel="alternate" title="{{ SITENAME }} RSS Feed" />
{% endif %}
```

... producing, in our example case, the following rendered link:

```
<link href="http://feeds.example.com/main" type="application/atom+xml" rel="alternate" title="ExampleBlog Atom Feed" />
```

Any thoughts on this before I issue a pull request?


Resolving patch:
diff --git a/pelican/settings.py b/pelican/settings.py
--- a/pelican/settings.py
+++ b/pelican/settings.py
@@ -22,6 +22,7 @@
                    'STATIC_PATHS': ['images', ],
                    'THEME_STATIC_PATHS': ['static', ],
                    'FEED': 'feeds/all.atom.xml',
+                   'FEED_MAIN_URL': 'feeds/all.atom.xml',
                    'CATEGORY_FEED': 'feeds/%s.atom.xml',
                    'TRANSLATION_FEED': 'feeds/all-%s.atom.xml',
                    'FEED_MAX_ITEMS': '',
@@ -71,26 +72,45 @@
 
 
 def read_settings(filename=None):
+    if filename:
+        local_settings = get_settings_from_file(filename)
+    else:
+        local_settings = _DEFAULT_CONFIG
+    configured_settings = configure_settings(local_settings, None, filename)
+    return configured_settings
+
+
+def get_settings_from_file(filename, default_settings=None):
     """Load a Python file into a dictionary.
     """
-    context = _DEFAULT_CONFIG.copy()
+    if default_settings == None:
+        default_settings = _DEFAULT_CONFIG
+    context = default_settings.copy()
     if filename:
         tempdict = {}
         execfile(filename, tempdict)
         for key in tempdict:
             if key.isupper():
                 context[key] = tempdict[key]
+    return context
+
 
-        # Make the paths relative to the settings file
+def configure_settings(settings, default_settings=None, filename=None):
+    """Provide optimizations, error checking, and warnings for loaded settings"""
+    if default_settings is None:
+        default_settings = _DEFAULT_CONFIG
+
+    # Make the paths relative to the settings file
+    if filename:
         for path in ['PATH', 'OUTPUT_PATH']:
-            if path in context:
-                if context[path] is not None and not isabs(context[path]):
-                    context[path] = os.path.abspath(os.path.normpath(
-                        os.path.join(os.path.dirname(filename), context[path]))
+            if path in settings:
+                if settings[path] is not None and not isabs(settings[path]):
+                    settings[path] = os.path.abspath(os.path.normpath(
+                        os.path.join(os.path.dirname(filename), settings[path]))
                     )
 
     # if locales is not a list, make it one
-    locales = context['LOCALE']
+    locales = settings['LOCALE']
 
     if isinstance(locales, basestring):
         locales = [locales]
@@ -108,11 +128,20 @@ def read_settings(filename=None):
     else:
         logger.warn("LOCALE option doesn't contain a correct value")
 
-    if not 'TIMEZONE' in context:
+    # If SITEURL is defined but FEED_DOMAIN isn't, set FEED_DOMAIN = SITEURL
+    if ('SITEURL' in settings) and (not 'FEED_DOMAIN' in settings):
+        settings['FEED_DOMAIN'] = settings['SITEURL']
+
+    # Warn if feeds are generated with both SITEURL & FEED_DOMAIN undefined
+    if (('FEED' in settings) or ('FEED_RSS' in settings)) and (not 'FEED_DOMAIN' in settings):
+        logger.warn("Since feed URLs should always be absolute, you should specify "
+                 "FEED_DOMAIN in your settings. (e.g., 'FEED_DOMAIN = "
+                 "http://www.example.com')")
+
+    if not 'TIMEZONE' in settings:
         logger.warn("No timezone information specified in the settings. Assuming"
                  " your timezone is UTC for feed generation. Check "
                  "http://docs.notmyidea.org/alexis/pelican/settings.html#timezone "
                  "for more information")
 
-    # set the locale
-    return context
+    return settings
diff --git a/samples/pelican.conf.py b/samples/pelican.conf.py
--- a/samples/pelican.conf.py
+++ b/samples/pelican.conf.py
@@ -29,7 +29,7 @@
 DEFAULT_METADATA = (('yeah', 'it is'),)
 
 # static paths will be copied under the same name
-STATIC_PATHS = ["pictures",]
+STATIC_PATHS = ["pictures", ]
 
 # A list of files to copy from the source to the destination
 FILES_TO_COPY = (('extra/robots.txt', 'robots.txt'),)
@@ -37,4 +37,3 @@
 # foobar will not be used, because it's not in caps. All configuration keys
 # have to be in caps
 foobar = "barbaz"
-


Test patch:
diff --git a/tests/test_settings.py b/tests/test_settings.py
--- a/tests/test_settings.py
+++ b/tests/test_settings.py
@@ -1,12 +1,13 @@
 from os.path import dirname, abspath, join
 
-from pelican.settings import read_settings, _DEFAULT_CONFIG
+from pelican.settings import read_settings, configure_settings, _DEFAULT_CONFIG
 from .support import unittest
 
 
-class TestSettingsFromFile(unittest.TestCase):
-    """Providing a file, it should read it, replace the default values and
-    append new values to the settings, if any
+class TestSettingsConfiguration(unittest.TestCase):
+    """Provided a file, it should read it, replace the default values,
+    append new values to the settings (if any), and apply basic settings
+    optimizations.
     """
     def setUp(self):
         self.PATH = abspath(dirname(__file__))
@@ -31,3 +32,15 @@ def test_read_empty_settings(self):
         """providing no file should return the default values."""
         settings = read_settings(None)
         self.assertDictEqual(settings, _DEFAULT_CONFIG)
+
+    def test_configure_settings(self):
+        """Manipulations to settings should be applied correctly."""
+
+        # FEED_DOMAIN, if undefined, should default to SITEURL
+        settings = {'SITEURL': 'http://blog.notmyidea.org', 'LOCALE': ''}
+        configure_settings(settings)
+        self.assertEqual(settings['FEED_DOMAIN'], 'http://blog.notmyidea.org')
+
+        settings = {'FEED_DOMAIN': 'http://feeds.example.com', 'LOCALE': ''}
+        configure_settings(settings)
+        self.assertEqual(settings['FEED_DOMAIN'], 'http://feeds.example.com')