Merge branch 'dev/uiPlugin' into devel
This commit is contained in:
commit
f0ab517857
7 changed files with 324 additions and 53 deletions
|
|
@ -18,6 +18,7 @@ StartupPlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.StartupPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-shutdownplugin:
|
||||
|
||||
|
|
@ -26,6 +27,7 @@ ShutdownPlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.ShutdownPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-settingsplugin:
|
||||
|
||||
|
|
@ -34,6 +36,7 @@ SettingsPlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.SettingsPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-assetplugin:
|
||||
|
||||
|
|
@ -42,6 +45,7 @@ AssetPlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.AssetPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-templateplugin:
|
||||
|
||||
|
|
@ -50,6 +54,7 @@ TemplatePlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.TemplatePlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-wizardplugin:
|
||||
|
||||
|
|
@ -58,6 +63,16 @@ WizardPlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.WizardPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-uiplugin:
|
||||
|
||||
UiPlugin
|
||||
--------
|
||||
|
||||
.. autoclass:: octoprint.plugin.UiPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-simpleapiplugin:
|
||||
|
||||
|
|
@ -66,6 +81,7 @@ SimpleApiPlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.SimpleApiPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-blueprintplugin:
|
||||
|
||||
|
|
@ -74,6 +90,7 @@ BlueprintPlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.BlueprintPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-eventhandlerplugin:
|
||||
|
||||
|
|
@ -82,6 +99,7 @@ EventHandlerPlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.EventHandlerPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-progressplugin:
|
||||
|
||||
|
|
@ -90,6 +108,7 @@ ProgressPlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.ProgressPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. _sec-plugins-mixins-slicerplugin:
|
||||
|
||||
|
|
@ -98,4 +117,5 @@ SlicerPlugin
|
|||
|
||||
.. autoclass:: octoprint.plugin.SlicerPlugin
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
|
|
|
|||
|
|
@ -104,7 +104,8 @@ def plugin_manager(init=False, plugin_folders=None, plugin_types=None, plugin_en
|
|||
SlicerPlugin,
|
||||
AppPlugin,
|
||||
ProgressPlugin,
|
||||
WizardPlugin]
|
||||
WizardPlugin,
|
||||
UiPlugin]
|
||||
if plugin_entry_points is None:
|
||||
plugin_entry_points = "octoprint.plugin"
|
||||
if plugin_disabled_list is None:
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@ way and could be extracted into a separate Python module in the future.
|
|||
.. autoclass:: Plugin
|
||||
:members:
|
||||
|
||||
.. autoclass:: RestartNeedingPlugin
|
||||
:members:
|
||||
|
||||
.. autoclass:: SortablePlugin
|
||||
:members:
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
|
@ -1280,10 +1286,34 @@ class Plugin(object):
|
|||
pass
|
||||
|
||||
class RestartNeedingPlugin(Plugin):
|
||||
pass
|
||||
"""
|
||||
Mixin for plugin types that need a restart in order to be enabled.
|
||||
"""
|
||||
|
||||
class SortablePlugin(Plugin):
|
||||
"""
|
||||
Mixin for plugin types that are sortable.
|
||||
"""
|
||||
|
||||
def get_sorting_key(self, context=None):
|
||||
"""
|
||||
Returns the sorting key to use for the implementation in the specified ``context``.
|
||||
|
||||
May return ``None`` if order is irrelevant.
|
||||
|
||||
Implementations returning None will be ordered by plugin identifier
|
||||
after all implementations which did return a sorting key value that was
|
||||
not None sorted by that.
|
||||
|
||||
Arguments:
|
||||
context (str): The sorting context for which to provide the
|
||||
sorting key value.
|
||||
|
||||
Returns:
|
||||
int or None: An integer signifying the sorting key value of the plugin
|
||||
(sorting will be done ascending), or None if the implementation
|
||||
doesn't care about calling order.
|
||||
"""
|
||||
return None
|
||||
|
||||
class PluginNeedsRestart(Exception):
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ Please note that the plugin implementation types are documented in the section
|
|||
.. autoclass:: OctoPrintPlugin
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: ReloadNeedingPlugin
|
||||
:show-inheritance:
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
|
@ -97,12 +100,18 @@ class OctoPrintPlugin(Plugin):
|
|||
|
||||
|
||||
class ReloadNeedingPlugin(Plugin):
|
||||
pass
|
||||
"""
|
||||
Mixin for plugin types that need a reload of the UI in order to become usable.
|
||||
"""
|
||||
|
||||
class StartupPlugin(OctoPrintPlugin, SortablePlugin):
|
||||
"""
|
||||
The ``StartupPlugin`` allows hooking into the startup of OctoPrint. It can be used to start up additional services
|
||||
on or just after the startup of the server.
|
||||
|
||||
``StartupPlugin`` is a :class:`~octoprint.plugin.core.SortablePlugin`. The
|
||||
relevant sorting context for :meth:`on_startup` is ``StartupPlugin.on_startup``,
|
||||
the one for :meth:`on_after_startup` will be ``StartupPlugin.on_after_startup``.
|
||||
"""
|
||||
|
||||
def on_startup(self, host, port):
|
||||
|
|
@ -132,6 +141,9 @@ class ShutdownPlugin(OctoPrintPlugin, SortablePlugin):
|
|||
The ``ShutdownPlugin`` allows hooking into the shutdown of OctoPrint. It's usually used in conjunction with the
|
||||
:class:`StartupPlugin` mixin, to cleanly shut down additional services again that where started by the :class:`StartupPlugin`
|
||||
part of the plugin.
|
||||
|
||||
``ShutdownPlugin`` is a :class:`~octoprint.plugin.core.SortablePlugin`.
|
||||
The relevant sorting context will be ``ShutdownPlugin.on_shutdown``.
|
||||
"""
|
||||
|
||||
def on_shutdown(self):
|
||||
|
|
@ -149,6 +161,8 @@ class AssetPlugin(OctoPrintPlugin, RestartNeedingPlugin):
|
|||
|
||||
A typical usage of the ``AssetPlugin`` functionality is to embed a custom view model to be used by templates injected
|
||||
through a :class:`TemplatePlugin`.
|
||||
|
||||
``AssetPlugin`` is a :class:`~octoprint.plugins.core.RestartNeedingPlugin`.
|
||||
"""
|
||||
|
||||
def get_asset_folder(self):
|
||||
|
|
@ -297,6 +311,8 @@ class TemplatePlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
|||
responsibility to ensure that all core functionality is still maintained.
|
||||
|
||||
Plugins can also add additional template types by implementing the :ref:`octoprint.ui.web.templatetypes <sec-plugins-hook-ui-web-templatetypes>` hook.
|
||||
|
||||
``TemplatePlugin`` is a :class:`~octoprint.plugin.core.ReloadNeedingPlugin`.
|
||||
"""
|
||||
|
||||
def get_template_configs(self):
|
||||
|
|
@ -485,6 +501,167 @@ class TemplatePlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
|||
return os.path.join(self._basefolder, "templates")
|
||||
|
||||
|
||||
class UiPlugin(OctoPrintPlugin, SortablePlugin):
|
||||
"""
|
||||
The ``UiPlugin`` mixin allows plugins to completely replace the UI served
|
||||
by OctoPrint when requesting the main page hosted at `/`.
|
||||
|
||||
OctoPrint will query whether your mixin implementation will handle a
|
||||
provided request by calling :meth:`~octoprint.plugin.UiPlugin.will_handle_ui` with the Flask
|
||||
`Request <http://flask.pocoo.org/docs/0.10/api/#flask.Request>`_ object as
|
||||
parameter. If you plugin returns `True` here, OctoPrint will next call
|
||||
:meth:`~octoprint.plugin.UiPlugin.on_ui_render` with a couple of parameters like
|
||||
- again - the Flask Request object and the render keyword arguments as
|
||||
used by the default OctoPrint web interface. For more information see below.
|
||||
|
||||
There are two methods used in order to allow for caching of the actual
|
||||
response sent to the client. Whatever a plugin implementation returns
|
||||
from the call to its :meth:`~octoprint.plugin.UiPlugin.on_ui_render` method
|
||||
will be cached server side. The cache will be emptied in case of explicit
|
||||
no-cache headers sent by the client, or if the ``_refresh`` query parameter
|
||||
on the request exists and is set to ``true``. To prevent caching of the
|
||||
response altogether, a plugin may set no-cache headers on the returned
|
||||
response as well.
|
||||
|
||||
``UiPlugin`` is a :class:`~octoprint.plugin.core.SortablePlugin`. The
|
||||
relevant sorting context when acting as a UiPlugin is ``UiPlugin.will_handle_ui``.
|
||||
The first plugin to return ``True`` will be the one whose ui will be used,
|
||||
no further calls to :meth:`~octoprint.plugin.UiPlugin.on_ui_render` will be performed.
|
||||
|
||||
If implementations want to serve custom templates in the :meth:`~octoprint.plugin.UiPlugin.on_ui_render`
|
||||
method it is recommended to also implement the :class:`~octoprint.plugin.TemplatePlugin`
|
||||
mixin.
|
||||
|
||||
**Example**
|
||||
|
||||
What follows is a very simple example that renders a different (non functional and
|
||||
only exemplary) UI if the requesting client has a UserAgent string hinting
|
||||
at it being a mobile device:
|
||||
|
||||
.. onlineinclude:: https://raw.githubusercontent.com/OctoPrint/Plugin-Examples/master/dummy_mobile_ui/__init__.py
|
||||
:linenos:
|
||||
:tab-width: 4
|
||||
:caption: `dummy_mobile_ui/__init__.py <https://github.com/OctoPrint/Plugin-Examples/blob/master/dummy_mobile_ui/__init__.py>`_
|
||||
|
||||
.. onlineinclude:: https://raw.githubusercontent.com/OctoPrint/Plugin-Examples/master/dummy_mobile_ui/templates/dummy_mobile_ui_index.jinja2
|
||||
:linenos:
|
||||
:tab-width: 4
|
||||
:caption: `dummy_mobile_ui/templates/dummy_mobile_ui_index.jinja2 <https://github.com/OctoPrint/Plugin-Examples/blob/master/dummy_mobile_ui/templates/dummy_mobile_ui_index.jinja2>`_
|
||||
|
||||
Try installing the above plugin ``dummy_mobile_ui`` (also available in the
|
||||
`plugin examples repository <https://github.com/OctoPrint/Plugin-Examples/blob/master/dummy_mobile_ui>`_)
|
||||
into your OctoPrint instance. If you access it from a regular desktop browser,
|
||||
you should still see the default UI. However if you access it from a mobile
|
||||
device (make sure to not have that request the desktop version of pages!)
|
||||
you should see the very simple dummy page defined above.
|
||||
"""
|
||||
|
||||
def will_handle_ui(self, request):
|
||||
"""
|
||||
Called by OctoPrint to determine if the mixin implementation will be
|
||||
able to handle the ``request`` provided as a parameter.
|
||||
|
||||
Return ``True`` here to signal that your implementation will handle
|
||||
the request and that the result of its :meth:`~octoprint.plugin.UiPlugin.on_ui_render` method
|
||||
is what should be served to the user.
|
||||
|
||||
Arguments:
|
||||
request (flask.Request): A Flask `Request <http://flask.pocoo.org/docs/0.10/api/#flask.Request>`_
|
||||
object.
|
||||
|
||||
Returns:
|
||||
bool: ``True`` if the the implementation will serve the request,
|
||||
``False`` otherwise.
|
||||
"""
|
||||
return False
|
||||
|
||||
def on_ui_render(self, now, request, render_kwargs):
|
||||
"""
|
||||
Called by OctoPrint to retrieve the response to send to the client
|
||||
for the ``request`` to ``/``. Only called if :meth:`~octoprint.plugin.UiPlugin.will_handle_ui`
|
||||
returned ``True``.
|
||||
|
||||
``render_kwargs`` will be a dictionary (whose contents are cached) which
|
||||
will contain the following key and value pairs (note that not all
|
||||
key value pairs contained in the dictionary are listed here, only
|
||||
those you should depend on as a plugin developer at the current time):
|
||||
|
||||
.. list-table::
|
||||
:widths: 5 95
|
||||
|
||||
* - debug
|
||||
- ``True`` if debug mode is enabled, ``False`` otherwise.
|
||||
* - firstRun
|
||||
- ``True`` if the server is being run for the first time (not
|
||||
configured yet), ``False`` otherwise.
|
||||
* - version
|
||||
- OctoPrint's version information. This is a ``dict`` with the
|
||||
following keys:
|
||||
|
||||
.. list-table::
|
||||
:widths: 5 95
|
||||
|
||||
* - number
|
||||
- The version number (e.g. ``x.y.z``)
|
||||
* - branch
|
||||
- The GIT branch from which the OctoPrint instance was built
|
||||
(e.g. ``master``)
|
||||
* - display
|
||||
- The full human readable version string, including the
|
||||
branch information (e.g. ``x.y.z (master branch)``
|
||||
|
||||
* - uiApiKey
|
||||
- The UI API key to use for unauthorized API requests. This is
|
||||
freshly generated on every server restart.
|
||||
* - templates
|
||||
- Template data to render in the UI. Will be a ``dict`` containing entries
|
||||
for all known template types.
|
||||
|
||||
The sub structure for each key will be as follows:
|
||||
|
||||
.. list-table::
|
||||
:widths: 5 95
|
||||
|
||||
* - order
|
||||
- A list of template names in the order they should appear
|
||||
in the final rendered page
|
||||
* - entries
|
||||
- The template entry definitions to render. Depending on the
|
||||
template type those are either 2-tuples of a name and a ``dict``
|
||||
or directly ``dicts`` with information regarding the
|
||||
template to render.
|
||||
|
||||
For the possible contents of the data ``dicts`` see the
|
||||
:class:`~octoprint.plugin.TemplatePlugin` mixin.
|
||||
|
||||
* - pluginNames
|
||||
- A list of names of :class:`~octoprint.plugin.TemplatePlugin`
|
||||
implementation that were enabled when creating the ``templates``
|
||||
value.
|
||||
* - locales
|
||||
- The locales for which there are translations available.
|
||||
|
||||
On top of that all additional template variables as provided by :meth:`~octoprint.plugin.TemplatePlugin.get_template_vars`
|
||||
will be contained in the dictionary as well.
|
||||
|
||||
Arguments:
|
||||
now (datetime.datetime): The datetime instance representing "now"
|
||||
for this request, in case your plugin implementation needs this
|
||||
information.
|
||||
request (flask.Request): A Flask `Request <http://flask.pocoo.org/docs/0.10/api/#flask.Request>`_ object.
|
||||
render_kwargs (dict): The (cached) render keyword arguments that
|
||||
would usually be provided to the core UI render function.
|
||||
|
||||
Returns:
|
||||
flask.Response: Should return a Flask `Response <http://flask.pocoo.org/docs/0.10/api/#flask.Response>`_
|
||||
object that can be served to the requesting client directly. May be
|
||||
created with ``flask.make_response`` combined with something like
|
||||
``flask.render_template``.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class WizardPlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
||||
"""
|
||||
The ``WizardPlugin`` mixin allows plugins to report to OctoPrint whether
|
||||
|
|
@ -546,6 +723,8 @@ class WizardPlugin(OctoPrintPlugin, ReloadNeedingPlugin):
|
|||
|
||||
def get_wizard_version(self):
|
||||
return 1
|
||||
|
||||
``WizardPlugin`` is a :class:`~octoprint.plugin.core.ReloadNeedingPlugin`.
|
||||
"""
|
||||
|
||||
def is_wizard_required(self):
|
||||
|
|
@ -850,6 +1029,8 @@ class BlueprintPlugin(OctoPrintPlugin, RestartNeedingPlugin):
|
|||
|
||||
flask.url_for("plugin.myblueprintplugin.myEcho") # will return "/plugin/myblueprintplugin/echo"
|
||||
|
||||
|
||||
``BlueprintPlugin`` implements :class:`~octoprint.plugins.core.RestartNeedingPlugin`.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -283,11 +283,11 @@ def cached(timeout=5 * 60, key=lambda: "view/%s" % flask.request.path, unless=No
|
|||
if not callable(refreshif) or not refreshif():
|
||||
rv = _cache.get(cache_key)
|
||||
if rv is not None:
|
||||
logger.debug("Serving entry for {path} from cache".format(path=flask.request.path))
|
||||
logger.debug("Serving entry for {path} from cache (key: {key})".format(path=flask.request.path, key=cache_key))
|
||||
return rv
|
||||
|
||||
# get value from wrapped function
|
||||
logger.debug("No cache entry or refreshing cache for {path}, calling wrapped function".format(path=flask.request.path))
|
||||
logger.debug("No cache entry or refreshing cache for {path} (key: {key}), calling wrapped function".format(path=flask.request.path, key=cache_key))
|
||||
rv = f(*args, **kwargs)
|
||||
|
||||
# do not store if the "unless_response" condition is true
|
||||
|
|
|
|||
|
|
@ -22,21 +22,95 @@ from . import util
|
|||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@app.route("/")
|
||||
@util.flask.cached(refreshif=lambda: util.flask.cache_check_headers() or "_refresh" in request.values,
|
||||
key=lambda: "view/%s/%s" % (request.path, g.locale),
|
||||
unless_response=util.flask.cache_check_response_headers)
|
||||
def index():
|
||||
_templates = None
|
||||
_plugin_names = None
|
||||
_plugin_vars = None
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
force_refresh = util.flask.cache_check_headers() or "_refresh" in request.values
|
||||
|
||||
global _templates, _plugin_names, _plugin_vars
|
||||
|
||||
if force_refresh or _templates is None or _plugin_names is None or _plugin_vars is None:
|
||||
_templates, _plugin_names, _plugin_vars = _process_templates()
|
||||
|
||||
now = datetime.datetime.utcnow()
|
||||
render_kwargs = _get_render_kwargs(_templates, _plugin_names, _plugin_vars, now)
|
||||
|
||||
def get_cached_view(key, view):
|
||||
return util.flask.cached(refreshif=lambda: force_refresh,
|
||||
key=lambda: "ui:{}:{}".format(key, g.locale),
|
||||
unless_response=util.flask.cache_check_response_headers)(view)
|
||||
|
||||
ui_plugins = pluginManager.get_implementations(octoprint.plugin.UiPlugin, sorting_context="UiPlugin.on_ui_render")
|
||||
for plugin in ui_plugins:
|
||||
if plugin.will_handle_ui(request):
|
||||
# plugin claims responsibility, let it render the UI
|
||||
cached = get_cached_view(plugin._identifier, plugin.on_ui_render)
|
||||
response = cached(now, request, render_kwargs)
|
||||
if response is not None:
|
||||
break
|
||||
|
||||
else:
|
||||
wizard = bool(_templates["wizard"]["order"])
|
||||
enable_accesscontrol = userManager is not None
|
||||
|
||||
render_kwargs.update(dict(
|
||||
webcamStream=settings().get(["webcam", "stream"]),
|
||||
enableTemperatureGraph=settings().get(["feature", "temperatureGraph"]),
|
||||
enableAccessControl=enable_accesscontrol,
|
||||
enableSdSupport=settings().get(["feature", "sdSupport"]),
|
||||
gcodeMobileThreshold=settings().get(["gcodeViewer", "mobileSizeThreshold"]),
|
||||
gcodeThreshold=settings().get(["gcodeViewer", "sizeThreshold"]),
|
||||
wizard=wizard,
|
||||
now=now,
|
||||
))
|
||||
|
||||
# no plugin took an interest, we'll use the default UI
|
||||
def make_default_ui():
|
||||
r = make_response(render_template("index.jinja2", **render_kwargs))
|
||||
if bool(render_kwargs["templates"]["wizard"]["order"]):
|
||||
r = util.flask.add_non_caching_response_headers(r)
|
||||
return r
|
||||
|
||||
cached = get_cached_view("_default", make_default_ui)
|
||||
response = cached()
|
||||
|
||||
response.headers["Last-Modified"] = now
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def _get_render_kwargs(templates, plugin_names, plugin_vars, now):
|
||||
#~~ a bunch of settings
|
||||
|
||||
first_run = settings().getBoolean(["server", "firstRun"])
|
||||
locales = dict((l.language, dict(language=l.language, display=l.display_name, english=l.english_name)) for l in LOCALES)
|
||||
|
||||
#~~ prepare full set of template vars for rendering
|
||||
|
||||
render_kwargs = dict(
|
||||
debug=debug,
|
||||
firstRun=first_run,
|
||||
version=dict(number=VERSION, display=DISPLAY_VERSION, branch=BRANCH),
|
||||
uiApiKey=UI_API_KEY,
|
||||
templates=templates,
|
||||
pluginNames=plugin_names,
|
||||
locales=locales,
|
||||
)
|
||||
render_kwargs.update(plugin_vars)
|
||||
|
||||
return render_kwargs
|
||||
|
||||
|
||||
def _process_templates():
|
||||
enable_accesscontrol = userManager is not None
|
||||
first_run = settings().getBoolean(["server", "firstRun"])
|
||||
enable_gcodeviewer = settings().getBoolean(["gcodeViewer", "enabled"])
|
||||
enable_timelapse = (settings().get(["webcam", "snapshot"]) and settings().get(["webcam", "ffmpeg"]))
|
||||
enable_systemmenu = settings().get(["system"]) is not None and settings().get(["system", "actions"]) is not None and len(settings().get(["system", "actions"])) > 0
|
||||
enable_accesscontrol = userManager is not None
|
||||
preferred_stylesheet = settings().get(["devel", "stylesheet"])
|
||||
locales = dict((l.language, dict(language=l.language, display=l.display_name, english=l.english_name)) for l in LOCALES)
|
||||
|
||||
##~~ prepare templates
|
||||
|
||||
|
|
@ -327,43 +401,7 @@ def index():
|
|||
templates[t]["entries"].update(template_sorting[t]["custom_insert_entries"](sorted_missing))
|
||||
templates[t]["order"] = template_sorting[t]["custom_insert_order"](templates[t]["order"], sorted_missing)
|
||||
|
||||
#~~ prepare full set of template vars for rendering
|
||||
|
||||
wizard = bool(templates["wizard"]["order"])
|
||||
now = datetime.datetime.utcnow()
|
||||
render_kwargs = dict(
|
||||
webcamStream=settings().get(["webcam", "stream"]),
|
||||
enableTemperatureGraph=settings().get(["feature", "temperatureGraph"]),
|
||||
enableAccessControl=enable_accesscontrol,
|
||||
enableSdSupport=settings().get(["feature", "sdSupport"]),
|
||||
firstRun=first_run,
|
||||
debug=debug,
|
||||
version=VERSION,
|
||||
display_version=DISPLAY_VERSION,
|
||||
branch=BRANCH,
|
||||
gcodeMobileThreshold=settings().get(["gcodeViewer", "mobileSizeThreshold"]),
|
||||
gcodeThreshold=settings().get(["gcodeViewer", "sizeThreshold"]),
|
||||
uiApiKey=UI_API_KEY,
|
||||
templates=templates,
|
||||
pluginNames=plugin_names,
|
||||
locales=locales,
|
||||
wizard=wizard,
|
||||
now=now
|
||||
)
|
||||
render_kwargs.update(plugin_vars)
|
||||
|
||||
#~~ render!
|
||||
|
||||
response = make_response(render_template(
|
||||
"index.jinja2",
|
||||
**render_kwargs
|
||||
))
|
||||
response.headers["Last-Modified"] = now
|
||||
|
||||
if wizard:
|
||||
response = util.flask.add_non_caching_response_headers(response)
|
||||
|
||||
return response
|
||||
return templates, plugin_names, plugin_vars
|
||||
|
||||
|
||||
def _process_template_configs(name, implementation, configs, rules):
|
||||
|
|
@ -454,7 +492,8 @@ def robotsTxt():
|
|||
|
||||
|
||||
@app.route("/i18n/<string:locale>/<string:domain>.js")
|
||||
@util.flask.cached(refreshif=lambda: util.flask.cache_check_headers() or "_refresh" in request.values, key=lambda: "view/%s/%s" % (request.path, g.locale))
|
||||
@util.flask.cached(refreshif=lambda: util.flask.cache_check_headers() or "_refresh" in request.values,
|
||||
key=lambda: "{}:{}".format(request.path, g.locale))
|
||||
def localeJs(locale, domain):
|
||||
messages = dict()
|
||||
plural_expr = None
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@
|
|||
var SOCKJS_CLOSE_NORMAL = 1000;
|
||||
|
||||
var UI_API_KEY = "{{ uiApiKey }}";
|
||||
var VERSION = "{{ version }}";
|
||||
var DISPLAY_VERSION = "{{ display_version }}";
|
||||
var BRANCH = "{{ branch }}";
|
||||
var VERSION = "{{ version.number }}";
|
||||
var DISPLAY_VERSION = "{{ version.display }}";
|
||||
var BRANCH = "{{ version.branch }}";
|
||||
var LOCALE = "{{ g.locale }}";
|
||||
var AVAILABLE_LOCALES = {{ locales|tojson }};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue