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
|
||||
|
||||
|
||||
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):
|
||||
"""call all available scripts in the event directory with some event information"""
|
||||
event_dir = self.prefs["Locations"]["EventDir"]
|
||||
|
|
|
@ -355,7 +355,7 @@ DefaultVolumePrefix = string(min=1)
|
|||
DefaultCipher = string(default="aes-cbc-essiv:sha256")
|
||||
ConfigVolumeLabel = string(min=1, default="cbox_config")
|
||||
UseConfigPartition = integer(min=0, max=1, default=0)
|
||||
DisabledPlugins = list(default=[])
|
||||
DisabledPlugins = list(default=list())
|
||||
|
||||
[Locations]
|
||||
MountParentDir = directoryExists(default="/var/cache/cryptobox-server/mnt")
|
||||
|
@ -373,7 +373,7 @@ Details = string(min=1)
|
|||
|
||||
[WebSettings]
|
||||
Stylesheet = string(min=1)
|
||||
Language = string(min=1, default="en")
|
||||
Languages = list(min=1,default=list("en"))
|
||||
|
||||
[Programs]
|
||||
cryptsetup = fileExecutable(default="/sbin/cryptsetup")
|
||||
|
|
|
@ -29,11 +29,12 @@ class CryptoBoxPlugin:
|
|||
fallbackIconFileName = "plugin_icon_unknown.gif"
|
||||
|
||||
|
||||
def __init__(self, cbox, pluginDir):
|
||||
def __init__(self, cbox, pluginDir, siteClass=None):
|
||||
self.cbox = cbox
|
||||
self.hdf = {}
|
||||
self.pluginDir = pluginDir
|
||||
self.hdf_prefix = "Data.Plugins.%s." % self.getName()
|
||||
self.site = siteClass
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -8,9 +8,10 @@ import logging
|
|||
class PluginManager:
|
||||
"""manage available plugins"""
|
||||
|
||||
def __init__(self, cbox, plugin_dirs="."):
|
||||
def __init__(self, cbox, plugin_dirs=".", siteClass=None):
|
||||
self.cbox = cbox
|
||||
self.log = logging.getLogger("CryptoBox")
|
||||
self.site = siteClass
|
||||
if hasattr(plugin_dirs, "__iter__"):
|
||||
self.plugin_dirs = [os.path.abspath(dir) for dir in plugin_dirs]
|
||||
else:
|
||||
|
@ -43,7 +44,7 @@ class PluginManager:
|
|||
pl_class = getattr(imp.load_source(name, plfile), name)
|
||||
except AttributeError:
|
||||
return None
|
||||
return pl_class(self.cbox, os.path.dirname(plfile))
|
||||
return pl_class(self.cbox, os.path.dirname(plfile), self.site)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
|
|
@ -22,13 +22,19 @@ class WebInterfaceDataset(dict):
|
|||
def setCryptoBoxState(self):
|
||||
import cherrypy
|
||||
import cryptobox.core.main
|
||||
import cryptobox.web.languages
|
||||
self["Data.Version"] = cryptobox.core.main.VERSION
|
||||
langs = self.cbox.getAvailableLanguages()
|
||||
langs = self.cbox.prefs["WebSettings"]["Languages"]
|
||||
langs.sort()
|
||||
for (index, lang) in enumerate(langs):
|
||||
self.cbox.log.info("language loaded: %s" % lang)
|
||||
self["Data.Languages.%d.name" % index] = lang
|
||||
self["Data.Languages.%d.link" % index] = self.__getLanguageName(lang)
|
||||
try:
|
||||
(langname, plural_info) = cryptobox.web.languages.LANGUAGE_INFO[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:
|
||||
self["Data.ScriptURL.Prot"] = cherrypy.request.scheme
|
||||
host = cherrypy.request.headers["Host"]
|
||||
|
@ -122,7 +128,7 @@ class WebInterfaceDataset(dict):
|
|||
self["Settings.LanguageDir"] = os.path.abspath(self.prefs["Locations"]["LangDir"])
|
||||
self["Settings.DocDir"] = os.path.abspath(self.prefs["Locations"]["DocDir"])
|
||||
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.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()
|
||||
## store the original http error handler
|
||||
self._cp_on_http_error = self.newHTTPErrorHandler
|
||||
## set initial language order
|
||||
self.langOrder = self.cbox.prefs["WebSettings"]["Languages"]
|
||||
|
||||
|
||||
def __resetDataset(self):
|
||||
|
@ -64,7 +66,7 @@ class WebInterfaceSites:
|
|||
|
||||
|
||||
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():
|
||||
if not plugin: continue
|
||||
plname = plugin.getName()
|
||||
|
@ -134,6 +136,14 @@ class WebInterfaceSites:
|
|||
return self.return_plugin_action(self.pluginList.getPlugin("disks"))(**param_dict)
|
||||
|
||||
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
|
||||
if (errorCode == 404) and \
|
||||
(cherrypy.request.path.startswith("/cryptobox-misc/") or \
|
||||
|
@ -151,8 +161,10 @@ class WebInterfaceSites:
|
|||
if errorCode == 500:
|
||||
## we fix the error code (200 is "OK")
|
||||
cherrypy.response.status = 200
|
||||
## TODO: 'message' is None - we should check a stack trace or something?
|
||||
self.cbox.log.warn("HTTP-ERROR[500] - a runtime error occoured: %s" % str(message))
|
||||
self.cbox.log.error("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"
|
||||
cherrypy.response.body = self.__render("empty")
|
||||
return
|
||||
|
@ -285,7 +297,8 @@ class WebInterfaceSites:
|
|||
|
||||
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():
|
||||
self.dataset["Data.EnvironmentWarning"] = "ReadOnlyConfig"
|
||||
# TODO: turn this on soon (add "not") - for now it is annoying
|
||||
|
@ -311,31 +324,28 @@ class WebInterfaceSites:
|
|||
|
||||
|
||||
def __setWebLang(self, value):
|
||||
guess = value
|
||||
availLangs = self.cbox.getAvailableLanguages()
|
||||
## no language specified: check browser language
|
||||
if not guess:
|
||||
guess = self.__getPreferredBrowserLanguage(availLangs)
|
||||
## no preferred language or invalid language?
|
||||
if not guess \
|
||||
or not guess in availLangs \
|
||||
or re.search(u'\W', guess):
|
||||
## warn only for invalid languages
|
||||
if not guess is None:
|
||||
self.cbox.log.info("invalid language choosen: %s" % guess)
|
||||
guess = self.prefs["WebSettings"]["Language"]
|
||||
## maybe the language is still not valid
|
||||
if not guess in availLangs:
|
||||
self.log.warn("the configured language is invalid: %s" % guess)
|
||||
guess = "en"
|
||||
## maybe there is no english dataset???
|
||||
if not guess in availLangs:
|
||||
self.log.warn("couldn't find the english dataset")
|
||||
guess = availLangs[0]
|
||||
self.dataset["Settings.Language"] = guess
|
||||
## we only have to save it, if it was specified correctly and explicitly
|
||||
if value == guess:
|
||||
self.dataset["Settings.LinkAttrs.weblang"] = guess
|
||||
"""set the preferred priority of languages according to the following order:
|
||||
1. language selected via web interface
|
||||
2. preferred browser language setting
|
||||
3. languages defined in the config file
|
||||
"""
|
||||
## start with the configured language order
|
||||
langOrder = self.cbox.prefs["WebSettings"]["Languages"][:]
|
||||
## put the preferred browser language in front
|
||||
guess = self.__getPreferredBrowserLanguage(langOrder)
|
||||
if guess:
|
||||
langOrder.remove(guess)
|
||||
langOrder.insert(0,guess)
|
||||
## is the chosen language (via web interface) valid? - put it in front
|
||||
if value and (value in langOrder) and (not re.search(u'\W',value)):
|
||||
langOrder.remove(value)
|
||||
langOrder.insert(0,value)
|
||||
elif value:
|
||||
self.cbox.log.info("invalid language selected: %s" % value)
|
||||
## store current language setting
|
||||
self.langOrder = langOrder
|
||||
self.dataset["Settings.Language"] = langOrder[0]
|
||||
self.dataset["Settings.LinkAttrs.weblang"] = langOrder[0]
|
||||
|
||||
|
||||
def __getPreferredBrowserLanguage(self, availLangs):
|
||||
|
@ -366,6 +376,8 @@ class WebInterfaceSites:
|
|||
|
||||
|
||||
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):
|
||||
self.log.debug("select device: %s" % device)
|
||||
return True
|
||||
|
@ -375,19 +387,10 @@ class WebInterfaceSites:
|
|||
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):
|
||||
"""substitute all texts in the hdf dataset with their translated
|
||||
counterparts as returned by gettext
|
||||
"""
|
||||
import gettext
|
||||
try:
|
||||
translator = gettext.translation(textDomain, languages=languages)
|
||||
|
@ -407,41 +410,32 @@ class WebInterfaceSites:
|
|||
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.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
|
||||
for p in self.pluginList.getPlugins():
|
||||
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)
|
||||
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
|
||||
|
||||
|
||||
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):
|
||||
'''renders from clearsilver templates and returns the resulting html
|
||||
'''
|
||||
|
@ -457,7 +451,7 @@ class WebInterfaceSites:
|
|||
|
||||
## load the language data
|
||||
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
|
||||
self.dataset["Settings.TemplateFile"] = os.path.abspath(os.path.join(self.prefs["Locations"]["TemplateDir"], template + ".cs"))
|
||||
|
|
Loading…
Reference in a new issue