Exemple #1
0
    def test_sitemap_lang_slug(self):
        """Test that the url is in the locale language"""
        site = Site.objects.get_current()
        from modeltranslation.utils import build_localized_fieldname  # pylint: disable=F0401

        kwargs1 = {}
        kwargs2 = {}

        for lang_code, lang_name in settings.LANGUAGES:
            loc_title_var = build_localized_fieldname('title', lang_code)
            loc_slug_var = build_localized_fieldname('slug', lang_code)
            kwargs1[loc_title_var] = 'article-{0}-1'.format(lang_name)
            kwargs2[loc_title_var] = 'other-article-{0}-2'.format(lang_name)
            kwargs1[loc_slug_var] = slugify(kwargs1[loc_title_var])
            kwargs2[loc_slug_var] = slugify(kwargs2[loc_title_var])

        article_class = get_article_class()

        article1 = mommy.make(article_class, publication=BaseArticle.PUBLISHED, **kwargs1)
        article2 = mommy.make(article_class, publication=BaseArticle.PUBLISHED, **kwargs2)

        factory = RequestFactory()
        request = factory.get('/sitemap.xml')
        response = sitemap_view(request, get_sitemaps())
        self.assertEqual(200, response.status_code)

        for (lang, name) in settings.LANGUAGES:
            activate(lang)
            self.assertContains(response, site.domain + article1.get_absolute_url())
            self.assertContains(response, site.domain + article2.get_absolute_url())
 def handle(self, *args, **options):
     """command"""
     #look for emailing to be sent
     verbose = options.get('verbosity', 1)
     
     if not is_localized():
         print("the site is not localized this is not required")
     
     from modeltranslation.utils import build_localized_fieldname
     
     for alias in Alias.objects.all():
         cursor = connection.cursor()
         
         cursor.execute(
             '''SELECT path, redirect_url FROM coop_cms_alias where id={0}'''.format(alias.id))
         row = cursor.fetchone()
         print(row)
         (path, redirect_url) = row
         
         languages = [x for (x, y) in settings.LANGUAGES]
         
         lang_code = languages[0]
         path_field_name = build_localized_fieldname('path', lang_code)
         redirect_url_field_name = build_localized_fieldname('redirect_url', lang_code)
         if (not getattr(alias, path_field_name)) and (not getattr(alias, redirect_url_field_name)):
             print("update", alias.id, path, redirect_url)
             setattr(alias, path_field_name, path)
             setattr(alias, redirect_url_field_name, redirect_url)
             alias.save()
 def _patch_prepopulated_fields(self):
     if self.prepopulated_fields:
         # prepopulated_fields_new = dict(self.prepopulated_fields)
         # for (k, v) in self.prepopulated_fields.items():
         #     if v[0] in self.trans_opts.fields:
         #         translation_fields = get_translation_fields(v[0])
         #         prepopulated_fields_new[k] = tuple([translation_fields[0]])
         prepopulated_fields_new = {}
         for (k, v) in self.prepopulated_fields.items():
             if k in self.trans_opts.fields:
                 translation_fields = [(l, build_localized_fieldname(k, l)) for l in AVAILABLE_LANGUAGES]
                 new_keys = tuple(translation_fields)
             else:
                 new_keys = ((None, k),)
             for lang, new_key in new_keys:
                 languages = AVAILABLE_LANGUAGES
                 if lang:
                     languages = [lang]
                 new_vals = []
                 for val in v:
                     if val in self.trans_opts.fields:
                         translation_fields = [build_localized_fieldname(val, l) for l in languages]
                         new_vals.extend(translation_fields)
                     else:
                         new_vals.append(val)
                 new_vals = tuple(new_vals)
                 prepopulated_fields_new[new_key] = new_vals
         self.prepopulated_fields = prepopulated_fields_new
    def modeltranslation_update_slugs(sender, **kwargs):
        # https://bitbucket.org/neithere/django-autoslug/pull-request/11/modeltranslation-support-fix-issue-19/
        # http://django-modeltranslation.readthedocs.org
        #
        # TODO: tests
        #
        if not modeltranslation_utils:
            return

        instance = kwargs['instance']
        slugs = {}

        for field in instance._meta.fields:
            if type(field) == AutoSlugField:
                for lang in settings.LANGUAGES:
                    lang_code = lang[0]
                    lang_code = lang_code.replace('-', '_')

                    populate_from_localized = modeltranslation_utils.build_localized_fieldname(field.populate_from, lang_code)
                    populate_from_value = getattr(instance, populate_from_localized)

                    field_name_localized = modeltranslation_utils.build_localized_fieldname(field.name, lang_code)
                    field_value = getattr(instance, field_name_localized)

                    if not field_value or field.always_update:
                        slug = field.slugify(populate_from_value)
                        slugs[field_name_localized] = slug

        sender.objects.filter(pk=instance.pk).update(**slugs)
def _new_set_url_path(self, parent):
    """
    This method override populates url_path for each specified language.
    This way we can get different urls for each language, defined
    by page slug.
    """
    for language in mt_settings.AVAILABLE_LANGUAGES:
        localized_slug_field = build_localized_fieldname('slug', language)
        default_localized_slug_field = build_localized_fieldname('slug', mt_settings.DEFAULT_LANGUAGE)
        localized_url_path_field = build_localized_fieldname('url_path', language)
        default_localized_url_path_field = build_localized_fieldname('url_path', mt_settings.DEFAULT_LANGUAGE)

        if parent:
            parent = parent.specific

            # Emulate the default behavior of django-modeltranslation to get the slug and url path
            # for the current language. If the value for the current language is invalid we get the one
            # for the default fallback language
            slug = getattr(self, localized_slug_field, None) or getattr(self, default_localized_slug_field, self.slug)
            parent_url_path = getattr(parent, localized_url_path_field, None) or \
                              getattr(parent, default_localized_url_path_field, parent.url_path)

            setattr(self, localized_url_path_field, parent_url_path + slug + '/')

        else:
            # a page without a parent is the tree root,
            # which always has a url_path of '/'
            setattr(self, localized_url_path_field, '/')

    # update url_path for children pages
    for child in self.get_children().specific():
        child.set_url_path(self.specific)
        child.save()

    return self.url_path
Exemple #6
0
def populate_translation_fields(sender, kwargs):
    """
    When models are created or loaded from fixtures, replicates values
    provided for translatable fields to some / all empty translation fields,
    according to the current population mode.

    Population is performed only on keys (field names) present in kwargs.
    Nothing is returned, but passed kwargs dictionary is altered.

    With ``mode`` set to:
    -- ``all``: fills all translation fields, skipping just those for
       which a translated value is also provided;
    -- ``default``: fills only the default translation (unless it is
       additionally provided);
    -- ``required``: like ``default``, but only if the original field is
       non-nullable;

    At least the ``required`` mode should be used when loading untranslated
    fixtures to keep the database consistent (note that Django management
    commands are normally forced to run with hardcoded ``en-us`` language
    active). The ``default`` mode is useful if you need to ensure fallback
    values are available, and ``all`` if you need to have all translations
    defined (for example to make lookups / filtering without resorting to
    query fallbacks).
    """
    populate = mt_settings.AUTO_POPULATE
    if not populate:
        return
    if populate is True:
        # What was meant by ``True`` is now called ``all``.
        populate = 'all'

    opts = translator.get_options_for_model(sender)
    for key, val in list(kwargs.items()):
        if key in opts.fields:
            if populate == 'all':
                # Set the value for every language.
                for translation_field in opts.fields[key]:
                    kwargs.setdefault(translation_field.name, val)
            elif populate == 'default':
                default = build_localized_fieldname(key, mt_settings.DEFAULT_LANGUAGE)
                kwargs.setdefault(default, val)
            elif populate == 'required':
                default = build_localized_fieldname(key, mt_settings.DEFAULT_LANGUAGE)
                if not sender._meta.get_field(key).null:
                    kwargs.setdefault(default, val)
            else:
                raise AttributeError("Unknown population mode '%s'." % populate)
 def get_attname_column(self):
     attname = self.get_attname()
     if self.translated_field.db_column:
         column = build_localized_fieldname(self.translated_field.db_column)
     else:
         column = attname
     return attname, column
 def __init__(self):
     super(Command, self).__init__()
     update_fields = ['url_path']
     for language in mt_settings.AVAILABLE_LANGUAGES:
         localized_url_path = build_localized_fieldname('url_path', language)
         update_fields.append(localized_url_path)
     self.update_fields = update_fields
 def __get__(self, instance, owner):
     """
     Returns value from the translation field for the current language, or
     value for some another language according to fallback languages, or the
     custom fallback value, or field's default value.
     """
     if instance is None:
         return self
     default = NONE
     undefined = self.fallback_undefined
     if undefined is NONE:
         default = self.field.get_default()
         undefined = default
     langs = resolution_order(get_language(), self.fallback_languages)
     for lang in langs:
         loc_field_name = build_localized_fieldname(self.field.name, lang)
         val = getattr(instance, loc_field_name, None)
         if val is not None and val != undefined:
             return val
     if mt_settings.ENABLE_FALLBACKS and self.fallback_value is not NONE:
         return self.fallback_value
     else:
         if default is NONE:
             default = self.field.get_default()
         return default
def add_translation_fields(model, opts):
    """
    Monkey patches the original model class to provide additional fields for
    every language.

    Adds newly created translation fields to the given translation options.
    """
    model_empty_values = getattr(opts, 'empty_values', NONE)
    for field_name in opts.local_fields.keys():
        field_empty_value = parse_field(model_empty_values, field_name, NONE)
        for l in mt_settings.AVAILABLE_LANGUAGES:
            # Create a dynamic translation field
            translation_field = create_translation_field(
                model=model, field_name=field_name, lang=l, empty_value=field_empty_value)
            # Construct the name for the localized field
            localized_field_name = build_localized_fieldname(field_name, l)
            # Check if the model already has a field by that name
            if hasattr(model, localized_field_name):
                raise ValueError(
                    "Error adding translation field. Model '%s' already contains a field named"
                    "'%s'." % (model._meta.object_name, localized_field_name))
            # This approach implements the translation fields as full valid
            # django model fields and therefore adds them via add_to_class
            model.add_to_class(localized_field_name, translation_field)
            opts.add_translation_field(field_name, translation_field)

    # Rebuild information about parents fields. If there are opts.local_fields, field cache would be
    # invalidated (by model._meta.add_field() function). Otherwise, we need to do it manually.
    if len(opts.local_fields) == 0:
        model._meta._fill_fields_cache()
def _validate_slugs(page):
    """
    Determine whether the given slug is available for use on a child page of
    parent_page.
    """
    parent_page = page.get_parent()

    if parent_page is None:
        # the root page's slug can be whatever it likes...
        return {}

    # Save the current active language
    current_language = get_language()

    siblings = page.get_siblings(inclusive=False).specific()

    errors = {}

    for language in mt_settings.AVAILABLE_LANGUAGES:
        # Temporarily activate every language because even though there might
        # be no repeated value for slug_pt the fallback of an empty slug could
        # already be in use

        trans_real.activate(language)

        siblings_slugs = [sibling.slug for sibling in siblings]

        if page.specific.slug in siblings_slugs:
            errors[build_localized_fieldname('slug', language)] = _("This slug is already in use")

    # Re-enable the original language
    trans_real.activate(current_language)

    return errors
 def get_attname_column(self):
     attname = self.get_attname()
     column = (
         build_localized_fieldname(self.translated_field.db_column or self.translated_field.name, self.language)
         or attname
     )
     return attname, column
Exemple #13
0
    def __init__(self, translated_field, language, *args, **kwargs):
        # Update the dict of this field with the content of the original one
        # This might be a bit radical?! Seems to work though...
        self.__dict__.update(translated_field.__dict__)

        # Store the originally wrapped field for later
        self.translated_field = translated_field
        self.language = language

        # Translation are always optional (for now - maybe add some parameters
        # to the translation options for configuring this)

        if not isinstance(self, fields.BooleanField):
            # TODO: Do we really want to enforce null *at all*? Shouldn't this
            # better honour the null setting of the translated field?
            self.null = True
        self.blank = True

        # Adjust the name of this field to reflect the language
        self.attname = build_localized_fieldname(self.translated_field.name, self.language)
        self.name = self.attname

        # Copy the verbose name and append a language suffix
        # (will show up e.g. in the admin).
        self.verbose_name = build_localized_verbose_name(translated_field.verbose_name, language)
Exemple #14
0
 def save(self):
     """
     Save each of the settings to the DB.
     """
     active_language = get_language()
     for (name, value) in self.cleaned_data.items():
         if name not in registry:
             name, code = name.rsplit('_modeltranslation_', 1)
         else:
             code = None
         setting_obj, created = Setting.objects.get_or_create(name=name)
         if settings.USE_MODELTRANSLATION:
             if registry[name]["translatable"]:
                 try:
                     activate(code)
                 except:
                     pass
                 finally:
                     setting_obj.value = value
                     activate(active_language)
             else:
                 # Duplicate the value of the setting for every language
                 for code in OrderedDict(settings.LANGUAGES):
                     setattr(setting_obj,
                             build_localized_fieldname('value', code),
                             value)
         else:
             setting_obj.value = value
         setting_obj.save()
    def __init__(self, translated_field, language, *args, **kwargs):
        # Store the originally wrapped field for later
        self.translated_field = translated_field
        self.language = language
        
        # Update the dict of this field with the content of the original one
        # This might be a bit radical?! Seems to work though...
        self.__dict__.update(translated_field.__dict__)        
        
        # Translation are always optional (for now - maybe add some parameters
        # to the translation options for configuring this)
        self.null = True
        self.blank = True

        # If translations are optional, uniqueness can't be enforced
        self._unique = False
        
        # Adjust the name of this field to reflect the language
        self.attname = build_localized_fieldname(translated_field.name, language)
        self.name = self.attname
        
        # Copy the verbose name and append a language suffix (will e.g. in the
        # admin). This might be a proxy function so we have to check that here.
        if hasattr(translated_field.verbose_name, '_proxy____unicode_cast'):            
            verbose_name = translated_field.verbose_name._proxy____unicode_cast()
        else:
            verbose_name = translated_field.verbose_name                       
        self.verbose_name = '%s [%s]' % (verbose_name, language)
def add_localized_fields(model):
    """
    Monkey patches the original model class to provide additional fields for
    every language. Only do that for fields which are defined in the
    translation options of the model.

    Returns a dict mapping the original fieldname to a list containing the
    names of the localized fields created for the original field.
    """
    localized_fields = dict()
    translation_opts = translator.get_options_for_model(model)
    for field_name in translation_opts.fields:
        localized_fields[field_name] = list()
        for l in settings.LANGUAGES:
            # Create a dynamic translation field
            translation_field = create_translation_field(
                model=model, field_name=field_name, lang=l[0])
            # Construct the name for the localized field
            localized_field_name = build_localized_fieldname(field_name, l[0])
            # Check if the model already has a field by that name
            if hasattr(model, localized_field_name):
                raise ValueError(
                    "Error adding translation field. Model '%s' already "
                    "contains a field named '%s'." % (
                        model._meta.object_name, localized_field_name))
            # This approach implements the translation fields as full valid
            # django model fields and therefore adds them via add_to_class
            model.add_to_class(localized_field_name, translation_field)
            localized_fields[field_name].append(localized_field_name)
    return localized_fields
 def cache_name(self):
     """
     Used in django 1.x
     """
     lang = get_language()
     cache = build_localized_fieldname(self.accessor, lang)
     return "_%s_cache" % cache
Exemple #18
0
 def __get__(self, instance, owner):
     """
     Returns value from the translation field for the current language, or
     value for some another language according to fallback languages, or the
     custom fallback value, or field's default value.
     """
     if instance is None:
         return self
     default = NONE
     undefined = self.fallback_undefined
     if undefined is NONE:
         default = self.field.get_default()
         undefined = default
     langs = resolution_order(get_language(), self.fallback_languages)
     for lang in langs:
         loc_field_name = build_localized_fieldname(self.field.name, lang)
         val = getattr(instance, loc_field_name, None)
         if self.meaningful_value(val, undefined):
             return val
     if mt_settings.ENABLE_FALLBACKS and self.fallback_value is not NONE:
         return self.fallback_value
     else:
         if default is NONE:
             default = self.field.get_default()
         # Some fields like FileField behave strange, as their get_default() doesn't return
         # instance of attr_class, but rather None or ''.
         # Normally this case is handled in the descriptor, but since we have overridden it, we
         # must mock it up.
         if (isinstance(self.field, fields.files.FileField) and
                 not isinstance(default, self.field.attr_class)):
             return self.field.attr_class(instance, self.field, default)
         return default
    def _patch_simple_panel(self, model, original_panel):
        panel_class = original_panel.__class__
        translated_panels = []
        translation_registered_fields = translator.get_options_for_model(model).fields

        # If the panel field is not registered for translation
        # the original one is returned
        if original_panel.field_name not in translation_registered_fields:
            return [original_panel]

        for language in mt_settings.AVAILABLE_LANGUAGES:
            original_field = model._meta.get_field(original_panel.field_name)
            localized_field_name = build_localized_fieldname(original_panel.field_name, language)

            # if the original field is required and the current language is the default one
            # this field's blank property is set to False
            if not original_field.blank and language == mt_settings.DEFAULT_LANGUAGE:
                localized_field = model._meta.get_field(localized_field_name)
                localized_field.blank = False

            localized_panel = panel_class(localized_field_name)

            # Pass the original panel extra attributes to the localized
            if hasattr(original_panel, 'classname'):
                localized_panel.classname = original_panel.classname
            if hasattr(original_panel, 'widget'):
                localized_panel.widget = original_panel.widget

            translated_panels.append(localized_panel)

        return translated_panels
def rewrite_lookup_key(model, lookup_key):
    translatable_fields = get_translatable_fields_for_model(model)
    if translatable_fields is not None:
        pieces = lookup_key.split('__')
        # If we are doing a lookup on a translatable field,
        # we want to rewrite it to the actual field name
        # For example, we want to rewrite "name__startswith" to "name_fr__startswith"
        if pieces[0] in translatable_fields:
            lookup_key = build_localized_fieldname(pieces[0], get_language())

            remaining_lookup = '__'.join(pieces[1:])
            if remaining_lookup:
                lookup_key = '%s__%s' % (lookup_key, remaining_lookup)

    pieces = lookup_key.split('__')
    if len(pieces) > 1:
        # Check if we are doing a lookup to a related trans model
        fields_to_trans_models = get_fields_to_translatable_models(model)
        for field_to_trans, transmodel in fields_to_trans_models:
            if pieces[0] == field_to_trans:
                sub_lookup = '__'.join(pieces[1:])
                if sub_lookup:
                    sub_lookup = rewrite_lookup_key(transmodel, sub_lookup)
                    lookup_key = '%s__%s' % (pieces[0], sub_lookup)
                break

    return lookup_key
def _update_translation_descendant_url_paths(old_record, page):
    # update children paths, must be done for all languages to ensure fallbacks are applied
    languages_changed = []
    default_localized_url_path = build_localized_fieldname('url_path', mt_settings.DEFAULT_LANGUAGE)
    for language in mt_settings.AVAILABLE_LANGUAGES:
        localized_url_path = build_localized_fieldname('url_path', language)
        old_url_path = getattr(old_record, localized_url_path) or getattr(old_record, default_localized_url_path)
        new_url_path = getattr(page, localized_url_path) or getattr(page, default_localized_url_path)

        if old_url_path == new_url_path:
            # nothing to do
            continue

        languages_changed.append(language)
        _localized_update_descendant_url_paths(page, old_url_path, new_url_path, language)

    _update_untranslated_descendants_url_paths(page, languages_changed)
 def __set__(self, instance, value):
     lang = get_language()
     loc_field_name = build_localized_fieldname(self.field_name, lang)
     # Localized field name with '_id'
     loc_attname = instance._meta.get_field(loc_field_name).get_attname()
     setattr(instance, loc_attname, value)
     base_attname = instance._meta.get_field(self.field_name).get_attname()
     instance.__dict__[base_attname] = value
    def __init__(self, translated_field, language, empty_value, *args, **kwargs):
        # Update the dict of this field with the content of the original one
        # This might be a bit radical?! Seems to work though...
        self.__dict__.update(translated_field.__dict__)

        # Store the originally wrapped field for later
        self.translated_field = translated_field
        self.language = language
        self.empty_value = empty_value
        if empty_value is NONE:
            self.empty_value = None if translated_field.null else ""

        # Translation are always optional (for now - maybe add some parameters
        # to the translation options for configuring this)

        if not isinstance(self, fields.BooleanField):
            # TODO: Do we really want to enforce null *at all*? Shouldn't this
            # better honour the null setting of the translated field?
            self.null = True
        self.blank = True

        # Adjust the name of this field to reflect the language
        self.attname = build_localized_fieldname(self.translated_field.name, self.language)
        self.name = self.attname

        # Copy the verbose name and append a language suffix
        # (will show up e.g. in the admin).
        self.verbose_name = build_localized_verbose_name(translated_field.verbose_name, language)

        # ForeignKey support - rewrite related_name
        if self.rel and self.related and not self.rel.is_hidden():
            import copy

            current = self.related.get_accessor_name()
            self.rel = copy.copy(self.rel)  # Since fields cannot share the same rel object.
            # self.related doesn't need to be copied, as it will be recreated in
            # ``RelatedField.do_related_class``

            if self.rel.related_name is None:
                # For implicit related_name use different query field name
                loc_related_query_name = build_localized_fieldname(self.related_query_name(), self.language)
                self.related_query_name = lambda: loc_related_query_name
            self.rel.related_name = build_localized_fieldname(current, self.language)
            self.rel.field = self  # Django 1.6
            if hasattr(self.rel.to._meta, "_related_objects_cache"):
                del self.rel.to._meta._related_objects_cache
 def get_missing_languages(self, field_name, db_table):
     """
     Gets only missings fields.
     """
     db_table_fields = self.get_table_fields(db_table)
     for lang_code, lang_name in settings.LANGUAGES:
         if build_localized_fieldname(field_name, lang_code) not in db_table_fields:
             yield lang_code
Exemple #25
0
 def mldict(self, field_name):
     data = {}
     for lang in AVAILABLE_LANGUAGES:
         localized_field_name = build_localized_fieldname(field_name, lang)
         value = getattr(self, localized_field_name)
         if lang == DEFAULT_LANGUAGE or value:
             data[lang] = value
     return data
Exemple #26
0
 def __set__(self, instance, value):
     lang = get_language()
     loc_field_name = build_localized_fieldname(self.name, lang)
     # also update the translation field of the current language
     setattr(instance, loc_field_name, value)
     # update the original field via the __dict__ to prevent calling the
     # descriptor
     instance.__dict__[self.name] = value
    def _patch_page_models(self, model):
        # PANEL PATCHING

        # Check if the model has a custom edit handler
        if hasattr(model, 'edit_handler'):
            tabs = model.edit_handler.children

            for tab in tabs:
                tab.children = self._patch_panels(tab.children)

        else:
            # If the page doesn't have an edit_handler we patch the panels that
            # wagtail uses by default

            if hasattr(model, 'content_panels'):
                model.content_panels = self._patch_panels(model.content_panels)
            if hasattr(model, 'promote_panels'):
                model.promote_panels = self._patch_panels(model.promote_panels)
            if hasattr(model, 'settings_panels'):
                model.settings_panels = self._patch_panels(model.settings_panels)

        # Clear the edit handler cached value, if it exists, so wagtail reconstructs
        # the edit_handler based on the patched panels
        model.get_edit_handler.cache_clear()

        # SEARCH FIELDS PATCHING

        translation_registered_fields = translator.get_options_for_model(model).fields

        for field in model.search_fields:
            # Check if the field is a SearchField and if it is one of the fields registered for translation
            if field.__class__ is SearchField and field.field_name in translation_registered_fields:
                # If it is we create a clone of the original SearchField to keep all the defined options
                # and replace its name by the translated one
                for language in mt_settings.AVAILABLE_LANGUAGES:
                    translated_field = copy.deepcopy(field)
                    translated_field.field_name = build_localized_fieldname(field.field_name, language)
                    model.search_fields = list(model.search_fields) + [translated_field]

        # OVERRIDE FIELDS
        model_fields = model._meta.get_fields()
        for field in model_fields:
            if isinstance(field, StreamField) and field.name in translation_registered_fields:
                descriptor = getattr(model, field.name)
                _patch_stream_field_meaningful_value(descriptor)

        # OVERRIDE PAGE METHODS
        if TRANSLATE_SLUGS:
            model.set_url_path = _new_set_url_path
            model.route = _new_route
            model._update_descendant_url_paths = _new_update_descendant_url_paths
            if not hasattr(model, '_get_site_root_paths'):
                model.get_url_parts = _new_get_url_parts  # Wagtail<1.11
            model._get_site_root_paths = _new_get_site_root_paths
            _patch_clean(model)

            if not model.save.__name__.startswith('localized'):
                setattr(model, 'save', LocalizedSaveDescriptor(model.save))
 def __set__(self, instance, value):
     if getattr(instance, '_mt_init', False):
         # When assignment takes place in model instance constructor, don't set value.
         # This is essential for only/defer to work, but I think it's sensible anyway.
         return
     lang = get_language()
     loc_field_name = build_localized_fieldname(self.field.name, lang)
     # also update the translation field of the current language
     setattr(instance, loc_field_name, value)
    def __call__(self, instance, *args, **kwargs):
        # when updating, save doesn't check if slug_xx has changed so it can only detect changes in slug
        # from current language. We need to ensure that if a given localized slug changes we call set_url_path
        if not instance.id:  # creating a record, wagtail will call set_url_path, nothing to do.
            return self.func(instance, *args, **kwargs)

        old_record = None
        change_url_path = change_descendant_url_path = False
        for language in mt_settings.AVAILABLE_LANGUAGES:
            localized_slug = build_localized_fieldname('slug', language)
            # similar logic used in save
            if not ('update_fields' in kwargs and localized_slug not in kwargs['update_fields']):
                old_record = old_record or Page.objects.get(id=instance.id)
                if getattr(old_record, localized_slug) != getattr(instance, localized_slug):
                    change_descendant_url_path = True
                    if language != get_language():
                        change_url_path = True
                        break

            # Pages may have have their url_path_xx changed upstream when a parent has its url_path changed.
            # If that's the case let's propagate the change to children
            if not change_descendant_url_path:
                localized_url_path = build_localized_fieldname('url_path', language)
                if not ('update_fields' in kwargs and localized_url_path not in kwargs['update_fields']):
                    old_record = old_record or Page.objects.get(id=instance.id)
                    if getattr(old_record, localized_url_path) != getattr(instance, localized_url_path):
                        change_descendant_url_path = True

        # if any language other than current language had it slug changed set_url_path will be executed
        if change_url_path:
            instance.set_url_path(instance.get_parent())

        result = self.func(instance, *args, **kwargs)

        # update children localized paths if any language had it slug changed
        if change_descendant_url_path:
            _update_translation_descendant_url_paths(old_record, instance)

        # Check if this is a root page of any sites and clear the 'wagtail_site_root_paths_XX' key if so
        if Site.objects.filter(root_page=instance).exists():
            for language in mt_settings.AVAILABLE_LANGUAGES:
                cache.delete('wagtail_site_root_paths_{}'.format(language))

        return result
Exemple #30
0
 def get_slug(self):
     """
     Allows subclasses to implement their own slug creation logic.
     """
     attr = "title"
     if settings.USE_MODELTRANSLATION:
         from modeltranslation.utils import build_localized_fieldname
         attr = build_localized_fieldname(attr, settings.LANGUAGE_CODE)
     # Get self.title_xx where xx is the default language, if any.
     # Get self.title otherwise.
     return slugify(getattr(self, attr, None) or self.title)
Exemple #31
0
def rewrite_lookup_key(model, lookup_key):
    pieces = lookup_key.split('__', 1)
    original_key = pieces[0]

    translatable_fields = get_translatable_fields_for_model(model)
    if translatable_fields is not None:
        # If we are doing a lookup on a translatable field,
        # we want to rewrite it to the actual field name
        # For example, we want to rewrite "name__startswith" to "name_fr__startswith"
        if pieces[0] in translatable_fields:
            pieces[0] = build_localized_fieldname(pieces[0], get_language())

    if len(pieces) > 1:
        # Check if we are doing a lookup to a related trans model
        fields_to_trans_models = get_fields_to_translatable_models(model)
        for field_to_trans, transmodel in fields_to_trans_models:
            # Check ``original key``, as pieces[0] may have been already rewritten.
            if original_key == field_to_trans:
                pieces[1] = rewrite_lookup_key(transmodel, pieces[1])
                break
    return '__'.join(pieces)
Exemple #32
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        required_fields = getattr(self.instance, "required_translation_fields",
                                  [])
        if not required_fields:
            required_fields = [
                f.name for f in self.instance._meta.local_fields
                if hasattr(f, "blank") and f.blank is False
            ]
        required_fields = set(required_fields + [
            'title',
            'slug',
        ])

        for lang_code in mt_settings.AVAILABLE_LANGUAGES:
            for field in required_fields:
                localized = build_localized_fieldname(field, lang_code)
                if localized in self.fields:
                    self.fields[localized].label = "{}*".format(
                        self.fields[localized].label)
def _update_untranslated_descendants_url_paths(page, languages_changed):
    """
    Updates localized URL Paths for child pages that don't have their localized URL Paths set yet
    """
    if not languages_changed:
        return

    condition = Q()
    update_fields = []
    for language in languages_changed:
        localized_url_path = build_localized_fieldname('url_path', language)
        condition |= Q(**{localized_url_path: None})
        update_fields.append(localized_url_path)

    # let's restrict the query to children who don't have localized_url_path set yet
    children = page.get_children().filter(condition)
    for child in children:
        for language in languages_changed:
            _localized_set_url_path(child, page, language)
        child.save(update_fields=update_fields
                   )  # this will trigger any required saves downstream
Exemple #34
0
def test_required_for_language(translated_page, settings, rf):

    edit_handler = translated_page.get_edit_handler()
    form_class = edit_handler.get_form_class()
    form = form_class()
    edit_handler.bind_to_instance(instance=translated_page,
                                  form=form,
                                  request=rf)

    fields = translated_page.get_required_translatable_fields()

    for name, (code, _) in itertools.product(fields, settings.LANGUAGES):
        field_name = build_localized_fieldname(name, lang=code)
        field = form_class.base_fields[field_name]

        if code == settings.LANGUAGE_CODE:
            assert field.required is True
        else:
            assert field.required is False
            assert field.widget.attrs['required_for_language'] is True
            assert field.widget.attrs['class'] == ' required-for-language'
    def _patch_prepopulated_fields(self):
        def localize(sources, lang):
            "Append lang suffix (if applicable) to field list"

            def append_lang(source):
                if source in self.trans_opts.fields:
                    return build_localized_fieldname(source, lang)
                return source

            return tuple(map(append_lang, sources))

        prepopulated_fields = {}
        for dest, sources in self.prepopulated_fields.items():
            if dest in self.trans_opts.fields:
                for lang in mt_settings.AVAILABLE_LANGUAGES:
                    key = build_localized_fieldname(dest, lang)
                    prepopulated_fields[key] = localize(sources, lang)
            else:
                lang = mt_settings.PREPOPULATE_LANGUAGE or get_language()
                prepopulated_fields[dest] = localize(sources, lang)
        self.prepopulated_fields = prepopulated_fields
Exemple #36
0
def add_translation_fields(model, opts):
    """
    Monkey patches the original model class to provide additional fields for
    every language.

    Adds newly created translation fields to the given translation options.
    """
    model_empty_values = getattr(opts, 'empty_values', NONE)
    for field_name in opts.local_fields.keys():
        field_empty_value = parse_field(model_empty_values, field_name, NONE)
        for lang in mt_settings.AVAILABLE_LANGUAGES:
            # Create a dynamic translation field
            translation_field = create_translation_field(
                model=model, field_name=field_name, lang=lang, empty_value=field_empty_value
            )
            # Construct the name for the localized field
            localized_field_name = build_localized_fieldname(field_name, lang)
            # Check if the model already has a field by that name

            if hasattr(model, localized_field_name):
                # Check if are not dealing with abstract field inherited.
                for cls in model.__mro__:
                    if hasattr(cls, '_meta') and cls.__dict__.get(localized_field_name, None):
                        cls_opts = translator._get_options_for_model(cls)
                        if not cls._meta.abstract or field_name not in cls_opts.local_fields:
                            raise ValueError(
                                "Error adding translation field. Model '%s' already"
                                " contains a field named '%s'."
                                % (model._meta.object_name, localized_field_name)
                            )
            # This approach implements the translation fields as full valid
            # django model fields and therefore adds them via add_to_class
            model.add_to_class(localized_field_name, translation_field)
            opts.add_translation_field(field_name, translation_field)

    # Rebuild information about parents fields. If there are opts.local_fields, field cache would be
    # invalidated (by model._meta.add_field() function). Otherwise, we need to do it manually.
    if len(opts.local_fields) == 0:
        model._meta._expire_cache()
        model._meta.get_fields()
Exemple #37
0
    def __mangle_field(self, name):
        model = self._model
        results = field_mangling.send(sender=self, field=name, model=model)
        results = [i[1] for i in results if i[1]]
        if results:
            # response in format tuple(priority: int, mangled_field_name: str)
            results.sort(key=lambda x: x[0], reverse=True)  # Sort by priority
            return results[0][1]

        # Backward compatibility. Deprecated:
        result = {
            'field': name,
        }
        field_conversion.send(sender=self,
                              result=result,
                              field=name,
                              model=model)
        mangled_field_name = result['field']
        if mangled_field_name != name:
            return mangled_field_name

        # django-multilingual-ext support
        if 'modeltranslation' in settings.INSTALLED_APPS:
            from modeltranslation.translator import translator, NotRegistered
            from modeltranslation.utils import get_language, build_localized_fieldname
        else:
            translator = None
        if translator:
            try:
                trans_opts = translator.get_options_for_model(model)
                if name in trans_opts.fields:
                    return build_localized_fieldname(name, get_language())
            except NotRegistered:
                pass

        if hasattr(model.objects, 'localize_fieldname'):
            return model.objects.localize_fieldname(name)

        return name
    def _post_init(self, translated_field, language):
        """
        Common init for subclasses of TranslationField.
        """
        # Store the originally wrapped field for later
        self.translated_field = translated_field
        self.language = language

        # Translation are always optional (for now - maybe add some parameters
        # to the translation options for configuring this)
        self.null = True
        self.blank = True

        # Adjust the name of this field to reflect the language
        self.attname = build_localized_fieldname(self.translated_field.name,
                                                 self.language)
        self.name = self.attname

        # Copy the verbose name and append a language suffix
        # (will show up e.g. in the admin).
        self.verbose_name = build_localized_verbose_name(
            translated_field.verbose_name, language)
Exemple #39
0
    def _build_occurrence(self, occurence):
        """

        Args:
            occurence: a tuple of class path and primary key

        Returns:
            a tuple of the class and the primary key

        """
        from modeltranslation.utils import build_localized_fieldname
        dotted_path, pk = occurence
        module_name, class_name, field_name = dotted_path.rsplit(".", 2)
        module = importlib.import_module(module_name)
        field_name = build_localized_fieldname(field_name, self.language)

        try:
            model_class = getattr(module, class_name)
        except AttributeError:
            model_class = apps.get_model(module_name, class_name)

        return model_class, field_name, pk
Exemple #40
0
    def _patch_simple_panel(self, model, original_panel):
        panel_class = original_panel.__class__
        translated_panels = []
        translation_registered_fields = translator.get_options_for_model(model).fields

        # If the panel field is not registered for translation
        # the original one is returned
        if original_panel.field_name not in translation_registered_fields:
            return [original_panel]

        for language in mt_settings.AVAILABLE_LANGUAGES:
            original_field = model._meta.get_field(original_panel.field_name)
            localized_field_name = build_localized_fieldname(original_panel.field_name, language)

            # if the original field is required and the current language is the default one
            # this field's blank property is set to False
            if not original_field.blank and language == mt_settings.DEFAULT_LANGUAGE:
                localized_field = model._meta.get_field(localized_field_name)
                localized_field.blank = False
            elif isinstance(original_field, StreamField):
                # otherwise the field is optional and
                # if it's a StreamField the stream_block need to be changed to non required
                localized_field = model._meta.get_field(localized_field_name)
                new_stream_block = copy.copy(localized_field.stream_block)
                new_stream_block.meta = copy.copy(localized_field.stream_block.meta)
                new_stream_block.meta.required = False
                localized_field.stream_block = new_stream_block

            localized_panel = panel_class(localized_field_name)

            # Pass the original panel extra attributes to the localized
            if hasattr(original_panel, 'classname'):
                localized_panel.classname = original_panel.classname
            if hasattr(original_panel, 'widget'):
                localized_panel.widget = original_panel.widget

            translated_panels.append(localized_panel)

        return translated_panels
Exemple #41
0
def _localized_update_descendant_url_paths(page, old_url_path, new_url_path, language=None):
    localized_url_path = 'url_path'
    if language:
        localized_url_path = build_localized_fieldname('url_path', language)

    if connection.vendor in ('mssql', 'microsoft'):
        cursor = connection.cursor()
        update_statement = """
            UPDATE wagtailcore_page
            SET {localized_url_path}= CONCAT(%s, (SUBSTRING({localized_url_path}, 0, %s)))
            WHERE path LIKE %s AND id <> %s
        """.format(localized_url_path=localized_url_path)
        cursor.execute(update_statement, [new_url_path, len(old_url_path) + 1, page.path + '%', page.id])
    else:
        (Page.objects
            .rewrite(False)
            .filter(path__startswith=page.path)
            .exclude(**{localized_url_path: None})  # url_path_xx may not be set yet
            .exclude(pk=page.pk)
            .update(**{localized_url_path: Concat(
                Value(new_url_path),
                Substr(localized_url_path, len(old_url_path) + 1))}))
    def handle(self, *args, **options):
        verbosity = int(options['verbosity'])
        if verbosity > 0:
            self.stdout.write("Using default language: %s\n" %
                              DEFAULT_LANGUAGE)
        models = translator.get_registered_models(abstract=False)
        for model in models:
            if verbosity > 0:
                self.stdout.write("Updating data of model '%s'\n" % model)
            opts = translator.get_options_for_model(model)
            for field_name in opts.fields.keys():
                def_lang_fieldname = build_localized_fieldname(
                    field_name, DEFAULT_LANGUAGE)

                # We'll only update fields which do not have an existing value
                q = Q(**{def_lang_fieldname: None})
                field = model._meta.get_field(field_name)
                if field.empty_strings_allowed:
                    q |= Q(**{def_lang_fieldname: ""})

                model._default_manager.filter(q).rewrite(False).update(
                    **{def_lang_fieldname: F(field_name)})
 def get_sync_sql(self, field_name, missing_langs, model):
     """ returns SQL needed for sync schema for a new translatable field """
     qn = connection.ops.quote_name
     style = no_style()
     sql_output = []
     db_table = model._meta.db_table
     for lang in missing_langs:
         new_field = build_localized_fieldname(field_name, lang)
         f = model._meta.get_field(new_field)
         col_type = f.db_type()
         field_sql = [
             style.SQL_FIELD(qn(f.column)),
             style.SQL_COLTYPE(col_type)
         ]
         # column creation
         sql_output.append("ALTER TABLE %s ADD COLUMN %s;" %
                           (qn(db_table), ' '.join(field_sql)))
         if not f.null and lang == settings.LANGUAGE_CODE:
             sql_output.append("ALTER TABLE %s MODIFY COLUMN %s %s %s;" % \
                               (qn(db_table), qn(f.column), col_type, \
                               style.SQL_KEYWORD('NOT NULL')))
     return sql_output
def add_translation_fields(model, opts):
    """
    Monkey patches the original model class to provide additional fields for
    every language.

    Adds newly created translation fields to the given translation options.
    """
    model_empty_values = getattr(opts, 'empty_values', NONE)
    for field_name in opts.local_fields.keys():
        field_empty_value = parse_field(model_empty_values, field_name, NONE)
        for l in mt_settings.AVAILABLE_LANGUAGES:
            # Create a dynamic translation field
            translation_field = create_translation_field(
                model=model,
                field_name=field_name,
                lang=l,
                empty_value=field_empty_value)
            # Construct the name for the localized field
            localized_field_name = build_localized_fieldname(field_name, l)
            # Check if the model already has a field by that name
            if hasattr(model, localized_field_name):
                raise ValueError(
                    "Error adding translation field. Model '%s' already contains a field named"
                    "'%s'." % (model._meta.object_name, localized_field_name))
            # This approach implements the translation fields as full valid
            # django model fields and therefore adds them via add_to_class
            model.add_to_class(localized_field_name, translation_field)
            opts.add_translation_field(field_name, translation_field)

    # Rebuild information about parents fields. If there are opts.local_fields, field cache would be
    # invalidated (by model._meta.add_field() function). Otherwise, we need to do it manually.
    if len(opts.local_fields) == 0:
        try:
            model._meta._fill_fields_cache()
        except AttributeError:
            # Django 1.8 removed _fill_fields_cache
            model._meta._expire_cache()
            model._meta.get_fields()
Exemple #45
0
 def __get__(self, instance, owner):
     """
     Returns value from the translation field for the current language, or
     value for some another language according to fallback languages, or the
     custom fallback value, or field's default value.
     """
     if instance is None:
         return self
     from modeltranslation.translator import translator
     current_language = get_language()
     default = NONE
     undefined = self.fallback_undefined
     if undefined is NONE:
         default = self.field.get_default()
         undefined = default
     langs = resolution_order(current_language, self.fallback_languages)
     for lang in langs:
         loc_field_name = build_localized_fieldname(self.field.name, lang)
         val = getattr(instance, loc_field_name, None)
         if self.meaningful_value(val, undefined):
             if lang != current_language and self.field.get_internal_type() in ["TextField", "CharField"]:
                 opts = translator.get_options_for_model(self.field.model)
                 if hasattr(opts, "fallback_prepend") and self.field.name in opts.fallback_prepend:
                     val = "{}{}".format(opts.fallback_prepend[self.field.name], val)
             return val
     if mt_settings.ENABLE_FALLBACKS and self.fallback_value is not NONE:
         return self.fallback_value
     else:
         if default is NONE:
             default = self.field.get_default()
         # Some fields like FileField behave strange, as their
         # get_default() doesn't return instance of attr_class, but rather
         # None or ''. Normally this case is handled in the descriptor, but
         # since we have overridden it, we must mock it up.
         if (isinstance(self.field, fields.files.FileField) and
                 not isinstance(default, self.field.attr_class)):
             return self.field.attr_class(instance, self.field, default)
         return default
Exemple #46
0
 def get_sync_sql(self, field_name, missing_langs, model):
     """
     Returns SQL needed for sync schema for a new translatable field.
     """
     qn = connection.ops.quote_name
     style = no_style()
     sql_output = []
     db_table = model._meta.db_table
     for lang in missing_langs:
         new_field = build_localized_fieldname(field_name, lang)
         f = model._meta.get_field(new_field)
         col_type = f.db_type(connection=connection)
         field_sql = [
             style.SQL_FIELD(qn(f.column)),
             style.SQL_COLTYPE(col_type)
         ]
         # column creation
         stmt = "ALTER TABLE %s ADD COLUMN %s" % (qn(db_table),
                                                  ' '.join(field_sql))
         if not f.null:
             stmt += " " + style.SQL_KEYWORD('NOT NULL')
         sql_output.append(stmt + ";")
     return sql_output
Exemple #47
0
def _localized_update_descendant_url_paths(self, old_url_path, new_url_path, language):
    localized_url_path = build_localized_fieldname('url_path', language)

    for db_table in WagtailTranslator._page_fields_tables:
        cursor = connection.cursor()
        if connection.vendor == 'sqlite':
            update_statement = """
                UPDATE {db_table}
                SET {localized_url_path} = %s || substr({localized_url_path}, %s)
                WHERE EXISTS (SELECT * FROM wagtailcore_page AS p
                    WHERE p.id = {db_table}.page_ptr_id AND p.path LIKE %s)
                AND page_ptr_id <> %s
            """.format(db_table=db_table, localized_url_path=localized_url_path)
        elif connection.vendor == 'mysql':
            update_statement = """
                UPDATE {db_table} t
                JOIN wagtailcore_page p ON p.id = t.page_ptr_id
                SET {localized_url_path}= CONCAT(%s, substring({localized_url_path}, %s))
                WHERE p.path LIKE %s AND t.page_ptr_id <> %s
            """.format(db_table=db_table, localized_url_path=localized_url_path)
        elif connection.vendor in ('mssql', 'microsoft'):
            update_statement = """
                UPDATE t
                SET {localized_url_path}= CONCAT(%s, (SUBSTRING({localized_url_path}, 0, %s)))
                FROM {db_table} t
                JOIN wagtailcore_page p
                    ON p.id = t.page_ptr_id
                WHERE p.path LIKE %s AND t.page_ptr_id <> %s
            """.format(db_table=db_table, localized_url_path=localized_url_path)
        else:
            update_statement = """
                UPDATE {db_table} as t
                SET {localized_url_path} = %s || substring({localized_url_path} from %s)
                FROM wagtailcore_page AS p
                WHERE p.id = t.page_ptr_id AND p.path LIKE %s AND t.page_ptr_id <> %s
            """.format(db_table=db_table, localized_url_path=localized_url_path)
        cursor.execute(update_statement, [new_url_path, len(old_url_path) + 1, self.path + '%', self.page_ptr_id])
Exemple #48
0
    def __init__(self, *args, **kwargs):
        super(BaseArticleAdminForm, self).__init__(*args, **kwargs)  # pylint: disable=E1002
        self.article = kwargs.get('instance', None)
        templates = get_article_templates(self.article, getattr(self, "current_user", None))
        if templates:
            self.fields['template'].widget = forms.Select(choices=templates)

        self.slug_fields = []
        if is_localized():
            for lang_and_name in settings.LANGUAGES:
                from modeltranslation.utils import build_localized_fieldname
                field_name = build_localized_fieldname('slug', lang_and_name[0])
                self.slug_fields.append(field_name)
        else:
            self.slug_fields = ['slug']

        can_change_article_slug = can_rewrite_url()

        if not can_change_article_slug:
            can_change_article_slug = (self.article.publication != BaseArticle.PUBLISHED) if self.article else True

        for slug_field in self.slug_fields:
            if not can_change_article_slug:
                self.fields[slug_field].widget = ReadOnlyInput()
Exemple #49
0
    def __init__(self, translated_field, language, *args, **kwargs):
        # Store the originally wrapped field for later
        self.translated_field = translated_field
        self.language = language

        # Update the dict of this field with the content of the original one
        # This might be a bit radical?! Seems to work though...
        self.__dict__.update(translated_field.__dict__)

        # Translation are always optional (for now - maybe add some parameters
        # to the translation options for configuring this)
        self.null = True
        self.blank = True

        # Adjust the name of this field to reflect the language
        self.attname = build_localized_fieldname(translated_field.name,
                                                 language)
        self.name = self.attname

        # Copy the verbose name and append a language suffix
        # (will show up e.g. in the admin).
        self.verbose_name =\
        build_localized_verbose_name(translated_field.verbose_name,
                                     language)
Exemple #50
0
    def set_required_for_language(form_class):
        """Mark fields that must be populated for a language to be available.

        Some fields are optional, but for their language to be available the
        field must be populated e.g., for German to be available for a page
        then the fields title_de and description_de must must be populated.

        `required_for_language` is used by templates to style the page and hint
        the user which fields they need to populate.

        Arguments:
            form_class {WagtailAdminPageForm}

        """

        css_classname = 'required-for-language'
        fields = form_class._meta.model.get_required_translatable_fields()
        for name, (code, _) in itertools.product(fields, settings.LANGUAGES):
            field_name = build_localized_fieldname(name, lang=code)
            field = form_class.base_fields[field_name]
            if field.required is False:
                attrs = field.widget.attrs
                attrs['required_for_language'] = True
                attrs['class'] = attrs.get('class', '') + ' ' + css_classname
def add_translation_fields(model, opts):
    """
    Monkey patches the original model class to provide additional fields for
    every language.

    Adds newly created translation fields to the given translation options.
    """
    for field_name in opts.local_fields.keys():
        for l in settings.LANGUAGES:
            # Create a dynamic translation field
            translation_field = create_translation_field(model=model,
                                                         field_name=field_name,
                                                         lang=l[0])
            # Construct the name for the localized field
            localized_field_name = build_localized_fieldname(field_name, l[0])
            # Check if the model already has a field by that name
            if hasattr(model, localized_field_name):
                raise ValueError(
                    "Error adding translation field. Model '%s' already contains a field named"
                    "'%s'." % (model._meta.object_name, localized_field_name))
            # This approach implements the translation fields as full valid
            # django model fields and therefore adds them via add_to_class
            model.add_to_class(localized_field_name, translation_field)
            opts.add_translation_field(field_name, translation_field)
Exemple #52
0
    def _translated(self):
        """
        Builds a list of translation strings using the instance's target_language

        Returns:
            None

        """
        # local import allows users w/o modeltranslations installed to build and export PO files
        from modeltranslation.utils import build_localized_fieldname

        for model_class in self.models_and_fields.keys():
            class_path = ".".join(
                [model_class.__module__, model_class.__name__])
            for instance in model_class._default_manager.all().order_by('pk'):
                for field_name in self.models_and_fields[model_class]:
                    field_value = getattr(instance, field_name) or ""
                    msgstr = getattr(
                        instance,
                        build_localized_fieldname(field_name,
                                                  self.target_language)) or ""
                    self.strings[field_value]['msgstr'] = msgstr
                    self.strings[field_value]['occurrences'].append(
                        (".".join([class_path, field_name]), instance.pk))
    def _patch_page_models(self, model):
        # PANEL PATCHING

        # Check if the model has a custom edit handler
        if hasattr(model, 'edit_handler'):
            tabs = model.edit_handler.children

            for tab in tabs:
                tab.children = self._patch_panels(tab.children)

        else:
            # If the page doesn't have an edit_handler we patch the panels that
            # wagtail uses by default

            if hasattr(model, 'content_panels'):
                model.content_panels = self._patch_panels(model.content_panels)
            if hasattr(model, 'promote_panels'):
                model.promote_panels = self._patch_panels(model.promote_panels)
            if hasattr(model, 'settings_panels'):
                model.settings_panels = self._patch_panels(
                    model.settings_panels)

        # Clear the edit handler cached value, if it exists, so wagtail reconstructs
        # the edit_handler based on the patched panels
        model.get_edit_handler.cache_clear()

        # SEARCH FIELDS PATCHING

        translation_registered_fields = translator.get_options_for_model(
            model).fields

        for field in model.search_fields:
            # Check if the field is a SearchField and if it is one of the fields registered for translation
            if isinstance(
                    field, SearchField
            ) and field.field_name in translation_registered_fields:
                # If it is we create a clone of the original SearchField to keep all the defined options
                # and replace its name by the translated one
                for language in mt_settings.AVAILABLE_LANGUAGES:
                    translated_field = copy.deepcopy(field)
                    translated_field.field_name = build_localized_fieldname(
                        field.field_name, language)
                    model.search_fields = list(
                        model.search_fields) + [translated_field]

        # OVERRIDE FIELDS
        model_fields = model._meta.get_fields()
        for field in model_fields:
            if isinstance(field, StreamField
                          ) and field.name in translation_registered_fields:
                descriptor = getattr(model, field.name)
                _patch_stream_field_meaningful_value(descriptor)

        # OVERRIDE CLEAN METHOD
        model.base_form_class = patch_admin_page_form(model.base_form_class)

        # OVERRIDE PAGE METHODS
        if TRANSLATE_SLUGS:
            model.set_url_path = _new_set_url_path
            model.route = _new_route
            model._update_descendant_url_paths = _new_update_descendant_url_paths
            if not hasattr(model, '_get_site_root_paths'):
                model.get_url_parts = _new_get_url_parts  # Wagtail<1.11
            model._get_site_root_paths = _new_get_site_root_paths
            _patch_clean(model)

            if not model.save.__name__.startswith('localized'):
                setattr(model, 'save', LocalizedSaveDescriptor(model.save))
 def cache_name(self):
     lang = get_language()
     cache = build_localized_fieldname(self.accessor, lang)
     return "_%s_cache" % cache
 def __set__(self, instance, value):
     lang = get_language()
     loc_field_name = build_localized_fieldname(self.field_name, lang)
     # Localized field name with '_id'
     loc_attname = instance._meta.get_field(loc_field_name).get_attname()
     setattr(instance, loc_attname, value)
Exemple #56
0
    class Meta:
        """Meta objects."""

        ordering = map(lambda (lang_code, __): build_localized_fieldname('company_name', lang_code),
                       settings.LANGUAGES)
Exemple #57
0
    def __init__(self, *args, **kwargs):
        # CopyPage must be passed a 'page' kwarg indicating the page to be copied
        self.page = kwargs.pop('page')
        self.user = kwargs.pop('user', None)
        can_publish = kwargs.pop('can_publish')
        super(CopyForm, self).__init__(*args, **kwargs)
        for lang in mt_settings.AVAILABLE_LANGUAGES:
            new_title_field = build_localized_fieldname("new_title", lang)
            title_field = build_localized_fieldname("title", lang)
            new_slug_field = build_localized_fieldname("new_slug", lang)
            slug_field = build_localized_fieldname("slug", lang)
            if getattr(self.page, title_field):
                self.fields[new_title_field] = forms.CharField(
                    initial=getattr(self.page, title_field),
                    label=_("New title [{}]".format(lang)))
                self.fields[new_slug_field] = forms.SlugField(
                    initial=getattr(self.page, slug_field),
                    label=_("New slug [{}]".format(lang)))

        self.fields['new_parent_page'] = forms.ModelChoiceField(
            initial=self.page.get_parent(),
            queryset=Page.objects.all(),
            widget=widgets.AdminPageChooser(can_choose_root=True,
                                            user_perms='copy_to'),
            label=_("New parent page"),
            help_text=_(
                "This copy will be a child of this given parent page."))
        pages_to_copy = self.page.get_descendants(inclusive=True)
        subpage_count = pages_to_copy.count() - 1
        if subpage_count > 0:
            self.fields['copy_subpages'] = forms.BooleanField(
                required=False,
                initial=True,
                label=_("Copy subpages"),
                help_text=ungettext("This will copy %(count)s subpage.",
                                    "This will copy %(count)s subpages.",
                                    subpage_count) % {'count': subpage_count})

        if can_publish:
            pages_to_publish_count = pages_to_copy.live().count()
            if pages_to_publish_count > 0:
                # In the specific case that there are no subpages, customise the field label and help text
                if subpage_count == 0:
                    label = _("Publish copied page")
                    help_text = _(
                        "This page is live. Would you like to publish its copy as well?"
                    )
                else:
                    label = _("Publish copies")
                    help_text = ungettext(
                        "%(count)s of the pages being copied is live. Would you like to publish its copy?",
                        "%(count)s of the pages being copied are live. Would you like to publish their copies?",
                        pages_to_publish_count) % {
                            'count': pages_to_publish_count
                        }

                self.fields['publish_copies'] = forms.BooleanField(
                    required=False,
                    initial=True,
                    label=label,
                    help_text=help_text)
def copy(request, page_id):
    page = Page.objects.get(id=page_id)

    # Parent page defaults to parent of source page
    parent_page = page.get_parent()

    # Check if the user has permission to publish subpages on the parent
    can_publish = parent_page.permissions_for_user(request.user).can_publish_subpage()

    # Create the form
    form = CopyForm(request.POST or None, user=request.user, page=page, can_publish=can_publish)

    next_url = get_valid_next_url_from_request(request)

    for fn in hooks.get_hooks('before_copy_page'):
        result = fn(request, page)
        if hasattr(result, 'status_code'):
            return result

    # Check if user is submitting
    if request.method == 'POST':
        # Prefill parent_page in case the form is invalid (as prepopulated value for the form field,
        # because ModelChoiceField seems to not fall back to the user given value)
        parent_page = Page.objects.get(id=request.POST['new_parent_page'])

        if form.is_valid():
            # Receive the parent page (this should never be empty)
            if form.cleaned_data['new_parent_page']:
                parent_page = form.cleaned_data['new_parent_page']

            if not page.permissions_for_user(request.user).can_copy_to(
                    parent_page,
                    form.cleaned_data.get('copy_subpages')):
                raise PermissionDenied

            # Re-check if the user has permission to publish subpages on the new parent
            can_publish = parent_page.permissions_for_user(request.user).can_publish_subpage()

            # build translated attrs
            translated_attrs = {}
            for lang in mt_settings.AVAILABLE_LANGUAGES:
                new_title_field = build_localized_fieldname("new_title", lang)
                if form.cleaned_data.get(new_title_field):
                    title_field = build_localized_fieldname("title", lang)
                    new_slug_field = build_localized_fieldname("new_slug", lang)
                    slug_field = build_localized_fieldname("slug", lang)
                    translated_attrs.update({
                        '{}'.format(title_field): form.cleaned_data[new_title_field],
                        '{}'.format(slug_field): form.cleaned_data[new_slug_field],
                    })

            # Copy the page
            new_page = page.copy(
                recursive=form.cleaned_data.get('copy_subpages'),
                to=parent_page,
                update_attrs=translated_attrs,
                keep_live=(can_publish and form.cleaned_data.get('publish_copies')),
                user=request.user,
            )

            # Give a success message back to the user
            if form.cleaned_data.get('copy_subpages'):
                messages.success(
                    request,
                    _("Page '{0}' and {1} subpages copied.").format(
                        page.get_admin_display_title(),
                        new_page.get_descendants().count())
                )
            else:
                messages.success(request, _("Page '{0}' copied.").format(
                    page.get_admin_display_title()))

            for fn in hooks.get_hooks('after_copy_page'):
                result = fn(request, page, new_page)
                if hasattr(result, 'status_code'):
                    return result

            # Redirect to explore of parent page
            if next_url:
                return redirect(next_url)
            return redirect('wagtailadmin_explore', parent_page.id)

    return render(request, 'wagtailadmin/pages/copy.html', {
        'page': page,
        'form': form,
        'next': next_url,
    })
Exemple #59
0
 def append_lang(source):
     if source in self.trans_opts.fields:
         return build_localized_fieldname(source, lang)
     return source
Exemple #60
0
    def test_translation_api_behaviour(self):
        """
        NOTE: This test covers for API behaviour for translation mixin serializer
        TODO: Use model with more then one field used for translation
        """
        country = Country.objects.create(name='country', iso='YY')
        district = District.objects.create(name='district', country=country)
        disaster_names = {
            'en': 'Disaster 1 (EN)',
            'es': 'Disaster 1 (ES)',
            'fr': 'Disaster 1 (FR)',
            'ar': 'Disaster 1 (AR)',
        }
        names = {
            'en': 'Project 1 (EN)',
            'es': 'Project 1 (ES)',
            'fr': 'Project 1 (FR)',
            'ar': 'Project 1 (AR)',
        }

        disaster_type = DisasterType()
        for lang, _ in settings.LANGUAGES:
            setattr(disaster_type, build_localized_fieldname('name', lang),
                    disaster_names[lang])
        disaster_type.save()

        self.authenticate(self.user)

        # Using both header and GET Param
        for using in ['header']:
            for current_language, _ in settings.LANGUAGES:
                body = {
                    'reporting_ns': country.id,
                    'project_country': district.country.id,
                    'dtype': disaster_type.pk,
                    'project_districts': [district.id],
                    'name': names[current_language],
                    'programme_type': ProgrammeTypes.BILATERAL.value,
                    'primary_sector': Sectors.WASH.value,
                    'secondary_sectors':
                    [Sectors.CEA.value, Sectors.PGI.value],
                    'operation_type': OperationTypes.EMERGENCY_OPERATION.value,
                    'start_date': '2012-11-12',
                    'end_date': '2013-11-13',
                    'budget_amount': 7000,
                    'target_total': 100,
                    'status': Statuses.PLANNED.value,
                }

                # POST (Creation)
                with self.capture_on_commit_callbacks(execute=True):
                    # https://docs.djangoproject.com/en/3.0/topics/testing/tools/#setting-the-language
                    resp = self.client.post(
                        '/api/v2/project/',
                        body,
                        HTTP_ACCEPT_LANGUAGE=current_language)

                # NOTE: non-safe methods are not allowd for non english language
                if current_language == settings.LANGUAGE_CODE:
                    self.assertEqual(resp.status_code, 201, resp.content)
                else:
                    self.assertEqual(resp.status_code, 405, resp.content)

                project_id = resp.json().get('id')
                if project_id is None:
                    continue

                # GET
                for lang, _ in settings.LANGUAGES:
                    resp = self.client.get(f'/api/v2/project/{project_id}/',
                                           HTTP_ACCEPT_LANGUAGE=lang)
                    self.assertEqual(resp.status_code, 200, resp.content)
                    resp_body = resp.json()
                    if lang == current_language:
                        assert resp_body['name'] == names[current_language], \
                            f"Name ({lang}): <{resp_body['name']}> should be <{names[current_language]}>"
                    else:
                        translated_text = self.aws_translator._fake_translation(
                            names[current_language], lang, current_language)
                        assert resp_body['name'] == translated_text,\
                            f"Name ({lang}): should be <{translated_text}> instead of <{resp_body['name']}>"
                    # Test Nested Field Disaster Type Name
                    assert resp_body['dtype_detail']['name'] == disaster_names[lang], \
                        f"Name ({lang}): <{resp_body['dtype_detail']['name']}> should be <{disaster_names[lang]}>"

                # Update (This doesn't reset other language)
                body['name'] += ''
                with self.capture_on_commit_callbacks(execute=True):
                    resp = self.client.put(
                        f'/api/v2/project/{project_id}/',
                        body,
                        HTTP_ACCEPT_LANGUAGE=current_language)
                self.assertEqual(resp.status_code, 200, resp.content)

                # non current language field should not be None
                project = Project.objects.get(pk=project_id)
                for lang, _ in settings.LANGUAGES:
                    value = getattr(project,
                                    build_localized_fieldname('name', lang))
                    if lang == current_language:
                        assert value == body[
                            'name'], f"Name ({lang}): <{value}> should be {body['name']}"
                    else:
                        assert value is not None, f"Name ({lang}): value shouldn't be None"

                # Update (Reset other language) (Without onCommit Trigger by not using self.capture_on_commit_callbacks)
                # This way the language field are reset but auto translation work is reverted back i.e reset is preserved
                body['name'] += ' Changed'
                resp = self.client.put(f'/api/v2/project/{project_id}/',
                                       body,
                                       HTTP_ACCEPT_LANGUAGE=current_language)
                self.assertEqual(resp.status_code, 200, resp.content)

                # non current language field should be None
                project = Project.objects.get(pk=project_id)
                for lang, _ in settings.LANGUAGES:
                    value = getattr(project,
                                    build_localized_fieldname('name', lang))
                    if lang == current_language:
                        assert value == body[
                            'name'], f"Name ({lang}): <{value}> should be {body['name']}"
                    else:
                        assert value is None, f"Name ({lang}): <{value}> should be None"

                # Again Update (With onCommit Trigger: Mock Translation)
                body['name'] += ' Again Changed'
                with self.capture_on_commit_callbacks(execute=True):
                    resp = self.client.put(
                        f'/api/v2/project/{project_id}/',
                        body,
                        HTTP_ACCEPT_LANGUAGE=current_language)
                self.assertEqual(resp.status_code, 200, resp.content)

                # non current language field should be None
                project = Project.objects.get(pk=project_id)
                for lang, _ in settings.LANGUAGES:
                    value = getattr(project,
                                    build_localized_fieldname('name', lang))
                    if lang == current_language:
                        assert value == body[
                            'name'], f"Name ({lang}): <{value}> should be {body['name']}"
                    else:
                        assert value is not None, f"Name ({lang}): <{value}> should not be None"