We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
user warnings about plugins are shown every time when kolibri starts
<!--
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
-->

When I started kolibri from a fresh install, I got this warning:
```
/Users/lingyiwang/.venvs/kolibri0.12.6b1/lib/python2.7/site-packages/kolibri/plugins/base.py:87: UserWarning: kolibri.plugins.setup_wizard exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly
  module=self._module_path()
```
After I created the facility and got navigated to the device page, I got another warning:
```
/Users/lingyiwang/.venvs/kolibri0.12.6b1/lib/python2.7/site-packages/kolibri/plugins/base.py:87: UserWarning: kolibri.plugins.device_management exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly
  module=self._module_path()
```

### Expected behavior
<!--
Description of what behavior was expected but did not occur
-->

we should try to eliminate the warnings if possible

### User-facing consequences
<!--
Implications and real-world consequences for learners, coaches, admins, and other users of the application
-->

users/developers who check the logs might be confused by these warnings

### Errors and logs
<!--
Relevant logs from:
 * the command line
 * ~/.kolibri/logs/kolibri.txt
 * the browser console

Please wrap errors in triple backticks for clean formatting like this:
```
01:10 info: something happened
01:12 error: something bad happened
```
-->

```
INFO     ENGINE Serving on http://0.0.0.0:8080
INFO     ENGINE Bus STARTED
/Users/lingyiwang/.venvs/kolibri0.12.6b1/lib/python2.7/site-packages/kolibri/plugins/base.py:87: UserWarning: kolibri.plugins.setup_wizard exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly
  module=self._module_path()
/Users/lingyiwang/.venvs/kolibri0.12.6b1/lib/python2.7/site-packages/kolibri/plugins/base.py:87: UserWarning: kolibri.plugins.device_management exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly
  module=self._module_path()
INFO     Downloading data for channel id a9b25ac9814742c883ce1b0579448337
```

### Steps to reproduce
<!--
Precise steps that someone else can follow in order to see this behavior
-->

1. get the 0.12.6b1 pex
2. run `python kolibri-0.12.6b1.pex start`
3. open localhost:8080
4. check the log (on Ubuntu, the error appeared in daemon.txt)
5. create the facility
6. check the log again

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

Kolibri version: 0.12.6b1
Operating System: OSX, Ubuntu Xenial


--- 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/base.py`
Content:
```
1 """The base of a Kolibri plugin is the inheritence from
2 :class:`.KolibriPluginBase`.
3 """
4 from __future__ import absolute_import
5 from __future__ import print_function
6 from __future__ import unicode_literals
7 
8 import logging
9 import sys
10 import warnings
11 from importlib import import_module
12 
13 from django.conf import settings
14 from django.utils.module_loading import module_has_submodule
15 
16 from kolibri.utils.conf import config
17 
18 logger = logging.getLogger(__name__)
19 
20 
21 class MandatoryPluginMethodNotImplemented(NotImplementedError):
22     def __init__(self):
23         super(MandatoryPluginMethodNotImplemented, self).__init__(
24             "Plugin needs to define this method"
25         )  # pragma: no cover
26 
27 
28 class MandatoryPluginAttributeNotImplemented(NotImplementedError):
29     def __init__(self):
30         super(MandatoryPluginAttributeNotImplemented, self).__init__(
31             "Plugin needs to define this attribute"
32         )  # pragma: no cover
33 
34 
35 class KolibriPluginBase(object):
36     """
37     This is the base class that all Kolibri plugins need to implement.
38     """
39 
40     #: Comment
41     # Name of a local module that contains url_patterns that define
42     # URLs for views that do not contain any
43     # translated content, and hence will not be prefixed
44     # with a language prefix
45     untranslated_view_urls = None
46 
47     #: Comment
48     # Name of a local module that contains url_patterns that define
49     # URLs for views that contain
50     # translated content, and hence will be prefixed
51     # with a language prefixs
52     translated_view_urls = None
53 
54     #: Comment
55     # Name of a local module that contains url_patterns that define
56     # URLs for views that should be attached to the domain root.
57     # Use with caution! The lack of namespacing is dangerous.
58     root_view_urls = None
59 
60     #: Comment
61     # Name of a local module that contains additional settings to augment
62     # Django settings.
63     # For settings that take a tuple or list, these will be appended to the value from
64     # the base settings module set through conventional Django means.
65     django_settings = None
66 
67     #: Comment
68     # Name of a local module, containing a config spec as the 'option_spec' value.
69     # These options should not override the core config spec, but may specify a new
70     # default value for a core config spec option.
71     kolibri_options = None
72 
73     # : Suggested property, not yet in use
74     migrate_on_enable = False
75 
76     # : Suggested property, not yet in use
77     collect_static_on_enable = False
78 
79     # : Suggested property, not yet in use
80     collect_static_on_enable = False
81 
82     def __init__(self):
83         if settings.configured:
84             # Check to see if a plugin is being initialized after Django
85             warnings.warn(
86                 "{module} exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly".format(
87                     module=self._module_path()
88                 )
89             )
90 
91     @classmethod
92     def _module_path(cls):
93         """
94         Returns the path of the class inheriting this classmethod.
95         There is no such thing as Class properties, that's why it's implemented
96         as such.
97 
98         Used in KolibriPluginBase._installed_apps_add
99         """
100         return ".".join(cls.__module__.split(".")[:-1])
101 
102     @classmethod
103     def _installed_apps_add(cls):
104         """Call this from your enable() method to have the plugin automatically
105         added to Kolibri configuration"""
106         module_path = cls._module_path()
107         if module_path not in config.ACTIVE_PLUGINS:
108             config.enable_plugin(module_path)
109         else:
110             logger.warning("{} already enabled".format(module_path))
111 
112     @classmethod
113     def _installed_apps_remove(cls):
114         """Call this from your enable() method to have the plugin automatically
115         added to Kolibri configuration"""
116         module_path = cls._module_path()
117         if module_path in config.ACTIVE_PLUGINS:
118             config.disable_plugin(module_path)
119         else:
120             logger.warning("{} already disabled".format(module_path))
121 
122     @classmethod
123     def enable(cls):
124         """Modify the kolibri config dict to your plugin's needs"""
125         cls._installed_apps_add()
126 
127     @classmethod
128     def disable(cls):
129         """Modify the kolibri config dict to your plugin's needs"""
130         cls._installed_apps_remove()
131 
132     def _return_module(self, module_name):
133         if module_has_submodule(sys.modules[self._module_path()], module_name):
134             models_module_name = "%s.%s" % (self._module_path(), module_name)
135             return import_module(models_module_name)
136 
137         return None
138 
139     def url_module(self):
140         """
141         Return a url module, containing ``urlpatterns = [...]``, a conventional
142         Django application url module.
143 
144         URLs are by default accessed through Django's reverse lookups like
145         this::
146 
147             reverse('kolibri:mypluginclass:url_name')
148 
149         To customize "mypluginclass" (which is automatically derived from the
150         plugin's class name), override ``url_namespace``.
151 
152         By default this will be discovered based on the translated_view_urls
153         property.
154         """
155         if self.translated_view_urls:
156             module = self._return_module(self.translated_view_urls)
157             if module is None:
158                 logging.warn(
159                     "{plugin} defined {urls} translated view urls but the module was not found".format(
160                         plugin=self._module_path(), urls=self.translated_view_urls
161                     )
162                 )
163             return module
164 
165     def api_url_module(self):
166         """
167         Return a url module, containing ``urlpatterns = [...]``, a conventional
168         Django application url module.
169 
170         Do this separately for API endpoints so that they do not need
171         to be prefixed by the language code.
172 
173         URLs are by default accessed through Django's reverse lookups like
174         this::
175 
176             reverse('kolibri:mypluginclass:url_name')
177 
178         To customize "mypluginclass" (which is automatically derived from the
179         plugin's class name), override ``url_namespace``.
180 
181         By default this will be discovered based on the untranslated_view_urls
182         property.
183         """
184         if self.untranslated_view_urls:
185             module = self._return_module(self.untranslated_view_urls)
186             if module is None:
187                 logging.warn(
188                     "{plugin} defined {urls} untranslated view urls but the module was not found".format(
189                         plugin=self._module_path(), urls=self.untranslated_view_urls
190                     )
191                 )
192             return module
193 
194     def root_url_module(self):
195         """
196         Return a url module, containing ``urlpatterns = [...]``, a conventional
197         Django application url module.
198 
199         Do this separately for endpoints that need to be attached at the root.
200 
201         URLs are by default accessed through Django's reverse lookups like
202         this::
203 
204             reverse('kolibri:url_name')
205 
206         By default this will be discovered based on the root_view_urls
207         property.
208         """
209         if self.root_view_urls:
210             module = self._return_module(self.root_view_urls)
211             logger.warning(
212                 "Setting up root URLs which is not recommended!\n plugin module: {}".format(
213                     self
214                 )
215             )
216             if module is None:
217                 logging.warn(
218                     "{plugin} defined {urls} root view urls but the module was not found".format(
219                         plugin=self._module_path(), urls=self.root_view_urls
220                     )
221                 )
222             return module
223 
224     def settings_module(self):
225         """
226         Return a settings module, containing Django settings that this
227         module wants to apply.
228 
229         For settings that take a tuple or list, these will be appended to the value from
230         the base settings module set through conventional Django means.
231 
232         By default this will be discovered based on the django_settings
233         property.
234         """
235         if self.django_settings:
236             module = self._return_module(self.django_settings)
237             if module is None:
238                 logging.warn(
239                     "{plugin} defined {module} django settings but the module was not found".format(
240                         plugin=self._module_path(), module=self.django_settings
241                     )
242                 )
243             return module
244 
245     def options_module(self):
246         """
247         Return an options module, containing a config spec as the 'option_spec' value.
248 
249         These options should not override the core config spec, but may specify only a new
250         default value for a core config spec option.
251 
252         By default this will be discovered based on the kolibri_options
253         property.
254         """
255         if self.kolibri_options:
256             module = self._return_module(self.kolibri_options)
257             if module is None:
258                 logging.warn(
259                     "{plugin} defined {module} kolibri options but the module was not found".format(
260                         plugin=self._module_path(), module=self.kolibri_options
261                     )
262                 )
263             return module
264 
265     def url_namespace(self):
266         """
267         Used for the ``namespace`` argument when including the plugin's
268         urlpatterns. By default, returns a lowercase of the class name.
269         """
270         return self.__class__.__name__.lower()
271 
272     def url_slug(self):
273         """
274         Where should urls be included? By default, this is a lower-case version
275         of the class name.
276 
277         Example::
278 
279             return r"my-plugin/"
280 
281         .. warning:: Avoid the empty string, as you might get conflicts.
282         """
283         return self.__class__.__name__.lower() + "/"
284
```
--- 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/plugins/base.py b/kolibri/plugins/base.py
--- a/kolibri/plugins/base.py
+++ b/kolibri/plugins/base.py
@@ -7,10 +7,8 @@
 
 import logging
 import sys
-import warnings
 from importlib import import_module
 
-from django.conf import settings
 from django.utils.module_loading import module_has_submodule
 
 from kolibri.utils.conf import config
@@ -79,15 +77,6 @@
     # : Suggested property, not yet in use
     collect_static_on_enable = False
 
-    def __init__(self):
-        if settings.configured:
-            # Check to see if a plugin is being initialized after Django
-            warnings.warn(
-                "{module} exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly".format(
-                    module=self._module_path()
-                )
-            )
-
     @classmethod
     def _module_path(cls):
         """

{"golden_diff": "diff --git a/kolibri/plugins/base.py b/kolibri/plugins/base.py\n--- a/kolibri/plugins/base.py\n+++ b/kolibri/plugins/base.py\n@@ -7,10 +7,8 @@\n \n import logging\n import sys\n-import warnings\n from importlib import import_module\n \n-from django.conf import settings\n from django.utils.module_loading import module_has_submodule\n \n from kolibri.utils.conf import config\n@@ -79,15 +77,6 @@\n     # : Suggested property, not yet in use\n     collect_static_on_enable = False\n \n-    def __init__(self):\n-        if settings.configured:\n-            # Check to see if a plugin is being initialized after Django\n-            warnings.warn(\n-                \"{module} exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly\".format(\n-                    module=self._module_path()\n-                )\n-            )\n-\n     @classmethod\n     def _module_path(cls):\n         \"\"\"\n", "issue": "user warnings about plugins are shown every time when kolibri starts\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\nWhen I started kolibri from a fresh install, I got this warning:\r\n```\r\n/Users/lingyiwang/.venvs/kolibri0.12.6b1/lib/python2.7/site-packages/kolibri/plugins/base.py:87: UserWarning: kolibri.plugins.setup_wizard exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly\r\n  module=self._module_path()\r\n```\r\nAfter I created the facility and got navigated to the device page, I got another warning:\r\n```\r\n/Users/lingyiwang/.venvs/kolibri0.12.6b1/lib/python2.7/site-packages/kolibri/plugins/base.py:87: UserWarning: kolibri.plugins.device_management exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly\r\n  module=self._module_path()\r\n```\r\n\r\n### Expected behavior\r\n<!--\r\nDescription of what behavior was expected but did not occur\r\n-->\r\n\r\nwe should try to eliminate the warnings if possible\r\n\r\n### User-facing consequences\r\n<!--\r\nImplications and real-world consequences for learners, coaches, admins, and other users of the application\r\n-->\r\n\r\nusers/developers who check the logs might be confused by these warnings\r\n\r\n### Errors and logs\r\n<!--\r\nRelevant logs from:\r\n * the command line\r\n * ~/.kolibri/logs/kolibri.txt\r\n * the browser console\r\n\r\nPlease wrap errors in triple backticks for clean formatting like this:\r\n```\r\n01:10 info: something happened\r\n01:12 error: something bad happened\r\n```\r\n-->\r\n\r\n```\r\nINFO     ENGINE Serving on http://0.0.0.0:8080\r\nINFO     ENGINE Bus STARTED\r\n/Users/lingyiwang/.venvs/kolibri0.12.6b1/lib/python2.7/site-packages/kolibri/plugins/base.py:87: UserWarning: kolibri.plugins.setup_wizard exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly\r\n  module=self._module_path()\r\n/Users/lingyiwang/.venvs/kolibri0.12.6b1/lib/python2.7/site-packages/kolibri/plugins/base.py:87: UserWarning: kolibri.plugins.device_management exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly\r\n  module=self._module_path()\r\nINFO     Downloading data for channel id a9b25ac9814742c883ce1b0579448337\r\n```\r\n\r\n### Steps to reproduce\r\n<!--\r\nPrecise steps that someone else can follow in order to see this behavior\r\n-->\r\n\r\n1. get the 0.12.6b1 pex\r\n2. run `python kolibri-0.12.6b1.pex start`\r\n3. open localhost:8080\r\n4. check the log (on Ubuntu, the error appeared in daemon.txt)\r\n5. create the facility\r\n6. check the log again\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 version: 0.12.6b1\r\nOperating System: OSX, Ubuntu Xenial\r\n\n", "before_files": [{"content": "\"\"\"The base of a Kolibri plugin is the inheritence from\n:class:`.KolibriPluginBase`.\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport logging\nimport sys\nimport warnings\nfrom importlib import import_module\n\nfrom django.conf import settings\nfrom django.utils.module_loading import module_has_submodule\n\nfrom kolibri.utils.conf import config\n\nlogger = logging.getLogger(__name__)\n\n\nclass MandatoryPluginMethodNotImplemented(NotImplementedError):\n    def __init__(self):\n        super(MandatoryPluginMethodNotImplemented, self).__init__(\n            \"Plugin needs to define this method\"\n        )  # pragma: no cover\n\n\nclass MandatoryPluginAttributeNotImplemented(NotImplementedError):\n    def __init__(self):\n        super(MandatoryPluginAttributeNotImplemented, self).__init__(\n            \"Plugin needs to define this attribute\"\n        )  # pragma: no cover\n\n\nclass KolibriPluginBase(object):\n    \"\"\"\n    This is the base class that all Kolibri plugins need to implement.\n    \"\"\"\n\n    #: Comment\n    # Name of a local module that contains url_patterns that define\n    # URLs for views that do not contain any\n    # translated content, and hence will not be prefixed\n    # with a language prefix\n    untranslated_view_urls = None\n\n    #: Comment\n    # Name of a local module that contains url_patterns that define\n    # URLs for views that contain\n    # translated content, and hence will be prefixed\n    # with a language prefixs\n    translated_view_urls = None\n\n    #: Comment\n    # Name of a local module that contains url_patterns that define\n    # URLs for views that should be attached to the domain root.\n    # Use with caution! The lack of namespacing is dangerous.\n    root_view_urls = None\n\n    #: Comment\n    # Name of a local module that contains additional settings to augment\n    # Django settings.\n    # For settings that take a tuple or list, these will be appended to the value from\n    # the base settings module set through conventional Django means.\n    django_settings = None\n\n    #: Comment\n    # Name of a local module, containing a config spec as the 'option_spec' value.\n    # These options should not override the core config spec, but may specify a new\n    # default value for a core config spec option.\n    kolibri_options = None\n\n    # : Suggested property, not yet in use\n    migrate_on_enable = False\n\n    # : Suggested property, not yet in use\n    collect_static_on_enable = False\n\n    # : Suggested property, not yet in use\n    collect_static_on_enable = False\n\n    def __init__(self):\n        if settings.configured:\n            # Check to see if a plugin is being initialized after Django\n            warnings.warn(\n                \"{module} exposes a KolibriPluginBase derived object but is initialized after Django - enable as a plugin to use this properly\".format(\n                    module=self._module_path()\n                )\n            )\n\n    @classmethod\n    def _module_path(cls):\n        \"\"\"\n        Returns the path of the class inheriting this classmethod.\n        There is no such thing as Class properties, that's why it's implemented\n        as such.\n\n        Used in KolibriPluginBase._installed_apps_add\n        \"\"\"\n        return \".\".join(cls.__module__.split(\".\")[:-1])\n\n    @classmethod\n    def _installed_apps_add(cls):\n        \"\"\"Call this from your enable() method to have the plugin automatically\n        added to Kolibri configuration\"\"\"\n        module_path = cls._module_path()\n        if module_path not in config.ACTIVE_PLUGINS:\n            config.enable_plugin(module_path)\n        else:\n            logger.warning(\"{} already enabled\".format(module_path))\n\n    @classmethod\n    def _installed_apps_remove(cls):\n        \"\"\"Call this from your enable() method to have the plugin automatically\n        added to Kolibri configuration\"\"\"\n        module_path = cls._module_path()\n        if module_path in config.ACTIVE_PLUGINS:\n            config.disable_plugin(module_path)\n        else:\n            logger.warning(\"{} already disabled\".format(module_path))\n\n    @classmethod\n    def enable(cls):\n        \"\"\"Modify the kolibri config dict to your plugin's needs\"\"\"\n        cls._installed_apps_add()\n\n    @classmethod\n    def disable(cls):\n        \"\"\"Modify the kolibri config dict to your plugin's needs\"\"\"\n        cls._installed_apps_remove()\n\n    def _return_module(self, module_name):\n        if module_has_submodule(sys.modules[self._module_path()], module_name):\n            models_module_name = \"%s.%s\" % (self._module_path(), module_name)\n            return import_module(models_module_name)\n\n        return None\n\n    def url_module(self):\n        \"\"\"\n        Return a url module, containing ``urlpatterns = [...]``, a conventional\n        Django application url module.\n\n        URLs are by default accessed through Django's reverse lookups like\n        this::\n\n            reverse('kolibri:mypluginclass:url_name')\n\n        To customize \"mypluginclass\" (which is automatically derived from the\n        plugin's class name), override ``url_namespace``.\n\n        By default this will be discovered based on the translated_view_urls\n        property.\n        \"\"\"\n        if self.translated_view_urls:\n            module = self._return_module(self.translated_view_urls)\n            if module is None:\n                logging.warn(\n                    \"{plugin} defined {urls} translated view urls but the module was not found\".format(\n                        plugin=self._module_path(), urls=self.translated_view_urls\n                    )\n                )\n            return module\n\n    def api_url_module(self):\n        \"\"\"\n        Return a url module, containing ``urlpatterns = [...]``, a conventional\n        Django application url module.\n\n        Do this separately for API endpoints so that they do not need\n        to be prefixed by the language code.\n\n        URLs are by default accessed through Django's reverse lookups like\n        this::\n\n            reverse('kolibri:mypluginclass:url_name')\n\n        To customize \"mypluginclass\" (which is automatically derived from the\n        plugin's class name), override ``url_namespace``.\n\n        By default this will be discovered based on the untranslated_view_urls\n        property.\n        \"\"\"\n        if self.untranslated_view_urls:\n            module = self._return_module(self.untranslated_view_urls)\n            if module is None:\n                logging.warn(\n                    \"{plugin} defined {urls} untranslated view urls but the module was not found\".format(\n                        plugin=self._module_path(), urls=self.untranslated_view_urls\n                    )\n                )\n            return module\n\n    def root_url_module(self):\n        \"\"\"\n        Return a url module, containing ``urlpatterns = [...]``, a conventional\n        Django application url module.\n\n        Do this separately for endpoints that need to be attached at the root.\n\n        URLs are by default accessed through Django's reverse lookups like\n        this::\n\n            reverse('kolibri:url_name')\n\n        By default this will be discovered based on the root_view_urls\n        property.\n        \"\"\"\n        if self.root_view_urls:\n            module = self._return_module(self.root_view_urls)\n            logger.warning(\n                \"Setting up root URLs which is not recommended!\\n plugin module: {}\".format(\n                    self\n                )\n            )\n            if module is None:\n                logging.warn(\n                    \"{plugin} defined {urls} root view urls but the module was not found\".format(\n                        plugin=self._module_path(), urls=self.root_view_urls\n                    )\n                )\n            return module\n\n    def settings_module(self):\n        \"\"\"\n        Return a settings module, containing Django settings that this\n        module wants to apply.\n\n        For settings that take a tuple or list, these will be appended to the value from\n        the base settings module set through conventional Django means.\n\n        By default this will be discovered based on the django_settings\n        property.\n        \"\"\"\n        if self.django_settings:\n            module = self._return_module(self.django_settings)\n            if module is None:\n                logging.warn(\n                    \"{plugin} defined {module} django settings but the module was not found\".format(\n                        plugin=self._module_path(), module=self.django_settings\n                    )\n                )\n            return module\n\n    def options_module(self):\n        \"\"\"\n        Return an options module, containing a config spec as the 'option_spec' value.\n\n        These options should not override the core config spec, but may specify only a new\n        default value for a core config spec option.\n\n        By default this will be discovered based on the kolibri_options\n        property.\n        \"\"\"\n        if self.kolibri_options:\n            module = self._return_module(self.kolibri_options)\n            if module is None:\n                logging.warn(\n                    \"{plugin} defined {module} kolibri options but the module was not found\".format(\n                        plugin=self._module_path(), module=self.kolibri_options\n                    )\n                )\n            return module\n\n    def url_namespace(self):\n        \"\"\"\n        Used for the ``namespace`` argument when including the plugin's\n        urlpatterns. By default, returns a lowercase of the class name.\n        \"\"\"\n        return self.__class__.__name__.lower()\n\n    def url_slug(self):\n        \"\"\"\n        Where should urls be included? By default, this is a lower-case version\n        of the class name.\n\n        Example::\n\n            return r\"my-plugin/\"\n\n        .. warning:: Avoid the empty string, as you might get conflicts.\n        \"\"\"\n        return self.__class__.__name__.lower() + \"/\"\n", "path": "kolibri/plugins/base.py"}], "after_files": [{"content": "\"\"\"The base of a Kolibri plugin is the inheritence from\n:class:`.KolibriPluginBase`.\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport logging\nimport sys\nfrom importlib import import_module\n\nfrom django.utils.module_loading import module_has_submodule\n\nfrom kolibri.utils.conf import config\n\nlogger = logging.getLogger(__name__)\n\n\nclass MandatoryPluginMethodNotImplemented(NotImplementedError):\n    def __init__(self):\n        super(MandatoryPluginMethodNotImplemented, self).__init__(\n            \"Plugin needs to define this method\"\n        )  # pragma: no cover\n\n\nclass MandatoryPluginAttributeNotImplemented(NotImplementedError):\n    def __init__(self):\n        super(MandatoryPluginAttributeNotImplemented, self).__init__(\n            \"Plugin needs to define this attribute\"\n        )  # pragma: no cover\n\n\nclass KolibriPluginBase(object):\n    \"\"\"\n    This is the base class that all Kolibri plugins need to implement.\n    \"\"\"\n\n    #: Comment\n    # Name of a local module that contains url_patterns that define\n    # URLs for views that do not contain any\n    # translated content, and hence will not be prefixed\n    # with a language prefix\n    untranslated_view_urls = None\n\n    #: Comment\n    # Name of a local module that contains url_patterns that define\n    # URLs for views that contain\n    # translated content, and hence will be prefixed\n    # with a language prefixs\n    translated_view_urls = None\n\n    #: Comment\n    # Name of a local module that contains url_patterns that define\n    # URLs for views that should be attached to the domain root.\n    # Use with caution! The lack of namespacing is dangerous.\n    root_view_urls = None\n\n    #: Comment\n    # Name of a local module that contains additional settings to augment\n    # Django settings.\n    # For settings that take a tuple or list, these will be appended to the value from\n    # the base settings module set through conventional Django means.\n    django_settings = None\n\n    #: Comment\n    # Name of a local module, containing a config spec as the 'option_spec' value.\n    # These options should not override the core config spec, but may specify a new\n    # default value for a core config spec option.\n    kolibri_options = None\n\n    # : Suggested property, not yet in use\n    migrate_on_enable = False\n\n    # : Suggested property, not yet in use\n    collect_static_on_enable = False\n\n    # : Suggested property, not yet in use\n    collect_static_on_enable = False\n\n    @classmethod\n    def _module_path(cls):\n        \"\"\"\n        Returns the path of the class inheriting this classmethod.\n        There is no such thing as Class properties, that's why it's implemented\n        as such.\n\n        Used in KolibriPluginBase._installed_apps_add\n        \"\"\"\n        return \".\".join(cls.__module__.split(\".\")[:-1])\n\n    @classmethod\n    def _installed_apps_add(cls):\n        \"\"\"Call this from your enable() method to have the plugin automatically\n        added to Kolibri configuration\"\"\"\n        module_path = cls._module_path()\n        if module_path not in config.ACTIVE_PLUGINS:\n            config.enable_plugin(module_path)\n        else:\n            logger.warning(\"{} already enabled\".format(module_path))\n\n    @classmethod\n    def _installed_apps_remove(cls):\n        \"\"\"Call this from your enable() method to have the plugin automatically\n        added to Kolibri configuration\"\"\"\n        module_path = cls._module_path()\n        if module_path in config.ACTIVE_PLUGINS:\n            config.disable_plugin(module_path)\n        else:\n            logger.warning(\"{} already disabled\".format(module_path))\n\n    @classmethod\n    def enable(cls):\n        \"\"\"Modify the kolibri config dict to your plugin's needs\"\"\"\n        cls._installed_apps_add()\n\n    @classmethod\n    def disable(cls):\n        \"\"\"Modify the kolibri config dict to your plugin's needs\"\"\"\n        cls._installed_apps_remove()\n\n    def _return_module(self, module_name):\n        if module_has_submodule(sys.modules[self._module_path()], module_name):\n            models_module_name = \"%s.%s\" % (self._module_path(), module_name)\n            return import_module(models_module_name)\n\n        return None\n\n    def url_module(self):\n        \"\"\"\n        Return a url module, containing ``urlpatterns = [...]``, a conventional\n        Django application url module.\n\n        URLs are by default accessed through Django's reverse lookups like\n        this::\n\n            reverse('kolibri:mypluginclass:url_name')\n\n        To customize \"mypluginclass\" (which is automatically derived from the\n        plugin's class name), override ``url_namespace``.\n\n        By default this will be discovered based on the translated_view_urls\n        property.\n        \"\"\"\n        if self.translated_view_urls:\n            module = self._return_module(self.translated_view_urls)\n            if module is None:\n                logging.warn(\n                    \"{plugin} defined {urls} translated view urls but the module was not found\".format(\n                        plugin=self._module_path(), urls=self.translated_view_urls\n                    )\n                )\n            return module\n\n    def api_url_module(self):\n        \"\"\"\n        Return a url module, containing ``urlpatterns = [...]``, a conventional\n        Django application url module.\n\n        Do this separately for API endpoints so that they do not need\n        to be prefixed by the language code.\n\n        URLs are by default accessed through Django's reverse lookups like\n        this::\n\n            reverse('kolibri:mypluginclass:url_name')\n\n        To customize \"mypluginclass\" (which is automatically derived from the\n        plugin's class name), override ``url_namespace``.\n\n        By default this will be discovered based on the untranslated_view_urls\n        property.\n        \"\"\"\n        if self.untranslated_view_urls:\n            module = self._return_module(self.untranslated_view_urls)\n            if module is None:\n                logging.warn(\n                    \"{plugin} defined {urls} untranslated view urls but the module was not found\".format(\n                        plugin=self._module_path(), urls=self.untranslated_view_urls\n                    )\n                )\n            return module\n\n    def root_url_module(self):\n        \"\"\"\n        Return a url module, containing ``urlpatterns = [...]``, a conventional\n        Django application url module.\n\n        Do this separately for endpoints that need to be attached at the root.\n\n        URLs are by default accessed through Django's reverse lookups like\n        this::\n\n            reverse('kolibri:url_name')\n\n        By default this will be discovered based on the root_view_urls\n        property.\n        \"\"\"\n        if self.root_view_urls:\n            module = self._return_module(self.root_view_urls)\n            logger.warning(\n                \"Setting up root URLs which is not recommended!\\n plugin module: {}\".format(\n                    self\n                )\n            )\n            if module is None:\n                logging.warn(\n                    \"{plugin} defined {urls} root view urls but the module was not found\".format(\n                        plugin=self._module_path(), urls=self.root_view_urls\n                    )\n                )\n            return module\n\n    def settings_module(self):\n        \"\"\"\n        Return a settings module, containing Django settings that this\n        module wants to apply.\n\n        For settings that take a tuple or list, these will be appended to the value from\n        the base settings module set through conventional Django means.\n\n        By default this will be discovered based on the django_settings\n        property.\n        \"\"\"\n        if self.django_settings:\n            module = self._return_module(self.django_settings)\n            if module is None:\n                logging.warn(\n                    \"{plugin} defined {module} django settings but the module was not found\".format(\n                        plugin=self._module_path(), module=self.django_settings\n                    )\n                )\n            return module\n\n    def options_module(self):\n        \"\"\"\n        Return an options module, containing a config spec as the 'option_spec' value.\n\n        These options should not override the core config spec, but may specify only a new\n        default value for a core config spec option.\n\n        By default this will be discovered based on the kolibri_options\n        property.\n        \"\"\"\n        if self.kolibri_options:\n            module = self._return_module(self.kolibri_options)\n            if module is None:\n                logging.warn(\n                    \"{plugin} defined {module} kolibri options but the module was not found\".format(\n                        plugin=self._module_path(), module=self.kolibri_options\n                    )\n                )\n            return module\n\n    def url_namespace(self):\n        \"\"\"\n        Used for the ``namespace`` argument when including the plugin's\n        urlpatterns. By default, returns a lowercase of the class name.\n        \"\"\"\n        return self.__class__.__name__.lower()\n\n    def url_slug(self):\n        \"\"\"\n        Where should urls be included? By default, this is a lower-case version\n        of the class name.\n\n        Example::\n\n            return r\"my-plugin/\"\n\n        .. warning:: Avoid the empty string, as you might get conflicts.\n        \"\"\"\n        return self.__class__.__name__.lower() + \"/\"\n", "path": "kolibri/plugins/base.py"}]}