Repository: django-cms/django-cms

Issue:
allow silent failure of placeholder with no page if `or` is defined



Discussion:

Resolving patch:
diff --git a/cms/api.py b/cms/api.py
--- a/cms/api.py
+++ b/cms/api.py
@@ -75,11 +75,18 @@ def _verify_apphook(apphook, namespace):
     """
     apphook_pool.discover_apps()
     if hasattr(apphook, '__module__') and issubclass(apphook, CMSApp):
-        assert apphook in apphook_pool.apps.values()
+        try:
+            assert apphook in apphook_pool.apps.values()
+        except AssertionError:
+            print(apphook_pool.apps.values())
+            raise
         return apphook.__name__
     elif isinstance(apphook, string_types):
-        apphook_pool.discover_apps()
-        assert apphook in apphook_pool.apps
+        try:
+            assert apphook in apphook_pool.apps
+        except AssertionError:
+            print(apphook_pool.apps.values())
+            raise
         apphook_name = apphook
     else:
         raise TypeError("apphook must be string or CMSApp instance")
diff --git a/cms/templatetags/cms_tags.py b/cms/templatetags/cms_tags.py
--- a/cms/templatetags/cms_tags.py
+++ b/cms/templatetags/cms_tags.py
@@ -307,10 +307,17 @@ def render_tag(self, context, name, extra_bits, nodelist=None):
         if not page or page == 'dummy':
             if nodelist:
                 return nodelist.render(context)
-
             return ''
-
-        content = get_placeholder_content(context, request, page, name, inherit, nodelist)
+        try:
+            content = get_placeholder_content(context, request, page, name, inherit, nodelist)
+        except PlaceholderNotFound:
+            if nodelist:
+                return nodelist.render(context)
+            raise
+        if not content:
+            if nodelist:
+                return nodelist.render(context)
+            return ''
         return content
 
     def get_name(self):


Test patch:
diff --git a/cms/test_utils/project/sampleapp/templates/sampleapp/home.html b/cms/test_utils/project/sampleapp/templates/sampleapp/home.html
--- a/cms/test_utils/project/sampleapp/templates/sampleapp/home.html
+++ b/cms/test_utils/project/sampleapp/templates/sampleapp/home.html
@@ -24,4 +24,5 @@ <h3>{{ message }}</h3>
 	</ul>
 	<h4>Sample image  - appmedia works?</h4>
 	<img src="{{ STATIC_URL }}sampleapp/img/gift.jpg" alt="gift">
+    {% placeholder "test" or %}<--noplaceholder-->{% endplaceholder %}
 {% endblock content %}
diff --git a/cms/tests/apphooks.py b/cms/tests/apphooks.py
--- a/cms/tests/apphooks.py
+++ b/cms/tests/apphooks.py
@@ -38,6 +38,7 @@ def tearDown(self):
             del sys.modules[APP_MODULE]
 
         self.reload_urls()
+        apphook_pool.clear()
 
     def reload_urls(self):
         from django.conf import settings
@@ -137,7 +138,7 @@ def test_apphook_on_root(self):
             with force_language("en"):
                 response = self.client.get(self.get_pages_root())
             self.assertTemplateUsed(response, 'sampleapp/home.html')
-
+            self.assertContains(response, '<--noplaceholder-->')
             response = self.client.get('/en/blankapp/')
             self.assertTemplateUsed(response, 'nav_playground.html')
 
@@ -437,6 +438,7 @@ def tearDown(self):
 
         if APP_MODULE in sys.modules:
             del sys.modules[APP_MODULE]
+        apphook_pool.clear()
 
     def reload_urls(self):
         from django.conf import settings
diff --git a/cms/tests/frontend.py b/cms/tests/frontend.py
--- a/cms/tests/frontend.py
+++ b/cms/tests/frontend.py
@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 import sys
 import datetime
+from cms.apphook_pool import apphook_pool
+from cms.exceptions import AppAlreadyRegistered
 from cms.test_utils.project.placeholderapp.models import Example1
 from django.core.urlresolvers import clear_url_caches
 from cms.appresolver import clear_app_resolvers
@@ -179,9 +181,14 @@ def test_toolbar_login_view(self):
             char_1='char_1', char_2='char_1', char_3='char_3', char_4='char_4',
             date_field=datetime.datetime.now()
         )
+        try:
+            apphook_pool.register(Example1App)
+        except AppAlreadyRegistered:
+            pass
+        self.reload_urls()
         create_page('apphook', 'simple.html', 'en', published=True,
                     apphook=Example1App)
-        self.reload_urls()
+
 
         url = '%s/%s/?edit' % (self.live_server_url, 'apphook/detail/%s' % ex1.pk)
         self.driver.get(url)
@@ -195,6 +202,11 @@ def test_toolbar_login_view(self):
 
     def test_toolbar_login_cbv(self):
         User = get_user_model()
+        try:
+            apphook_pool.register(Example1App)
+        except AppAlreadyRegistered:
+            pass
+        self.reload_urls()
         create_page('Home', 'simple.html', 'en', published=True)
         ex1 = Example1.objects.create(
             char_1='char_1', char_2='char_1', char_3='char_3', char_4='char_4',
@@ -202,8 +214,6 @@ def test_toolbar_login_cbv(self):
         )
         create_page('apphook', 'simple.html', 'en', published=True,
                     apphook=Example1App)
-        self.reload_urls()
-
         url = '%s/%s/?edit' % (self.live_server_url, 'apphook/detail/class/%s' % ex1.pk)
         self.driver.get(url)
         username_input = self.driver.find_element_by_id("id_cms-username")
diff --git a/cms/tests/plugins.py b/cms/tests/plugins.py
--- a/cms/tests/plugins.py
+++ b/cms/tests/plugins.py
@@ -3,6 +3,7 @@
 import datetime
 import json
 from cms.test_utils.util.fuzzy_int import FuzzyInt
+from django.core.cache import cache
 import os
 
 from djangocms_googlemap.models import GoogleMap
@@ -568,6 +569,7 @@ def test_inheritplugin_media(self):
         """
         Test case for InheritPagePlaceholder
         """
+
         inheritfrompage = api.create_page('page to inherit from',
                                       'nav_playground.html',
                                       'en')
@@ -604,9 +606,10 @@ def test_inheritplugin_media(self):
         page.publish('en')
 
         self.client.logout()
+        cache.clear()
         response = self.client.get(page.get_absolute_url())
         self.assertTrue(
-            'https://maps-api-ssl.google.com/maps/api/js?v=3&amp;sensor=true' in response.content.decode('utf8'))
+            'https://maps-api-ssl.google.com/maps/api/js?v=3&sensor=true' in response.content.decode('utf8').replace("&amp;", "&"))
 
     def test_inherit_plugin_with_empty_plugin(self):
         inheritfrompage = api.create_page('page to inherit from',
diff --git a/cms/tests/templatetags.py b/cms/tests/templatetags.py
--- a/cms/tests/templatetags.py
+++ b/cms/tests/templatetags.py
@@ -323,3 +323,20 @@ def test_render_plugin(self):
         with self.assertNumQueries(0):
             output = template.render(context)
         self.assertIn('<b>Test</b>', output)
+
+    def test_render_placeholder_with_no_page(self):
+        page = create_page('Test', 'col_two.html', 'en', published=True)
+        template = Template(
+            "{% load cms_tags %}{% placeholder test or %}< --- empty --->{% endplaceholder %}")
+        request = RequestFactory().get('/asdadsaasd/')
+        user = self.get_superuser()
+        request.user = user
+        request.current_page = page
+        request.session = {}
+        request.toolbar = CMSToolbar(request)
+        request.toolbar.edit_mode = True
+        request.toolbar.is_staff = True
+        context = RequestContext(request)
+        with self.assertNumQueries(4):
+            template.render(context)
+