Repository: django-cms/django-cms

Issue:
language_fallback in placeholder conf can make plugins disappear (edit and live mode)
when having a placeholder with "language_fallback", no more plugins at all are shown in draft mode (neither in "edit" nor in "structure", and neither for plugins with no fallback). simply removing the fallback (as below) makes things work again. Then I found out, that I had not defined CMS_LANGUAGES and so following, no fallbacks in there...~~adding fallbacks in CMS_LANGUAGES solves the problem~~ EDIT: The problem is a tricky one, see comments.

placeholder conf

```
CMS_PLACEHOLDER_CONF = {
    'header_left': {
        'plugins': ['ImagePlugin', ],
        'extra_context': {"size": "698x520"},
        'name': ugettext("Headerbild"),
        #'language_fallback': True,
    },
    'header_right': {
        'plugins': ['ImagePlugin', ],
        'extra_context': {"size": "266x520"},
        'name': ugettext("Header Rechts"),
        #'language_fallback': True,
    },
    'content': {
        "plugins": ['MultiLineTitlePlugin','TextPlugin', 'AccordionPlugin'],
        'text_only_plugins': ["LinkPlugin",],
        "extra_context": {"width":280},
        'name': ugettext("Inhalt"),
    },
}
```

language conf

```
LANGUAGE_CODE = 'de'
LANGUAGES = (
    ('de', ugettext(u"Deutsch")),
    ('fr', ugettext(u"Francais")),
    ('en', ugettext(u"English")),
)
```



Discussion:

Resolving patch:
diff --git a/cms/utils/plugins.py b/cms/utils/plugins.py
--- a/cms/utils/plugins.py
+++ b/cms/utils/plugins.py
@@ -222,8 +222,9 @@ def assign_plugins(request, placeholders, template, lang=None, no_fallback=False
                 fallbacks = get_fallback_languages(lang)
                 for fallback_language in fallbacks:
                     assign_plugins(request, [placeholder], template, fallback_language, no_fallback=True)
-                    plugins = placeholder._plugins_cache
-                    if plugins:
+                    fallback_plugins = placeholder._plugins_cache
+                    if fallback_plugins:
+                        plugins += fallback_plugins
                         break
     # If no plugin is present, create default plugins if enabled)
     if not plugins:


Test patch:
diff --git a/cms/tests/placeholder.py b/cms/tests/placeholder.py
--- a/cms/tests/placeholder.py
+++ b/cms/tests/placeholder.py
@@ -44,7 +44,7 @@
 from cms.test_utils.util.mock import AttributeObject
 from cms.utils.compat.dj import force_unicode
 from cms.utils.placeholder import PlaceholderNoAction, MLNGPlaceholderActions, get_placeholder_conf
-from cms.utils.plugins import get_placeholders
+from cms.utils.plugins import get_placeholders, assign_plugins
 from cms.compat import get_user_model
 from cms.test_utils.project.objectpermissionsapp.models import UserObjectPermission
 
@@ -449,6 +449,44 @@ def push(self):
             content_en = render_placeholder(placeholder_en, context_en)
             self.assertRegexpMatches(content_en, "^en body$")
 
+    def test_plugins_discarded_with_language_fallback(self):
+        """
+        Tests side effect of language fallback: if fallback enabled placeholder
+        existed, it discards all other existing plugins
+        """
+        page_en = create_page('page_en', 'col_two.html', 'en')
+        create_title("de", "page_de", page_en)
+        placeholder_sidebar_en = page_en.placeholders.get(slot='col_sidebar')
+        placeholder_en = page_en.placeholders.get(slot='col_left')
+        add_plugin(placeholder_sidebar_en, TextPlugin, 'en', body='en body')
+
+        class NoPushPopContext(Context):
+            def push(self):
+                pass
+
+            pop = push
+
+        context_en = NoPushPopContext()
+        context_en['request'] = self.get_request(language="en", page=page_en)
+
+        conf = {
+            'col_left': {
+                'language_fallback': True,
+            },
+        }
+        with SettingsOverride(CMS_PLACEHOLDER_CONF=conf):
+            # call assign plugins first, as this is what is done in real cms life
+            # for all placeholders in a page at once
+            assign_plugins(context_en['request'],
+                           [placeholder_sidebar_en, placeholder_en], 'col_two.html')
+            # if the normal, non fallback enabled placeholder still has content
+            content_en = render_placeholder(placeholder_sidebar_en, context_en)
+            self.assertRegexpMatches(content_en, "^en body$")
+
+            # remove the cached plugins instances
+            del(placeholder_sidebar_en._plugins_cache)
+            cache.clear()
+
     def test_plugins_prepopulate(self):
         """ Tests prepopulate placeholder configuration """