From 762ec228da49feaa205ea70f4462816a0b9496ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 16 Jan 2015 17:11:31 +0100 Subject: [PATCH] More work on the documentation --- docs/api/index.rst | 6 +- docs/events/index.rst | 6 +- docs/images/template-plugin-type-navbar.png | Bin 0 -> 2923 bytes docs/images/template-plugin-type-settings.png | Bin 0 -> 12458 bytes docs/images/template-plugin-type-sidebar.png | Bin 0 -> 12241 bytes docs/images/template-plugin-type-tab.png | Bin 0 -> 12255 bytes docs/images/template_plugin_types.ep | 309 ++++++++++++++++++ docs/plugins/developing.rst | 54 +-- docs/plugins/index.rst | 54 +-- docs/plugins/using.rst | 44 +++ src/octoprint/plugin/types.py | 270 ++++++++------- 11 files changed, 526 insertions(+), 217 deletions(-) create mode 100644 docs/images/template-plugin-type-navbar.png create mode 100644 docs/images/template-plugin-type-settings.png create mode 100644 docs/images/template-plugin-type-sidebar.png create mode 100644 docs/images/template-plugin-type-tab.png create mode 100644 docs/images/template_plugin_types.ep create mode 100644 docs/plugins/using.rst diff --git a/docs/api/index.rst b/docs/api/index.rst index c4142e9f..4f1d643f 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -1,8 +1,8 @@ .. _sec-api: -################# -API Documentation -################# +### +API +### .. toctree:: :maxdepth: 2 diff --git a/docs/events/index.rst b/docs/events/index.rst index 55200cab..58e3dd71 100644 --- a/docs/events/index.rst +++ b/docs/events/index.rst @@ -1,8 +1,8 @@ .. _sec-events: -#################### -Events Documentation -#################### +###### +Events +###### .. contents:: diff --git a/docs/images/template-plugin-type-navbar.png b/docs/images/template-plugin-type-navbar.png new file mode 100644 index 0000000000000000000000000000000000000000..f00be86354d65525aee6ab5642d62c15db3ee0b6 GIT binary patch literal 2923 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 a)w+_jmXQ4l+W`{|a{JxGsT_XjuYUoGb-XPA literal 0 HcmV?d00001 diff --git a/docs/images/template-plugin-type-settings.png b/docs/images/template-plugin-type-settings.png new file mode 100644 index 0000000000000000000000000000000000000000..ae9c849ea7470ec2ba11622e6c765e7afde224d0 GIT binary patch literal 12458 zcmeHuXIPV2wC)#VkWo>v0iu8d>e#3PQUfYY6jTsYN>l_CAxM{!GRmlfAPPc6gfJEm zX$nFpl3=4(Q4o?4lx`rQCZwMo6vsLDJoo;%=RVK9&y`>Bef!&ct+m%$?^^HP{B_9Q zT4u?*B@hJ3*xKwn0zr#l5Cpp-Ed`#a>BYN2kSb)m&%){Ay>Tk$wwqV_@FcTgr0HVH z4#w`N6es$rt)aK&^m8)R8>C;K_2s|pOUQ#;4QHm#S{v@gpHc{If(5H>{0Q51%TGPa zLe4h6E@oHbvY@A)nX8@dw{SfJJI6!!$sK<=@>}kXL?2N#xg1rAJW35PJxb%52+--% zhKt|1Q;+62MwsEhd_l^|Sxaw>kzIKRy4bf|*~F%<`W6LV)7f=Zc2C{AYg!UeVUx;x z^*m`ctn8j^7BORp`@o^sy)&h zrNDQ6$(+k~W7eFKl-(n({eM6NlQ?X@vwH3NN*%)pPQ$=#Npd}>Q7KJ-(fOS=>o?l%3U|n}v2QigXtLY% z>vpxd+IzrmD`T$)d!DS#x4ChgsMDRk_V0d|fUxtl3x7tDD+_~N!x`bYaU*k^+uaqx zq^RMj!f^3BWAyCMm_nI1=gB~hPSfREJDTT&wdeNHh*Owf_A83h{$t?O5Yl~@&3blOlbyS=7A z1)zCm^t2UsDu;XbGT6et&$YhsZ?qQ%d#b=lLS<~eG24|ElBpBs_8~A1y1zsXf-<#R z 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 Z2&5sd|Nf^Ie7Mi1r(>X9bloB9e*hniCG-FQ literal 0 HcmV?d00001 diff --git a/docs/images/template_plugin_types.ep b/docs/images/template_plugin_types.ep new file mode 100644 index 00000000..d036b24c --- /dev/null +++ b/docs/images/template_plugin_types.ep @@ -0,0 +1,309 @@ + +sidebar1421408377849_14071531847true#FFFFFFFFtransparent + + + + + + + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + ]]> + styles_content / styles
classes_content / classes
]]>
+ + + + + + + + + +
data_bind
styles_wrapper / styles
classes_wrapper / classes
]]>
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + tab1421409530685_85131531847true#FFFFFFFFtransparent + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + data_bind
styles_link / styles
classes_link / classes
]]>
+ + + + + + + + + +
data_bind
styles_content / styles
classes_content / classes
]]>
+ + + + + + + + + +
settings1421410253180_35791531847true#FFFFFFFFtransparent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ]]> + + data_bind
styles_content / styles
classes_content / classes
]]>
+ + + + + + + + + +
data_bind
styles_link / styles
classes_link / classes
]]>
+ + + + + + + + + +
navbar1421410510071_96931531847true#FFFFFFFFtransparent + + + + + + + + + + + + + + + + + data_bind
styles
classes
]]>
+ + + + + + + + + +
\ No newline at end of file diff --git a/docs/plugins/developing.rst b/docs/plugins/developing.rst index 143ad3d8..34681bf1 100644 --- a/docs/plugins/developing.rst +++ b/docs/plugins/developing.rst @@ -13,6 +13,8 @@ Developing Plugins This section is still a heavy WIP, so take it with a bit of caution ;) +.. _sec-plugins-developing-structure: + OctoPrint Plugin Structure ========================== @@ -37,7 +39,7 @@ a couple of properties describing the module: Method called upon initializing of the plugin by the plugin subsystem, can be used to instantiate plugin implementations, connecting them to hooks etc. -A very simple example plugin which only hooks into OctoPrint's startup sequence and prints out "Hello World" to stdout would +A very simple example plugin which only hooks into OctoPrint's startup sequence and logs "Oh hello!" would be the following snippet: .. code-block:: python @@ -49,15 +51,17 @@ be the following snippet: __plugin_name__ = "Example Plugin" __plugin_version__ = "0.1" - __plugin_description__ = "Prints \"Hello World!\" to stdout upon OctoPrint's startup" + __plugin_description__ = "Logs \"Oh hello!\" upon OctoPrint's startup" def __plugin_init__(): global __plugin_implementations__ - __plugin_implementations__ = [HelloWorldPlugin()] + __plugin_implementations__ = [ExamplePlugin()] - class HelloWorldPlugin(octoprint.plugin.StartupPlugin): + class ExamplePlugin(octoprint.plugin.StartupPlugin): def on_startup(self, host, port): - print("Hello World!") + self._logger.info("Oh hello!") + +.. _sec-plugins-developing-distribution: Distributing your plugin ======================== @@ -72,43 +76,8 @@ You can distribute a plugin with OctoPrint via two ways: your plugin's ``setup.py``, this way it will be found automatically by OctoPrint upon initialization of the plugin subsystem [#f1]_. - In this case you'll have a directory structure like the following: - - .. code-block:: none - - `-+ helloworld - | `-+ static - | | `-+ css - | | `-- helloworld.css - | | `-+ js - | | `-- helloworld.js - | | `-+ less - | | `-- helloworld.less - | `-+ templates - | | `-- helloworld_settings_dialog.js - | `-- __init__.py - `-- setup.py - - The plugin itself will be distributed as a Python package, which your ``setup.py`` will refer to via the entry point - like this: - - .. code-block:: python - - import setuptools - - def params(): - name = "OctoPrint-HelloWorld" - version = "0.1" - packages = ["helloworld"] - install_requires = open("requirements.txt").read().split("\n") - - entry_points = { - "octoprint.plugin" = ["helloworld = octoprint_helloworld"] - } - - return locals() - - setuptools.setup(**params()) + For an example of how the directory structure and related files would look like in this case, please take a + look at the `helloworld example from OctoPrint's example plugins `_. This variant is highly recommended for pretty much any plugin besides the most basic ones since it also allows requirements management and pretty much any thing else that Python's setuptools provide to the developer. @@ -119,7 +88,6 @@ You can distribute a plugin with OctoPrint via two ways: environments), so make sure to instruct your users to use the exact same Python installation for installing the plugin that they also used for installing & running OctoPrint. - .. _sec-plugins-developing-mixins: Available plugin mixins diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index bd085f73..25cff1f9 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -1,60 +1,18 @@ .. _sec-plugins: -##################### -Plugins Documentation -##################### +####### +Plugins +####### Starting with OctoPrint 1.2.0, there's now a plugin system in place which allows to individually extend OctoPrint's functionality. -Right now plugins can be used to extend OctoPrint's settings dialog, to execute specific tasks on server startup and +Right now plugins can be used to extend OctoPrint's web interface, to execute specific tasks on server startup and shutdown, to provide custom (API) endpoints with special functionality, to react on system events or to add support for additional slicers. More plugin types are planned for the future. -.. _sec-plugins-installation: - -Installing Plugins -================== - -Plugins can be installed either by unpacking them into one of the configured plugin folders (regularly those are -``/plugins`` and ``~/.octoprint/plugins`` or by installing them as regular python modules via ``pip``. -Please refer to the documentation of the plugin for installations instructions. - -The latter is the more common case since all currently published plugins not bundled with OctoPrint can and should be installed -this way. - -For a plugin available on the Python Package Index (PyPi), the process is as simple as issuing a - -.. code-block:: bash - - pip install - -For plugins not available on PyPi, you'll have to give ``pip`` an URL from which to install the package (e.g. the URL to -a ZIP file of the current master branch of a Github repository hosting a plugin, or even a ``git+https`` URL), example: - -.. code-block:: bash - - pip install https://github.com/OctoPrint/OctoPrint-Growl/archive/master.zip - -See `the pip install documentation `_ for what URL -types are possible. - -.. _sec-plugins-available: - -Available Plugins -================= - -Currently there's no such thing as a centralized plugin repository for available plugins. - -Plugins may be found in the lists provided in `the OctoPrint wiki `_ -and on the `OctoPrint organization Github page `_. - -Developing Plugins -================== - -Please see the following sub topics for information on how to develop your own OctoPrint plugins. - .. toctree:: - :maxdepth: 2 + :maxdepth: 3 + using.rst developing.rst diff --git a/docs/plugins/using.rst b/docs/plugins/using.rst new file mode 100644 index 00000000..898996e5 --- /dev/null +++ b/docs/plugins/using.rst @@ -0,0 +1,44 @@ +.. _sec-plugins-using: + +************* +Using Plugins +************* + +.. _sec-plugins-using-available: + +Finding Plugins +=============== + +Currently there's no such thing as a centralized plugin repository for available plugins. + +Plugins may be found in the lists provided in `the OctoPrint wiki `_ +and on the `OctoPrint organization Github page `_. + +.. _sec-plugins-using-installing: + +Installing Plugins +================== + +Plugins can be installed either by unpacking them into one of the configured plugin folders (regularly those are +``/plugins`` and ``~/.octoprint/plugins`` or by installing them as regular python modules via ``pip``. +Please refer to the documentation of the plugin for installations instructions. + +The latter is the more common case since all currently published plugins not bundled with OctoPrint can and should be installed +this way. + +For a plugin available on the Python Package Index (PyPi), the process is as simple as issuing a + +.. code-block:: bash + + pip install + +For plugins not available on PyPi, you'll have to give ``pip`` an URL from which to install the package (e.g. the URL to +a ZIP file of the current master branch of a Github repository hosting a plugin, or even a ``git+https`` URL), example: + +.. code-block:: bash + + pip install https://github.com/OctoPrint/OctoPrint-Growl/archive/master.zip + +See `the pip install documentation `_ for what URL +types are possible. + diff --git a/src/octoprint/plugin/types.py b/src/octoprint/plugin/types.py index b2980b71..a584cdc8 100644 --- a/src/octoprint/plugin/types.py +++ b/src/octoprint/plugin/types.py @@ -186,149 +186,179 @@ class TemplatePlugin(Plugin): Further keys to be included in the dictionary depend on the type: ``navbar`` type + .. figure:: ../images/template-plugin-type-navbar.png + :align: center + :alt: Structure of navbar plugins + Configures a navbar component to inject. The following keys are supported: - template - Name of the template to inject, defaults to ``_navbar.jinja2``. - suffix - Suffix to attach to the element ID of the injected template, will be ``_`` if not provided and not - the first template of the type, with ``index`` counting from 1 and increasing for each template of the same - type. - custom_bindings - A boolean value indicating whether the default view model should be bound to the navbar entry (``false``) - or if a custom binding will be used by the plugin (``true``, default). - data_bind - Additional knockout data bindings to apply to the navbar entry, can be used to add further behaviour to - the container based on internal state if necessary. - classes - Additional classes to apply to the navbar entry, as a list of individual classes - (e.g. ``classes=["myclass", "myotherclass"]``) which will be joined into the correct format by the template engine. - styles - Additional CSS styles to apply to the navbar entry, as a list of individual declarations - (e.g. ``styles=["color: red", "display: block"]``) which will be joined into the correct format by the template - engine. + .. list-table:: + :widths: 5 95 + + * - template + - Name of the template to inject, defaults to ``_navbar.jinja2``. + * - suffix + - Suffix to attach to the element ID of the injected template, will be ``_`` if not provided and not + the first template of the type, with ``index`` counting from 1 and increasing for each template of the same + type. + * - custom_bindings + - A boolean value indicating whether the default view model should be bound to the navbar entry (``false``) + or if a custom binding will be used by the plugin (``true``, default). + * - data_bind + - Additional knockout data bindings to apply to the navbar entry, can be used to add further behaviour to + the container based on internal state if necessary. + * - classes + - Additional classes to apply to the navbar entry, as a list of individual classes + (e.g. ``classes=["myclass", "myotherclass"]``) which will be joined into the correct format by the template engine. + * - styles + - Additional CSS styles to apply to the navbar entry, as a list of individual declarations + (e.g. ``styles=["color: red", "display: block"]``) which will be joined into the correct format by the template + engine. ``sidebar`` type + .. figure:: ../images/template-plugin-type-sidebar.png + :align: center + :alt: Structure of sidebar plugins + Configures a sidebar component to inject. The following keys are supported: - name - The name of the sidebar entry, if not set the name of the plugin will be used. - icon - Icon to use for the sidebar header, should be the name of a Font Awesome icon without the leading ``icon-`` part. - template - Name of the template to inject, defaults to ``_sidebar.jinja2``. - template_header - Additional template to include in the head section of the sidebar item. For an example of this, see the additional - options included in the "Files" section. - suffix - Suffix to attach to the element ID of the injected template, will be ``_`` if not provided and not - the first template of the type, with ``index`` counting from 1 and increasing for each template of the same - type. - custom_bindings - A boolean value indicating whether the default view model should be bound to the sidebar container (``false``) - or if a custom binding will be used by the plugin (``true``, default). - data_bind - Additional knockout data bindings to apply to the template container, can be used to add further behaviour to - the container based on internal state if necessary. - classes - Additional classes to apply to both the wrapper around the sidebar box as well as the content pane itself, as a - list of individual classes (e.g. ``classes=["myclass", "myotherclass"]``) which will be joined into the correct - format by the template engine. - classes_wrapper - Like ``classes`` but only applied to the whole wrapper around the sidebar box. - classes_content - Like ``classes`` but only applied to the content pane itself. - styles - Additional CSS styles to apply to both the wrapper around the sidebar box as well as the content pane itself, - as a list of individual declarations (e.g. ``styles=["color: red", "display: block"]``) which will be joined - into the correct format by the template engine. - styles_wrapper - Like ``styles`` but only applied to the whole wrapper around the sidebar box. - styles_content - Like ``styles`` but only applied to the content pane itself + .. list-table:: + :widths: 5 95 + + * - name + - The name of the sidebar entry, if not set the name of the plugin will be used. + * - icon + - Icon to use for the sidebar header, should be the name of a Font Awesome icon without the leading ``icon-`` part. + * - template + - Name of the template to inject, defaults to ``_sidebar.jinja2``. + * - template_header + - Additional template to include in the head section of the sidebar item. For an example of this, see the additional + options included in the "Files" section. + * - suffix + - Suffix to attach to the element ID of the injected template, will be ``_`` if not provided and not + the first template of the type, with ``index`` counting from 1 and increasing for each template of the same + type. + * - custom_bindings + - A boolean value indicating whether the default view model should be bound to the sidebar container (``false``) + or if a custom binding will be used by the plugin (``true``, default). + * - data_bind + - Additional knockout data bindings to apply to the template container, can be used to add further behaviour to + the container based on internal state if necessary. + * - classes + - Additional classes to apply to both the wrapper around the sidebar box as well as the content pane itself, as a + list of individual classes (e.g. ``classes=["myclass", "myotherclass"]``) which will be joined into the correct + format by the template engine. + * - classes_wrapper + - Like ``classes`` but only applied to the whole wrapper around the sidebar box. + * - classes_content + - Like ``classes`` but only applied to the content pane itself. + * - styles + - Additional CSS styles to apply to both the wrapper around the sidebar box as well as the content pane itself, + as a list of individual declarations (e.g. ``styles=["color: red", "display: block"]``) which will be joined + into the correct format by the template engine. + * - styles_wrapper + - Like ``styles`` but only applied to the whole wrapper around the sidebar box. + * - styles_content + - Like ``styles`` but only applied to the content pane itself ``tab`` type + .. figure:: ../images/template-plugin-type-tab.png + :align: center + :alt: Structure of tab plugins + Configures a tab component to inject. The value must be a dictionary, supported values are the following: - name - The name under which to include the tab, if not set the name of the plugin will be used. - template - Name of the template to inject, defaults to ``_tab.jinja2``. - suffix - Suffix to attach to the element ID of the injected template, will be ``_`` if not provided and not - the first template of the type, with ``index`` counting from 1 and increasing for each template of the same - type. - custom_bindings - A boolean value indicating whether the default view model should be bound to the tab pane and link - in the navigation (``false``) or if a custom binding will be used by the plugin (``true``, default). - data_bind - Additional knockout data bindings to apply to the template container, can be used to add further behaviour to - the container based on internal state if necessary. - classes - Additional classes to apply to both the wrapper around the sidebar box as well as the content pane itself, as a - list of individual classes (e.g. ``classes=["myclass", "myotherclass"]``) which will be joined into the correct - format by the template engine. - classes_link - Like ``classes`` but only applied to the link in the navigation. - classes_content - Like ``classes`` but only applied to the content pane itself. - styles - Additional CSS styles to apply to both the wrapper around the sidebar box as well as the content pane itself, - as a list of individual declarations (e.g. ``styles=["color: red", "display: block"]``) which will be joined - into the correct format by the template engine. - styles_link - Like ``styles`` but only applied to the link in the navigation. - styles_content - Like ``styles`` but only applied to the content pane itself + .. list-table:: + :widths: 5 95 + + * - name + - The name under which to include the tab, if not set the name of the plugin will be used. + * - template + - Name of the template to inject, defaults to ``_tab.jinja2``. + * - suffix + - Suffix to attach to the element ID of the injected template, will be ``_`` if not provided and not + the first template of the type, with ``index`` counting from 1 and increasing for each template of the same + type. + * - custom_bindings + - A boolean value indicating whether the default view model should be bound to the tab pane and link + in the navigation (``false``) or if a custom binding will be used by the plugin (``true``, default). + * - data_bind + - Additional knockout data bindings to apply to the template container, can be used to add further behaviour to + the container based on internal state if necessary. + * - classes + - Additional classes to apply to both the wrapper around the sidebar box as well as the content pane itself, as a + list of individual classes (e.g. ``classes=["myclass", "myotherclass"]``) which will be joined into the correct + format by the template engine. + * - classes_link + - Like ``classes`` but only applied to the link in the navigation. + * - classes_content + - Like ``classes`` but only applied to the content pane itself. + * - styles + - Additional CSS styles to apply to both the wrapper around the sidebar box as well as the content pane itself, + as a list of individual declarations (e.g. ``styles=["color: red", "display: block"]``) which will be joined + into the correct format by the template engine. + * - styles_link + - Like ``styles`` but only applied to the link in the navigation. + * - styles_content + - Like ``styles`` but only applied to the content pane itself. ``settings`` type + .. figure:: ../images/template-plugin-type-settings.png + :align: center + :alt: Structure of settings plugins + Configures a settings component to inject. The value must be a dictionary, supported values are the following: - name - The name under which to include the settings pane, if not set the name of the plugin will be used. - template - Name of the template to inject, defaults to ``_settings.jinja2``. - suffix - Suffix to attach to the element ID of the injected template, will be ``_`` if not provided and not - the first template of the type, with ``index`` counting from 1 and increasing for each template of the same - type. - custom_bindings - A boolean value indicating whether the default settings view model should be bound to the settings pane and link - in the navigation (``false``) or if a custom binding will be used by the plugin (``true``, default). - data_bind - Additional knockout data bindings to apply to the template container, can be used to add further behaviour to - the container based on internal state if necessary. - classes - Additional classes to apply to both the wrapper around the navigation link as well as the content pane itself, as a - list of individual classes (e.g. ``classes=["myclass", "myotherclass"]``) which will be joined into the correct - format by the template engine. - classes_link - Like ``classes`` but only applied to the link in the navigation. - classes_content - Like ``classes`` but only applied to the content pane itself. - styles - Additional CSS styles to apply to both the wrapper around the navigation link as well as the content pane itself, - as a list of individual declarations (e.g. ``styles=["color: red", "display: block"]``) which will be joined - into the correct format by the template engine. - styles_link - Like ``styles`` but only applied to the link in the navigation. - styles_content - Like ``styles`` but only applied to the content pane itself + .. list-table:: + :widths: 5 95 + + * - name + - The name under which to include the settings pane, if not set the name of the plugin will be used. + * - template + - Name of the template to inject, defaults to ``_settings.jinja2``. + * - suffix + - Suffix to attach to the element ID of the injected template, will be ``_`` if not provided and not + the first template of the type, with ``index`` counting from 1 and increasing for each template of the same + type. + * - custom_bindings + - A boolean value indicating whether the default settings view model should be bound to the settings pane and link + in the navigation (``false``) or if a custom binding will be used by the plugin (``true``, default). + * - data_bind + - Additional knockout data bindings to apply to the template container, can be used to add further behaviour to + the container based on internal state if necessary. + * - classes + - Additional classes to apply to both the wrapper around the navigation link as well as the content pane itself, as a + list of individual classes (e.g. ``classes=["myclass", "myotherclass"]``) which will be joined into the correct + format by the template engine. + * - classes_link + - Like ``classes`` but only applied to the link in the navigation. + * - classes_content + - Like ``classes`` but only applied to the content pane itself. + * - styles + - Additional CSS styles to apply to both the wrapper around the navigation link as well as the content pane itself, + as a list of individual declarations (e.g. ``styles=["color: red", "display: block"]``) which will be joined + into the correct format by the template engine. + * - styles_link + - Like ``styles`` but only applied to the link in the navigation. + * - styles_content + - Like ``styles`` but only applied to the content pane itself ``generic`` type Configures a generic template to inject. The following keys are supported: - template - Name of the template to inject, defaults to ``_settings.jinja2``. + .. list-table:: + :widths: 5 95 + + * - template + - Name of the template to inject, defaults to ``_settings.jinja2``. .. note:: As already outlined above, each template type has a default template name (i.e. the default navbar template of a plugin is called ``_navbar.jinja2``), which may be overridden using the template configuration. - If a plugin needs to include two navbar templates, it needs to provide two entries here, at least one with ``template`` - overridden. If only one entry for a given template type is present with an overridden ``template`` parameter, - OctoPrint will assume that the default template does not exist and hence only include the defined template, - skipping the default one. + If a plugin needs to include more than one template of a given type, it needs to provide an entry for each of + those, since the implicit default template will only be included automatically if no other templates of that + type are defined. :return: a list containing the configuration options for the plugin's injected templates """