We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Make learner polling indicator conditional for only SoUD
For the new polling indicator on the dropdown menu, this should be updated to appear and poll only when the user is a learner who is logged in on a subset of users device. 

<img width="270" alt="Screen Shot 2021-08-05 at 10 58 50 AM" src="https://user-images.githubusercontent.com/17235236/128372343-da85c951-2258-4d5e-ade3-a15b753e6806.png">


--- 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/plugins/facility/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 kolibri.core.auth.constants.user_kinds import ADMIN
6 from kolibri.core.hooks import NavigationHook
7 from kolibri.core.hooks import RoleBasedRedirectHook
8 from kolibri.core.webpack.hooks import WebpackBundleHook
9 from kolibri.plugins import KolibriPluginBase
10 from kolibri.plugins.hooks import register_hook
11 
12 
13 class FacilityManagementPlugin(KolibriPluginBase):
14     translated_view_urls = "urls"
15     untranslated_view_urls = "api_urls"
16 
17 
18 @register_hook
19 class FacilityManagementAsset(WebpackBundleHook):
20     bundle_id = "app"
21 
22 
23 @register_hook
24 class FacilityRedirect(RoleBasedRedirectHook):
25     roles = (ADMIN,)
26 
27     @property
28     def url(self):
29         return self.plugin_url(FacilityManagementPlugin, "facility_management")
30 
31 
32 @register_hook
33 class FacilityManagementNavItem(NavigationHook):
34     bundle_id = "side_nav"
35
```

Path: `kolibri/plugins/coach/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 kolibri.core.auth.constants.user_kinds import COACH
6 from kolibri.core.hooks import NavigationHook
7 from kolibri.core.hooks import RoleBasedRedirectHook
8 from kolibri.core.webpack import hooks as webpack_hooks
9 from kolibri.plugins import KolibriPluginBase
10 from kolibri.plugins.hooks import register_hook
11 
12 
13 class Coach(KolibriPluginBase):
14     untranslated_view_urls = "api_urls"
15     translated_view_urls = "urls"
16 
17 
18 @register_hook
19 class CoachRedirect(RoleBasedRedirectHook):
20     roles = (COACH,)
21 
22     @property
23     def url(self):
24         return self.plugin_url(Coach, "coach")
25 
26 
27 @register_hook
28 class CoachNavItem(NavigationHook):
29     bundle_id = "side_nav"
30 
31 
32 @register_hook
33 class CoachAsset(webpack_hooks.WebpackBundleHook):
34     bundle_id = "app"
35
```

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

{"golden_diff": "diff --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@@ -22,6 +22,7 @@\n from kolibri.core.content.utils.paths import get_zip_content_config\n from kolibri.core.device.models import ContentCacheKey\n from kolibri.core.device.utils import allow_other_browsers_to_connect\n+from kolibri.core.device.utils import get_device_setting\n from kolibri.core.hooks import NavigationHook\n from kolibri.core.oidc_provider_hook import OIDCProviderHook\n from kolibri.core.theme_hook import ThemeHook\n@@ -127,6 +128,7 @@\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):\ndiff --git a/kolibri/plugins/coach/kolibri_plugin.py b/kolibri/plugins/coach/kolibri_plugin.py\n--- a/kolibri/plugins/coach/kolibri_plugin.py\n+++ b/kolibri/plugins/coach/kolibri_plugin.py\n@@ -3,6 +3,7 @@\n from __future__ import unicode_literals\n \n from kolibri.core.auth.constants.user_kinds import COACH\n+from kolibri.core.device.utils import get_device_setting\n from kolibri.core.hooks import NavigationHook\n from kolibri.core.hooks import RoleBasedRedirectHook\n from kolibri.core.webpack import hooks as webpack_hooks\n@@ -12,7 +13,14 @@\n \n class Coach(KolibriPluginBase):\n     untranslated_view_urls = \"api_urls\"\n-    translated_view_urls = \"urls\"\n+\n+    @property\n+    def translated_view_urls(self):\n+        # On an SoUD this plugin should be disabled. In lieu of properly\n+        # disabling the plugin, we will just not register any urls for now\n+        if not get_device_setting(\"subset_of_users_device\", False):\n+            return \"urls\"\n+        return None\n \n \n @register_hook\ndiff --git a/kolibri/plugins/facility/kolibri_plugin.py b/kolibri/plugins/facility/kolibri_plugin.py\n--- a/kolibri/plugins/facility/kolibri_plugin.py\n+++ b/kolibri/plugins/facility/kolibri_plugin.py\n@@ -3,6 +3,7 @@\n from __future__ import unicode_literals\n \n from kolibri.core.auth.constants.user_kinds import ADMIN\n+from kolibri.core.device.utils import get_device_setting\n from kolibri.core.hooks import NavigationHook\n from kolibri.core.hooks import RoleBasedRedirectHook\n from kolibri.core.webpack.hooks import WebpackBundleHook\n@@ -11,9 +12,16 @@\n \n \n class FacilityManagementPlugin(KolibriPluginBase):\n-    translated_view_urls = \"urls\"\n     untranslated_view_urls = \"api_urls\"\n \n+    @property\n+    def translated_view_urls(self):\n+        # On an SoUD this plugin should be disabled. In lieu of properly\n+        # disabling the plugin, we will just not register any urls for now\n+        if not get_device_setting(\"subset_of_users_device\", False):\n+            return \"urls\"\n+        return None\n+\n \n @register_hook\n class FacilityManagementAsset(WebpackBundleHook):\n", "issue": "Make learner polling indicator conditional for only SoUD\nFor the new polling indicator on the dropdown menu, this should be updated to appear and poll only when the user is a learner who is logged in on a subset of users device. \r\n\r\n<img width=\"270\" alt=\"Screen Shot 2021-08-05 at 10 58 50 AM\" src=\"https://user-images.githubusercontent.com/17235236/128372343-da85c951-2258-4d5e-ade3-a15b753e6806.png\">\r\n\n", "before_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nfrom kolibri.core.auth.constants.user_kinds import ADMIN\nfrom kolibri.core.hooks import NavigationHook\nfrom kolibri.core.hooks import RoleBasedRedirectHook\nfrom kolibri.core.webpack.hooks import WebpackBundleHook\nfrom kolibri.plugins import KolibriPluginBase\nfrom kolibri.plugins.hooks import register_hook\n\n\nclass FacilityManagementPlugin(KolibriPluginBase):\n    translated_view_urls = \"urls\"\n    untranslated_view_urls = \"api_urls\"\n\n\n@register_hook\nclass FacilityManagementAsset(WebpackBundleHook):\n    bundle_id = \"app\"\n\n\n@register_hook\nclass FacilityRedirect(RoleBasedRedirectHook):\n    roles = (ADMIN,)\n\n    @property\n    def url(self):\n        return self.plugin_url(FacilityManagementPlugin, \"facility_management\")\n\n\n@register_hook\nclass FacilityManagementNavItem(NavigationHook):\n    bundle_id = \"side_nav\"\n", "path": "kolibri/plugins/facility/kolibri_plugin.py"}, {"content": "from __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nfrom kolibri.core.auth.constants.user_kinds import COACH\nfrom kolibri.core.hooks import NavigationHook\nfrom kolibri.core.hooks import RoleBasedRedirectHook\nfrom kolibri.core.webpack import hooks as webpack_hooks\nfrom kolibri.plugins import KolibriPluginBase\nfrom kolibri.plugins.hooks import register_hook\n\n\nclass Coach(KolibriPluginBase):\n    untranslated_view_urls = \"api_urls\"\n    translated_view_urls = \"urls\"\n\n\n@register_hook\nclass CoachRedirect(RoleBasedRedirectHook):\n    roles = (COACH,)\n\n    @property\n    def url(self):\n        return self.plugin_url(Coach, \"coach\")\n\n\n@register_hook\nclass CoachNavItem(NavigationHook):\n    bundle_id = \"side_nav\"\n\n\n@register_hook\nclass CoachAsset(webpack_hooks.WebpackBundleHook):\n    bundle_id = \"app\"\n", "path": "kolibri/plugins/coach/kolibri_plugin.py"}, {"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.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        }\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"}], "after_files": [{"content": "from __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nfrom kolibri.core.auth.constants.user_kinds import ADMIN\nfrom kolibri.core.device.utils import get_device_setting\nfrom kolibri.core.hooks import NavigationHook\nfrom kolibri.core.hooks import RoleBasedRedirectHook\nfrom kolibri.core.webpack.hooks import WebpackBundleHook\nfrom kolibri.plugins import KolibriPluginBase\nfrom kolibri.plugins.hooks import register_hook\n\n\nclass FacilityManagementPlugin(KolibriPluginBase):\n    untranslated_view_urls = \"api_urls\"\n\n    @property\n    def translated_view_urls(self):\n        # On an SoUD this plugin should be disabled. In lieu of properly\n        # disabling the plugin, we will just not register any urls for now\n        if not get_device_setting(\"subset_of_users_device\", False):\n            return \"urls\"\n        return None\n\n\n@register_hook\nclass FacilityManagementAsset(WebpackBundleHook):\n    bundle_id = \"app\"\n\n\n@register_hook\nclass FacilityRedirect(RoleBasedRedirectHook):\n    roles = (ADMIN,)\n\n    @property\n    def url(self):\n        return self.plugin_url(FacilityManagementPlugin, \"facility_management\")\n\n\n@register_hook\nclass FacilityManagementNavItem(NavigationHook):\n    bundle_id = \"side_nav\"\n", "path": "kolibri/plugins/facility/kolibri_plugin.py"}, {"content": "from __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nfrom kolibri.core.auth.constants.user_kinds import COACH\nfrom kolibri.core.device.utils import get_device_setting\nfrom kolibri.core.hooks import NavigationHook\nfrom kolibri.core.hooks import RoleBasedRedirectHook\nfrom kolibri.core.webpack import hooks as webpack_hooks\nfrom kolibri.plugins import KolibriPluginBase\nfrom kolibri.plugins.hooks import register_hook\n\n\nclass Coach(KolibriPluginBase):\n    untranslated_view_urls = \"api_urls\"\n\n    @property\n    def translated_view_urls(self):\n        # On an SoUD this plugin should be disabled. In lieu of properly\n        # disabling the plugin, we will just not register any urls for now\n        if not get_device_setting(\"subset_of_users_device\", False):\n            return \"urls\"\n        return None\n\n\n@register_hook\nclass CoachRedirect(RoleBasedRedirectHook):\n    roles = (COACH,)\n\n    @property\n    def url(self):\n        return self.plugin_url(Coach, \"coach\")\n\n\n@register_hook\nclass CoachNavItem(NavigationHook):\n    bundle_id = \"side_nav\"\n\n\n@register_hook\nclass CoachAsset(webpack_hooks.WebpackBundleHook):\n    bundle_id = \"app\"\n", "path": "kolibri/plugins/coach/kolibri_plugin.py"}, {"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"}]}