backport stable branch 3.5
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Fri, 11 Sep 2009 15:55:15 +0200
branch3.5
changeset 3184 613064b49331
parent 3177 e7ae807554d9 (current diff)
parent 3183 e916f1e856c8 (diff)
child 3185 bd0126d17e83
child 3192 93c8fdcd943e
backport stable branch
__pkginfo__.py
_exceptions.py
cwctl.py
cwvreg.py
entity.py
etwist/server.py
server/serverctl.py
server/session.py
sobjects/supervising.py
test/unittest_entity.py
web/data/cubicweb.edition.js
web/test/unittest_breadcrumbs.py
web/uicfg.py
web/views/basecomponents.py
web/views/basetemplates.py
web/views/ibreadcrumbs.py
--- a/.hgtags	Fri Sep 11 12:42:37 2009 +0200
+++ b/.hgtags	Fri Sep 11 15:55:15 2009 +0200
@@ -62,3 +62,7 @@
 7fd294cbf6ff3cf34475cc50e972f650a34ae6e8 cubicweb-debian-version-3.4.5-1
 921fdbf8b3038dc27a2ec5398a0fbcbc5b9ba4be cubicweb-version-3.4.6
 52dba800ca4d4b82c47f3befb824bd91ef015368 cubicweb-debian-version-3.4.6-1
+0e549b299f0b357837ea620c561aa843f46de17a cubicweb-version-3.4.7
+ebb92e62eb040a070deb1f2d2434734cfac3af01 cubicweb-debian-version-3.4.7-1
+ba43e084e8841f62c3b4c2449b26a7546233e5fb cubicweb-version-3.4.8
+97273eeaaead11c0f422dc5a4fe2d4f14fc6a2dd cubicweb-debian-version-3.4.8-1
--- a/_exceptions.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/_exceptions.py	Fri Sep 11 15:55:15 2009 +0200
@@ -117,7 +117,7 @@
 
 class NoSelectableObject(RegistryException):
     """some views with the given vid have been found but no
-    one is applyable to the result set
+    one is applicable to the result set
     """
 
 class UnknownProperty(RegistryException):
--- a/cwctl.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/cwctl.py	Fri Sep 11 15:55:15 2009 +0200
@@ -152,6 +152,7 @@
             else:
                 self.run_arg(appid)
 
+
 # base commands ###############################################################
 
 class ListCommand(Command):
@@ -365,7 +366,7 @@
 
 # instance commands ########################################################
 
-class StartInstanceCommand(InstanceCommand):
+class StartInstanceCommand(InstanceCommandFork):
     """Start the given instances. If no instance is given, start them all.
 
     <instance>...
@@ -397,17 +398,15 @@
 
     def start_instance(self, appid):
         """start the instance's server"""
-        # use get() since start may be used from other commands (eg upgrade)
-        # without all options defined
-        debug = self.get('debug')
-        force = self.get('force')
-        loglevel = self.get('loglevel')
+        debug = self['debug']
+        force = self['force']
+        loglevel = self['loglevel']
         config = cwcfg.config_for(appid)
         if loglevel is not None:
             loglevel = 'LOG_%s' % loglevel.upper()
             config.global_set_option('log-threshold', loglevel)
             config.init_log(loglevel, debug=debug, force=True)
-        if self.get('profile'):
+        if self['profile']:
             config.global_set_option('profile', self.config.profile)
         helper = self.config_helper(config, cmdname='start')
         pidf = config['pid-file']
@@ -415,8 +414,10 @@
             msg = "%s seems to be running. Remove %s by hand if necessary or use \
 the --force option."
             raise ExecutionError(msg % (appid, pidf))
-        helper.start_command(config, debug)
-        return True
+        helper.start_server(config, debug)
+        if not debug:
+            # in debug mode, we reach this point once the instance is stopped...
+            print 'instance %s %s' % (appid, self.actionverb)
 
 
 class StopInstanceCommand(InstanceCommand):
@@ -469,8 +470,7 @@
         print 'instance %s stopped' % appid
 
 
-class RestartInstanceCommand(StartInstanceCommand,
-                                StopInstanceCommand):
+class RestartInstanceCommand(StartInstanceCommand):
     """Restart the given instances.
 
     <instance>...
@@ -489,14 +489,12 @@
         print ('some specific start order is specified, will first stop all '
                'instances then restart them.')
         # get instances in startorder
-        stopped = []
         for appid in args:
             if askconfirm:
                 print '*'*72
                 if not ASK.confirm('%s instance %r ?' % (self.name, appid)):
                     continue
-            self.stop_instance(appid)
-            stopped.append(appid)
+            StopInstanceCommand().stop_instance(appid)
         forkcmd = [w for w in sys.argv if not w in args]
         forkcmd[1] = 'start'
         forkcmd = ' '.join(forkcmd)
@@ -506,9 +504,8 @@
                 sys.exit(status)
 
     def restart_instance(self, appid):
-        self.stop_instance(appid)
-        if self.start_instance(appid):
-            print 'instance %s %s' % (appid, self.actionverb)
+        StopInstanceCommand().stop_instance(appid)
+        self.start_instance(appid)
 
 
 class ReloadConfigurationCommand(RestartInstanceCommand):
@@ -559,9 +556,7 @@
             print "running with pid %s" % (pid)
 
 
-class UpgradeInstanceCommand(InstanceCommandFork,
-                                StartInstanceCommand,
-                                StopInstanceCommand):
+class UpgradeInstanceCommand(InstanceCommandFork):
     """Upgrade an instance after cubicweb and/or component(s) upgrade.
 
     For repository update, you will be prompted for a login / password to use
@@ -617,10 +612,6 @@
           }),
         )
 
-    def ordered_instances(self):
-        # need this since mro return StopInstanceCommand implementation
-        return InstanceCommand.ordered_instances(self)
-
     def upgrade_instance(self, appid):
         print '\n' + underline_title('Upgrading the instance %s' % appid)
         from logilab.common.changelog import Version
@@ -667,7 +658,7 @@
             print '-> migration needed from %s to %s for %s' % (fromversion, toversion, cube)
         # only stop once we're sure we have something to do
         if not (cwcfg.mode == 'dev' or self.config.nostartstop):
-            self.stop_instance(appid)
+            StopCommand().stop_instance(appid)
         # run cubicweb/componants migration scripts
         mih.migrate(vcconf, reversed(toupgrade), self.config)
         # rewrite main configuration file
@@ -690,7 +681,7 @@
         print
         print '-> instance migrated.'
         if not (cwcfg.mode == 'dev' or self.config.nostartstop):
-            self.start_instance(appid)
+            StartCommand().start_instance(appid)
         print
 
 
--- a/cwvreg.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/cwvreg.py	Fri Sep 11 15:55:15 2009 +0200
@@ -130,19 +130,23 @@
         baseschemas = [eschema] + eschema.ancestors()
         # browse ancestors from most specific to most generic and try to find an
         # associated custom entity class
+        cls = None
         for baseschema in baseschemas:
             try:
                 btype = ETYPE_NAME_MAP[baseschema]
             except KeyError:
                 btype = str(baseschema)
-            try:
-                objects = self[btype]
-                assert len(objects) == 1, objects
-                cls = objects[0]
-                break
-            except ObjectNotFound:
-                pass
-        else:
+            if cls is None:
+                try:
+                    objects = self[btype]
+                    assert len(objects) == 1, objects
+                    cls = objects[0]
+                except ObjectNotFound:
+                    pass
+            else:
+                # ensure parent classes are built first
+                self.etype_class(btype)
+        if cls is None:
             # no entity class for any of the ancestors, fallback to the default
             # one
             objects = self['Any']
--- a/debian/changelog	Fri Sep 11 12:42:37 2009 +0200
+++ b/debian/changelog	Fri Sep 11 15:55:15 2009 +0200
@@ -1,3 +1,21 @@
+cubicweb (3.4.9-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Fri, 11 Sep 2009 14:27:39 +0200
+
+cubicweb (3.4.8-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Thu, 10 Sep 2009 15:27:17 +0200
+
+cubicweb (3.4.7-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Wed, 09 Sep 2009 14:08:41 +0200
+
 cubicweb (3.4.6-1) unstable; urgency=low
 
   * new upstream release
--- a/doc/book/en/B0015-define-permissions.en.txt	Fri Sep 11 12:42:37 2009 +0200
+++ b/doc/book/en/B0015-define-permissions.en.txt	Fri Sep 11 15:55:15 2009 +0200
@@ -114,7 +114,7 @@
 	require_group = SubjectRelation('EGroup', cardinality='+*',
 					description=_('groups to which the permission is granted'))
 	require_state = SubjectRelation('State',
-				    description=_("entity'state in which the permission is applyable"))
+				    description=_("entity'state in which the permission is applicable"))
 	# can be used on any entity
 	require_permission = ObjectRelation('**', cardinality='*1', composite='subject',
 					    description=_("link a permission to the entity. This "
--- a/doc/book/en/development/datamodel/definition.rst	Fri Sep 11 12:42:37 2009 +0200
+++ b/doc/book/en/development/datamodel/definition.rst	Fri Sep 11 15:55:15 2009 +0200
@@ -390,7 +390,7 @@
 	require_group = SubjectRelation('CWGroup', cardinality='+*',
 					description=_('groups to which the permission is granted'))
 	require_state = SubjectRelation('State',
-				    description=_("entity'state in which the permission is applyable"))
+                                        description=_("entity's state in which the permission is applicable"))
 	# can be used on any entity
 	require_permission = ObjectRelation('**', cardinality='*1', composite='subject',
 					    description=_("link a permission to the entity. This "
--- a/entity.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/entity.py	Fri Sep 11 15:55:15 2009 +0200
@@ -682,14 +682,16 @@
         self.set_related_cache(rtype, role, rset)
         return self.related(rtype, role, limit, entities)
 
-    def related_rql(self, rtype, role='subject'):
+    def related_rql(self, rtype, role='subject', targettypes=None):
         rschema = self.schema[rtype]
         if role == 'subject':
-            targettypes = rschema.objects(self.e_schema)
+            if targettypes is None:
+                targettypes = rschema.objects(self.e_schema)
             restriction = 'E eid %%(x)s, E %s X' % rtype
             card = greater_card(rschema, (self.e_schema,), targettypes, 0)
         else:
-            targettypes = rschema.subjects(self.e_schema)
+            if targettypes is None:
+                targettypes = rschema.subjects(self.e_schema)
             restriction = 'E eid %%(x)s, X %s E' % rtype
             card = greater_card(rschema, targettypes, (self.e_schema,), 1)
         if len(targettypes) > 1:
--- a/etwist/server.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/etwist/server.py	Fri Sep 11 15:55:15 2009 +0200
@@ -16,12 +16,6 @@
 import hotshot
 
 from twisted.application import strports
-try:
-    from twisted.scripts._twistd_unix import daemonize
-except ImportError:
-    def daemonize():
-        raise NotImplementedError('not yet for win32')
-
 from twisted.internet import reactor, task, threads
 from twisted.internet.defer import maybeDeferred
 from twisted.web2 import channel, http, server, iweb
@@ -35,6 +29,36 @@
 
 from cubicweb.etwist.request import CubicWebTwistedRequestAdapter
 
+def daemonize(uid):
+    # XXX unix specific
+    # XXX factorize w/ code in cw.server.server and cw.server.serverctl
+    # (start-repository command)
+    if uid is not None:
+        try:
+            uid = int(uid)
+        except ValueError:
+            from pwd import getpwnam
+            uid = getpwnam(uid).pw_uid
+        os.setuid(uid)
+    # See http://www.erlenstar.demon.co.uk/unix/faq_toc.html#TOC16
+    if os.fork():   # launch child and...
+        return -1
+    os.setsid()
+    if os.fork():   # launch child and...
+        os._exit(0) # kill off parent again.
+    # move to the root to avoit mount pb
+    os.chdir('/')
+    # set paranoid umask
+    os.umask(077)
+    null = os.open('/dev/null', os.O_RDWR)
+    for i in range(3):
+        try:
+            os.dup2(null, i)
+        except OSError, e:
+            if e.errno != errno.EBADF:
+                raise
+    os.close(null)
+    return None
 
 def start_task(interval, func):
     lc = task.LoopingCall(func)
@@ -373,7 +397,9 @@
     logger = getLogger('cubicweb.twisted')
     logger.info('instance started on %s', baseurl)
     if not debug:
-        daemonize()
+        if daemonize(config['uid']):
+            # child process
+            return
         if config['pid-file']:
             # ensure the directory where the pid-file should be set exists (for
             # instance /var/run/cubicweb may be deleted on computer restart)
--- a/etwist/twctl.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/etwist/twctl.py	Fri Sep 11 15:55:15 2009 +0200
@@ -18,7 +18,7 @@
     cmdname = 'start'
     cfgname = 'twisted'
 
-    def start_command(self, config, debug):
+    def start_server(self, config, debug):
         from cubicweb.etwist import server
         server.run(config, debug)
 
--- a/i18n/en.po	Fri Sep 11 12:42:37 2009 +0200
+++ b/i18n/en.po	Fri Sep 11 15:55:15 2009 +0200
@@ -608,7 +608,9 @@
 msgid "You can use any of the following substitutions in your text"
 msgstr ""
 
-msgid "You have no access to this view or it's not applyable to current data"
+msgid ""
+"You have no access to this view or it can not be used to display the current "
+"data."
 msgstr ""
 
 msgid ""
@@ -1646,12 +1648,13 @@
 msgid "download"
 msgstr ""
 
+#, python-format
+msgid "download %s"
+msgstr ""
+
 msgid "download icon"
 msgstr ""
 
-msgid "download image"
-msgstr ""
-
 msgid "download schema as owl"
 msgstr ""
 
@@ -2052,6 +2055,12 @@
 msgid "invalid date"
 msgstr ""
 
+msgid "invalid float value"
+msgstr ""
+
+msgid "invalid integer value"
+msgstr ""
+
 msgid "is"
 msgstr ""
 
--- a/i18n/es.po	Fri Sep 11 12:42:37 2009 +0200
+++ b/i18n/es.po	Fri Sep 11 15:55:15 2009 +0200
@@ -625,8 +625,10 @@
 "Puede realizar cualquiera de las siguientes sustituciones en el contenido de "
 "su email."
 
-msgid "You have no access to this view or it's not applyable to current data"
-msgstr "No tiene acceso a esta vista o No es aplicable a los datos actuales"
+msgid ""
+"You have no access to this view or it can not be used to display the current "
+"data."
+msgstr "No tiene acceso a esta vista o No se puede utilizare para los datos actuales."
 
 msgid ""
 "You're not authorized to access this page. If you think you should, please "
@@ -1708,12 +1710,13 @@
 msgid "download"
 msgstr "Descargar"
 
+#, python-format
+msgid "download %s"
+msgstr ""
+
 msgid "download icon"
 msgstr "ícono de descarga"
 
-msgid "download image"
-msgstr ""
-
 msgid "download schema as owl"
 msgstr "Descargar esquema en OWL"
 
@@ -2128,6 +2131,12 @@
 msgid "invalid date"
 msgstr "Esta fecha no es válida"
 
+msgid "invalid float value"
+msgstr ""
+
+msgid "invalid integer value"
+msgstr ""
+
 msgid "is"
 msgstr "es"
 
@@ -3183,3 +3192,7 @@
 
 msgid "you should probably delete that property"
 msgstr "deberia probablamente suprimir esta propriedad"
+
+#~ msgid ""
+#~ "You have no access to this view or it's not applyable to current data"
+#~ msgstr "No tiene acceso a esta vista o No es aplicable a los datos actuales"
--- a/i18n/fr.po	Fri Sep 11 12:42:37 2009 +0200
+++ b/i18n/fr.po	Fri Sep 11 15:55:15 2009 +0200
@@ -624,9 +624,10 @@
 "Vous pouvez utiliser n'importe quelle substitution parmi la liste suivante "
 "dans le contenu de votre courriel."
 
-msgid "You have no access to this view or it's not applyable to current data"
-msgstr ""
-"Vous n'avez pas accès à cette vue ou elle ne s'applique pas aux données"
+msgid ""
+"You have no access to this view or it can not be used to display the current "
+"data."
+msgstr "Vous n'avez pas accès à cette vue ou elle ne peut pas afficher ces données."
 
 msgid ""
 "You're not authorized to access this page. If you think you should, please "
@@ -1721,12 +1722,13 @@
 msgid "download"
 msgstr "télécharger"
 
+#, python-format
+msgid "download %s"
+msgstr "télécharger %s"
+
 msgid "download icon"
 msgstr "icône de téléchargement"
 
-msgid "download image"
-msgstr "image de téléchargement"
-
 msgid "download schema as owl"
 msgstr "télécharger le schéma OWL"
 
@@ -2141,6 +2143,12 @@
 msgid "invalid date"
 msgstr "cette date n'est pas valide"
 
+msgid "invalid float value"
+msgstr "nombre flottant non valide"
+
+msgid "invalid integer value"
+msgstr "nombre entier non valide"
+
 msgid "is"
 msgstr "de type"
 
@@ -3199,3 +3207,11 @@
 
 msgid "you should probably delete that property"
 msgstr "vous devriez probablement supprimer cette propriété"
+
+#~ msgid ""
+#~ "You have no access to this view or it's not applyable to current data"
+#~ msgstr ""
+#~ "Vous n'avez pas accès à cette vue ou elle ne s'applique pas aux données"
+
+#~ msgid "download image"
+#~ msgstr "image de téléchargement"
--- a/server/querier.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/server/querier.py	Fri Sep 11 15:55:15 2009 +0200
@@ -508,7 +508,7 @@
             if repo.schema.rschema(rtype).inlined:
                 entity = session.entity_from_eid(subj)
                 entity[rtype] = obj
-                repo.glob_update_entity(session, entity)
+                repo.glob_update_entity(session, entity, set((rtype,)))
             else:
                 repo.glob_add_relation(session, subj, rtype, obj)
 
--- a/server/serverctl.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/server/serverctl.py	Fri Sep 11 15:55:15 2009 +0200
@@ -210,12 +210,12 @@
     cmdname = 'start'
     cfgname = 'repository'
 
-    def start_command(self, ctlconf, debug):
+    def start_server(self, ctlconf, debug):
         command = ['cubicweb-ctl start-repository ']
         if debug:
             command.append('--debug')
         command.append(self.config.appid)
-        return ' '.join(command)
+        os.system(' '.join(command))
 
 
 class RepositoryStopHandler(CommandHandler):
--- a/server/session.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/server/session.py	Fri Sep 11 15:55:15 2009 +0200
@@ -81,7 +81,7 @@
     def hijack_user(self, user):
         """return a fake request/session using specified user"""
         session = Session(user, self.repo)
-        session._threaddata = self._threaddata
+        session._threaddata = self.actual_session()._threaddata
         return session
 
     def _change_relation(self, cb, fromeid, rtype, toeid):
--- a/skeleton/migration/postcreate.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/skeleton/migration/postcreate.py	Fri Sep 11 15:55:15 2009 +0200
@@ -1,4 +1,4 @@
-# postcreate script. You could setup a workflow here for example
+# postcreate script. You could setup site properties or a workflow here for example
 """
 
 :organization: Logilab
@@ -7,3 +7,6 @@
 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
 """
 
+# Example of site property change
+#set_property('ui.site-title', "<sitename>")
+
--- a/sobjects/supervising.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/sobjects/supervising.py	Fri Sep 11 15:55:15 2009 +0200
@@ -23,6 +23,8 @@
     accepts = ('Any',)
 
     def call(self, session, *args):
+        if session.is_super_session or session.repo.config.repairing:
+            return # ignore changes triggered by hooks or maintainance shell
         dest = self.config['supervising-addrs']
         if not dest: # no supervisors, don't do this for nothing...
             return
--- a/test/unittest_entity.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/test/unittest_entity.py	Fri Sep 11 15:55:15 2009 +0200
@@ -182,15 +182,16 @@
         from cubicweb.entities import fetch_config
         Personne = self.vreg['etypes'].etype_class('Personne')
         Note = self.vreg['etypes'].etype_class('Note')
+        self.failUnless(issubclass(self.vreg['etypes'].etype_class('SubNote'), Note))
         Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', 'type'))
         Note.fetch_attrs, Note.fetch_order = fetch_config(('type',))
-        aff = self.add_entity('Personne', nom=u'pouet')
-        self.assertEquals(aff.related_rql('evaluee'),
+        p = self.add_entity('Personne', nom=u'pouet')
+        self.assertEquals(p.related_rql('evaluee'),
                           'Any X,AA,AB ORDERBY AA ASC WHERE E eid %(x)s, E evaluee X, '
                           'X type AA, X modification_date AB')
         Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', ))
         # XXX
-        self.assertEquals(aff.related_rql('evaluee'),
+        self.assertEquals(p.related_rql('evaluee'),
                           'Any X,AA ORDERBY Z DESC WHERE X modification_date Z, E eid %(x)s, E evaluee X, X modification_date AA')
 
     def test_entity_unrelated(self):
--- a/web/data/cubicweb.edition.js	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/data/cubicweb.edition.js	Fri Sep 11 15:55:15 2009 +0200
@@ -255,6 +255,11 @@
         updateInlinedEntitiesCounters(rtype);
         reorderTabindex();
         form.trigger('inlinedform-added');
+        // if the inlined form contains a file input, we must force
+        // the form enctype to multipart/form-data
+        if (form.find('input:file').length) {
+            form.closest('form').attr('enctype', 'multipart/form-data');
+        }
         postAjaxLoad(dom);
     });
     d.addErrback(function (xxx) {
--- a/web/data/cubicweb.facets.js	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/data/cubicweb.facets.js	Fri Sep 11 15:55:15 2009 +0200
@@ -66,6 +66,7 @@
 	extraparams['divid'] = divid;
 	copyParam(zipped, extraparams, 'divid');
 	copyParam(zipped, extraparams, 'subvid');
+	copyParam(zipped, extraparams, 'fromformfilter');
 	// paginate used to know if the filter box is acting, in which case we
 	// want to reload action box to match current selection (we don't want
 	// this from a table filter)
--- a/web/data/cubicweb.python.js	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/data/cubicweb.python.js	Fri Sep 11 15:55:15 2009 +0200
@@ -83,10 +83,10 @@
     var skip0 = new RegExp('^0*[0-9]+');
     var parsed = {};
     for (var i1=0,i2=0;i1<format.length;i1++,i2++) {
-	var c1 = format[i1];
-	var c2 = datestring[i2];
+	var c1 = format.charAt(i1);
+	var c2 = datestring.charAt(i2);
 	if (c1 == '%') {
-	    c1 = format[++i1];
+	    c1 = format.charAt(++i1);
 	    var data = _DATE_FORMAT_REGXES[c1].exec(datestring.substring(i2));
 	    if (!data.length) {
 		return null;
--- a/web/test/unittest_breadcrumbs.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/test/unittest_breadcrumbs.py	Fri Sep 11 15:55:15 2009 +0200
@@ -10,8 +10,8 @@
         self.commit()
         childrset = self.execute('Folder F WHERE F eid %s' % f2.eid)
         self.assertEquals(childrset.get_entity(0,0).view('breadcrumbs'),
-                          '<a href="http://testing.fr/cubicweb/folder/715" title="">chi&amp;ld</a>')
+                          '<a href="http://testing.fr/cubicweb/folder/%s" title="">chi&amp;ld</a>' % f1.eid)
         ibc = self.vreg['components'].select('breadcrumbs', self.request(), rset=childrset)
         self.assertEquals(ibc.render(),
-                          """<span id="breadcrumbs" class="pathbar">&#160;&gt;&#160;<a href="http://testing.fr/cubicweb/Folder">folder_plural</a>&#160;&gt;&#160;<a href="http://testing.fr/cubicweb/folder/714" title="">par&amp;ent</a>&#160;&gt;&#160;
-chi&amp;ld</span>""")
+                          """<span id="breadcrumbs" class="pathbar">&#160;&gt;&#160;<a href="http://testing.fr/cubicweb/Folder">folder_plural</a>&#160;&gt;&#160;<a href="http://testing.fr/cubicweb/folder/%s" title="">par&amp;ent</a>&#160;&gt;&#160;
+chi&amp;ld</span>""" % f2.eid)
--- a/web/test/unittest_urlrewrite.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/test/unittest_urlrewrite.py	Fri Sep 11 15:55:15 2009 +0200
@@ -106,6 +106,73 @@
         self.assertEquals(len(rset), 1)
         self.assertEquals(rset[0][0], self.p1.eid)
 
+    def test_inheritance_precedence(self):
+        RQL1 = 'Any C WHERE C is CWEType'
+        RQL2 = 'Any C WHERE C is CWUser'
+
+        class BaseRewriter(SchemaBasedRewriter):
+            rules = [
+               (rgx('/collector(.*)'),
+                rgx_action(rql=RQL1,
+                    form=dict(vid='baseindex')),
+                ),
+                ]
+        class Rewriter(BaseRewriter):
+            rules = [
+               (rgx('/collector/something(/?)'),
+                rgx_action(rql=RQL2,
+                    form=dict(vid='index')),
+                ),
+                ]
+
+        rewriter = Rewriter()
+        req = self.request()
+        pmid, rset = rewriter.rewrite(req, '/collector')
+        self.assertEquals(rset.rql, RQL1)
+        self.assertEquals(req.form, {'vid' : "baseindex"})
+        pmid, rset = rewriter.rewrite(req, '/collector/something')
+        self.assertEquals(rset.rql, RQL2)
+        self.assertEquals(req.form, {'vid' : "index"})
+        pmid, rset = rewriter.rewrite(req, '/collector/something/')
+        self.assertEquals(req.form, {'vid' : "index"})
+        self.assertEquals(rset.rql, RQL2)
+        pmid, rset = rewriter.rewrite(req, '/collector/somethingelse/')
+        self.assertEquals(rset.rql, RQL1)
+        self.assertEquals(req.form, {'vid' : "baseindex"})
+
+    def test_inheritance_precedence_same_rgx(self):
+        RQL1 = 'Any C WHERE C is CWEType'
+        RQL2 = 'Any C WHERE C is CWUser'
+
+        class BaseRewriter(SchemaBasedRewriter):
+            rules = [
+               (rgx('/collector(.*)'),
+                rgx_action(rql=RQL1,
+                    form=dict(vid='baseindex')),
+                ),
+                ]
+        class Rewriter(BaseRewriter):
+            rules = [
+               (rgx('/collector(.*)'),
+                rgx_action(rql=RQL2,
+                    form=dict(vid='index')),
+                ),
+                ]
+
+        rewriter = Rewriter()
+        req = self.request()
+        pmid, rset = rewriter.rewrite(req, '/collector')
+        self.assertEquals(rset.rql, RQL2)
+        self.assertEquals(req.form, {'vid' : "index"})
+        pmid, rset = rewriter.rewrite(req, '/collector/something')
+        self.assertEquals(rset.rql, RQL2)
+        self.assertEquals(req.form, {'vid' : "index"})
+        pmid, rset = rewriter.rewrite(req, '/collector/something/')
+        self.assertEquals(req.form, {'vid' : "index"})
+        self.assertEquals(rset.rql, RQL2)
+        pmid, rset = rewriter.rewrite(req, '/collector/somethingelse/')
+        self.assertEquals(rset.rql, RQL2)
+        self.assertEquals(req.form, {'vid' : "index"})
 
 
 if __name__ == '__main__':
--- a/web/uicfg.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/uicfg.py	Fri Sep 11 15:55:15 2009 +0200
@@ -166,23 +166,28 @@
 
 def init_autoform_section(rtag, sschema, rschema, oschema, role):
     if rtag.get(sschema, rschema, oschema, role) is None:
-        if role == 'subject':
-            card = rschema.rproperty(sschema, oschema, 'cardinality')[0]
-            composed = rschema.rproperty(sschema, oschema, 'composite') == 'object'
-        else:
-            card = rschema.rproperty(sschema, oschema, 'cardinality')[1]
-            composed = rschema.rproperty(sschema, oschema, 'composite') == 'subject'
-        if sschema.is_metadata(rschema):
+        if autoform_is_inlined.get(sschema, rschema, oschema, role):
+            section = 'generated'
+        elif sschema.is_metadata(rschema):
             section = 'metadata'
-        elif card in '1+':
-            if not rschema.is_final() and composed:
-                section = 'generated'
+        else:
+            if role == 'subject':
+                card = rschema.rproperty(sschema, oschema, 'cardinality')[0]
+                composed = rschema.rproperty(sschema, oschema, 'composite') == 'object'
             else:
-                section = 'primary'
-        elif rschema.is_final():
-            section = 'secondary'
-        else:
-            section = 'generic'
+                card = rschema.rproperty(sschema, oschema, 'cardinality')[1]
+                composed = rschema.rproperty(sschema, oschema, 'composite') == 'subject'
+            if card in '1+':
+                if not rschema.is_final() and composed:
+                    # XXX why? probably because we want it unlined, though this
+                    # is not the case by default
+                    section = 'generated'
+                else:
+                    section = 'primary'
+            elif rschema.is_final():
+                section = 'secondary'
+            else:
+                section = 'generic'
         rtag.tag_relation((sschema, rschema, oschema, role), section)
 
 autoform_section = RelationTags('autoform_section', init_autoform_section,
--- a/web/views/basecomponents.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/views/basecomponents.py	Fri Sep 11 15:55:15 2009 +0200
@@ -149,7 +149,7 @@
         title = self.req.property_value('ui.site-title')
         if title:
             self.w(u'<span id="appliName"><a href="%s">%s</a></span>' % (
-                self.req.base_url(), title))
+                self.req.base_url(), xml_escape(title)))
 
 
 class SeeAlsoVComponent(component.RelatedObjectsVComponent):
--- a/web/views/basecontrollers.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/views/basecontrollers.py	Fri Sep 11 15:55:15 2009 +0200
@@ -129,7 +129,8 @@
             if rset:
                 req.set_message(req._("The view %s can not be applied to this query") % vid)
             else:
-                req.set_message(req._("You have no access to this view or it's not applyable to current data"))
+                req.set_message(req._("You have no access to this view or it can not "
+                                      "be used to display the current data."))
             self.warning("the view %s can not be applied to this query", vid)
             vid = vid_from_rset(req, rset, self.schema)
             view = self.vreg['views'].select(vid, req, rset=rset)
--- a/web/views/basetemplates.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/views/basetemplates.py	Fri Sep 11 15:55:15 2009 +0200
@@ -480,8 +480,12 @@
         self.req.add_css('cubicweb.login.css')
         self.w(u'<div id="%s" class="%s">' % (id, klass))
         if title:
-            self.w(u'<div id="loginTitle">%s</div>'
-                   % (self.req.property_value('ui.site-title') or u'&#160;'))
+            stitle = self.req.property_value('ui.site-title')
+            if stitle:
+                stitle = xml_escape(stitle)
+            else:
+                stitle = u'&#160;'
+            self.w(u'<div id="loginTitle">%s</div>' % stitle)
         self.w(u'<div id="loginContent">\n')
 
         if message:
--- a/web/views/editcontroller.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/views/editcontroller.py	Fri Sep 11 15:55:15 2009 +0200
@@ -194,19 +194,7 @@
         # NOTE: raising ValidationError here is not a good solution because
         #       we can't gather all errors at once. Hopefully, the new 3.6.x
         #       form handling will fix that
-        if attrtype == 'Int':
-            try:
-                value = int(value)
-            except ValueError:
-                raise ValidationError(entity.eid,
-                                      {attr: self.req._("invalid integer value")})
-        elif attrtype == 'Float':
-            try:
-                value = float(value)
-            except ValueError:
-                raise ValidationError(entity.eid,
-                                      {attr: self.req._("invalid float value")})
-        elif attrtype == 'Boolean':
+        if attrtype == 'Boolean':
             value = bool(value)
         elif attrtype == 'Decimal':
             value = Decimal(value)
@@ -248,6 +236,18 @@
                 # no specified value, skip
                 return
         elif value is not None:
+            if attrtype == 'Int':
+                try:
+                    value = int(value)
+                except ValueError:
+                    raise ValidationError(entity.eid,
+                                          {attr: self.req._("invalid integer value")})
+            elif attrtype == 'Float':
+                try:
+                    value = float(value)
+                except ValueError:
+                    raise ValidationError(entity.eid,
+                                          {attr: self.req._("invalid float value")})
             if attrtype in ('Date', 'Datetime', 'Time'):
                 try:
                     value = self.parse_datetime(value, attrtype)
--- a/web/views/tableview.py	Fri Sep 11 12:42:37 2009 +0200
+++ b/web/views/tableview.py	Fri Sep 11 15:55:15 2009 +0200
@@ -51,6 +51,7 @@
         """display a form to filter table's content. This should only
         occurs when a context eid is given
         """
+        self.req.add_css('cubicweb.facets.css')
         self.req.add_js( ('cubicweb.ajax.js', 'cubicweb.facets.js'))
         # drop False / None values from vidargs
         vidargs = dict((k, v) for k, v in vidargs.iteritems() if v)
@@ -58,7 +59,9 @@
                xml_escape(dumps([divid, 'table', False, vidargs])))
         self.w(u'<fieldset id="%sForm" class="%s">' % (divid, hidden and 'hidden' or ''))
         self.w(u'<input type="hidden" name="divid" value="%s" />' % divid)
-        filter_hiddens(self.w, facets=','.join(wdg.facet.id for wdg in fwidgets), baserql=baserql)
+        self.w(u'<input type="hidden" name="fromformfilter" value="1" />')
+        filter_hiddens(self.w, facets=','.join(wdg.facet.id for wdg in fwidgets),
+                       baserql=baserql)
         self.w(u'<table class="filter">\n')
         self.w(u'<tr>\n')
         for wdg in fwidgets:
@@ -138,7 +141,6 @@
                 actions += self.form_filter(divid, displaycols, displayfilter,
                                             displayactions)
         elif displayfilter:
-            req.add_css('cubicweb.facets.css')
             actions += self.show_hide_actions(divid, True)
         self.w(u'<div id="%s"' % divid)
         if displayactions: