def __call__()

in bedrock/cms/middleware.py [0:0]


    def __call__(self, request):
        response = self.get_response(request)

        if response.status_code == HTTPStatus.NOT_FOUND:
            if self._has_null_byte(request) is True:
                # Don't bother processing URLs with null-byte content - they
                # are fake/vuln scan requests
                return response

            # At this point we have a request that has resulted in a 404,
            # which means it didn't match any Django URLs, and didn't match
            # a CMS page for the current locale+path combination in the URL.

            # Let's see if there is an alternative version available in a
            # different locale that the user would actually like to see.
            # And failing that, if we have it in the default locale, we can
            # fall back to that (which is consistent with what we do with
            # Fluent-based hard-coded pages).

            _path = request.path.lstrip("/")
            lang_prefix, _, sub_path = _path.partition("/")
            # (There will be a language-code prefix, thanks to earlier i18n middleware)

            # Is the requested path available in other languages, checked in
            # order of user preference?
            accept_lang_header = request.headers.get("Accept-Language")

            # We only want the language codes from parse_accept_lang_header,
            # not their weighting, and we want them to be formatted the way
            # we expect them to be

            if accept_lang_header:
                ranked_locales = [normalize_language(x[0]) for x in parse_accept_lang_header(accept_lang_header)]
            else:
                ranked_locales = []

            # Ensure the default locale is also included, as a last-ditch option.
            # NOTE: remove if controversial in terms of user intent but then
            # we'll have to make sure we pass a locale code into the call to
            # url() in templates, so that cms_only_urls.py returns a useful
            # language code

            if settings.LANGUAGE_CODE not in ranked_locales:
                ranked_locales.append(settings.LANGUAGE_CODE)

            _url_path = sub_path.lstrip("/")
            if not _url_path.endswith("/"):
                _url_path += "/"

            # Now try to get hold of all the pages that exist in the CMS for the extracted path
            # that are also in a locale that is acceptable to the user or maybe the fallback locale.

            # We do this by seeking full url_paths that are prefixed with /home/ (for the
            # default locale) or home-<locale_code> - Wagtail sort of 'denorms' the
            # language code into the root of the page tree for each separate locale - eg:
            # * /home/test-path/to/a/page for en-US
            # * /home-fr/test-path/to/a/page for French

            possible_url_path_patterns = []
            for locale_code in ranked_locales:
                if locale_code == settings.LANGUAGE_CODE:
                    root = "/home"
                else:
                    root = f"/home-{locale_code}"

                full_url_path = f"{root}/{_url_path}"
                possible_url_path_patterns.append(full_url_path)

            cms_pages_with_viable_locales = Page.objects.live().filter(
                url_path__in=possible_url_path_patterns,
                # There's no extra value in filtering with locale__language_code__in=ranked_locales
                # due to the locale code being embedded in the url_path strings
            )

            if cms_pages_with_viable_locales:
                # OK, we have some candidate pages with that desired path and at least one
                # viable locale. Let's try to send the user to their most preferred one.

                # Evaluate the queryset just once, then explore the results in memory
                lookup = defaultdict(list)
                for page in cms_pages_with_viable_locales:
                    lookup[page.locale.language_code].append(page)

                for locale_code in ranked_locales:
                    if locale_code in lookup:
                        page_list = lookup[locale_code]
                        # There _should_ only be one matching for this locale, but let's not assume
                        if len(page_list) > 1:
                            logger.warning(f"CMS 404-fallback problem - multiple pages with same path found: {page_list}")
                        page = page_list[0]  # page_list should be a list of 1 item
                        return HttpResponseRedirect(page.url)

                # Note: we can make this more efficient by leveraging the cached page tree
                # (once the work to pre-cache the page tree lands)

        return response