#6230 [RFC 54] Internationalisation UI changes for snippets/modeladmin

Open kaedroho
Coverage Reach
admin/views/pages/edit.py admin/views/pages/create.py admin/views/pages/revisions.py admin/views/pages/workflow.py admin/views/pages/preview.py admin/views/pages/search.py admin/views/pages/history.py admin/views/pages/moderation.py admin/views/pages/move.py admin/views/pages/copy.py admin/views/pages/listing.py admin/views/pages/lock.py admin/views/pages/convert_alias.py admin/views/pages/unpublish.py admin/views/pages/delete.py admin/views/pages/ordering.py admin/views/pages/usage.py admin/views/pages/utils.py admin/views/workflows.py admin/views/generic.py admin/views/account.py admin/views/mixins.py admin/views/chooser.py admin/views/reports.py admin/views/home.py admin/views/collections.py admin/views/page_privacy.py admin/views/collection_privacy.py admin/views/userbar.py admin/views/tags.py admin/rich_text/converters/html_to_contentstate.py admin/rich_text/converters/editor_html.py admin/rich_text/converters/html_ruleset.py admin/rich_text/converters/contentstate_models.py admin/rich_text/converters/contentstate.py admin/rich_text/editors/hallo.py admin/rich_text/editors/draftail/__init__.py admin/rich_text/editors/draftail/features.py admin/rich_text/__init__.py admin/edit_handlers.py admin/forms/collections.py admin/forms/workflows.py admin/forms/pages.py admin/forms/models.py admin/forms/choosers.py admin/forms/view_restrictions.py admin/forms/tags.py admin/forms/auth.py admin/forms/search.py admin/forms/__init__.py admin/compare.py admin/widgets/chooser.py admin/widgets/datetime.py admin/widgets/button.py admin/widgets/filtered_select.py admin/widgets/tags.py admin/widgets/workflows.py admin/widgets/button_select.py admin/widgets/auto_height_text.py admin/widgets/__init__.py admin/wagtail_hooks.py admin/templatetags/wagtailadmin_tags.py admin/templatetags/wagtailuserbar.py admin/action_menu.py admin/mail.py admin/api/serializers.py admin/api/views.py admin/api/filters.py admin/api/urls.py admin/filters.py admin/auth.py admin/menu.py admin/userbar.py admin/viewsets/model.py admin/viewsets/__init__.py admin/viewsets/base.py admin/checks.py admin/search.py admin/urls/__init__.py admin/urls/pages.py admin/urls/collections.py admin/urls/workflows.py admin/urls/reports.py admin/urls/password_reset.py admin/site_summary.py admin/messages.py admin/log_action_registry.py admin/staticfiles.py admin/navigation.py admin/models.py admin/signal_handlers.py admin/localization.py admin/modal_workflow.py admin/apps.py admin/jinja2tags.py admin/datetimepicker.py admin/blocks.py admin/signals.py admin/__init__.py contrib/modeladmin/views.py contrib/modeladmin/options.py contrib/modeladmin/helpers/button.py contrib/modeladmin/helpers/permission.py contrib/modeladmin/helpers/url.py contrib/modeladmin/helpers/search.py contrib/modeladmin/helpers/__init__.py contrib/modeladmin/templatetags/modeladmin_tags.py contrib/modeladmin/menus.py contrib/modeladmin/mixins.py contrib/modeladmin/forms.py contrib/modeladmin/apps.py contrib/modeladmin/__init__.py contrib/redirects/views.py contrib/redirects/base_formats.py contrib/redirects/management/commands/import_redirects.py contrib/redirects/models.py contrib/redirects/forms.py contrib/redirects/tmp_storages.py contrib/redirects/middleware.py contrib/redirects/utils.py contrib/redirects/wagtail_hooks.py contrib/redirects/apps.py contrib/redirects/urls.py contrib/redirects/permissions.py contrib/redirects/__init__.py contrib/postgres_search/backend.py contrib/postgres_search/query.py contrib/postgres_search/utils.py contrib/postgres_search/models.py contrib/postgres_search/apps.py contrib/postgres_search/__init__.py contrib/forms/views.py contrib/forms/models.py contrib/forms/forms.py contrib/forms/utils.py contrib/forms/edit_handlers.py contrib/forms/wagtail_hooks.py contrib/forms/apps.py contrib/forms/urls.py contrib/forms/__init__.py contrib/frontend_cache/tests.py contrib/frontend_cache/backends.py contrib/frontend_cache/utils.py contrib/frontend_cache/signal_handlers.py contrib/frontend_cache/apps.py contrib/frontend_cache/__init__.py contrib/search_promotions/tests.py contrib/search_promotions/views.py contrib/search_promotions/forms.py contrib/search_promotions/wagtail_hooks.py contrib/search_promotions/models.py contrib/search_promotions/templatetags/wagtailsearchpromotions_tags.py contrib/search_promotions/apps.py contrib/search_promotions/admin_urls.py contrib/search_promotions/__init__.py contrib/routable_page/tests.py contrib/routable_page/models.py contrib/routable_page/templatetags/wagtailroutablepage_tags.py contrib/routable_page/apps.py contrib/routable_page/__init__.py contrib/settings/views.py contrib/settings/models.py contrib/settings/jinja2tags.py contrib/settings/registry.py contrib/settings/context_processors.py contrib/settings/forms.py contrib/settings/templatetags/wagtailsettings_tags.py contrib/settings/wagtail_hooks.py contrib/settings/apps.py contrib/settings/urls.py contrib/settings/permissions.py contrib/settings/__init__.py contrib/table_block/tests.py contrib/table_block/blocks.py contrib/table_block/templatetags/table_block_tags.py contrib/table_block/apps.py contrib/table_block/__init__.py contrib/sitemaps/tests.py contrib/sitemaps/sitemap_generator.py contrib/sitemaps/views.py contrib/sitemaps/apps.py contrib/sitemaps/__init__.py contrib/styleguide/views.py contrib/styleguide/wagtail_hooks.py contrib/styleguide/tests.py contrib/styleguide/apps.py contrib/styleguide/__init__.py core/models.py core/blocks/field_block.py core/blocks/stream_block.py core/blocks/base.py core/blocks/struct_block.py core/blocks/list_block.py core/blocks/static_block.py core/blocks/__init__.py core/blocks/utils.py core/management/commands/fixtree.py core/management/commands/publish_scheduled_pages.py core/management/commands/create_log_entries_from_revisions.py core/management/commands/replace_text.py core/management/commands/purge_revisions.py core/management/commands/move_pages.py core/management/commands/set_url_paths.py core/permission_policies/collections.py core/permission_policies/base.py core/permission_policies/__init__.py core/query.py core/rich_text/rewriters.py core/rich_text/feature_registry.py core/rich_text/__init__.py core/rich_text/pages.py core/utils.py core/templatetags/wagtailcore_tags.py core/fields.py core/whitelist.py core/treebeard.py core/wagtail_hooks.py core/hooks.py core/jinja2tags.py core/views.py core/signal_handlers.py core/forms.py core/workflows.py core/sites.py core/signals.py core/urls.py core/apps.py core/url_routing.py core/compat.py core/permissions.py core/__init__.py core/exceptions.py images/views/images.py images/views/multiple.py images/views/chooser.py images/views/serve.py images/models.py images/image_operations.py images/rect.py images/templatetags/wagtailimages_tags.py images/wagtail_hooks.py images/fields.py images/rich_text/contentstate.py images/rich_text/editor_html.py images/rich_text/__init__.py images/formats.py images/api/v2/views.py images/api/v2/serializers.py images/api/fields.py images/api/admin/views.py images/api/admin/serializers.py images/utils.py images/forms.py images/checks.py images/blocks.py images/widgets.py images/jinja2tags.py images/signal_handlers.py images/edit_handlers.py images/__init__.py images/apps.py images/shortcuts.py images/admin.py images/admin_urls.py images/permissions.py images/urls.py images/exceptions.py search/backends/elasticsearch2.py search/backends/base.py search/backends/db.py search/backends/elasticsearch7.py search/backends/__init__.py search/backends/elasticsearch5.py search/backends/elasticsearch6.py search/index.py search/management/commands/update_index.py search/management/commands/search_garbage_collect.py search/management/commands/wagtail_update_index.py search/utils.py search/models.py search/query.py search/views/queries.py search/signal_handlers.py search/apps.py search/queryset.py search/wagtail_hooks.py search/urls/admin.py search/forms.py search/__init__.py snippets/tests.py snippets/views/snippets.py snippets/views/chooser.py snippets/action_menu.py snippets/wagtail_hooks.py snippets/widgets.py snippets/models.py snippets/blocks.py snippets/permissions.py snippets/templatetags/wagtailsnippets_admin_tags.py snippets/edit_handlers.py snippets/apps.py snippets/urls.py snippets/__init__.py users/tests.py users/forms.py users/views/users.py users/views/groups.py users/wagtail_hooks.py users/templatetags/wagtailusers_tags.py users/models.py users/utils.py users/apps.py users/widgets.py users/urls/users.py users/__init__.py api/v2/views.py api/v2/serializers.py api/v2/filters.py api/v2/utils.py api/v2/router.py api/v2/signal_handlers.py api/v2/pagination.py api/v2/apps.py api/v2/__init__.py api/conf.py api/__init__.py documents/views/documents.py documents/views/chooser.py documents/views/serve.py documents/views/multiple.py documents/models.py documents/wagtail_hooks.py documents/rich_text/editor_html.py documents/rich_text/contentstate.py documents/rich_text/__init__.py documents/api/v2/views.py documents/api/v2/serializers.py documents/api/admin/views.py documents/forms.py documents/widgets.py documents/blocks.py documents/__init__.py documents/apps.py documents/signal_handlers.py documents/admin.py documents/edit_handlers.py documents/admin_urls.py documents/permissions.py documents/urls.py embeds/finders/oembed.py embeds/finders/embedly.py embeds/finders/__init__.py embeds/finders/base.py embeds/oembed_providers.py embeds/rich_text/contentstate.py embeds/rich_text/__init__.py embeds/rich_text/editor_html.py embeds/blocks.py embeds/models.py embeds/views/chooser.py embeds/embeds.py embeds/wagtail_hooks.py embeds/templatetags/wagtailembeds_tags.py embeds/format.py embeds/apps.py embeds/exceptions.py embeds/forms.py embeds/urls.py embeds/__init__.py utils/sendfile.py utils/setup.py utils/sendfile_streaming_backend.py utils/deprecation.py utils/decorators.py utils/version.py utils/widgets.py utils/apps.py utils/loading.py utils/urlpatterns.py utils/utils.py sites/tests.py sites/views.py sites/wagtail_hooks.py sites/forms.py sites/apps.py sites/__init__.py locales/tests.py locales/views.py locales/wagtail_hooks.py locales/forms.py locales/utils.py locales/apps.py locales/__init__.py bin/wagtail.py __init__.py

Flags

Flags have been temporarily removed from this view while the flagging feature is refactored for better performance and user experience.

You can still use flags when viewing individual files. Flag-level thresholds will also remain on pull and merge requests in your repository provider.

More information can be found in our documentation.


@@ -16,6 +16,7 @@
Loading
16 16
from wagtail.admin.edit_handlers import ObjectList, extract_panel_definitions_from_model_class
17 17
from wagtail.admin.forms.search import SearchForm
18 18
from wagtail.core import hooks
19 +
from wagtail.core.models import TranslatableMixin, Locale
19 20
from wagtail.search.backends import get_search_backend
20 21
from wagtail.search.index import class_is_indexed
21 22
from wagtail.snippets.action_menu import SnippetActionMenu
@@ -84,6 +85,22 @@
Loading
84 85
85 86
    items = model.objects.all()
86 87
88 +
    if issubclass(model, TranslatableMixin):
89 +
        if 'lang' in request.GET:
90 +
            try:
91 +
                locale = Locale.objects.get(language_code=request.GET['lang'])
92 +
            except Locale.DoesNotExist:
93 +
                # Redirect to snippet without language
94 +
                return redirect('wagtailsnippets:list', app_label, model_name)
95 +
        else:
96 +
            # Default to active locale (this will take into account the user's chosen admin language)
97 +
            locale = Locale.get_active()
98 +
99 +
        items = items.filter(locale=locale)
100 +
101 +
    else:
102 +
        locale = None
103 +
87 104
    # Preserve the snippet's model-level ordering if specified, but fall back on PK if not
88 105
    # (to ensure pagination is consistent)
89 106
    if not items.ordered:
@@ -128,6 +145,8 @@
Loading
128 145
        'search_form': search_form,
129 146
        'is_searching': is_searching,
130 147
        'query_string': search_query,
148 +
        'locales': Locale.objects.all() if issubclass(model, TranslatableMixin) else [],
149 +
        'chosen_locale': locale,
131 150
    })
132 151
133 152
@@ -144,6 +163,16 @@
Loading
144 163
            return result
145 164
146 165
    instance = model()
166 +
167 +
    # Set locale of the new instance
168 +
    if issubclass(model, TranslatableMixin):
169 +
        selected_locale = request.GET.get('locale')
170 +
        if selected_locale:
171 +
            instance.locale = get_object_or_404(Locale, language_code=selected_locale)
172 +
        else:
173 +
            instance.locale = Locale.get_default()
174 +
175 +
    # Make edit handler
147 176
    edit_handler = get_snippet_edit_handler(model)
148 177
    edit_handler = edit_handler.bind_to(request=request)
149 178
    form_class = edit_handler.get_form_class()
@@ -172,7 +201,11 @@
Loading
172 201
                if hasattr(result, 'status_code'):
173 202
                    return result
174 203
175 -
            return redirect('wagtailsnippets:list', app_label, model_name)
204 +
            urlquery = ''
205 +
            if isinstance(instance, TranslatableMixin) and instance.locale is not Locale.get_default():
206 +
                urlquery = '?lang=' + instance.locale.language_code
207 +
208 +
            return redirect(reverse('wagtailsnippets:list', args=[app_label, model_name]) + urlquery)
176 209
        else:
177 210
            messages.validation_error(
178 211
                request, _("The snippet could not be created due to errors."), form
@@ -187,6 +220,7 @@
Loading
187 220
        'edit_handler': edit_handler,
188 221
        'form': form,
189 222
        'action_menu': SnippetActionMenu(request, view='create', model=model),
223 +
        'locale': instance.locale if isinstance(instance, TranslatableMixin) else None,
190 224
    })
191 225
192 226
@@ -248,6 +282,7 @@
Loading
248 282
        'edit_handler': edit_handler,
249 283
        'form': form,
250 284
        'action_menu': SnippetActionMenu(request, view='edit', instance=instance),
285 +
        'locale': instance.locale if isinstance(instance, TranslatableMixin) else None,
251 286
    })
252 287
253 288

@@ -7,6 +7,7 @@
Loading
7 7
8 8
from wagtail.admin.forms.search import SearchForm
9 9
from wagtail.admin.modal_workflow import render_modal_workflow
10 +
from wagtail.core.models import Locale, TranslatableMixin
10 11
from wagtail.search.backends import get_search_backend
11 12
from wagtail.search.index import class_is_indexed
12 13
from wagtail.snippets.views.snippets import get_snippet_model_from_url_params
@@ -22,6 +23,24 @@
Loading
22 23
    if not items.ordered:
23 24
        items = items.order_by('pk')
24 25
26 +
    # Filter by locale
27 +
    locale = None
28 +
    locale_filter = None
29 +
    selected_locale = None
30 +
    if issubclass(model, TranslatableMixin):
31 +
        # 'locale' is the Locale of the object that this snippet is being chosen for
32 +
        if request.GET.get('locale'):
33 +
            locale = get_object_or_404(Locale, language_code=request.GET['locale'])
34 +
35 +
        # 'locale_filter' is the current value of the "Locale" selector in the UI
36 +
        if request.GET.get('locale_filter'):
37 +
            locale_filter = get_object_or_404(Locale, language_code=request.GET['locale_filter'])
38 +
39 +
        selected_locale = locale_filter or locale
40 +
41 +
        if selected_locale:
42 +
            items = items.filter(locale=selected_locale)
43 +
25 44
    # Search
26 45
    is_searchable = class_is_indexed(model)
27 46
    is_searching = False
@@ -54,6 +73,9 @@
Loading
54 73
            'items': paginated_items,
55 74
            'query_string': search_query,
56 75
            'is_searching': is_searching,
76 +
            'locale': locale,
77 +
            'locale_filter': locale_filter,
78 +
            'selected_locale': selected_locale,
57 79
        })
58 80
59 81
    return render_modal_workflow(
@@ -66,6 +88,10 @@
Loading
66 88
            'search_form': search_form,
67 89
            'query_string': search_query,
68 90
            'is_searching': is_searching,
91 +
            'locale': locale,
92 +
            'locale_filter': locale_filter,
93 +
            'selected_locale': selected_locale,
94 +
            'locale_options': Locale.objects.all() if issubclass(model, TranslatableMixin) else [],
69 95
        }, json_data={'step': 'choose'}
70 96
    )
71 97

@@ -9,7 +9,7 @@
Loading
9 9
from wagtail.admin.checks import check_panels_in_model
10 10
from wagtail.admin.edit_handlers import ObjectList, extract_panel_definitions_from_model_class
11 11
from wagtail.core import hooks
12 -
from wagtail.core.models import Page
12 +
from wagtail.core.models import Page, TranslatableMixin
13 13
14 14
from .helpers import (
15 15
    AdminURLHelper, ButtonHelper, DjangoORMSearchHandler, PageAdminURLHelper, PageButtonHelper,
@@ -222,7 +222,12 @@
Loading
222 222
        Returns a sequence containing the fields to be displayed as filters in
223 223
        the right sidebar in the list view.
224 224
        """
225 -
        return self.list_filter
225 +
        list_filter = self.list_filter
226 +
227 +
        if issubclass(self.model, TranslatableMixin):
228 +
            list_filter += ('locale', )
229 +
230 +
        return list_filter
226 231
227 232
    def get_ordering(self, request):
228 233
        """

@@ -252,7 +252,8 @@
Loading
252 252
    SEARCH_VAR = 'q'
253 253
    ERROR_FLAG = 'e'
254 254
    EXPORT_VAR = 'export'
255 -
    IGNORED_PARAMS = (ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, EXPORT_VAR)
255 +
    LANG_VAR = 'lang'
256 +
    IGNORED_PARAMS = (ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, EXPORT_VAR, LANG_VAR)
256 257
257 258
    # sortable_by is required by the django.contrib.admin.templatetags.admin_list.result_headers
258 259
    # template tag as of Django 2.1 - see https://docs.djangoproject.com/en/2.1/ref/contrib/admin/#django.contrib.admin.ModelAdmin.sortable_by

Learn more Showing 44 files with coverage changes found.

client/src/components/Explorer/ExplorerItem.tsx
Loading file...
client/src/utils/actions.ts
Loading file...
client/src/components/LoadingSpinner/LoadingSpinner.js
Loading file...
client/src/includes/initSubmenus.js
Loading file...
client/src/components/Draftail/blocks/ImageBlock.js
Loading file...
client/src/components/Draftail/decorators/TooltipEntity.js
Loading file...
client/src/components/Draftail/decorators/Link.js
Loading file...
client/src/components/Explorer/ExplorerHeader.tsx
Loading file...
client/src/config/wagtailConfig.js
Loading file...
client/src/components/Draftail/sources/ModalWorkflowSource.js
Loading file...
client/src/api/client.js
Loading file...
client/src/components/Draftail/DraftUtils.js
Loading file...
client/src/components/Explorer/actions.ts
Loading file...
client/src/components/Explorer/index.tsx
Loading file...
wagtail/admin/static_src/wagtailadmin/js/page-editor.js
Loading file...
client/src/includes/initSkipLink.js
Loading file...
wagtail/admin/static_src/wagtailadmin/app/wagtailadmin.entry.js
Loading file...
client/src/components/Draftail/index.js
Loading file...
client/src/utils/focus.js
Loading file...
client/src/components/Icon/Icon.js
Loading file...
client/src/includes/initIE11Warning.js
Loading file...
client/src/components/Explorer/reducers/nodes.ts
Loading file...
client/src/index.ts
Loading file...
client/src/components/Draftail/Tooltip/Tooltip.js
Loading file...
client/src/components/Explorer/PageCount.tsx
Loading file...
client/src/components/PublicationStatus/PublicationStatus.js
Loading file...
client/src/components/Explorer/Explorer.tsx
Loading file...
client/src/components/Button/Button.js
Loading file...
client/src/components/Draftail/EditorFallback/EditorFallback.js
Loading file...
client/src/components/Draftail/decorators/Document.js
Loading file...
wagtail/admin/static_src/wagtailadmin/js/vendor/urlify.js
Loading file...
client/src/components/Draftail/blocks/MediaBlock.js
Loading file...
client/src/components/Explorer/ExplorerToggle.tsx
Loading file...
client/src/components/Portal/Portal.js
Loading file...
wagtail/admin/static_src/wagtailadmin/app/draftail.entry.js
Loading file...
client/src/components/UpgradeNotification/index.js
Loading file...
client/src/components/Explorer/ExplorerPanel.tsx
Loading file...
client/src/components/Transition/Transition.js
Loading file...
client/src/utils/version.js
Loading file...
wagtail/admin/static_src/wagtailadmin/js/vendor/jquery-3.5.1.min.js
Loading file...
client/src/components/Explorer/reducers/explorer.ts
Loading file...
client/src/components/Draftail/blocks/EmbedBlock.js
Loading file...
client/src/api/admin.ts
Loading file...
Changes in wagtail/contrib/modeladmin/views.py
-1
+1
Loading file...
Files Coverage
wagtail 0.50% 90.52%
Project Totals (395 files) 90.52%
Loading