We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Brand new Setup Wizard infinitely redirects
<!--
Instructions:
 * Fill out the sections below, replace …'s with information about your issue
 * Use the 'preview' function above this text box to verify formatting before submitting
-->

## Observed behavior
<!--
Description of the behavior that was observed, including screenshots or other references when applicable
-->

On 0.15 branch, with a fresh KOLIBRI_HOME folder, going to the server causes infinite redirects and does not allow the user to proceed with the Setup Wizard (it doesn't load at all - tries to request the page like 10 times and then gives up).

## Context
<!--
Tell us about your environment, including:
 * Kolibri version
 * Operating system
 * Browser
-->

Kolibri: release-v0.15.x branch
OS: Fedora
Browser: Chrome


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `kolibri/core/kolibri_plugin.py`
Content:
```
1 from __future__ import absolute_import
2 from __future__ import print_function
3 from __future__ import unicode_literals
4 
5 from django.conf import settings
6 from django.contrib.staticfiles.templatetags.staticfiles import static
7 from django.core.urlresolvers import get_resolver
8 from django.core.urlresolvers import reverse
9 from django.template.loader import render_to_string
10 from django.utils.html import mark_safe
11 from django.utils.translation import get_language
12 from django.utils.translation import get_language_bidi
13 from django.utils.translation import get_language_info
14 from django_js_reverse.core import _safe_json
15 from django_js_reverse.core import generate_json
16 from django_js_reverse.rjsmin import jsmin
17 
18 import kolibri
19 from kolibri.core.content.utils.paths import get_content_storage_url
20 from kolibri.core.content.utils.paths import get_hashi_path
21 from kolibri.core.content.utils.paths import get_zip_content_base_path
22 from kolibri.core.content.utils.paths import get_zip_content_config
23 from kolibri.core.device.models import ContentCacheKey
24 from kolibri.core.device.utils import allow_other_browsers_to_connect
25 from kolibri.core.device.utils import get_device_setting
26 from kolibri.core.hooks import NavigationHook
27 from kolibri.core.oidc_provider_hook import OIDCProviderHook
28 from kolibri.core.theme_hook import ThemeHook
29 from kolibri.core.webpack.hooks import WebpackBundleHook
30 from kolibri.plugins.app.utils import interface
31 from kolibri.plugins.hooks import register_hook
32 from kolibri.utils import i18n
33 from kolibri.utils.conf import OPTIONS
34 
35 
36 @register_hook
37 class FrontEndCoreAppAssetHook(WebpackBundleHook):
38     bundle_id = "default_frontend"
39 
40     def url_tag(self):
41         # Modified from:
42         # https://github.com/ierror/django-js-reverse/blob/master/django_js_reverse/core.py#L101
43         js_name = "window.kolibriPluginDataGlobal['{bundle}'].urls".format(
44             bundle=self.unique_id
45         )
46         default_urlresolver = get_resolver(None)
47 
48         data = generate_json(default_urlresolver)
49 
50         # Generate the JS that exposes functions to reverse all Django URLs
51         # in the frontend.
52         js = render_to_string(
53             "django_js_reverse/urls_js.tpl",
54             {"data": _safe_json(data), "js_name": "__placeholder__"},
55             # For some reason the js_name gets escaped going into the template
56             # so this was the easiest way to inject it.
57         ).replace("__placeholder__", js_name)
58         zip_content_origin, zip_content_port = get_zip_content_config()
59         return [
60             mark_safe(
61                 """<script type="text/javascript">"""
62                 # Minify the generated Javascript
63                 + jsmin(js)
64                 # Add URL references for our base static URL, the Django media URL
65                 # and our content storage URL - this allows us to calculate
66                 # the path at which to access a local file on the frontend if needed.
67                 + """
68             {js_name}.__staticUrl = '{static_url}';
69             {js_name}.__mediaUrl = '{media_url}';
70             {js_name}.__contentUrl = '{content_url}';
71             {js_name}.__zipContentUrl = '{zip_content_url}';
72             {js_name}.__hashiUrl = '{hashi_url}';
73             {js_name}.__zipContentOrigin = '{zip_content_origin}';
74             {js_name}.__zipContentPort = {zip_content_port};
75             </script>
76             """.format(
77                     js_name=js_name,
78                     static_url=settings.STATIC_URL,
79                     media_url=settings.MEDIA_URL,
80                     content_url=get_content_storage_url(
81                         baseurl=OPTIONS["Deployment"]["URL_PATH_PREFIX"]
82                     ),
83                     zip_content_url=get_zip_content_base_path(),
84                     hashi_url=get_hashi_path(),
85                     zip_content_origin=zip_content_origin,
86                     zip_content_port=zip_content_port,
87                 )
88             )
89         ]
90 
91     def navigation_tags(self):
92         return [
93             hook.render_to_page_load_sync_html()
94             for hook in NavigationHook.registered_hooks
95         ]
96 
97     def render_to_page_load_sync_html(self):
98         """
99         Don't render the frontend message files in the usual way
100         as the global object to register them does not exist yet.
101         Instead they are loaded through plugin data.
102         """
103         tags = (
104             self.plugin_data_tag()
105             + self.url_tag()
106             + list(self.js_and_css_tags())
107             + self.navigation_tags()
108         )
109 
110         return mark_safe("\n".join(tags))
111 
112     @property
113     def plugin_data(self):
114         language_code = get_language()
115         static_root = static("assets/fonts/noto-full")
116         full_file = "{}.{}.{}.css?v={}"
117         return {
118             "fullCSSFileModern": full_file.format(
119                 static_root, language_code, "modern", kolibri.__version__
120             ),
121             "fullCSSFileBasic": full_file.format(
122                 static_root, language_code, "basic", kolibri.__version__
123             ),
124             "allowRemoteAccess": allow_other_browsers_to_connect()
125             or not interface.enabled,
126             "appCapabilities": interface.capabilities,
127             "contentCacheKey": ContentCacheKey.get_cache_key(),
128             "languageGlobals": self.language_globals(),
129             "oidcProviderEnabled": OIDCProviderHook.is_enabled(),
130             "kolibriTheme": ThemeHook.get_theme(),
131             "isSubsetOfUsersDevice": get_device_setting("subset_of_users_device"),
132         }
133 
134     def language_globals(self):
135         language_code = get_language()
136         lang_dir = "rtl" if get_language_bidi() else "ltr"
137 
138         languages = {}
139         for code, language_name in settings.LANGUAGES:
140             lang_info = i18n.KOLIBRI_LANGUAGE_INFO[code]
141             languages[code] = {
142                 # Format to match the schema of the content Language model
143                 "id": code,
144                 "lang_name": language_name,
145                 "english_name": lang_info["english_name"]
146                 if lang_info
147                 else get_language_info(code)["name"],
148                 "lang_direction": get_language_info(code)["bidi"],
149             }
150         return {
151             "coreLanguageMessages": self.frontend_messages(),
152             "languageCode": language_code,
153             "languageDir": lang_dir,
154             "languages": languages,
155         }
156 
157 
158 @register_hook
159 class FrontendHeadAssetsHook(WebpackBundleHook):
160     """
161     Render these assets in the <head> tag of base.html, before other JS and assets.
162     """
163 
164     bundle_id = "frontend_head_assets"
165     inline = True
166 
167     def render_to_page_load_sync_html(self):
168         """
169         Add in the extra language font file tags needed
170         for preloading our custom font files.
171         """
172         tags = (
173             self.plugin_data_tag()
174             + self.language_font_file_tags()
175             + self.frontend_message_tag()
176             + list(self.js_and_css_tags())
177         )
178 
179         return mark_safe("\n".join(tags))
180 
181     def language_font_file_tags(self):
182         language_code = get_language()
183         common_file = static("assets/fonts/noto-common.css")
184         subset_file = static("assets/fonts/noto-subset.{}.css".format(language_code))
185         return [
186             '<link type="text/css" href="{common_css_file}?v={version}" rel="stylesheet"/>'.format(
187                 common_css_file=common_file, version=kolibri.__version__
188             ),
189             '<link type="text/css" href="{subset_css_file}?v={version}" rel="stylesheet"/>'.format(
190                 subset_css_file=subset_file, version=kolibri.__version__
191             ),
192         ]
193 
194     @property
195     def plugin_data(self):
196         return {"unsupportedUrl": reverse("kolibri:core:unsupported")}
197
```

Path: `kolibri/core/device/middleware.py`
Content:
```
1 from django.conf import settings
2 from django.http import HttpResponseRedirect
3 from django.shortcuts import redirect
4 from django.urls import is_valid_path
5 from django.utils import translation
6 
7 from .translation import get_language_from_request_and_is_from_path
8 from kolibri.core.device.hooks import SetupHook
9 from kolibri.core.device.utils import DeviceNotProvisioned
10 from kolibri.utils.conf import OPTIONS
11 
12 
13 class KolibriLocaleMiddleware(object):
14     """
15     Copied and then modified into a new style middleware from:
16     https://github.com/django/django/blob/stable/1.11.x/django/middleware/locale.py
17     Also has several other changes to suit our purposes.
18     The principal concern of this middleware is to activate translation for the current
19     language, so that throughout the lifecycle of this request, any translation or language
20     related functionality is set to the appropriate locale.
21     Unlike the Django middleware, this middleware only runs on requests to URLs that are
22     prefixed by a language code. Other URLs, such as for untranslated API endpoints do not
23     have a language code set on them.
24     """
25 
26     def __init__(self, get_response):
27         # Standard boilerplate for a new style Django middleware.
28         self.get_response = get_response
29 
30     def __call__(self, request):
31         # First get the language code, and whether this was calculated from the path
32         # i.e. was this a language-prefixed URL.
33         language, language_from_path = get_language_from_request_and_is_from_path(
34             request
35         )
36         # If this URL has been resolved to a view, and the view is not on a language prefixed
37         # URL, then the function above will return None for the language code to indicate that
38         # no translation is necessary.
39         if language is not None:
40             # Only activate translation if there is a language code returned.
41             translation.activate(language)
42             request.LANGUAGE_CODE = translation.get_language()
43 
44         response = self.get_response(request)
45 
46         if language is not None:
47 
48             language = translation.get_language()
49 
50             if response.status_code == 404 and not language_from_path:
51                 # Maybe the language code is missing in the URL? Try adding the
52                 # language prefix and redirecting to that URL.
53                 # First get any global prefix that is being used.
54                 script_prefix = OPTIONS["Deployment"]["URL_PATH_PREFIX"]
55                 # Replace the global prefix with the global prefix and the language prefix.
56                 language_path = request.path_info.replace(
57                     script_prefix, "%s%s/" % (script_prefix, language), 1
58                 )
59 
60                 # Get the urlconf from the request, default to the global settings ROOT_URLCONF
61                 urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
62                 # Check if this is a valid path
63                 path_valid = is_valid_path(language_path, urlconf)
64                 # Check if the path is only invalid because it is missing a trailing slash
65                 path_needs_slash = not path_valid and (
66                     settings.APPEND_SLASH
67                     and not language_path.endswith("/")
68                     and is_valid_path("%s/" % language_path, urlconf)
69                 )
70                 # If the constructed path is valid, or it would be valid with a trailing slash
71                 # then redirect to the prefixed path, with a trailing slash added if needed.
72                 if path_valid or path_needs_slash:
73                     # Insert language after the script prefix and before the
74                     # rest of the URL
75                     language_url = request.get_full_path(
76                         force_append_slash=path_needs_slash
77                     ).replace(script_prefix, "%s%s/" % (script_prefix, language), 1)
78                     return HttpResponseRedirect(language_url)
79 
80             # Add a content language header to the response if not already present.
81             if "Content-Language" not in response:
82                 response["Content-Language"] = language
83 
84         return response
85 
86 
87 class ProvisioningErrorHandler(object):
88     def __init__(self, get_response):
89         self.get_response = get_response
90 
91     def process_exception(self, request, exception):
92         if isinstance(exception, DeviceNotProvisioned) and SetupHook.provision_url():
93             return redirect(SetupHook.provision_url())
94         return None
95 
96     def __call__(self, request):
97         return self.get_response(request)
98
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/kolibri/core/device/middleware.py b/kolibri/core/device/middleware.py
--- a/kolibri/core/device/middleware.py
+++ b/kolibri/core/device/middleware.py
@@ -89,7 +89,11 @@
         self.get_response = get_response
 
     def process_exception(self, request, exception):
-        if isinstance(exception, DeviceNotProvisioned) and SetupHook.provision_url():
+        if (
+            isinstance(exception, DeviceNotProvisioned)
+            and SetupHook.provision_url()
+            and not request.path.startswith(SetupHook.provision_url())
+        ):
             return redirect(SetupHook.provision_url())
         return None
 
diff --git a/kolibri/core/kolibri_plugin.py b/kolibri/core/kolibri_plugin.py
--- a/kolibri/core/kolibri_plugin.py
+++ b/kolibri/core/kolibri_plugin.py
@@ -128,7 +128,9 @@
             "languageGlobals": self.language_globals(),
             "oidcProviderEnabled": OIDCProviderHook.is_enabled(),
             "kolibriTheme": ThemeHook.get_theme(),
-            "isSubsetOfUsersDevice": get_device_setting("subset_of_users_device"),
+            "isSubsetOfUsersDevice": get_device_setting(
+                "subset_of_users_device", False
+            ),
         }
 
     def language_globals(self):

{"golden_diff": "diff --git a/kolibri/core/device/middleware.py b/kolibri/core/device/middleware.py\n--- a/kolibri/core/device/middleware.py\n+++ b/kolibri/core/device/middleware.py\n@@ -89,7 +89,11 @@\n         self.get_response = get_response\n \n     def process_exception(self, request, exception):\n-        if isinstance(exception, DeviceNotProvisioned) and SetupHook.provision_url():\n+        if (\n+            isinstance(exception, DeviceNotProvisioned)\n+            and SetupHook.provision_url()\n+            and not request.path.startswith(SetupHook.provision_url())\n+        ):\n             return redirect(SetupHook.provision_url())\n         return None\n \ndiff --git a/kolibri/core/kolibri_plugin.py b/kolibri/core/kolibri_plugin.py\n--- a/kolibri/core/kolibri_plugin.py\n+++ b/kolibri/core/kolibri_plugin.py\n@@ -128,7 +128,9 @@\n             \"languageGlobals\": self.language_globals(),\n             \"oidcProviderEnabled\": OIDCProviderHook.is_enabled(),\n             \"kolibriTheme\": ThemeHook.get_theme(),\n-            \"isSubsetOfUsersDevice\": get_device_setting(\"subset_of_users_device\"),\n+            \"isSubsetOfUsersDevice\": get_device_setting(\n+                \"subset_of_users_device\", False\n+            ),\n         }\n \n     def language_globals(self):\n", "issue": "Brand new Setup Wizard infinitely redirects\n<!--\r\nInstructions:\r\n * Fill out the sections below, replace \u2026's with information about your issue\r\n * Use the 'preview' function above this text box to verify formatting before submitting\r\n-->\r\n\r\n## Observed behavior\r\n<!--\r\nDescription of the behavior that was observed, including screenshots or other references when applicable\r\n-->\r\n\r\nOn 0.15 branch, with a fresh KOLIBRI_HOME folder, going to the server causes infinite redirects and does not allow the user to proceed with the Setup Wizard (it doesn't load at all - tries to request the page like 10 times and then gives up).\r\n\r\n## Context\r\n<!--\r\nTell us about your environment, including:\r\n * Kolibri version\r\n * Operating system\r\n * Browser\r\n-->\r\n\r\nKolibri: release-v0.15.x branch\r\nOS: Fedora\r\nBrowser: Chrome\r\n\n", "before_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nfrom django.conf import settings\nfrom django.contrib.staticfiles.templatetags.staticfiles import static\nfrom django.core.urlresolvers import get_resolver\nfrom django.core.urlresolvers import reverse\nfrom django.template.loader import render_to_string\nfrom django.utils.html import mark_safe\nfrom django.utils.translation import get_language\nfrom django.utils.translation import get_language_bidi\nfrom django.utils.translation import get_language_info\nfrom django_js_reverse.core import _safe_json\nfrom django_js_reverse.core import generate_json\nfrom django_js_reverse.rjsmin import jsmin\n\nimport kolibri\nfrom kolibri.core.content.utils.paths import get_content_storage_url\nfrom kolibri.core.content.utils.paths import get_hashi_path\nfrom kolibri.core.content.utils.paths import get_zip_content_base_path\nfrom kolibri.core.content.utils.paths import get_zip_content_config\nfrom kolibri.core.device.models import ContentCacheKey\nfrom kolibri.core.device.utils import allow_other_browsers_to_connect\nfrom kolibri.core.device.utils import get_device_setting\nfrom kolibri.core.hooks import NavigationHook\nfrom kolibri.core.oidc_provider_hook import OIDCProviderHook\nfrom kolibri.core.theme_hook import ThemeHook\nfrom kolibri.core.webpack.hooks import WebpackBundleHook\nfrom kolibri.plugins.app.utils import interface\nfrom kolibri.plugins.hooks import register_hook\nfrom kolibri.utils import i18n\nfrom kolibri.utils.conf import OPTIONS\n\n\n@register_hook\nclass FrontEndCoreAppAssetHook(WebpackBundleHook):\n    bundle_id = \"default_frontend\"\n\n    def url_tag(self):\n        # Modified from:\n        # https://github.com/ierror/django-js-reverse/blob/master/django_js_reverse/core.py#L101\n        js_name = \"window.kolibriPluginDataGlobal['{bundle}'].urls\".format(\n            bundle=self.unique_id\n        )\n        default_urlresolver = get_resolver(None)\n\n        data = generate_json(default_urlresolver)\n\n        # Generate the JS that exposes functions to reverse all Django URLs\n        # in the frontend.\n        js = render_to_string(\n            \"django_js_reverse/urls_js.tpl\",\n            {\"data\": _safe_json(data), \"js_name\": \"__placeholder__\"},\n            # For some reason the js_name gets escaped going into the template\n            # so this was the easiest way to inject it.\n        ).replace(\"__placeholder__\", js_name)\n        zip_content_origin, zip_content_port = get_zip_content_config()\n        return [\n            mark_safe(\n                \"\"\"<script type=\"text/javascript\">\"\"\"\n                # Minify the generated Javascript\n                + jsmin(js)\n                # Add URL references for our base static URL, the Django media URL\n                # and our content storage URL - this allows us to calculate\n                # the path at which to access a local file on the frontend if needed.\n                + \"\"\"\n            {js_name}.__staticUrl = '{static_url}';\n            {js_name}.__mediaUrl = '{media_url}';\n            {js_name}.__contentUrl = '{content_url}';\n            {js_name}.__zipContentUrl = '{zip_content_url}';\n            {js_name}.__hashiUrl = '{hashi_url}';\n            {js_name}.__zipContentOrigin = '{zip_content_origin}';\n            {js_name}.__zipContentPort = {zip_content_port};\n            </script>\n            \"\"\".format(\n                    js_name=js_name,\n                    static_url=settings.STATIC_URL,\n                    media_url=settings.MEDIA_URL,\n                    content_url=get_content_storage_url(\n                        baseurl=OPTIONS[\"Deployment\"][\"URL_PATH_PREFIX\"]\n                    ),\n                    zip_content_url=get_zip_content_base_path(),\n                    hashi_url=get_hashi_path(),\n                    zip_content_origin=zip_content_origin,\n                    zip_content_port=zip_content_port,\n                )\n            )\n        ]\n\n    def navigation_tags(self):\n        return [\n            hook.render_to_page_load_sync_html()\n            for hook in NavigationHook.registered_hooks\n        ]\n\n    def render_to_page_load_sync_html(self):\n        \"\"\"\n        Don't render the frontend message files in the usual way\n        as the global object to register them does not exist yet.\n        Instead they are loaded through plugin data.\n        \"\"\"\n        tags = (\n            self.plugin_data_tag()\n            + self.url_tag()\n            + list(self.js_and_css_tags())\n            + self.navigation_tags()\n        )\n\n        return mark_safe(\"\\n\".join(tags))\n\n    @property\n    def plugin_data(self):\n        language_code = get_language()\n        static_root = static(\"assets/fonts/noto-full\")\n        full_file = \"{}.{}.{}.css?v={}\"\n        return {\n            \"fullCSSFileModern\": full_file.format(\n                static_root, language_code, \"modern\", kolibri.__version__\n            ),\n            \"fullCSSFileBasic\": full_file.format(\n                static_root, language_code, \"basic\", kolibri.__version__\n            ),\n            \"allowRemoteAccess\": allow_other_browsers_to_connect()\n            or not interface.enabled,\n            \"appCapabilities\": interface.capabilities,\n            \"contentCacheKey\": ContentCacheKey.get_cache_key(),\n            \"languageGlobals\": self.language_globals(),\n            \"oidcProviderEnabled\": OIDCProviderHook.is_enabled(),\n            \"kolibriTheme\": ThemeHook.get_theme(),\n            \"isSubsetOfUsersDevice\": get_device_setting(\"subset_of_users_device\"),\n        }\n\n    def language_globals(self):\n        language_code = get_language()\n        lang_dir = \"rtl\" if get_language_bidi() else \"ltr\"\n\n        languages = {}\n        for code, language_name in settings.LANGUAGES:\n            lang_info = i18n.KOLIBRI_LANGUAGE_INFO[code]\n            languages[code] = {\n                # Format to match the schema of the content Language model\n                \"id\": code,\n                \"lang_name\": language_name,\n                \"english_name\": lang_info[\"english_name\"]\n                if lang_info\n                else get_language_info(code)[\"name\"],\n                \"lang_direction\": get_language_info(code)[\"bidi\"],\n            }\n        return {\n            \"coreLanguageMessages\": self.frontend_messages(),\n            \"languageCode\": language_code,\n            \"languageDir\": lang_dir,\n            \"languages\": languages,\n        }\n\n\n@register_hook\nclass FrontendHeadAssetsHook(WebpackBundleHook):\n    \"\"\"\n    Render these assets in the <head> tag of base.html, before other JS and assets.\n    \"\"\"\n\n    bundle_id = \"frontend_head_assets\"\n    inline = True\n\n    def render_to_page_load_sync_html(self):\n        \"\"\"\n        Add in the extra language font file tags needed\n        for preloading our custom font files.\n        \"\"\"\n        tags = (\n            self.plugin_data_tag()\n            + self.language_font_file_tags()\n            + self.frontend_message_tag()\n            + list(self.js_and_css_tags())\n        )\n\n        return mark_safe(\"\\n\".join(tags))\n\n    def language_font_file_tags(self):\n        language_code = get_language()\n        common_file = static(\"assets/fonts/noto-common.css\")\n        subset_file = static(\"assets/fonts/noto-subset.{}.css\".format(language_code))\n        return [\n            '<link type=\"text/css\" href=\"{common_css_file}?v={version}\" rel=\"stylesheet\"/>'.format(\n                common_css_file=common_file, version=kolibri.__version__\n            ),\n            '<link type=\"text/css\" href=\"{subset_css_file}?v={version}\" rel=\"stylesheet\"/>'.format(\n                subset_css_file=subset_file, version=kolibri.__version__\n            ),\n        ]\n\n    @property\n    def plugin_data(self):\n        return {\"unsupportedUrl\": reverse(\"kolibri:core:unsupported\")}\n", "path": "kolibri/core/kolibri_plugin.py"}, {"content": "from django.conf import settings\nfrom django.http import HttpResponseRedirect\nfrom django.shortcuts import redirect\nfrom django.urls import is_valid_path\nfrom django.utils import translation\n\nfrom .translation import get_language_from_request_and_is_from_path\nfrom kolibri.core.device.hooks import SetupHook\nfrom kolibri.core.device.utils import DeviceNotProvisioned\nfrom kolibri.utils.conf import OPTIONS\n\n\nclass KolibriLocaleMiddleware(object):\n    \"\"\"\n    Copied and then modified into a new style middleware from:\n    https://github.com/django/django/blob/stable/1.11.x/django/middleware/locale.py\n    Also has several other changes to suit our purposes.\n    The principal concern of this middleware is to activate translation for the current\n    language, so that throughout the lifecycle of this request, any translation or language\n    related functionality is set to the appropriate locale.\n    Unlike the Django middleware, this middleware only runs on requests to URLs that are\n    prefixed by a language code. Other URLs, such as for untranslated API endpoints do not\n    have a language code set on them.\n    \"\"\"\n\n    def __init__(self, get_response):\n        # Standard boilerplate for a new style Django middleware.\n        self.get_response = get_response\n\n    def __call__(self, request):\n        # First get the language code, and whether this was calculated from the path\n        # i.e. was this a language-prefixed URL.\n        language, language_from_path = get_language_from_request_and_is_from_path(\n            request\n        )\n        # If this URL has been resolved to a view, and the view is not on a language prefixed\n        # URL, then the function above will return None for the language code to indicate that\n        # no translation is necessary.\n        if language is not None:\n            # Only activate translation if there is a language code returned.\n            translation.activate(language)\n            request.LANGUAGE_CODE = translation.get_language()\n\n        response = self.get_response(request)\n\n        if language is not None:\n\n            language = translation.get_language()\n\n            if response.status_code == 404 and not language_from_path:\n                # Maybe the language code is missing in the URL? Try adding the\n                # language prefix and redirecting to that URL.\n                # First get any global prefix that is being used.\n                script_prefix = OPTIONS[\"Deployment\"][\"URL_PATH_PREFIX\"]\n                # Replace the global prefix with the global prefix and the language prefix.\n                language_path = request.path_info.replace(\n                    script_prefix, \"%s%s/\" % (script_prefix, language), 1\n                )\n\n                # Get the urlconf from the request, default to the global settings ROOT_URLCONF\n                urlconf = getattr(request, \"urlconf\", settings.ROOT_URLCONF)\n                # Check if this is a valid path\n                path_valid = is_valid_path(language_path, urlconf)\n                # Check if the path is only invalid because it is missing a trailing slash\n                path_needs_slash = not path_valid and (\n                    settings.APPEND_SLASH\n                    and not language_path.endswith(\"/\")\n                    and is_valid_path(\"%s/\" % language_path, urlconf)\n                )\n                # If the constructed path is valid, or it would be valid with a trailing slash\n                # then redirect to the prefixed path, with a trailing slash added if needed.\n                if path_valid or path_needs_slash:\n                    # Insert language after the script prefix and before the\n                    # rest of the URL\n                    language_url = request.get_full_path(\n                        force_append_slash=path_needs_slash\n                    ).replace(script_prefix, \"%s%s/\" % (script_prefix, language), 1)\n                    return HttpResponseRedirect(language_url)\n\n            # Add a content language header to the response if not already present.\n            if \"Content-Language\" not in response:\n                response[\"Content-Language\"] = language\n\n        return response\n\n\nclass ProvisioningErrorHandler(object):\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def process_exception(self, request, exception):\n        if isinstance(exception, DeviceNotProvisioned) and SetupHook.provision_url():\n            return redirect(SetupHook.provision_url())\n        return None\n\n    def __call__(self, request):\n        return self.get_response(request)\n", "path": "kolibri/core/device/middleware.py"}], "after_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nfrom django.conf import settings\nfrom django.contrib.staticfiles.templatetags.staticfiles import static\nfrom django.core.urlresolvers import get_resolver\nfrom django.core.urlresolvers import reverse\nfrom django.template.loader import render_to_string\nfrom django.utils.html import mark_safe\nfrom django.utils.translation import get_language\nfrom django.utils.translation import get_language_bidi\nfrom django.utils.translation import get_language_info\nfrom django_js_reverse.core import _safe_json\nfrom django_js_reverse.core import generate_json\nfrom django_js_reverse.rjsmin import jsmin\n\nimport kolibri\nfrom kolibri.core.content.utils.paths import get_content_storage_url\nfrom kolibri.core.content.utils.paths import get_hashi_path\nfrom kolibri.core.content.utils.paths import get_zip_content_base_path\nfrom kolibri.core.content.utils.paths import get_zip_content_config\nfrom kolibri.core.device.models import ContentCacheKey\nfrom kolibri.core.device.utils import allow_other_browsers_to_connect\nfrom kolibri.core.device.utils import get_device_setting\nfrom kolibri.core.hooks import NavigationHook\nfrom kolibri.core.oidc_provider_hook import OIDCProviderHook\nfrom kolibri.core.theme_hook import ThemeHook\nfrom kolibri.core.webpack.hooks import WebpackBundleHook\nfrom kolibri.plugins.app.utils import interface\nfrom kolibri.plugins.hooks import register_hook\nfrom kolibri.utils import i18n\nfrom kolibri.utils.conf import OPTIONS\n\n\n@register_hook\nclass FrontEndCoreAppAssetHook(WebpackBundleHook):\n    bundle_id = \"default_frontend\"\n\n    def url_tag(self):\n        # Modified from:\n        # https://github.com/ierror/django-js-reverse/blob/master/django_js_reverse/core.py#L101\n        js_name = \"window.kolibriPluginDataGlobal['{bundle}'].urls\".format(\n            bundle=self.unique_id\n        )\n        default_urlresolver = get_resolver(None)\n\n        data = generate_json(default_urlresolver)\n\n        # Generate the JS that exposes functions to reverse all Django URLs\n        # in the frontend.\n        js = render_to_string(\n            \"django_js_reverse/urls_js.tpl\",\n            {\"data\": _safe_json(data), \"js_name\": \"__placeholder__\"},\n            # For some reason the js_name gets escaped going into the template\n            # so this was the easiest way to inject it.\n        ).replace(\"__placeholder__\", js_name)\n        zip_content_origin, zip_content_port = get_zip_content_config()\n        return [\n            mark_safe(\n                \"\"\"<script type=\"text/javascript\">\"\"\"\n                # Minify the generated Javascript\n                + jsmin(js)\n                # Add URL references for our base static URL, the Django media URL\n                # and our content storage URL - this allows us to calculate\n                # the path at which to access a local file on the frontend if needed.\n                + \"\"\"\n            {js_name}.__staticUrl = '{static_url}';\n            {js_name}.__mediaUrl = '{media_url}';\n            {js_name}.__contentUrl = '{content_url}';\n            {js_name}.__zipContentUrl = '{zip_content_url}';\n            {js_name}.__hashiUrl = '{hashi_url}';\n            {js_name}.__zipContentOrigin = '{zip_content_origin}';\n            {js_name}.__zipContentPort = {zip_content_port};\n            </script>\n            \"\"\".format(\n                    js_name=js_name,\n                    static_url=settings.STATIC_URL,\n                    media_url=settings.MEDIA_URL,\n                    content_url=get_content_storage_url(\n                        baseurl=OPTIONS[\"Deployment\"][\"URL_PATH_PREFIX\"]\n                    ),\n                    zip_content_url=get_zip_content_base_path(),\n                    hashi_url=get_hashi_path(),\n                    zip_content_origin=zip_content_origin,\n                    zip_content_port=zip_content_port,\n                )\n            )\n        ]\n\n    def navigation_tags(self):\n        return [\n            hook.render_to_page_load_sync_html()\n            for hook in NavigationHook.registered_hooks\n        ]\n\n    def render_to_page_load_sync_html(self):\n        \"\"\"\n        Don't render the frontend message files in the usual way\n        as the global object to register them does not exist yet.\n        Instead they are loaded through plugin data.\n        \"\"\"\n        tags = (\n            self.plugin_data_tag()\n            + self.url_tag()\n            + list(self.js_and_css_tags())\n            + self.navigation_tags()\n        )\n\n        return mark_safe(\"\\n\".join(tags))\n\n    @property\n    def plugin_data(self):\n        language_code = get_language()\n        static_root = static(\"assets/fonts/noto-full\")\n        full_file = \"{}.{}.{}.css?v={}\"\n        return {\n            \"fullCSSFileModern\": full_file.format(\n                static_root, language_code, \"modern\", kolibri.__version__\n            ),\n            \"fullCSSFileBasic\": full_file.format(\n                static_root, language_code, \"basic\", kolibri.__version__\n            ),\n            \"allowRemoteAccess\": allow_other_browsers_to_connect()\n            or not interface.enabled,\n            \"appCapabilities\": interface.capabilities,\n            \"contentCacheKey\": ContentCacheKey.get_cache_key(),\n            \"languageGlobals\": self.language_globals(),\n            \"oidcProviderEnabled\": OIDCProviderHook.is_enabled(),\n            \"kolibriTheme\": ThemeHook.get_theme(),\n            \"isSubsetOfUsersDevice\": get_device_setting(\n                \"subset_of_users_device\", False\n            ),\n        }\n\n    def language_globals(self):\n        language_code = get_language()\n        lang_dir = \"rtl\" if get_language_bidi() else \"ltr\"\n\n        languages = {}\n        for code, language_name in settings.LANGUAGES:\n            lang_info = i18n.KOLIBRI_LANGUAGE_INFO[code]\n            languages[code] = {\n                # Format to match the schema of the content Language model\n                \"id\": code,\n                \"lang_name\": language_name,\n                \"english_name\": lang_info[\"english_name\"]\n                if lang_info\n                else get_language_info(code)[\"name\"],\n                \"lang_direction\": get_language_info(code)[\"bidi\"],\n            }\n        return {\n            \"coreLanguageMessages\": self.frontend_messages(),\n            \"languageCode\": language_code,\n            \"languageDir\": lang_dir,\n            \"languages\": languages,\n        }\n\n\n@register_hook\nclass FrontendHeadAssetsHook(WebpackBundleHook):\n    \"\"\"\n    Render these assets in the <head> tag of base.html, before other JS and assets.\n    \"\"\"\n\n    bundle_id = \"frontend_head_assets\"\n    inline = True\n\n    def render_to_page_load_sync_html(self):\n        \"\"\"\n        Add in the extra language font file tags needed\n        for preloading our custom font files.\n        \"\"\"\n        tags = (\n            self.plugin_data_tag()\n            + self.language_font_file_tags()\n            + self.frontend_message_tag()\n            + list(self.js_and_css_tags())\n        )\n\n        return mark_safe(\"\\n\".join(tags))\n\n    def language_font_file_tags(self):\n        language_code = get_language()\n        common_file = static(\"assets/fonts/noto-common.css\")\n        subset_file = static(\"assets/fonts/noto-subset.{}.css\".format(language_code))\n        return [\n            '<link type=\"text/css\" href=\"{common_css_file}?v={version}\" rel=\"stylesheet\"/>'.format(\n                common_css_file=common_file, version=kolibri.__version__\n            ),\n            '<link type=\"text/css\" href=\"{subset_css_file}?v={version}\" rel=\"stylesheet\"/>'.format(\n                subset_css_file=subset_file, version=kolibri.__version__\n            ),\n        ]\n\n    @property\n    def plugin_data(self):\n        return {\"unsupportedUrl\": reverse(\"kolibri:core:unsupported\")}\n", "path": "kolibri/core/kolibri_plugin.py"}, {"content": "from django.conf import settings\nfrom django.http import HttpResponseRedirect\nfrom django.shortcuts import redirect\nfrom django.urls import is_valid_path\nfrom django.utils import translation\n\nfrom .translation import get_language_from_request_and_is_from_path\nfrom kolibri.core.device.hooks import SetupHook\nfrom kolibri.core.device.utils import DeviceNotProvisioned\nfrom kolibri.utils.conf import OPTIONS\n\n\nclass KolibriLocaleMiddleware(object):\n    \"\"\"\n    Copied and then modified into a new style middleware from:\n    https://github.com/django/django/blob/stable/1.11.x/django/middleware/locale.py\n    Also has several other changes to suit our purposes.\n    The principal concern of this middleware is to activate translation for the current\n    language, so that throughout the lifecycle of this request, any translation or language\n    related functionality is set to the appropriate locale.\n    Unlike the Django middleware, this middleware only runs on requests to URLs that are\n    prefixed by a language code. Other URLs, such as for untranslated API endpoints do not\n    have a language code set on them.\n    \"\"\"\n\n    def __init__(self, get_response):\n        # Standard boilerplate for a new style Django middleware.\n        self.get_response = get_response\n\n    def __call__(self, request):\n        # First get the language code, and whether this was calculated from the path\n        # i.e. was this a language-prefixed URL.\n        language, language_from_path = get_language_from_request_and_is_from_path(\n            request\n        )\n        # If this URL has been resolved to a view, and the view is not on a language prefixed\n        # URL, then the function above will return None for the language code to indicate that\n        # no translation is necessary.\n        if language is not None:\n            # Only activate translation if there is a language code returned.\n            translation.activate(language)\n            request.LANGUAGE_CODE = translation.get_language()\n\n        response = self.get_response(request)\n\n        if language is not None:\n\n            language = translation.get_language()\n\n            if response.status_code == 404 and not language_from_path:\n                # Maybe the language code is missing in the URL? Try adding the\n                # language prefix and redirecting to that URL.\n                # First get any global prefix that is being used.\n                script_prefix = OPTIONS[\"Deployment\"][\"URL_PATH_PREFIX\"]\n                # Replace the global prefix with the global prefix and the language prefix.\n                language_path = request.path_info.replace(\n                    script_prefix, \"%s%s/\" % (script_prefix, language), 1\n                )\n\n                # Get the urlconf from the request, default to the global settings ROOT_URLCONF\n                urlconf = getattr(request, \"urlconf\", settings.ROOT_URLCONF)\n                # Check if this is a valid path\n                path_valid = is_valid_path(language_path, urlconf)\n                # Check if the path is only invalid because it is missing a trailing slash\n                path_needs_slash = not path_valid and (\n                    settings.APPEND_SLASH\n                    and not language_path.endswith(\"/\")\n                    and is_valid_path(\"%s/\" % language_path, urlconf)\n                )\n                # If the constructed path is valid, or it would be valid with a trailing slash\n                # then redirect to the prefixed path, with a trailing slash added if needed.\n                if path_valid or path_needs_slash:\n                    # Insert language after the script prefix and before the\n                    # rest of the URL\n                    language_url = request.get_full_path(\n                        force_append_slash=path_needs_slash\n                    ).replace(script_prefix, \"%s%s/\" % (script_prefix, language), 1)\n                    return HttpResponseRedirect(language_url)\n\n            # Add a content language header to the response if not already present.\n            if \"Content-Language\" not in response:\n                response[\"Content-Language\"] = language\n\n        return response\n\n\nclass ProvisioningErrorHandler(object):\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def process_exception(self, request, exception):\n        if (\n            isinstance(exception, DeviceNotProvisioned)\n            and SetupHook.provision_url()\n            and not request.path.startswith(SetupHook.provision_url())\n        ):\n            return redirect(SetupHook.provision_url())\n        return None\n\n    def __call__(self, request):\n        return self.get_response(request)\n", "path": "kolibri/core/device/middleware.py"}]}