merge 3.17.12 into 3.18 branch cubicweb-centos-version-3.18.2-1 cubicweb-debian-version-3.18.2-1 cubicweb-version-3.18.2
authorJulien Cristau <julien.cristau@logilab.fr>
Tue, 21 Jan 2014 15:11:16 +0100
changeset 9440 6880674c1a26
parent 9439 549c999d06d2 (current diff)
parent 9438 1910d86afcbc (diff)
child 9441 e282875e8274
child 9478 2d7521881d3d
merge 3.17.12 into 3.18 branch
.hgtags
__pkginfo__.py
cubicweb.spec
dataimport.py
debian/changelog
debian/control
devtools/testlib.py
doc/book/en/admin/setup.rst
entities/adapters.py
entities/test/unittest_base.py
entity.py
i18n/fr.po
test/unittest_entity.py
web/views/workflow.py
--- a/.hgtags	Wed Sep 11 18:04:05 2013 +0200
+++ b/.hgtags	Tue Jan 21 15:11:16 2014 +0100
@@ -320,6 +320,9 @@
 7f67db7c848ec20152daf489d9e11f0fc8402e9b cubicweb-version-3.17.11
 7f67db7c848ec20152daf489d9e11f0fc8402e9b cubicweb-debian-version-3.17.11-1
 b02e2912cad5d80395e488c55b548495e8320198 cubicweb-debian-version-3.17.11-2
+838d58a30f7efc6a8f83ac27ae8de7d79b84b2bb cubicweb-version-3.17.12
+838d58a30f7efc6a8f83ac27ae8de7d79b84b2bb cubicweb-debian-version-3.17.12-1
+838d58a30f7efc6a8f83ac27ae8de7d79b84b2bb cubicweb-centos-version-3.17.12-1
 db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-version-3.18.0
 db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-debian-version-3.18.0-1
 db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-centos-version-3.18.0-1
--- a/__pkginfo__.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/__pkginfo__.py	Tue Jan 21 15:11:16 2014 +0100
@@ -1,5 +1,5 @@
 # pylint: disable=W0622,C0103
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
--- a/dataimport.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/dataimport.py	Tue Jan 21 15:11:16 2014 +0100
@@ -1064,7 +1064,7 @@
             if subjtype is None:
                 # Try to infer it
                 targets = [t.type for t in
-                           self.schema.rschema(rtype).targets()]
+                           self.schema.rschema(rtype).subjects()]
                 if len(targets) == 1:
                     subjtype = targets[0]
                 else:
--- a/debian/changelog	Wed Sep 11 18:04:05 2013 +0200
+++ b/debian/changelog	Tue Jan 21 15:11:16 2014 +0100
@@ -16,6 +16,12 @@
 
  -- Julien Cristau <julien.cristau@logilab.fr>  Fri, 10 Jan 2014 17:14:18 +0100
 
+cubicweb (3.17.12-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Aurelien Campeas <aurelien.campeas@logilab.fr>  Tue, 21 Jan 2014 13:10:22 +0100
+
 cubicweb (3.17.11-2) unstable; urgency=low
 
   * Override lintian false-positive about debian/rules.tmpl in the cube
--- a/debian/control	Wed Sep 11 18:04:05 2013 +0200
+++ b/debian/control	Tue Jan 21 15:11:16 2014 +0100
@@ -43,7 +43,8 @@
 
 Package: cubicweb-server
 Architecture: all
-Conflicts: cubicweb-multisources
+Conflicts:
+ cubicweb-multisources
 Replaces: cubicweb-multisources
 Provides: cubicweb-multisources
 Depends:
--- a/devtools/fake.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/devtools/fake.py	Tue Jan 21 15:11:16 2014 +0100
@@ -71,10 +71,6 @@
         self._headers_in.setHeader('Cookie', cookie)
 
     ## Implement request abstract API
-    def header_accept_language(self):
-        """returns an ordered list of preferred languages"""
-        return ('en',)
-
     def http_method(self):
         return self._http_method
 
--- a/devtools/testlib.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/devtools/testlib.py	Tue Jan 21 15:11:16 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -85,6 +85,28 @@
     def parse_string(self, data):
         return json.loads(data)
 
+@contextmanager
+def real_error_handling(app):
+    """By default, CubicWebTC `app` attribute (ie the publisher) is monkey
+    patched so that unexpected error are raised rather than going through the
+    `error_handler` method.
+
+    By using this context manager you disable this monkey-patching temporarily.
+    Hence when publishihng a request no error will be raised, you'll get
+    req.status_out set to an HTTP error status code and the generated page will
+    usually hold a traceback as HTML.
+
+    >>> with real_error_handling(app):
+    >>>     page = app.handle_request(req)
+    """
+    # remove the monkey patched error handler
+    fake_error_handler = app.error_handler
+    del app.error_handler
+    # return the app
+    yield app
+    # restore
+    app.error_handler = fake_error_handler
+
 # email handling, to test emails sent by an application ########################
 
 MAILBOX = []
@@ -520,9 +542,9 @@
         it2 = set(getattr(x, 'eid', x) for x in it2)
         super(CubicWebTC, self).assertItemsEqual(it1, it2, *args, **kwargs)
 
-    def assertMessageEqual(self, req, params, msg):
+    def assertMessageEqual(self, req, params, expected_msg):
         msg = req.session.data[params['_cwmsgid']]
-        self.assertEqual(msg, msg)
+        self.assertEqual(expected_msg, msg)
 
     # workflow utilities #######################################################
 
@@ -702,17 +724,14 @@
         return self.ctrl_publish(req, ctrlid, rset)
 
     def http_publish(self, url, data=None):
-        """like `url_publish`, except this returns a http response, even in case of errors"""
+        """like `url_publish`, except this returns a http response, even in case
+        of errors. You may give form parameters using the `data` argument.
+        """
         req = self.req_from_url(url)
         if data is not None:
             req.form.update(data)
-        # remove the monkey patched error handler
-        fake_error_handler = self.app.error_handler
-        del self.app.error_handler
-        try:
+        with real_error_handling(self.app):
             result = self.app_handle_request(req, req.relative_path(False))
-        finally:
-            self.app.error_handler = fake_error_handler
         return result, req
 
     @staticmethod
--- a/doc/book/en/admin/setup.rst	Wed Sep 11 18:04:05 2013 +0200
+++ b/doc/book/en/admin/setup.rst	Tue Jan 21 15:11:16 2014 +0100
@@ -51,9 +51,9 @@
 Depending on the distribution you are using, add the appropriate line to your
 `list of sources` (for example by editing ``/etc/apt/sources.list``).
 
-For Debian 6.0 Squeeze (stable)::
+For Debian 7.0 Wheezy (stable)::
 
-  deb http://download.logilab.org/production/ squeeze/
+  deb http://download.logilab.org/production/ wheezy/
 
 For Debian Sid (unstable)::
 
--- a/doc/book/en/devweb/edition/examples.rst	Wed Sep 11 18:04:05 2013 +0200
+++ b/doc/book/en/devweb/edition/examples.rst	Tue Jan 21 15:11:16 2014 +0100
@@ -55,7 +55,7 @@
 
 .. sourcecode:: python
 
-    def sender_value(form):
+    def sender_value(form, field):
 	return '%s <%s>' % (form._cw.user.dc_title(), form._cw.user.get_email())
 
     def recipient_choices(form, field):
@@ -63,7 +63,7 @@
                  for e in form.cw_rset.entities()
 		 if e.get_email()]
 
-    def recipient_value(form):
+    def recipient_value(form, field):
 	return [e.eid for e in form.cw_rset.entities()
                 if e.get_email()]
 
--- a/entities/adapters.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/entities/adapters.py	Tue Jan 21 15:11:16 2014 +0100
@@ -102,17 +102,16 @@
         _done.add(entity.eid)
         containers = tuple(entity.e_schema.fulltext_containers())
         if containers:
-            for rschema, target in containers:
-                if target == 'object':
+            for rschema, role in containers:
+                if role == 'object':
                     targets = getattr(entity, rschema.type)
                 else:
                     targets = getattr(entity, 'reverse_%s' % rschema)
-                for entity in targets:
-                    if entity.eid in _done:
+                for target in targets:
+                    if target.eid in _done:
                         continue
-                    for container in entity.cw_adapt_to('IFTIndexable').fti_containers(_done):
+                    for container in target.cw_adapt_to('IFTIndexable').fti_containers(_done):
                         yield container
-                        yielded = True
         else:
             yield entity
 
--- a/entities/test/data/schema.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/entities/test/data/schema.py	Tue Jan 21 15:11:16 2014 +0100
@@ -24,6 +24,7 @@
 
 class Company(EntityType):
     name = String()
+    description = RichString()
 
 class Division(Company):
     __specializes_schema__ = True
--- a/entities/test/unittest_base.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/entities/test/unittest_base.py	Tue Jan 21 15:11:16 2014 +0100
@@ -133,6 +133,27 @@
         self.request().create_entity('CWGroup', name=u'logilab', reverse_in_group=e)
 
 
+class HTMLtransformTC(BaseEntityTC):
+
+    def test_sanitized_html(self):
+        r = self.request()
+        c = r.create_entity('Company', name=u'Babar',
+                            description=u"""
+Title
+=====
+
+Elephant management best practices.
+
+.. raw:: html
+
+   <script>alert("coucou")</script>
+""", description_format=u'text/rest')
+        self.commit()
+        c.cw_clear_all_caches()
+        self.assertIn('alert', c.printable_value('description', format='text/plain'))
+        self.assertNotIn('alert', c.printable_value('description', format='text/html'))
+
+
 class SpecializedEntityClassesTC(CubicWebTC):
 
     def select_eclass(self, etype):
--- a/entity.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/entity.py	Tue Jan 21 15:11:16 2014 +0100
@@ -764,7 +764,7 @@
                           _engine=ENGINE):
         trdata = TransformData(data, format, encoding, appobject=self)
         data = _engine.convert(trdata, target_format).decode()
-        if format == 'text/html':
+        if target_format == 'text/html':
             data = soup2xhtml(data, self._cw.encoding)
         return data
 
--- a/i18n/fr.po	Wed Sep 11 18:04:05 2013 +0200
+++ b/i18n/fr.po	Tue Jan 21 15:11:16 2014 +0100
@@ -942,7 +942,7 @@
 "box, or edit file content online with the widget below."
 msgstr ""
 "Vous pouvez soit soumettre un nouveau fichier en utilisant le bouton\n"
-"\"parcourir\" ci-dessus, soit suprrimer le fichier déjà présent en\n"
+"\"parcourir\" ci-dessus, soit supprimer le fichier déjà présent en\n"
 "cochant la case \"détacher fichier attaché\", soit éditer le contenu\n"
 "du fichier en ligne avec le champ ci-dessous."
 
@@ -951,7 +951,7 @@
 "content online with the widget below."
 msgstr ""
 "Vous pouvez soit soumettre un nouveau fichier en utilisant le bouton\n"
-"\"parcourir\" ci-dessu, soit éditer le contenu du fichier en ligne\n"
+"\"parcourir\" ci-dessus, soit éditer le contenu du fichier en ligne\n"
 "avec le champ ci-dessous."
 
 msgid "You can't change this relation"
--- a/test/unittest_entity.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/test/unittest_entity.py	Tue Jan 21 15:11:16 2014 +0100
@@ -551,7 +551,7 @@
         e = self.request().create_entity('Card', title=u'rest test', content=u'du :eid:`1:*ReST*`',
                                          content_format=u'text/rest')
         self.assertEqual(e.printable_value('content'),
-                         '<p>du <a class="reference" href="http://testing.fr/cubicweb/cwsource/system">*ReST*</a></p>\n')
+                         '<p>du <a class="reference" href="http://testing.fr/cubicweb/cwsource/system">*ReST*</a></p>')
         e.cw_attr_cache['content'] = 'du <em>html</em> <ref rql="CWUser X">users</ref>'
         e.cw_attr_cache['content_format'] = 'text/html'
         self.assertEqual(e.printable_value('content'),
@@ -559,7 +559,7 @@
         e.cw_attr_cache['content'] = 'du *texte*'
         e.cw_attr_cache['content_format'] = 'text/plain'
         self.assertEqual(e.printable_value('content'),
-                          '<p>\ndu *texte*<br/>\n</p>')
+                          '<p>\ndu *texte*<br/></p>')
         e.cw_attr_cache['title'] = 'zou'
         e.cw_attr_cache['content'] = '''\
 a title
@@ -590,24 +590,21 @@
             if tuple(int(i) for i in pygments.__version__.split('.')[:2]) >= (1, 3):
                 self.assertEqual(e.printable_value('data'),
                                   '''<div class="highlight"><pre><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="mi">1</span>
-</pre></div>
-''')
+</pre></div>''')
             else:
                 self.assertEqual(e.printable_value('data'),
                                   '''<div class="highlight"><pre><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="mf">1</span>
-</pre></div>
-''')
+</pre></div>''')
         else:
             self.assertEqual(e.printable_value('data'),
                               '''<pre class="python">
 <span style="color: #C00000;">lambda</span> <span style="color: #000000;">x</span><span style="color: #0000C0;">:</span> <span style="color: #0080C0;">1</span>
-</pre>
-''')
+</pre>''')
 
         e = req.create_entity('File', data=Binary('*héhéhé*'), data_format=u'text/rest',
                             data_encoding=u'utf-8', data_name=u'toto.txt')
         self.assertEqual(e.printable_value('data'),
-                          u'<p><em>héhéhé</em></p>\n')
+                          u'<p><em>héhéhé</em></p>')
 
     def test_printable_value_bad_html(self):
         """make sure we don't crash if we try to render invalid XHTML strings"""
--- a/web/application.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/web/application.py	Tue Jan 21 15:11:16 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -39,7 +39,7 @@
 from cubicweb.web import LOGGER, component
 from cubicweb.web import (
     StatusResponse, DirectResponse, Redirect, NotFound, LogOut,
-    RemoteCallFailed, InvalidSession, RequestError)
+    RemoteCallFailed, InvalidSession, RequestError, PublishException)
 
 from cubicweb.web.request import CubicWebRequestBase
 
@@ -567,7 +567,7 @@
             content = self.vreg['views'].main_template(req, template, view=errview)
         except Exception:
             content = self.vreg['views'].main_template(req, 'error-template')
-        if getattr(ex, 'status', None) is not None:
+        if isinstance(ex, PublishException) and ex.status is not None:
             req.status_out = ex.status
         return content
 
@@ -580,11 +580,11 @@
 
     def ajax_error_handler(self, req, ex):
         req.set_header('content-type', 'application/json')
-        status = ex.status
-        if status is None:
-            status = httplib.INTERNAL_SERVER_ERROR
+        status = httplib.INTERNAL_SERVER_ERROR
+        if isinstance(ex, PublishException) and ex.status is not None:
+            status = ex.status
+        req.status_out = status
         json_dumper = getattr(ex, 'dumps', lambda : unicode(ex))
-        req.status_out = status
         return json_dumper()
 
     # special case handling
--- a/web/http_headers.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/web/http_headers.py	Tue Jan 21 15:11:16 2014 +0100
@@ -1307,7 +1307,7 @@
         return self._toRaw(name)
 
     def getHeader(self, name, default=None):
-        """Ret9urns the parsed representation of the given header.
+        """Returns the parsed representation of the given header.
         The exact form of the return value depends on the header in question.
 
         If no parser for the header exists, raise ValueError.
--- a/web/test/unittest_application.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/web/test/unittest_application.py	Tue Jan 21 15:11:16 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -19,13 +19,15 @@
 
 import base64, Cookie
 import sys
+import httplib
 from urllib import unquote
 
 from logilab.common.testlib import TestCase, unittest_main
 from logilab.common.decorators import clear_cache, classproperty
 
 from cubicweb import AuthenticationError, Unauthorized
-from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb import view
+from cubicweb.devtools.testlib import CubicWebTC, real_error_handling
 from cubicweb.devtools.fake import FakeRequest
 from cubicweb.web import LogOut, Redirect, INTERNAL_FIELD_VALUE
 from cubicweb.web.views.basecontrollers import ViewController
@@ -264,6 +266,18 @@
                           {'login-subject': u'the value "admin" is already used, use another one'})
         self.assertEqual(forminfo['values'], req.form)
 
+    def test_ajax_view_raise_arbitrary_error(self):
+        class ErrorAjaxView(view.View):
+            __regid__ = 'test.ajax.error'
+            def call(self):
+                raise Exception('whatever')
+        with self.temporary_appobjects(ErrorAjaxView):
+            with real_error_handling(self.app) as app:
+                req = self.request(vid='test.ajax.error')
+                req.ajax_request = True
+                page = app.handle_request(req, '')
+        self.assertEqual(httplib.INTERNAL_SERVER_ERROR,
+                         req.status_out)
 
     def _test_cleaned(self, kwargs, injected, cleaned):
         req = self.request(**kwargs)
@@ -433,5 +447,6 @@
         req.form['rql'] = 'rql:Any OV1, X WHERE X custom_workflow OV1?'
         self.app_handle_request(req)
 
+
 if __name__ == '__main__':
     unittest_main()
--- a/web/uicfg.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/web/uicfg.py	Tue Jan 21 15:11:16 2014 +0100
@@ -16,7 +16,7 @@
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 """
-This module is now deprecated, see web.views.uicfg.
+This module has been moved to web.views.uicfg.
 """
 __docformat__ = "restructuredtext en"
 
--- a/web/views/workflow.py	Wed Sep 11 18:04:05 2013 +0200
+++ b/web/views/workflow.py	Tue Jan 21 15:11:16 2014 +0100
@@ -445,6 +445,13 @@
     binary = True
 
     def cell_call(self, row=0, col=0):
-        tmpfile = self._cw.session.data[self._cw.form['tmpfile']]
+        key = self._cw.form['tmpfile']
+        if key not in self._cw.session.data:
+            # the temp file is gone and there's nothing
+            # we can do about it
+            # we should probably write it to some well
+            # behaved place and serve it
+            return
+        tmpfile = self._cw.session.data.pop(key)
         self.w(open(tmpfile, 'rb').read())
         os.unlink(tmpfile)