Skip to content

Commit 0b4a035

Browse files
mrbazzanfsbraun
andauthored
chore: Clean up get_extra_menu_items methods & Test for djangocms-transfer's PluginImporter (#50)
Co-authored-by: Fabian Braun <fsbraun@gmx.de>
1 parent 56b392a commit 0b4a035

File tree

12 files changed

+283
-54
lines changed

12 files changed

+283
-54
lines changed

djangocms_transfer/cms_plugins.py

Lines changed: 28 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@
1313

1414
from .forms import ExportImportForm, PluginExportForm, PluginImportForm
1515

16+
try:
17+
from cms.toolbar.utils import get_plugin_tree
18+
except ImportError:
19+
# django CMS 4.1 still uses the legacy data bridge
20+
# This method serves as a compatibility shim
21+
from cms.toolbar.utils import get_plugin_tree_as_json
22+
23+
def get_plugin_tree(request, plugins):
24+
json_str = get_plugin_tree_as_json(request, plugins)
25+
return json.loads(json_str)
26+
27+
1628

1729
class PluginImporter(CMSPluginBase):
1830
system = True
@@ -33,21 +45,12 @@ def get_plugin_urls(self):
3345
]
3446
return urlpatterns
3547

36-
def get_extra_global_plugin_menu_items(self, request, plugin):
37-
# django-cms 3.4 compatibility
38-
return self.get_extra_plugin_menu_items(request, plugin)
39-
40-
@classmethod
41-
def get_extra_plugin_menu_items(cls, request, plugin):
42-
if plugin.plugin_type == cls.__name__:
43-
return
44-
45-
data = urlencode(
46-
{
47-
"language": get_language_from_request(request),
48-
"plugin": plugin.pk,
49-
}
50-
)
48+
@staticmethod
49+
def _get_extra_menu_items(query_params, request):
50+
data = urlencode({
51+
"language": get_language_from_request(request),
52+
**query_params
53+
})
5154
return [
5255
PluginMenuItem(
5356
_("Export plugins"),
@@ -69,34 +72,18 @@ def get_extra_plugin_menu_items(cls, request, plugin):
6972
),
7073
]
7174

75+
@classmethod
76+
def get_extra_plugin_menu_items(cls, request, plugin):
77+
if plugin.plugin_type == cls.__name__:
78+
return
79+
80+
data = {"plugin": plugin.pk}
81+
return cls._get_extra_menu_items(data, request)
82+
7283
@classmethod
7384
def get_extra_placeholder_menu_items(cls, request, placeholder): # noqa
74-
data = urlencode(
75-
{
76-
"language": get_language_from_request(request),
77-
"placeholder": placeholder.pk,
78-
}
79-
)
80-
return [
81-
PluginMenuItem(
82-
_("Export plugins"),
83-
admin_reverse("cms_export_plugins") + "?" + data,
84-
data={},
85-
action="none",
86-
attributes={
87-
"icon": "export",
88-
},
89-
),
90-
PluginMenuItem(
91-
_("Import plugins"),
92-
admin_reverse("cms_import_plugins") + "?" + data,
93-
data={},
94-
action="modal",
95-
attributes={
96-
"icon": "import",
97-
},
98-
),
99-
]
85+
data = {"placeholder": placeholder.pk}
86+
return cls._get_extra_menu_items(data, request)
10087

10188
@classmethod
10289
def import_plugins_view(cls, request):
@@ -160,8 +147,6 @@ def import_plugins_view(cls, request):
160147
request, obj=new_plugins[0]
161148
)
162149

163-
from cms.toolbar.utils import get_plugin_tree
164-
165150
# Placeholder plugins import
166151
new_plugins = placeholder.get_plugins(language).exclude(pk__in=tree_order)
167152
data = get_plugin_tree(request, list(new_plugins))

djangocms_transfer/forms.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,9 @@ def clean(self):
8484
)
8585
raise forms.ValidationError(message)
8686

87+
plugin_is_bound = False
8788
if plugin:
88-
plugin_model = plugin.get_plugin_class().model
89-
plugin_is_bound = plugin_model.objects.filter(cmsplugin_ptr=plugin).exists()
90-
else:
91-
plugin_is_bound = False
89+
plugin_is_bound = plugin.get_bound_plugin()
9290

9391
if plugin and not plugin_is_bound:
9492
raise ValidationError("Plugin is unbound.")

setup.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#!/usr/bin/env python
22
from setuptools import setup
33

4-
54
setup()
Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class FunctionalityBaseTestCase(CMSTestCase):
1010

1111
def setUp(self):
1212
self.page = self._create_page()
13-
self.page_content = self.page.pagecontent_set(manager="admin_manager").first()
13+
self.page_content = self.page.pagecontent_set(manager="admin_manager").filter(language="en").first()
1414
self.page.set_as_homepage()
1515

1616
def _create_plugin(self, plugin_type="TextPlugin", parent=None, **kwargs):
@@ -40,11 +40,7 @@ def _add_plugin_to_page(self, plugin_publisher, *args, page=None, **kwargs):
4040
if page is None:
4141
page = self.page
4242
return add_plugin(
43-
page.pagecontent_set(manager="admin_manager")
44-
.filter(language="en")
45-
.first()
46-
.get_placeholders()
47-
.get(slot="content"),
43+
self.page_content.get_placeholders().get(slot="content"),
4844
plugin_publisher,
4945
"en",
5046
*args,

tests/settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"django.contrib.sessions.middleware.SessionMiddleware",
2525
"django.contrib.auth.middleware.AuthenticationMiddleware",
2626
"django.contrib.messages.middleware.MessageMiddleware",
27+
"cms.middleware.toolbar.ToolbarMiddleware"
2728
]
2829

2930
TEMPLATES = [
@@ -74,4 +75,6 @@
7475

7576
LANGUAGE_CODE = "en"
7677

78+
ROOT_URLCONF = "tests.urls"
79+
7780
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

tests/test_plugins.py

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
import json
2+
3+
from cms.plugin_pool import plugin_pool
4+
from cms.utils.urlutils import admin_reverse
5+
from django.core.exceptions import PermissionDenied
6+
from django.core.files.uploadedfile import SimpleUploadedFile
7+
8+
from .abstract import FunctionalityBaseTestCase
9+
10+
11+
class PluginImporterTestCase(FunctionalityBaseTestCase):
12+
def setUp(self):
13+
super().setUp()
14+
self.user = self._create_user("test", True, True)
15+
self.plugin_importer = next(
16+
(
17+
plugin
18+
for plugin in plugin_pool.get_all_plugins()
19+
if plugin.__name__ == "PluginImporter"
20+
),
21+
None
22+
)
23+
24+
def test_get_plugin_urls(self):
25+
urlpatterns = self.plugin_importer().get_plugin_urls()
26+
self.assertEqual(len(urlpatterns), 2)
27+
self.assertEqual(urlpatterns[0].name, "cms_export_plugins")
28+
self.assertEqual(urlpatterns[1].name, "cms_import_plugins")
29+
30+
def test_get_extra_menu_items(self):
31+
request = self.get_request()
32+
33+
with self.subTest("extra plugin menu items"):
34+
text_plugin = self._create_plugin()
35+
pluginimporter_plugin = self._add_plugin_to_page("PluginImporter")
36+
menu_items = self.plugin_importer.get_extra_plugin_menu_items(request, text_plugin)
37+
self.assertEqual(len(menu_items), 2)
38+
self.assertEqual(menu_items[0].name, "Export plugins")
39+
self.assertEqual(menu_items[1].name, "Import plugins")
40+
self.assertEqual(
41+
menu_items[0].url,
42+
"/en/admin/cms/page/plugin/plugin_importer/export-plugins/?language=en&plugin=1"
43+
)
44+
self.assertEqual(
45+
menu_items[1].url,
46+
"/en/admin/cms/page/plugin/plugin_importer/import-plugins/?language=en&plugin=1"
47+
)
48+
# no menu item for PluginImporter itself
49+
self.assertIsNone(self.plugin_importer.get_extra_plugin_menu_items(request, pluginimporter_plugin))
50+
51+
with self.subTest("extra placeholder menu items"):
52+
placeholder = self.page_content.get_placeholders().get(slot="content")
53+
menu_items = self.plugin_importer.get_extra_placeholder_menu_items(request, placeholder)
54+
self.assertEqual(
55+
menu_items[0].url,
56+
"/en/admin/cms/page/plugin/plugin_importer/export-plugins/?language=en&placeholder=1"
57+
)
58+
self.assertEqual(
59+
menu_items[1].url,
60+
"/en/admin/cms/page/plugin/plugin_importer/import-plugins/?language=en&placeholder=1"
61+
)
62+
63+
def test_import_plugin_views(self):
64+
with self.login_user_context(self.user):
65+
response = self.client.get(admin_reverse("cms_import_plugins"))
66+
self.assertEqual(response.content, b"Form received unexpected values.")
67+
68+
del self.user # self.get_request() checks for "user" attribute
69+
self.assertRaises(
70+
PermissionDenied,
71+
self.plugin_importer.import_plugins_view,
72+
self.get_request()
73+
)
74+
75+
def test_import_plugin_views_for_page(self):
76+
with self.login_user_context(self.user):
77+
# GET page import
78+
response = self.client.get(
79+
admin_reverse("cms_import_plugins") + f"?language=en&cms_pagecontent={self.page_content.id}"
80+
)
81+
self.assertEqual(response.templates[0].name, "djangocms_transfer/import_plugins.html")
82+
self.assertEqual(response.context["form"].initial["cms_pagecontent"], self.page_content)
83+
84+
# POST page import
85+
post_data = {
86+
"language": "en", "cms_pagecontent": [self.page_content.id],
87+
"import_file": SimpleUploadedFile("file.txt", bytes(json.dumps(self._get_expected_page_export_data()), "utf-8"))
88+
}
89+
response = self.client.post(
90+
admin_reverse("cms_import_plugins") + f"?language=en&cms_pagecontent={self.page_content.id}",
91+
post_data
92+
)
93+
self.assertIn(b'<div class="success"></div>', response.content)
94+
95+
def test_import_plugin_views_for_placeholder(self):
96+
placeholder = self.page_content.get_placeholders().get(slot="content")
97+
with self.login_user_context(self.user):
98+
# GET placeholder import
99+
response = self.client.get(
100+
admin_reverse("cms_import_plugins") + f"?language=en&placeholder={placeholder.id}"
101+
)
102+
self.assertEqual(response.templates[0].name, "djangocms_transfer/import_plugins.html")
103+
self.assertEqual(response.context["form"].initial["placeholder"], placeholder)
104+
105+
# empty placeholder import (no existing plugin)
106+
request_path = admin_reverse("cms_import_plugins") + f"?language=en&placeholder={placeholder.id}"
107+
post_data = {
108+
"language": "en", "placeholder": [placeholder.id],
109+
"import_file": SimpleUploadedFile("file.txt", b"[null, null]")
110+
}
111+
with self.assertRaises(IndexError):
112+
self.client.post(path=request_path, data=post_data)
113+
114+
# create plugins in the placeholder
115+
text_plugin = self._create_plugin()
116+
self._add_plugin_to_page("PluginImporter")
117+
self._create_plugin(plugin_type="LinkPlugin", parent=text_plugin)
118+
119+
# empty placeholder import
120+
request_path = admin_reverse("cms_import_plugins") + f"?language=en&placeholder={placeholder.id}"
121+
post_data = {
122+
"language": "en", "placeholder": [placeholder.id],
123+
"import_file": SimpleUploadedFile("file.txt", b"[null, null]")
124+
}
125+
response = self.client.post(path=request_path, data=post_data)
126+
self.assertIn(b'<div class="success"></div>', response.content)
127+
self.assertEqual(response.context[2].template_name, "djangocms_transfer/placeholder_close_frame.html")
128+
129+
structure_data = json.loads(response.context["structure_data"])
130+
# Since the import file is empty, only child plugins gets
131+
# passed to the frontend data bridge
132+
self.assertEqual(len(structure_data["plugins"]), 1)
133+
134+
# POST placeholder import: simple data
135+
post_data["import_file"] = SimpleUploadedFile(
136+
"file.txt",
137+
bytes(json.dumps(self._get_expected_placeholder_export_data()), "utf-8")
138+
)
139+
response = self.client.post(path=request_path, data=post_data)
140+
self.assertIn(b'<div class="success"></div>', response.content)
141+
142+
structure_data = json.loads(response.context["structure_data"])
143+
# newly imported plugin and child plugin gets passed to the frontend data bridge
144+
self.assertEqual(len(structure_data["plugins"]), 2)
145+
146+
def test_import_plugin_views_for_plugin(self):
147+
# create plugins
148+
text_plugin = self._create_plugin()
149+
pluginimporter_plugin = self._create_plugin("PluginImporter")
150+
self._create_plugin(plugin_type="LinkPlugin", parent=text_plugin)
151+
152+
with self.login_user_context(self.user):
153+
# GET plugin import
154+
response = self.client.get(
155+
admin_reverse("cms_import_plugins") + f"?language=en&plugin={text_plugin.pk}"
156+
)
157+
self.assertEqual(response.templates[0].name, "djangocms_transfer/import_plugins.html")
158+
self.assertEqual(response.context["form"].initial["plugin"], text_plugin.cmsplugin_ptr)
159+
160+
# empty POST plugin import
161+
request_path = admin_reverse("cms_import_plugins") + f"?language=en&plugin={text_plugin.pk}"
162+
post_data = {
163+
"language": "en", "plugin": [text_plugin.id],
164+
"import_file": SimpleUploadedFile("file.txt", b"[null, null]")
165+
}
166+
with self.assertRaises(IndexError):
167+
self.client.post(path=request_path, data=post_data)
168+
169+
# plugin import on TextPlugin
170+
post_data["import_file"] = SimpleUploadedFile(
171+
"file.txt",
172+
bytes(json.dumps(self._get_expected_placeholder_export_data()), "utf-8")
173+
)
174+
response = self.client.post(path=request_path, data=post_data)
175+
self.assertIn(b'<div class="success"></div>', response.content)
176+
177+
# plugin import on PluginImporterPlugin
178+
request_path = admin_reverse("cms_import_plugins") + f"?language=en&plugin={pluginimporter_plugin.pk}"
179+
post_data = {"language": "en", "plugin": [pluginimporter_plugin.id]}
180+
post_data["import_file"] = SimpleUploadedFile(
181+
"file.txt",
182+
bytes(json.dumps(self._get_expected_placeholder_export_data()), "utf-8")
183+
)
184+
response = self.client.post(path=request_path, data=post_data)
185+
self.assertIn(b'<div class="success"></div>', response.content)
186+
187+
def test_export_plugin_views(self):
188+
with self.login_user_context(self.user):
189+
response = self.client.get(admin_reverse("cms_export_plugins"))
190+
self.assertEqual(response.content, b"Form received unexpected values.")
191+
192+
del self.user # self.get_request() checks for "user" attribute
193+
self.assertRaises(
194+
PermissionDenied,
195+
self.plugin_importer.export_plugins_view,
196+
self.get_request()
197+
)
198+
199+
def test_export_plugin_views_for_plugin(self):
200+
# create plugins
201+
text_plugin = self._create_plugin()
202+
self._create_plugin(plugin_type="LinkPlugin", parent=text_plugin)
203+
204+
with self.login_user_context(self.user):
205+
response = self.client.get(
206+
admin_reverse("cms_export_plugins") + f"?language=en&plugin={text_plugin.pk}"
207+
)
208+
self.assertEqual(response.status_code, 200)
209+
exported_data = json.loads(response.content)
210+
# exported content should be TextPlugin and its child (LinkPlugin).
211+
self.assertEqual(len(exported_data), 2)
212+
self.assertEqual(list(map(lambda i: i["plugin_type"], exported_data)), ["TextPlugin", "LinkPlugin"])
213+
214+
def test_export_plugin_views_for_placeholder(self):
215+
placeholder = self.page_content.get_placeholders().get(slot="content")
216+
# create plugins in the placeholder
217+
text_plugin = self._create_plugin()
218+
self._add_plugin_to_page("PluginImporter")
219+
self._create_plugin(plugin_type="LinkPlugin", parent=text_plugin)
220+
221+
with self.login_user_context(self.user):
222+
response = self.client.get(
223+
admin_reverse("cms_export_plugins") + f"?language=en&placeholder={placeholder.pk}"
224+
)
225+
self.assertEqual(response.status_code, 200)
226+
exported_data = json.loads(response.content)
227+
self.assertEqual(len(exported_data), 3)
228+
self.assertEqual(list(map(lambda i: i["plugin_type"], exported_data)), ["TextPlugin", "LinkPlugin", "PluginImporter"])
229+
230+
def test_export_plugin_views_for_page(self):
231+
with self.login_user_context(self.user):
232+
response = self.client.get(
233+
admin_reverse("cms_export_plugins") + f"?language=en&cms_pagecontent={self.page_content.pk}"
234+
)
235+
self.assertEqual(response.status_code, 200)
236+
exported_data = json.loads(response.content)
237+
self.assertEqual(exported_data[0]["placeholder"], "content")
238+
self.assertEqual(len(exported_data[0]["plugins"]), 0)

0 commit comments

Comments
 (0)