forked from ungleich-public/dynamicweb
889 lines
36 KiB
Python
889 lines
36 KiB
Python
import sys
|
|
from io import StringIO
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth import get_user_model
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.contrib.sites.models import Site
|
|
from django.core import management
|
|
from django.core.management import CommandError
|
|
from django.db import connection, models
|
|
from django.test.utils import CaptureQueriesContext, override_settings
|
|
from djangocms_text.cms_plugins import TextPlugin
|
|
|
|
from cms.api import add_plugin, create_page, create_page_content
|
|
from cms.management.commands.subcommands.list import plugin_report
|
|
from cms.models import Page, PageUrl
|
|
from cms.models.placeholdermodel import Placeholder
|
|
from cms.models.pluginmodel import CMSPlugin
|
|
from cms.test_utils.fixtures.navextenders import NavextendersFixture
|
|
from cms.test_utils.project.sampleapp.cms_apps import SampleApp
|
|
from cms.test_utils.testcases import CMSTestCase
|
|
from cms.test_utils.util.context_managers import apphooks
|
|
|
|
APPHOOK = "SampleApp"
|
|
PLUGIN = "TextPlugin"
|
|
|
|
TEST_INSTALLED_APPS = [
|
|
"django.contrib.auth",
|
|
"cms",
|
|
"menus",
|
|
"sekizai",
|
|
"treebeard",
|
|
] + settings.PLUGIN_APPS
|
|
if settings.AUTH_USER_MODEL == "emailuserapp.EmailUser":
|
|
TEST_INSTALLED_APPS.append("cms.test_utils.project.emailuserapp")
|
|
if settings.AUTH_USER_MODEL == "customuserapp.User":
|
|
TEST_INSTALLED_APPS.append("cms.test_utils.project.customuserapp")
|
|
|
|
|
|
class ManagementTestCase(CMSTestCase):
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_list_apphooks(self):
|
|
with apphooks(SampleApp):
|
|
out = StringIO()
|
|
create_page('Hello Title', "nav_playground.html", "en", apphook=APPHOOK)
|
|
self.assertEqual(Page.objects.filter(application_urls=APPHOOK).count(), 1)
|
|
management.call_command(
|
|
"cms",
|
|
"list",
|
|
"apphooks",
|
|
interactive=False,
|
|
stdout=out,
|
|
)
|
|
self.assertEqual(out.getvalue(), "SampleApp (active)\n")
|
|
|
|
def test_uninstall_apphooks_without_apphook(self):
|
|
with apphooks():
|
|
out = StringIO()
|
|
management.call_command(
|
|
"cms",
|
|
"uninstall",
|
|
"apphooks",
|
|
APPHOOK,
|
|
interactive=False,
|
|
stdout=out,
|
|
)
|
|
self.assertEqual(out.getvalue(), "no 'SampleApp' apphooks found\n")
|
|
|
|
def test_fix_tree(self):
|
|
create_page("home", "nav_playground.html", "en")
|
|
page1 = create_page("page", "nav_playground.html", "en")
|
|
page1.depth = 3
|
|
page1.numchild = 4
|
|
page1.path = "00100010"
|
|
page1.save()
|
|
out = StringIO()
|
|
management.call_command('cms', 'fix-tree', interactive=False, stdout=out)
|
|
self.assertEqual(out.getvalue(), 'fixing page tree\nfixing page URLs\nall done\n')
|
|
page1 = page1.reload()
|
|
self.assertEqual(page1.path, "0002")
|
|
self.assertEqual(page1.depth, 1)
|
|
self.assertEqual(page1.numchild, 0)
|
|
|
|
def test_fix_tree_regression_5641(self):
|
|
# ref: https://github.com/divio/django-cms/issues/5641
|
|
alpha = create_page("Alpha", "nav_playground.html", "en")
|
|
beta = create_page("Beta", "nav_playground.html", "en")
|
|
gamma = create_page("Gamma", "nav_playground.html", "en")
|
|
delta = create_page("Delta", "nav_playground.html", "en")
|
|
theta = create_page("Theta", "nav_playground.html", "en")
|
|
|
|
beta.move_page(alpha, position='last-child')
|
|
gamma.move_page(beta, position='last-child')
|
|
delta.move_page(gamma, position='last-child')
|
|
theta.move_page(delta, position='last-child')
|
|
|
|
out = StringIO()
|
|
management.call_command('cms', 'fix-tree', interactive=False, stdout=out)
|
|
|
|
tree = [
|
|
(alpha, '0001'),
|
|
(beta, '00010001'),
|
|
(gamma, '000100010001'),
|
|
(delta, '0001000100010001'),
|
|
(theta, '00010001000100010001'),
|
|
]
|
|
|
|
for page, path in tree:
|
|
self.assertEqual(page.path, path)
|
|
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_uninstall_apphooks_with_apphook(self):
|
|
with apphooks(SampleApp):
|
|
out = StringIO()
|
|
create_page('Hello Title', "nav_playground.html", "en", apphook=APPHOOK)
|
|
self.assertEqual(Page.objects.filter(application_urls=APPHOOK).count(), 1)
|
|
management.call_command(
|
|
"cms",
|
|
"uninstall",
|
|
"apphooks",
|
|
APPHOOK,
|
|
interactive=False,
|
|
stdout=out,
|
|
)
|
|
self.assertEqual(out.getvalue(), "1 'SampleApp' apphooks uninstalled\n")
|
|
self.assertEqual(Page.objects.filter(application_urls=APPHOOK).count(), 0)
|
|
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_list_plugins(self):
|
|
out = StringIO()
|
|
placeholder = Placeholder.objects.create(slot="test")
|
|
add_plugin(placeholder, TextPlugin, "en", body="en body")
|
|
add_plugin(placeholder, TextPlugin, "en", body="en body")
|
|
link_plugin = add_plugin(placeholder, "LinkPlugin", "en",
|
|
name="A Link", external_link="https://www.django-cms.org")
|
|
self.assertEqual(
|
|
CMSPlugin.objects.filter(plugin_type=PLUGIN).count(),
|
|
2)
|
|
self.assertEqual(
|
|
CMSPlugin.objects.filter(plugin_type="LinkPlugin").count(),
|
|
1)
|
|
|
|
# create a CMSPlugin with an unsaved instance
|
|
instanceless_plugin = CMSPlugin.objects.create(
|
|
position=4,
|
|
language="en",
|
|
plugin_type="TextPlugin",
|
|
placeholder=placeholder,
|
|
)
|
|
|
|
# create a bogus CMSPlugin to simulate one which used to exist but
|
|
# is no longer installed
|
|
CMSPlugin.objects.create(
|
|
position=5,
|
|
language="en",
|
|
plugin_type="BogusPlugin",
|
|
placeholder=placeholder,
|
|
)
|
|
|
|
management.call_command('cms', 'list', 'plugins', interactive=False, stdout=out)
|
|
report = plugin_report()
|
|
|
|
# there should be reports for three plugin types
|
|
self.assertEqual(
|
|
len(report),
|
|
3)
|
|
|
|
# check the bogus plugin
|
|
for plugin in report:
|
|
if plugin["type"] == "BogusPlugin":
|
|
self.assertEqual(len(plugin["instances"]), 1)
|
|
self.assertFalse(plugin["model"])
|
|
if plugin["type"] == "LinkPlugin":
|
|
self.assertEqual(len(plugin["instances"]), 1)
|
|
if plugin["type"] == "TextPlugin":
|
|
self.assertEqual(len(plugin["instances"]), 3)
|
|
|
|
# check the link plugin
|
|
link_plugins_report = report[1]
|
|
self.assertEqual(
|
|
link_plugins_report["model"],
|
|
link_plugin.__class__)
|
|
|
|
self.assertEqual(
|
|
link_plugins_report["type"],
|
|
'LinkPlugin')
|
|
|
|
self.assertEqual(
|
|
link_plugins_report["instances"][0].get_plugin_instance()[0],
|
|
link_plugin)
|
|
|
|
# check the text plugins
|
|
text_plugins_report = report[2]
|
|
self.assertEqual(
|
|
text_plugins_report["model"],
|
|
TextPlugin.model)
|
|
|
|
self.assertEqual(
|
|
text_plugins_report["type"],
|
|
'TextPlugin')
|
|
|
|
self.assertEqual(
|
|
len(text_plugins_report["instances"]),
|
|
3)
|
|
|
|
self.assertEqual(
|
|
text_plugins_report["instances"][2],
|
|
instanceless_plugin)
|
|
|
|
self.assertEqual(
|
|
text_plugins_report["unsaved_instances"],
|
|
[instanceless_plugin])
|
|
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_delete_orphaned_plugins(self):
|
|
placeholder = Placeholder.objects.create(slot="test")
|
|
add_plugin(placeholder, TextPlugin, "en", body="en body")
|
|
add_plugin(placeholder, "LinkPlugin", "en",
|
|
name="A Link", external_link="https://www.django-cms.org")
|
|
|
|
instanceless_plugin = CMSPlugin(
|
|
language="en", plugin_type="TextPlugin")
|
|
instanceless_plugin.save()
|
|
|
|
# create a bogus CMSPlugin to simulate one which used to exist but
|
|
# is no longer installed
|
|
bogus_plugin = CMSPlugin(language="en", plugin_type="BogusPlugin", placeholder=placeholder)
|
|
placeholder.add_plugin(bogus_plugin)
|
|
add_plugin(placeholder, TextPlugin, "en", body="en body")
|
|
|
|
# Test detached placeholders case
|
|
User = get_user_model()
|
|
content_type = ContentType.objects.get_for_model(
|
|
get_user_model()) # Use an existing model
|
|
obj = User.objects.create(username="test_delete_orphaned")
|
|
ph_detached = Placeholder.objects.create(slot="test_detached", content_type=content_type, object_id=obj.pk)
|
|
pl_detached = add_plugin(ph_detached, TextPlugin, "en", body="en body")
|
|
obj.delete() # Now ph_detached is detached
|
|
|
|
# Test model class not existing
|
|
bogus_ct = ContentType.objects.create(app_label="bogus", model="bogus")
|
|
ph_bogus = Placeholder.objects.create(slot="test_bogus", content_type=bogus_ct, object_id=1)
|
|
pl_bogus = add_plugin(ph_bogus, TextPlugin, "en", body="en body")
|
|
|
|
report = plugin_report()
|
|
|
|
# there should be reports for three plugin types
|
|
self.assertEqual(
|
|
len(report),
|
|
3)
|
|
|
|
# check the bogus plugin
|
|
for plugin in report:
|
|
if plugin["type"] == "BogusPlugin":
|
|
self.assertEqual(len(plugin["instances"]), 1)
|
|
self.assertFalse(plugin["model"])
|
|
if plugin["type"] == "LinkPlugin":
|
|
self.assertEqual(len(plugin["instances"]), 1)
|
|
if plugin["type"] == "TextPlugin":
|
|
self.assertEqual(len(plugin["instances"]), 5)
|
|
|
|
out = StringIO()
|
|
management.call_command('cms', 'delete-orphaned-plugins', interactive=False, stdout=out)
|
|
report = plugin_report()
|
|
|
|
# there should be reports for two plugin types (one should have been deleted)
|
|
self.assertEqual(
|
|
len(report),
|
|
2)
|
|
|
|
# check the link plugin
|
|
link_plugins_report = report[0]
|
|
self.assertEqual(
|
|
len(link_plugins_report["instances"]),
|
|
1)
|
|
|
|
# check the text plugins
|
|
text_plugins_report = report[1]
|
|
self.assertEqual(
|
|
len(text_plugins_report["instances"]),
|
|
2)
|
|
|
|
self.assertEqual(
|
|
len(text_plugins_report["unsaved_instances"]),
|
|
0)
|
|
|
|
# No gaps in plugin tree
|
|
max_position = placeholder.cmsplugin_set.aggregate(models.Max('position'))['position__max']
|
|
self.assertEqual(max_position, 3)
|
|
|
|
self.assertEqual(CMSPlugin.objects.all().count(), 3)
|
|
|
|
# Then both detached placeholders and their plugins should be gone
|
|
self.assertFalse(Placeholder.objects.filter(pk=ph_detached.pk).exists())
|
|
self.assertFalse(Placeholder.objects.filter(pk=ph_bogus.pk).exists())
|
|
self.assertFalse(CMSPlugin.objects.filter(pk=pl_detached.pk).exists())
|
|
self.assertFalse(CMSPlugin.objects.filter(pk=pl_bogus.pk).exists())
|
|
|
|
# check stdout
|
|
output = out.getvalue()
|
|
self.assertIn("1 uninstalled plugins", output)
|
|
self.assertIn("1 plugins with unsaved instances", output)
|
|
self.assertIn("2 detached placeholders", output)
|
|
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_plugin_report_query_count_does_not_scale_with_instances(self):
|
|
"""
|
|
plugin_report() must resolve unsaved instances with bulk queries.
|
|
|
|
It previously called get_plugin_instance() once per plugin row, an
|
|
N+1 that made ``cms check`` and ``cms delete-orphaned-plugins`` appear
|
|
to hang on databases with many plugins. Guard against a regression by
|
|
asserting the query count is independent of the number of instances.
|
|
"""
|
|
placeholder = Placeholder.objects.create(slot="test")
|
|
|
|
def count_queries():
|
|
with CaptureQueriesContext(connection) as ctx:
|
|
# Force full evaluation so deferred querysets are counted too.
|
|
for plugin in plugin_report():
|
|
list(plugin["instances"])
|
|
list(plugin["unsaved_instances"])
|
|
return len(ctx.captured_queries)
|
|
|
|
add_plugin(placeholder, TextPlugin, "en", body="en body")
|
|
baseline = count_queries()
|
|
|
|
for _ in range(10):
|
|
add_plugin(placeholder, TextPlugin, "en", body="en body")
|
|
scaled = count_queries()
|
|
|
|
self.assertEqual(
|
|
baseline,
|
|
scaled,
|
|
"plugin_report() issues a query per plugin instance (N+1 regression)",
|
|
)
|
|
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_delete_orphaned_plugins_keeps_positions_consecutive(self):
|
|
placeholder = Placeholder.objects.create(slot="test")
|
|
add_plugin(placeholder, TextPlugin, "en", body="first")
|
|
CMSPlugin.objects.create(
|
|
position=placeholder.get_next_plugin_position("en", insert_order="last"),
|
|
language="en",
|
|
plugin_type="BogusPlugin",
|
|
placeholder=placeholder,
|
|
)
|
|
add_plugin(placeholder, TextPlugin, "en", body="second")
|
|
CMSPlugin.objects.create(
|
|
position=placeholder.get_next_plugin_position("en", insert_order="last"),
|
|
language="en",
|
|
plugin_type="BogusPlugin",
|
|
placeholder=placeholder,
|
|
)
|
|
add_plugin(placeholder, TextPlugin, "en", body="third")
|
|
|
|
self.assertEqual(
|
|
list(placeholder.cmsplugin_set.order_by("position").values_list("plugin_type", "position")),
|
|
[
|
|
("TextPlugin", 1),
|
|
("BogusPlugin", 2),
|
|
("TextPlugin", 3),
|
|
("BogusPlugin", 4),
|
|
("TextPlugin", 5),
|
|
],
|
|
)
|
|
|
|
management.call_command("cms", "delete-orphaned-plugins", interactive=False, stdout=StringIO())
|
|
|
|
self.assertEqual(
|
|
list(placeholder.cmsplugin_set.order_by("position").values_list("plugin_type", "position")),
|
|
[
|
|
("TextPlugin", 1),
|
|
("TextPlugin", 2),
|
|
("TextPlugin", 3),
|
|
],
|
|
)
|
|
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_delete_plugin_uses_stale_position_from_prefetched_instance(self):
|
|
placeholder = Placeholder.objects.create(slot="test")
|
|
add_plugin(placeholder, TextPlugin, "en", body="first")
|
|
CMSPlugin.objects.create(
|
|
position=placeholder.get_next_plugin_position("en", insert_order="last"),
|
|
language="en",
|
|
plugin_type="BogusPlugin",
|
|
placeholder=placeholder,
|
|
)
|
|
add_plugin(placeholder, TextPlugin, "en", body="second")
|
|
CMSPlugin.objects.create(
|
|
position=placeholder.get_next_plugin_position("en", insert_order="last"),
|
|
language="en",
|
|
plugin_type="BogusPlugin",
|
|
placeholder=placeholder,
|
|
)
|
|
add_plugin(placeholder, TextPlugin, "en", body="third")
|
|
|
|
stale_plugins = list(CMSPlugin.objects.filter(plugin_type="BogusPlugin").order_by("position"))
|
|
self.assertEqual([plugin.position for plugin in stale_plugins], [2, 4])
|
|
|
|
placeholder.delete_plugin(stale_plugins[0])
|
|
|
|
self.assertEqual(stale_plugins[1].position, 4)
|
|
stale_plugins[1].refresh_from_db()
|
|
|
|
self.assertEqual(stale_plugins[1].position, 3)
|
|
|
|
def test_uninstall_plugins_without_plugin(self):
|
|
out = StringIO()
|
|
management.call_command('cms', 'uninstall', 'plugins', PLUGIN, interactive=False, stdout=out)
|
|
self.assertEqual(out.getvalue(), "no 'TextPlugin' plugins found\n")
|
|
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_uninstall_plugins_with_plugin(self):
|
|
out = StringIO()
|
|
placeholder = Placeholder.objects.create(slot="test")
|
|
add_plugin(placeholder, TextPlugin, "en", body="en body")
|
|
self.assertEqual(CMSPlugin.objects.filter(plugin_type=PLUGIN).count(), 1)
|
|
management.call_command('cms', 'uninstall', 'plugins', PLUGIN, interactive=False, stdout=out)
|
|
self.assertEqual(out.getvalue(), "1 'TextPlugin' plugins uninstalled\n")
|
|
self.assertEqual(CMSPlugin.objects.filter(plugin_type=PLUGIN).count(), 0)
|
|
|
|
def test_for_running_only_cms_command(self):
|
|
with self.assertRaises(CommandError) as e:
|
|
management.call_command('cms')
|
|
self.assertEqual(str(e.exception), 'Error: one of the available sub commands must be provided')
|
|
|
|
|
|
class PageFixtureManagementTestCase(NavextendersFixture, CMSTestCase):
|
|
|
|
def _fill_page_body(self, page, lang):
|
|
placeholders = page.get_placeholders(lang)
|
|
ph_en = placeholders.get(slot="body")
|
|
# add misc plugins
|
|
mcol1 = add_plugin(ph_en, "MultiColumnPlugin", lang, position="first-child")
|
|
add_plugin(ph_en, "ColumnPlugin", lang, position="first-child", target=mcol1)
|
|
col2 = add_plugin(ph_en, "ColumnPlugin", lang, position="first-child", target=mcol1)
|
|
mcol2 = add_plugin(ph_en, "MultiColumnPlugin", lang, position="first-child", target=col2)
|
|
add_plugin(ph_en, "ColumnPlugin", lang, position="first-child", target=mcol2)
|
|
col4 = add_plugin(ph_en, "ColumnPlugin", lang, position="first-child", target=mcol2)
|
|
# add a *nested* link plugin
|
|
add_plugin(ph_en, "LinkPlugin", lang, target=col4, name="A Link", external_link="https://www.django-cms.org")
|
|
placeholder = placeholders.get(slot="right-column")
|
|
placeholder.save()
|
|
add_plugin(placeholder, "TextPlugin", lang, body="example content")
|
|
|
|
def setUp(self):
|
|
pages = Page.objects.all()
|
|
for page in pages:
|
|
self._fill_page_body(page, "en")
|
|
|
|
def test_copy_langs(self):
|
|
"""
|
|
Various checks here:
|
|
|
|
* plugins are exactly doubled, half per language with no orphaned plugin
|
|
* the bottom-most plugins in the nesting chain maintain the same position and the same content
|
|
* the top-most plugin are of the same type
|
|
"""
|
|
site = 1
|
|
number_start_plugins = CMSPlugin.objects.all().count()
|
|
number_urls = PageUrl.objects.filter(language='en').count()
|
|
|
|
out = StringIO()
|
|
management.call_command(
|
|
'cms', 'copy', 'lang', '--from-lang=en', '--to-lang=de', '--userid=%d' % self.get_superuser().id,
|
|
interactive=False, stdout=out
|
|
)
|
|
pages = Page.objects.on_site(site)
|
|
for page in pages:
|
|
self.assertEqual({'en', 'de'}, set(page.get_languages()))
|
|
# These asserts that no orphaned plugin exists
|
|
self.assertEqual(CMSPlugin.objects.all().count(), number_start_plugins * 2)
|
|
self.assertEqual(CMSPlugin.objects.filter(language='en').count(), number_start_plugins)
|
|
self.assertEqual(CMSPlugin.objects.filter(language='de').count(), number_start_plugins)
|
|
|
|
root_page = Page.objects.get_home(site)
|
|
root_plugins = CMSPlugin.objects.filter(placeholder=root_page.get_placeholders('en').get(slot="body"))
|
|
|
|
first_plugin_en, _ = root_plugins.get(language='en', parent=None).get_plugin_instance()
|
|
first_plugin_de, _ = root_plugins.get(language='de', parent=None).get_plugin_instance()
|
|
|
|
self.assertEqual(first_plugin_en.plugin_type, first_plugin_de.plugin_type)
|
|
|
|
link_en, _ = root_plugins.get(language='en', plugin_type='LinkPlugin').get_plugin_instance()
|
|
link_de, _ = root_plugins.get(language='de', plugin_type='LinkPlugin').get_plugin_instance()
|
|
|
|
self.assertEqual(link_en.external_link, link_de.external_link)
|
|
self.assertEqual(link_en.position, link_de.position)
|
|
|
|
stack_plugins = CMSPlugin.objects.filter(placeholder=root_page.get_placeholders('en').get(slot="right-column"))
|
|
|
|
stack_text_en, _ = stack_plugins.get(language='en', plugin_type='TextPlugin').get_plugin_instance()
|
|
stack_text_de, _ = stack_plugins.get(language='de', plugin_type='TextPlugin').get_plugin_instance()
|
|
|
|
self.assertEqual(stack_text_en.plugin_type, stack_text_de.plugin_type)
|
|
self.assertEqual(stack_text_en.body, stack_text_de.body)
|
|
|
|
self.assertEqual(PageUrl.objects.filter(language='de').count(), number_urls)
|
|
|
|
def test_copy_langs_no_content(self):
|
|
"""
|
|
Various checks here:
|
|
|
|
* page structure is copied
|
|
* no plugin is copied
|
|
"""
|
|
site = 1
|
|
number_start_plugins = CMSPlugin.objects.all().count()
|
|
|
|
out = StringIO()
|
|
management.call_command(
|
|
'cms', 'copy', 'lang', '--from-lang=en', '--to-lang=de', '--skip-content',
|
|
'--userid=%d' % self.get_superuser().id,
|
|
interactive=False, stdout=out
|
|
)
|
|
pages = Page.objects.on_site(site)
|
|
for page in pages:
|
|
self.assertEqual({'en', 'de'}, set(page.get_languages()))
|
|
# These asserts that no orphaned plugin exists
|
|
self.assertEqual(CMSPlugin.objects.all().count(), number_start_plugins)
|
|
self.assertEqual(CMSPlugin.objects.filter(language='en').count(), number_start_plugins)
|
|
self.assertEqual(CMSPlugin.objects.filter(language='de').count(), 0)
|
|
|
|
root_page = Page.objects.get_home(site)
|
|
root_plugins = CMSPlugin.objects.filter(
|
|
placeholder=root_page.get_placeholders('en').get(slot="body"))
|
|
|
|
first_plugin_en, _ = root_plugins.get(language='en', parent=None).get_plugin_instance()
|
|
first_plugin_de = None
|
|
with self.assertRaises(CMSPlugin.DoesNotExist):
|
|
first_plugin_de, _ = root_plugins.get(language='de', parent=None).get_plugin_instance()
|
|
|
|
self.assertIsNone(first_plugin_de)
|
|
|
|
stack_plugins = CMSPlugin.objects.filter(
|
|
placeholder=root_page.get_placeholders('en').get(slot="right-column")
|
|
)
|
|
|
|
stack_text_en, _ = stack_plugins.get(language='en',
|
|
plugin_type='TextPlugin').get_plugin_instance()
|
|
with self.assertRaises(CMSPlugin.DoesNotExist):
|
|
stack_text_de, _ = stack_plugins.get(language='de',
|
|
plugin_type='TextPlugin').get_plugin_instance()
|
|
|
|
def test_copy_sites(self):
|
|
"""
|
|
Various checks here:
|
|
|
|
* plugins are exactly doubled, half per site with no orphaned plugin
|
|
* the bottom-most plugins in the nesting chain maintain the same position and the same content
|
|
* the top-most plugin are of the same type
|
|
"""
|
|
site_1_pk = 1
|
|
site_1 = Site.objects.get(pk=site_1_pk)
|
|
site_2 = Site.objects.create(name='site 2')
|
|
site_2_pk = site_2.pk
|
|
phs = []
|
|
for page in Page.objects.on_site(site_1_pk):
|
|
phs.extend(page.get_placeholders('en').values_list('pk', flat=True))
|
|
number_start_plugins = CMSPlugin.objects.filter(placeholder__in=phs).count()
|
|
|
|
out = StringIO()
|
|
management.call_command(
|
|
'cms', 'copy', 'site', '--from-site=%s' % site_1_pk, '--to-site=%s' % site_2_pk,
|
|
'--userid=%d' % self.get_superuser().id,
|
|
stdout=out
|
|
)
|
|
|
|
pages_1 = list(Page.objects.on_site(site_1).order_by('path'))
|
|
pages_2 = list(Page.objects.on_site(site_2).order_by('path'))
|
|
for index, page in enumerate(pages_1):
|
|
self.assertEqual(page.get_title('en'), pages_2[index].get_title('en'))
|
|
self.assertEqual(page.depth, pages_2[index].depth)
|
|
|
|
phs_1 = []
|
|
phs_2 = []
|
|
for page in Page.objects.on_site(site_1_pk):
|
|
phs_1.extend(page.get_placeholders('en').values_list('pk', flat=True))
|
|
for page in Page.objects.on_site(site_2_pk):
|
|
phs_2.extend(page.get_placeholders('en').values_list('pk', flat=True))
|
|
|
|
# These assert that no orphaned plugin exists
|
|
self.assertEqual(CMSPlugin.objects.filter(placeholder__in=phs_1).count(), number_start_plugins)
|
|
self.assertEqual(CMSPlugin.objects.filter(placeholder__in=phs_2).count(), number_start_plugins)
|
|
|
|
root_page_1 = Page.objects.get_home(site_1)
|
|
root_page_2 = Page.objects.get_home(site_2)
|
|
root_plugins_1 = CMSPlugin.objects.filter(placeholder=root_page_1.get_placeholders('en').get(slot="body"))
|
|
root_plugins_2 = CMSPlugin.objects.filter(placeholder=root_page_2.get_placeholders('en').get(slot="body"))
|
|
|
|
first_plugin_1, _ = root_plugins_1.get(language='en', parent=None).get_plugin_instance()
|
|
first_plugin_2, _ = root_plugins_2.get(language='en', parent=None).get_plugin_instance()
|
|
|
|
self.assertEqual(first_plugin_1.plugin_type, first_plugin_2.plugin_type)
|
|
|
|
link_1, _ = root_plugins_1.get(language='en', plugin_type='LinkPlugin').get_plugin_instance()
|
|
link_2, _ = root_plugins_2.get(language='en', plugin_type='LinkPlugin').get_plugin_instance()
|
|
|
|
self.assertEqual(link_1.external_link, link_2.external_link)
|
|
self.assertEqual(link_1.position, link_2.position)
|
|
|
|
def test_copy_existing_title(self):
|
|
"""
|
|
Even if a title already exists the copy is successful, the original
|
|
title remains untouched
|
|
"""
|
|
site = 1
|
|
number_start_plugins = CMSPlugin.objects.all().count()
|
|
|
|
# create an empty title language
|
|
root_page = Page.objects.get_home(site)
|
|
create_page_content("de", "root page de", root_page)
|
|
|
|
out = StringIO()
|
|
management.call_command(
|
|
'cms', 'copy', 'lang', '--from-lang=en', '--to-lang=de',
|
|
'--userid=%d' % self.get_superuser().id,
|
|
interactive=False, stdout=out
|
|
)
|
|
pages = Page.objects.on_site(site)
|
|
for page in pages:
|
|
self.assertEqual({'en', 'de'}, set(page.get_languages()))
|
|
|
|
# Original PageContent untouched
|
|
self.assertEqual("root page de", Page.objects.get_home(site).get_title("de"))
|
|
|
|
# Plugins still copied
|
|
self.assertEqual(CMSPlugin.objects.all().count(), number_start_plugins * 2)
|
|
self.assertEqual(CMSPlugin.objects.filter(language='en').count(), number_start_plugins)
|
|
self.assertEqual(CMSPlugin.objects.filter(language='de').count(), number_start_plugins)
|
|
|
|
def test_copy_filled_placeholder(self):
|
|
"""
|
|
If an existing title in the target language has plugins in a placeholder
|
|
that placeholder is skipped
|
|
"""
|
|
site = 1
|
|
number_start_plugins = CMSPlugin.objects.all().count()
|
|
|
|
# create an empty title language
|
|
root_page = Page.objects.get_home(site)
|
|
create_page_content("de", "root page de", root_page)
|
|
ph = root_page.get_placeholders('en').get(slot="body")
|
|
add_plugin(ph, "TextPlugin", "de", body="Hello World")
|
|
|
|
out = StringIO()
|
|
management.call_command(
|
|
'cms', 'copy', 'lang', '--from-lang=en', '--to-lang=de',
|
|
'--userid=%d' % self.get_superuser().id,
|
|
interactive=False, stdout=out
|
|
)
|
|
|
|
self.assertEqual(CMSPlugin.objects.filter(language='en').count(), number_start_plugins)
|
|
# one placeholder (with 7 plugins) is skipped, so the difference must be 6
|
|
self.assertEqual(CMSPlugin.objects.filter(language='de').count(), number_start_plugins - 6)
|
|
|
|
def test_copy_filled_placeholder_force_copy(self):
|
|
"""
|
|
If an existing title in the target language has plugins in a placeholder
|
|
and the command is called with *force-copy*, the plugins are copied on
|
|
top of the existing one
|
|
"""
|
|
site = 1
|
|
number_start_plugins = CMSPlugin.objects.all().count()
|
|
|
|
# create an empty title language
|
|
root_page = Page.objects.get_home(site)
|
|
create_page_content("de", "root page de", root_page)
|
|
ph = root_page.get_placeholders('en').get(slot="body")
|
|
add_plugin(ph, "TextPlugin", "de", body="Hello World")
|
|
|
|
root_plugins = CMSPlugin.objects.filter(placeholder=ph)
|
|
text_de_orig, _ = root_plugins.get(language='de', plugin_type='TextPlugin').get_plugin_instance()
|
|
|
|
out = StringIO()
|
|
management.call_command(
|
|
'cms', 'copy', 'lang', '--from-lang=en', '--to-lang=de', '--force',
|
|
'--userid=%d' % self.get_superuser().id,
|
|
interactive=False,
|
|
stdout=out
|
|
)
|
|
|
|
CMSPlugin.objects.filter(placeholder=root_page.get_placeholders('en').get(slot="body"))
|
|
|
|
self.assertEqual(CMSPlugin.objects.filter(language='en').count(), number_start_plugins)
|
|
# we have an existing plugin in one placeholder, so we have one more
|
|
self.assertEqual(CMSPlugin.objects.filter(language='de').count(), number_start_plugins + 1)
|
|
|
|
def test_copy_from_non_existing_lang(self):
|
|
"""
|
|
If an existing title in the target language has plugins in a placeholder
|
|
and the command is called with *force-copy*, the plugins are copied on
|
|
top of the existing one
|
|
"""
|
|
site = 1
|
|
out = StringIO()
|
|
management.call_command(
|
|
'cms', 'copy', 'lang', '--from-lang=de', '--to-lang=fr',
|
|
'--userid=%d' % self.get_superuser().id,
|
|
verbosity=3,
|
|
interactive=False, stdout=out
|
|
)
|
|
text = out.getvalue()
|
|
page_count = Page.objects.on_site(site).count() + 1
|
|
for idx in range(1, page_count):
|
|
self.assertTrue("Skipping page page%d, language de not defined" % idx in text)
|
|
|
|
def test_copy_site_safe(self):
|
|
"""
|
|
Check that copy of languages on one site does not interfere with other
|
|
sites
|
|
"""
|
|
site_other = 1
|
|
site_active = 2
|
|
origina_site1_langs = {}
|
|
|
|
number_start_plugins = CMSPlugin.objects.all().count()
|
|
site_obj = Site.objects.create(domain="sample2.com", name="sample2.com", pk=site_active)
|
|
|
|
for page in Page.objects.on_site(1):
|
|
origina_site1_langs[page.pk] = set(page.get_languages())
|
|
|
|
p1 = create_page('page1', in_navigation=True, language='de', template='nav_playground.html', site=site_obj)
|
|
create_page('page4', in_navigation=True, language='de', template='nav_playground.html', site=site_obj)
|
|
create_page(
|
|
'page2', in_navigation=True, parent=p1, language='de', template='nav_playground.html', site=site_obj
|
|
)
|
|
|
|
for page in Page.objects.on_site(site_active):
|
|
self._fill_page_body(page, 'de')
|
|
|
|
number_site2_plugins = CMSPlugin.objects.all().count() - number_start_plugins
|
|
|
|
out = StringIO()
|
|
management.call_command(
|
|
'cms', 'copy', 'lang', '--from-lang=de', '--to-lang=fr', '--site=%s' % site_active,
|
|
'--userid=%d' % self.get_superuser().id,
|
|
interactive=False, stdout=out
|
|
)
|
|
|
|
for page in Page.objects.on_site(site_other):
|
|
self.assertEqual(origina_site1_langs[page.pk], set(page.get_languages()))
|
|
|
|
for page in Page.objects.on_site(site_active):
|
|
self.assertEqual({'de', 'fr'}, set(page.get_languages()))
|
|
|
|
# plugins for site 1
|
|
self.assertEqual(CMSPlugin.objects.filter(language='en').count(), number_start_plugins)
|
|
# plugins for site 2 de
|
|
self.assertEqual(CMSPlugin.objects.filter(language='de').count(), number_site2_plugins)
|
|
# plugins for site 2 fr
|
|
self.assertEqual(CMSPlugin.objects.filter(language='fr').count(), number_site2_plugins)
|
|
# global number of plugins
|
|
self.assertEqual(CMSPlugin.objects.all().count(), number_start_plugins + number_site2_plugins * 2)
|
|
|
|
def test_copy_bad_languages(self):
|
|
out = StringIO()
|
|
with self.assertRaises(CommandError) as command_error:
|
|
management.call_command(
|
|
'cms', 'copy', 'lang', '--from-lang=it', '--to-lang=fr',
|
|
'--userid=%d' % self.get_superuser().id,
|
|
interactive=False, stdout=out
|
|
)
|
|
|
|
self.assertEqual(
|
|
str(command_error.exception),
|
|
'Both languages have to be present in settings.LANGUAGES and settings.CMS_LANGUAGES'
|
|
)
|
|
|
|
|
|
class DjangoCmsCommandTestCase(CMSTestCase):
|
|
"""
|
|
Test that the django-cms command can be run without any subcommand
|
|
"""
|
|
def test_djangocms_command_returns_version(self):
|
|
from cms import __version__
|
|
from cms.management import djangocms
|
|
|
|
out = StringIO()
|
|
old_stdout = sys.stdout
|
|
sys.stdout = out
|
|
try:
|
|
djangocms.execute_from_command_line(['djangocms', '--version'])
|
|
finally:
|
|
sys.stdout = old_stdout
|
|
result = out.getvalue()
|
|
self.assertEqual(result, __version__ + "\n")
|
|
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_delete_orphaned_plugins_with_versioning_simulation(self):
|
|
# We simulate versioning (meaning rows might be filtered by the default manager).
|
|
# We will create a user, but assign it a custom manager that filters it out.
|
|
User = get_user_model()
|
|
|
|
class FilteredUser(User):
|
|
class Meta:
|
|
proxy = True
|
|
app_label = 'auth'
|
|
|
|
class MyManager(models.Manager):
|
|
def get_queryset(self):
|
|
return super().get_queryset().none()
|
|
|
|
objects = MyManager()
|
|
|
|
content_type = ContentType.objects.get_for_model(FilteredUser, for_concrete_model=False)
|
|
self.assertEqual(content_type.model_class(), FilteredUser)
|
|
obj = FilteredUser.objects.create(username="test_versioning")
|
|
|
|
ph = Placeholder.objects.create(slot="test_versioning_slot", content_type=content_type, object_id=obj.pk)
|
|
add_plugin(ph, TextPlugin, "en", body="en body versioning")
|
|
|
|
out = StringIO()
|
|
management.call_command('cms', 'delete-orphaned-plugins', interactive=False, stdout=out)
|
|
|
|
# Test that the placeholder wasn't deleted
|
|
self.assertTrue(Placeholder.objects.filter(pk=ph.pk).exists())
|
|
obj.delete()
|
|
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_delete_orphaned_plugins_keeps_positions_consecutive(self):
|
|
placeholder = Placeholder.objects.create(slot="test")
|
|
add_plugin(placeholder, TextPlugin, "en", body="first")
|
|
CMSPlugin.objects.create(
|
|
position=placeholder.get_next_plugin_position("en", insert_order="last"),
|
|
language="en",
|
|
plugin_type="BogusPlugin",
|
|
placeholder=placeholder,
|
|
)
|
|
add_plugin(placeholder, TextPlugin, "en", body="second")
|
|
CMSPlugin.objects.create(
|
|
position=placeholder.get_next_plugin_position("en", insert_order="last"),
|
|
language="en",
|
|
plugin_type="BogusPlugin",
|
|
placeholder=placeholder,
|
|
)
|
|
add_plugin(placeholder, TextPlugin, "en", body="third")
|
|
|
|
self.assertEqual(
|
|
list(placeholder.cmsplugin_set.order_by("position").values_list("plugin_type", "position")),
|
|
[
|
|
("TextPlugin", 1),
|
|
("BogusPlugin", 2),
|
|
("TextPlugin", 3),
|
|
("BogusPlugin", 4),
|
|
("TextPlugin", 5),
|
|
],
|
|
)
|
|
|
|
management.call_command("cms", "delete-orphaned-plugins", interactive=False, stdout=StringIO())
|
|
|
|
self.assertEqual(
|
|
list(placeholder.cmsplugin_set.order_by("position").values_list("plugin_type", "position")),
|
|
[
|
|
("TextPlugin", 1),
|
|
("TextPlugin", 2),
|
|
("TextPlugin", 3),
|
|
],
|
|
)
|
|
|
|
@override_settings(INSTALLED_APPS=TEST_INSTALLED_APPS)
|
|
def test_delete_plugin_uses_stale_position_from_prefetched_instance(self):
|
|
placeholder = Placeholder.objects.create(slot="test")
|
|
add_plugin(placeholder, TextPlugin, "en", body="first")
|
|
CMSPlugin.objects.create(
|
|
position=placeholder.get_next_plugin_position("en", insert_order="last"),
|
|
language="en",
|
|
plugin_type="BogusPlugin",
|
|
placeholder=placeholder,
|
|
)
|
|
add_plugin(placeholder, TextPlugin, "en", body="second")
|
|
CMSPlugin.objects.create(
|
|
position=placeholder.get_next_plugin_position("en", insert_order="last"),
|
|
language="en",
|
|
plugin_type="BogusPlugin",
|
|
placeholder=placeholder,
|
|
)
|
|
add_plugin(placeholder, TextPlugin, "en", body="third")
|
|
|
|
stale_plugins = list(CMSPlugin.objects.filter(plugin_type="BogusPlugin").order_by("position"))
|
|
self.assertEqual([plugin.position for plugin in stale_plugins], [2, 4])
|
|
|
|
placeholder.delete_plugin(stale_plugins[0])
|
|
|
|
self.assertEqual(stale_plugins[1].position, 4)
|
|
stale_plugins[1].refresh_from_db()
|
|
|
|
self.assertEqual(stale_plugins[1].position, 3)
|