Module: check_mk
Branch: master
Commit: 08c1bc0e9114c6cb60d70c542c75e1fb2ee9878d
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=08c1bc0e9114c6…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Thu Jul 19 17:21:49 2018 +0200
6355 FIX Fix possible activation warning message about /etc/ssl/certs/localhost.crt certificate
During configuration activation the "trusted certificates file" var/ssl/ca-certificates.crt is
computed based on the configured global settings. In case the system certificates are trusted
all certificates in /etc/ssl/certs are read.
We found several RH/CentOS distros to have a /etc/ssl/certs/localhost.crt which seems to be some
kind of default certificate for local servers. The files may have a permission of 600 which makes
it not readable for the site user.
This results in an activation warning like this: ca-certificates: Failed to add certificate
'/etc/ssl/certs/localhost.crt' to trusted CA certificates. See web.log for details and these
entries in the var/log/web.log:
2018-06-21 03:55:52,120 [40] [cmk.web 19066] /master/check_mk/wato.py Internal error: Traceback (most recent call last):
File "/omd/sites/master/share/check_mk/web/htdocs/watolib.py", line 501, in _get_system_wide_trusted_ca_certificates
trusted_cas.update(self._get_certificates_from_file(os.path.join(cert_path, entry)))
File "/omd/sites/master/share/check_mk/web/htdocs/watolib.py", line 514, in _get_certificates_from_file
return [ match.group(0) for match in self._PEM_RE.finditer(open(path).read()) ]
IOError: [Errno 13] Permission denied: '/etc/ssl/certs/localhost.crt'
Because this may be a standard configuration and affect a lot of users we decided to remove this
warning for the /etc/ssl/certs/localhost.crt.
In case you need this /etc/ssl/certs/localhost.crt to be added to the trusted CA certificates
simply chown it to 644. It is a public certificate and not a secret.
Change-Id: I7b9708929670a328085cb17dcb5b60fabbd62919
---
.werks/6355 | 33 +++++++++++++++++++++++++++++++++
cmk/gui/watolib.py | 17 +++++++++++++++--
2 files changed, 48 insertions(+), 2 deletions(-)
diff --git a/.werks/6355 b/.werks/6355
new file mode 100644
index 0000000..3d89147
--- /dev/null
+++ b/.werks/6355
@@ -0,0 +1,33 @@
+Title: Fix possible activation warning message about /etc/ssl/certs/localhost.crt certificate
+Level: 1
+Component: wato
+Compatible: compat
+Edition: cre
+Version: 1.5.0b9
+Date: 1532013352
+Class: fix
+
+During configuration activation the "trusted certificates file" var/ssl/ca-certificates.crt is
+computed based on the configured global settings. In case the system certificates are trusted
+all certificates in /etc/ssl/certs are read.
+
+We found several RH/CentOS distros to have a /etc/ssl/certs/localhost.crt which seems to be some
+kind of default certificate for local servers. The files may have a permission of 600 which makes
+it not readable for the site user.
+
+This results in an activation warning like this: ca-certificates: Failed to add certificate
+'/etc/ssl/certs/localhost.crt' to trusted CA certificates. See web.log for details and these
+entries in the var/log/web.log:
+
+ 2018-06-21 03:55:52,120 [40] [cmk.web 19066] /master/check_mk/wato.py Internal error: Traceback (most recent call last):
+ File "/omd/sites/master/share/check_mk/web/htdocs/watolib.py", line 501, in _get_system_wide_trusted_ca_certificates
+ trusted_cas.update(self._get_certificates_from_file(os.path.join(cert_path, entry)))
+ File "/omd/sites/master/share/check_mk/web/htdocs/watolib.py", line 514, in _get_certificates_from_file
+ return [ match.group(0) for match in self._PEM_RE.finditer(open(path).read()) ]
+IOError: [Errno 13] Permission denied: '/etc/ssl/certs/localhost.crt'
+
+Because this may be a standard configuration and affect a lot of users we decided to remove this
+warning for the /etc/ssl/certs/localhost.crt.
+
+In case you need this /etc/ssl/certs/localhost.crt to be added to the trusted CA certificates
+simply chown it to 644. It is a public certificate and not a secret.
diff --git a/cmk/gui/watolib.py b/cmk/gui/watolib.py
index 0e8b050..c93b028 100644
--- a/cmk/gui/watolib.py
+++ b/cmk/gui/watolib.py
@@ -529,16 +529,29 @@ class ConfigDomainCACertificates(ConfigDomain):
continue
for entry in os.listdir(cert_path):
+ cert_file_path = os.path.join(cert_path, entry)
try:
ext = os.path.splitext(entry)[-1]
if ext not in [ ".pem", ".crt" ]:
continue
- trusted_cas.update(self._get_certificates_from_file(os.path.join(cert_path, entry)))
+ trusted_cas.update(self._get_certificates_from_file(cert_file_path))
except IOError:
logger.exception()
+
+ # This error is shown to the user as warning message during "activate changes".
+ # We keep this message for the moment because we think that it is a helpful
+ # trigger for further checking web.log when a really needed certificate can
+ # not be read.
+ #
+ # We know a permission problem with some files that are created by default on
+ # some distros. We simply ignore these files because we assume that they are
+ # not needed.
+ if cert_file_path == "/etc/ssl/certs/localhost.crt":
+ continue
+
errors.append("Failed to add certificate '%s' to trusted CA certificates. "
- "See web.log for details." % os.path.join(cert_path, entry))
+ "See web.log for details." % cert_file_path)
break
Module: check_mk
Branch: master
Commit: 60a567a3d598302329b01d1157ddacbd133f2fa4
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=60a567a3d59830…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Mon Jul 23 08:03:26 2018 +0200
Refactored path access in ConfigDomainCACertificates to pathlib
Change-Id: I2be49963d3410505dee80a6d3e79b75c2ca5d4e0
---
cmk/gui/watolib.py | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/cmk/gui/watolib.py b/cmk/gui/watolib.py
index c93b028..9a055e1 100644
--- a/cmk/gui/watolib.py
+++ b/cmk/gui/watolib.py
@@ -524,15 +524,16 @@ class ConfigDomainCACertificates(ConfigDomain):
def _get_system_wide_trusted_ca_certificates(self):
trusted_cas, errors = set([]), []
- for cert_path in self.system_wide_trusted_ca_search_paths:
- if not os.path.isdir(cert_path):
+ for p in self.system_wide_trusted_ca_search_paths:
+ cert_path = Path(p)
+
+ if not cert_path.is_dir():
continue
- for entry in os.listdir(cert_path):
- cert_file_path = os.path.join(cert_path, entry)
+ for entry in cert_path.iterdir():
+ cert_file_path = entry.absolute()
try:
- ext = os.path.splitext(entry)[-1]
- if ext not in [ ".pem", ".crt" ]:
+ if entry.suffix not in [ ".pem", ".crt" ]:
continue
trusted_cas.update(self._get_certificates_from_file(cert_file_path))
@@ -547,7 +548,7 @@ class ConfigDomainCACertificates(ConfigDomain):
# We know a permission problem with some files that are created by default on
# some distros. We simply ignore these files because we assume that they are
# not needed.
- if cert_file_path == "/etc/ssl/certs/localhost.crt":
+ if cert_file_path == Path("/etc/ssl/certs/localhost.crt"):
continue
errors.append("Failed to add certificate '%s' to trusted CA certificates. "
@@ -560,7 +561,7 @@ class ConfigDomainCACertificates(ConfigDomain):
def _get_certificates_from_file(self, path):
try:
- return [ match.group(0) for match in self._PEM_RE.finditer(open(path).read()) ]
+ return [ match.group(0) for match in self._PEM_RE.finditer(open("%s" % path).read()) ]
except IOError, e:
if e.errno == 2: # No such file or directory
# Silently ignore e.g. dangling symlinks
Module: check_mk
Branch: master
Commit: 0ae551cccac14429a90c62194b820fd24bbbfb58
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=0ae551cccac144…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Mon Jul 23 09:53:17 2018 +0200
Fixed handling of exceptions with non ascii characters in messages
Exceptions may use str or unicode messages which both may contain
non ascii characters. The crash handling and exception logging did
not handle all cases correctly resuling in subsequent encoding or
decoding related exceptions.
The logging and crash handling code has now been changed to get the
message from the exception and decode it to a unicode string before
processing it.
Change-Id: I6a084fef8b07b98719a2549a7926946cb455d1b7
---
cmk/crash_reporting.py | 8 +++++++-
cmk/gui/crash_reporting.py | 9 ++++++++-
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/cmk/crash_reporting.py b/cmk/crash_reporting.py
index 4f17ffc..414ec73 100644
--- a/cmk/crash_reporting.py
+++ b/cmk/crash_reporting.py
@@ -79,6 +79,12 @@ def create_crash_info(crash_type, details=None, version=None):
if exc_type.__name__ == "MKParseFunctionError":
tb_list += traceback.extract_tb(exc_value.exc_info()[2])
+ # Unify different string types from exception messages to a unicode string
+ try:
+ exc_txt = unicode(exc_value)
+ except UnicodeDecodeError:
+ exc_txt = str(exc_value).decode("utf-8")
+
return {
"crash_type" : crash_type,
"time" : time.time(),
@@ -89,7 +95,7 @@ def create_crash_info(crash_type, details=None, version=None):
"python_version": sys.version,
"python_paths" : sys.path,
"exc_type" : exc_type.__name__,
- "exc_value" : "%s" % exc_value,
+ "exc_value" : exc_txt,
"exc_traceback" : tb_list,
"local_vars" : get_local_vars_of_last_exception(),
"details" : details,
diff --git a/cmk/gui/crash_reporting.py b/cmk/gui/crash_reporting.py
index 1f4ce7b..a9cd960 100644
--- a/cmk/gui/crash_reporting.py
+++ b/cmk/gui/crash_reporting.py
@@ -85,7 +85,14 @@ def page_crashed(what):
details = {}
if what == "gui":
- html.show_error("<b>%s:</b> %s" % (_("Internal error"), sys.exc_info()[1]))
+ # Unify different string types from exception messages to a unicode string
+ exc_value = sys.exc_info()[1]
+ try:
+ exc_txt = unicode(exc_value)
+ except UnicodeDecodeError:
+ exc_txt = str(exc_value).decode("utf-8")
+
+ html.show_error("<b>%s:</b> %s" % (_("Internal error"), exc_txt))
html.p(_("An internal error occured while processing your request. "
"You can report this issue to the Check_MK team to help "
"fixing this issue. Please use the form below for reporting."))
Module: check_mk
Branch: master
Commit: 0a3f17d05b8ef65be2b6b1ba3773cfafc2a14243
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=0a3f17d05b8ef6…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Mon Jul 23 09:47:32 2018 +0200
6361 FIX Fixed HTML fragments in customer column of views in PDF reports
When a view that contains the customer column e.g. of a host, this column
was containing HTML tags instead of just the customer name.
Change-Id: Ie3f02475e94fa2de08aeab4e36b781121a71d1f5
---
.werks/6361 | 12 ++++++++++++
cmk/gui/plugins/views/utils.py | 2 +-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/.werks/6361 b/.werks/6361
new file mode 100644
index 0000000..9d362c1
--- /dev/null
+++ b/.werks/6361
@@ -0,0 +1,12 @@
+Title: Fixed HTML fragments in customer column of views in PDF reports
+Level: 1
+Component: reporting
+Class: fix
+Compatible: compat
+Edition: cme
+State: unknown
+Version: 1.6.0i1
+Date: 1532196113
+
+When a view that contains the customer column e.g. of a host, this column
+was containing HTML tags instead of just the customer name.
diff --git a/cmk/gui/plugins/views/utils.py b/cmk/gui/plugins/views/utils.py
index 5fabbfa..2fac4e8 100644
--- a/cmk/gui/plugins/views/utils.py
+++ b/cmk/gui/plugins/views/utils.py
@@ -1321,7 +1321,7 @@ class Cell(object):
txt = img_filename
if isinstance(txt, HTML):
- txt = "%s" % txt
+ txt = html.strip_tags("%s" % txt)
elif not isinstance(txt, tuple):
txt = html.escaper.unescape_attributes(txt)
Module: check_mk
Branch: master
Commit: abdc673c713cb11d6877699dec3ee33014db7113
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=abdc673c713cb1…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Mon Jul 23 08:05:48 2018 +0200
6358 FIX Fixed stale services on cluster nodes
When using Check_MK clusters it could happen that some of the services
on a node went stale and remained in this state. In this situation you
can find multiple messages in the cmc.log when using the CEE:
2018-07-21 12:47:52 [5] [Check_MK helper 4644] started, commandline: /omd/sites/beta/bin/cmk --keepalive
2018-07-21 12:48:42 [2] [Check_MK helper 4644] ignoring check result for lxclu1 / Filesystem /boot: no such service
2018-07-21 12:48:43 [4] [Check_MK helper 4644] restarting dead helper
2018-07-21 12:48:43 [5] [Check_MK helper 4644] exited normally
The issue was triggered because the cluster nodes reported service check
results for services which are assigned to the cluster using the "Clustered
services" rule set.
The problem could only happen when services of one check type were assigned to
the cluster and the cluster node. For example in case you have at least one
Filesystem service assigned to the node and at least one Filesystem service
assigned to the cluster.
This regression was introduced with 1.5.0b7 (Werk #5814).
Change-Id: Ibddc6e489c21b8664164d1437878f7021186f740
---
.werks/6358 | 29 +++++++++++++++++++++++++++++
cmk_base/checking.py | 3 +++
2 files changed, 32 insertions(+)
diff --git a/.werks/6358 b/.werks/6358
new file mode 100644
index 0000000..84c6395
--- /dev/null
+++ b/.werks/6358
@@ -0,0 +1,29 @@
+Title: Fixed stale services on cluster nodes
+Level: 2
+Component: core
+Class: fix
+Compatible: compat
+Edition: cre
+State: unknown
+Version: 1.6.0i1
+Date: 1532171683
+
+When using Check_MK clusters it could happen that some of the services
+on a node went stale and remained in this state. In this situation you
+can find multiple messages in the cmc.log when using the CEE:
+
+2018-07-21 12:47:52 [5] [Check_MK helper 4644] started, commandline: /omd/sites/beta/bin/cmk --keepalive
+2018-07-21 12:48:42 [2] [Check_MK helper 4644] ignoring check result for lxclu1 / Filesystem /boot: no such service
+2018-07-21 12:48:43 [4] [Check_MK helper 4644] restarting dead helper
+2018-07-21 12:48:43 [5] [Check_MK helper 4644] exited normally
+
+The issue was triggered because the cluster nodes reported service check
+results for services which are assigned to the cluster using the "Clustered
+services" rule set.
+
+The problem could only happen when services of one check type were assigned to
+the cluster and the cluster node. For example in case you have at least one
+Filesystem service assigned to the node and at least one Filesystem service
+assigned to the cluster.
+
+This regression was introduced with 1.5.0b7 (Werk #5814).
diff --git a/cmk_base/checking.py b/cmk_base/checking.py
index a7362ba..8598ca8 100644
--- a/cmk_base/checking.py
+++ b/cmk_base/checking.py
@@ -197,6 +197,9 @@ def _do_all_checks_on_host(sources, hostname, ipaddress, only_check_plugin_names
if only_check_plugin_names != None and check_plugin_name not in only_check_plugin_names:
continue
+ if belongs_to_cluster and hostname != config.host_of_clustered_service(hostname, description):
+ continue
+
success = execute_check(multi_host_sections, hostname, ipaddress, check_plugin_name, item, params, description)
if success:
num_success += 1
Module: check_mk
Branch: master
Commit: 8945063120da22639e30d2c284f2813837b927e9
URL: http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=8945063120da22…
Author: Lars Michelsen <lm(a)mathias-kettner.de>
Date: Sat Jul 21 16:50:47 2018 +0200
Make simple GUI themes possible
* Theme is now configurable
a) Override the default theme for the the whole installation
b) Each user can change the theme in his user profile
A theme is created by adding a new theme directory to web/htdocs/themes
and placing a theme.css in this directory. Images must be placed in
the images sub directory.
To be able to choose the theme using the GUI it needs to be added to
the lists of themes in the valuespecs.
* Themes can now override standard icons
The icons need to be placed into web/htdocs/themes/[theme]/images
with the exact same name as the original below web/htdocs/images.
Simplified and documented icon path detection during this progress.
Change-Id: I5176401fc0a553c9c5985332af82ad26d35f3ba5
---
cmk/gui/htmllib.py | 48 ++++++++++++++++------
cmk/gui/plugins/config/base.py | 3 ++
cmk/gui/plugins/userdb/user_attributes.py | 31 ++++++++++++++
cmk/gui/plugins/wato/check_mk_configuration.py | 15 +++++++
.../check_mk/skel/etc/check_mk/apache.conf | 2 +-
web/app/index.wsgi | 3 ++
6 files changed, 89 insertions(+), 13 deletions(-)
diff --git a/cmk/gui/htmllib.py b/cmk/gui/htmllib.py
index f62279a..8d028b2 100644
--- a/cmk/gui/htmllib.py
+++ b/cmk/gui/htmllib.py
@@ -1081,6 +1081,7 @@ class html(HTMLGenerator):
# Settings
self.mobile = False
+ self._theme = None
# Forms
self.form_name = None
@@ -1129,6 +1130,15 @@ class html(HTMLGenerator):
self._init_screenshot_mode()
self._init_debug_mode()
+ self.init_theme()
+
+
+ def init_theme(self):
+ self.set_theme(config.ui_theme)
+
+
+ def set_theme(self, theme_id):
+ self._theme = theme_id or config.ui_theme
def _verify_not_using_threaded_mpm(self):
@@ -1781,6 +1791,11 @@ class html(HTMLGenerator):
gui_colors.GUIColors().render_html()
+ if self._theme and self._theme != "classic":
+ fname = self._css_filename_for_browser("themes/%s/theme" % self._theme)
+ self.stylesheet(fname)
+
+
def _plugin_stylesheets(self):
plugin_stylesheets = set([])
for dir in [ cmk.paths.web_dir + "/htdocs/css", cmk.paths.local_web_dir + "/htdocs/css" ]:
@@ -2760,20 +2775,29 @@ class html(HTMLGenerator):
def _detect_icon_path(self, icon_name):
- # Detect whether or not the icon is available as images/icon_*.png
- # or images/icons/*.png. When an icon is available as internal icon,
- # always use this one
- is_internal = False
- rel_path = "share/check_mk/web/htdocs/images/icon_"+icon_name+".png"
- if os.path.exists(cmk.paths.omd_root+"/"+rel_path):
- is_internal = True
- elif os.path.exists(cmk.paths.omd_root+"/local/"+rel_path):
- is_internal = True
+ """Detect from which place an icon shall be used and return it's path relative to
+ htdocs/
+
+ Priority:
+ 1. In case a theme is active: themes/images/icon_[name].png in site local hierarc
+hy
+ 2. In case a theme is active: themes/images/icon_[name].png in standard hierarchy
+ 3. images/icon_[name].png in site local hierarchy
+ 4. images/icon_[name].png in standard hierarchy
+ 5. images/icons/[name].png in site local hierarchy
+ 6. images/icons/[name].png in standard hierarchy
+ """
+
+ if self._theme and self._theme != "classic":
+ rel_path = "share/check_mk/web/htdocs/themes/%s/images/icon_%s.png" % (self._theme, icon_name)
+ if os.path.exists(cmk.paths.omd_root+"/"+rel_path) or os.path.exists(cmk.paths.omd_root+"/local/"+rel_path):
+ return "themes/%s/images/icon_%s.png" % (self._theme, icon_name)
- if is_internal:
+ rel_path = "share/check_mk/web/htdocs/images/icon_"+icon_name+".png"
+ if os.path.exists(cmk.paths.omd_root+"/"+rel_path) or os.path.exists(cmk.paths.omd_root+"/local/"+rel_path):
return "images/icon_%s.png" % icon_name
- else:
- return "images/icons/%s.png" % icon_name
+
+ return "images/icons/%s.png" % icon_name
def render_icon_button(self, url, help, icon, id=None, onclick=None,
diff --git a/cmk/gui/plugins/config/base.py b/cmk/gui/plugins/config/base.py
index 4c4a9fe..b2dda5a 100644
--- a/cmk/gui/plugins/config/base.py
+++ b/cmk/gui/plugins/config/base.py
@@ -211,6 +211,9 @@ service_view_grouping = []
# Custom user stylesheet to load (resides in htdocs/)
custom_style_sheet = None
+# UI theme to use
+ui_theme = "classic"
+
# URL for start page in main frame (welcome page)
start_url = "dashboard.py"
diff --git a/cmk/gui/plugins/userdb/user_attributes.py b/cmk/gui/plugins/userdb/user_attributes.py
index 03c4b25..9e91fbf 100644
--- a/cmk/gui/plugins/userdb/user_attributes.py
+++ b/cmk/gui/plugins/userdb/user_attributes.py
@@ -143,3 +143,34 @@ class StartURLUserAttribute(UserAttribute):
def domain(self):
return "multisite"
+
+
+
+class UIThemeUserAttribute(UserAttribute):
+ def name(self):
+ return "ui_theme"
+
+
+ def valuespec(self):
+ return Alternative(
+ title = _("User interface theme"),
+ style = "dropdown",
+ orientation = "horizontal",
+ elements = [
+ FixedValue(None,
+ title = _("Use the default theme"),
+ totext = "",
+ ),
+ DropdownChoice(
+ title = _("Set custom theme"),
+ choices = [
+ ("classic", _("Classic user interface")),
+ ("facelift", _("Face lifted")),
+ ],
+ ),
+ ],
+ )
+
+
+ def domain(self):
+ return "multisite"
diff --git a/cmk/gui/plugins/wato/check_mk_configuration.py b/cmk/gui/plugins/wato/check_mk_configuration.py
index 936d9d2..487cc7d 100644
--- a/cmk/gui/plugins/wato/check_mk_configuration.py
+++ b/cmk/gui/plugins/wato/check_mk_configuration.py
@@ -105,6 +105,21 @@ def web_log_level_elements():
return elements
+register_configvar(
+ group,
+ "ui_theme",
+ DropdownChoice(
+ title = _("User interface theme"),
+ help = _("Change the default user interface theme of your Check_MK installation"),
+ choices = [
+ ("classic", _("Classic user interface")),
+ ("facelift", _("Face lifted")),
+ ],
+ ),
+ domain = "multisite",
+)
+
+
register_configvar(group,
"bulk_discovery_default_settings",
vs_bulk_discovery(),
diff --git a/omd/packages/check_mk/skel/etc/check_mk/apache.conf b/omd/packages/check_mk/skel/etc/check_mk/apache.conf
index a9f789d..3c38b82 100644
--- a/omd/packages/check_mk/skel/etc/check_mk/apache.conf
+++ b/omd/packages/check_mk/skel/etc/check_mk/apache.conf
@@ -69,7 +69,7 @@ Alias /###SITE###/check_mk ###ROOT###/share/check_mk/web/htdocs
# the version strings. This is done with mod_rewrite.
# e.g. change http://localhost/event/check_mk/js/checkmk-2014.10.22.js to
# e.g. change http://localhost/event/check_mk/js/checkmk.js
- RewriteRule "^(js/|)([a-z0-9_]+)-(.+?)\.(js|css)$" "/###SITE###/check_mk/$1$2.$4"
+ RewriteRule "^(js/|themes/.*?/|)([a-z0-9_]+)-(.+?)\.(js|css)$" "/###SITE###/c heck_mk/$1$2.$4"
# Rewrite rules for handling the local-hierarchy. All URI to static
# files are redirected, if those exist in local/. Thus the user
diff --git a/web/app/index.wsgi b/web/app/index.wsgi
index dcd4605..e0cc0d1 100644
--- a/web/app/index.wsgi
+++ b/web/app/index.wsgi
@@ -190,6 +190,9 @@ class Application(object):
# has been initialized
self._localize_request()
+ # Update the UI theme with the attribute configured by the user
+ html.set_theme(config.user.get_attribute("ui_theme"))
+
self._ensure_general_access()
handler()