backport stable branch, take care a lot of conflicts occured, this may be the revision you're looking for... tls-sprint
authorsylvain.thenault@logilab.fr
Mon, 02 Mar 2009 21:03:54 +0100
branchtls-sprint
changeset 985 6a25c58a1c23
parent 984 536e421b082b (current diff)
parent 979 93447d75c4b9 (diff)
child 986 e24b45bf92cc
backport stable branch, take care a lot of conflicts occured, this may be the revision you're looking for...
common/entity.py
common/mixins.py
common/uilib.py
common/view.py
debian/control
entity.py
i18n/en.po
i18n/es.po
i18n/fr.po
server/session.py
test/unittest_schema.py
view.py
web/data/cubicweb.ajax.js
web/facet.py
web/test/unittest_views_baseforms.py
web/views/baseforms.py
web/views/baseviews.py
web/views/euser.py
web/views/idownloadable.py
web/views/tabs.py
web/views/treeview.py
--- a/.hgtags	Mon Mar 02 20:44:14 2009 +0100
+++ b/.hgtags	Mon Mar 02 21:03:54 2009 +0100
@@ -16,3 +16,5 @@
 fc222bc99929d395c1c2235c40d3bb6f247b4ba9 cubicweb-debian-version-3_0_4-1
 7ad527099393ef56f27af313392022bb8ed73082 cubicweb-version-3_0_9
 a8e9e53b245d53838a07aa8c76d1bed352692a9f cubicweb-debian-version-3_0_9-1
+a711c7c185d15a1bd22b7eaab46a26b98b74fbf3 cubicweb-version-3_1_0
+dd3efdf58d281286d6f52f7416db349b75b7789c cubicweb-debian-version-3_1_0-1
--- a/__pkginfo__.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/__pkginfo__.py	Mon Mar 02 21:03:54 2009 +0100
@@ -6,7 +6,7 @@
 distname = "cubicweb"
 modname = "cubicweb"
 
-numversion = (3, 0, 10)
+numversion = (3, 1, 0)
 version = '.'.join(str(num) for num in numversion)
 
 license = 'GPL'
--- a/common/mixins.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/common/mixins.py	Mon Mar 02 21:03:54 2009 +0100
@@ -175,14 +175,14 @@
         return self.req._(self.state)
 
     def wf_state(self, statename):
-        rset = self.req.execute('Any S, SN WHERE S name %(n)s, S state_of E, E name %(e)s',
+        rset = self.req.execute('Any S, SN WHERE S name SN, S name %(n)s, S state_of E, E name %(e)s',
                                 {'n': statename, 'e': str(self.e_schema)})
         if rset:
             return rset.get_entity(0, 0)
         return None
     
     def wf_transition(self, trname):
-        rset = self.req.execute('Any T, TN WHERE T name %(n)s, T transition_of E, E name %(e)s',
+        rset = self.req.execute('Any T, TN WHERE T name TN, T name %(n)s, T transition_of E, E name %(e)s',
                                 {'n': trname, 'e': str(self.e_schema)})
         if rset:
             return rset.get_entity(0, 0)
@@ -368,22 +368,18 @@
     """provide default implementations for IProgress interface methods"""
 
     @property
-    @cached
     def cost(self):
         return self.progress_info()['estimated']
 
     @property
-    @cached
     def revised_cost(self):
         return self.progress_info().get('estimatedcorrected', self.cost)
 
     @property
-    @cached
     def done(self):
         return self.progress_info()['done']
 
     @property
-    @cached
     def todo(self):
         return self.progress_info()['todo']
 
@@ -402,6 +398,6 @@
             return 100. * self.done / self.revised_cost
         except ZeroDivisionError:
             # total cost is 0 : if everything was estimated, task is completed
-            if self.progress_info().get('notestmiated'):
+            if self.progress_info().get('notestimated'):
                 return 0.
             return 100
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/test/unittest_mixins.py	Mon Mar 02 21:03:54 2009 +0100
@@ -0,0 +1,25 @@
+from logilab.common.testlib import unittest_main
+from cubicweb.devtools.apptest import EnvBasedTC
+
+class WorkfloableMixInTC(EnvBasedTC):
+    def test_wf_state(self):
+        s = self.add_entity('State', name=u'activated')
+        self.execute('SET X state_of ET WHERE ET name "Bookmark", X eid %(x)s',
+                     {'x': s.eid})
+        es = self.user().wf_state('activated')
+        self.assertEquals(es.state_of[0].name, 'EUser')
+        
+    def test_wf_transition(self):
+        t = self.add_entity('Transition', name=u'deactivate')
+        self.execute('SET X transition_of ET WHERE ET name "Bookmark", X eid %(x)s',
+                     {'x': t.eid})
+        et = self.user().wf_transition('deactivate')
+        self.assertEquals(et.transition_of[0].name, 'EUser')
+
+    def test_change_state(self):
+        user = self.user()
+        user.change_state(user.wf_state('deactivated').eid)
+        self.assertEquals(user.state, 'deactivated')
+    
+if __name__ == '__main__':
+    unittest_main()
--- a/common/uilib.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/common/uilib.py	Mon Mar 02 21:03:54 2009 +0100
@@ -179,7 +179,7 @@
     if text is None:
         return u''
     words = text.split()
-    text = ' '.join(words) # normalize spaces
+    text = u' '.join(words) # normalize spaces
     minlength = len(' '.join(words[:nbwords]))
     textlength = text.find('.', minlength) + 1
     if textlength == 0: # no point found
--- a/debian/changelog	Mon Mar 02 20:44:14 2009 +0100
+++ b/debian/changelog	Mon Mar 02 21:03:54 2009 +0100
@@ -1,3 +1,9 @@
+cubicweb (3.1.0-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Wed, 25 Feb 2009 18:41:47 +0100
+
 cubicweb (3.0.10-1) unstable; urgency=low
 
   * merge cubicweb-core package into cubicweb-common
--- a/debian/control	Mon Mar 02 20:44:14 2009 +0100
+++ b/debian/control	Mon Mar 02 21:03:54 2009 +0100
@@ -59,7 +59,7 @@
 Package: cubicweb-web
 Architecture: all
 XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), python-simplejson (>= 1.3), python-docutils, python-vobject, python-elementtree
+Depends: ${python:Depends}, cubicweb-common (= ${source:Version}), python-docutils, python-vobject, python-elementtree
 Recommends: fckeditor
 Description: web interface library for the CubicWeb framework
  CubicWeb is a semantic web application framework.
@@ -74,7 +74,7 @@
 Package: cubicweb-common
 Architecture: all
 XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, python-logilab-mtconverter (>= 0.4.0), python-simpletal (>= 4.0), graphviz, gettext, python-lxml, python-logilab-common (>= 0.37.2), python-yams (>= 0.20.2), python-rql (>= 0.20.2)
+Depends: ${python:Depends}, python-logilab-mtconverter (>= 0.6.0), python-simpletal (>= 4.0), graphviz, gettext, python-lxml, python-logilab-common (>= 0.38.1), python-yams (>= 0.20.2), python-rql (>= 0.20.2), python-simplejson (>= 1.3)
 Recommends: python-psyco
 Conflicts: cubicweb-core
 Replaces: cubicweb-core
--- a/devtools/__init__.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/devtools/__init__.py	Mon Mar 02 21:03:54 2009 +0100
@@ -260,6 +260,8 @@
        - http://www.sqlite.org/cvstrac/tktview?tn=1327,33
        (some dates are returned as strings rather thant date objects)
     """
+    if hasattr(querier.__class__, '_devtools_sqlite_patched'):
+        return # already monkey patched
     def wrap_execute(base_execute):
         def new_execute(*args, **kwargs):
             rset = base_execute(*args, **kwargs)
@@ -288,7 +290,7 @@
             return rset
         return new_execute
     querier.__class__.execute = wrap_execute(querier.__class__.execute)
-
+    querier.__class__._devtools_sqlite_patched = True
 
 def init_test_database(driver='sqlite', configdir='data', config=None,
                        vreg=None):
--- a/devtools/apptest.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/devtools/apptest.py	Mon Mar 02 21:03:54 2009 +0100
@@ -407,6 +407,7 @@
         self.__close(self.cnxid)
 
     # other utilities #########################################################
+    
     def set_debug(self, debugmode):
         from cubicweb.server import set_debug
         set_debug(debugmode)
@@ -452,7 +453,7 @@
         self.__commit = repo.commit
         self.__rollback = repo.rollback
         self.__close = repo.close
-        self.cnxid = repo.connect(*self.default_user_password())
+        self.cnxid = self.cnx.sessionid
         self.session = repo._sessions[self.cnxid]
         # XXX copy schema since hooks may alter it and it may be not fully
         #     cleaned (missing some schema synchronization support)
--- a/entity.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/entity.py	Mon Mar 02 21:03:54 2009 +0100
@@ -45,6 +45,8 @@
                                'inlineview'))
 
     def __init__(self, eclass, tagdefs):
+        # XXX if a rtag is redefined in a subclass,
+        # the rtag of the base class overwrite the rtag of the subclass
         self.eclass = eclass
         self._tagdefs = {}
         for relation, tags in tagdefs.iteritems():
--- a/goa/dbmyams.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/goa/dbmyams.py	Mon Mar 02 21:03:54 2009 +0100
@@ -109,6 +109,7 @@
         super(GaeSchemaLoader, self).__init__(*args, **kwargs)
         self.defined = {}
         self.created = []
+        self.loaded_files = []
         self._instantiate_handlers()
         
     def finalize(self, register_base_types=False):
--- a/i18n/en.po	Mon Mar 02 20:44:14 2009 +0100
+++ b/i18n/en.po	Mon Mar 02 21:03:54 2009 +0100
@@ -47,10 +47,26 @@
 msgstr ""
 
 #, python-format
+msgid "%d days"
+msgstr ""
+
+#, python-format
+msgid "%d hours"
+msgstr ""
+
+#, python-format
+msgid "%d minutes"
+msgstr ""
+
+#, python-format
 msgid "%d months"
 msgstr ""
 
 #, python-format
+msgid "%d seconds"
+msgstr ""
+
+#, python-format
 msgid "%d weeks"
 msgstr ""
 
@@ -59,7 +75,31 @@
 msgstr ""
 
 #, python-format
-msgid "%s days"
+msgid "%d&nbsp;days"
+msgstr ""
+
+#, python-format
+msgid "%d&nbsp;hours"
+msgstr ""
+
+#, python-format
+msgid "%d&nbsp;minutes"
+msgstr ""
+
+#, python-format
+msgid "%d&nbsp;months"
+msgstr ""
+
+#, python-format
+msgid "%d&nbsp;seconds"
+msgstr ""
+
+#, python-format
+msgid "%d&nbsp;weeks"
+msgstr ""
+
+#, python-format
+msgid "%d&nbsp;years"
 msgstr ""
 
 #, python-format
@@ -67,14 +107,6 @@
 msgstr ""
 
 #, python-format
-msgid "%s hours"
-msgstr ""
-
-#, python-format
-msgid "%s minutes"
-msgstr ""
-
-#, python-format
 msgid "%s not estimated"
 msgstr ""
 
@@ -83,10 +115,6 @@
 msgstr ""
 
 #, python-format
-msgid "%s seconds"
-msgstr ""
-
-#, python-format
 msgid "%s software version of the database"
 msgstr ""
 
@@ -376,9 +404,6 @@
 msgid "Problem occured"
 msgstr ""
 
-msgid "Project linked data"
-msgstr ""
-
 msgid "RQLExpression"
 msgstr "RQL expression"
 
@@ -2203,15 +2228,12 @@
 msgid "ordernum"
 msgstr "order"
 
-msgid "owl (tbox+abox)"
+msgid "owl"
 msgstr ""
 
 msgid "owlabox"
 msgstr ""
 
-msgid "owlaboxlight"
-msgstr ""
-
 msgid "owned_by"
 msgstr "owned by"
 
@@ -2583,9 +2605,6 @@
 msgid "task progression"
 msgstr ""
 
-msgid "tbox"
-msgstr ""
-
 msgid "text"
 msgstr ""
 
--- a/i18n/es.po	Mon Mar 02 20:44:14 2009 +0100
+++ b/i18n/es.po	Mon Mar 02 21:03:54 2009 +0100
@@ -52,34 +52,66 @@
 msgstr "%(subject)s %(etype)s #%(eid)s (%(login)s)"
 
 #, python-format
+msgid "%d days"
+msgstr "%d días"
+
+#, python-format
+msgid "%d hours"
+msgstr "%d horas"
+
+#, python-format
+msgid "%d minutes"
+msgstr "%d minutos"
+
+#, python-format
 msgid "%d months"
 msgstr "%d meses"
 
 #, python-format
+msgid "%d seconds"
+msgstr "%d segundos"
+
+#, python-format
 msgid "%d weeks"
 msgstr "%d semanas"
 
 #, python-format
-msgid "%d years"
-msgstr "%d años"
+msgid "%d&nbsp;years"
+msgstr "%d&nbsp;años"
+
+#, python-format
+msgid "%d&nbsp;days"
+msgstr "%d&nbsp;días"
+
+#, python-format
+msgid "%d&nbsp;hours"
+msgstr "%d&nbsp;horas"
 
 #, python-format
-msgid "%s days"
-msgstr "%d días"
+msgid "%d&nbsp;minutes"
+msgstr "%d&nbsp;minutos"
+
+#, python-format
+msgid "%d&nbsp;months"
+msgstr "%d&nbsp;meses"
+
+#, python-format
+msgid "%d&nbsp;seconds"
+msgstr "%d&nbsp;segundos"
+
+#, python-format
+msgid "%d&nbsp;weeks"
+msgstr "%d&nbsp;semanas"
+
+#, python-format
+msgid "%d&nbsp;years"
+msgstr "%d&nbsp;años"
 
 #, python-format
 msgid "%s error report"
 msgstr "%s reporte de errores"
 
 #, python-format
-msgid "%s hours"
-msgstr "%s horas"
-
-#, python-format
-msgid "%s minutes"
-msgstr "%s minutos"
-
-#, python-format
 msgid "%s not estimated"
 msgstr "%s no estimado(s)"
 
@@ -88,10 +120,6 @@
 msgstr "%s resultados de la demanda"
 
 #, python-format
-msgid "%s seconds"
-msgstr "%s segundos"
-
-#, python-format
 msgid "%s software version of the database"
 msgstr "version sistema de la base para %s"
 
@@ -381,9 +409,6 @@
 msgid "Problem occured"
 msgstr "Ha ocurrido un error"
 
-msgid "Project linked data"
-msgstr ""
-
 msgid "RQLExpression"
 msgstr "Expresión RQL"
 
@@ -2295,15 +2320,12 @@
 msgid "ordernum"
 msgstr "ordre"
 
-msgid "owl (tbox+abox)"
+msgid "owl"
 msgstr ""
 
 msgid "owlabox"
 msgstr ""
 
-msgid "owlaboxlight"
-msgstr ""
-
 msgid "owned_by"
 msgstr "appartient ‡"
 
@@ -2688,9 +2710,6 @@
 msgid "task progression"
 msgstr "avancement de la t‚che"
 
-msgid "tbox"
-msgstr ""
-
 msgid "text"
 msgstr "text"
 
--- a/i18n/fr.po	Mon Mar 02 20:44:14 2009 +0100
+++ b/i18n/fr.po	Mon Mar 02 21:03:54 2009 +0100
@@ -52,10 +52,26 @@
 msgstr "%(subject)s %(etype)s #%(eid)s (%(login)s)"
 
 #, python-format
+msgid "%d days"
+msgstr "%d jours"
+
+#, python-format
+msgid "%d hours"
+msgstr "%d heures"
+
+#, python-format
+msgid "%d minutes"
+msgstr "%d minutes"
+
+#, python-format
 msgid "%d months"
 msgstr "%d mois"
 
 #, python-format
+msgid "%d seconds"
+msgstr "%d secondes"
+
+#, python-format
 msgid "%d weeks"
 msgstr "%d semaines"
 
@@ -64,22 +80,38 @@
 msgstr "%d années"
 
 #, python-format
-msgid "%s days"
-msgstr "%d jours"
+msgid "%d&nbsp;days"
+msgstr "%d&nbsp;jours"
+
+#, python-format
+msgid "%d&nbsp;hours"
+msgstr "%d&nbsp;heures"
+
+#, python-format
+msgid "%d&nbsp;minutes"
+msgstr "%d&nbsp;minutes"
+
+#, python-format
+msgid "%d&nbsp;months"
+msgstr "%d&nbsp;mois"
+
+#, python-format
+msgid "%d&nbsp;seconds"
+msgstr "%d&nbsp;secondes"
+
+#, python-format
+msgid "%d&nbsp;weeks"
+msgstr "%d&nbsp;semaines"
+
+#, python-format
+msgid "%d&nbsp;years"
+msgstr "%d&nbsp;années"
 
 #, python-format
 msgid "%s error report"
 msgstr "%s rapport d'erreur"
 
 #, python-format
-msgid "%s hours"
-msgstr "%s heures"
-
-#, python-format
-msgid "%s minutes"
-msgstr "%s minutes"
-
-#, python-format
 msgid "%s not estimated"
 msgstr "%s non estimé(s)"
 
@@ -88,10 +120,6 @@
 msgstr "%s résultats pour la requête"
 
 #, python-format
-msgid "%s seconds"
-msgstr "%s secondes"
-
-#, python-format
 msgid "%s software version of the database"
 msgstr "version logicielle de la base pour %s"
 
@@ -381,9 +409,6 @@
 msgid "Problem occured"
 msgstr "Une erreur est survenue"
 
-msgid "Project linked data"
-msgstr ""
-
 msgid "RQLExpression"
 msgstr "Expression RQL"
 
@@ -2299,15 +2324,12 @@
 msgid "ordernum"
 msgstr "ordre"
 
-msgid "owl (tbox+abox)"
+msgid "owl"
 msgstr ""
 
 msgid "owlabox"
 msgstr ""
 
-msgid "owlaboxlight"
-msgstr ""
-
 msgid "owned_by"
 msgstr "appartient à"
 
@@ -2692,9 +2714,6 @@
 msgid "task progression"
 msgstr "avancement de la tâche"
 
-msgid "tbox"
-msgstr ""
-
 msgid "text"
 msgstr "text"
 
--- a/server/migractions.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/server/migractions.py	Mon Mar 02 21:03:54 2009 +0100
@@ -57,7 +57,7 @@
             self.repo_connect()
         if not schema:
             schema = config.load_schema(expand_cubes=True)
-        self.new_schema = schema
+        self.fs_schema = schema
         self._synchronized = set()
 
     @cached
@@ -217,7 +217,9 @@
                         'rql': self.rqlexec,
                         'rqliter': self.rqliter,
                         'schema': self.repo.schema,
-                        'newschema': self.new_schema,
+                        # XXX deprecate
+                        'newschema': self.fs_schema,
+                        'fsschema': self.fs_schema,
                         'cnx': self.cnx,
                         'session' : self.session,
                         'repo' : self.repo,
@@ -281,22 +283,22 @@
         if not update_database:
             self.commit()
             return
-        with_new_cubes = self.config.load_schema()
+        newcubes_schema = self.config.load_schema()
         new = set()
         # execute pre-create files
         for pack in reversed(newcubes):
             self.exec_event_script('precreate', self.config.cube_dir(pack))
         # add new entity and relation types
-        for rschema in with_new_cubes.relations():
+        for rschema in newcubes_schema.relations():
             if not rschema in self.repo.schema:
                 self.cmd_add_relation_type(rschema.type)
                 new.add(rschema.type)
-        for eschema in with_new_cubes.entities():
+        for eschema in newcubes_schema.entities():
             if not eschema in self.repo.schema:
                 self.cmd_add_entity_type(eschema.type)
                 new.add(eschema.type)
         # check if attributes has been added to existing entities
-        for rschema in with_new_cubes.relations():
+        for rschema in newcubes_schema.relations():
             existingschema = self.repo.schema.rschema(rschema.type)
             for (fromtype, totype) in rschema.iter_rdefs():
                 if existingschema.has_rdef(fromtype, totype):
@@ -315,25 +317,25 @@
         removedcubes = super(ServerMigrationHelper, self).cmd_remove_cube(cube)
         if not removedcubes:
             return
-        oldschema = self.new_schema
-        self.new_schema = newschema = self.config.load_schema()
+        fsschema = self.fs_schema
+        removedcubes_schema = self.config.load_schema()
         reposchema = self.repo.schema
         # execute pre-remove files
         for pack in reversed(removedcubes):
             self.exec_event_script('preremove', self.config.cube_dir(pack))
         # remove cubes'entity and relation types
-        for rschema in oldschema.relations():
-            if not rschema in newschema and rschema in reposchema:
+        for rschema in fsschema.relations():
+            if not rschema in removedcubes_schema and rschema in reposchema:
                 self.cmd_drop_relation_type(rschema.type)
-        for eschema in oldschema.entities():
-            if not eschema in newschema and eschema in reposchema:
+        for eschema in fsschema.entities():
+            if not eschema in removedcubes_schema and eschema in reposchema:
                 self.cmd_drop_entity_type(eschema.type)
-        for rschema in oldschema.relations():
-            if rschema in newschema and rschema in reposchema: 
+        for rschema in fsschema.relations():
+            if rschema in removedcubes_schema and rschema in reposchema: 
                 # check if attributes/relations has been added to entities from 
                 # other cubes
                 for fromtype, totype in rschema.iter_rdefs():
-                    if not newschema[rschema.type].has_rdef(fromtype, totype) and \
+                    if not removedcubes_schema[rschema.type].has_rdef(fromtype, totype) and \
                            reposchema[rschema.type].has_rdef(fromtype, totype):
                         self.cmd_drop_relation_definition(
                             str(fromtype), rschema.type, str(totype))
@@ -349,7 +351,7 @@
     def cmd_add_attribute(self, etype, attrname, attrtype=None, commit=True):
         """add a new attribute on the given entity type"""
         if attrtype is None:
-            rschema = self.new_schema.rschema(attrname)
+            rschema = self.fs_schema.rschema(attrname)
             attrtype = rschema.objects(etype)[0]
         self.cmd_add_relation_definition(etype, attrname, attrtype, commit=commit)
         
@@ -368,7 +370,7 @@
         `oldname` is a string giving the name of the existing attribute
         `newname` is a string giving the name of the renamed attribute
         """
-        eschema = self.new_schema.eschema(etype)
+        eschema = self.fs_schema.eschema(etype)
         attrtype = eschema.destination(newname)
         # have to commit this first step anyway to get the definition
         # actually in the schema
@@ -393,7 +395,7 @@
             if eschema.is_final():
                 applschema.del_entity_type(etype)
         else:
-            eschema = self.new_schema.eschema(etype)
+            eschema = self.fs_schema.eschema(etype)
         confirm = self.verbosity >= 2
         # register the entity into EEType
         self.rqlexecall(ss.eschema2rql(eschema), ask_confirm=confirm)
@@ -499,7 +501,7 @@
         committing depends on the `commit` argument value).
         
         """
-        rschema = self.new_schema.rschema(rtype)
+        rschema = self.fs_schema.rschema(rtype)
         # register the relation into ERType and insert necessary relation
         # definitions
         self.rqlexecall(ss.rschema2rql(rschema, addrdef=False),
@@ -537,7 +539,7 @@
         """register a new relation definition, from its definition found in the
         schema definition file
         """
-        rschema = self.new_schema.rschema(rtype)
+        rschema = self.fs_schema.rschema(rtype)
         if not rtype in self.repo.schema:
             self.cmd_add_relation_type(rtype, addrdef=False, commit=True)
         self.rqlexecall(ss.rdef2rql(rschema, subjtype, objtype),
@@ -564,7 +566,7 @@
         """permission synchronization for an entity or relation type"""
         if ertype in ('eid', 'has_text', 'identity'):
             return
-        newrschema = self.new_schema[ertype]
+        newrschema = self.fs_schema[ertype]
         teid = self.repo.schema[ertype].eid
         if 'update' in newrschema.ACTIONS or newrschema.is_final():
             # entity type
@@ -644,7 +646,7 @@
         if rtype in self._synchronized:
             return
         self._synchronized.add(rtype)
-        rschema = self.new_schema.rschema(rtype)
+        rschema = self.fs_schema.rschema(rtype)
         self.rqlexecall(ss.updaterschema2rql(rschema),
                         ask_confirm=self.verbosity>=2)
         reporschema = self.repo.schema.rschema(rtype)
@@ -674,7 +676,7 @@
         self._synchronized.add(etype)
         repoeschema = self.repo.schema.eschema(etype)
         try:
-            eschema = self.new_schema.eschema(etype)
+            eschema = self.fs_schema.eschema(etype)
         except KeyError:
             return
         repospschema = repoeschema.specializes()
@@ -719,7 +721,7 @@
         * constraints
         """
         subjtype, objtype = str(subjtype), str(objtype)
-        rschema = self.new_schema.rschema(rtype)
+        rschema = self.fs_schema.rschema(rtype)
         reporschema = self.repo.schema.rschema(rschema)
         if (subjtype, rschema, objtype) in self._synchronized:
             return
@@ -903,6 +905,13 @@
         if commit:
             self.commit()
 
+    def cmd_set_state(self, eid, statename, commit=False):
+        self.session.set_pool() # ensure pool is set
+        entity = self.session.eid_rset(eid).get_entity(0, 0)
+        entity.change_state(entity.wf_state(statename).eid)
+        if commit:
+            self.commit()
+        
     # EProperty handling ######################################################
 
     def cmd_property_value(self, pkey):
--- a/server/rqlannotation.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/server/rqlannotation.py	Mon Mar 02 21:03:54 2009 +0100
@@ -260,7 +260,6 @@
                 has_text_query = True
         return has_text_query
 
-
     def is_ambiguous(self, var):
         # ignore has_text relation
         if len([rel for rel in var.stinfo['relations']
@@ -337,7 +336,7 @@
                 except KeyError:
                     # no relation to deambiguify
                     continue
-
+        
     def _debug_print(self):
         print 'varsols', dict((x, sorted(str(v) for v in values))
                                for x, values in self.varsols.iteritems())
@@ -375,8 +374,9 @@
             otheretypes = (other.uidtype,)
             deambiguifier = None
         if otheretypes is not None:
-            # unless types for variable are already non-ambigous, check
-            # if this relation has some type ambiguity
+            # to restrict, we must check that for all type in othertypes,
+            # possible types on the other end of the relation are matching
+            # variable's possible types
             rschema = self.rschema(rel.r_type)
             if onlhs:
                 rtypefunc = rschema.subjects
@@ -386,7 +386,8 @@
                 reltypes = frozenset(rtypefunc(otheretype))
                 if var.stinfo['possibletypes'] != reltypes:
                     break
-                self.restrict(var, reltypes)
+            else:
+                self.restrict(var, var.stinfo['possibletypes'])
                 self.deambification_map[var] = deambiguifier
                 return True
         return False
--- a/server/sources/ldapuser.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/server/sources/ldapuser.py	Mon Mar 02 21:03:54 2009 +0100
@@ -176,6 +176,10 @@
         external repository
         """
         self.info('synchronizing ldap source %s', self.uri)
+        try:
+            ldap_emailattr = self.user_rev_attrs['email']
+        except KeyError:
+            return # no email in ldap, we're done
         session = self.repo.internal_session()
         try:
             cursor = session.system_sql("SELECT eid, extid FROM entities WHERE "
@@ -184,7 +188,7 @@
                 # if no result found, _search automatically delete entity information
                 res = self._search(session, extid, BASE)
                 if res: 
-                    ldapemailaddr = res[0].get(self.user_rev_attrs['email'])
+                    ldapemailaddr = res[0].get(ldap_emailattr)
                     if ldapemailaddr:
                         rset = session.execute('EmailAddress X,A WHERE '
                                                'U use_email X, U eid %(u)s',
@@ -628,7 +632,10 @@
             filter = '(%s%s)' % (self._ldap_attrs[relation.r_type],
                                  rhs.accept(self))
         except KeyError:
-            assert relation.r_type == 'password' # 2.38 migration
+            # unsupported attribute
+            self.source.warning('%s source can\'t handle relation %s, no '
+                                'results will be returned from this source',
+                                self.source.uri, relation)
             raise UnknownEid # trick to return no result
         return filter
 
--- a/server/sources/native.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/server/sources/native.py	Mon Mar 02 21:03:54 2009 +0100
@@ -451,6 +451,7 @@
         try:
             res = session.system_sql(sql).fetchone()
         except:
+            assert self.pool, 'session has no pool set'
             raise UnknownEid(eid)
         if res is None:
             raise UnknownEid(eid)
--- a/server/test/data/schema/relations.rel	Mon Mar 02 20:44:14 2009 +0100
+++ b/server/test/data/schema/relations.rel	Mon Mar 02 21:03:54 2009 +0100
@@ -43,3 +43,6 @@
 EPermission require_state State
 
 Note migrated_from Note
+
+Note attachment File
+Note attachment Image
--- a/server/test/unittest_ldapuser.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/server/test/unittest_ldapuser.py	Mon Mar 02 21:03:54 2009 +0100
@@ -33,8 +33,8 @@
 repo, cnx = init_test_database('sqlite', config=config)
 
 class LDAPUserSourceTC(RepositoryBasedTC):
-    repo = repo
-        
+    repo, cnx = repo, cnx
+    
     def patch_authenticate(self):
         self._orig_authenticate = LDAPUserSource.authenticate
         LDAPUserSource.authenticate = nopwd_authenticate
@@ -242,7 +242,10 @@
                                               ['users', 'cochon'],
                                               ['users', 'syt']])
         
-
+    def test_cd_restriction(self):
+        rset = self.execute('EUser X WHERE X creation_date > "2009-02-01"')
+        self.assertEquals(len(rset), 2) # admin/anon but no ldap user since it doesn't support creation_date
+        
     def test_union(self):
         afeids = self.execute('State X')
         ueids = self.execute('EUser X')
--- a/server/test/unittest_migractions.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/server/test/unittest_migractions.py	Mon Mar 02 21:03:54 2009 +0100
@@ -40,6 +40,8 @@
         self.mh = ServerMigrationHelper(self.repo.config, migrschema,
                                         repo=self.repo, cnx=self.cnx,
                                         interactive=False)
+        assert self.cnx is self.mh._cnx
+        assert self.session is self.mh.session, (self.session.id, self.mh.session.id)
         
     def test_add_attribute_int(self):
         self.failIf('whatever' in self.schema)
@@ -424,6 +426,16 @@
             # why this commit is necessary is unclear to me (though without it
             # next test may fail complaining of missing tables
             self.commit() 
+
+    def test_set_state(self):
+        user = self.session.user
+        self.set_debug(True)
+        self.mh.set_state(user.eid, 'deactivated')
+        user.clear_related_cache('in_state', 'subject')
+        try:
+            self.assertEquals(user.state, 'deactivated')
+        finally:
+            self.set_debug(False)
         
 if __name__ == '__main__':
     unittest_main()
--- a/server/test/unittest_rqlannotation.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/server/test/unittest_rqlannotation.py	Mon Mar 02 21:03:54 2009 +0100
@@ -286,6 +286,12 @@
                               '(EXISTS(S owned_by 1)) OR (EXISTS(S documented_by N, N title "published"))')
         self.assertEquals(rqlst.defined_vars['S']._q_invariant, True)
 
+    def test_nonregr_ambiguity(self):        
+        rqlst = self._prepare('Note N WHERE N attachment F')
+        # N may be an image as well, not invariant
+        self.assertEquals(rqlst.defined_vars['N']._q_invariant, False)
+        self.assertEquals(rqlst.defined_vars['F']._q_invariant, True)
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/test/unittest_schema.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/test/unittest_schema.py	Mon Mar 02 21:03:54 2009 +0100
@@ -226,6 +226,7 @@
     def setUp(self):
         self.loader = CubicWebSchemaLoader()
         self.loader.defined = {}
+        self.loader.loaded_files = []
         self.loader._instantiate_handlers()
 
     def _test(self, schemafile, msg):
--- a/view.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/view.py	Mon Mar 02 21:03:54 2009 +0100
@@ -304,7 +304,22 @@
     def create_url(self, etype, **kwargs):
         """ return the url of the entity creation form for a given entity type"""
         return self.req.build_url('add/%s'%etype, **kwargs)
+<<<<<<< /home/syt/src/fcubicweb/cubicweb/view.py
     
+=======
+
+
+# concrete views base classes #################################################
+
+class EntityView(View):
+    """base class for views applying on an entity (i.e. uniform result set)
+    """
+    __registerer__ = accepts_registerer
+    __selectors__ = (accept,)
+    accepts = ('Any',)
+    category = 'entityview'
+
+>>>>>>> /tmp/view.py~other.mliJlS
     def field(self, label, value, row=True, show_label=True, w=None, tr=True):
         """ read-only field """
         if w is None:
--- a/web/data/cubicweb.ajax.js	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/data/cubicweb.ajax.js	Mon Mar 02 21:03:54 2009 +0100
@@ -35,6 +35,9 @@
     if (typeof buildWidgets != 'undefined') {
 	buildWidgets(node);
     }
+    if (typeof roundedCornersOnLoad != 'undefined') {
+	roundedCornersOnLoad();
+    }
 }
 
 // cubicweb loadxhtml plugin to make jquery handle xhtml response
--- a/web/data/cubicweb.edition.js	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/data/cubicweb.edition.js	Mon Mar 02 21:03:54 2009 +0100
@@ -402,9 +402,8 @@
 
 /* disable form buttons while the validation is being done */
 function freezeFormButtons(formid) {
-    var formbuttons = jQuery(formid + ' input.validateButton');
     jQuery('#progress').show();
-    jQuery(formid + ' input.validateButton').attr('disabled', 'disabled');
+    jQuery('#' + formid + ' input.validateButton').attr('disabled', 'disabled');
     return true;
 }
 
--- a/web/data/cubicweb.formfilter.js	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/data/cubicweb.formfilter.js	Mon Mar 02 21:03:54 2009 +0100
@@ -105,14 +105,18 @@
 var SELECTED_IMG = baseuri()+"data/black-check.png";
 var UNSELECTED_IMG = baseuri()+"data/no-check-no-border.png";
 
-function initFacetBoxEvents(root){
+function initFacetBoxEvents(root) {
+    // facetargs : (divid, vid, paginate, extraargs)
     root = root || document;
     jQuery(root).find('form').each(function () {
 	var form = jQuery(this);
-	var facetargs = evalJSON(form.attr('cubicweb:facetargs'));
-	if (facetargs !== undefined && facetargs.length) {
+	// NOTE: don't evaluate facetargs here but in callbacks since its value
+	//       may changes and we must send its value when the callback is
+	//       called, not when the page is initialized
+	var facetargs = form.attr('cubicweb:facetargs');
+	if (facetargs !== undefined) {
 	    form.submit(function() {
-	        buildRQL.apply(null, facetargs); //(divid, vid, paginate, extraargs);
+	        buildRQL.apply(null, evalJSON(form.attr('cubicweb:facetargs'))); 
 	        return false;
 	    });
 	    form.find('div.facet').each(function() {
@@ -144,13 +148,13 @@
 			jQuery(this).addClass('facetValueSelected');
 			jQuery(this).find('img').attr('src', SELECTED_IMG);
 		    }
-		    buildRQL.apply(null, facetargs); // (divid, vid, paginate, extraargs);
+		    buildRQL.apply(null, evalJSON(form.attr('cubicweb:facetargs'))); 
 		    facet.find('.facetBody').animate({scrollTop: 0}, '');
 		});
 		facet.find('select.facetOperator').change(function() {
 		    var nbselected = facet.find('div.facetValueSelected').length;
 		    if (nbselected >= 2) {
-			buildRQL.apply(null, facetargs); // (divid, vid, paginate, extraargs);
+			buildRQL.apply(null, evalJSON(form.attr('cubicweb:facetargs')));
 		    }
 		});
 		facet.find('div.facetTitle').click(function() {
@@ -169,8 +173,7 @@
     root = root || document;
     jQuery(root).find('form').each(function () {
 	var form = jQuery(this);
-	var facetargs = form.attr('cubicweb:facetargs');
-	if (facetargs) {
+	if (form.attr('cubicweb:facetargs')) {
 	    form.find('div.facet').each(function() {
 		var facet = jQuery(this);
 		var lastSelected = null;
--- a/web/data/cubicweb.widgets.js	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/data/cubicweb.widgets.js	Mon Mar 02 21:03:54 2009 +0100
@@ -181,6 +181,14 @@
     }
 }
 
+Widgets.TreeView = defclass("TreeView", null, {
+    __init__: function(wdgnode) {
+	jQuery(wdgnode).treeview({toggle: toggleTree,
+				  prerendered: true
+				 });
+    }
+});
+
 
 /* widget based on SIMILE's timeline widget
  * http://code.google.com/p/simile-widgets/
--- a/web/facet.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/facet.py	Mon Mar 02 21:03:54 2009 +0100
@@ -506,10 +506,7 @@
     def _render(self):
         title = html_escape(self.facet.title)
         facetid = html_escape(self.facet.id)
-        if len(self.items) > 6:
-            self.w(u'<div id="%s" class="facet overflowed">\n' % facetid)
-        else:
-            self.w(u'<div id="%s" class="facet">\n' % facetid)
+        self.w(u'<div id="%s" class="facet">\n' % facetid)
         self.w(u'<div class="facetTitle" cubicweb:facetName="%s">%s</div>\n' %
                (html_escape(facetid), title))
         if self.facet.support_and():
@@ -522,6 +519,8 @@
         cssclass = ''
         if not self.facet.start_unfolded:
             cssclass += ' hidden'
+        if len(self.items) > 6:
+            cssclass +=' overflowed'
         self.w(u'<div class="facetBody%s">\n' % cssclass)
         for item in self.items:
             item.render(self.w)
--- a/web/test/unittest_views_baseforms.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/test/unittest_views_baseforms.py	Mon Mar 02 21:03:54 2009 +0100
@@ -19,7 +19,7 @@
         return DateTime(0000, 1, 1)
     widgets.today = widgets.now = _today
 
-def teardown_module(options, result):
+def teardown_module(options, results):
     widgets.today = orig_today
     widgets.now = orig_now
 
--- a/web/views/baseforms.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/views/baseforms.py	Mon Mar 02 21:03:54 2009 +0100
@@ -46,6 +46,7 @@
         """ask for confirmation before real deletion"""
         req, w = self.req, self.w
         _ = req._
+        req.add_css('cubicweb.form.css')
         req.add_js('cubicweb.edition.js')
         w(u'<script type="text/javascript">updateMessage(\'%s\');</script>\n'
           % _('this action is not reversible!'))
@@ -98,6 +99,7 @@
         transition = self.req.eid_rset(self.req.form['treid']).get_entity(0, 0)
         dest = transition.destination()
         self.req.add_js('cubicweb.edition.js')
+        self.req.add_css('cubicweb.form.css')
         _ = self.req._
         form = ChangeStateForm(self.req, entity=entity,
                                redirect_path=self.redirectpath(entity))
@@ -479,7 +481,6 @@
     def on_submit(self, entity):
         return u'return freezeFormButtons(\'%s\')' % (self.domid)
 
-
     def submited_message(self):
         return self.req._('element edited')
 
--- a/web/views/baseviews.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/views/baseviews.py	Mon Mar 02 21:03:54 2009 +0100
@@ -11,6 +11,8 @@
 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
+#from __future__ import with_statement
+
 __docformat__ = "restructuredtext en"
 
 from warnings import warn
@@ -50,6 +52,21 @@
     entities) 
     """
     id = 'final'
+    # record generated i18n catalog messages
+    _('%d&nbsp;years')
+    _('%d&nbsp;months')
+    _('%d&nbsp;weeks')
+    _('%d&nbsp;days')
+    _('%d&nbsp;hours')
+    _('%d&nbsp;minutes')
+    _('%d&nbsp;seconds')
+    _('%d years')
+    _('%d months')
+    _('%d weeks')
+    _('%d days')
+    _('%d hours')
+    _('%d minutes')
+    _('%d seconds')
             
     def cell_call(self, row, col, props=None, displaytime=False, format='text/html'):
         etype = self.rset.description[row][col]
@@ -61,23 +78,26 @@
                 self.w(entity.printable_value(rtype, value, format=format))
                 return
         if etype in ('Time', 'Interval'):
-            _ = self.req._
             # value is DateTimeDelta but we have no idea about what is the 
             # reference date here, so we can only approximate years and months
+            if format == 'text/html':
+                space = '&nbsp;'
+            else:
+                space = ' '
             if value.days > 730: # 2 years
-                self.w(_('%d years') % (value.days // 365))
+                self.w(self.req.__('%%d%syears' % space) % (value.days // 365))
             elif value.days > 60: # 2 months
-                self.w(_('%d months') % (value.days // 30))
+                self.w(self.req.__('%%d%smonths' % space) % (value.days // 30))
             elif value.days > 14: # 2 weeks
-                self.w(_('%d weeks') % (value.days // 7))
+                self.w(self.req.__('%%d%sweeks' % space) % (value.days // 7))
             elif value.days > 2:
-                self.w(_('%s days') % int(value.days))
+                self.w(self.req.__('%%d%sdays' % space) % int(value.days))
             elif value.hours > 2:
-                self.w(_('%s hours') % int(value.hours))
+                self.w(self.req.__('%%d%shours' % space) % int(value.hours))
             elif value.minutes >= 2:
-                self.w(_('%s minutes') % int(value.minutes))
+                self.w(self.req.__('%%d%sminutes' % space) % int(value.minutes))
             else:
-                self.w(_('%s seconds') % int(value.seconds))
+                self.w(self.req.__('%%d%sseconds' % space) % int(value.seconds))
             return
         self.wdata(printable_value(self.req, etype, value, props, displaytime=displaytime))
 
@@ -223,27 +243,43 @@
         non-meta in a first step, meta in a second step
         """
         if hasattr(self, 'get_side_boxes_defs'):
-            for label, rset in self.get_side_boxes_defs(entity):
-                if rset:
+            sideboxes = [(label, rset) for label, rset in self.get_side_boxes_defs(entity)
+                         if rset]
+            if sideboxes:
+                self.w(u'<table width="100%">')
+                for label, rset in sideboxes:
+                    self.w(u'<tr><td>')
                     self.w(u'<div class="sideRelated">')
                     self.wview('sidebox', rset, title=label)
                     self.w(u'</div>')
+                    self.w(u'</td></tr>')
+                self.w(u'</table>')
         elif siderelations:
+            self.w(u'<table width="100%">')
+            self.w(u'<tr><td>')
             self.w(u'<div class="sideRelated">')
             for relatedinfos in siderelations:
                 # if not relatedinfos[0].meta:
                 #    continue
                 self._render_related_entities(entity, *relatedinfos)
             self.w(u'</div>')
-        for box in self.vreg.possible_vobjects('boxes', self.req, self.rset,
-                                               row=self.row, view=self,
-                                               context='incontext'):
-            try:
-                box.dispatch(w=self.w, row=self.row)
-            except NotImplementedError:
-                # much probably a context insensitive box, which only implements
-                # .call() and not cell_call()
-                box.dispatch(w=self.w)
+            self.w(u'</td></tr>')
+            self.w(u'</table>')
+        boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
+                                                 row=self.row, view=self,
+                                                 context='incontext'))
+        if boxes:
+            self.w(u'<table width="100%">')
+            for box in boxes:
+                self.w(u'<tr><td>')
+                try:
+                    box.dispatch(w=self.w, row=self.row)
+                except NotImplementedError:
+                    # much probably a context insensitive box, which only implements
+                    # .call() and not cell_call()
+                    box.dispatch(w=self.w)
+                self.w(u'</td></tr>')
+            self.w(u'</table>')
                 
     def is_side_related(self, rschema, eschema):
         return rschema.meta and \
--- a/web/views/euser.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/views/euser.py	Mon Mar 02 21:03:54 2009 +0100
@@ -87,14 +87,6 @@
         self.w(u'</foaf:Person>\n')
 
 
-class FoafUsableView(FoafView):
-    id = 'foaf_usable'
-    # XXX killme
-    title = None
-    def call(self):
-        self.cell_call(0, 0)
-
-            
 class EditGroups(FormMixIn, EntityView):
     """displays a simple euser / egroups editable table"""
     
--- a/web/views/idownloadable.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/views/idownloadable.py	Mon Mar 02 21:03:54 2009 +0100
@@ -17,6 +17,12 @@
 
 _ = unicode
 
+def is_image(entity):
+    mt = entity.download_content_type()
+    if not (mt and mt.startswith('image/')):
+        return 0
+    return 1
+
 def download_box(w, entity, title=None, label=None):
     req = entity.req
     w(u'<div class="sideRelated">')
@@ -32,11 +38,14 @@
     w(u'</div>')
     w(u'</div>\n</div>\n')
 
-    
+
 class DownloadBox(EntityBoxTemplate):
     id = 'download_box'
-    __select__ = (one_line_rset() & implements(IDownloadable) & match_context_prop())
+    # no download box for images
+    # XXX primary_view selector ?
+    __select__ = (one_line_rset() & implements(IDownloadable) & match_context_prop() & ~ score_entity(is_image)
     order = 10
+    
     def cell_call(self, row, col, title=None, label=None, **kwargs):
         entity = self.entity(row, col)
         download_box(self.w, entity, title, label)
@@ -87,7 +96,8 @@
                                                                                 
 class IDownloadablePrimaryView(baseviews.PrimaryView):
     __select__ = implements(IDownloadable)
-    #skip_attrs = ('eid', 'data',) # XXX
+    # XXX File/Image attributes but this is not specified in the IDownloadable interface
+    skip_attrs = baseviews.PrimaryView.skip_attrs + ('data', 'name')
 
     def render_entity_title(self, entity):
         self.w(u'<h1>%s %s</h1>'
@@ -96,12 +106,12 @@
     
     def render_entity_attributes(self, entity, siderelations):
         super(IDownloadablePrimaryView, self).render_entity_attributes(entity, siderelations)
-        self.wview('downloadlink', entity.rset, title=self.req._('download'), row=entity.row)
         self.w(u'<div class="content">')
         contenttype = entity.download_content_type()
         if contenttype.startswith('image/'):
             self.wview('image', entity.rset, row=entity.row)
         else:
+            self.wview('downloadlink', entity.rset, title=self.req._('download'), row=entity.row)
             try:
                 if ENGINE.has_input(contenttype):
                     self.w(entity.printable_value('data'))
@@ -130,12 +140,6 @@
                (url, name, durl, self.req._('download')))
 
 
-def is_image(entity):
-    mt = entity.download_content_type()
-    if not (mt and mt.startswith('image/')):
-        return 0
-    return 1
-    
 class ImageView(baseviews.EntityView):
     id = 'image'
     __select__ = implements(IDownloadable) & score_entity(is_image)
--- a/web/views/tabs.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/views/tabs.py	Mon Mar 02 21:03:54 2009 +0100
@@ -32,22 +32,15 @@
   });""" % {'event': 'load_%s' % vid, 'vid': vid,
             'reloadable' : str(reloadable).lower()})
 
-    def lazyview(self, vid, rql=None, eid=None, rset=None, static=False,
-                 reloadable=False, show_spinbox=True, w=None):
+    def lazyview(self, vid, eid=None, reloadable=False, show_spinbox=True, w=None):
         """a lazy version of wview
         first version only support lazy viewing for an entity at a time
         """
-        assert rql or eid or rset or static, \
-            'lazyview wants at least : rql, or an eid, or an rset -- or call it with static=True'
         w = w or self.w
         self.req.add_js('cubicweb.lazy.js')
         urlparams = {'vid' : vid, 'mode' : 'html'}
-        if rql:
-            urlparams['rql'] = rql
-        elif eid:
+        if eid:
             urlparams['rql'] = uilib.rql_for_eid(eid)
-        elif rset:
-            urlparams['rql'] = rset.printable_rql()
         w(u'<div id="lazy-%s" cubicweb:loadurl="%s">' % (
             vid, html_escape(self.build_url('json', **urlparams))))
         if show_spinbox:
@@ -71,12 +64,12 @@
         return str('%s_active_tab' % self.config.appid)
 
     def active_tab(self, tabs, default):
-        cookies = self.req.get_cookie()
+        cookie = self.req.get_cookie()
         cookiename = self.cookie_name
-        activetab = cookies.get(cookiename)
+        activetab = cookie.get(cookiename)
         if activetab is None:
-            cookies[cookiename] = default
-            self.req.set_cookie(cookies, cookiename)
+            cookie[cookiename] = default
+            self.req.set_cookie(cookie, cookiename)
             tab = default
         else:
             tab = activetab.value
@@ -102,7 +95,7 @@
         active_tab = self.active_tab(tabs, default)
         # build the html structure
         w = self.w
-        w(u'<div id="entity-tabs-%s">' % entity.eid)
+        w(u'<div id="entity-tabs">')
         w(u'<ul>')
         for tab in tabs:
             w(u'<li>')
@@ -116,16 +109,15 @@
         w(u'</div>')
         for tab in tabs:
             w(u'<div id="as-%s">' % tab)
-            self.lazyview(tab, eid=entity.eid)
+            self.lazyview(tab, entity.eid)
             w(u'</div>')
         # call the set_tab() JS function *after* each tab is generated
         # because the callback binding needs to be done before
         self.req.html_headers.add_onload(u"""
-   jQuery('#entity-tabs-%(eeid)s > ul').tabs( { selected: %(tabindex)s });
+   jQuery('#entity-tabs > ul').tabs( { selected: %(tabindex)s });
    set_tab('%(vid)s', '%(cookiename)s');
  """ % {'tabindex'   : tabs.index(active_tab),
         'vid'        : active_tab,
-        'eeid'       : entity.eid,
         'cookiename' : self.cookie_name})
 
 
--- a/web/views/treeview.py	Mon Mar 02 20:44:14 2009 +0100
+++ b/web/views/treeview.py	Mon Mar 02 21:03:54 2009 +0100
@@ -18,26 +18,29 @@
     return str('treestate-%s' % treeid)
 
 
+from cubicweb.web.views.baseviews import OneLineView
+
 class TreeView(EntityView):
     id = 'treeview'
     itemvid = 'treeitemview'
     css_classes = 'treeview widget'
     title = _('tree view')
 
-    def call(self, subvid=None, treeid=None, initial_load=True):
+    def call(self, subvid=None):
         if subvid is None and 'subvid' in self.req.form:
             subvid = self.req.form.pop('subvid') # consume it
         if subvid is None:
             subvid = 'oneline'
-        if treeid is None and 'treeid' in self.req.form:
-            treeid = self.req.form.pop('treeid')
-        assert treeid is not None
-        if initial_load:
-            self.req.add_css('jquery.treeview.css')
-            self.req.add_js(('cubicweb.ajax.js', 'jquery.treeview.js'))
-            self.req.html_headers.add_onload(u"""
-jQuery("#tree-%s").treeview({toggle: toggleTree, prerendered: true});""" % treeid)
-        self.w(u'<ul id="tree-%s" class="%s">' % (treeid, self.css_classes))
+        self.req.add_css('jquery.treeview.css')
+        self.req.add_js(('cubicweb.ajax.js', 'jquery.treeview.js', 'cubicweb.widgets.js'))
+        # XXX noautoload is a quick hack to avoid treeview to be rebuilt
+        #     after a json query and avoid double toggling bugs.
+        #     Need to find a way to do that cleanly.
+        if 'noautoload' in self.req.form:
+            self.w(u'<ul class="%s" cubicweb:wdgtype="TreeView">' % self.css_classes)
+        else:
+            self.w(u'<ul class="%s" cubicweb:loadtype="auto" cubicweb:wdgtype="TreeView">'
+                   % self.css_classes)
         for rowidx in xrange(len(self.rset)):
             self.wview(self.itemvid, self.rset, row=rowidx, col=0,
                        vid=subvid, parentvid=self.id, treeid=treeid)
@@ -54,6 +57,8 @@
     def call(self, subvid=None, treeid=None, initial_load=True):
         super(FileTreeView, self).call(treeid=treeid, subvid='filetree-oneline', initial_load=initial_load)
 
+
+
 class FileItemInnerView(EntityView):
     """inner view used by the TreeItemView instead of oneline view
 
@@ -65,18 +70,17 @@
     def cell_call(self, row, col):
         entity = self.entity(row, col)
         if ITree.is_implemented_by(entity.__class__) and not entity.is_leaf():
-            self.w(u'<div class="folder">%s</div>\n' % entity.view('oneline'))
+            self.w(u'<div class="folder">%s</div>' % entity.view('oneline'))
         else:
             # XXX define specific CSS classes according to mime types
-            self.w(u'<div class="file">%s</div>\n' % entity.view('oneline'))
+            self.w(u'<div class="file">%s</div>' % entity.view('oneline'))
 
 
 class DefaultTreeViewItemView(EntityView):
     """default treeitem view for entities which don't implement ITree"""
     id = 'treeitemview'
-
-    def cell_call(self, row, col, vid='oneline', parentvid='treeview', treeid=None):
-        assert treeid is not None
+    
+    def cell_call(self, row, col, vid='oneline', parentvid='treeview'):
         entity = self.entity(row, col)
         itemview = self.view(vid, self.rset, row=row, col=col)
         if row == len(self.rset) - 1:
@@ -92,81 +96,32 @@
     """
     id = 'treeitemview'
     __select_ = implements(ITree)
-
-    def open_state(self, eeid, treeid):
-        cookies = self.req.get_cookie()
-        treestate = cookies.get(treecookiename(treeid))
-        if treestate:
-            return str(eeid) in treestate.value.split(';')
-        return False
-
-    def cell_call(self, row, col, treeid, vid='oneline', parentvid='treeview'):
-        w = self.w
+    
+    def cell_call(self, row, col, vid='oneline', parentvid='treeview'):
         entity = self.entity(row, col)
-        liclasses = []
+        cssclasses = []
         is_leaf = False
-        is_last = row == len(self.rset) - 1
-        is_open = self.open_state(entity.eid, treeid)
+        if row == len(self.rset) - 1:
+            is_leaf = True
         if not hasattr(entity, 'is_leaf') or entity.is_leaf():
-            if is_last:
-                liclasses.append('last')
-            w(u'<li class="%s">' % u' '.join(liclasses))
+            if is_leaf : cssclasses.append('last')
+            self.w(u'<li class="%s">' % u' '.join(cssclasses))
         else:
             rql = entity.children_rql() % {'x': entity.eid}
             url = html_escape(self.build_url('json', rql=rql, vid=parentvid,
                                              pageid=self.req.pageid,
-                                             treeid=treeid,
-                                             subvid=vid))
-            divclasses = ['hitarea']
-            if is_open:
-                liclasses.append('collapsable')
-                divclasses.append('collapsable-hitarea')
-            else:
-                liclasses.append('expandable')
-                divclasses.append('closed-hitarea expandable-hitarea')
-            if is_last:
-                if is_open:
-                    liclasses.append('lastCollapsable')
-                    divclasses.append('lastCollapsable-hitarea')
-                else:
-                    liclasses.append('lastExpandable')
-                    divclasses.append('lastExpandable-hitarea')
-            if is_open:
-                w(u'<li class="%s">' % u' '.join(liclasses))
-            else:
-                w(u'<li cubicweb:loadurl="%s" class="%s">' % (url, u' '.join(liclasses)))
-            if is_leaf:
-                divtail = ''
-            else:
-                divtail = ''' onclick="async_remote_exec('node_clicked', '%s', '%s')"''' % \
-                    (treeid, entity.eid)
-            w(u'<div class="%s"%s></div>' % (u' '.join(divclasses), divtail))
-
+                                             subvid=vid,
+                                             noautoload=True))
+            cssclasses.append('expandable')
+            divclasses = ['hitarea expandable-hitarea']
+            if is_leaf :
+                cssclasses.append('lastExpandable')
+                divclasses.append('lastExpandable-hitarea')
+            self.w(u'<li cubicweb:loadurl="%s" class="%s">' % (url, u' '.join(cssclasses)))
+            self.w(u'<div class="%s"> </div>' % u' '.join(divclasses))
             # add empty <ul> because jquery's treeview plugin checks for
             # sublists presence
-            if not is_open:
-                w(u'<ul class="placeholder"><li>place holder</li></ul>')
-        # the local node info
+            self.w(u'<ul class="placeholder"><li>place holder</li></ul>')
         self.wview(vid, self.rset, row=row, col=col)
-        if is_open: # recurse if needed
-            self.wview(parentvid, self.req.execute(rql), treeid=treeid, initial_load=False)
-        w(u'</li>')
-
+        self.w(u'</li>')
 
-@monkeypatch(JSonController)
-def js_node_clicked(self, treeid, nodeeid):
-    """add/remove eid in treestate cookie"""
-    cookies = self.req.get_cookie()
-    statename = treecookiename(treeid)
-    treestate = cookies.get(statename)
-    if treestate is None:
-        cookies[statename] = nodeeid
-        self.req.set_cookie(cookies, statename)
-    else:
-        marked = set(filter(None, treestate.value.split(';')))
-        if nodeeid in marked:
-            marked.remove(nodeeid)
-        else:
-            marked.add(nodeeid)
-        cookies[statename] = ';'.join(marked)
-        self.req.set_cookie(cookies, statename)