Repository: django-cms/django-cms

Issue:
CMS Plugin cancel button is destructive in Admin.
# Steps to reproduce
- Create a page in the Django admin.
- Add a plugin - say, a link - to a placeholder.
- Save it, then re-open it for editing.
- modify some portion of the saved data, and hit cancel.
# Expectation

The changes you've made should be canceled, and the the plugin editor should probably disappear.
# Current result

It's a submit button, so it triggers full validation, and if successful, saves whatever you've cancelled back to the database.
# Additional notes

Works fine in the frontend-editing, as some JavaScript is responsible for destroying the plugin window. This ought to be ported to the admin (if possible, in such a way as the plugin editor iframe itself can deal with it, which ought to ensure it works in both FE & BE admin). Possibly also should have the plugin save method test whether _cancel was clicked, in case there's a javascript error?

I think the the comment from  @calinf on October 28, 2011, in ticket #861 may be piggybacking that issue to describe this issue, and proposes a potential fix (which, FWIW, I don't like, as it's a different intention), but that ticket is tagged for 2.3, and I think given it's a potentially destructive feature in 2.2.0/develop, it ought to be pushed to 2.2.1. For our purposes, we've hidden the cancel button entirely in the admin, to side-step it.

Final note: the buttons are in different positions in the frontend vs backend. Minor niggle.



Discussion:
Addendum: It'd be nice if the [cancel input](https://github.com/divio/django-cms/blob/develop/cms/templates/admin/cms/page/plugin_change_form.html#L114) had an ID or class, too so it can be easily targeted via CSS. At the moment, to hide it, I've got something like (LESS):

```
.plugin-submit-row {
    input {
        display: none;
        &.default {
            display: block;
        }
    }
}
```

which nets me:

```
.plugin-submit-row input{display:none;}
.plugin-submit-row input.default{display:block;}
```

which just hides the cancel button altogether.


Resolving patch:
diff --git a/cms/admin/pageadmin.py b/cms/admin/pageadmin.py
--- a/cms/admin/pageadmin.py
+++ b/cms/admin/pageadmin.py
@@ -1234,6 +1234,27 @@ def edit_plugin(self, request, plugin_id):
             context = RequestContext(request)
             return render_to_response(plugin_admin.render_template, plugin_admin.render(context, instance, plugin_admin.placeholder))
 
+        if request.POST.get("_cancel", False):
+            # cancel buton was clicked
+            context = {
+                'CMS_MEDIA_URL': settings.CMS_MEDIA_URL,
+                'plugin': cms_plugin,
+                'is_popup': True,
+                'name': unicode(cms_plugin),
+                "type": cms_plugin.get_plugin_name(),
+                'plugin_id': plugin_id,
+                'icon': force_escape(escapejs(cms_plugin.get_instance_icon_src())),
+                'alt': force_escape(escapejs(cms_plugin.get_instance_icon_alt())),
+                'cancel': True,
+            }
+            instance = cms_plugin.get_plugin_instance()[0]
+            if not instance:
+                # cancelled before any content was added to plugin
+                cms_plugin.delete()
+                context.update({
+                    "deleted":True,
+                })
+            return render_to_response('admin/cms/page/plugin_forms_ok.html', context, RequestContext(request))
 
         if not instance:
             # instance doesn't exist, call add view


Test patch:
diff --git a/cms/tests/plugins.py b/cms/tests/plugins.py
--- a/cms/tests/plugins.py
+++ b/cms/tests/plugins.py
@@ -102,6 +102,44 @@ def test_add_edit_plugin(self):
         self.assertEquals(response.status_code, 200)
         txt = Text.objects.all()[0]
         self.assertEquals("Hello World", txt.body)
+        # edit body, but click cancel button
+        data = {
+            "body":"Hello World!!",
+            "_cancel":True,
+        }
+        response = self.client.post(edit_url, data)
+        self.assertEquals(response.status_code, 200)
+        txt = Text.objects.all()[0]
+        self.assertEquals("Hello World", txt.body)
+
+    def test_add_cancel_plugin(self):
+        """
+        Test that you can cancel a new plugin before editing and 
+        that the plugin is removed.
+        """
+        # add a new text plugin
+        page_data = self.get_new_page_data()
+        response = self.client.post(URL_CMS_PAGE_ADD, page_data)
+        page = Page.objects.all()[0]
+        plugin_data = {
+            'plugin_type':"TextPlugin",
+            'language':settings.LANGUAGES[0][0],
+            'placeholder':page.placeholders.get(slot="body").pk,
+        }
+        response = self.client.post(URL_CMS_PLUGIN_ADD, plugin_data)
+        self.assertEquals(response.status_code, 200)
+        self.assertEquals(int(response.content), CMSPlugin.objects.all()[0].pk)
+        # now click cancel instead of editing
+        edit_url = URL_CMS_PLUGIN_EDIT + response.content + "/"
+        response = self.client.get(edit_url)
+        self.assertEquals(response.status_code, 200)
+        data = {
+            "body":"Hello World",
+            "_cancel":True,
+        }
+        response = self.client.post(edit_url, data)
+        self.assertEquals(response.status_code, 200)
+        self.assertEquals(0, Text.objects.count())
 
     def test_copy_plugins(self):
         """