From 528fd78c76573d87ad67bbb73ef187062e2298d0 Mon Sep 17 00:00:00 2001 From: Sage Abdullah Date: Thu, 26 Jan 2023 15:27:24 +0000 Subject: [PATCH] Exclude latest_revision when copying objects for translation --- wagtail/actions/copy_for_translation.py | 70 ++++++++++++++++++++++++- wagtail/models/__init__.py | 5 ++ wagtail/models/i18n.py | 20 +++---- 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/wagtail/actions/copy_for_translation.py b/wagtail/actions/copy_for_translation.py index 613bdddcbf..74e6cbb17a 100644 --- a/wagtail/actions/copy_for_translation.py +++ b/wagtail/actions/copy_for_translation.py @@ -2,6 +2,7 @@ from django.core.exceptions import PermissionDenied from django.db import transaction from wagtail.coreutils import find_available_slug +from wagtail.models.copying import _copy class ParentNotTranslatedError(Exception): @@ -13,14 +14,18 @@ class ParentNotTranslatedError(Exception): pass -class CopyPageForTranslationPermissionError(PermissionDenied): +class CopyForTranslationPermissionError(PermissionDenied): """ - Raised when the page translation copy cannot be performed due to insufficient permissions. + Raised when the object translation copy cannot be performed due to insufficient permissions. """ pass +class CopyPageForTranslationPermissionError(CopyForTranslationPermissionError): + pass + + class CopyPageForTranslationAction: """ Creates a copy of this page in the specified locale. @@ -153,3 +158,64 @@ class CopyPageForTranslationAction: self.walk(self.page) return translated_page + + +class CopyForTranslationAction: + """ + Creates a copy of this object in the specified locale. + + The ``exclude_fields`` parameter can be used to set any fields to a blank value + in the copy. + """ + + def __init__( + self, + object, + locale, + exclude_fields=None, + user=None, + ): + self.object = object + self.locale = locale + self.exclude_fields = exclude_fields + self.user = user + + def check(self, skip_permission_checks=False): + # Permission checks + if ( + self.user + and not skip_permission_checks + and not self.user.has_perms(["simple_translation.submit_translation"]) + ): + raise CopyForTranslationPermissionError( + "You do not have permission to submit a translation for this object." + ) + + @transaction.atomic + def _copy_for_translation(self, object, locale, exclude_fields=None): + from wagtail.models import TranslatableMixin + + exclude_fields = ( + getattr(object, "default_exclude_fields_in_copy", []) + + getattr(object, "exclude_fields_in_copy", []) + + (exclude_fields or []) + ) + translated, child_object_map = _copy(object, exclude_fields=exclude_fields) + translated.locale = locale + + # Update locale on any translatable child objects as well + # Note: If this is not a subclass of ClusterableModel, child_object_map will always be '{}' + for (_child_relation, _old_pk), child_object in child_object_map.items(): + if isinstance(child_object, TranslatableMixin): + child_object.locale = locale + + return translated + + def execute(self, skip_permission_checks=False): + self.check(skip_permission_checks=skip_permission_checks) + + translated_object = self._copy_for_translation( + self.object, self.locale, self.exclude_fields + ) + + return translated_object diff --git a/wagtail/models/__init__.py b/wagtail/models/__init__.py index 9365038f09..9014fae016 100644 --- a/wagtail/models/__init__.py +++ b/wagtail/models/__init__.py @@ -234,6 +234,11 @@ class RevisionMixin(models.Model): editable=False, ) + # An array of additional field names that will not be included when the object is copied. + default_exclude_fields_in_copy = [ + "latest_revision", + ] + @property def revisions(self): """ diff --git a/wagtail/models/i18n.py b/wagtail/models/i18n.py index 85689be346..b946d3d8a9 100644 --- a/wagtail/models/i18n.py +++ b/wagtail/models/i18n.py @@ -10,14 +10,13 @@ from django.utils import translation from django.utils.encoding import force_str from modelcluster.fields import ParentalKey +from wagtail.actions.copy_for_translation import CopyForTranslationAction from wagtail.coreutils import ( get_content_languages, get_supported_content_language_variant, ) from wagtail.signals import pre_validate_delete -from .copying import _copy - def pk(obj): if isinstance(obj, models.Model): @@ -205,22 +204,17 @@ class TranslatableMixin(models.Model): self.get_translations(inclusive=True).filter(locale_id=pk(locale)).exists() ) - def copy_for_translation(self, locale): + def copy_for_translation(self, locale, exclude_fields=None): """ Creates a copy of this instance with the specified locale. Note that the copy is initially unsaved. """ - translated, child_object_map = _copy(self) - translated.locale = locale - - # Update locale on any translatable child objects as well - # Note: If this is not a subclass of ClusterableModel, child_object_map will always be '{}' - for (_child_relation, _old_pk), child_object in child_object_map.items(): - if isinstance(child_object, TranslatableMixin): - child_object.locale = locale - - return translated + return CopyForTranslationAction( + self, + locale, + exclude_fields=exclude_fields, + ).execute() def get_default_locale(self): """