Repository: django-cms/django-cms

Issue:
Failure to configure django-cms prevents syncdb
As soon as 'cms' is added to _settings.INSTALLED_APPS_ the configuration values must be added. Specifically, _CMS_TEMPLATES_ as without it, syncdb fails with an error unrelated to the actual running of schema creation.

**File "/blah/blah/cms/conf/patch.py", line 27, in post_patch
    settings.CMS_TEMPLATES = tuple(settings.CMS_TEMPLATES) + (
TypeError: 'NoneType' object is not iterable**

I can see the validity of requiring the user to configure the system properly -- lord knows it won't work without it -- but as far as I can tell, CMS_TEMPLATES is not explicitly required in the database setup (because its stored in a field on cms_page), and if it is going to error, it should probably do so in a way that explains the problem to the user.

This was tested against b14c8b924bdeb655580826abaa5034e2309aa564



Discussion:

Resolving patch:
diff --git a/cms/conf/global_settings.py b/cms/conf/global_settings.py
--- a/cms/conf/global_settings.py
+++ b/cms/conf/global_settings.py
@@ -12,8 +12,9 @@
 SITE_ID = 1
 
 # Which templates should be used for extracting the placeholders?
+# Empty by default, as we don't impose any rigid requirements on users.
 # example: CMS_TEMPLATES = (('base.html', 'default template'),)
-CMS_TEMPLATES = None
+CMS_TEMPLATES = ()
 
 # Should pages be allowed to inherit their parent templates?
 CMS_TEMPLATE_INHERITANCE = True
diff --git a/cms/conf/patch.py b/cms/conf/patch.py
--- a/cms/conf/patch.py
+++ b/cms/conf/patch.py
@@ -33,7 +33,11 @@ def post_patch_check():
     """Post patch check, just make sure there isn't any misconfiguration. All
     the code for checking settings should go here.
     """
-    if settings.CMS_TEMPLATES is None:
+
+    # Ensure templates are set, and more than just the inheritance setting.
+    cms_templates_length = len(settings.CMS_TEMPLATES)
+    if (cms_templates_length < 1 or
+        (cms_templates_length == 1 and settings.CMS_TEMPLATES[0][0] == settings.CMS_TEMPLATE_INHERITANCE_MAGIC)):
         raise ImproperlyConfigured('Please make sure you specified a CMS_TEMPLATES setting.')
     
     # check if is user middleware installed


Test patch:
diff --git a/cms/tests/settings.py b/cms/tests/settings.py
--- a/cms/tests/settings.py
+++ b/cms/tests/settings.py
@@ -1,7 +1,75 @@
 # -*- coding: utf-8 -*-
 from __future__ import with_statement
+from django.core.exceptions import ImproperlyConfigured
+from cms.conf.patch import post_patch, post_patch_check
+from cms.conf.global_settings import CMS_TEMPLATE_INHERITANCE_MAGIC
+from cms.test_utils.util.context_managers import SettingsOverride
 from cms.test_utils.testcases import CMSTestCase
 
-
 class SettingsTests(CMSTestCase):
-    pass
+    def test_cms_templates_length(self):
+        '''
+        Ensure that the correct exception is raised when CMS_TEMPLATES is
+        configured with an empty tuple or the magic value 'INHERIT'
+        '''
+        improperly_configured_template_tests = (
+            # don't allow 0 length
+            (),
+
+            # don't allow length of 1 if the only value is the magic inheritance
+            ((CMS_TEMPLATE_INHERITANCE_MAGIC, None),),
+        )
+        for value_to_test in improperly_configured_template_tests:
+            with SettingsOverride(DEBUG=True, CMS_TEMPLATES=value_to_test):
+                self.assertRaises(ImproperlyConfigured, post_patch_check)
+
+    def test_cms_templates_none(self):
+        '''
+        In fixing #814, CMS_TEMPLATES default after patching changes from None
+        to an empty tuple. As such, If the user has decided to set None for some
+        reason, this test lets us know what to expect.
+        As it stands, we should get a TypeError because post_patch attempts to
+        turn None into a tuple explicitly.
+        '''
+
+        # with CMS_TEMPLATE_INHERITANCE we step into an if statement that errors
+        with SettingsOverride(DEBUG=True, CMS_TEMPLATE_INHERITANCE=True, CMS_TEMPLATES=None):
+            self.assertRaises(TypeError, post_patch)
+
+        # without CMS_TEMPLATE_INHERITANCE enabled, the function should return nothing
+        with SettingsOverride(DEBUG=True, CMS_TEMPLATE_INHERITANCE=False, CMS_TEMPLATES=None):
+            self.assertEqual(None, post_patch())
+
+    def test_cms_templates_valid(self):
+        '''
+        These are all formats that should be valid, thus return nothing when DEBUG is True.
+        '''
+        success_template_tests = (
+            # one valid template
+            (('col_two.html', 'two columns'),),
+
+            # two valid templates
+            (('col_two.html', 'two columns'),
+             ('col_three.html', 'three columns'),),
+
+            # three valid templates
+            (('col_two.html', 'two columns'),
+             ('col_three.html', 'three columns'),
+             ('nav_playground.html', 'navigation examples'),),
+
+            # three valid templates + inheritance
+            (('col_two.html', 'two columns'),
+             ('col_three.html', 'three columns'),
+             ('nav_playground.html', 'navigation examples'),
+             (CMS_TEMPLATE_INHERITANCE_MAGIC, None),),
+
+            # same valid templates as above, ensuring we don't short circuit when inheritance
+            # magic comes first.
+            ((CMS_TEMPLATE_INHERITANCE_MAGIC, None),
+             ('col_two.html', 'two columns'),
+             ('col_three.html', 'three columns'),
+             ('nav_playground.html', 'navigation examples'),),
+        )
+        for value_to_test in success_template_tests:
+            with SettingsOverride(DEBUG=True, CMS_TEMPLATES=value_to_test):
+                self.assertEqual(None, post_patch_check())