merge stable cubicweb-version-3.4.7
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 09 Sep 2009 16:48:39 +0200
branchstable
changeset 3143 0e549b299f0b
parent 3141 ecead3f2cff9 (diff)
parent 3142 5c07c3b2b332 (current diff)
child 3144 a5deac822a13
child 3158 ebb92e62eb04
merge
--- a/__pkginfo__.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/__pkginfo__.py	Wed Sep 09 16:48:39 2009 +0200
@@ -7,7 +7,7 @@
 distname = "cubicweb"
 modname = "cubicweb"
 
-numversion = (3, 4, 6)
+numversion = (3, 4, 7)
 version = '.'.join(str(num) for num in numversion)
 
 license = 'LGPL v2'
--- a/common/mail.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/common/mail.py	Wed Sep 09 16:48:39 2009 +0200
@@ -21,6 +21,7 @@
         return 'XXX'
 
 from cubicweb.view import EntityView
+from cubicweb.entity import Entity
 
 def header(ustring):
     return Header(ustring.encode('UTF-8'), 'UTF-8')
@@ -141,14 +142,6 @@
 
     msgid_timestamp = True
 
-    def user_login(self):
-        try:
-            # if req is actually a session (we are on the server side), and we
-            # have to prevent nested internal session
-            return self.req.actual_session().user.login
-        except AttributeError:
-            return self.req.user.login
-
     def recipients(self):
         finder = self.vreg['components'].select('recipients_finder', self.req,
                                                 rset=self.rset,
@@ -161,7 +154,7 @@
         subject = self.req._(self.message)
         etype = entity.dc_type()
         eid = entity.eid
-        login = self.user_login()
+        login = self.user_data['login']
         return self.req._('%(subject)s %(etype)s #%(eid)s (%(login)s)') % locals()
 
     def context(self, **kwargs):
@@ -169,7 +162,7 @@
         for key, val in kwargs.iteritems():
             if val and isinstance(val, unicode) and val.strip():
                kwargs[key] = self.req._(val)
-        kwargs.update({'user': self.user_login(),
+        kwargs.update({'user': self.user_data['login'],
                        'eid': entity.eid,
                        'etype': entity.dc_type(),
                        'url': entity.absolute_url(),
@@ -183,18 +176,12 @@
         return construct_message_id(self.config.appid, eid, self.msgid_timestamp)
 
     def render_emails(self, **kwargs):
-        """generate and send an email message for this view"""
+        """generate and send emails for this view (one per recipient)"""
         self._kwargs = kwargs
         recipients = self.recipients()
         if not recipients:
             self.info('skipping %s notification, no recipients', self.id)
             return
-        if not isinstance(recipients[0], tuple):
-            from warnings import warn
-            warn('recipients should now return a list of 2-uple (email, language)',
-                 DeprecationWarning, stacklevel=1)
-            lang = self.vreg.property_value('ui.language')
-            recipients = zip(recipients, repeat(lang))
         if self.rset is not None:
             entity = self.entity(self.row or 0, self.col or 0)
             # if the view is using timestamp in message ids, no way to reference
@@ -208,22 +195,32 @@
         else:
             refs = ()
             msgid = None
-        userdata = self.req.user_data()
-        origlang = self.req.lang
-        for emailaddr, lang in recipients:
-            self.req.set_language(lang)
+        req = self.req
+        self.user_data = req.user_data()
+        origlang = req.lang
+        for something in recipients:
+            if isinstance(something, Entity):
+                # hi-jack self.req to get a session for the returned user
+                self.req = self.req.hijack_user(something)
+                emailaddr = something.get_email()
+            else:
+                emailaddr, lang = something
+                self.req.set_language(lang)
             # since the same view (eg self) may be called multiple time and we
             # need a fresh stream at each iteration, reset it explicitly
             self.w = None
             # XXX call render before subject to set .row/.col attributes on the
             #     view
-            content = self.render(row=0, col=0, **kwargs)
-            subject = self.subject()
-            msg = format_mail(userdata, [emailaddr], content, subject,
+            try:
+                content = self.render(row=0, col=0, **kwargs)
+                subject = self.subject()
+            except SkipEmail:
+                continue
+            msg = format_mail(self.user_data, [emailaddr], content, subject,
                               config=self.config, msgid=msgid, references=refs)
             yield [emailaddr], msg
         # restore language
-        self.req.set_language(origlang)
+        req.set_language(origlang)
 
     def render_and_send(self, **kwargs):
         """generate and send an email message for this view"""
@@ -243,3 +240,7 @@
         raise NotImplementedError
 
     send = send_now
+
+
+class SkipEmail(Exception):
+    """raise this if you decide to skip an email during its generation"""
--- a/dbapi.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/dbapi.py	Wed Sep 09 16:48:39 2009 +0200
@@ -284,6 +284,12 @@
 
     # server session compat layer #############################################
 
+    def hijack_user(self, user):
+        """return a fake request/session using specified user"""
+        req = DBAPIRequest(self.vreg)
+        req.set_connection(self.cnx, user)
+        return req
+
     @property
     def user(self):
         if self._user is None and self.cnx:
--- a/debian/changelog	Wed Sep 09 16:11:07 2009 +0200
+++ b/debian/changelog	Wed Sep 09 16:48:39 2009 +0200
@@ -1,3 +1,9 @@
+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/entity.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/entity.py	Wed Sep 09 16:48:39 2009 +0200
@@ -689,14 +689,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/i18n/en.po	Wed Sep 09 16:11:07 2009 +0200
+++ b/i18n/en.po	Wed Sep 09 16:48:39 2009 +0200
@@ -1646,12 +1646,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 +2053,12 @@
 msgid "invalid date"
 msgstr ""
 
+msgid "invalid float value"
+msgstr ""
+
+msgid "invalid integer value"
+msgstr ""
+
 msgid "is"
 msgstr ""
 
--- a/i18n/es.po	Wed Sep 09 16:11:07 2009 +0200
+++ b/i18n/es.po	Wed Sep 09 16:48:39 2009 +0200
@@ -1708,12 +1708,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 +2129,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"
 
--- a/i18n/fr.po	Wed Sep 09 16:11:07 2009 +0200
+++ b/i18n/fr.po	Wed Sep 09 16:48:39 2009 +0200
@@ -1721,12 +1721,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 +2142,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 +3206,6 @@
 
 msgid "you should probably delete that property"
 msgstr "vous devriez probablement supprimer cette propriété"
+
+#~ msgid "download image"
+#~ msgstr "image de téléchargement"
--- a/server/migractions.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/server/migractions.py	Wed Sep 09 16:48:39 2009 +0200
@@ -166,14 +166,20 @@
                                            % (self.config.appid, backupfile)):
             return
         # unpack backup
-        bkup = tarfile.open(backupfile, 'r|gz')
-        for name in bkup.getnames():
-            if name[0] in '/.':
-                raise Exception('Security check failed, path starts with "/" or "."')
-        bkup.close() # XXX seek error if not close+open !?!
-        bkup = tarfile.open(backupfile, 'r|gz')
         tmpdir = tempfile.mkdtemp()
-        bkup.extractall(path=tmpdir)
+        try:
+            bkup = tarfile.open(backupfile, 'r|gz')
+        except tarfile.ReadError:
+            # assume restoring old backup
+            shutil.copy(backupfile, osp.join(tmpdir, 'system'))  
+        else:
+            for name in bkup.getnames():
+                if name[0] in '/.':
+                    raise Exception('Security check failed, path starts with "/" or "."')
+            bkup.close() # XXX seek error if not close+open !?!
+            bkup = tarfile.open(backupfile, 'r|gz')
+            bkup.extractall(path=tmpdir)
+            bkup.close()
 
         self.config.open_connections_pools = False
         repo = self.repo_connect()
@@ -186,7 +192,6 @@
                 print '-> error trying to restore [%s]' % exc
                 if not self.confirm('Continue anyway?', default='n'):
                     raise SystemExit(1)
-        bkup.close()
         shutil.rmtree(tmpdir)
         # call hooks
         repo.open_connections_pools()
--- a/server/querier.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/server/querier.py	Wed Sep 09 16:48:39 2009 +0200
@@ -608,6 +608,8 @@
                     # return an empty result instead of raising UnknownEid
                     return empty_rset(session, rql, args)
                 cachekey.append(etype)
+                # ensure eid is correctly typed in args
+                args[key] = typed_eid(args[key])
             cachekey = tuple(cachekey)
         else:
             cachekey = rql
--- a/server/session.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/server/session.py	Wed Sep 09 16:48:39 2009 +0200
@@ -78,16 +78,51 @@
     def schema(self):
         return self.repo.schema
 
-    def add_relation(self, fromeid, rtype, toeid):
+    def hijack_user(self, user):
+        """return a fake request/session using specified user"""
+        session = Session(user, self.repo)
+        session._threaddata = self._threaddata
+        return session
+
+    def _change_relation(self, cb, fromeid, rtype, toeid):
         if self.is_super_session:
-            self.repo.glob_add_relation(self, fromeid, rtype, toeid)
+            cb(self, fromeid, rtype, toeid)
             return
         self.is_super_session = True
         try:
-            self.repo.glob_add_relation(self, fromeid, rtype, toeid)
+            cb(self, fromeid, rtype, toeid)
         finally:
             self.is_super_session = False
 
+    def add_relation(self, fromeid, rtype, toeid):
+        """provide direct access to the repository method to add a relation.
+
+        This is equivalent to the following rql query:
+
+          SET X rtype Y WHERE X eid  fromeid, T eid toeid
+
+        without read security check but also all the burden of rql execution.
+        You may use this in hooks when you know both eids of the relation you
+        want to add.
+        """
+        self._change_relation(self.repo.glob_add_relation,
+                              fromeid, rtype, toeid)
+    def delete_relation(self, fromeid, rtype, toeid):
+        """provide direct access to the repository method to delete a relation.
+
+        This is equivalent to the following rql query:
+
+          DELETE X rtype Y WHERE X eid  fromeid, T eid toeid
+
+        without read security check but also all the burden of rql execution.
+        You may use this in hooks when you know both eids of the relation you
+        want to delete.
+        """
+        self._change_relation(self.repo.glob_delete_relation,
+                              fromeid, rtype, toeid)
+
+    # relations cache handling #################################################
+
     def update_rel_cache_add(self, subject, rtype, object, symetric=False):
         self._update_entity_rel_cache_add(subject, rtype, 'subject', object)
         if symetric:
@@ -129,8 +164,11 @@
                 if row[0] == targeteid:
                     break
             else:
-                raise Exception('cache inconsistency for %s %s %s %s' %
-                                (eid, rtype, role, targeteid))
+                # this may occurs if the cache has been filed by a hook
+                # after the database update
+                self.debug('cache inconsistency for %s %s %s %s', eid, rtype,
+                           role, targeteid)
+                return
             del rset.rows[idx]
             if isinstance(rset.description, list): # else description not set
                 del rset.description[idx]
--- a/server/test/unittest_querier.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/server/test/unittest_querier.py	Wed Sep 09 16:48:39 2009 +0200
@@ -210,6 +210,11 @@
         # should return an empty result set
         self.failIf(self.execute('Any X WHERE X eid 99999999'))
 
+    def test_typed_eid(self):
+        # should return an empty result set
+        rset = self.execute('Any X WHERE X eid %(x)s', {'x': '1'}, 'x')
+        self.assertIsInstance(rset[0][0], (int, long))
+
     def test_bytes_storage(self):
         feid = self.execute('INSERT File X: X name "foo.pdf", X data_format "text/plain", X data %(data)s',
                             {'data': Binary("xxx")})[0][0]
--- a/sobjects/notification.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/sobjects/notification.py	Wed Sep 09 16:48:39 2009 +0200
@@ -21,7 +21,7 @@
 from cubicweb.server.hookhelper import SendMailOp
 from cubicweb.server.hooksmanager import Hook
 
-parse_message_id = deprecated('parse_message_id is now defined in cubicweb.common.mail')
+parse_message_id = deprecated('parse_message_id is now defined in cubicweb.common.mail')(parse_message_id)
 
 
 class RecipientsFinder(Component):
@@ -190,6 +190,6 @@
     def subject(self):
         entity = self.entity(self.row or 0, self.col or 0)
         return  u'%s #%s (%s)' % (self.req.__('New %s' % entity.e_schema),
-                                  entity.eid, self.user_login())
+                                  entity.eid, self.user_data['login'])
 
 NormalizedTextView = class_renamed('NormalizedTextView', ContentAddedView)
--- a/sobjects/supervising.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/sobjects/supervising.py	Wed Sep 09 16:48:39 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	Wed Sep 09 16:11:07 2009 +0200
+++ b/test/unittest_entity.py	Wed Sep 09 16:48:39 2009 +0200
@@ -132,7 +132,8 @@
         seschema.subject_relation('evaluee').set_rproperty(seschema, Note.e_schema, 'cardinality', '1*')
         # testing basic fetch_attrs attribute
         self.assertEquals(Personne.fetch_rql(user),
-                          'Any X,AA,AB,AC ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB, X modification_date AC')
+                          'Any X,AA,AB,AC ORDERBY AA ASC '
+                          'WHERE X is Personne, X nom AA, X prenom AB, X modification_date AC')
         pfetch_attrs = Personne.fetch_attrs
         sfetch_attrs = Societe.fetch_attrs
         try:
@@ -142,18 +143,21 @@
             # testing one non final relation
             Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
             self.assertEquals(Personne.fetch_rql(user),
-                              'Any X,AA,AB,AC,AD ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB, X travaille AC, AC nom AD')
+                              'Any X,AA,AB,AC,AD ORDERBY AA ASC '
+                              'WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD')
             # testing two non final relations
             Personne.fetch_attrs = ('nom', 'prenom', 'travaille', 'evaluee')
             self.assertEquals(Personne.fetch_rql(user),
-                              'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA ASC,AF DESC WHERE X is Personne, X nom AA, '
-                              'X prenom AB, X travaille AC, AC nom AD, X evaluee AE, AE modification_date AF')
+                              'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA ASC,AF DESC '
+                              'WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD, '
+                              'X evaluee AE?, AE modification_date AF')
             # testing one non final relation with recursion
             Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
             Societe.fetch_attrs = ('nom', 'evaluee')
             self.assertEquals(Personne.fetch_rql(user),
-                              'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA ASC,AF DESC WHERE X is Personne, X nom AA, X prenom AB, '
-                              'X travaille AC, AC nom AD, AC evaluee AE, AE modification_date AF'
+                              'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA ASC,AF DESC '
+                              'WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD, '
+                              'AC evaluee AE?, AE modification_date AF'
                               )
             # testing symetric relation
             Personne.fetch_attrs = ('nom', 'connait')
@@ -178,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/utils.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/utils.py	Wed Sep 09 16:48:39 2009 +0200
@@ -7,6 +7,8 @@
 """
 __docformat__ = "restructuredtext en"
 
+from logilab.mtconverter import xml_escape
+
 import locale
 from md5 import md5
 from datetime import datetime, timedelta, date
@@ -262,17 +264,18 @@
         # 2/ css files
         for cssfile, media in self.cssfiles:
             w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
-              (media, cssfile))
+              (media, xml_escape(cssfile)))
         # 3/ ie css if necessary
         if self.ie_cssfiles:
             w(u'<!--[if lt IE 8]>\n')
             for cssfile, media in self.ie_cssfiles:
                 w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
-                  (media, cssfile))
+                  (media, xml_escape(cssfile)))
             w(u'<![endif]--> \n')
         # 4/ js files
         for jsfile in self.jsfiles:
-            w(u'<script type="text/javascript" src="%s"></script>\n' % jsfile)
+            w(u'<script type="text/javascript" src="%s"></script>\n' %
+              xml_escape(jsfile))
         # 5/ post inlined scripts (i.e. scripts depending on other JS files)
         if self.post_inlined_scripts:
             w(u'<script type="text/javascript">\n')
@@ -305,7 +308,8 @@
         self.htmltag = u'<html xmlns="http://www.w3.org/1999/xhtml" ' \
                        'xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" ' \
                        'xml:lang="%s" lang="%s">' % (req.lang, req.lang)
-
+        # keep main_stream's reference on req for easier text/html demoting
+        req.main_stream = self
 
     def write(self, data):
         """StringIO interface: this method will be assigned to self.w
--- a/web/data/cubicweb.ajax.js	Wed Sep 09 16:11:07 2009 +0200
+++ b/web/data/cubicweb.ajax.js	Wed Sep 09 16:48:39 2009 +0200
@@ -387,24 +387,30 @@
     return stripped;
 }
 
-/* convenience function that returns a DOM node based on req's result. */
+/* convenience function that returns a DOM node based on req's result.
+ * XXX clarify the need to clone
+ * */
 function getDomFromResponse(response) {
     if (typeof(response) == 'string') {
-	return html2dom(response);
+	var doc = html2dom(response);
+    } else {
+        var doc = response.documentElement;
     }
-    var doc = response.documentElement;
     var children = doc.childNodes;
     if (!children.length) {
 	// no child (error cases) => return the whole document
-	return doc.cloneNode(true);
+	return jQuery(doc).clone().context;
     }
     children = stripEmptyTextNodes(children);
     if (children.length == 1) {
 	// only one child => return it
-	return children[0].cloneNode(true);
+	return jQuery(children[0]).clone().context;
     }
     // several children => wrap them in a single node and return the wrap
-    return DIV(null, map(methodcaller('cloneNode', true), children));
+    return DIV(null, map(function(node) {
+                           return jQuery(node).clone().context;
+                         },
+                         children));
 }
 
 function postJSON(url, data, callback) {
--- a/web/data/cubicweb.edition.js	Wed Sep 09 16:11:07 2009 +0200
+++ b/web/data/cubicweb.edition.js	Wed Sep 09 16:48:39 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	Wed Sep 09 16:11:07 2009 +0200
+++ b/web/data/cubicweb.facets.js	Wed Sep 09 16:48:39 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/request.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/web/request.py	Wed Sep 09 16:48:39 2009 +0200
@@ -26,7 +26,7 @@
 from cubicweb.common.mail import header
 from cubicweb.common.uilib import remove_html_tags
 from cubicweb.utils import SizeConstrainedList, HTMLHead
-from cubicweb.view import STRICT_DOCTYPE
+from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE_NOEXT
 from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit,
                           RequestError, StatusResponse)
 
@@ -663,6 +663,16 @@
         """
         raise NotImplementedError()
 
+    def demote_to_html(self):
+        """helper method to dynamically set request content type to text/html
+
+        The global doctype and xmldec must also be changed otherwise the browser
+        will display '<[' at the beginning of the page
+        """
+        self.set_content_type('text/html')
+        self.main_stream.doctype = TRANSITIONAL_DOCTYPE_NOEXT
+        self.main_stream.xmldecl = u''
+
     # page data management ####################################################
 
     def get_page_data(self, key, default=None):
--- a/web/uicfg.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/web/uicfg.py	Wed Sep 09 16:48:39 2009 +0200
@@ -164,23 +164,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/editcontroller.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/web/views/editcontroller.py	Wed Sep 09 16:48:39 2009 +0200
@@ -191,7 +191,22 @@
             return
         attrtype = rschema.objects(entity.e_schema)[0].type
         # on checkbox or selection, the field may not be in params
-        if attrtype == 'Boolean':
+        # 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':
             value = bool(value)
         elif attrtype == 'Decimal':
             value = Decimal(value)
--- a/web/views/editforms.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/web/views/editforms.py	Wed Sep 09 16:48:39 2009 +0200
@@ -20,7 +20,8 @@
 from cubicweb.utils import make_uid
 from cubicweb.view import EntityView
 from cubicweb.common import tags
-from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param, uicfg
+from cubicweb.web import INTERNAL_FIELD_VALUE, RequestError, stdmsgs, eid_param
+from cubicweb.web import uicfg
 from cubicweb.web.form import FormViewMixIn, FieldNotFound
 from cubicweb.web.formfields import guess_field
 from cubicweb.web.formwidgets import Button, SubmitButton, ResetButton
@@ -327,13 +328,13 @@
         """creation view for an entity"""
         etype = kwargs.pop('etype', self.req.form.get('etype'))
         try:
-            entity = self.vreg['etypes'].etype_class(etype)(self.req)
-        except:
-            self.w(self.req._('no such entity type %s') % etype)
-        else:
-            self.initialize_varmaker()
-            entity.eid = self.varmaker.next()
-            self.render_form(entity)
+            etype = self.vreg.case_insensitive_etypes[etype.lower()]
+        except KeyError:
+            raise RequestError(self.req._('no such entity type %s') % etype)
+        entity = self.vreg['etypes'].etype_class(etype)(self.req)
+        self.initialize_varmaker()
+        entity.eid = self.varmaker.next()
+        self.render_form(entity)
 
     def form_title(self, entity):
         """the form view title"""
--- a/web/views/igeocodable.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/web/views/igeocodable.py	Wed Sep 09 16:48:39 2009 +0200
@@ -74,6 +74,7 @@
     need_navigation = False
 
     def call(self, gmap_key, width=400, height=400, uselabel=True, urlparams=None):
+        self.req.demote_to_html()
         # remove entities that don't define latitude and longitude
         self.rset = self.rset.filtered_rset(lambda e: e.latitude and e.longitude)
         self.req.add_js('http://maps.google.com/maps?sensor=false&file=api&amp;v=2&amp;key=%s' % gmap_key,
--- a/web/views/tableview.py	Wed Sep 09 16:11:07 2009 +0200
+++ b/web/views/tableview.py	Wed Sep 09 16:48:39 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: