From 271bbe513f6fce33b262553b1ce95a39bb784f37 Mon Sep 17 00:00:00 2001 From: Dario Marcelino Date: Tue, 12 Dec 2017 12:22:25 +0000 Subject: [PATCH 1/3] #141 Failing tests for original slug field issue --- wagtail_modeltranslation/tests/tests.py | 91 ++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/wagtail_modeltranslation/tests/tests.py b/wagtail_modeltranslation/tests/tests.py index e037770..4305453 100755 --- a/wagtail_modeltranslation/tests/tests.py +++ b/wagtail_modeltranslation/tests/tests.py @@ -5,7 +5,7 @@ import django from django.apps import apps as django_apps from django.core.exceptions import ValidationError from django.core.management import call_command -from django.test import TestCase, TransactionTestCase +from django.test import TestCase, TransactionTestCase, RequestFactory from django.test.utils import override_settings from django.utils.translation import get_language, trans_real from modeltranslation import settings as mt_settings, translator @@ -336,6 +336,95 @@ class WagtailModeltranslationTest(WagtailModeltranslationTestBase): self.assertRaises(ValidationError, child2.clean) + def test_original_slug_update(self): + from wagtail.wagtailcore.models import Page + # save the page in the default language + root = models.TestRootPage(title='original slug', title_de='originalschnecke', depth=1, path='0002', + slug_en='test-slug-en', slug_de='test-slug-de') + root.save() + + # some control checks, we don't expect any surprises here + self.assertEqual(root.slug, 'test-slug-de', 'slug has the wrong value.') + self.assertEqual(root.slug_de, 'test-slug-de', 'slug_de has the wrong value.') + self.assertEqual(root.slug_en, 'test-slug-en', 'slug_en has the wrong value.') + + # fetches the correct Page using slug + page = Page.objects.filter(slug='test-slug-de').first() + self.assertEqual(page.specific, root, 'The wrong page was retrieved from DB.') + + trans_real.activate('en') + + # fetches the correct Page using slug using non-default language + page = Page.objects.filter(slug='test-slug-de').first() + self.assertEqual(page.specific, root, 'The wrong page was retrieved from DB.') + + # save the page 2 in the non-default language + root2 = models.TestRootPage(title='original slug 2', title_de='originalschnecke 2', depth=1, path='0003', + slug_en='test-slug2-en', slug_de='test-slug2-de') + root2.save() + + # sanity checks + self.assertEqual(root2.slug, 'test-slug2-en', 'slug has the wrong value.') + self.assertEqual(root2.slug_de, 'test-slug2-de', 'slug_de has the wrong value.') + self.assertEqual(root2.slug_en, 'test-slug2-en', 'slug_en has the wrong value.') + + # fetches the correct Page using slug using non-default language + page = Page.objects.filter(slug='test-slug2-de').first() + self.assertEqual(page.specific, root2, 'The wrong page was retrieved from DB.') + + trans_real.activate('de') + + # fetches the correct Page using slug using default language + page = Page.objects.filter(slug='test-slug2-de').first() + self.assertEqual(page.specific, root2, 'The wrong page was retrieved from DB.') + + + + def test_relative_url(self): + from wagtail.wagtailcore.models import Site + # Create a test Site with a root page + root = models.TestRootPage(title='title slugurl', depth=1, path='0004', + slug_en='title_slugurl_en', slug_de='title_slugurl_de') + root.save() + site = Site(root_page=root) + site.save() + + # Add children to the root + child = root.add_child( + instance=models.TestSlugPage1(title='child1 slugurl', + slug_en='child-slugurl-en', slug_de='child-slugurl-de', + depth=2, path='00040001') + ) + child.save_revision().publish() + + url_1_de = child.relative_url(site) + self.assertEqual(url_1_de, '/de/child-slugurl-de/', + 'When using the default language, slugurl produces the wrong url.') + + trans_real.activate('en') + + url_1_en = child.relative_url(site) + self.assertEqual(url_1_en, '/en/child-slugurl-en/', + 'When using non-default language, slugurl produces the wrong url.') + + # Add children using non-default language + child2 = root.add_child( + instance=models.TestSlugPage2(title='child2 slugurl', title_de='child2 slugurl DE', + slug_de='child2-slugurl-de', slug_en='child2-slugurl-en', + depth=2, path='00040002') + ) + child2.save_revision().publish() + + url_2_en = child2.relative_url(site) + self.assertEqual(url_2_en, '/en/child2-slugurl-en/', + 'When using non-default language, slugurl produces the wrong url.') + + trans_real.activate('de') + + url_2_de = child2.relative_url(site) + self.assertEqual(url_2_de, '/de/child2-slugurl-de/', + 'When using non-default language, slugurl produces the wrong url.') + def test_searchfield_patching(self): # Check if the search fields have the original field plus the translated ones expected_fields = ['title', 'title_de', 'title_en', 'description', 'description_de', 'description_en'] From 7cf94069b63e6316b6a0efc79d510fcc4cdc9a4d Mon Sep 17 00:00:00 2001 From: Dario Marcelino Date: Tue, 12 Dec 2017 12:26:02 +0000 Subject: [PATCH 2/3] #141: patch Slug Field pre_save() to ensure slug value consistency --- runtests.py | 1 + .../patch_wagtailadmin.py | 38 +++++++++++++++++-- wagtail_modeltranslation/settings.py | 2 + 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/runtests.py b/runtests.py index e374ca3..6831e31 100755 --- a/runtests.py +++ b/runtests.py @@ -61,6 +61,7 @@ def runtests(): ('en', 'English'), ), MIDDLEWARE_CLASSES=(), + WAGTAILMODELTRANSLATION_ORIGINAL_SLUG_LANGUAGE='default', ) if django.VERSION >= (1, 7): diff --git a/wagtail_modeltranslation/patch_wagtailadmin.py b/wagtail_modeltranslation/patch_wagtailadmin.py index fbb6eb7..bcd611d 100644 --- a/wagtail_modeltranslation/patch_wagtailadmin.py +++ b/wagtail_modeltranslation/patch_wagtailadmin.py @@ -2,7 +2,7 @@ import copy import logging -from django.core.exceptions import ValidationError +from django.core.exceptions import ValidationError, FieldDoesNotExist from django.core.urlresolvers import reverse from django.db import transaction from django.http import Http404 @@ -23,7 +23,7 @@ from wagtail.wagtailsearch.index import SearchField from wagtail.wagtailsnippets.models import get_snippet_models from wagtail.wagtailsnippets.views.snippets import SNIPPET_EDIT_HANDLERS -from wagtail_modeltranslation.settings import CUSTOM_SIMPLE_PANELS, CUSTOM_COMPOSED_PANELS +from wagtail_modeltranslation.settings import CUSTOM_SIMPLE_PANELS, CUSTOM_COMPOSED_PANELS, ORIGINAL_SLUG_LANGUAGE from wagtail_modeltranslation.utils import compare_class_tree_depth logger = logging.getLogger('wagtail.core') @@ -88,8 +88,15 @@ class WagtailTranslator(object): translated_field.field_name = build_localized_fieldname(field.field_name, language) model.search_fields = list(model.search_fields) + [translated_field] - # OVERRIDE PAGE METHODS + # SLUG FIELD PATCHING + try: + slug_field = model._meta.get_field('slug') + _patch_pre_save(slug_field) + except FieldDoesNotExist: + pass + + # OVERRIDE PAGE METHODS model.move = _new_move model.set_url_path = _new_set_url_path model.route = _new_route @@ -201,7 +208,6 @@ class WagtailTranslator(object): # patched, leaving the original untouched return panel - # Overridden Page methods adapted to the translated fields @transaction.atomic # only commit when all descendants are properly updated @@ -403,6 +409,30 @@ def _patch_clean(model): model.clean = clean +def _patch_pre_save(field): + if not ORIGINAL_SLUG_LANGUAGE: + return + + if ORIGINAL_SLUG_LANGUAGE == 'default': + reference_slug_language = mt_settings.DEFAULT_LANGUAGE + else: + reference_slug_language = ORIGINAL_SLUG_LANGUAGE + + def pre_save(self, model_instance, add): + """ + Returns slug field's value using the language set by `WAGTAILMODELTRANSLATION_ORIGINAL_SLUG_LANGUAGE` + just before saving. + """ + current_language = get_language() + # using ORIGINAL_SLUG_LANGUAGE makes Page's slug value consistent + trans_real.activate(reference_slug_language) + value = getattr(model_instance, self.attname) + trans_real.activate(current_language) + return value + + field.pre_save = pre_save.__get__(field) + + def patch_wagtail_models(): # After all models being registered the Page or BaseSetting subclasses and snippets are patched registered_models = translator.get_registered_models() diff --git a/wagtail_modeltranslation/settings.py b/wagtail_modeltranslation/settings.py index c3eadf8..370d156 100644 --- a/wagtail_modeltranslation/settings.py +++ b/wagtail_modeltranslation/settings.py @@ -10,3 +10,5 @@ CUSTOM_SIMPLE_PANELS = [import_from_string(panel_class) for panel_class in getattr(settings, 'WAGTAILMODELTRANSLATION_CUSTOM_SIMPLE_PANELS', [])] CUSTOM_COMPOSED_PANELS = [import_from_string(panel_class) for panel_class in getattr(settings, 'WAGTAILMODELTRANSLATION_CUSTOM_COMPOSED_PANELS', [])] + +ORIGINAL_SLUG_LANGUAGE = getattr(settings, 'WAGTAILMODELTRANSLATION_ORIGINAL_SLUG_LANGUAGE', None) From e91101977e013d02b200478b97a7220d35ca099a Mon Sep 17 00:00:00 2001 From: Dario Marcelino Date: Tue, 12 Dec 2017 12:26:25 +0000 Subject: [PATCH 3/3] #141: documentation for original slug field language changes --- docs/advanced settings.rst | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/advanced settings.rst b/docs/advanced settings.rst index ff7f82d..679759f 100644 --- a/docs/advanced settings.rst +++ b/docs/advanced settings.rst @@ -23,8 +23,36 @@ If, for example, you're using wagtail-embedvideos the EmbedVideoChooserPanel is Default: ``[]`` (empty list) -This settings behaves as the above but should be used for panels that are composed by other panels (MultiFieldPanel or FieldRowPanel for example). +This setting behaves as the above but should be used for panels that are composed by other panels (MultiFieldPanel or FieldRowPanel for example). .. code-block:: python WAGTAILMODELTRANSLATION_CUSTOM_COMPOSED_PANELS = ['app_x.module_y.PanelZ'] + + +``WAGTAILMODELTRANSLATION_ORIGINAL_SLUG_LANGUAGE`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 0.6.0 + +Default: ``None`` + +This setting enables consistency for the original (not translated) 'slug' value that is saved to the ``Page`` model table (wagtailcore_page). The value saved to the DB will be independent of user's current language and instead will rely on this setting's language. +This is specially useful when Wagtail pages are edited by users of different languages and the site makes use of `slugurl `_. + +``None`` + [set by default] + + Setting turned off. Behaviour is the same as django-modeltranslation, meaning the value of the original slug field is undetermined (check `The State of the Original Field `_). + +``'default'`` + Original slug field saved to the DB is in django-modeltranslation's default language. + +``'xx'`` (language code) + Use a language code to ensure the value saved to the original slug field is in the chosen language. For example: ``'en'``. + +Example: + +.. code-block:: python + + WAGTAILMODELTRANSLATION_ORIGINAL_SLUG_LANGUAGE = 'default'