gettext interface cleaned up
error handling improved
This commit is contained in:
parent
d1c4835d8e
commit
210c27a495
7 changed files with 102 additions and 92 deletions
|
@ -203,21 +203,6 @@ class CryptoBoxProps(CryptoBox):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def getAvailableLanguages(self):
|
|
||||||
'''reads all files in path LangDir and returns a list of
|
|
||||||
basenames from existing hdf files, that should are all available
|
|
||||||
languages'''
|
|
||||||
# TODO: for now we hardcode it - change this!
|
|
||||||
return "en fr si de".split(" ")
|
|
||||||
# TODO: old implementation (before gettext) - remove it
|
|
||||||
languages = [ f.rstrip(".hdf")
|
|
||||||
for f in os.listdir(self.prefs["Locations"]["LangDir"])
|
|
||||||
if f.endswith(".hdf") ]
|
|
||||||
if len(languages) < 1:
|
|
||||||
self.log.error("No .hdf files found! The website won't render properly.")
|
|
||||||
return languages
|
|
||||||
|
|
||||||
|
|
||||||
def sendEventNotification(self, event, event_infos):
|
def sendEventNotification(self, event, event_infos):
|
||||||
"""call all available scripts in the event directory with some event information"""
|
"""call all available scripts in the event directory with some event information"""
|
||||||
event_dir = self.prefs["Locations"]["EventDir"]
|
event_dir = self.prefs["Locations"]["EventDir"]
|
||||||
|
|
|
@ -355,7 +355,7 @@ DefaultVolumePrefix = string(min=1)
|
||||||
DefaultCipher = string(default="aes-cbc-essiv:sha256")
|
DefaultCipher = string(default="aes-cbc-essiv:sha256")
|
||||||
ConfigVolumeLabel = string(min=1, default="cbox_config")
|
ConfigVolumeLabel = string(min=1, default="cbox_config")
|
||||||
UseConfigPartition = integer(min=0, max=1, default=0)
|
UseConfigPartition = integer(min=0, max=1, default=0)
|
||||||
DisabledPlugins = list(default=[])
|
DisabledPlugins = list(default=list())
|
||||||
|
|
||||||
[Locations]
|
[Locations]
|
||||||
MountParentDir = directoryExists(default="/var/cache/cryptobox-server/mnt")
|
MountParentDir = directoryExists(default="/var/cache/cryptobox-server/mnt")
|
||||||
|
@ -373,7 +373,7 @@ Details = string(min=1)
|
||||||
|
|
||||||
[WebSettings]
|
[WebSettings]
|
||||||
Stylesheet = string(min=1)
|
Stylesheet = string(min=1)
|
||||||
Language = string(min=1, default="en")
|
Languages = list(min=1,default=list("en"))
|
||||||
|
|
||||||
[Programs]
|
[Programs]
|
||||||
cryptsetup = fileExecutable(default="/sbin/cryptsetup")
|
cryptsetup = fileExecutable(default="/sbin/cryptsetup")
|
||||||
|
|
|
@ -29,11 +29,12 @@ class CryptoBoxPlugin:
|
||||||
fallbackIconFileName = "plugin_icon_unknown.gif"
|
fallbackIconFileName = "plugin_icon_unknown.gif"
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, cbox, pluginDir):
|
def __init__(self, cbox, pluginDir, siteClass=None):
|
||||||
self.cbox = cbox
|
self.cbox = cbox
|
||||||
self.hdf = {}
|
self.hdf = {}
|
||||||
self.pluginDir = pluginDir
|
self.pluginDir = pluginDir
|
||||||
self.hdf_prefix = "Data.Plugins.%s." % self.getName()
|
self.hdf_prefix = "Data.Plugins.%s." % self.getName()
|
||||||
|
self.site = siteClass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,10 @@ import logging
|
||||||
class PluginManager:
|
class PluginManager:
|
||||||
"""manage available plugins"""
|
"""manage available plugins"""
|
||||||
|
|
||||||
def __init__(self, cbox, plugin_dirs="."):
|
def __init__(self, cbox, plugin_dirs=".", siteClass=None):
|
||||||
self.cbox = cbox
|
self.cbox = cbox
|
||||||
self.log = logging.getLogger("CryptoBox")
|
self.log = logging.getLogger("CryptoBox")
|
||||||
|
self.site = siteClass
|
||||||
if hasattr(plugin_dirs, "__iter__"):
|
if hasattr(plugin_dirs, "__iter__"):
|
||||||
self.plugin_dirs = [os.path.abspath(dir) for dir in plugin_dirs]
|
self.plugin_dirs = [os.path.abspath(dir) for dir in plugin_dirs]
|
||||||
else:
|
else:
|
||||||
|
@ -43,7 +44,7 @@ class PluginManager:
|
||||||
pl_class = getattr(imp.load_source(name, plfile), name)
|
pl_class = getattr(imp.load_source(name, plfile), name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
return pl_class(self.cbox, os.path.dirname(plfile))
|
return pl_class(self.cbox, os.path.dirname(plfile), self.site)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -22,13 +22,19 @@ class WebInterfaceDataset(dict):
|
||||||
def setCryptoBoxState(self):
|
def setCryptoBoxState(self):
|
||||||
import cherrypy
|
import cherrypy
|
||||||
import cryptobox.core.main
|
import cryptobox.core.main
|
||||||
|
import cryptobox.web.languages
|
||||||
self["Data.Version"] = cryptobox.core.main.VERSION
|
self["Data.Version"] = cryptobox.core.main.VERSION
|
||||||
langs = self.cbox.getAvailableLanguages()
|
langs = self.cbox.prefs["WebSettings"]["Languages"]
|
||||||
langs.sort()
|
langs.sort()
|
||||||
for (index, lang) in enumerate(langs):
|
for (index, lang) in enumerate(langs):
|
||||||
self.cbox.log.info("language loaded: %s" % lang)
|
try:
|
||||||
self["Data.Languages.%d.name" % index] = lang
|
(langname, plural_info) = cryptobox.web.languages.LANGUAGE_INFO[lang]
|
||||||
self["Data.Languages.%d.link" % index] = self.__getLanguageName(lang)
|
self["Data.Languages.%d.link" % index] = langname
|
||||||
|
self["Data.Languages.%d.name" % index] = lang
|
||||||
|
self.cbox.log.info("language loaded: %s" % lang)
|
||||||
|
except KeyError:
|
||||||
|
## language was not found
|
||||||
|
self.cbox.log.warn("invalid language specified in configuration: %s" % lang)
|
||||||
try:
|
try:
|
||||||
self["Data.ScriptURL.Prot"] = cherrypy.request.scheme
|
self["Data.ScriptURL.Prot"] = cherrypy.request.scheme
|
||||||
host = cherrypy.request.headers["Host"]
|
host = cherrypy.request.headers["Host"]
|
||||||
|
@ -122,7 +128,7 @@ class WebInterfaceDataset(dict):
|
||||||
self["Settings.LanguageDir"] = os.path.abspath(self.prefs["Locations"]["LangDir"])
|
self["Settings.LanguageDir"] = os.path.abspath(self.prefs["Locations"]["LangDir"])
|
||||||
self["Settings.DocDir"] = os.path.abspath(self.prefs["Locations"]["DocDir"])
|
self["Settings.DocDir"] = os.path.abspath(self.prefs["Locations"]["DocDir"])
|
||||||
self["Settings.Stylesheet"] = self.prefs["WebSettings"]["Stylesheet"]
|
self["Settings.Stylesheet"] = self.prefs["WebSettings"]["Stylesheet"]
|
||||||
self["Settings.Language"] = self.prefs["WebSettings"]["Language"]
|
self["Settings.Language"] = self.prefs["WebSettings"]["Languages"][0]
|
||||||
self["Settings.PluginDir"] = self.prefs["Locations"]["PluginDir"]
|
self["Settings.PluginDir"] = self.prefs["Locations"]["PluginDir"]
|
||||||
self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"]
|
self["Settings.SettingsDir"] = self.prefs["Locations"]["SettingsDir"]
|
||||||
|
|
||||||
|
|
23
src/cryptobox/web/languages.py
Normal file
23
src/cryptobox/web/languages.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
"""supply information about existing languages
|
||||||
|
"""
|
||||||
|
|
||||||
|
## every language information should contain (name, pluralformat)
|
||||||
|
LANGUAGE_INFO = {
|
||||||
|
"cs": ('Český', ('3', '(n==1) ? 0 : (n>=2 && n< =4) ? 1 : 2')),
|
||||||
|
"da": ('Dansk', ('2', '(n != 1)')),
|
||||||
|
"de": ('Deutsch', ('2', '(n != 1)')),
|
||||||
|
"en": ('English', ('2', '(n != 1)')),
|
||||||
|
"es": ('Español', ('2', '(n != 1)')),
|
||||||
|
"fi": ('Suomi', ('2', '(n != 1)')),
|
||||||
|
"fr": ('Français', ('2', '(n != 1)')),
|
||||||
|
"hu": ('Magyar', ('1', '0')),
|
||||||
|
"it": ('Italiano', ('2', '(n != 1)')),
|
||||||
|
"ja": ('日本語', ('1', '0')),
|
||||||
|
"nl": ('Nederlands', ('2', '(n != 1)')),
|
||||||
|
"pl": ('Polski', ('3', '(n==1 ? 0 : n%10>=2 && n%10< =4 && (n%100<10 || n%100>=20) ? 1 : 2)')),
|
||||||
|
"pt": ('Português', ('2', '(n != 1)')),
|
||||||
|
"ru": ('Русский', ('3', '(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10< =4 && (n%100<10 || n%100>=20) ? 1 : 2)')),
|
||||||
|
"sl": ('Slovensko', ('4', '(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3)')),
|
||||||
|
"sv": ('Svenska', ('2', '(n != 1)')),
|
||||||
|
}
|
||||||
|
|
|
@ -46,6 +46,8 @@ class WebInterfaceSites:
|
||||||
self.__resetDataset()
|
self.__resetDataset()
|
||||||
## store the original http error handler
|
## store the original http error handler
|
||||||
self._cp_on_http_error = self.newHTTPErrorHandler
|
self._cp_on_http_error = self.newHTTPErrorHandler
|
||||||
|
## set initial language order
|
||||||
|
self.langOrder = self.cbox.prefs["WebSettings"]["Languages"]
|
||||||
|
|
||||||
|
|
||||||
def __resetDataset(self):
|
def __resetDataset(self):
|
||||||
|
@ -64,7 +66,7 @@ class WebInterfaceSites:
|
||||||
|
|
||||||
|
|
||||||
def __loadPlugins(self):
|
def __loadPlugins(self):
|
||||||
self.pluginList = cryptobox.plugins.manage.PluginManager(self.cbox, self.prefs["Locations"]["PluginDir"])
|
self.pluginList = cryptobox.plugins.manage.PluginManager(self.cbox, self.prefs["Locations"]["PluginDir"], self)
|
||||||
for plugin in self.pluginList.getPlugins():
|
for plugin in self.pluginList.getPlugins():
|
||||||
if not plugin: continue
|
if not plugin: continue
|
||||||
plname = plugin.getName()
|
plname = plugin.getName()
|
||||||
|
@ -134,6 +136,14 @@ class WebInterfaceSites:
|
||||||
return self.return_plugin_action(self.pluginList.getPlugin("disks"))(**param_dict)
|
return self.return_plugin_action(self.pluginList.getPlugin("disks"))(**param_dict)
|
||||||
|
|
||||||
def newHTTPErrorHandler(self, errorCode, message):
|
def newHTTPErrorHandler(self, errorCode, message):
|
||||||
|
"""handle http errors gracefully
|
||||||
|
|
||||||
|
404 - not found errors: ignored if url is below /cryptobox-misc/
|
||||||
|
other 404 errors: send the error code and return a nice informative page
|
||||||
|
500 - runtime errors: return "ok" exit code and show a polite excuse
|
||||||
|
others: are there any other possible http errors?
|
||||||
|
"""
|
||||||
|
import traceback, sys
|
||||||
## we ignore uninteresting not-found errors
|
## we ignore uninteresting not-found errors
|
||||||
if (errorCode == 404) and \
|
if (errorCode == 404) and \
|
||||||
(cherrypy.request.path.startswith("/cryptobox-misc/") or \
|
(cherrypy.request.path.startswith("/cryptobox-misc/") or \
|
||||||
|
@ -151,8 +161,10 @@ class WebInterfaceSites:
|
||||||
if errorCode == 500:
|
if errorCode == 500:
|
||||||
## we fix the error code (200 is "OK")
|
## we fix the error code (200 is "OK")
|
||||||
cherrypy.response.status = 200
|
cherrypy.response.status = 200
|
||||||
## TODO: 'message' is None - we should check a stack trace or something?
|
self.cbox.log.error("HTTP-ERROR[500] - a runtime error occoured: %s" % str(message))
|
||||||
self.cbox.log.warn("HTTP-ERROR[500] - a runtime error occoured: %s" % str(message))
|
## add a traceback and exception information to the lo
|
||||||
|
for a in traceback.format_exception(*sys.exc_info()):
|
||||||
|
self.cbox.log.error("\t%s" % a)
|
||||||
self.dataset["Data.Warning"] = "RuntimeError"
|
self.dataset["Data.Warning"] = "RuntimeError"
|
||||||
cherrypy.response.body = self.__render("empty")
|
cherrypy.response.body = self.__render("empty")
|
||||||
return
|
return
|
||||||
|
@ -285,7 +297,8 @@ class WebInterfaceSites:
|
||||||
|
|
||||||
examples are: non-https, readonly-config, ...
|
examples are: non-https, readonly-config, ...
|
||||||
"""
|
"""
|
||||||
## TODO: maybe add an option "mount" (if it is available, but inactive)?
|
## this check is done _after_ "resetDataSet" -> a possible config partition was
|
||||||
|
## loaded before
|
||||||
if self.cbox.prefs.requiresPartition() and not self.cbox.prefs.getActivePartition():
|
if self.cbox.prefs.requiresPartition() and not self.cbox.prefs.getActivePartition():
|
||||||
self.dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig"
|
self.dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig"
|
||||||
# TODO: turn this on soon (add "not") - for now it is annoying
|
# TODO: turn this on soon (add "not") - for now it is annoying
|
||||||
|
@ -311,31 +324,28 @@ class WebInterfaceSites:
|
||||||
|
|
||||||
|
|
||||||
def __setWebLang(self, value):
|
def __setWebLang(self, value):
|
||||||
guess = value
|
"""set the preferred priority of languages according to the following order:
|
||||||
availLangs = self.cbox.getAvailableLanguages()
|
1. language selected via web interface
|
||||||
## no language specified: check browser language
|
2. preferred browser language setting
|
||||||
if not guess:
|
3. languages defined in the config file
|
||||||
guess = self.__getPreferredBrowserLanguage(availLangs)
|
"""
|
||||||
## no preferred language or invalid language?
|
## start with the configured language order
|
||||||
if not guess \
|
langOrder = self.cbox.prefs["WebSettings"]["Languages"][:]
|
||||||
or not guess in availLangs \
|
## put the preferred browser language in front
|
||||||
or re.search(u'\W', guess):
|
guess = self.__getPreferredBrowserLanguage(langOrder)
|
||||||
## warn only for invalid languages
|
if guess:
|
||||||
if not guess is None:
|
langOrder.remove(guess)
|
||||||
self.cbox.log.info("invalid language choosen: %s" % guess)
|
langOrder.insert(0,guess)
|
||||||
guess = self.prefs["WebSettings"]["Language"]
|
## is the chosen language (via web interface) valid? - put it in front
|
||||||
## maybe the language is still not valid
|
if value and (value in langOrder) and (not re.search(u'\W',value)):
|
||||||
if not guess in availLangs:
|
langOrder.remove(value)
|
||||||
self.log.warn("the configured language is invalid: %s" % guess)
|
langOrder.insert(0,value)
|
||||||
guess = "en"
|
elif value:
|
||||||
## maybe there is no english dataset???
|
self.cbox.log.info("invalid language selected: %s" % value)
|
||||||
if not guess in availLangs:
|
## store current language setting
|
||||||
self.log.warn("couldn't find the english dataset")
|
self.langOrder = langOrder
|
||||||
guess = availLangs[0]
|
self.dataset["Settings.Language"] = langOrder[0]
|
||||||
self.dataset["Settings.Language"] = guess
|
self.dataset["Settings.LinkAttrs.weblang"] = langOrder[0]
|
||||||
## we only have to save it, if it was specified correctly and explicitly
|
|
||||||
if value == guess:
|
|
||||||
self.dataset["Settings.LinkAttrs.weblang"] = guess
|
|
||||||
|
|
||||||
|
|
||||||
def __getPreferredBrowserLanguage(self, availLangs):
|
def __getPreferredBrowserLanguage(self, availLangs):
|
||||||
|
@ -366,6 +376,8 @@ class WebInterfaceSites:
|
||||||
|
|
||||||
|
|
||||||
def __setDevice(self, device):
|
def __setDevice(self, device):
|
||||||
|
"""check a device name that was chosen via the web interface
|
||||||
|
issue a warning if the device is invalid"""
|
||||||
if device and re.match(u'[\w /\-]+$', device) and self.cbox.getContainer(device):
|
if device and re.match(u'[\w /\-]+$', device) and self.cbox.getContainer(device):
|
||||||
self.log.debug("select device: %s" % device)
|
self.log.debug("select device: %s" % device)
|
||||||
return True
|
return True
|
||||||
|
@ -375,19 +387,10 @@ class WebInterfaceSites:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def __checkVolumeName(self, name):
|
|
||||||
if name and re.match(u'[\w \-]+$', name):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def __getLanguageValue(self, value):
|
|
||||||
hdf = self.__getLanguageData(self.dataset["Settings.Language"])
|
|
||||||
return hdf.getValue(value, "")
|
|
||||||
|
|
||||||
|
|
||||||
def __substituteGettext(self, languages, textDomain, hdf):
|
def __substituteGettext(self, languages, textDomain, hdf):
|
||||||
|
"""substitute all texts in the hdf dataset with their translated
|
||||||
|
counterparts as returned by gettext
|
||||||
|
"""
|
||||||
import gettext
|
import gettext
|
||||||
try:
|
try:
|
||||||
translator = gettext.translation(textDomain, languages=languages)
|
translator = gettext.translation(textDomain, languages=languages)
|
||||||
|
@ -407,41 +410,32 @@ class WebInterfaceSites:
|
||||||
walk_tree(hdf)
|
walk_tree(hdf)
|
||||||
|
|
||||||
|
|
||||||
def __getLanguageData(self, web_lang="en"):
|
def __getLanguageData(self):
|
||||||
|
"""return the hdf dataset of the main interface and all plugins
|
||||||
|
translations are done according to self.langOrder
|
||||||
|
"""
|
||||||
|
## check if the language setting was changed - use cached data if possible
|
||||||
|
try:
|
||||||
|
if self.cachedLanguageData["langOrder"] == self.langOrder:
|
||||||
|
self.cbox.log.debug("using cached language data")
|
||||||
|
return self.cachedLanguageData["hdf"]
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
self.cbox.log.debug("generating language data")
|
||||||
hdf = neo_util.HDF()
|
hdf = neo_util.HDF()
|
||||||
hdf.readFile(os.path.join(self.prefs["Locations"]["TemplateDir"],"language.hdf"))
|
hdf.readFile(os.path.join(self.prefs["Locations"]["TemplateDir"],"language.hdf"))
|
||||||
self.__substituteGettext([web_lang], GETTEXT_DOMAIN, hdf)
|
self.__substituteGettext(self.langOrder, GETTEXT_DOMAIN, hdf)
|
||||||
## load the language data of all plugins
|
## load the language data of all plugins
|
||||||
for p in self.pluginList.getPlugins():
|
for p in self.pluginList.getPlugins():
|
||||||
pl_lang = p.getLanguageData()
|
pl_lang = p.getLanguageData()
|
||||||
self.__substituteGettext([web_lang], "%s-feature-%s" % (GETTEXT_DOMAIN, p.getName()), pl_lang)
|
self.__substituteGettext(self.langOrder, "%s-feature-%s" % (GETTEXT_DOMAIN, p.getName()), pl_lang)
|
||||||
hdf.copy("Plugins.%s" % p.getName(), pl_lang)
|
hdf.copy("Plugins.%s" % p.getName(), pl_lang)
|
||||||
self.cbox.log.debug("language data for plugin loaded: %s" % p.getName())
|
self.cbox.log.debug("language data for plugin loaded: %s" % p.getName())
|
||||||
|
## cache result for later retrieval
|
||||||
|
self.cachedLanguageData = {"langOrder": self.langOrder, "hdf": hdf}
|
||||||
return hdf
|
return hdf
|
||||||
|
|
||||||
|
|
||||||
def __getLanguageData2(self, web_lang="en"):
|
|
||||||
default_lang = "en"
|
|
||||||
conf_lang = self.prefs["WebSettings"]["Language"]
|
|
||||||
hdf = neo_util.HDF()
|
|
||||||
langDir = os.path.abspath(self.prefs["Locations"]["LangDir"])
|
|
||||||
langFiles = []
|
|
||||||
## first: read default language (en)
|
|
||||||
if (default_lang != conf_lang) and (default_lang != web_lang):
|
|
||||||
langFiles.append(os.path.join(langDir, default_lang + ".hdf"))
|
|
||||||
## second: read language as defined in the config file
|
|
||||||
if (conf_lang != web_lang):
|
|
||||||
langFiles.append(os.path.join(langDir, conf_lang + ".hdf"))
|
|
||||||
## third: read language as configured via web interface
|
|
||||||
langFiles.append(os.path.join(langDir, web_lang + ".hdf"))
|
|
||||||
for langFile in langFiles:
|
|
||||||
if os.access(langFile, os.R_OK):
|
|
||||||
hdf.readFile(langFile)
|
|
||||||
else:
|
|
||||||
log.warn("Couldn't read language file: %s" % langFile)
|
|
||||||
return hdf
|
|
||||||
|
|
||||||
|
|
||||||
def __render(self, renderInfo, plugin=None):
|
def __render(self, renderInfo, plugin=None):
|
||||||
'''renders from clearsilver templates and returns the resulting html
|
'''renders from clearsilver templates and returns the resulting html
|
||||||
'''
|
'''
|
||||||
|
@ -457,7 +451,7 @@ class WebInterfaceSites:
|
||||||
|
|
||||||
## load the language data
|
## load the language data
|
||||||
hdf = neo_util.HDF()
|
hdf = neo_util.HDF()
|
||||||
hdf.copy("Lang", self.__getLanguageData(self.dataset["Settings.Language"]))
|
hdf.copy("Lang", self.__getLanguageData())
|
||||||
|
|
||||||
## first: assume, that the template file is in the global template directory
|
## first: assume, that the template file is in the global template directory
|
||||||
self.dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join(self.prefs["Locations"]["TemplateDir"], template + ".cs"))
|
self.dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join(self.prefs["Locations"]["TemplateDir"], template + ".cs"))
|
||||||
|
|
Loading…
Reference in a new issue