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) extensions = map(lambda ext: ".{}".format(ext), get_all_extensions()) #~~ 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, supportedExtensions=extensions) render_kwargs.update(plugin_vars) return render_kwargs
def _get_render_kwargs(templates, plugin_names, plugin_vars, now): global _logger #~~ a bunch of settings first_run = settings().getBoolean(["server", "firstRun"]) locales = dict() for l in LOCALES: try: locales[l.language] = dict(language=l.language, display=l.display_name, english=l.english_name) except: _logger.exception("Error while collecting available locales") filetypes = sorted(full_extension_tree().keys()) extensions = map(lambda ext: ".{}".format(ext), get_all_extensions()) #~~ 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), templates=templates, pluginNames=plugin_names, locales=locales, supportedFiletypes=filetypes, supportedExtensions=extensions ) render_kwargs.update(plugin_vars) return render_kwargs
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) extensions = map(lambda ext: ".{}".format(ext), get_all_extensions()) #~~ 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, supportedExtensions=extensions ) render_kwargs.update(plugin_vars) return render_kwargs
def index(): #~~ a bunch of settings 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 enable_accesscontrol = userManager.enabled 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) enable_devMode = userManager.devModeEnabled() ##~~ prepare templates templates = defaultdict(lambda: dict(order=[], entries=dict())) # rules for transforming template configs to template entries template_rules = dict( navbar=dict(div=lambda x: "navbar_plugin_" + x, template=lambda x: x + "_navbar.jinja2", to_entry=lambda data: data), sidebar=dict(div=lambda x: "sidebar_plugin_" + x, template=lambda x: x + "_sidebar.jinja2", to_entry=lambda data: (data["name"], data)), tab=dict(div=lambda x: "tab_plugin_" + x, template=lambda x: x + "_tab.jinja2", to_entry=lambda data: (data["name"], data)), settings=dict(div=lambda x: "settings_plugin_" + x, template=lambda x: x + "_settings.jinja2", to_entry=lambda data: (data["name"], data)), usersettings=dict(div=lambda x: "usersettings_plugin_" + x, template=lambda x: x + "_usersettings.jinja2", to_entry=lambda data: (data["name"], data)), about=dict(div=lambda x: "about_plugin_" + x, template=lambda x: x + "_about.jinja2", to_entry=lambda data: (data["name"], data)), generic=dict(template=lambda x: x + ".jinja2", to_entry=lambda data: data) ) # sorting orders template_sorting = dict( navbar=dict(add="prepend", key=None), sidebar=dict(add="append", key="name"), tab=dict(add="append", key="name"), settings=dict(add="custom_append", key="name", custom_add_entries=lambda missing: dict(section_plugins=(gettext("Plugins"), None)), custom_add_order=lambda missing: ["section_plugins"] + missing), usersettings=dict(add="append", key="name"), about=dict(add="append", key="name"), generic=dict(add="append", key=None) ) hooks = pluginManager.get_hooks("octoprint.ui.web.templatetypes") for name, hook in hooks.items(): try: result = hook(dict(template_sorting), dict(template_rules)) except: _logger.exception("Error while retrieving custom template type definitions from plugin {name}".format(**locals())) else: if not isinstance(result, list): continue for entry in result: if not isinstance(entry, tuple) or not len(entry) == 3: continue key, order, rule = entry # order defaults if "add" not in order: order["add"] = "prepend" if "key" not in order: order["key"] = "name" # rule defaults if "div" not in rule: # default div name: <hook plugin>_<template_key>_plugin_<plugin> div = "{name}_{key}_plugin_".format(**locals()) rule["div"] = lambda x: div + x if "template" not in rule: # default template name: <plugin>_plugin_<hook plugin>_<template key>.jinja2 template = "_plugin_{name}_{key}.jinja2".format(**locals()) rule["template"] = lambda x: x + template if "to_entry" not in rule: # default to_entry assumes existing "name" property to be used as label for 2-tuple entry data structure (<name>, <properties>) rule["to_entry"] = lambda data: (data["name"], data) template_rules["plugin_" + name + "_" + key] = rule template_sorting["plugin_" + name + "_" + key] = order template_types = template_rules.keys() # navbar templates["navbar"]["entries"] = dict( settings=dict(template="navbar/settings.jinja2", _div="navbar_settings", styles=["display: none"], data_bind="visible: loginState.isAdmin"), maintenance=dict(template="navbar/maintenance.jinja2", _div="navbar_maintenance") ) if enable_accesscontrol: templates["navbar"]["entries"]["login"] = dict(template="navbar/login.jinja2", _div="navbar_login", classes=["dropdown"], custom_bindings=False) if enable_systemmenu: templates["navbar"]["entries"]["systemmenu"] = dict(template="navbar/systemmenu.jinja2", _div="navbar_systemmenu", styles=["display: none"], classes=["dropdown"], data_bind="visible: loginState.isAdmin", custom_bindings=False) # sidebar templates["sidebar"]["entries"]= dict( #connection=(gettext("Connection"), dict(template="sidebar/connection.jinja2", _div="connection", icon="signal", styles_wrapper=["display: none"], data_bind="visible: loginState.isAdmin")), files=(gettext("Files"), dict(template="sidebar/files.jinja2", _div="files", classes_content=["overflow_visible"], template_header="sidebar/files_header.jinja2")), state = ("", dict(template="sidebar/printer_status.jinja2", _div="state", template_header="sidebar/printer_status_header.jinja2")) ) # tabs templates["tab"]["entries"] = dict( workbench=(gettext("Workbench"), dict(template="tabs/workbench.jinja2", _div="workbench")), ) if enable_gcodeviewer: templates["tab"]["entries"]["gcodeviewer"] = (gettext("GCode Viewer"), dict(template="tabs/gcodeviewer.jinja2", _div="gcode")) # Tabs for developer mode if enable_devMode: templates["tab"]["entries"]["temperature"] = (gettext("Temperature"), dict(template="tabs/temperature.jinja2", _div="temp")) templates["tab"]["entries"]["control"] = (gettext("Control"), dict(template="tabs/control.jinja2", _div="control")) templates["tab"]["entries"]["terminal"] = (gettext("Terminal"), dict(template="tabs/terminal.jinja2", _div="term")) if enable_timelapse: templates["tab"]["entries"]["timelapse"] = (gettext("Timelapse"), dict(template="tabs/timelapse.jinja2", _div="timelapse")) # settings dialog templates["settings"]["entries"] = dict( section_printer=(gettext("Printer"), None), #serial=(gettext("Serial Connection"), dict(template="dialogs/settings/serialconnection.jinja2", _div="settings_serialConnection", custom_bindings=False)), printerprofiles=(gettext("Printer Profiles"), dict(template="dialogs/settings/printerprofiles.jinja2", _div="settings_printerProfiles", custom_bindings=False)), section_features=(gettext("General Settings"), None), ) if enable_accesscontrol: templates["settings"]["entries"]["accesscontrol"] = (gettext("Access Control"), dict(template="dialogs/settings/accesscontrol.jinja2", _div="settings_users", custom_bindings=False)) # Settings for developer mode if enable_devMode: templates["settings"]["entries"]["folders"] = (gettext("Folders"), dict(template="dialogs/settings/folders.jinja2", _div="settings_folders", custom_bindings=False)) templates["settings"]["entries"]["appearance"] = (gettext("Appearance"),dict(template="dialogs/settings/appearance.jinja2", _div="settings_appearance", custom_bindings=False)) templates["settings"]["entries"]["temperatures"] = (gettext("Temperatures"), dict(template="dialogs/settings/temperatures.jinja2", _div="settings_temperature", custom_bindings=False)) templates["settings"]["entries"]["terminalfilters"] = (gettext("Terminal Filters"), dict(template="dialogs/settings/terminalfilters.jinja2", _div="settings_terminalFilters", custom_bindings=False)) templates["settings"]["entries"]["gcodescripts"] = (gettext("GCODE Scripts"), dict(template="dialogs/settings/gcodescripts.jinja2", _div="settings_gcodeScripts", custom_bindings=False)) templates["settings"]["entries"]["features"] = (gettext("Features"), dict(template="dialogs/settings/features.jinja2", _div="settings_features", custom_bindings=False)) templates["settings"]["entries"]["api"] = (gettext("API"), dict(template="dialogs/settings/api.jinja2", _div="settings_api", custom_bindings=False)) templates["settings"]["entries"]["logs"] = (gettext("Logs"), dict(template="dialogs/settings/logs.jinja2", _div="settings_logs")) templates["settings"]["entries"]["server"] = (gettext("Server"), dict(template="dialogs/settings/server.jinja2", _div="settings_server", custom_bindings=False)) templates["settings"]["entries"]["webcam"] = (gettext("Webcam"), dict(template="dialogs/settings/webcam.jinja2", _div="settings_webcam", custom_bindings=False)) # user settings dialog if enable_accesscontrol: templates["usersettings"]["entries"] = dict( access=(gettext("Access"), dict(template="dialogs/usersettings/access.jinja2", _div="usersettings_access", custom_bindings=False)), interface=(gettext("Interface"), dict(template="dialogs/usersettings/interface.jinja2", _div="usersettings_interface", custom_bindings=False)), ) # about dialog templates["about"]["entries"] = dict( about=("About OctoPrint", dict(template="dialogs/about/about.jinja2", _div="about_about", custom_bindings=False)), license=("OctoPrint License", dict(template="dialogs/about/license.jinja2", _div="about_license", custom_bindings=False)), thirdparty=("Third Party Licenses", dict(template="dialogs/about/thirdparty.jinja2", _div="about_thirdparty", custom_bindings=False)), authors=("Authors", dict(template="dialogs/about/authors.jinja2", _div="about_authors", custom_bindings=False)), changelog=("Changelog", dict(template="dialogs/about/changelog.jinja2", _div="about_changelog", custom_bindings=False)), supporters=("Supporters", dict(template="dialogs/about/supporters.jinja2", _div="about_sponsors", custom_bindings=False)) ) # extract data from template plugins template_plugins = pluginManager.get_implementations(octoprint.plugin.TemplatePlugin) plugin_vars = dict() plugin_names = set() for implementation in template_plugins: name = implementation._identifier plugin_names.add(name) try: vars = implementation.get_template_vars() configs = implementation.get_template_configs() except: _logger.exception("Error while retrieving template data for plugin {}, ignoring it".format(name)) continue if not isinstance(vars, dict): vars = dict() if not isinstance(configs, (list, tuple)): configs = [] for var_name, var_value in vars.items(): plugin_vars["plugin_" + name + "_" + var_name] = var_value includes = _process_template_configs(name, implementation, configs, template_rules) for t in template_types: for include in includes[t]: if t == "navbar" or t == "generic": data = include else: data = include[1] key = data["_key"] if "replaces" in data: key = data["replaces"] templates[t]["entries"][key] = include #~~ order internal templates and plugins # make sure that # 1) we only have keys in our ordered list that we have entries for and # 2) we have all entries located somewhere within the order for t in template_types: default_order = settings().get(["appearance", "components", "order", t], merged=True, config=dict()) or [] configured_order = settings().get(["appearance", "components", "order", t], merged=True) or [] configured_disabled = settings().get(["appearance", "components", "disabled", t]) or [] # first create the ordered list of all component ids according to the configured order templates[t]["order"] = [x for x in configured_order if x in templates[t]["entries"] and not x in configured_disabled] # now append the entries from the default order that are not already in there templates[t]["order"] += [x for x in default_order if not x in templates[t]["order"] and x in templates[t]["entries"] and not x in configured_disabled] all_ordered = set(templates[t]["order"]) all_disabled = set(configured_disabled) # check if anything is missing, if not we are done here missing_in_order = set(templates[t]["entries"].keys()).difference(all_ordered).difference(all_disabled) if len(missing_in_order) == 0: continue # finally add anything that's not included in our order yet sorted_missing = list(missing_in_order) if template_sorting[t]["key"] is not None: # default extractor: works with entries that are dicts and entries that are 2-tuples with the # entry data at index 1 def extractor(item, key): if isinstance(item, dict) and key in item: return item[key] elif isinstance(item, tuple) and len(item) > 1 and isinstance(item[1], dict) and key in item[1]: return item[1][key] return None # if template type provides custom extractor, make sure its exceptions are handled if "key_extractor" in template_sorting[t] and callable(template_sorting[t]["key_extractor"]): def create_safe_extractor(extractor): def f(x, k): try: return extractor(x, k) except: _logger.exception("Error while extracting sorting keys for template {}".format(t)) return None return f extractor = create_safe_extractor(template_sorting[t]["key_extractor"]) sort_key = template_sorting[t]["key"] sorted_missing = sorted(missing_in_order, key=lambda x: extractor(templates[t]["entries"][x], sort_key)) if template_sorting[t]["add"] == "prepend": templates[t]["order"] = sorted_missing + templates[t]["order"] elif template_sorting[t]["add"] == "append": templates[t]["order"] += sorted_missing elif template_sorting[t]["add"] == "custom_prepend" and "custom_add_entries" in template_sorting[t] and "custom_add_order" in template_sorting[t]: templates[t]["entries"].update(template_sorting[t]["custom_add_entries"](sorted_missing)) templates[t]["order"] = template_sorting[t]["custom_add_order"](sorted_missing) + templates[t]["order"] elif template_sorting[t]["add"] == "custom_append" and "custom_add_entries" in template_sorting[t] and "custom_add_order" in template_sorting[t]: templates[t]["entries"].update(template_sorting[t]["custom_add_entries"](sorted_missing)) templates[t]["order"] += template_sorting[t]["custom_add_order"](sorted_missing) #~~ prepare full set of template vars for rendering now = datetime.datetime.utcnow() # Removed the hasBeenCustomized verification to allow custom user for beepanel first_run = settings().getBoolean(["server", "firstRun"]) and userManager.enabled # and not userManager.hasBeenCustomized() render_kwargs = dict( webcamStream=settings().get(["webcam", "stream"]), enableTemperatureGraph=settings().get(["feature", "temperatureGraph"]), enableAccessControl=userManager.enabled, 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, now=now, supportedExtensions=map(lambda ext: ".{}".format(ext), get_all_extensions()) ) render_kwargs.update(plugin_vars) #~~ render! response = make_response(render_template( "index.jinja2", **render_kwargs )) if first_run: response = util.flask.add_non_caching_response_headers(response) return response
def index(): #~~ a bunch of settings 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 enable_accesscontrol = userManager.enabled 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 templates = defaultdict(lambda: dict(order=[], entries=dict())) # rules for transforming template configs to template entries template_rules = dict( navbar=dict(div=lambda x: "navbar_plugin_" + x, template=lambda x: x + "_navbar.jinja2", to_entry=lambda data: data), sidebar=dict(div=lambda x: "sidebar_plugin_" + x, template=lambda x: x + "_sidebar.jinja2", to_entry=lambda data: (data["name"], data)), tab=dict(div=lambda x: "tab_plugin_" + x, template=lambda x: x + "_tab.jinja2", to_entry=lambda data: (data["name"], data)), settings=dict(div=lambda x: "settings_plugin_" + x, template=lambda x: x + "_settings.jinja2", to_entry=lambda data: (data["name"], data)), usersettings=dict(div=lambda x: "usersettings_plugin_" + x, template=lambda x: x + "_usersettings.jinja2", to_entry=lambda data: (data["name"], data)), about=dict(div=lambda x: "about_plugin_" + x, template=lambda x: x + "_about.jinja2", to_entry=lambda data: (data["name"], data)), generic=dict(template=lambda x: x + ".jinja2", to_entry=lambda data: data) ) # sorting orders template_sorting = dict( navbar=dict(add="prepend", key=None), sidebar=dict(add="append", key="name"), tab=dict(add="append", key="name"), settings=dict(add="custom_append", key="name", custom_add_entries=lambda missing: dict(section_plugins=(gettext("Plugins"), None)), custom_add_order=lambda missing: ["section_plugins"] + missing), usersettings=dict(add="append", key="name"), about=dict(add="append", key="name"), generic=dict(add="append", key=None) ) hooks = pluginManager.get_hooks("octoprint.ui.web.templatetypes") for name, hook in hooks.items(): try: result = hook(dict(template_sorting), dict(template_rules)) except: _logger.exception("Error while retrieving custom template type definitions from plugin {name}".format(**locals())) else: if not isinstance(result, list): continue for entry in result: if not isinstance(entry, tuple) or not len(entry) == 3: continue key, order, rule = entry # order defaults if "add" not in order: order["add"] = "prepend" if "key" not in order: order["key"] = "name" # rule defaults if "div" not in rule: # default div name: <hook plugin>_<template_key>_plugin_<plugin> div = "{name}_{key}_plugin_".format(**locals()) rule["div"] = lambda x: div + x if "template" not in rule: # default template name: <plugin>_plugin_<hook plugin>_<template key>.jinja2 template = "_plugin_{name}_{key}.jinja2".format(**locals()) rule["template"] = lambda x: x + template if "to_entry" not in rule: # default to_entry assumes existing "name" property to be used as label for 2-tuple entry data structure (<name>, <properties>) rule["to_entry"] = lambda data: (data["name"], data) template_rules["plugin_" + name + "_" + key] = rule template_sorting["plugin_" + name + "_" + key] = order template_types = template_rules.keys() # navbar templates["navbar"]["entries"] = dict( settings=dict(template="navbar/settings.jinja2", _div="navbar_settings", styles=["display: none"], data_bind="visible: loginState.isAdmin") ) if enable_accesscontrol: templates["navbar"]["entries"]["login"] = dict(template="navbar/login.jinja2", _div="navbar_login", classes=["dropdown"], custom_bindings=False) if enable_systemmenu: templates["navbar"]["entries"]["systemmenu"] = dict(template="navbar/systemmenu.jinja2", _div="navbar_systemmenu", styles=["display: none"], classes=["dropdown"], data_bind="visible: loginState.isAdmin", custom_bindings=False) # sidebar templates["sidebar"]["entries"]= dict( connection=(gettext("Connection"), dict(template="sidebar/connection.jinja2", _div="connection", icon="signal", styles_wrapper=["display: none"], data_bind="visible: loginState.isUser")), state=(gettext("State"), dict(template="sidebar/state.jinja2", _div="state", icon="info-sign")), files=(gettext("Files"), dict(template="sidebar/files.jinja2", _div="files", icon="list", classes_content=["overflow_visible"], template_header="sidebar/files_header.jinja2")) ) # tabs templates["tab"]["entries"] = dict( temperature=(gettext("Temperature"), dict(template="tabs/temperature.jinja2", _div="temp")), control=(gettext("Control"), dict(template="tabs/control.jinja2", _div="control")), terminal=(gettext("Terminal"), dict(template="tabs/terminal.jinja2", _div="term")), ) if enable_gcodeviewer: templates["tab"]["entries"]["gcodeviewer"] = (gettext("GCode Viewer"), dict(template="tabs/gcodeviewer.jinja2", _div="gcode")) if enable_timelapse: templates["tab"]["entries"]["timelapse"] = (gettext("Timelapse"), dict(template="tabs/timelapse.jinja2", _div="timelapse")) # settings dialog templates["settings"]["entries"] = dict( section_printer=(gettext("Printer"), None), serial=(gettext("Serial Connection"), dict(template="dialogs/settings/serialconnection.jinja2", _div="settings_serialConnection", custom_bindings=False)), printerprofiles=(gettext("Printer Profiles"), dict(template="dialogs/settings/printerprofiles.jinja2", _div="settings_printerProfiles", custom_bindings=False)), temperatures=(gettext("Temperatures"), dict(template="dialogs/settings/temperatures.jinja2", _div="settings_temperature", custom_bindings=False)), terminalfilters=(gettext("Terminal Filters"), dict(template="dialogs/settings/terminalfilters.jinja2", _div="settings_terminalFilters", custom_bindings=False)), gcodescripts=(gettext("GCODE Scripts"), dict(template="dialogs/settings/gcodescripts.jinja2", _div="settings_gcodeScripts", custom_bindings=False)), section_features=(gettext("Features"), None), features=(gettext("Features"), dict(template="dialogs/settings/features.jinja2", _div="settings_features", custom_bindings=False)), webcam=(gettext("Webcam"), dict(template="dialogs/settings/webcam.jinja2", _div="settings_webcam", custom_bindings=False)), api=(gettext("API"), dict(template="dialogs/settings/api.jinja2", _div="settings_api", custom_bindings=False)), section_octoprint=(gettext("OctoPrint"), None), folders=(gettext("Folders"), dict(template="dialogs/settings/folders.jinja2", _div="settings_folders", custom_bindings=False)), appearance=(gettext("Appearance"), dict(template="dialogs/settings/appearance.jinja2", _div="settings_appearance", custom_bindings=False)), logs=(gettext("Logs"), dict(template="dialogs/settings/logs.jinja2", _div="settings_logs")), server=(gettext("Server"), dict(template="dialogs/settings/server.jinja2", _div="settings_server", custom_bindings=False)), ) if enable_accesscontrol: templates["settings"]["entries"]["accesscontrol"] = (gettext("Access Control"), dict(template="dialogs/settings/accesscontrol.jinja2", _div="settings_users", custom_bindings=False)) # user settings dialog if enable_accesscontrol: templates["usersettings"]["entries"] = dict( access=(gettext("Access"), dict(template="dialogs/usersettings/access.jinja2", _div="usersettings_access", custom_bindings=False)), interface=(gettext("Interface"), dict(template="dialogs/usersettings/interface.jinja2", _div="usersettings_interface", custom_bindings=False)), ) # about dialog templates["about"]["entries"] = dict( about=("About OctoPrint", dict(template="dialogs/about/about.jinja2", _div="about_about", custom_bindings=False)), license=("OctoPrint License", dict(template="dialogs/about/license.jinja2", _div="about_license", custom_bindings=False)), thirdparty=("Third Party Licenses", dict(template="dialogs/about/thirdparty.jinja2", _div="about_thirdparty", custom_bindings=False)), authors=("Authors", dict(template="dialogs/about/authors.jinja2", _div="about_authors", custom_bindings=False)), changelog=("Changelog", dict(template="dialogs/about/changelog.jinja2", _div="about_changelog", custom_bindings=False)), supporters=("Supporters", dict(template="dialogs/about/supporters.jinja2", _div="about_sponsors", custom_bindings=False)) ) # extract data from template plugins template_plugins = pluginManager.get_implementations(octoprint.plugin.TemplatePlugin) plugin_vars = dict() plugin_names = set() for implementation in template_plugins: name = implementation._identifier plugin_names.add(name) try: vars = implementation.get_template_vars() configs = implementation.get_template_configs() except: _logger.exception("Error while retrieving template data for plugin {}, ignoring it".format(name)) continue if not isinstance(vars, dict): vars = dict() if not isinstance(configs, (list, tuple)): configs = [] for var_name, var_value in vars.items(): plugin_vars["plugin_" + name + "_" + var_name] = var_value includes = _process_template_configs(name, implementation, configs, template_rules) for t in template_types: for include in includes[t]: if t == "navbar" or t == "generic": data = include else: data = include[1] key = data["_key"] if "replaces" in data: key = data["replaces"] templates[t]["entries"][key] = include #~~ order internal templates and plugins # make sure that # 1) we only have keys in our ordered list that we have entries for and # 2) we have all entries located somewhere within the order for t in template_types: default_order = settings().get(["appearance", "components", "order", t], merged=True, config=dict()) or [] configured_order = settings().get(["appearance", "components", "order", t], merged=True) or [] configured_disabled = settings().get(["appearance", "components", "disabled", t]) or [] # first create the ordered list of all component ids according to the configured order templates[t]["order"] = [x for x in configured_order if x in templates[t]["entries"] and not x in configured_disabled] # now append the entries from the default order that are not already in there templates[t]["order"] += [x for x in default_order if not x in templates[t]["order"] and x in templates[t]["entries"] and not x in configured_disabled] all_ordered = set(templates[t]["order"]) all_disabled = set(configured_disabled) # check if anything is missing, if not we are done here missing_in_order = set(templates[t]["entries"].keys()).difference(all_ordered).difference(all_disabled) if len(missing_in_order) == 0: continue # finally add anything that's not included in our order yet sorted_missing = list(missing_in_order) if template_sorting[t]["key"] is not None: # default extractor: works with entries that are dicts and entries that are 2-tuples with the # entry data at index 1 def extractor(item, key): if isinstance(item, dict) and key in item: return item[key] elif isinstance(item, tuple) and len(item) > 1 and isinstance(item[1], dict) and key in item[1]: return item[1][key] return None # if template type provides custom extractor, make sure its exceptions are handled if "key_extractor" in template_sorting[t] and callable(template_sorting[t]["key_extractor"]): def create_safe_extractor(extractor): def f(x, k): try: return extractor(x, k) except: _logger.exception("Error while extracting sorting keys for template {}".format(t)) return None return f extractor = create_safe_extractor(template_sorting[t]["key_extractor"]) sort_key = template_sorting[t]["key"] sorted_missing = sorted(missing_in_order, key=lambda x: extractor(templates[t]["entries"][x], sort_key)) if template_sorting[t]["add"] == "prepend": templates[t]["order"] = sorted_missing + templates[t]["order"] elif template_sorting[t]["add"] == "append": templates[t]["order"] += sorted_missing elif template_sorting[t]["add"] == "custom_prepend" and "custom_add_entries" in template_sorting[t] and "custom_add_order" in template_sorting[t]: templates[t]["entries"].update(template_sorting[t]["custom_add_entries"](sorted_missing)) templates[t]["order"] = template_sorting[t]["custom_add_order"](sorted_missing) + templates[t]["order"] elif template_sorting[t]["add"] == "custom_append" and "custom_add_entries" in template_sorting[t] and "custom_add_order" in template_sorting[t]: templates[t]["entries"].update(template_sorting[t]["custom_add_entries"](sorted_missing)) templates[t]["order"] += template_sorting[t]["custom_add_order"](sorted_missing) #~~ prepare full set of template vars for rendering now = datetime.datetime.utcnow() first_run = settings().getBoolean(["server", "firstRun"]) and userManager.enabled and not userManager.hasBeenCustomized() render_kwargs = dict( webcamStream=settings().get(["webcam", "stream"]), enableTemperatureGraph=settings().get(["feature", "temperatureGraph"]), enableAccessControl=userManager.enabled, 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, now=now, supportedExtensions=map(lambda ext: ".{}".format(ext), get_all_extensions()) ) render_kwargs.update(plugin_vars) #~~ render! response = make_response(render_template( "index.jinja2", **render_kwargs )) if first_run: response = util.flask.add_non_caching_response_headers(response) return response