diff --git a/djangocms_snippet/admin.py b/djangocms_snippet/admin.py index 2e4cdb1e..ba5c9f2f 100644 --- a/djangocms_snippet/admin.py +++ b/djangocms_snippet/admin.py @@ -38,6 +38,8 @@ class SnippetAdmin(*snippet_admin_classes): formfield_overrides = { models.TextField: {'widget': Textarea(attrs=text_area_attrs)} } + # This was move here from model, otherwise first() and last() return the same when handling grouper queries + ordering = ('name',) class Meta: model = Snippet diff --git a/djangocms_snippet/cms_plugins.py b/djangocms_snippet/cms_plugins.py index bf432da9..63e691fc 100644 --- a/djangocms_snippet/cms_plugins.py +++ b/djangocms_snippet/cms_plugins.py @@ -8,6 +8,7 @@ from cms.plugin_pool import plugin_pool from .models import SnippetPtr +from .utils import show_draft_content CACHE_ENABLED = getattr(settings, "DJANGOCMS_SNIPPET_CACHE", False) @@ -22,19 +23,20 @@ class SnippetPlugin(CMSPluginBase): cache = CACHE_ENABLED def render(self, context, instance, placeholder): + snippet = instance.snippet_grouper.snippet(show_editable=show_draft_content(context["request"])) try: - if instance.snippet.template: + if snippet.template: context = context.flatten() - context.update({"html": mark_safe(instance.snippet.html)}) - t = template.loader.get_template(instance.snippet.template) + context.update({"html": mark_safe(snippet.html)}) + t = template.loader.get_template(snippet.template) content = t.render(context) else: # only html provided - t = template.Template(instance.snippet.html) + t = template.Template(snippet.html) content = t.render(context) except template.TemplateDoesNotExist: content = _("Template %(template)s does not exist.") % { - "template": instance.snippet.template + "template": snippet.template } except Exception as e: content = escape(str(e)) @@ -43,7 +45,7 @@ def render(self, context, instance, placeholder): { "placeholder": placeholder, "object": instance, - "html": mark_safe(instance.snippet.html), + "html": mark_safe(snippet.html), "content": content, } ) diff --git a/djangocms_snippet/forms.py b/djangocms_snippet/forms.py index 1afac618..55b5f993 100644 --- a/djangocms_snippet/forms.py +++ b/djangocms_snippet/forms.py @@ -23,6 +23,7 @@ class Meta: "html", "slug", "snippet_grouper", + "template", ) def __init__(self, *args, **kwargs): @@ -55,8 +56,10 @@ def clean(self): @transaction.atomic def save(self, **kwargs): - if not self.cleaned_data.get("snippet_grouper"): - super().save(commit=False) - self.save_m2m() - self.instance.snippet_grouper = SnippetGrouper.objects.create() - return super().save() + commit = kwargs.get("commit", True) + snippet = super().save(commit=False) + if commit: + if not hasattr(snippet, "snippet_grouper"): + snippet.snippet_grouper = SnippetGrouper.objects.create() + snippet.save() + return snippet diff --git a/djangocms_snippet/migrations/0014_auto_20211019_0522.py b/djangocms_snippet/migrations/0014_auto_20211019_0522.py new file mode 100644 index 00000000..1f70ec08 --- /dev/null +++ b/djangocms_snippet/migrations/0014_auto_20211019_0522.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2.24 on 2021-10-19 10:22 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('djangocms_snippet', '0013_auto_20210915_0751'), + ] + + operations = [ + migrations.AlterModelOptions( + name='snippet', + options={'verbose_name': 'Snippet', 'verbose_name_plural': 'Snippets'}, + ), + ] diff --git a/djangocms_snippet/models.py b/djangocms_snippet/models.py index 459cea08..177caddc 100644 --- a/djangocms_snippet/models.py +++ b/djangocms_snippet/models.py @@ -5,12 +5,17 @@ from cms.models import CMSPlugin +from djangocms_versioning.constants import DRAFT, PUBLISHED + # Search is enabled by default to keep backwards compatibility. SEARCH_ENABLED = getattr(settings, 'DJANGOCMS_SNIPPET_SEARCH', False) class SnippetGrouper(models.Model): + """ + The Grouper model for snippet, this is required for versioning + """ @property def name(self): snippet_qs = Snippet._base_manager.filter( @@ -18,6 +23,16 @@ def name(self): ) return snippet_qs.first().name or super().__str__ + def snippet(self, show_editable=False): + if show_editable: + # When in "edit" or "preview" mode we should be able to see the latest content + return Snippet._base_manager.filter( + versions__state__in=[DRAFT, PUBLISHED], + snippet_grouper=self, + ).order_by("-pk").first() + # When in "live" mode we should only be able to see the default published version + return Snippet.objects.filter(snippet_grouper=self).first() + def __str__(self): return self.name @@ -68,7 +83,6 @@ def get_preview_url(self): ) class Meta: - ordering = ['name'] verbose_name = _('Snippet') verbose_name_plural = _('Snippets') @@ -95,7 +109,3 @@ class SnippetPtr(CMSPlugin): class Meta: verbose_name = _('Snippet Ptr') verbose_name_plural = _('Snippet Ptrs') - - @property - def snippet(self): - return self.snippet_grouper.snippet_set.first() diff --git a/djangocms_snippet/utils.py b/djangocms_snippet/utils.py new file mode 100644 index 00000000..13d88b7f --- /dev/null +++ b/djangocms_snippet/utils.py @@ -0,0 +1,11 @@ +from cms.toolbar.utils import get_toolbar_from_request + + +def show_draft_content(request=None): + """ + Returns True if draft contents should be shown. + """ + if not request: + return False + request_toolbar = get_toolbar_from_request(request) + return request_toolbar.edit_mode_active or request_toolbar.preview_mode_active diff --git a/tests/test_admin.py b/tests/test_admin.py index 2e007de9..e1ea1563 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -8,14 +8,17 @@ from djangocms_snippet import admin as snippet_admin from djangocms_snippet import cms_config from djangocms_snippet.forms import SnippetForm -from djangocms_snippet.models import Snippet - -from .utils.factories import SnippetWithVersionFactory +from djangocms_snippet.models import Snippet, SnippetGrouper class SnippetAdminTestCase(CMSTestCase): def setUp(self): - self.snippet = SnippetWithVersionFactory() + self.snippet = Snippet.objects.create( + name="Test Snippet", + slug="test-snippet", + html="
Hello World
") - self.assertEqual(plugin.snippet.slug, "plugin_snippet") + self.assertEqual(result_snippet.name, "plugin_snippet") + self.assertEqual(result_snippet.html, "Hello World
") + self.assertEqual(result_snippet.slug, "plugin_snippet") with self.login_user_context(self.superuser): response = self.client.get(request_url) @@ -111,9 +93,9 @@ def test_template_rendering(self): self.language, snippet_grouper=snippet_grouper, ) - - self.assertEqual(plugin.snippet.name, "plugin_snippet") - self.assertEqual(plugin.snippet.slug, "plugin_snippet") + result_snippet = plugin.snippet_grouper.snippet(True) + self.assertEqual(result_snippet.name, "plugin_snippet") + self.assertEqual(result_snippet.slug, "plugin_snippet") with self.login_user_context(self.superuser): response = self.client.get(request_url) @@ -144,3 +126,84 @@ def test_failing_template_rendering(self): response = self.client.get(request_url) self.assertContains(response, "Template some_template does not exist") + + +class SnippetPluginVersioningRenderTestCase(CMSTestCase): + def setUp(self): + self.language = "en" + self.superuser = self.get_superuser() + snippet_grouper = SnippetGrouper.objects.create() + # Create a draft snippet, to be published later + self.snippet = Snippet.objects.create( + name="plugin_snippet", + html="live content
", + slug="plugin_snippet", + snippet_grouper=snippet_grouper, + ) + + # Publish the snippet + snippet_version = Version.objects.create( + content=self.snippet, + created_by=self.superuser, + created=datetime.datetime.now() + ) + snippet_version.publish(user=self.superuser) + # Copy the snippet to create a draft + draft_user = self.get_staff_page_user() + draft_snippet_version = snippet_version.copy(draft_user) + self.draft_snippet = draft_snippet_version.content + self.draft_snippet.html = "draft content
" + self.draft_snippet.save() + + # Create a page + self.page = create_page( + title="help", + template="page.html", + language=self.language, + created_by=self.superuser, + ) + # Publish its page content + self.pagecontent = PageContent._base_manager.filter(page=self.page, language=self.language).first() + self.pagecontent_version = self.pagecontent.versions.first() + self.pagecontent_version.publish(self.superuser) + + # Copy our published pagecontent to make a draft + draft_pagecontent_version = self.pagecontent_version.copy(self.superuser) + self.draft_pagecontent = draft_pagecontent_version.content + + # Add plugin to our published page! + add_plugin( + self.pagecontent.placeholders.get(slot="content"), + "SnippetPlugin", + self.language, + snippet_grouper=self.snippet.snippet_grouper, + ) + # Add plugin to our draft page + add_plugin( + self.draft_pagecontent.placeholders.get(slot="content"), + "SnippetPlugin", + self.language, + snippet_grouper=self.draft_snippet.snippet_grouper, + ) + + def test_correct_versioning_state_published_snippet_and_page(self): + """ + If a page is published, the published snippet should be rendered + """ + # Request for published page + request_url = self.page.get_absolute_url(self.language) + with self.login_user_context(self.superuser): + response = self.client.get(request_url) + + self.assertContains(response, "live content
") + + def test_correct_versioning_state_draft_snippet_and_page(self): + """ + If we have a draft, the draft snippet should be rendered. + """ + # Request for draft page + request_url = get_object_edit_url(self.draft_pagecontent, "en") + with self.login_user_context(self.superuser): + response = self.client.get(request_url) + + self.assertContains(response, "draft content
") diff --git a/tests/utils/factories.py b/tests/utils/factories.py index 9d6210c6..af8d18ce 100644 --- a/tests/utils/factories.py +++ b/tests/utils/factories.py @@ -6,8 +6,11 @@ from cms.models import Placeholder import factory +from factory.fuzzy import ( + FuzzyInteger, + FuzzyText, +) from djangocms_versioning.models import Version -from factory.fuzzy import FuzzyInteger, FuzzyText from djangocms_snippet.models import Snippet, SnippetGrouper, SnippetPtr