# HG changeset patch # User David Douard # Date 1373378306 -7200 # Node ID 9b58a6406a64fbb3277d709dfc5af0a45ad1a5cb # Parent 65b8236e1bb4431263215727955ab21817119fdc# Parent 32b4d5314fd90fe050c931886190f9a372686148 [merge] start 3.18 development diff -r 65b8236e1bb4 -r 9b58a6406a64 __pkginfo__.py --- a/__pkginfo__.py Thu Jun 13 17:27:43 2013 +0200 +++ b/__pkginfo__.py Tue Jul 09 15:58:26 2013 +0200 @@ -22,7 +22,7 @@ modname = distname = "cubicweb" -numversion = (3, 17, 2) +numversion = (3, 17, 3) version = '.'.join(str(num) for num in numversion) description = "a repository of entities / relations for knowledge management" diff -r 65b8236e1bb4 -r 9b58a6406a64 cubicweb.spec --- a/cubicweb.spec Thu Jun 13 17:27:43 2013 +0200 +++ b/cubicweb.spec Tue Jul 09 15:58:26 2013 +0200 @@ -7,7 +7,7 @@ %endif Name: cubicweb -Version: 3.17.2 +Version: 3.17.3 Release: logilab.1%{?dist} Summary: CubicWeb is a semantic web application framework Source0: http://download.logilab.org/pub/cubicweb/cubicweb-%{version}.tar.gz diff -r 65b8236e1bb4 -r 9b58a6406a64 debian.hardy/compat --- a/debian.hardy/compat Thu Jun 13 17:27:43 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -5 diff -r 65b8236e1bb4 -r 9b58a6406a64 debian.hardy/rules --- a/debian.hardy/rules Thu Jun 13 17:27:43 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -#!/usr/bin/make -f -# Sample debian/rules that uses debhelper. -# GNU copyright 1997 to 1999 by Joey Hess. - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -PY_VERSION:=$(shell pyversions -d) - -build: build-stamp -build-stamp: - dh_testdir - # XXX doesn't work if logilab-doctools, logilab-xml are not in build depends - # and I can't get pbuilder find them in its chroot :( - # cd doc && make - # FIXME cleanup and use sphinx-build as build-depends ? - NO_SETUPTOOLS=1 python setup.py build - touch build-stamp - -clean: - dh_testdir - dh_testroot - rm -f build-stamp configure-stamp - rm -rf build - #rm -rf debian/cubicweb-*/ - find . -name "*.pyc" -delete - rm -f $(basename $(wildcard debian/*.in)) - dh_clean - -install: build $(basename $(wildcard debian/*.in)) - dh_testdir - dh_testroot - dh_clean - dh_installdirs - - NO_SETUPTOOLS=1 python setup.py -q install --no-compile --prefix=debian/tmp/usr - - # Put all the python library and data in cubicweb-common - # and scripts in cubicweb-server - dh_install -vi - # cwctl in the cubicweb-ctl package - rm -f debian/cubicweb-common/usr/share/pyshared/cubicweb/cwctl.py - - - # Remove unittests directory (should be available in cubicweb-dev only) - rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/server/test - rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/hooks/test - rm -rf debian/cubicweb-server/usr/lib/${PY_VERSION}/site-packages/cubicweb/sobjects/test - rm -rf debian/cubicweb-web/usr/lib/${PY_VERSION}/site-packages/cubicweb/web/test - rm -rf debian/cubicweb-twisted/usr/lib/${PY_VERSION}/site-packages/cubicweb/etwist/test - rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/ext/test - rm -rf debian/cubicweb-common/usr/lib/${PY_VERSION}/site-packages/cubicweb/entities/test - - # cubes directory must be managed as a valid python module - touch debian/cubicweb-common/usr/share/cubicweb/cubes/__init__.py - -%: %.in - sed "s/PY_VERSION/${PY_VERSION}/g" < $< > $@ - -# Build architecture-independent files here. -binary-indep: build install - dh_testdir - dh_testroot -i - dh_pycentral -i - dh_installinit -i -n --name cubicweb -u"defaults 99" - dh_installlogrotate -i - dh_installdocs -i -A README - dh_installman -i - dh_installchangelogs -i - dh_link -i - dh_compress -i -X.py -X.ini -X.xml - dh_fixperms -i - dh_installdeb -i - dh_gencontrol -i - dh_md5sums -i - dh_builddeb -i - -binary-arch: - -binary: binary-indep -.PHONY: build clean binary binary-indep binary-arch - diff -r 65b8236e1bb4 -r 9b58a6406a64 debian/changelog --- a/debian/changelog Thu Jun 13 17:27:43 2013 +0200 +++ b/debian/changelog Tue Jul 09 15:58:26 2013 +0200 @@ -1,3 +1,9 @@ +cubicweb (3.17.3-1) unstable; urgency=low + + * new upstream release + + -- David Douard Tue, 09 Jul 2013 15:10:16 +0200 + cubicweb (3.17.2-1) unstable; urgency=low * new upstream release diff -r 65b8236e1bb4 -r 9b58a6406a64 devtools/httptest.py --- a/devtools/httptest.py Thu Jun 13 17:27:43 2013 +0200 +++ b/devtools/httptest.py Tue Jul 09 15:58:26 2013 +0200 @@ -20,6 +20,7 @@ """ __docformat__ = "restructuredtext en" +import random import threading import socket import httplib @@ -46,6 +47,8 @@ .. see:: :func:`test.test_support.bind_port` """ + ports_scan = list(ports_scan) + random.shuffle(ports_scan) # lower the chance of race condition for port in ports_scan: try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) diff -r 65b8236e1bb4 -r 9b58a6406a64 entities/adapters.py --- a/entities/adapters.py Thu Jun 13 17:27:43 2013 +0200 +++ b/entities/adapters.py Tue Jul 09 15:58:26 2013 +0200 @@ -379,6 +379,7 @@ class IUserFriendlyError(view.EntityAdapter): __regid__ = 'IUserFriendlyError' __abstract__ = True + def __init__(self, *args, **kwargs): self.exc = kwargs.pop('exc') super(IUserFriendlyError, self).__init__(*args, **kwargs) @@ -386,11 +387,27 @@ class IUserFriendlyUniqueTogether(IUserFriendlyError): __select__ = match_exception(UniqueTogetherError) + def raise_user_exception(self): etype, rtypes = self.exc.args - msg = self._cw._('violates unique_together constraints (%s)') % ( - ', '.join([self._cw._(rtype) for rtype in rtypes])) - raise ValidationError(self.entity.eid, dict((col, msg) for col in rtypes)) + # Because of index name size limits (e.g: postgres around 64, + # sqlserver around 128), we cannot be sure of what we got, + # especially for the rtypes part. + # Hence we will try to validate them, and handle invalid ones + # in the most user-friendly manner ... + _ = self._cw._ + schema = self.entity._cw.vreg.schema + rtypes_msg = {} + for rtype in rtypes: + if rtype in schema: + rtypes_msg[rtype] = _('%s is part of violated unicity constraint') % rtype + globalmsg = _('some relations %sviolate a unicity constraint') + if len(rtypes) != len(rtypes_msg): # we got mangled/missing rtypes + globalmsg = globalmsg % _('(not all shown here) ') + else: + globalmsg = globalmsg % '' + rtypes_msg['unicity constraint'] = globalmsg + raise ValidationError(self.entity.eid, rtypes_msg) # deprecated ################################################################### diff -r 65b8236e1bb4 -r 9b58a6406a64 hooks/security.py --- a/hooks/security.py Thu Jun 13 17:27:43 2013 +0200 +++ b/hooks/security.py Tue Jul 09 15:58:26 2013 +0200 @@ -23,10 +23,13 @@ from logilab.common.registry import objectify_predicate +from yams import buildobjs + from cubicweb import Unauthorized from cubicweb.server import BEFORE_ADD_RELATIONS, ON_COMMIT_ADD_RELATIONS, hook +_DEFAULT_UPDATE_ATTRPERM = buildobjs.DEFAULT_ATTRPERMS['update'] def check_entity_attributes(session, entity, editedattrs=None, creation=False): eid = entity.eid eschema = entity.e_schema @@ -39,9 +42,26 @@ if attr in dontcheck: continue rdef = eschema.rdef(attr) - if rdef.final: # non final relation are checked by other hooks - # add/delete should be equivalent (XXX: unify them into 'update' ?) - if creation and not rdef.permissions.get('update'): + if rdef.final: # non final relation are checked by standard hooks + # attributes only have a specific 'update' permission + updateperm = rdef.permissions.get('update') + # comparison below works because the default update perm is: + # + # ('managers', ERQLExpression(Any X WHERE U has_update_permission X, X eid %(x)s, U eid %(u)s)) + # + # is deserialized in this order (groups first), and ERQLExpression + # implements comparison by expression. + if updateperm == _DEFAULT_UPDATE_ATTRPERM: + # The default update permission is to delegate to the entity + # update permission. This is an historical artefact but it is + # costly (in general). Hence we take this permission object as a + # marker saying "no specific" update permissions for this + # attribute. Thus we just do nothing. + continue + if creation and updateperm == (): + # That actually means an immutable attribute. We make an + # _exception_ to the `check attr update perms at entity create & + # update time` rule for this case. continue rdef.check_perm(session, 'update', eid=eid) diff -r 65b8236e1bb4 -r 9b58a6406a64 i18n/de.po --- a/i18n/de.po Thu Jun 13 17:27:43 2013 +0200 +++ b/i18n/de.po Tue Jul 09 15:58:26 2013 +0200 @@ -114,6 +114,10 @@ msgstr "%s Fehlerbericht" #, python-format +msgid "%s is part of violated unicity constraint" +msgstr "" + +#, python-format msgid "%s not estimated" msgstr "%s unbekannt(e)" @@ -145,6 +149,9 @@ msgid "(UNEXISTANT EID)" msgstr "(EID nicht gefunden)" +msgid "(not all shown here) " +msgstr "" + #, python-format msgid "(suppressed) entity #%d" msgstr "" @@ -3861,6 +3868,10 @@ "Eine oder mehrere frühere Transaktion(en) betreffen die Tntität. Machen Sie " "sie zuerst rückgängig." +#, python-format +msgid "some relations %sviolate a unicity constraint" +msgstr "" + msgid "sorry, the server is unable to handle this query" msgstr "Der Server kann diese Anfrage leider nicht bearbeiten." diff -r 65b8236e1bb4 -r 9b58a6406a64 i18n/en.po --- a/i18n/en.po Thu Jun 13 17:27:43 2013 +0200 +++ b/i18n/en.po Tue Jul 09 15:58:26 2013 +0200 @@ -106,6 +106,10 @@ msgstr "" #, python-format +msgid "%s is part of violated unicity constraint" +msgstr "" + +#, python-format msgid "%s not estimated" msgstr "" @@ -137,6 +141,9 @@ msgid "(UNEXISTANT EID)" msgstr "" +msgid "(not all shown here) " +msgstr "" + #, python-format msgid "(suppressed) entity #%d" msgstr "" @@ -3766,6 +3773,10 @@ msgid "some later transaction(s) touch entity, undo them first" msgstr "" +#, python-format +msgid "some relations %sviolate a unicity constraint" +msgstr "" + msgid "sorry, the server is unable to handle this query" msgstr "" diff -r 65b8236e1bb4 -r 9b58a6406a64 i18n/es.po --- a/i18n/es.po Thu Jun 13 17:27:43 2013 +0200 +++ b/i18n/es.po Tue Jul 09 15:58:26 2013 +0200 @@ -115,6 +115,10 @@ msgstr "%s reporte de errores" #, python-format +msgid "%s is part of violated unicity constraint" +msgstr "" + +#, python-format msgid "%s not estimated" msgstr "%s no estimado(s)" @@ -146,6 +150,9 @@ msgid "(UNEXISTANT EID)" msgstr "(EID INEXISTENTE" +msgid "(not all shown here) " +msgstr "" + #, python-format msgid "(suppressed) entity #%d" msgstr "" @@ -3909,6 +3916,10 @@ msgstr "" "Las transacciones más recientes modificaron esta entidad, anúlelas primero" +#, python-format +msgid "some relations %sviolate a unicity constraint" +msgstr "" + msgid "sorry, the server is unable to handle this query" msgstr "Lo sentimos, el servidor no puede manejar esta consulta" diff -r 65b8236e1bb4 -r 9b58a6406a64 i18n/fr.po --- a/i18n/fr.po Thu Jun 13 17:27:43 2013 +0200 +++ b/i18n/fr.po Tue Jul 09 15:58:26 2013 +0200 @@ -115,6 +115,10 @@ msgstr "%s rapport d'erreur" #, python-format +msgid "%s is part of violated unicity constraint" +msgstr "%s appartient à une contrainte d'unicité transgressée" + +#, python-format msgid "%s not estimated" msgstr "%s non estimé(s)" @@ -148,6 +152,9 @@ msgid "(UNEXISTANT EID)" msgstr "(EID INTROUVABLE)" +msgid "(not all shown here) " +msgstr "(toutes ne sont pas montrées)" + #, python-format msgid "(suppressed) entity #%d" msgstr "entité #%d (supprimée)" @@ -3922,6 +3929,10 @@ msgstr "" "des transactions plus récentes modifient cette entité, annulez les d'abord" +#, python-format +msgid "some relations %sviolate a unicity constraint" +msgstr "certaines relations %stransgressent une contrainte d'unicité" + msgid "sorry, the server is unable to handle this query" msgstr "désolé, le serveur ne peut traiter cette requête" diff -r 65b8236e1bb4 -r 9b58a6406a64 server/repository.py --- a/server/repository.py Thu Jun 13 17:27:43 2013 +0200 +++ b/server/repository.py Tue Jul 09 15:58:26 2013 +0200 @@ -1661,7 +1661,7 @@ # client was not yet connected to the repo return if not session.closed: - session.close() + self.close(session.id) daemon.removeConnection = removeConnection return daemon diff -r 65b8236e1bb4 -r 9b58a6406a64 server/session.py --- a/server/session.py Thu Jun 13 17:27:43 2013 +0200 +++ b/server/session.py Tue Jul 09 15:58:26 2013 +0200 @@ -720,14 +720,13 @@ class Session(RequestSessionBase): """Repository user session - This tie all together: + This ties all together: * session id, * user, * connections set, * other session data. - About session storage / transactions - ------------------------------------ + **About session storage / transactions** Here is a description of internal session attributes. Besides :attr:`data` and :attr:`transaction_data`, you should not have to use attributes diff -r 65b8236e1bb4 -r 9b58a6406a64 server/sources/native.py --- a/server/sources/native.py Thu Jun 13 17:27:43 2013 +0200 +++ b/server/sources/native.py Tue Jul 09 15:58:26 2013 +0200 @@ -410,14 +410,14 @@ def init(self, activated, source_entity): - super(NativeSQLSource, self).init(activated, source_entity) - self.init_creating(source_entity._cw.cnxset) try: # test if 'asource' column exists query = self.dbhelper.sql_add_limit_offset('SELECT asource FROM entities', 1) source_entity._cw.system_sql(query) except Exception as ex: self.eid_type_source = self.eid_type_source_pre_131 + super(NativeSQLSource, self).init(activated, source_entity) + self.init_creating(source_entity._cw.cnxset) def shutdown(self): if self._eid_creation_cnx: @@ -757,15 +757,15 @@ if ex.__class__.__name__ == 'IntegrityError': # need string comparison because of various backends for arg in ex.args: - mo = re.search('unique_cw_[^ ]+_idx', arg) + # postgres and sqlserver + mo = re.search('"unique_cw_[^ ]+"', arg) if mo is not None: - index_name = mo.group(0) - # right-chop '_idx' postfix - # (garanteed to be there, see regexp above) - elements = index_name[:-4].split('_cw_')[1:] + index_name = mo.group(0)[1:-1] # eat the surrounding " pair + elements = index_name.split('_cw_')[1:] etype = elements[0] rtypes = elements[1:] raise UniqueTogetherError(etype, rtypes) + # sqlite mo = re.search('columns (.*) are not unique', arg) if mo is not None: # sqlite in use # we left chop the 'cw_' prefix of attribute names diff -r 65b8236e1bb4 -r 9b58a6406a64 server/test/unittest_ldapsource.py --- a/server/test/unittest_ldapsource.py Thu Jun 13 17:27:43 2013 +0200 +++ b/server/test/unittest_ldapsource.py Tue Jul 09 15:58:26 2013 +0200 @@ -142,16 +142,26 @@ return self._pull(self.session) def setup_database(self): - if self.test_db_id == 'ldap-feed': - with self.session.repo.internal_session(safe=True) as session: - session.execute('DELETE Any E WHERE E cw_source S, S name "ldap"') - session.commit() - if self.test_db_id == 'ldap-feed': - src = self.sexecute('CWSource S WHERE S name "ldap"').get_entity(0,0) - src.cw_set(config=CONFIG_LDAPFEED) - self.session.commit() + with self.session.repo.internal_session(safe=True) as session: + session.execute('DELETE Any E WHERE E cw_source S, S name "ldap"') + session.execute('SET S config %(conf)s, S url %(url)s ' + 'WHERE S is CWSource, S name "ldap"', + {"conf": CONFIG_LDAPFEED, 'url': URL} ) + session.commit() self.pull() + def add_ldap_entry(self, dn, mods): + """ + add an LDAP entity + """ + modcmd = ['dn: %s'%dn, 'changetype: add'] + for key, values in mods.iteritems(): + if isinstance(values, basestring): + values = [values] + for value in values: + modcmd.append('%s: %s'%(key, value)) + self._ldapmodify(modcmd) + def delete_ldap_entry(self, dn): """ delete an LDAP entity @@ -328,9 +338,23 @@ 'deactivated') # check that it doesn't choke self.pull() - # reset the ldap database - self.tearDownClass() - self.setUpClass() + # reinsert syt + self.add_ldap_entry('uid=syt,ou=People,dc=cubicweb,dc=test', + { 'objectClass': ['OpenLDAPperson','posixAccount','top','shadowAccount'], + 'cn': 'Sylvain Thenault', + 'sn': 'Thenault', + 'gidNumber': '1004', + 'uid': 'syt', + 'homeDirectory': '/home/syt', + 'shadowFlag': '134538764', + 'uidNumber': '1004', + 'givenName': 'Sylvain', + 'telephoneNumber': '106', + 'displayName': 'sthenault', + 'gecos': 'Sylvain Thenault', + 'mail': ['sylvain.thenault@logilab.fr','syt@logilab.fr'], + 'userPassword': 'syt', + }) self.pull() self.assertEqual(self.execute('Any N WHERE U login "syt", ' 'U in_state S, S name N').rows[0][0], @@ -429,8 +453,5 @@ - - - if __name__ == '__main__': unittest_main() diff -r 65b8236e1bb4 -r 9b58a6406a64 server/test/unittest_repository.py --- a/server/test/unittest_repository.py Thu Jun 13 17:27:43 2013 +0200 +++ b/server/test/unittest_repository.py Tue Jul 09 15:58:26 2013 +0200 @@ -52,16 +52,18 @@ and relation """ - def test_uniquetogether(self): + def test_unique_together_constraint(self): self.execute('INSERT Societe S: S nom "Logilab", S type "SSLL", S cp "75013"') with self.assertRaises(ValidationError) as wraperr: self.execute('INSERT Societe S: S nom "Logilab", S type "SSLL", S cp "75013"') - self.assertEqual({'nom': u'violates unique_together constraints (cp, nom, type)', - 'cp': u'violates unique_together constraints (cp, nom, type)', - 'type': u'violates unique_together constraints (cp, nom, type)'}, - wraperr.exception.args[1]) + self.assertEqual( + {'cp': u'cp is part of violated unicity constraint', + 'nom': u'nom is part of violated unicity constraint', + 'type': u'type is part of violated unicity constraint', + 'unicity constraint': u'some relations violate a unicity constraint'}, + wraperr.exception.args[1]) - def test_unique_together(self): + def test_unique_together_schema(self): person = self.repo.schema.eschema('Personne') self.assertEqual(len(person._unique_together), 1) self.assertItemsEqual(person._unique_together[0], @@ -272,19 +274,21 @@ def test_initial_schema(self): schema = self.repo.schema # check order of attributes is respected - self.assertListEqual([r.type for r in schema.eschema('CWAttribute').ordered_relations() - if not r.type in ('eid', 'is', 'is_instance_of', 'identity', - 'creation_date', 'modification_date', 'cwuri', - 'owned_by', 'created_by', 'cw_source', - 'update_permission', 'read_permission', - 'in_basket')], - ['relation_type', - 'from_entity', 'to_entity', - 'constrained_by', - 'cardinality', 'ordernum', - 'indexed', 'fulltextindexed', 'internationalizable', - 'defaultval', 'extra_props', - 'description', 'description_format']) + notin = set(('eid', 'is', 'is_instance_of', 'identity', + 'creation_date', 'modification_date', 'cwuri', + 'owned_by', 'created_by', 'cw_source', + 'update_permission', 'read_permission', + 'in_basket')) + self.assertListEqual(['relation_type', + 'from_entity', 'to_entity', + 'constrained_by', + 'cardinality', 'ordernum', + 'indexed', 'fulltextindexed', 'internationalizable', + 'defaultval', 'extra_props', + 'description', 'description_format'], + [r.type + for r in schema.eschema('CWAttribute').ordered_relations() + if r.type not in notin]) self.assertEqual(schema.eschema('CWEType').main_attribute(), 'name') self.assertEqual(schema.eschema('State').main_attribute(), 'name') diff -r 65b8236e1bb4 -r 9b58a6406a64 server/test/unittest_schemaserial.py --- a/server/test/unittest_schemaserial.py Thu Jun 13 17:27:43 2013 +0200 +++ b/server/test/unittest_schemaserial.py Tue Jul 09 15:58:26 2013 +0200 @@ -28,6 +28,7 @@ from logilab.database import get_db_helper from yams import register_base_type, unregister_base_type +schema = config = None def setUpModule(*args): register_base_type('BabarTestType', ('jungle_speed',)) helper = get_db_helper('sqlite') @@ -44,7 +45,7 @@ def tearDownModule(*args): global schema, config - del schema, config + schema = config = None unregister_base_type('BabarTestType') helper = get_db_helper('sqlite') @@ -63,29 +64,29 @@ class Schema2RQLTC(TestCase): def test_eschema2rql1(self): - self.assertListEqual(list(eschema2rql(schema.eschema('CWAttribute'))), - [ + self.assertListEqual([ ('INSERT CWEType X: X description %(description)s,X final %(final)s,X name %(name)s', {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the instance schema', - 'name': u'CWAttribute', 'final': False}) - ]) + 'name': u'CWAttribute', 'final': False})], + list(eschema2rql(schema.eschema('CWAttribute')))) def test_eschema2rql2(self): - self.assertListEqual(list(eschema2rql(schema.eschema('String'))), [ + self.assertListEqual([ ('INSERT CWEType X: X description %(description)s,X final %(final)s,X name %(name)s', - {'description': u'', 'final': True, 'name': u'String'})]) + {'description': u'', 'final': True, 'name': u'String'})], + list(eschema2rql(schema.eschema('String')))) def test_eschema2rql_specialization(self): # x: None since eschema.eid are None - self.assertListEqual(sorted(specialize2rql(schema)), - [('SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', - {'et': None, 'x': None}), - ('SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', - {'et': None, 'x': None}), - ('SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', - {'et': None, 'x': None}), - ('SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', - {'et': None, 'x': None})]) + self.assertListEqual([('SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', + {'et': None, 'x': None}), + ('SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', + {'et': None, 'x': None}), + ('SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', + {'et': None, 'x': None}), + ('SET X specializes ET WHERE X eid %(x)s, ET eid %(et)s', + {'et': None, 'x': None})], + sorted(specialize2rql(schema))) def test_esche2rql_custom_type(self): expected = [('INSERT CWEType X: X description %(description)s,X final %(final)s,X name %(name)s', @@ -95,8 +96,7 @@ self.assertListEqual(expected, got) def test_rschema2rql1(self): - self.assertListEqual(list(rschema2rql(schema.rschema('relation_type'), cstrtypemap)), - [ + self.assertListEqual([ ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symmetric %(symmetric)s', {'description': u'link a relation definition to its relation type', 'symmetric': False, 'name': u'relation_type', 'final' : False, 'fulltext_container': None, 'inlined': True}), @@ -113,11 +113,11 @@ 'ordernum': 1, 'cardinality': u'1*'}), ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT eid %(ct)s, EDEF eid %(x)s', {'x': None, 'ct': u'RQLConstraint_eid', 'value': u';O;O final FALSE\n'}), - ]) + ], + list(rschema2rql(schema.rschema('relation_type'), cstrtypemap))) def test_rschema2rql2(self): - self.assertListEqual(list(rschema2rql(schema.rschema('add_permission'), cstrtypemap)), - [ + self.assertListEqual([ ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symmetric %(symmetric)s', {'description': u'', 'symmetric': False, 'name': u'add_permission', 'final': False, 'fulltext_container': None, 'inlined': False}), ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE eid %(se)s,ER eid %(rt)s,OE eid %(oe)s', @@ -132,12 +132,11 @@ 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'ordernum': 9999, 'cardinality': u'**'}), ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE eid %(se)s,ER eid %(rt)s,OE eid %(oe)s', {'se': None, 'rt': None, 'oe': None, - 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'ordernum': 9999, 'cardinality': u'*?'}), - ]) + 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'ordernum': 9999, 'cardinality': u'*?'})], + list(rschema2rql(schema.rschema('add_permission'), cstrtypemap))) def test_rschema2rql3(self): - self.assertListEqual(list(rschema2rql(schema.rschema('cardinality'), cstrtypemap)), - [ + self.assertListEqual([ ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symmetric %(symmetric)s', {'description': u'', 'symmetric': False, 'name': u'cardinality', 'final': True, 'fulltext_container': None, 'inlined': False}), @@ -155,8 +154,8 @@ ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT eid %(ct)s, EDEF eid %(x)s', {'x': None, 'ct': u'SizeConstraint_eid', 'value': u'max=2'}), ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT eid %(ct)s, EDEF eid %(x)s', - {'x': None, 'ct': u'StaticVocabularyConstraint_eid', 'value': u"u'?*', u'1*', u'+*', u'**', u'?+', u'1+', u'++', u'*+', u'?1', u'11', u'+1', u'*1', u'??', u'1?', u'+?', u'*?'"}), - ]) + {'x': None, 'ct': u'StaticVocabularyConstraint_eid', 'value': u"u'?*', u'1*', u'+*', u'**', u'?+', u'1+', u'++', u'*+', u'?1', u'11', u'+1', u'*1', u'??', u'1?', u'+?', u'*?'"})], + list(rschema2rql(schema.rschema('cardinality'), cstrtypemap))) def test_rschema2rql_custom_type(self): expected = [('INSERT CWRType X: X description %(description)s,X final %(final)s,' @@ -195,37 +194,34 @@ self.assertListEqual(expected, got) def test_rdef2rql(self): - self.assertListEqual(list(rdef2rql(schema['description_format'].rdefs[('CWRType', 'String')], cstrtypemap)), - [ + self.assertListEqual([ ('INSERT CWAttribute X: X cardinality %(cardinality)s,X defaultval %(defaultval)s,X description %(description)s,X fulltextindexed %(fulltextindexed)s,X indexed %(indexed)s,X internationalizable %(internationalizable)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE eid %(se)s,ER eid %(rt)s,OE eid %(oe)s', {'se': None, 'rt': None, 'oe': None, 'description': u'', 'internationalizable': True, 'fulltextindexed': False, 'ordernum': 3, 'defaultval': u'text/plain', 'indexed': False, 'cardinality': u'?1'}), ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT eid %(ct)s, EDEF eid %(x)s', {'x': None, 'value': u'None', 'ct': 'FormatConstraint_eid'}), ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT eid %(ct)s, EDEF eid %(x)s', - {'x': None, 'value': u'max=50', 'ct': 'SizeConstraint_eid'})]) + {'x': None, 'value': u'max=50', 'ct': 'SizeConstraint_eid'})], + list(rdef2rql(schema['description_format'].rdefs[('CWRType', 'String')], cstrtypemap))) def test_updateeschema2rql1(self): - self.assertListEqual(list(updateeschema2rql(schema.eschema('CWAttribute'), 1)), - [('SET X description %(description)s,X final %(final)s,X name %(name)s WHERE X eid %(x)s', - {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the instance schema', 'x': 1, 'final': False, 'name': u'CWAttribute'}), - ]) + self.assertListEqual([('SET X description %(description)s,X final %(final)s,X name %(name)s WHERE X eid %(x)s', + {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the instance schema', 'x': 1, 'final': False, 'name': u'CWAttribute'})], + list(updateeschema2rql(schema.eschema('CWAttribute'), 1))) def test_updateeschema2rql2(self): - self.assertListEqual(list(updateeschema2rql(schema.eschema('String'), 1)), - [('SET X description %(description)s,X final %(final)s,X name %(name)s WHERE X eid %(x)s', - {'description': u'', 'x': 1, 'final': True, 'name': u'String'}) - ]) + self.assertListEqual([('SET X description %(description)s,X final %(final)s,X name %(name)s WHERE X eid %(x)s', + {'description': u'', 'x': 1, 'final': True, 'name': u'String'})], + list(updateeschema2rql(schema.eschema('String'), 1))) def test_updaterschema2rql1(self): - self.assertListEqual(list(updaterschema2rql(schema.rschema('relation_type'), 1)), - [ + self.assertListEqual([ ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symmetric %(symmetric)s WHERE X eid %(x)s', {'x': 1, 'symmetric': False, 'description': u'link a relation definition to its relation type', - 'final': False, 'fulltext_container': None, 'inlined': True, 'name': u'relation_type'}) - ]) + 'final': False, 'fulltext_container': None, 'inlined': True, 'name': u'relation_type'})], + list(updaterschema2rql(schema.rschema('relation_type'), 1))) def test_updaterschema2rql2(self): expected = [ @@ -235,7 +231,7 @@ 'inlined': False, 'name': u'add_permission'}) ] for i, (rql, args) in enumerate(updaterschema2rql(schema.rschema('add_permission'), 1)): - yield self.assertEqual, (rql, args), expected[i] + yield self.assertEqual, expected[i], (rql, args) class Perms2RQLTC(TestCase): GROUP_MAPPING = { @@ -246,31 +242,33 @@ } def test_eperms2rql1(self): - self.assertListEqual([(rql, kwargs) for rql, kwargs in erperms2rql(schema.eschema('CWEType'), self.GROUP_MAPPING)], - [('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), - ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 1}), - ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 2}), - ('SET X add_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), - ('SET X update_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), - ('SET X delete_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), - ]) + self.assertListEqual([('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), + ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 1}), + ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 2}), + ('SET X add_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), + ('SET X update_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), + ('SET X delete_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0})], + [(rql, kwargs) + for rql, kwargs in erperms2rql(schema.eschema('CWEType'), self.GROUP_MAPPING)]) def test_rperms2rql2(self): - self.assertListEqual([(rql, kwargs) for rql, kwargs in erperms2rql(schema.rschema('read_permission').rdef('CWEType', 'CWGroup'), self.GROUP_MAPPING)], - [('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), - ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 1}), - ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 2}), - ('SET X add_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), - ('SET X delete_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), - ]) + self.assertListEqual([('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), + ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 1}), + ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 2}), + ('SET X add_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), + ('SET X delete_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0})], + [(rql, kwargs) + for rql, kwargs in erperms2rql(schema.rschema('read_permission').rdef('CWEType', 'CWGroup'), + self.GROUP_MAPPING)]) def test_rperms2rql3(self): - self.assertListEqual([(rql, kwargs) for rql, kwargs in erperms2rql(schema.rschema('name').rdef('CWEType', 'String'), self.GROUP_MAPPING)], - [('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), - ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 1}), - ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 2}), - ('SET X update_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), - ]) + self.assertListEqual([('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0}), + ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 1}), + ('SET X read_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 2}), + ('SET X update_permission Y WHERE Y eid %(g)s, X eid %(x)s', {'g': 0})], + [(rql, kwargs) + for rql, kwargs in erperms2rql(schema.rschema('name').rdef('CWEType', 'String'), + self.GROUP_MAPPING)]) #def test_perms2rql(self): # self.assertListEqual(perms2rql(schema, self.GROUP_MAPPING), diff -r 65b8236e1bb4 -r 9b58a6406a64 web/data/cubicweb.facets.js --- a/web/data/cubicweb.facets.js Thu Jun 13 17:27:43 2013 +0200 +++ b/web/data/cubicweb.facets.js Tue Jul 09 15:58:26 2013 +0200 @@ -30,7 +30,7 @@ }); // FacetStringWidget (e.g. has-text) $(this).find('input:text').each(function(){ - names.push(facetName); + names.push(this.name); values.push(this.value); }); }); diff -r 65b8236e1bb4 -r 9b58a6406a64 web/facet.py --- a/web/facet.py Thu Jun 13 17:27:43 2013 +0200 +++ b/web/facet.py Tue Jul 09 15:58:26 2013 +0200 @@ -74,10 +74,11 @@ def rtype_facet_title(facet): - ptypes = facet.cw_rset.column_types(0) - if len(ptypes) == 1: - return display_name(facet._cw, facet.rtype, form=facet.role, - context=iter(ptypes).next()) + if facet.cw_rset: + ptypes = facet.cw_rset.column_types(0) + if len(ptypes) == 1: + return display_name(facet._cw, facet.rtype, form=facet.role, + context=iter(ptypes).next()) return display_name(facet._cw, facet.rtype, form=facet.role) def get_facet(req, facetid, select, filtered_variable): @@ -1516,7 +1517,8 @@ cssclass += ' hideFacetBody' w(u'
%s
\n' % (cssclass, xml_escape(self.facet.__regid__), title)) - w(u'\n' % (facetid, self.value or u'')) + w(u'\n' % ( + xml_escape(self.facet.__regid__), self.value or u'')) w(u'\n') diff -r 65b8236e1bb4 -r 9b58a6406a64 web/formwidgets.py --- a/web/formwidgets.py Thu Jun 13 17:27:43 2013 +0200 +++ b/web/formwidgets.py Tue Jul 09 15:58:26 2013 +0200 @@ -1016,6 +1016,8 @@ time, you should not give an already translated string. """ type = 'button' + css_class = 'validateButton' + def __init__(self, label=stdmsgs.BUTTON_OK, attrs=None, setdomid=None, settabindex=None, name='', value='', onclick=None, cwaction=None): @@ -1030,7 +1032,7 @@ self.value = '' self.onclick = onclick self.cwaction = cwaction - self.attrs.setdefault('class', 'validateButton') + self.attrs.setdefault('class', self.css_class) def render(self, form, field=None, renderer=None): label = form._cw._(self.label) diff -r 65b8236e1bb4 -r 9b58a6406a64 web/request.py --- a/web/request.py Thu Jun 13 17:27:43 2013 +0200 +++ b/web/request.py Tue Jul 09 15:58:26 2013 +0200 @@ -922,7 +922,7 @@ if reset_xmldecl is not None: warn('[3.17] reset_xmldecl is deprecated as we only serve html', DeprecationWarning, stacklevel=2) - self.main_stream.set_doctype(doctype, reset_xmldecl) + self.main_stream.set_doctype(doctype) # page data management #################################################### diff -r 65b8236e1bb4 -r 9b58a6406a64 web/test/unittest_views_baseviews.py --- a/web/test/unittest_views_baseviews.py Thu Jun 13 17:27:43 2013 +0200 +++ b/web/test/unittest_views_baseviews.py Tue Jul 09 15:58:26 2013 +0200 @@ -144,7 +144,7 @@ class MyView(StartupView): __regid__ = 'my-view' def call(self): - self._cw.set_doctype(html_doctype, reset_xmldecl=False) + self._cw.set_doctype(html_doctype) self._cw.main_stream.set_htmlattrs([('lang', 'cz')]) with self.temporary_appobjects(MyView): diff -r 65b8236e1bb4 -r 9b58a6406a64 web/views/navigation.py --- a/web/views/navigation.py Thu Jun 13 17:27:43 2013 +0200 +++ b/web/views/navigation.py Tue Jul 09 15:58:26 2013 +0200 @@ -398,7 +398,7 @@ title = self._cw._('i18nprevnext_previous') icon = self.prev_icon cssclass = u'previousEntity left' - content = icon + content + content = icon + '  ' + content else: title = self._cw._('i18nprevnext_next') icon = self.next_icon