From 06f0a8d3bec81d7f031baad18f12ebcf7f22a4d7 Mon Sep 17 00:00:00 2001 From: Conor Holden Date: Mon, 16 Dec 2024 13:52:05 +0100 Subject: [PATCH] :sparkles: support new placeholders ands static alias --- README.rst | 14 ++++----- djangocms_export_page/export/common.py | 42 +++++++++++++++++--------- djangocms_export_page/settings.py | 26 +++++++++++++++- pyproject.toml | 6 ++-- testapp/models.py | 25 +++++++++++++-- testapp/settings.py | 1 + testapp/templates/blog.html | 12 ++++++++ testapp/templates/test.html | 4 ++- tests/factories.py | 28 ++++++++++++++++- tests/test_models.py | 24 +++++++++++++-- tests/test_pages.py | 25 +++++++++++++-- 11 files changed, 175 insertions(+), 32 deletions(-) create mode 100644 testapp/templates/blog.html diff --git a/README.rst b/README.rst index 90a6e1e..578b526 100644 --- a/README.rst +++ b/README.rst @@ -116,16 +116,16 @@ add the following to the model class: It's better to put the PlaceholderField (here `content`) in a separate section. -Static Placeholders +Static Aliases ------------------- -If you also want to export the static placeholders of a page, some extra configuration -is required. There is a setting called `EXPORT_STATIC_PLACEHOLDERS`. +If you also want to export the static aliases of a page, some extra configuration +is required. There is a setting called `EXPORT_STATIC_ALIASES`. .. code-block:: python - EXPORT_STATIC_PLACEHOLDERS = { - 'template_name': ['static_placeholder_code'] + EXPORT_STATIC_ALIASES = { + 'template_name': ['static_alias_code'] } So with the cms settings it will look like this: @@ -134,7 +134,7 @@ So with the cms settings it will look like this: # test.html
- {% static_placeholder 'test-placeholder' %} + {% static_alias 'test-placeholder' %}
# settings.py @@ -142,7 +142,7 @@ So with the cms settings it will look like this: ('test.html', _('Test page')), ] - EXPORT_STATIC_PLACEHOLDERS = { + EXPORT_STATIC_ALIASES = { 'test.html': ['test-placeholder'] } diff --git a/djangocms_export_page/export/common.py b/djangocms_export_page/export/common.py index c8fa174..e04dcc7 100644 --- a/djangocms_export_page/export/common.py +++ b/djangocms_export_page/export/common.py @@ -3,8 +3,10 @@ from django.contrib.sites.shortcuts import get_current_site from django.db.models import CharField, ForeignKey, TextField -from cms.models import CMSPlugin, Placeholder, StaticPlaceholder +from cms.models import CMSPlugin, Page, Placeholder from cms.models.fields import PlaceholderField + +from djangocms_alias.models import Alias from djangocms_page_meta.utils import get_page_meta from djangocms_export_page import settings @@ -101,9 +103,14 @@ def get_base_url(self): ) def get_page_url(self): - return "{domain}{url}".format( - domain=self.get_base_url(), url=self.object.get_absolute_url(self.language) - ) + + # requires language to get teh URL + if isinstance(self.object, Page): + url = self.object.get_absolute_url(self.language) + else: + url = self.object.get_absolute_url() + + return "{domain}{url}".format(domain=self.get_base_url(), url=url) def export(self): raise NotImplementedError @@ -123,9 +130,9 @@ def get_data(self): plugins = self.get_ordered_plugins(placeholder, self.language) for plugin in plugins: components.extend(self.get_components(plugin)) - elif isinstance(section, StaticPlaceholder): - placeholder = section - plugins = self.get_ordered_plugins(placeholder.public, self.language) + elif isinstance(section, Alias): + placeholder = section.get_placeholder(self.language) + plugins = self.get_ordered_plugins(placeholder, self.language) for plugin in plugins: components.extend(self.get_components(plugin)) @@ -150,9 +157,9 @@ def get_sections(self): ) ) if hasattr(self.object, "template"): - codes = settings.EXPORT_STATIC_PLACEHOLDERS.get(self.object.template) + codes = settings.EXPORT_STATIC_ALIASES.get(self.object.template) if codes: - queryset = StaticPlaceholder.objects.filter(code__in=codes) + queryset = Alias.objects.filter(static_code__in=codes) sections.extend(self.get_static_placeholders(queryset)) if self.page_meta: @@ -259,11 +266,19 @@ def get_defined_components(self, obj, field_names=None): in the placeholder fields. """ field_names = field_names if field_names else obj._export_page["fields"] + model_fields = {f.name: f for f in obj._meta.fields} - fields = [(name, model_fields[name]) for name in field_names] - regular_fields = [f for f in fields if not isinstance(f[1], PlaceholderField)] - placeholder_fields = [f for f in fields if isinstance(f[1], PlaceholderField)] + regular_fields = [] + placeholders = [] + + for field_name in field_names: + if field_name in model_fields: + regular_fields.append((field_name, model_fields[field_name])) + elif hasattr(obj, field_name): + attribute = getattr(obj, field_name) + if isinstance(attribute, Placeholder): + placeholders.append(attribute) components = [ Component( @@ -273,8 +288,7 @@ def get_defined_components(self, obj, field_names=None): ) ] - for field_name, field in placeholder_fields: - placeholder = getattr(obj, field_name) + for placeholder in placeholders: plugins = self.get_ordered_plugins(placeholder, self.language) for plugin in plugins: components.extend(self.get_components(plugin)) diff --git a/djangocms_export_page/settings.py b/djangocms_export_page/settings.py index 40197d4..d952909 100644 --- a/djangocms_export_page/settings.py +++ b/djangocms_export_page/settings.py @@ -1,12 +1,36 @@ import sys +from warnings import warn from django.conf import settings class _Settings(object): + + _placeholder_dep_msg = ( + "Static Placeholders are disabled form Django CMS 4. " + "Pleas use djangocms-alias's static_alias " + "and use the EXPORT_STATIC_ALIASES setting" + ) + @property def EXPORT_STATIC_PLACEHOLDERS(self): - return getattr(settings, "EXPORT_STATIC_PLACEHOLDERS", {}) + + warn(self._placeholder_dep_msg, DeprecationWarning, stacklevel=2) + + setting = getattr(settings, "EXPORT_STATIC_PLACEHOLDERS", {}) + return setting + + @property + def EXPORT_STATIC_ALIASES(self): + + old_setting = getattr(settings, "EXPORT_STATIC_PLACEHOLDERS", {}) + + if old_setting: + warn(self._placeholder_dep_msg, DeprecationWarning, stacklevel=2) + + return old_setting + + return getattr(settings, "EXPORT_STATIC_ALIASES", {}) def __getattr__(self, name): return globals()[name] diff --git a/pyproject.toml b/pyproject.toml index c47c9b9..6f87fa3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,8 @@ requires-python = ">=3.10" dependencies = [ "django~=3.2.0", "django-cms>=4.1", - "djangocms-page-meta@git+https://github.com/maykinmedia/djangocms-page-meta@77f464d93bcc4cb525814632b92a82738da5ef8e", + "djangocms-alias", + "djangocms-page-meta-maykin", "python-docx", ] @@ -65,8 +66,9 @@ profile = "black" combine_as_imports = true skip = ["env", ".tox", ".history", ".eggs"] known_django = "django" +known_cms = "cms" known_first_party="djangocms_export_page" -sections=["FUTURE", "STDLIB", "DJANGO", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] +sections=["FUTURE", "STDLIB", "DJANGO", "CMS", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/testapp/models.py b/testapp/models.py index dfb13c7..8b8c433 100644 --- a/testapp/models.py +++ b/testapp/models.py @@ -1,20 +1,37 @@ from django.db import models from django.urls import NoReverseMatch, reverse +from django.utils.functional import cached_property + +from cms.models.fields import PlaceholderRelationField +from cms.utils.placeholder import get_placeholder_from_slot from autoslug import AutoSlugField # Create your models here. class Blog(models.Model): + + _export_page = { + "sections": [ + {"name": "Meta", "fields": ["title", "slug", "date_posted"]}, + {"name": "Body", "fields": ["content"]}, + ], + } + title = models.CharField(max_length=100) - content = models.TextField() slug = AutoSlugField(populate_from="title", editable=True, blank=True) date_posted = models.DateTimeField(auto_now_add=True) + placeholders = PlaceholderRelationField() + + @cached_property + def content(self): + return get_placeholder_from_slot(self.placeholders, "blog_content") + def __str__(self): return self.title - def get_absolute_url(self, *args, **kwargs): + def get_absolute_url(self): try: return reverse( "blog:detail", @@ -28,3 +45,7 @@ def get_absolute_url(self, *args, **kwargs): def get_title(self): return self.title + + @property + def template(self): + return "blog.html" diff --git a/testapp/settings.py b/testapp/settings.py index 8cba2b7..d7dd3bd 100644 --- a/testapp/settings.py +++ b/testapp/settings.py @@ -24,6 +24,7 @@ "easy_thumbnails", "djangocms_text_ckeditor", "meta", + "djangocms_alias", "djangocms_page_meta", "djangocms_export_page", "testapp", diff --git a/testapp/templates/blog.html b/testapp/templates/blog.html new file mode 100644 index 0000000..9738928 --- /dev/null +++ b/testapp/templates/blog.html @@ -0,0 +1,12 @@ + +{% load cms_tags djangocms_alias_tags %} + + +{% block content %} + + {% render_placeholder 'instance.blog_content' %} + + + {% static_alias 'footer' %} +{% endblock content %} + diff --git a/testapp/templates/test.html b/testapp/templates/test.html index facf9cf..f6b5a2d 100644 --- a/testapp/templates/test.html +++ b/testapp/templates/test.html @@ -1,10 +1,12 @@ -{% load cms_tags %} +{% load cms_tags djangocms_alias_tags %} {% block content %} {% placeholder 'test' %} + + {% static_alias 'footer' %} {% endblock content %} diff --git a/tests/factories.py b/tests/factories.py index 95d0de7..4d71ee9 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -1,4 +1,5 @@ import factory +from djangocms_alias.models import Alias, AliasContent, Category from testapp.models import Blog @@ -8,4 +9,29 @@ class Meta: model = Blog title = factory.Faker("sentence", nb_words=4) - content = factory.Faker("text") + + +class CategoryFactory(factory.django.DjangoModelFactory): + class Meta: + model = Category + + +class AliasContentFactory(factory.django.DjangoModelFactory): + + class Meta: + model = AliasContent + + +class AliasFactory(factory.django.DjangoModelFactory): + + category = factory.SubFactory(CategoryFactory) + + static_code = factory.Faker("word") + + log = factory.RelatedFactory( + AliasContentFactory, + factory_related_name="alias", + ) + + class Meta: + model = Alias diff --git a/tests/test_models.py b/tests/test_models.py index 59c308e..d592a95 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,9 +1,11 @@ -from django.test import RequestFactory, TestCase +from django.test import RequestFactory, TestCase, override_settings + +from cms.api import add_plugin from djangocms_export_page.export.common import PageExport from djangocms_export_page.export.docx import DocxPageExport -from .factories import BlogFactory +from .factories import AliasFactory, BlogFactory class ExportModelTests(TestCase): @@ -12,6 +14,9 @@ def setUp(self): self.language = "nl" self.request = RequestFactory().get(self.object.get_absolute_url()) + static_alias = AliasFactory(static_code="footer") + self.static_placeholder = static_alias.get_placeholder(self.language) + def test_model_export(self): export = DocxPageExport(self.request, self.object, language=self.language) export_file = export.export() @@ -22,3 +27,18 @@ def test_page_url(self): self.assertEqual( export.page_url, "http://example.com" + self.object.get_absolute_url() ) + + @override_settings(EXPORT_STATIC_ALIASES={"blog.html": ["footer"]}) + def test_page_with_placeholders(self): + add_plugin( + self.object.content, "TextPlugin", self.language, body="Body Content" + ) + + add_plugin( + self.static_placeholder, "TextPlugin", self.language, body="footer info" + ) + export = DocxPageExport(self.request, self.object, language=self.language) + + data = export.get_data() + self.assertEqual(data[1].components[1].fields[0].value, "Body Content") + self.assertEqual(data[2].components[0].fields[0].value, "footer info") diff --git a/tests/test_pages.py b/tests/test_pages.py index d1c5f7a..9a968b7 100644 --- a/tests/test_pages.py +++ b/tests/test_pages.py @@ -1,12 +1,14 @@ from unittest.mock import patch -from django.test import RequestFactory, TestCase +from django.test import RequestFactory, TestCase, override_settings from cms.api import add_plugin, create_page, create_page_content + from meta.views import Meta from djangocms_export_page.export.common import Field, PageExport from djangocms_export_page.export.docx import DocxPageExport +from tests.factories import AliasFactory class ExportPageTests(TestCase): @@ -23,6 +25,9 @@ def setUp(self): ) self.request = RequestFactory().get("/nl/") + static_alias = AliasFactory(static_code="footer") + self.static_placeholder = static_alias.get_placeholder(self.language) + def test_export_non_implemented(self): with self.assertRaises(NotImplementedError): PageExport(self.request, self.page, language=self.language).export() @@ -41,7 +46,6 @@ def test_page_url(self): export = PageExport(self.request, self.page, language=self.language) self.assertEqual(export.page_url, "http://example.com/nl/title-nl/") - en_export = PageExport(self.request, self.page, language="en") en_export = PageExport(self.request, self.page, language="en") self.assertEqual(en_export.page_url, "http://example.com/en/title-en/") @@ -74,3 +78,20 @@ def test_page_with_control_char_in_text(self): self.assertEqual( export.get_data()[0].components[0].fields[0].value, "Some text" ) + + @override_settings(EXPORT_STATIC_ALIASES={"test.html": ["footer"]}) + def test_page_with_static_alias(self): + + add_plugin(self.placeholder, "TextPlugin", "nl", body="Some text \f") + + add_plugin(self.static_placeholder, "TextPlugin", "nl", body="footer info") + + export = DocxPageExport(self.request, self.page, language=self.language) + + self.assertEqual( + export.get_data()[0].components[0].fields[0].value, "Some text" + ) + + self.assertEqual( + export.get_data()[1].components[0].fields[0].value, "footer info" + )