Repository: django-cms/django-cms

Issue:
0707054a breaks {% render_plugin %}
0707054a breaks `{% render_plugin %}` outside static placeholders. It requires a variable `cms_placeholder_instance` to be set in the template context, which only seems to be done by `StaticPlaceholderNode`: https://github.com/divio/django-cms/blob/35890c047b976157398746d17b9f3b5d3a26c88f/cms/templatetags/cms_tags.py#L1088

Without it, it fails at https://github.com/divio/django-cms/blob/35890c047b976157398746d17b9f3b5d3a26c88f/cms/templatetags/cms_tags.py#L352 with `'NoneType' object has no attribute 'has_change_permission'` as `placeholder` is `None`.



Discussion:

Resolving patch:
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
@@ -341,14 +341,13 @@ class RenderPlugin(InclusionTag):
         Argument('plugin')
     )
 
-    def get_processors(self, context, plugin):
+    def get_processors(self, context, plugin, placeholder):
         #
         # Prepend frontedit toolbar output if applicable. Moved to its own
         # method to aide subclassing the whole RenderPlugin if required.
         #
         request = context['request']
         toolbar = getattr(request, 'toolbar', None)
-        placeholder = context.get('cms_placeholder_instance', None)
         if toolbar and toolbar.edit_mode and placeholder.has_change_permission(request) and getattr(placeholder, 'is_editable', True):
             from cms.middleware.toolbar import toolbar_plugin_processor
             processors = (toolbar_plugin_processor,)
@@ -362,10 +361,20 @@ def get_context(self, context, plugin):
         if not plugin:
             return {'content': ''}
 
-        placeholder = context.get('cms_placeholder_instance', None)
-        processors = self.get_processors(context, plugin)
+        try:
+            placeholder = context['cms_placeholder_instance']
+        except KeyError:
+            placeholder = plugin.placeholder
+
+        processors = self.get_processors(context, plugin, placeholder)
 
-        return {'content': plugin.render_plugin(context, placeholder=placeholder, processors=processors)}
+        return {
+            'content': plugin.render_plugin(
+                context,
+                placeholder=placeholder,
+                processors=processors
+            )
+        }
 
 
 register.tag(RenderPlugin)


Test patch:
diff --git a/cms/tests/templatetags.py b/cms/tests/templatetags.py
--- a/cms/tests/templatetags.py
+++ b/cms/tests/templatetags.py
@@ -1,6 +1,7 @@
 from __future__ import with_statement
 import copy
 import os
+from classytags.tests import DummyParser, DummyTokens
 
 from django.contrib.auth.models import AnonymousUser
 from django.contrib.sites.models import Site
@@ -18,7 +19,7 @@
 from cms.models.pagemodel import Page, Placeholder
 from cms.templatetags.cms_tags import (_get_page_by_untyped_arg,
                                        _show_placeholder_for_page,
-                                       _get_placeholder)
+                                       _get_placeholder, RenderPlugin)
 from cms.test_utils.fixtures.templatetags import TwoPagesFixture
 from cms.test_utils.testcases import SettingsOverrideTestCase, CMSTestCase
 from cms.test_utils.util.context_managers import SettingsOverride
@@ -26,6 +27,7 @@
 from cms.utils import get_cms_setting, get_site_id
 from cms.utils.plugins import get_placeholders
 from cms.utils.compat.dj import get_user_model
+from sekizai.context import SekizaiContext
 
 
 class TemplatetagTests(TestCase):
@@ -326,6 +328,30 @@ def test_render_plugin(self):
             output = template.render(context)
         self.assertIn('<b>Test</b>', output)
 
+    def test_render_plugin_no_context(self):
+        placeholder = Placeholder.objects.create(slot='test')
+        plugin = add_plugin(placeholder, TextPlugin, 'en', body='Test')
+        parser = DummyParser()
+        tokens = DummyTokens(plugin)
+        tag = RenderPlugin(parser, tokens)
+        superuser = self.get_superuser()
+        request = RequestFactory().get('/')
+        request.current_page = None
+        request.user = superuser
+        request.session = {}
+        request.toolbar = CMSToolbar(request)
+        request.toolbar.edit_mode = True
+        context = SekizaiContext({
+            'request': request
+        })
+        output = tag.render(context)
+        self.assertEqual(
+            output,
+            '<div class="cms_plugin cms_plugin-{0}">Test</div>'.format(
+                plugin.pk
+            )
+        )
+
     def test_render_placeholder_with_no_page(self):
         page = create_page('Test', 'col_two.html', 'en', published=True)
         template = Template(