[request/session] refactor language handling: don't attempt to sync web/repo languages
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 10 Sep 2012 13:53:59 +0200
changeset 8538 00597256de18
parent 8537 e30d0a7f0087
child 8539 d32ab8570e5d
[request/session] refactor language handling: don't attempt to sync web/repo languages This decouple thing and make code easier to understand. Previous behaviour was fine w/ validation error that were translated on the server side, but isn't satisfying for notification and all. Now: * translations should be done on the ui side (see previous validation error refactoring) * consistent behaviour: each side (web/repo) is responsible to deal with its request/session of language, no weird interaction between them on this topic * drop on the way the 'session properties' api, used only for that matter. -> much simpler/cleaner/saner api The thing missing being a way for user to tell "My favorite language is xxx but I also want cw to consider my http language header)
dbapi.py
entities/authobjs.py
req.py
server/repository.py
server/session.py
web/request.py
--- a/dbapi.py	Mon Sep 10 14:00:09 2012 +0200
+++ b/dbapi.py	Mon Sep 10 13:53:59 2012 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -80,9 +80,8 @@
 
 
 class ConnectionProperties(object):
-    def __init__(self, cnxtype=None, lang=None, close=True, log=False):
+    def __init__(self, cnxtype=None, close=True, log=False):
         self.cnxtype = cnxtype or 'pyro'
-        self.lang = lang
         self.log_queries = log
         self.close_on_del = close
 
@@ -283,6 +282,8 @@
         return '<DBAPISession %r>' % self.sessionid
 
 class DBAPIRequest(RequestSessionBase):
+    #: Request language identifier eg: 'en'
+    lang = None
 
     def __init__(self, vreg, session=None):
         super(DBAPIRequest, self).__init__(vreg)
@@ -292,9 +293,6 @@
             self.translations = vreg.config.translations
         except AttributeError:
             self.translations = {}
-        #: Request language identifier eg: 'en'
-        self.lang = None
-        self.set_default_language(vreg)
         #: cache entities built during the request
         self._eid_cache = {}
         if session is not None:
@@ -304,6 +302,7 @@
             # established
             self.session = None
             self.cnx = self.user = _NeedAuthAccessMock()
+        self.set_default_language(vreg)
 
     def from_controller(self):
         return 'view'
@@ -317,7 +316,7 @@
             self.cnx = session.cnx
             self.execute = session.cnx.cursor(self).execute
             if user is None:
-                user = self.cnx.user(self, {'lang': self.lang})
+                user = self.cnx.user(self)
         if user is not None:
             self.user = user
             self.set_entity_cache(user)
@@ -330,19 +329,15 @@
 
     def set_default_language(self, vreg):
         try:
-            self.lang = vreg.property_value('ui.language')
+            lang = vreg.property_value('ui.language')
         except Exception: # property may not be registered
-            self.lang = 'en'
-        # use req.__ to translate a message without registering it to the catalog
+            lang = 'en'
         try:
-            gettext, pgettext = self.translations[self.lang]
-            self._ = self.__ = gettext
-            self.pgettext = pgettext
+            self.set_language(lang)
         except KeyError:
             # this occurs usually during test execution
             self._ = self.__ = unicode
             self.pgettext = lambda x, y: unicode(y)
-        self.debug('request default language: %s', self.lang)
 
     # server-side service call #################################################
 
@@ -670,11 +665,6 @@
     # session data methods #####################################################
 
     @check_not_closed
-    def set_session_props(self, **props):
-        """raise `BadConnectionId` if the connection is no more valid"""
-        self._repo.set_session_props(self.sessionid, props)
-
-    @check_not_closed
     def get_shared_data(self, key, default=None, pop=False, txdata=False):
         """return value associated to key in the session's data dictionary or
         session's transaction's data if `txdata` is true.
--- a/entities/authobjs.py	Mon Sep 10 14:00:09 2012 +0200
+++ b/entities/authobjs.py	Mon Sep 10 13:53:59 2012 +0200
@@ -77,6 +77,19 @@
             self._properties = dict((p.pkey, p.value) for p in self.reverse_for_user)
             return self._properties
 
+    def prefered_language(self, language=None):
+        """return language used by this user, if explicitly defined (eg not
+        using http negociation)
+        """
+        language = language or self.property_value('ui.language')
+        vreg = self._cw.vreg
+        try:
+            vreg.config.translations[language]
+        except KeyError:
+            language = vreg.property_value('ui.language')
+            assert language in vreg.config.translations[language], language
+        return language
+
     def property_value(self, key):
         try:
             # properties stored on the user aren't correctly typed
--- a/req.py	Mon Sep 10 14:00:09 2012 +0200
+++ b/req.py	Mon Sep 10 13:53:59 2012 +0200
@@ -77,6 +77,17 @@
         self.local_perm_cache = {}
         self._ = unicode
 
+    def set_language(self, lang):
+        """install i18n configuration for `lang` translation.
+
+        Raises :exc:`KeyError` if translation doesn't exist.
+        """
+        self.lang = lang
+        gettext, pgettext = self.vreg.config.translations[lang]
+        # use _cw.__ to translate a message without registering it to the catalog
+        self._ = self.__ = gettext
+        self.pgettext = pgettext
+
     def property_value(self, key):
         """return value of the property with the given key, giving priority to
         user specific value if any, else using site value
--- a/server/repository.py	Mon Sep 10 14:00:09 2012 +0200
+++ b/server/repository.py	Mon Sep 10 13:53:59 2012 +0200
@@ -747,10 +747,10 @@
         raise `AuthenticationError` if the authentication failed
         raise `ConnectionError` if we can't open a connection
         """
+        cnxprops = kwargs.pop('cnxprops', None)
         # use an internal connection
         with self.internal_session() as session:
             # try to get a user object
-            cnxprops = kwargs.pop('cnxprops', None)
             user = self.authenticate_user(session, login, **kwargs)
         session = Session(user, self, cnxprops)
         user._cw = user.cw_rset.req = session
@@ -901,22 +901,9 @@
         * update user information on each user's request (i.e. groups and
           custom properties)
         """
-        session = self._get_session(sessionid, setcnxset=False)
-        if props is not None:
-            self.set_session_props(sessionid, props)
-        user = session.user
+        user = self._get_session(sessionid, setcnxset=False).user
         return user.eid, user.login, user.groups, user.properties
 
-    def set_session_props(self, sessionid, props):
-        """this method should be used by client to:
-        * check session id validity
-        * update user information on each user's request (i.e. groups and
-          custom properties)
-        """
-        session = self._get_session(sessionid, setcnxset=False)
-        for prop, value in props.items():
-            session.change_property(prop, value)
-
     def undoable_transactions(self, sessionid, ueid=None, txid=None,
                               **actionfilters):
         """See :class:`cubicweb.dbapi.Connection.undoable_transactions`"""
--- a/server/session.py	Mon Sep 10 14:00:09 2012 +0200
+++ b/server/session.py	Mon Sep 10 13:53:59 2012 +0200
@@ -265,7 +265,7 @@
         # and the rql server
         self.data = {}
         # i18n initialization
-        self.set_language(cnxprops.lang)
+        self.set_language(user.prefered_language())
         # internals
         self._tx_data = {}
         self.__threaddata = threading.local()
@@ -463,28 +463,6 @@
             self.cnxset.reconnect(source)
             return source.doexec(self, sql, args, rollback=rollback_on_failure)
 
-    def set_language(self, language):
-        """i18n configuration for translation"""
-        language = language or self.user.property_value('ui.language')
-        try:
-            gettext, pgettext = self.vreg.config.translations[language]
-            self._ = self.__ = gettext
-            self.pgettext = pgettext
-        except KeyError:
-            language = self.vreg.property_value('ui.language')
-            try:
-                gettext, pgettext = self.vreg.config.translations[language]
-                self._ = self.__ = gettext
-                self.pgettext = pgettext
-            except KeyError:
-                self._ = self.__ = unicode
-                self.pgettext = lambda x, y: y
-        self.lang = language
-
-    def change_property(self, prop, value):
-        assert prop == 'lang' # this is the only one changeable property for now
-        self.set_language(value)
-
     def deleted_in_transaction(self, eid):
         """return True if the entity of the given eid is being deleted in the
         current transaction
--- a/web/request.py	Mon Sep 10 14:00:09 2012 +0200
+++ b/web/request.py	Mon Sep 10 13:53:59 2012 +0200
@@ -226,14 +226,6 @@
         # 3. default language
         self.set_default_language(vreg)
 
-    def set_language(self, lang):
-        gettext, self.pgettext = self.translations[lang]
-        self._ = self.__ = gettext
-        self.lang = lang
-        self.debug('request language: %s', lang)
-        if self.cnx:
-            self.cnx.set_session_props(lang=lang)
-
     # input form parameters management ########################################
 
     # common form parameters which should be protected against html values