Repository: django-cms/django-cms

Issue:
cache placeholder removes sekizai entries



Discussion:

Resolving patch:
diff --git a/cms/plugin_rendering.py b/cms/plugin_rendering.py
--- a/cms/plugin_rendering.py
+++ b/cms/plugin_rendering.py
@@ -5,13 +5,15 @@
 from cms.utils.compat.type_checks import string_types
 from cms.utils.conf import get_cms_setting
 from cms.utils.django_load import iterload_objects
-from cms.utils.placeholder import get_placeholder_conf
+from cms.utils.placeholder import get_placeholder_conf, restore_sekizai_context
 from django.template import Template, Context
 from django.template.loader import render_to_string
 from django.utils.safestring import mark_safe
 
 
 # these are always called before all other plugin context processors
+from sekizai.helpers import Watcher
+
 DEFAULT_PLUGIN_CONTEXT_PROCESSORS = (
     plugin_meta_context_processor,
 )
@@ -127,7 +129,8 @@ def render_placeholder(placeholder, context_to_copy, name_fallback="Placeholder"
         if not edit and placeholder and not hasattr(placeholder, 'cache_checked'):
             cached_value = cache.get(cache_key)
             if not cached_value is None:
-                return mark_safe(cached_value)
+                restore_sekizai_context(context, cached_value['sekizai'])
+                return mark_safe(cached_value['content'])
     if page:
         template = page.template
     else:
@@ -148,7 +151,7 @@ def render_placeholder(placeholder, context_to_copy, name_fallback="Placeholder"
             context[key] = value
 
     content = []
-
+    watcher = Watcher(context)
     content.extend(render_plugins(plugins, context, placeholder, processors))
     toolbar_content = ''
 
@@ -170,9 +173,9 @@ def render_placeholder(placeholder, context_to_copy, name_fallback="Placeholder"
     context['placeholder'] = toolbar_content
     context['edit'] = edit
     result = render_to_string("cms/toolbar/content.html", context)
-
+    changes = watcher.get_changes()
     if placeholder and not edit and placeholder.cache_placeholder and get_cms_setting('PLACEHOLDER_CACHE'):
-        cache.set(cache_key, result, get_cms_setting('CACHE_DURATIONS')['content'])
+        cache.set(cache_key, {'content': result, 'sekizai': changes}, get_cms_setting('CACHE_DURATIONS')['content'])
     context.pop()
     return result
 
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
@@ -18,7 +18,7 @@
 from cms.utils.i18n import force_language
 from cms.utils.moderator import use_draft
 from cms.utils.page_resolver import get_page_queryset
-from cms.utils.placeholder import validate_placeholder_name, get_toolbar_plugin_struct
+from cms.utils.placeholder import validate_placeholder_name, get_toolbar_plugin_struct, restore_sekizai_context
 from django import template
 from django.conf import settings
 from django.contrib.sites.models import Site
@@ -31,7 +31,7 @@
 from django.utils.safestring import mark_safe
 from django.utils.translation import ugettext_lazy as _, get_language
 import re
-from sekizai.helpers import Watcher, get_varname
+from sekizai.helpers import Watcher
 from sekizai.templatetags.sekizai_tags import SekizaiParser, RenderBlock
 
 register = template.Library()
@@ -184,9 +184,10 @@ def _get_placeholder(current_page, page, context, name):
     else:
         for placeholder in placeholders:
             cache_key = placeholder.get_cache_key(get_language())
-            content = cache.get(cache_key)
-            if not content is None:
-                placeholder.content_cache = content
+            cached_value = cache.get(cache_key)
+            if not cached_value is None:
+                restore_sekizai_context(context, cached_value['sekizai'])
+                placeholder.content_cache = cached_value['content']
             else:
                 fetch_placeholders.append(placeholder)
             placeholder.cache_checked = True
@@ -223,7 +224,8 @@ def get_placeholder_content(context, request, current_page, name, inherit, defau
                 cache_key = placeholder.get_cache_key(get_language())
                 cached_value = cache.get(cache_key)
                 if not cached_value is None:
-                    return mark_safe(cached_value)
+                    restore_sekizai_context(context, cached_value['sekizai'])
+                    return mark_safe(cached_value['content'])
         if not get_plugins(request, placeholder, page.get_template()):
             continue
         content = render_placeholder(placeholder, context, name)
@@ -487,15 +489,6 @@ def get_context(self, context, cl, spec):
         return {'title': spec.title(), 'choices': unique_choices}
 
 
-def _restore_sekizai(context, changes):
-    varname = get_varname()
-    sekizai_container = context[varname]
-    for key, values in changes.items():
-        sekizai_namespace = sekizai_container[key]
-        for value in values:
-            sekizai_namespace.append(value)
-
-
 def _show_placeholder_for_page(context, placeholder_name, page_lookup, lang=None,
                                site=None, cache_result=True):
     """
@@ -522,12 +515,9 @@ def _show_placeholder_for_page(context, placeholder_name, page_lookup, lang=None
         base_key = _get_cache_key('_show_placeholder_for_page', page_lookup, lang, site_id)
         cache_key = _clean_key('%s_placeholder:%s' % (base_key, placeholder_name))
         cached_value = cache.get(cache_key)
-        if isinstance(cached_value, dict): # new style
-            _restore_sekizai(context, cached_value['sekizai'])
+        if cached_value:
+            restore_sekizai_context(context, cached_value['sekizai'])
             return {'content': mark_safe(cached_value['content'])}
-        elif isinstance(cached_value, string_types): # old style
-            return {'content': mark_safe(cached_value)}
-
     page = _get_page_by_untyped_arg(page_lookup, request, site_id)
     if not page:
         return {'content': ''}
diff --git a/cms/utils/placeholder.py b/cms/utils/placeholder.py
--- a/cms/utils/placeholder.py
+++ b/cms/utils/placeholder.py
@@ -6,6 +6,7 @@
 from django.core.exceptions import ImproperlyConfigured
 from cms.utils.compat.dj import force_unicode
 from django.db.models.query_utils import Q
+from sekizai.helpers import get_varname
 
 
 def get_toolbar_plugin_struct(plugins_list, slot, page, parent=None):
@@ -136,3 +137,12 @@ def get_copy_languages(self, placeholder, model, fieldname, **kwargs):
 
         language_codes = manager.filter(query).values_list('language_code', flat=True).distinct()
         return [(lc, dict(settings.LANGUAGES)[lc]) for lc in language_codes]
+
+
+def restore_sekizai_context(context, changes):
+    varname = get_varname()
+    sekizai_container = context.get(varname)
+    for key, values in changes.items():
+        sekizai_namespace = sekizai_container[key]
+        for value in values:
+            sekizai_namespace.append(value)
\ No newline at end of file


Test patch:
diff --git a/cms/test_utils/project/pluginapp/plugins/caching/cms_plugins.py b/cms/test_utils/project/pluginapp/plugins/caching/cms_plugins.py
--- a/cms/test_utils/project/pluginapp/plugins/caching/cms_plugins.py
+++ b/cms/test_utils/project/pluginapp/plugins/caching/cms_plugins.py
@@ -13,3 +13,14 @@ class NoCachePlugin(CMSPluginBase):
     def render(self, context, instance, placeholder):
         context['now'] = datetime.now().microsecond
         return context
+
+
+class SekizaiPlugin(CMSPluginBase):
+    name = 'WITH SEki'
+    module = 'Test'
+    render_plugin = True
+    render_template = "plugins/sekizai.html"
+
+    def render(self, context, instance, placeholder):
+        context['now'] = datetime.now().microsecond
+        return context
diff --git a/cms/test_utils/project/templates/plugins/sekizai.html b/cms/test_utils/project/templates/plugins/sekizai.html
new file mode 100644
--- /dev/null
+++ b/cms/test_utils/project/templates/plugins/sekizai.html
@@ -0,0 +1,8 @@
+{% load sekizai_tags %}
+$$${{ now }}$$$
+
+{% addtoblock "js" %}
+<script>
+    alert('{{ now }}');
+</script>
+{% endaddtoblock %}
\ No newline at end of file
diff --git a/cms/tests/__init__.py b/cms/tests/__init__.py
--- a/cms/tests/__init__.py
+++ b/cms/tests/__init__.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 
-
+from cms.tests.static_analysis import StaticAnalysisTest  # nopyflakes
 from cms.tests.admin import *  # nopyflakes
 from cms.tests.api import *  # nopyflakes
 from cms.tests.apphooks import *  # nopyflakes
@@ -44,4 +44,4 @@
 from cms.tests.signals import *  # nopyflakes
 from cms.tests.no_i18n import *  # nopyflakes
 from cms.tests.cache import *  # nopyflakes
-from cms.tests.static_analysis import StaticAnalysisTest  # nopyflakes
+
diff --git a/cms/tests/cache.py b/cms/tests/cache.py
--- a/cms/tests/cache.py
+++ b/cms/tests/cache.py
@@ -2,7 +2,7 @@
 from cms.api import add_plugin, create_page
 from cms.models import Page
 from cms.plugin_pool import plugin_pool
-from cms.test_utils.project.pluginapp.plugins.caching.cms_plugins import NoCachePlugin
+from cms.test_utils.project.pluginapp.plugins.caching.cms_plugins import NoCachePlugin, SekizaiPlugin
 from cms.test_utils.testcases import CMSTestCase
 from cms.test_utils.util.context_managers import SettingsOverride
 from cms.test_utils.util.fuzzy_int import FuzzyInt
@@ -203,3 +203,18 @@ def test_cache_page(self):
             with self.assertNumQueries(FuzzyInt(1, 20)):
                 response = self.client.get('/en/')
             self.assertEqual(response.status_code, 200)
+
+    def test_sekizai_plugin(self):
+        page1 = create_page('test page 1', 'nav_playground.html', 'en',
+                            published=True)
+
+        placeholder1 = page1.placeholders.filter(slot="body")[0]
+        placeholder2 = page1.placeholders.filter(slot="right-column")[0]
+        plugin_pool.register_plugin(SekizaiPlugin)
+        add_plugin(placeholder1, "SekizaiPlugin", 'en')
+        add_plugin(placeholder2, "TextPlugin", 'en', body="Deutsch")
+        page1.publish('en')
+        response = self.client.get('/en/')
+        self.assertContains(response, 'alert(')
+        response = self.client.get('/en/')
+        self.assertContains(response, 'alert(')