backport stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 05 Jan 2011 17:50:21 +0100
changeset 6782 b5d6f5391695
parent 6762 812445504835 (current diff)
parent 6781 5062d86d6ffe (diff)
child 6783 5bbf827b6caf
backport stable
__pkginfo__.py
i18n/fr.po
server/serverctl.py
server/test/unittest_multisources.py
--- a/__pkginfo__.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/__pkginfo__.py	Wed Jan 05 17:50:21 2011 +0100
@@ -40,7 +40,7 @@
 ]
 
 __depends__ = {
-    'logilab-common': '>= 0.52.0',
+    'logilab-common': '>= 0.54.0',
     'logilab-mtconverter': '>= 0.8.0',
     'rql': '>= 0.27.0',
     'yams': '>= 0.30.1',
--- a/cwconfig.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/cwconfig.py	Wed Jan 05 17:50:21 2011 +0100
@@ -317,6 +317,12 @@
           'help': 'server\'s log level',
           'group': 'main', 'level': 1,
           }),
+        ('umask',
+         {'type' : 'int',
+          'default': 077,
+          'help': 'permission umask for files created by the server',
+          'group': 'main', 'level': 2,
+          }),
         # pyro options
         ('pyro-instance-id',
          {'type' : 'string',
--- a/cwctl.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/cwctl.py	Wed Jan 05 17:50:21 2011 +0100
@@ -494,7 +494,8 @@
             msg = "%s seems to be running. Remove %s by hand if necessary or use \
 the --force option."
             raise ExecutionError(msg % (appid, pidf))
-        helper.start_server(config)
+        if helper.start_server(config) == 1:
+            print 'instance %s started' % appid
 
 
 def init_cmdline_log_threshold(config, loglevel):
--- a/debian/control	Tue Jan 04 14:11:54 2011 +0100
+++ b/debian/control	Wed Jan 05 17:50:21 2011 +0100
@@ -97,7 +97,7 @@
 Package: cubicweb-common
 Architecture: all
 XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.8.0), python-logilab-common (>= 0.51.0), python-yams (>= 0.30.1), python-rql (>= 0.27.0), python-lxml
+Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.8.0), python-logilab-common (>= 0.54.0), python-yams (>= 0.30.1), python-rql (>= 0.27.0), python-lxml
 Recommends: python-simpletal (>= 4.0), python-crypto
 Conflicts: cubicweb-core
 Replaces: cubicweb-core
--- a/devtools/htmlparser.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/devtools/htmlparser.py	Wed Jan 05 17:50:21 2011 +0100
@@ -15,16 +15,17 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""defines a validating HTML parser used in web application tests
-
-"""
+"""defines a validating HTML parser used in web application tests"""
 
 import re
 import sys
 
 from lxml import etree
 
+from logilab.common.deprecation import class_deprecated
+
 from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE
+
 STRICT_DOCTYPE = str(STRICT_DOCTYPE)
 TRANSITIONAL_DOCTYPE = str(TRANSITIONAL_DOCTYPE)
 
@@ -51,10 +52,7 @@
     def __init__(self):
         Validator.__init__(self)
         # XXX understand what's happening under windows
-        validate = True
-        if sys.platform == 'win32':
-            validate = False
-        self.parser = etree.XMLParser(dtd_validation=validate)
+        self.parser = etree.XMLParser(dtd_validation=sys.platform != 'win32')
 
     def preprocess_data(self, data):
         """used to fix potential blockquote mess generated by docutils"""
@@ -87,12 +85,14 @@
         Validator.__init__(self)
         self.parser = etree.XMLParser()
 
+
 class XMLDemotingValidator(SaxOnlyValidator):
     """ some views produce html instead of xhtml, using demote_to_html
 
     this is typically related to the use of external dependencies
     which do not produce valid xhtml (google maps, ...)
     """
+    __metaclass__ = class_deprecated
 
     def preprocess_data(self, data):
         if data.startswith('<?xml'):
--- a/devtools/test/unittest_testlib.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/devtools/test/unittest_testlib.py	Wed Jan 05 17:50:21 2011 +0100
@@ -15,24 +15,23 @@
 #
 # You should have received a copy of the GNU Lesser General Public License along
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
-"""unittests for gct.apptest module
-
-"""
+"""unittests for cw.devtools.testlib module"""
 
 from cStringIO import StringIO
 
-from logilab.common.testlib import (TestCase, unittest_main, TestSuite,
-                                    SkipAwareTextTestRunner)
+from unittest import TextTestRunner
+from logilab.common.testlib import TestSuite, TestCase, unittest_main
 
 from cubicweb.devtools import htmlparser
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.pytestconf import clean_repo_test_cls
 
+
 class WebTestTC(TestCase):
 
     def setUp(self):
         output = StringIO()
-        self.runner = SkipAwareTextTestRunner(stream=output)
+        self.runner = TextTestRunner(stream=output)
 
     def test_error_raised(self):
         class MyWebTest(CubicWebTC):
--- a/devtools/testlib.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/devtools/testlib.py	Wed Jan 05 17:50:21 2011 +0100
@@ -775,20 +775,21 @@
     @nocoverage
     def _check_html(self, output, view, template='main-template'):
         """raises an exception if the HTML is invalid"""
+        output = output.strip()
         try:
             validatorclass = self.vid_validators[view.__regid__]
         except KeyError:
             if view.content_type in ('text/html', 'application/xhtml+xml'):
-                if template is None:
+                if output.startswith('<?xml'):
+                    default_validator = htmlparser.DTDValidator
+                else:
                     default_validator = htmlparser.HTMLValidator
-                else:
-                    default_validator = htmlparser.DTDValidator
             else:
                 default_validator = None
             validatorclass = self.content_type_validators.get(view.content_type,
                                                               default_validator)
         if validatorclass is None:
-            return output.strip()
+            return
         validator = validatorclass()
         if isinstance(validator, htmlparser.DTDValidator):
             # XXX remove <canvas> used in progress widget, unknown in html dtd
--- a/etwist/server.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/etwist/server.py	Wed Jan 05 17:50:21 2011 +0100
@@ -415,8 +415,9 @@
                                      "commands (e.g : 'net start my_instance)'")
         from logilab.common.daemon import daemonize
         LOGGER.info('instance started in the background on %s', root_resource.base_url)
-        if daemonize(config['pid-file']):
-            return # child process
+        whichproc = daemonize(config['pid-file'], umask=config['umask'])
+        if whichproc: # 1 = orig process, 2 = first fork, None = second fork (eg daemon process)
+            return whichproc # parent process
     root_resource.init_publisher() # before changing uid
     if config['uid'] is not None:
         try:
--- a/etwist/twctl.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/etwist/twctl.py	Wed Jan 05 17:50:21 2011 +0100
@@ -32,7 +32,7 @@
 
     def start_server(self, config):
         from cubicweb.etwist import server
-        server.run(config)
+        return server.run(config)
 
 class TWStopHandler(CommandHandler):
     cmdname = 'stop'
--- a/hooks/notification.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/hooks/notification.py	Wed Jan 05 17:50:21 2011 +0100
@@ -22,6 +22,7 @@
 
 from logilab.common.textutils import normalize_text
 
+from cubicweb import RegistryNotFound
 from cubicweb.selectors import is_instance
 from cubicweb.server import hook
 from cubicweb.sobjects.supervising import SupervisionMailOp
@@ -42,8 +43,14 @@
     category = 'notification'
 
     def select_view(self, vid, rset, row=0, col=0):
-        return self._cw.vreg['views'].select_or_none(vid, self._cw, rset=rset,
-                                                     row=row, col=col)
+        try:
+            return self._cw.vreg['views'].select_or_none(vid, self._cw, rset=rset,
+                                                         row=row, col=col)
+        except RegistryNotFound: # can happen in some config
+                                 # (e.g. repo only config with no
+                                 # notification views registered by
+                                 # the instance's cubes)
+            return None
 
 
 class StatusChangeHook(NotificationHook):
@@ -69,7 +76,6 @@
             'comment': comment, 'previous_state': entity.previous_state.name,
             'current_state': entity.new_state.name})
 
-
 class RelationChangeHook(NotificationHook):
     __regid__ = 'notifyrelationchange'
     events = ('before_add_relation', 'after_add_relation',
--- a/hooks/test/unittest_syncschema.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/hooks/test/unittest_syncschema.py	Wed Jan 05 17:50:21 2011 +0100
@@ -24,7 +24,7 @@
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.devtools.repotest import schema_eids_idx, restore_schema_eids_idx
 
-def teardown_module(*args):
+def tearDownModule(*args):
     del SchemaModificationHooksTC.schema_eids
 
 class SchemaModificationHooksTC(CubicWebTC):
--- a/i18n/fr.po	Tue Jan 04 14:11:54 2011 +0100
+++ b/i18n/fr.po	Wed Jan 05 17:50:21 2011 +0100
@@ -4,7 +4,7 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: cubicweb 2.46.0\n"
-"PO-Revision-Date: 2010-09-15 15:12+0200\n"
+"PO-Revision-Date: 2011-01-03 14:35+0100\n"
 "Last-Translator: Logilab Team <contact@logilab.fr>\n"
 "Language-Team: fr <contact@logilab.fr>\n"
 "MIME-Version: 1.0\n"
@@ -215,7 +215,7 @@
 msgstr "À propos de ce site"
 
 msgid "Any"
-msgstr "N'importe"
+msgstr "Tous"
 
 msgid "Attributes permissions:"
 msgstr "Permissions des attributs"
--- a/server/hook.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/hook.py	Wed Jan 05 17:50:21 2011 +0100
@@ -307,9 +307,10 @@
 
     def call_hooks(self, event, session=None, **kwargs):
         try:
-            self.vreg['%s_hooks' % event].call_hooks(event, session, **kwargs)
+            registry = self.vreg['%s_hooks' % event]
         except RegistryNotFound:
-            pass # no hooks for this event
+            return # no hooks for this event
+        registry.call_hooks(event, session, **kwargs)
 
 
 for event in ALL_HOOKS:
@@ -354,6 +355,7 @@
         self.expected = expected
         self.frometypes = more.pop('frometypes', None)
         self.toetypes = more.pop('toetypes', None)
+        assert not more, "unexpected kwargs in match_rtype: %s" % more
 
     @lltrace
     def __call__(self, cls, req, *args, **kwargs):
--- a/server/serverctl.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/serverctl.py	Wed Jan 05 17:50:21 2011 +0100
@@ -237,6 +237,7 @@
         command.append('--loglevel %s' % config['log-threshold'].lower())
         command.append(config.appid)
         os.system(' '.join(command))
+        return 1
 
 
 class RepositoryStopHandler(CommandHandler):
@@ -593,7 +594,7 @@
         # go ! (don't daemonize in debug mode)
         if not os.path.exists(piddir):
             os.makedirs(piddir)
-        if not debug and daemonize(pidfile):
+        if not debug and daemonize(pidfile, umask=config['umask']):
             return
         uid = config['uid']
         if uid is not None:
--- a/server/test/unittest_fti.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_fti.py	Wed Jan 05 17:50:21 2011 +0100
@@ -7,13 +7,18 @@
 from cubicweb.selectors import is_instance
 from cubicweb.entities.adapters import IFTIndexableAdapter
 
+AT_LOGILAB = socket.gethostname().endswith('.logilab.fr')
+
+from logilab.common.testlib import SkipTest
+
+
 class PostgresFTITC(CubicWebTC):
     config = ApptestConfiguration('data', sourcefile='sources_fti')
 
-    def setUp(self):
-        if not socket.gethostname().endswith('.logilab.fr'):
-            self.skipTest('XXX require logilab configuration')
-        super(PostgresFTITC, self).setUp()
+    @classmethod
+    def setUpClass(cls):
+        if not AT_LOGILAB:
+            raise SkipTest('XXX %s: require logilab configuration' % cls.__name__)
 
     def test_occurence_count(self):
         req = self.request()
@@ -44,7 +49,6 @@
             self.assertEqual(req.execute('Card X ORDERBY FTIRANK(X) DESC WHERE X has_text "cubicweb"').rows,
                               [[c3.eid], [c1.eid], [c2.eid]])
 
-
     def test_entity_weight(self):
         class PersonneIFTIndexableAdapter(IFTIndexableAdapter):
             __select__ = is_instance('Personne')
@@ -58,6 +62,7 @@
             self.assertEqual(req.execute('Any X ORDERBY FTIRANK(X) DESC WHERE X has_text "cubicweb"').rows,
                               [[c1.eid], [c3.eid], [c2.eid]])
 
+
 if __name__ == '__main__':
     from logilab.common.testlib import unittest_main
     unittest_main()
--- a/server/test/unittest_hook.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_hook.py	Wed Jan 05 17:50:21 2011 +0100
@@ -79,7 +79,7 @@
 config.bootstrap_cubes()
 schema = config.load_schema()
 
-def teardown_module(*args):
+def tearDownModule(*args):
     global config, schema
     del config, schema
 
--- a/server/test/unittest_ldapuser.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_ldapuser.py	Wed Jan 05 17:50:21 2011 +0100
@@ -70,13 +70,13 @@
     # don't check upassword !
     return self.extid2eid(user['dn'], 'CWUser', session)
 
-def setup_module(*args):
+def setUpModule(*args):
     global repo
     LDAPUserSourceTC._init_repo()
     repo = LDAPUserSourceTC.repo
     add_ldap_source(LDAPUserSourceTC.cnx)
 
-def teardown_module(*args):
+def tearDownModule(*args):
     global repo
     repo.shutdown()
     del repo
--- a/server/test/unittest_migractions.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_migractions.py	Wed Jan 05 17:50:21 2011 +0100
@@ -34,7 +34,7 @@
 from cubicweb.server.migractions import *
 
 migrschema = None
-def teardown_module(*args):
+def tearDownModule(*args):
     global migrschema
     del migrschema
     del MigrationCommandsTC.origschema
--- a/server/test/unittest_msplanner.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_msplanner.py	Wed Jan 05 17:50:21 2011 +0100
@@ -76,11 +76,11 @@
 
 
 # keep cnx so it's not garbage collected and the associated session is closed
-def setup_module(*args):
+def setUpModule(*args):
     global repo, cnx
     repo, cnx = init_test_database(apphome=BaseMSPlannerTC.datadir)
 
-def teardown_module(*args):
+def tearDownModule(*args):
     global repo, cnx
     del repo, cnx
 
--- a/server/test/unittest_multisources.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_multisources.py	Wed Jan 05 17:50:21 2011 +0100
@@ -45,6 +45,7 @@
 PyroRQLSource_get_connection = PyroRQLSource.get_connection
 Connection_close = Connection.close
 
+<<<<<<< /home/syt/src/fcubicweb/cubicweb/server/test/unittest_multisources.py
 def add_extern_mapping(source):
     execute = source._cw.execute
     for etype in ('Card', 'Affaire', 'State'):
@@ -56,6 +57,9 @@
 
 
 def setup_module(*args):
+=======
+def setUpModule(*args):
+>>>>>>> /tmp/unittest_multisources.py~other.zhOXgM
     global repo2, cnx2, repo3, cnx3
     cfg1 = ExternalSource1Configuration('data', apphome=TwoSourcesTC.datadir)
     repo2, cnx2 = init_test_database(config=cfg1)
@@ -74,7 +78,7 @@
     # pool though we want to keep cnx2 valid
     Connection.close = lambda x: None
 
-def teardown_module(*args):
+def tearDownModule(*args):
     PyroRQLSource.get_connection = PyroRQLSource_get_connection
     Connection.close = Connection_close
     global repo2, cnx2, repo3, cnx3
--- a/server/test/unittest_querier.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_querier.py	Wed Jan 05 17:50:21 2011 +0100
@@ -62,11 +62,11 @@
                           ('C0 text,C1 integer', {'A': 'table0.C0', 'B': 'table0.C1'}))
 
 
-def setup_module(*args):
+def setUpModule(*args):
     global repo, cnx
     repo, cnx = init_test_database(apphome=UtilsTC.datadir)
 
-def teardown_module(*args):
+def tearDownModule(*args):
     global repo, cnx
     cnx.close()
     repo.shutdown()
--- a/server/test/unittest_rql2sql.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_rql2sql.py	Wed Jan 05 17:50:21 2011 +0100
@@ -38,7 +38,7 @@
     pass # already registered
 
 
-def setup_module(*args):
+def setUpModule():
     global config, schema
     config = TestServerConfiguration('data', apphome=CWRQLTC.datadir)
     config.bootstrap_cubes()
@@ -47,7 +47,7 @@
     schema['state_of'].inlined = False
     schema['comments'].inlined = False
 
-def teardown_module(*args):
+def tearDownModule():
     global config, schema
     del config, schema
 
--- a/server/test/unittest_rqlannotation.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_rqlannotation.py	Wed Jan 05 17:50:21 2011 +0100
@@ -22,11 +22,11 @@
 from cubicweb.devtools.repotest import BaseQuerierTC
 
 
-def setup_module(*args):
+def setUpModule(*args):
     global repo, cnx
     repo, cnx = init_test_database(apphome=SQLGenAnnotatorTC.datadir)
 
-def teardown_module(*args):
+def tearDownModule(*args):
     global repo, cnx
     del repo, cnx
 
--- a/server/test/unittest_schemaserial.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_schemaserial.py	Wed Jan 05 17:50:21 2011 +0100
@@ -25,14 +25,14 @@
 from cubicweb.schema import CubicWebSchemaLoader
 from cubicweb.devtools import TestServerConfiguration
 
-def setup_module(*args):
+def setUpModule(*args):
     global schema, config
     loader = CubicWebSchemaLoader()
     config = TestServerConfiguration('data', apphome=Schema2RQLTC.datadir)
     config.bootstrap_cubes()
     schema = loader.load(config)
 
-def teardown_module(*args):
+def tearDownModule(*args):
     global schema, config
     del schema, config
 
--- a/server/test/unittest_ssplanner.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/test/unittest_ssplanner.py	Wed Jan 05 17:50:21 2011 +0100
@@ -21,11 +21,11 @@
 from cubicweb.server.ssplanner import SSPlanner
 
 # keep cnx so it's not garbage collected and the associated session closed
-def setup_module(*args):
+def setUpModule(*args):
     global repo, cnx
     repo, cnx = init_test_database(apphome=SSPlannerTC.datadir)
 
-def teardown_module(*args):
+def tearDownModule(*args):
     global repo, cnx
     del repo, cnx
 
--- a/server/utils.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/server/utils.py	Wed Jan 05 17:50:21 2011 +0100
@@ -20,6 +20,7 @@
 
 import sys
 import string
+import logging
 from threading import Timer, Thread
 from getpass import getpass
 from random import choice
@@ -129,6 +130,10 @@
         def auto_restart_func(self=self, func=func, args=args):
             try:
                 func(*args)
+            except:
+                logger = logging.getLogger('cubicweb.repository')
+                logger.exception('Unhandled exception in LoopTask %s', self.name)
+                raise
             finally:
                 self.start()
         self.func = auto_restart_func
@@ -158,6 +163,10 @@
         def auto_remove_func(self=self, func=target):
             try:
                 func()
+            except:
+                logger = logging.getLogger('cubicweb.repository')
+                logger.exception('Unhandled exception in RepoThread %s', self._name)
+                raise
             finally:
                 self.running_threads.remove(self)
         Thread.__init__(self, target=auto_remove_func)
--- a/skeleton/test/realdb_test_CUBENAME.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/skeleton/test/realdb_test_CUBENAME.py	Wed Jan 05 17:50:21 2011 +0100
@@ -21,7 +21,7 @@
 from cubicweb.devtools import buildconfig, loadconfig
 from cubicweb.devtools.testlib import RealDBTest
 
-def setup_module(options):
+def setUpModule(options):
     if options.source:
         configcls = loadconfig(options.source)
     elif options.dbname is None:
@@ -33,7 +33,7 @@
     RealDatabaseTC.configcls = configcls
 
 class RealDatabaseTC(RealDBTest):
-    configcls = None # set by setup_module()
+    configcls = None # set by setUpModule()
 
     def test_all_primaries(self):
         for rset in self.iter_individual_rsets(limit=50):
--- a/test/unittest_rqlrewrite.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/test/unittest_rqlrewrite.py	Wed Jan 05 17:50:21 2011 +0100
@@ -27,7 +27,7 @@
 from cubicweb.devtools import repotest, TestServerConfiguration
 
 
-def setup_module(*args):
+def setUpModule(*args):
     global rqlhelper, schema
     config = TestServerConfiguration(RQLRewriteTC.datapath('rewrite'))
     config.bootstrap_cubes()
@@ -39,7 +39,7 @@
                                                      'has_text': 'fti'})
     repotest.do_monkey_patch()
 
-def teardown_module(*args):
+def tearDownModule(*args):
     repotest.undo_monkey_patch()
     global rqlhelper, schema
     del rqlhelper, schema
--- a/test/unittest_schema.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/test/unittest_schema.py	Wed Jan 05 17:50:21 2011 +0100
@@ -226,7 +226,7 @@
 
                               'value',
 
-                              'wf_info_for', 'wikiid', 'workflow_of']
+                              'wf_info_for', 'wikiid', 'workflow_of', 'tr_count']
 
         self.assertListEqual(relations, sorted(expected_relations))
 
--- a/view.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/view.py	Wed Jan 05 17:50:21 2011 +0100
@@ -40,11 +40,6 @@
 NOINDEX = u'<meta name="ROBOTS" content="NOINDEX" />'
 NOFOLLOW = u'<meta name="ROBOTS" content="NOFOLLOW" />'
 
-
-CW_XMLNS = '''[
-  <!ATTLIST html xmlns:cubicweb CDATA  #FIXED \'http://www.logilab.org/2008/cubicweb\'  >
-  ]
-'''
 CW_XHTML_EXTENSIONS = '''[
   <!ATTLIST html xmlns:cubicweb CDATA  #FIXED \'http://www.logilab.org/2008/cubicweb\'  >
 
@@ -86,9 +81,9 @@
   "> ] '''
 
 TRANSITIONAL_DOCTYPE = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" %s>\n' % CW_XHTML_EXTENSIONS
-TRANSITIONAL_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" %s>\n' % CW_XMLNS
+TRANSITIONAL_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n'
 STRICT_DOCTYPE = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" %s>\n' % CW_XHTML_EXTENSIONS
-STRICT_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" %s>\n' % CW_XMLNS
+STRICT_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
 
 # base view object ############################################################
 
--- a/web/component.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/web/component.py	Wed Jan 05 17:50:21 2011 +0100
@@ -334,30 +334,39 @@
 # high level abstract classes ##################################################
 
 class RQLCtxComponent(CtxComponent):
-    """abstract box for boxes displaying the content of a rql query not
-    related to the current result set.
+    """abstract box for boxes displaying the content of a rql query not related
+    to the current result set.
+
+    Notice that this class's init_rendering implemention is overwriting context
+    result set (eg `cw_rset`) with the result set returned by execution of
+    `to_display_rql()`.
     """
-    rql  = None
+    rql = None
 
     def to_display_rql(self):
+        """return arguments to give to self._cw.execute, as a tuple, to build
+        the result set to be displayed by this box.
+        """
         assert self.rql is not None, self.__regid__
         return (self.rql,)
 
     def init_rendering(self):
-        rset = self._cw.execute(*self.to_display_rql())
-        if not rset:
+        super(RQLCtxComponent, self).init_rendering()
+        self.cw_rset = self._cw.execute(*self.to_display_rql())
+        if not self.cw_rset:
             raise EmptyComponent()
+
+    def render_body(self, w):
+        rset = self.cw_rset
         if len(rset[0]) == 2:
-            self.items = []
+            items = []
             for i, (eid, label) in enumerate(rset):
                 entity = rset.get_entity(i, 0)
-                self.items.append(self.build_link(label, entity.absolute_url()))
+                items.append(self.build_link(label, entity.absolute_url()))
         else:
-            self.items = [self.build_link(e.dc_title(), e.absolute_url())
-                          for e in rset.entities()]
-
-    def render_body(self, w):
-        self.render_items(w)
+            items = [self.build_link(e.dc_title(), e.absolute_url())
+                     for e in rset.entities()]
+        self.render_items(w, items)
 
 
 class EditRelationMixIn(ReloadableMixIn):
--- a/web/test/unittest_formfields.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/web/test/unittest_formfields.py	Wed Jan 05 17:50:21 2011 +0100
@@ -29,7 +29,7 @@
 
 from cubes.file.entities import File
 
-def setup_module(*args):
+def setUpModule(*args):
     global schema
     config = TestServerConfiguration('data', apphome=GuessFieldTC.datadir)
     config.bootstrap_cubes()
--- a/web/test/unittest_formwidgets.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/web/test/unittest_formwidgets.py	Wed Jan 05 17:50:21 2011 +0100
@@ -24,7 +24,7 @@
 
 from cubes.file.entities import File
 
-def setup_module(*args):
+def setUpModule(*args):
     global schema
     config = TestServerConfiguration('data', apphome=WidgetsTC.datadir)
     config.bootstrap_cubes()
--- a/web/views/basetemplates.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/web/views/basetemplates.py	Wed Jan 05 17:50:21 2011 +0100
@@ -347,7 +347,7 @@
                 self._cw, rset=self.cw_rset, view=view, context=context)
             for comp in components:
                 comp.render(w=w)
-                w(u'&nbsp;')
+                w(u'&#160;')
             w(u'</td>')
         w(u'</tr></table>\n')
 
--- a/web/views/workflow.py	Tue Jan 04 14:11:54 2011 +0100
+++ b/web/views/workflow.py	Wed Jan 05 17:50:21 2011 +0100
@@ -74,6 +74,7 @@
 _afs = uicfg.autoform_section
 _afs.tag_subject_of(('TrInfo', 'to_state', '*'), 'main', 'hidden')
 _afs.tag_subject_of(('TrInfo', 'from_state', '*'), 'main', 'hidden')
+_afs.tag_attribute(('TrInfo', 'tr_count'), 'main', 'hidden')
 _afs.tag_object_of(('State', 'allowed_transition', '*'), 'main', 'attributes')