From 8caca66b66ac2ac4b4c377707dfa9e1b5c06cad2 Mon Sep 17 00:00:00 2001 From: Bruksx Date: Sun, 4 Oct 2026 12:26:48 +0100 Subject: [PATCH] fix djangocms blog models --- .gitignore | 3 + datacenterlight/factories.py | 21 - datacenterlight/test_views.py | 2 +- digitalglarus/templates/contact.html | 10 +- digitalglarus/templates/supporters.html | 6 +- digitalglarus/tests.py | 476 ++++++++++++++++++ digitalglarus/views.py | 6 +- ...orytranslation_unique_together_and_more.py | 2 +- djangocms_blog/models.py | 31 +- factories.py | 101 ++++ utils/views.py | 25 +- vendor/stored_messages/models.py | 2 +- 12 files changed, 640 insertions(+), 45 deletions(-) delete mode 100644 datacenterlight/factories.py create mode 100644 digitalglarus/tests.py create mode 100644 factories.py diff --git a/.gitignore b/.gitignore index a41d813d..bf420ce3 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ secret-key 2024-* 2025-* 2023-* + + +files/ \ No newline at end of file diff --git a/datacenterlight/factories.py b/datacenterlight/factories.py deleted file mode 100644 index 3bbe6e17..00000000 --- a/datacenterlight/factories.py +++ /dev/null @@ -1,21 +0,0 @@ -import factory -from django.contrib.sites.models import Site - -from datacenterlight.cms_models import CMSIntegration - - -class SiteFactory(factory.django.DjangoModelFactory): - class Meta: - model = Site - django_get_or_create = ("domain",) - - domain = factory.Sequence(lambda n: f"example{n}.test") - name = factory.LazyAttribute(lambda obj: obj.domain) - - -class CMSIntegrationFactory(factory.django.DjangoModelFactory): - class Meta: - model = CMSIntegration - - name = factory.Sequence(lambda n: f"integration-{n}") - domain = factory.SubFactory(SiteFactory) \ No newline at end of file diff --git a/datacenterlight/test_views.py b/datacenterlight/test_views.py index f0b1bbcf..12d3767a 100644 --- a/datacenterlight/test_views.py +++ b/datacenterlight/test_views.py @@ -22,7 +22,7 @@ from datacenterlight.views import ( show_error, ) from utils.forms import BillingAddressFormSignup -from .factories import CMSIntegrationFactory +from factories import CMSIntegrationFactory class SessionRequestMixin: diff --git a/digitalglarus/templates/contact.html b/digitalglarus/templates/contact.html index ba12c5ae..c913bd39 100755 --- a/digitalglarus/templates/contact.html +++ b/digitalglarus/templates/contact.html @@ -14,17 +14,17 @@ {% endfor %}

- {% static_placeholder "digital_glarus_contact_title" %} + {% placeholder "digital_glarus_contact_title" %}


- {% static_placeholder "digital_glarus_contact_location" %} + {% placeholder "digital_glarus_contact_location" %}

- {% static_placeholder "digital_glarus_contact_information" %} + {% placeholder "digital_glarus_contact_information" %}
 
@@ -36,10 +36,10 @@

- {% static_placeholder "digital_glarus_contact_form_title" %} + {% placeholder "digital_glarus_contact_form_title" %}


-

{% static_placeholder "digital_glarus_contact_form_text" %}

+

{% placeholder "digital_glarus_contact_form_text" %}

{% csrf_token %}
diff --git a/digitalglarus/templates/supporters.html b/digitalglarus/templates/supporters.html index 2c53f11c..0ccb8387 100644 --- a/digitalglarus/templates/supporters.html +++ b/digitalglarus/templates/supporters.html @@ -10,13 +10,13 @@

- {% static_placeholder "digital_glarus_supporters_title" %} + {% placeholder "digital_glarus_supporters_title" %}


- {% static_placeholder "digital_glarus_supporters_info" %} + {% placeholder "digital_glarus_supporters_info" %}
@@ -27,7 +27,7 @@
{% endfor %}
- {% static_placeholder "digital_glarus_supporters_become_supporter" %} + {% placeholder "digital_glarus_supporters_become_supporter" %}
diff --git a/digitalglarus/tests.py b/digitalglarus/tests.py new file mode 100644 index 00000000..b60b2282 --- /dev/null +++ b/digitalglarus/tests.py @@ -0,0 +1,476 @@ +from datetime import date +from unittest.mock import patch + +from django.contrib.auth import get_user_model +from django.test import RequestFactory, TestCase +from django.urls import reverse + +from digitalglarus.forms import ( + BookingDateForm +) +from utils.forms import ContactUsForm +from digitalglarus.models import ( + BookingPrice, + MembershipType, + Supporter, +) +from digitalglarus.views import ( + IndexView, + SupportusView, + LoginView, + SignupView, + PasswordResetView, + PasswordResetConfirmView, + HistoryView, + MembershipPricingView, + MembershipActivatedView, + ContactView, + BookingSelectDatesView, +) + +from factories import ( + SupporterFactory, + BookingPriceFactory, + CustomUserFactory, + PostFactory, +) + + +User = get_user_model() + + +class IndexViewTests(TestCase): + + def test_get_renders_template(self): + response = self.client.get( + reverse("digitalglarus:landing") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "digitalglarus/index.html", + ) + + +class SupportusViewTests(TestCase): + + @patch("digitalglarus.views.Post.objects.filter") + def test_get_renders_template(self, mock_filter): + mock_filter.return_value.translated.return_value = [] + + response = self.client.get( + reverse("digitalglarus:supportus") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "digitalglarus/supportus.html", + ) + self.assertIn("post_list", response.context) + + @patch("digitalglarus.views.Post.objects.filter") + def test_context_contains_posts(self, mock_filter): + posts = ["post1", "post2"] + + mock_filter.return_value.translated.return_value = posts + + request = RequestFactory().get( + reverse("digitalglarus:supportus") + ) + + view = SupportusView() + view.setup(request) + + context = view.get_context_data() + + self.assertEqual( + context["post_list"], + posts, + ) + + +class HistoryViewTests(TestCase): + + def test_get_renders_template(self): + SupporterFactory() + + response = self.client.get( + reverse("digitalglarus:history") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "digitalglarus/history.html", + ) + + self.assertIn( + "supporters", + response.context, + ) + + def test_context_contains_supporters(self): + supporter = SupporterFactory() + + request = RequestFactory().get( + reverse("digitalglarus:history") + ) + + view = HistoryView() + view.setup(request) + + context = view.get_context_data() + + self.assertIn( + supporter, + context["supporters"], + ) + + +class LoginViewTests(TestCase): + + def test_get_renders_template(self): + response = self.client.get( + reverse("digitalglarus:login") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "digitalglarus/login.html", + ) + + def test_get_success_url_without_membership_orders(self): + request = RequestFactory().get( + reverse("digitalglarus:login") + ) + + request.user = CustomUserFactory.create() + + view = LoginView() + view.setup(request) + + result = view.get_success_url() + + self.assertEqual( + result, + reverse("digitalglarus:membership_pricing"), + ) + + +class SignupViewTests(TestCase): + + def test_get_renders_template(self): + response = self.client.get( + reverse("digitalglarus:signup") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "digitalglarus/signup.html", + ) + + def test_success_url(self): + view = SignupView() + + self.assertEqual( + str(view.success_url), + reverse("digitalglarus:login"), + ) + + +class PasswordResetViewTests(TestCase): + + def test_get_renders_template(self): + response = self.client.get( + reverse("digitalglarus:reset_password") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "digitalglarus/reset_password.html", + ) + + +class PasswordResetConfirmViewTests(TestCase): + + def test_template_name(self): + view = PasswordResetConfirmView() + + self.assertEqual( + view.template_name, + "digitalglarus/confirm_reset_password.html", + ) + + def test_success_url(self): + view = PasswordResetConfirmView() + + self.assertEqual( + str(view.success_url), + reverse("digitalglarus:login"), + ) + + +class MembershipPricingViewTests(TestCase): + + @patch( + "digitalglarus.views.MembershipType.objects.last" + ) + def test_get_renders_template(self, mock_last): + membership_type = object() + mock_last.return_value = membership_type + + response = self.client.get( + reverse("digitalglarus:membership_pricing") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "digitalglarus/membership_pricing.html", + ) + + self.assertEqual( + response.context["membership_type"], + membership_type, + ) + + +class MembershipActivatedViewTests(TestCase): + + def test_get_renders_template(self): + session = self.client.session + + session["membership_price"] = 100 + session["membership_dates"] = "01/01/2026 - 01/31/2026" + session.save() + + response = self.client.get( + reverse("digitalglarus:membership_activated") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "digitalglarus/membership_activated.html", + ) + + self.assertEqual( + response.context["membership_price"], + 100, + ) + + self.assertEqual( + response.context["membership_dates"], + "01/01/2026 - 01/31/2026", + ) + + +class ContactViewTests(TestCase): + + def test_get_renders_template(self): + response = self.client.get( + reverse("digitalglarus:contact") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "contact.html", + ) + + def test_get_uses_contact_form(self): + response = self.client.get( + reverse("digitalglarus:contact") + ) + + self.assertIsInstance( + response.context["form"], + ContactUsForm, + ) + + def test_form_valid_redirects_to_contact(self): + view = ContactView() + + request = RequestFactory().post( + reverse("digitalglarus:contact") + ) + + view.setup(request) + + form = ContactUsForm() + + with patch.object( + ContactUsForm, + "is_valid", + return_value=True, + ): + response = view.form_valid(form) + + self.assertEqual( + response.status_code, + 302, + ) + + self.assertEqual( + response.url, + reverse("digitalglarus:contact"), + ) + + +class BookingSelectDatesViewTests(TestCase): + + def test_get_requires_login(self): + response = self.client.get( + reverse("digitalglarus:booking") + ) + + self.assertEqual(response.status_code, 302) + + def test_success_url(self): + view = BookingSelectDatesView() + + self.assertEqual( + str(view.success_url), + reverse("digitalglarus:booking_payment"), + ) + + def test_get_form_kwargs_contains_user(self): + user = CustomUserFactory.create( + email="test@example.com", + password="password123", + ) + + request = RequestFactory().get( + reverse("digitalglarus:booking") + ) + request.user = user + + view = BookingSelectDatesView() + view.setup(request) + + with patch( + "digitalglarus.views.FormView.get_form_kwargs", + return_value={}, + ): + kwargs = view.get_form_kwargs() + + self.assertEqual( + kwargs["user"], + user, + ) + + +class OldFunctionViewTests(TestCase): + + @patch("digitalglarus.views.Post.objects.translated") + def test_blog_renders_template(self, mock_translated): + mock_translated.return_value = [] + + response = self.client.get( + reverse("digitalglarus:blog") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "glarus_blog/post_list.html", + ) + + self.assertIn( + "post_list", + response.context, + ) + + def test_blog_detail_renders_template(self): + post = PostFactory.create() + response = self.client.get( + reverse( + "digitalglarus:blog-detail", + kwargs={"slug": post.slug}, + ) + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "glarus_blog/post_detail.html", + ) + + self.assertIn( + "post", + response.context, + ) + + def test_blog_detail_returns_404_when_post_does_not_exist(self): + with patch( + "digitalglarus.views.Post.objects.translated" + ) as mock_translated: + + mock_translated.return_value.first.return_value = None + + response = self.client.get( + reverse( + "digitalglarus:blog-detail", + kwargs={"slug": "missing-post"}, + ) + ) + + self.assertEqual(response.status_code, 404) + + def test_support_renders_template(self): + response = self.client.get( + reverse("digitalglarus:support") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "digitalglarus/supportus.html", + ) + + def test_supporters_renders_template(self): + SupporterFactory() + + response = self.client.get( + reverse("digitalglarus:supporters") + ) + + self.assertEqual(response.status_code, 200) + self.assertTemplateUsed( + response, + "supporters.html", + ) + + self.assertIn( + "supporters", + response.context, + ) + + +class BookingPaymentViewTests(TestCase): + + def test_get_without_booking_data_redirects_to_booking(self): + response = self.client.get( + reverse("digitalglarus:booking_payment") + ) + + self.assertRedirects( + response, + reverse("digitalglarus:booking"), + fetch_redirect_response=False, + ) + + +class MembershipDeactivateViewTests(TestCase): + + def test_get_requires_login(self): + response = self.client.get( + reverse("digitalglarus:membership_deactivate") + ) + + self.assertEqual(response.status_code, 302) \ No newline at end of file diff --git a/digitalglarus/views.py b/digitalglarus/views.py index 6e620843..248fd855 100644 --- a/digitalglarus/views.py +++ b/digitalglarus/views.py @@ -64,11 +64,9 @@ class LoginView(LoginViewMixin): def get_success_url(self): # redirect to membership orders list if user has at least one. - if self.request.user \ - and MembershipOrder.objects.filter(customer__user=self.request.user): - + membership_order = MembershipOrder.objects.filter(customer__user=self.request.user) + if self.request.user and membership_order: return reverse_lazy('digitalglarus:membership_orders_list') - return reverse_lazy('digitalglarus:membership_pricing') diff --git a/djangocms_blog/migrations/0043_alter_blogcategorytranslation_unique_together_and_more.py b/djangocms_blog/migrations/0043_alter_blogcategorytranslation_unique_together_and_more.py index c6a7492c..36ce38b6 100644 --- a/djangocms_blog/migrations/0043_alter_blogcategorytranslation_unique_together_and_more.py +++ b/djangocms_blog/migrations/0043_alter_blogcategorytranslation_unique_together_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 6.1.1 on 2026-10-02 08:25 +# Generated by Django 6.1.1 on 2026-10-04 11:16 from django.db import migrations, models diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index 0e707b1b..ac15a7e0 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -4,6 +4,7 @@ from aldryn_apphooks_config.fields import AppHookConfigField from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableManager from cms.models import CMSPlugin from cms.models.fields import PlaceholderField, PlaceholderRelationField +from cms.utils.placeholder import get_placeholder_from_slot from django.conf import settings as dj_settings from django.contrib.auth import get_user_model from django.contrib.sites.shortcuts import get_current_site @@ -268,9 +269,33 @@ class Post(KnockerModel, BlogMetaMixin, TranslatableModel): post_text=HTMLField(_("text"), default="", blank=True, configuration="BLOG_POST_TEXT_CKEDITOR"), meta={"unique_together": (("language_code", "slug"),)}, ) - media = PlaceholderRelationField("media", related_name="media") - content = PlaceholderRelationField("post_content", related_name="post_content") - liveblog = PlaceholderRelationField("live_blog", related_name="live_blog") + placeholders = PlaceholderRelationField() + + @cached_property + def media(self): + return get_placeholder_from_slot( + self.placeholders, + "media", + ) + + @cached_property + def content(self): + return get_placeholder_from_slot( + self.placeholders, + "post_content", + ) + + @cached_property + def liveblog(self): + return get_placeholder_from_slot( + self.placeholders, + "live_blog", + ) + + enable_liveblog = models.BooleanField( + verbose_name=_("enable liveblog on post"), + default=False, + ) enable_liveblog = models.BooleanField(verbose_name=_("enable liveblog on post"), default=False) objects = GenericDateTaggedManager() diff --git a/factories.py b/factories.py new file mode 100644 index 00000000..f99728d8 --- /dev/null +++ b/factories.py @@ -0,0 +1,101 @@ +import factory +from django.contrib.sites.models import Site +from django.conf import settings + +from datacenterlight.cms_models import CMSIntegration +from digitalglarus.models import BookingPrice, Supporter +from membership.models import CustomUser +from djangocms_blog.models import Post + + +class SiteFactory(factory.django.DjangoModelFactory): + class Meta: + model = Site + django_get_or_create = ("domain",) + + domain = factory.Sequence(lambda n: f"example{n}.test") + name = factory.LazyAttribute(lambda obj: obj.domain) + + +class CMSIntegrationFactory(factory.django.DjangoModelFactory): + class Meta: + model = CMSIntegration + + name = factory.Sequence(lambda n: f"integration-{n}") + domain = factory.SubFactory(SiteFactory) + + +class SupporterFactory(factory.django.DjangoModelFactory): + class Meta: + model = Supporter + + name = factory.Sequence(lambda n: f"Supporter {n}") + + +class BookingPriceFactory(factory.django.DjangoModelFactory): + class Meta: + model = BookingPrice + + price_per_day = 10 + special_month_price = 100 + + +class CustomUserFactory(factory.django.DjangoModelFactory): + class Meta: + model = CustomUser + + site = factory.LazyFunction(lambda: Site.objects.get(pk=1)) + name = factory.Sequence(lambda n: f"Test User {n}") + email = factory.Sequence(lambda n: f"user{n}@example.com") + username = factory.Sequence(lambda n: f"user{n}") + + validated = 1 + in_ldap = False + is_admin = False + + @factory.post_generation + def password(self, create, extracted, **kwargs): + if not create: + return + + password = extracted or "testpassword123" + self.set_password(password) + self.save(update_fields=["password"]) + + +class PostFactory(factory.django.DjangoModelFactory): + class Meta: + model = Post + + author = factory.SubFactory(CustomUserFactory) + + publish = True + include_in_rss = True + enable_comments = False + enable_liveblog = False + + @classmethod + def _create(cls, model_class, *args, **kwargs): + language_code = kwargs.pop( + "language_code", + settings.LANGUAGE_CODE, + ) + + title = kwargs.pop("title", "Test Post") + slug = kwargs.pop("slug", "test-post") + + post = model_class(*args, **kwargs) + + post.set_current_language(language_code) + post.title = title + post.slug = slug + post.subtitle = "" + post.abstract = "" + post.meta_description = "" + post.meta_keywords = "" + post.meta_title = "" + post.post_text = "" + + post.save() + + return post \ No newline at end of file diff --git a/utils/views.py b/utils/views.py index dc728ce2..9a9cf26b 100644 --- a/utils/views.py +++ b/utils/views.py @@ -13,6 +13,7 @@ from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import cache_control from django.views.generic import FormView, CreateView +from django.utils.decorators import method_decorator from datacenterlight.utils import get_cms_integration from hosting.forms import UserHostingKeyForm @@ -51,28 +52,40 @@ class LoginViewMixin(FormView): success_url = None def get_success_url(self): - next_url = self.request.POST.get('next', self.success_url) + next_url = self.request.POST.get("next", self.success_url) + if not next_url: return self.success_url + return next_url def form_valid(self, form): - email = form.cleaned_data.get('email') - password = form.cleaned_data.get('password') + email = form.cleaned_data.get("email") + password = form.cleaned_data.get("password") + auth_user = authenticate(email=email, password=password) if auth_user: login(self.request, auth_user) - return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url()) - @cache_control(no_cache=True, must_revalidate=True, no_store=True) + @method_decorator( + cache_control( + no_cache=True, + must_revalidate=True, + no_store=True, + ) + ) def get(self, request, *args, **kwargs): if self.request.user.is_authenticated: return HttpResponseRedirect(self.get_success_url()) - return super(LoginViewMixin, self).get(request, *args, **kwargs) + return super(LoginViewMixin, self).get( + request, + *args, + **kwargs + ) class ResendActivationLinkViewMixin(FormView): diff --git a/vendor/stored_messages/models.py b/vendor/stored_messages/models.py index ff40cdbd..f112fbb8 100644 --- a/vendor/stored_messages/models.py +++ b/vendor/stored_messages/models.py @@ -8,7 +8,7 @@ try: except ImportError: def python_2_unicode_compatible(cls): return cls -from django.utils.translation import pgettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from .settings import stored_messages_settings