Repository: django-cms/django-cms

Issue:
copy plugins broken?
After updating from 3.0.3 to 3.0.7 copying plugins to clipboard doesnt work anymore if the plugin is nested within another plugin. Seems the `request.POST['source_placeholder_id']` in `PlaceholderAdminMixin.copy_plugins` contains a totally wrong id (points to some other placeholder in the template).
A quickfix for my case was to fall back to the placeholder_id of the source_plugin since the database still got the right value:

``` patch
--- placeholderadmin.py 2014-12-03 16:07:10.773331000 +0100
+++ placeholderadmin_fix.py 2014-12-03 16:09:16.933332000 +0100
@@ -308,6 +308,7 @@
                 inst, cls = source_plugin.get_plugin_instance(self)
                 plugins = inst.placeholder_ref.get_plugins_list()
             else:
+                source_placeholder = get_object_or_404(Placeholder, pk=source_plugin.placeholder_id)
                 plugins = list(
                     source_placeholder.cmsplugin_set.filter(tree_id=source_plugin.tree_id, lft__gte=source_plugin.lft,
                                                             rght__lte=source_plugin.rght).order_by('tree_id', 'level',

```

I wonder why you dont do it this way in the first place instead of relying on the request value. I guess I missed there something and it has side effect? Maybe you could have a look at it.



Discussion:
Update:
This behavior only happens if the nested plugins placeholder follows a static placeholder in the template.
Example:

```
{% static_placeholder 'pl_static' %}
{% placeholder pl %}
```

placeholder_id always contains the 'pl_static' id for nested plugins in `pl`

Can you provide us with a list of steps to reproduce please?

I can reproduce that bug in v3.0.6 too by following these steps:

I. Create a basic cms-template with a static_placeholder followed by a regular placeholder.

```
...
<section role="main">
    {% static_placeholder "static content" %}
    {% placeholder "content" %}
</section>
...
```

II. Open page using that cms-template in edit mode and place an instance of e.g. `MultiColumnPlugin` in the "content" placeholder. 

III. Create an instance of `ColumnPlugin` within that `MultiColumnPlugin`. Now just try to make a copy of the `ColumnPlugin` or any other sibling of the MultiColumnPlugin – It will fail:
The site will just reload but clipboard stays empty (actually it get's cleared from what I can see).

![django cms plugin structure for copy fail](https://cloud.githubusercontent.com/assets/5698638/5380489/d8f35016-8098-11e4-8a58-21099c6cf5ba.png)

Making a copy of the `MultiColumnsPlugin` will work.

Yeah what hepptux wrote will reproduce it. I also tested it with all versions since 3.0.3, seems 3.0.3 is the last working version regarding this behavior.

Just drop me a note if you need a downstripped example django app. If you prefer a pull-patch, I could create one, but as I wrote earlier my patch is merely a sympthomatic one with nearly no knowledge about the interna (and possible side effects).

Thanks @heppstux and @jerch for all the details.

I just investigated the problem further:

Seems like the `static_placeholder` template tag adds a variable `cms_placeholder_instance` to the context. (Btw. no other placeholder seems to do that – why is that in the first place? Just saw some commits regarding permissions for static_placeholders – might be it's somehow related to that – still I don't get the way. Maybe somebody can help me here.)

In `get_context(self, context, plugin):` (cms/templatetags/cms_tags.py) comes this:

```
try:
    placeholder = context['cms_placeholder_instance']
except KeyError:
    placeholder = plugin.placeholder
```

This leads to whenever a static_placeholder is inserted, any of the following plugins will get that static_placeholder rather than its real placeholder.

I created a pull request #3671 for that issue.

What do you think?


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
@@ -361,10 +361,7 @@ def get_context(self, context, plugin):
         if not plugin:
             return {'content': ''}
 
-        try:
-            placeholder = context['cms_placeholder_instance']
-        except KeyError:
-            placeholder = plugin.placeholder
+        placeholder = plugin.placeholder
 
         processors = self.get_processors(context, plugin, placeholder)
 
@@ -1094,7 +1091,6 @@ def render_tag(self, context, code, extra_bits, nodelist=None):
         else:
             placeholder = static_placeholder.public
         placeholder.is_static = True
-        context.update({'cms_placeholder_instance': placeholder})
         content = render_placeholder(placeholder, context, name_fallback=code, default=nodelist)
         return content
 register.tag(StaticPlaceholderNode)


Test patch: