Repository: django-cms/django-cms

Issue:
{% static_placeholder or %}
Implement something like:

`{% placeholder "content" or %}There is no content.{% endplaceholder %}`

for static_placeholder like:

`{% static_placeholder "content" or %}There is no content.{% endstatic_placeholder %}`



Discussion:
for `render_placeholder` too ?

@ollb render_placeholder already supports this.


Resolving patch:
diff --git a/cms/plugin_rendering.py b/cms/plugin_rendering.py
--- a/cms/plugin_rendering.py
+++ b/cms/plugin_rendering.py
@@ -84,7 +84,7 @@ def render_plugins(plugins, context, placeholder, processors=None):
     return out
 
 
-def render_placeholder(placeholder, context_to_copy, name_fallback="Placeholder", lang=None):
+def render_placeholder(placeholder, context_to_copy, name_fallback="Placeholder", lang=None, default=None):
     """
     Renders plugins for a placeholder on the given page using shallow copies of the
     given context, and returns a string containing the rendered output.
@@ -148,7 +148,13 @@ def render_placeholder(placeholder, context_to_copy, name_fallback="Placeholder"
             request.toolbar.placeholders[placeholder.pk] = placeholder
     if edit:
         toolbar_content = mark_safe(render_placeholder_toolbar(placeholder, context, name_fallback, save_language))
-    content = mark_safe("".join(content))
+    if content:
+        content = mark_safe("".join(content))
+    elif default:
+        #should be nodelist from a template
+        content = mark_safe(default.render(context_to_copy))
+    else:
+        content = ''
     context['content'] = content
     context['placeholder'] = toolbar_content
     context['edit'] = edit
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
@@ -167,7 +167,7 @@ def _get_placeholder(current_page, page, context, name):
     return placeholder
 
 
-def get_placeholder_content(context, request, current_page, name, inherit):
+def get_placeholder_content(context, request, current_page, name, inherit, default):
     edit_mode = getattr(request, 'toolbar', None) and getattr(request.toolbar, 'edit_mode')
     pages = [current_page]
     # don't display inherited plugins in edit mode, so that the user doesn't
@@ -188,7 +188,7 @@ def get_placeholder_content(context, request, current_page, name, inherit):
             # call _get_placeholder again to get the placeholder properly rendered
             # in frontend editing
     placeholder = _get_placeholder(current_page, current_page, context, name)
-    return render_placeholder(placeholder, context, name)
+    return render_placeholder(placeholder, context, name, default=default)
 
 
 class PlaceholderParser(Parser):
@@ -263,9 +263,7 @@ def render_tag(self, context, name, extra_bits, nodelist=None):
 
             return ''
 
-        content = get_placeholder_content(context, request, page, name, inherit)
-        if not content and nodelist:
-            return nodelist.render(context)
+        content = get_placeholder_content(context, request, page, name, inherit, nodelist)
         return content
 
     def get_name(self):
@@ -913,22 +911,28 @@ def get_context(self, context, instance, edit_fields, language,
 
 class StaticPlaceholderNode(Tag):
     name = 'static_placeholder'
-    options = Options(
+    options = PlaceholderOptions(
         Argument('code', required=True),
-        'as',
-        Argument('varname', required=False, resolve=False)
+        MultiValueArgument('extra_bits', required=False, resolve=False),
+        blocks=[
+            ('endstatic_placeholder', 'nodelist'),
+        ]
     )
 
-    def render_tag(self, context, code, varname):
+    def render_tag(self, context, code, extra_bits, nodelist=None):
         # TODO: language override (the reason this is not implemented, is that language selection is buried way
         #       down somewhere in some method called in render_plugins. There it gets extracted from the request
         #       and a language in request.GET always overrides everything.)
         if not code:
             # an empty string was passed in or the variable is not available in the context
+            if nodelist:
+                return nodelist.render(context)
             return ''
             # TODO: caching?
         request = context.get('request', False)
         if not request:
+            if nodelist:
+                return nodelist.render(context)
             return ''
         if isinstance(code, StaticPlaceholder):
             static_placeholder = code
@@ -943,7 +947,8 @@ def render_tag(self, context, code, varname):
         else:
             placeholder = static_placeholder.public
         placeholder.is_static = True
-        return render_placeholder(placeholder, context, name_fallback=code)
+        content = render_placeholder(placeholder, context, name_fallback=code, default=nodelist)
+        return content
 
 
 register.tag(StaticPlaceholderNode)


Test patch:
diff --git a/cms/test_utils/cli.py b/cms/test_utils/cli.py
--- a/cms/test_utils/cli.py
+++ b/cms/test_utils/cli.py
@@ -188,7 +188,7 @@ def configure(db_url, **extra):
             ('col_three.html', gettext('three columns')),
             ('nav_playground.html', gettext('navigation examples')),
             ('simple.html', 'simple'),
-            ('stacks.html', 'stacks'),
+            ('static.html', 'static placeholders'),
         ),
         CMS_PLACEHOLDER_CONF={
             'col_sidebar': {
diff --git a/cms/test_utils/project/templates/stacks.html b/cms/test_utils/project/templates/static.html
similarity index 74%
rename from cms/test_utils/project/templates/stacks.html
rename to cms/test_utils/project/templates/static.html
--- a/cms/test_utils/project/templates/stacks.html
+++ b/cms/test_utils/project/templates/static.html
@@ -6,7 +6,7 @@
 {% block content %}
 	<h1>{% render_model request.current_page "page_title" %}</h1>
 
-    {% static_placeholder "logo" %}
+    {% static_placeholder "logo" or %}Here comes a logo{% endstatic_placeholder %}
 
 	<div id="col1" class="grid_3">
 		{% include "sidebar_submenu.html" %}
@@ -24,4 +24,7 @@ <h1>{% render_model request.current_page "page_title" %}</h1>
 			{% endwith %}
 		{% endblock %}
     </div>
+    <div id="col3" class="grid_12">
+        {% static_placeholder "footer" %}
+     </div>
 {% endblock %}
\ No newline at end of file
diff --git a/cms/tests/page.py b/cms/tests/page.py
--- a/cms/tests/page.py
+++ b/cms/tests/page.py
@@ -811,7 +811,7 @@ def test_plugin_loading_queries(self):
             context['request'].current_page.get_languages()
             with self.assertNumQueries(4):
                 for i, placeholder in enumerate(placeholders):
-                    content = get_placeholder_content(context, context['request'], page, placeholder.slot, False)
+                    content = get_placeholder_content(context, context['request'], page, placeholder.slot, False, None)
                     for j in range(5):
                         self.assertIn('text-%d-%d' % (i, j), content)
                         self.assertIn('link-%d-%d' % (i, j), content)
diff --git a/cms/tests/static_placeholder.py b/cms/tests/static_placeholder.py
--- a/cms/tests/static_placeholder.py
+++ b/cms/tests/static_placeholder.py
@@ -59,6 +59,17 @@ def test_template_creation(self):
         self.assertObjectExist(StaticPlaceholder.objects.all(), code='foobar', creation_method=StaticPlaceholder.CREATION_BY_TEMPLATE)
         self.assertEqual(Placeholder.objects.filter(slot='foobar').count(), 2)
 
+    def test_empty(self):
+        self.assertObjectDoesNotExist(Stack.objects.all(), code='foobar')
+        self.assertObjectDoesNotExist(Placeholder.objects.all(), slot='foobar')
+        t = Template('{% load cms_tags %}{% static_placeholder "foobar" or %}No Content{% endstatic_placeholder %}')
+        rendered = t.render(self.get_context('/'))
+        self.assertIn("No Content", rendered)
+        for p in Placeholder.objects.all():
+            add_plugin(p, 'TextPlugin', 'en', body='test')
+        rendered = t.render(self.get_context('/'))
+        self.assertNotIn("No Content", rendered)
+
     def test_publish_stack(self):
         static_placeholder = StaticPlaceholder.objects.create(name='foo', code='bar')
         self.fill_placeholder(static_placeholder.draft)