merge treeview-tabs into default (for 3.1.1)
authorAurelien Campeas <aurelien.campeas@logilab.fr>
Tue, 03 Mar 2009 15:06:03 +0100
changeset 999 999198995a53
parent 994 98f19cf755a1 (diff)
parent 998 9c6ce9d6384f (current diff)
child 1000 90705536b7c8
merge treeview-tabs into default (for 3.1.1)
web/views/tabs.py
web/views/treeview.py
--- a/.hgtags	Tue Mar 03 14:54:31 2009 +0100
+++ b/.hgtags	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/__pkginfo__.py	Tue Mar 03 15:06:03 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/entity.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/common/entity.py	Tue Mar 03 15:06:03 2009 +0100
@@ -41,6 +41,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/common/mixins.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/common/mixins.py	Tue Mar 03 15:06:03 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)
@@ -369,22 +369,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']
 
@@ -403,6 +399,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	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/common/uilib.py	Tue Mar 03 15:06:03 2009 +0100
@@ -190,7 +190,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/common/view.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/common/view.py	Tue Mar 03 15:06:03 2009 +0100
@@ -302,6 +302,7 @@
     """
     __registerer__ = accepts_registerer
     __selectors__ = (accept,)
+    accepts = ('Any',)
     category = 'entityview'
 
     def field(self, label, value, row=True, show_label=True, w=None, tr=True):
--- a/debian/changelog	Tue Mar 03 14:54:31 2009 +0100
+++ b/debian/changelog	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/debian/control	Tue Mar 03 15:06:03 2009 +0100
@@ -60,7 +60,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.
@@ -75,7 +75,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	Tue Mar 03 14:54:31 2009 +0100
+++ b/devtools/__init__.py	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/devtools/apptest.py	Tue Mar 03 15:06:03 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/goa/dbmyams.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/goa/dbmyams.py	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/i18n/en.po	Tue Mar 03 15:06:03 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"
 
@@ -2197,15 +2222,12 @@
 msgid "ordernum"
 msgstr "order"
 
-msgid "owl (tbox+abox)"
+msgid "owl"
 msgstr ""
 
 msgid "owlabox"
 msgstr ""
 
-msgid "owlaboxlight"
-msgstr ""
-
 msgid "owned_by"
 msgstr "owned by"
 
@@ -2577,9 +2599,6 @@
 msgid "task progression"
 msgstr ""
 
-msgid "tbox"
-msgstr ""
-
 msgid "text"
 msgstr ""
 
--- a/i18n/es.po	Tue Mar 03 14:54:31 2009 +0100
+++ b/i18n/es.po	Tue Mar 03 15:06:03 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"
 
@@ -2292,15 +2317,12 @@
 msgid "ordernum"
 msgstr "ordre"
 
-msgid "owl (tbox+abox)"
+msgid "owl"
 msgstr ""
 
 msgid "owlabox"
 msgstr ""
 
-msgid "owlaboxlight"
-msgstr ""
-
 msgid "owned_by"
 msgstr "appartient ‡"
 
@@ -2685,9 +2707,6 @@
 msgid "task progression"
 msgstr "avancement de la t‚che"
 
-msgid "tbox"
-msgstr ""
-
 msgid "text"
 msgstr "text"
 
--- a/i18n/fr.po	Tue Mar 03 14:54:31 2009 +0100
+++ b/i18n/fr.po	Tue Mar 03 15:06:03 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"
 
@@ -2293,15 +2318,12 @@
 msgid "ordernum"
 msgstr "ordre"
 
-msgid "owl (tbox+abox)"
+msgid "owl"
 msgstr ""
 
 msgid "owlabox"
 msgstr ""
 
-msgid "owlaboxlight"
-msgstr ""
-
 msgid "owned_by"
 msgstr "appartient à"
 
@@ -2686,9 +2708,6 @@
 msgid "task progression"
 msgstr "avancement de la tâche"
 
-msgid "tbox"
-msgstr ""
-
 msgid "text"
 msgstr "text"
 
--- a/interfaces.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/interfaces.py	Tue Mar 03 15:06:03 2009 +0100
@@ -238,7 +238,20 @@
     def rss_feed_url(self):
         """return an url which layout sub-entities item
         """
-class ISIOC(Interface):
-    """interface for entities with sioc views"""
+class ISiocItem(Interface):
+    """interface for entities (which are item
+    in sioc specification) with sioc views"""
     
-   
+    def isioc_content(self):
+        """return content entity"""
+
+    def isioc_container(self):
+        """return container entity"""           
+            
+class ISiocContainer(Interface):
+    """interface for entities (which are container
+    in sioc specification) with sioc views"""
+
+    def isioc_type(self):
+        """return container type (forum, weblog, mailinglist)"""
+    
--- a/server/migractions.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/server/migractions.py	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/server/rqlannotation.py	Tue Mar 03 15:06:03 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/session.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/server/session.py	Tue Mar 03 15:06:03 2009 +0100
@@ -1,7 +1,7 @@
 """Repository users' and internal' sessions.
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
--- a/server/sources/ldapuser.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/server/sources/ldapuser.py	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/server/sources/native.py	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/server/test/data/schema/relations.rel	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/server/test/unittest_ldapuser.py	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/server/test/unittest_migractions.py	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/server/test/unittest_rqlannotation.py	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/test/unittest_schema.py	Tue Mar 03 15:06:03 2009 +0100
@@ -227,6 +227,7 @@
     def setUp(self):
         self.loader = CubicWebSchemaLoader()
         self.loader.defined = {}
+        self.loader.loaded_files = []
         self.loader._instantiate_handlers()
 
     def _test(self, schemafile, msg):
--- a/web/data/cubicweb.ajax.js	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/data/cubicweb.ajax.js	Tue Mar 03 15:06:03 2009 +0100
@@ -24,6 +24,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.css	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/data/cubicweb.css	Tue Mar 03 15:06:03 2009 +0100
@@ -586,9 +586,13 @@
   margin-bottom: 0px;
 }
 
+div.primaryRight{
+  float:right;
+  
+ }
+
 div.sideRelated {
   margin-right: 1em;
-  float: right;
   padding: 12px 0px 12px 12px;
   min-width: 21em;
   max-width: 50em;
--- a/web/data/cubicweb.edition.js	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/data/cubicweb.edition.js	Tue Mar 03 15:06:03 2009 +0100
@@ -264,7 +264,8 @@
     var d = async_rawremote_exec('inline_creation_form', peid, ptype, ttype, rtype, role);
     d.addCallback(function (response) {
 	var linknode = getNode('add' + rtype + ':' + peid + 'link');
-	var form = jQuery(getDomFromResponse(response));
+        var dom = getDomFromResponse(response);
+	var form = jQuery(dom);
 	form.css('display', 'none');
 	form.insertBefore(linknode.parentNode).slideDown('fast');
 	// setStyle(form, {display: 'none'});
@@ -273,6 +274,7 @@
 	// slideDown(form, {'duration':0.6});
 	reorderTabindex();
 	form.trigger('inlinedform-added');
+        postAjaxLoad(dom);
 	// MochiKit.Signal.signal(CubicWeb, 'inlinedform-added', form);
     });
     d.addErrback(function (xxx) {
@@ -402,9 +404,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	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/data/cubicweb.formfilter.js	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/data/cubicweb.widgets.js	Tue Mar 03 15:06:03 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	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/facet.py	Tue Mar 03 15:06:03 2009 +0100
@@ -507,10 +507,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():
@@ -523,6 +520,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	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/test/unittest_views_baseforms.py	Tue Mar 03 15:06:03 2009 +0100
@@ -18,7 +18,7 @@
         return DateTime(0000, 1, 1)
     widgets.today = widgets.now = _today
 
-def teardown_module(options):
+def teardown_module(options, results):
     widgets.today = orig_today
     widgets.now = orig_now
 
--- a/web/views/baseforms.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/views/baseforms.py	Tue Mar 03 15:06:03 2009 +0100
@@ -40,6 +40,7 @@
     def call(self):
         """ask for confirmation before real deletion"""
         _ = self.req._
+        self.req.add_css('cubicweb.form.css')
         self.req.add_js('cubicweb.edition.js')
         self.w(u'<script type="text/javascript">updateMessage(\'%s\');</script>\n' % _('this action is not reversible!'))
         # XXX above message should have style of a warning
@@ -101,6 +102,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._
         self.w(self.error_message())
         self.w(u'<h4>%s %s</h4>\n' % (_(transition.name), entity.view('oneline')))
@@ -519,7 +521,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	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/views/baseviews.py	Tue Mar 03 15:06:03 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
@@ -56,6 +58,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]
@@ -67,23 +84,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))
 
@@ -138,22 +158,19 @@
         self.render_entity_metadata(entity)
         # entity's attributes and relations, excluding meta data
         # if the entity isn't meta itself
-        self.w(u'<table border="0" width="100%">')
-        self.w(u'<tr>')
-        self.w(u'<td valign="top">')
+        self.w(u'<div>')
         self.w(u'<div class="mainInfo">')
         self.render_entity_attributes(entity, siderelations)
         self.w(u'</div>')
         self.content_navigation_components('navcontenttop')
         if self.main_related_section:
             self.render_entity_relations(entity, siderelations)
-        self.w(u'</td>')
+        self.w(u'</div>')
         # side boxes
-        self.w(u'<td valign="top">')
+        self.w(u'<div class="primaryRight">')
         self.render_side_related(entity, siderelations)
-        self.w(u'</td>')
-        self.w(u'</tr>')
-        self.w(u'</table>')        
+        self.w(u'</div>')
+        self.w(u'<div class="clear"></div>')          
         self.content_navigation_components('navcontentbottom')
 
     def content_navigation_components(self, context):
@@ -243,8 +260,10 @@
         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:
+                for label, rset in sideboxes:
                     self.w(u'<div class="sideRelated">')
                     self.wview('sidebox', rset, title=label)
                     self.w(u'</div>')
@@ -255,15 +274,17 @@
                 #    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)
+        boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
+                                                 row=self.row, view=self,
+                                                 context='incontext'))
+        if boxes:
+            for box in boxes:
+                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)               
                 
     def is_side_related(self, rschema, eschema):
         return rschema.meta and \
@@ -989,7 +1010,8 @@
         else:
             role = 'object'
         rset = self.rset.get_entity(row, col).related(self.rtype, role)
-        self.w(u'<h1>%s</h1>' % self.req._(self.title).capitalize())
+        if self.title:
+            self.w(u'<h1>%s</h1>' % self.req._(self.title).capitalize())
         self.w(u'<div class="mainInfo">')
         self.wview(self.vid, rset, 'noresult')
         self.w(u'</div>')
--- a/web/views/euser.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/views/euser.py	Tue Mar 03 15:06:03 2009 +0100
@@ -40,7 +40,7 @@
     content_type = 'text/xml'
 
     def call(self):
-        self.w('''<?xml version="1.0" encoding="%s"?>
+        self.w(u'''<?xml version="1.0" encoding="%s"?>
 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
          xmlns:rdfs="http://www.w3org/2000/01/rdf-schema#"
          xmlns:foaf="http://xmlns.com/foaf/0.1/"> '''% self.req.encoding)
@@ -67,13 +67,7 @@
         if emailaddr:
             self.w(u'<foaf:mbox>%s</foaf:mbox>\n' % html_escape(emailaddr))
         self.w(u'</foaf:Person>\n')
-
-class FoafUsableView(FoafView):
-    id = 'foaf_usable'
-  
-    def call(self):
-        self.cell_call(0, 0)
-            
+                   
 class EditGroups(EntityForm):
     """displays a simple euser / egroups editable table"""
     
--- a/web/views/idownloadable.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/views/idownloadable.py	Tue Mar 03 15:06:03 2009 +0100
@@ -35,9 +35,19 @@
     
 class DownloadBox(EntityBoxTemplate):
     id = 'download_box'
-    __selectors__ = (one_line_rset, implement_interface, match_context_prop)
+    # XXX primary_view selector ?
+    __selectors__ = (one_line_rset, implement_interface, match_context_prop, score_entity_selector)
     accepts_interfaces = (IDownloadable,)
     order = 10
+
+    @classmethod
+    def score_entity(cls, entity):
+        mt = entity.download_content_type()
+        # no download box for images
+        if mt and mt.startswith('image/'):
+            return 0
+        return 1
+    
     def cell_call(self, row, col, title=None, label=None, **kwargs):
         entity = self.entity(row, col)
         download_box(self.w, entity, title, label)
@@ -90,8 +100,9 @@
                                                                                 
 class IDownloadablePrimaryView(baseviews.PrimaryView):
     __selectors__ = (implement_interface,)
-    #skip_attrs = ('eid', 'data',) # XXX
     accepts_interfaces = (IDownloadable,)
+    # 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>'
@@ -100,12 +111,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'))
--- a/web/views/tabs.py	Tue Mar 03 14:54:31 2009 +0100
+++ b/web/views/tabs.py	Tue Mar 03 15:06:03 2009 +0100
@@ -33,22 +33,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'] = 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:
@@ -72,12 +65,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
@@ -108,7 +101,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>')
@@ -122,16 +115,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})
 
 
@@ -151,10 +143,11 @@
         vid = 'gallery'
         __selectors__ = EntityRelationView.__selectors__ + (one_line_rset,)
 
+
     This is the view we want to have in a tab, only if there is something to show.
     Then, just define as below, and declare this being the tab content :
 
-    class ProjectScreenshotTab(EntityRelatedTab, ProjectScreenshotsView):
+    class ProjectScreenshotTab(DataDependantTab, ProjectScreenshotsView):
         id = 'screenshots_tab'
     """
     __selectors__ = EntityView.__selectors__ + (has_related_entities,)