gettext interface cleaned up

error handling improved
This commit is contained in:
lars 2006-11-29 14:02:33 +00:00
parent d1c4835d8e
commit 210c27a495
7 changed files with 102 additions and 92 deletions

View file

@ -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"]

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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"]

View 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)')),
}

View file

@ -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"))