# HG changeset patch # User Sylvain Thénault # Date 1315498176 -7200 # Node ID aa547cf3bf0d5a8d9b7d2426a315c5022db534ec # Parent 8af09eeee1301381ffa4b4b1ecef161ec3c5d4f0# Parent 72699a8e739d9e9deef06cfc225d1ca98b2b4f7d backport stable diff -r 8af09eeee130 -r aa547cf3bf0d .hgtags --- a/.hgtags Wed Sep 07 17:50:09 2011 +0200 +++ b/.hgtags Thu Sep 08 18:09:36 2011 +0200 @@ -221,3 +221,5 @@ 223ecf0620b6c87d997f8011aca0d9f0ee4750af cubicweb-version-3.13.4 52f26475d764129c5559b2d80fd57e6ea1bdd6ba cubicweb-debian-version-3.13.4-1 a62f24e1497e953fbaed5894f6064a64f7ac0be3 cubicweb-version-3.10.x +20d9c550c57eb6f9adcb0cfab1c11b6b8793afb6 cubicweb-version-3.13.5 +2e9dd7d945557c210d3b79153c65f6885e755315 cubicweb-debian-version-3.13.5-1 diff -r 8af09eeee130 -r aa547cf3bf0d __pkginfo__.py --- a/__pkginfo__.py Wed Sep 07 17:50:09 2011 +0200 +++ b/__pkginfo__.py Thu Sep 08 18:09:36 2011 +0200 @@ -22,7 +22,7 @@ modname = distname = "cubicweb" -numversion = (3, 13, 4) +numversion = (3, 13, 5) version = '.'.join(str(num) for num in numversion) description = "a repository of entities / relations for knowledge management" @@ -40,7 +40,7 @@ ] __depends__ = { - 'logilab-common': '>= 0.55.2', + 'logilab-common': '>= 0.56.2', 'logilab-mtconverter': '>= 0.8.0', 'rql': '>= 0.28.0', 'yams': '>= 0.33.0', diff -r 8af09eeee130 -r aa547cf3bf0d debian/changelog --- a/debian/changelog Wed Sep 07 17:50:09 2011 +0200 +++ b/debian/changelog Thu Sep 08 18:09:36 2011 +0200 @@ -1,3 +1,9 @@ +cubicweb (3.13.5-1) unstable; urgency=low + + * new upstream release + + -- Sylvain Thénault Thu, 08 Sep 2011 16:53:13 +0200 + cubicweb (3.13.4-1) unstable; urgency=low * new upstream release diff -r 8af09eeee130 -r aa547cf3bf0d debian/control --- a/debian/control Wed Sep 07 17:50:09 2011 +0200 +++ b/debian/control Thu Sep 08 18:09:36 2011 +0200 @@ -7,9 +7,9 @@ Adrien Di Mascio , Aurélien Campéas , Nicolas Chauvat -Build-Depends: debhelper (>= 7), python (>= 2.5), python-central (>= 0.5) +Build-Depends: debhelper (>= 7), python (>= 2.5), python-central (>= 0.5), python-sphinx # for the documentation: -# python-sphinx, python-logilab-common, python-unittest2, +# python-sphinx, python-logilab-common, python-unittest2, logilab-doctools, logilab-xml Standards-Version: 3.9.1 Homepage: http://www.cubicweb.org XS-Python-Version: >= 2.5, << 2.7 @@ -35,7 +35,7 @@ Conflicts: cubicweb-multisources Replaces: cubicweb-multisources Provides: cubicweb-multisources -Depends: ${misc:Depends}, ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (>= 1.5.0), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2 +Depends: ${misc:Depends}, ${python:Depends}, cubicweb-common (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-logilab-database (>= 1.5.0), cubicweb-postgresql-support | cubicweb-mysql-support | python-pysqlite2, python-logilab-common (>= 0.56.2) Recommends: pyro (<< 4.0.0), cubicweb-documentation (= ${source:Version}) Description: server part of the CubicWeb framework CubicWeb is a semantic web application framework. @@ -70,7 +70,7 @@ Architecture: all XB-Python-Version: ${python:Versions} Provides: cubicweb-web-frontend -Depends: ${misc:Depends}, ${python:Depends}, cubicweb-web (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-twisted-web +Depends: ${misc:Depends}, ${python:Depends}, cubicweb-web (= ${source:Version}), cubicweb-ctl (= ${source:Version}), python-twisted-web, python-logilab-common (>= 0.56.2) Recommends: pyro (<< 4.0.0), cubicweb-documentation (= ${source:Version}) Description: twisted-based web interface for the CubicWeb framework CubicWeb is a semantic web application framework. diff -r 8af09eeee130 -r aa547cf3bf0d debian/rules --- a/debian/rules Wed Sep 07 17:50:09 2011 +0200 +++ b/debian/rules Thu Sep 08 18:09:36 2011 +0200 @@ -10,14 +10,11 @@ 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 - # XXX uncomment this and associated build-depends in control - #when necessary sphinx version is in all built distribution - #PYTHONPATH=$(CURDIR)/.. $(MAKE) -C doc/book/en all + # documentation build is now made optional since it can break for old + # distributions and we don't want to block a new release of Cubicweb + # because of documentation issues. + -PYTHONPATH=$(CURDIR)/.. $(MAKE) -C doc/book/en all touch build-stamp clean: @@ -73,7 +70,7 @@ dh_installman -i dh_installchangelogs -i dh_link -i - dh_compress -i -X.py -X.ini -X.xml + dh_compress -i -X.py -X.ini -X.xml -X.js -X.rst dh_fixperms -i dh_installdeb -i dh_gencontrol -i diff -r 8af09eeee130 -r aa547cf3bf0d devtools/__init__.py --- a/devtools/__init__.py Wed Sep 07 17:50:09 2011 +0200 +++ b/devtools/__init__.py Thu Sep 08 18:09:36 2011 +0200 @@ -672,8 +672,9 @@ if 'global-db-name' not in self.system_source: self.system_source['global-db-name'] = self.system_source['db-name'] process_db = self.system_source['db-name'] + str(os.getpid()) - self.__TMPDB.add(process_db) self.system_source['db-name'] = process_db + process_db = self.absolute_dbfile() # update db-name to absolute path + self.__TMPDB.add(process_db) @staticmethod def _cleanup_database(dbfile): @@ -694,7 +695,6 @@ self.config.sources()['system']['db-name'] = dbfile return dbfile - def process_cache_entry(self, directory, dbname, db_id, entry): return entry.get('sqlite') diff -r 8af09eeee130 -r aa547cf3bf0d devtools/testlib.py --- a/devtools/testlib.py Wed Sep 07 17:50:09 2011 +0200 +++ b/devtools/testlib.py Thu Sep 08 18:09:36 2011 +0200 @@ -493,7 +493,7 @@ def assertModificationDateGreater(self, entity, olddate): entity.cw_attr_cache.pop('modification_date', None) - self.failUnless(entity.modification_date > olddate) + self.assertTrue(entity.modification_date > olddate) def assertItemsEqual(self, it1, it2, *args, **kwargs): it1 = set(getattr(x, 'eid', x) for x in it1) diff -r 8af09eeee130 -r aa547cf3bf0d etwist/server.py --- a/etwist/server.py Wed Sep 07 17:50:09 2011 +0200 +++ b/etwist/server.py Thu Sep 08 18:09:36 2011 +0200 @@ -25,7 +25,6 @@ import os import os.path as osp import select -import errno import traceback import threading import re @@ -523,12 +522,8 @@ return whichproc # parent process root_resource.init_publisher() # before changing uid if config['uid'] is not None: - try: - uid = int(config['uid']) - except ValueError: - from pwd import getpwnam - uid = getpwnam(config['uid']).pw_uid - os.setuid(uid) + from logilab.common.daemon import setugid + setugid(config['uid']) root_resource.start_service() LOGGER.info('instance started on %s', root_resource.base_url) # avoid annoying warnign if not in Main Thread diff -r 8af09eeee130 -r aa547cf3bf0d i18n/de.po --- a/i18n/de.po Wed Sep 07 17:50:09 2011 +0200 +++ b/i18n/de.po Thu Sep 08 18:09:36 2011 +0200 @@ -1974,6 +1974,9 @@ msgid "data directory url" msgstr "URL des Daten-Pools" +msgid "data model schema" +msgstr "Schema der Website" + msgid "data sources" msgstr "" @@ -3755,9 +3758,6 @@ msgid "site documentation" msgstr "Dokumentation der Website" -msgid "data model schema" -msgstr "Schema der Website" - msgid "site title" msgstr "Titel der Website" diff -r 8af09eeee130 -r aa547cf3bf0d i18n/en.po --- a/i18n/en.po Wed Sep 07 17:50:09 2011 +0200 +++ b/i18n/en.po Thu Sep 08 18:09:36 2011 +0200 @@ -1929,6 +1929,9 @@ msgid "data directory url" msgstr "" +msgid "data model schema" +msgstr "" + msgid "data sources" msgstr "" @@ -3659,9 +3662,6 @@ msgid "site documentation" msgstr "" -msgid "data model schema" -msgstr "" - msgid "site title" msgstr "" diff -r 8af09eeee130 -r aa547cf3bf0d i18n/es.po --- a/i18n/es.po Wed Sep 07 17:50:09 2011 +0200 +++ b/i18n/es.po Thu Sep 08 18:09:36 2011 +0200 @@ -2003,6 +2003,9 @@ msgid "data directory url" msgstr "Url del repertorio de datos" +msgid "data model schema" +msgstr "Esquema del Sistema" + msgid "data sources" msgstr "fuente de datos" @@ -3806,9 +3809,6 @@ msgid "site documentation" msgstr "Documentación Sistema" -msgid "data model schema" -msgstr "Esquema del Sistema" - msgid "site title" msgstr "Nombre del Sistema" diff -r 8af09eeee130 -r aa547cf3bf0d i18n/fr.po --- a/i18n/fr.po Wed Sep 07 17:50:09 2011 +0200 +++ b/i18n/fr.po Thu Sep 08 18:09:36 2011 +0200 @@ -164,7 +164,9 @@ #, python-format msgid "" "'%s' action for in_state relation should at least have 'linkattr=name' option" -msgstr "l'action '%s' pour la relation in_state doit au moins avoir l'option 'linkattr=name'" +msgstr "" +"l'action '%s' pour la relation in_state doit au moins avoir l'option " +"'linkattr=name'" #, python-format msgid "'%s' action requires 'linkattr' option" @@ -1260,7 +1262,8 @@ msgstr "automatique" msgid "autocomputed attribute used to ensure transition coherency" -msgstr "attribut calculé automatiquement pour assurer la cohérence de la transition" +msgstr "" +"attribut calculé automatiquement pour assurer la cohérence de la transition" msgid "automatic" msgstr "automatique" @@ -2006,6 +2009,9 @@ msgid "data directory url" msgstr "url du répertoire de données" +msgid "data model schema" +msgstr "schéma du modèle de données" + msgid "data sources" msgstr "sources de données" @@ -3677,7 +3683,8 @@ msgstr "expression rql autorisant à lire des entités/relations de ce type" msgid "rql expression allowing to update entities/relations of this type" -msgstr "expression rql autorisant à mettre à jour des entités/relations de ce type" +msgstr "" +"expression rql autorisant à mettre à jour des entités/relations de ce type" msgid "rql expressions" msgstr "conditions rql" @@ -3806,9 +3813,6 @@ msgid "site documentation" msgstr "documentation du site" -msgid "data model schema" -msgstr "schéma modèle de données" - msgid "site title" msgstr "titre du site" diff -r 8af09eeee130 -r aa547cf3bf0d server/msplanner.py --- a/server/msplanner.py Wed Sep 07 17:50:09 2011 +0200 +++ b/server/msplanner.py Thu Sep 08 18:09:36 2011 +0200 @@ -1623,17 +1623,7 @@ def visit_relation(self, node, newroot, terms): if not node.is_types_restriction(): if not node in terms and node in self.skip and self.solindices.issubset(self.skip[node]): - if not self.schema.rschema(node.r_type).final: - # can't really skip the relation if one variable is selected - # and only referenced by this relation - for vref in node.iget_nodes(VariableRef): - stinfo = vref.variable.stinfo - if stinfo['selected'] and len(stinfo['relations']) == 1: - break - else: - return None, node - else: - return None, node + return None, node if not self._relation_supported(node): raise UnsupportedBranch() # don't copy type restriction unless this is the only supported relation diff -r 8af09eeee130 -r aa547cf3bf0d server/serverctl.py --- a/server/serverctl.py Wed Sep 07 17:50:09 2011 +0200 +++ b/server/serverctl.py Thu Sep 08 18:09:36 2011 +0200 @@ -651,7 +651,7 @@ ) def run(self, args): - from logilab.common.daemon import daemonize + from logilab.common.daemon import daemonize, setugid from cubicweb.cwctl import init_cmdline_log_threshold from cubicweb.server.server import RepositoryServer appid = args[0] @@ -675,12 +675,7 @@ return uid = config['uid'] if uid is not None: - try: - uid = int(uid) - except ValueError: - from pwd import getpwnam - uid = getpwnam(uid).pw_uid - os.setuid(uid) + setugid(uid) server.install_sig_handlers() server.connect(config['host'], 0) server.run() diff -r 8af09eeee130 -r aa547cf3bf0d server/test/unittest_msplanner.py --- a/server/test/unittest_msplanner.py Wed Sep 07 17:50:09 2011 +0200 +++ b/server/test/unittest_msplanner.py Thu Sep 08 18:09:36 2011 +0200 @@ -1212,12 +1212,12 @@ [{'X': 'Note', 'S': 'State'}])], [self.cards, self.system], None, {'X': 'table0.C0', 'S': 'table0.C1'}, []), ('UnionStep', None, None, - [('OneFetchStep', [('Any X,S,U WHERE X in_state S, X todo_by U, S is State, U is CWUser, X is Note', + [('OneFetchStep', [('Any X,S,U WHERE X in_state S, X todo_by U, S is State, U is Personne, X is Affaire', + [{'X': 'Affaire', 'S': 'State', 'U': 'Personne'}])], + None, None, [self.system], {}, []), + ('OneFetchStep', [('Any X,S,U WHERE X todo_by U, S is State, U is CWUser, X is Note', [{'X': 'Note', 'S': 'State', 'U': 'CWUser'}])], None, None, [self.system], {'X': 'table0.C0', 'S': 'table0.C1'}, []), - ('OneFetchStep', [('Any X,S,U WHERE X in_state S, X todo_by U, S is State, U is Personne, X is Affaire', - [{'X': 'Affaire', 'S': 'State', 'U': 'Personne'}])], - None, None, [self.system], {}, []), ]) ]) @@ -2456,6 +2456,21 @@ [])], {'x': 999999}) + def test_nonregr_dont_readd_already_processed_relation(self): + self._test('Any WO,D,SO WHERE WO is Note, D tags WO, WO in_state SO', + [('FetchStep', + [('Any WO,SO WHERE WO in_state SO, SO is State, WO is Note', + [{'SO': 'State', 'WO': 'Note'}])], + [self.cards, self.system], None, + {'SO': 'table0.C1', 'WO': 'table0.C0'}, + []), + ('OneFetchStep', + [('Any WO,D,SO WHERE D tags WO, D is Tag, SO is State, WO is Note', + [{'D': 'Tag', 'SO': 'State', 'WO': 'Note'}])], + None, None, [self.system], + {'SO': 'table0.C1', 'WO': 'table0.C0'}, + []) + ]) class MSPlannerTwoSameExternalSourcesTC(BasePlannerTC): """test planner related feature on a 3-sources repository: diff -r 8af09eeee130 -r aa547cf3bf0d server/test/unittest_multisources.py --- a/server/test/unittest_multisources.py Wed Sep 07 17:50:09 2011 +0200 +++ b/server/test/unittest_multisources.py Thu Sep 08 18:09:36 2011 +0200 @@ -381,6 +381,9 @@ def test_nonregr3(self): self.sexecute('DELETE Card X WHERE X eid %(x)s, NOT X multisource_inlined_rel Y', {'x': self.ic1}) + def test_nonregr4(self): + self.sexecute('Any X,S,U WHERE X in_state S, X todo_by U') + def test_delete_source(self): req = self.request() req.execute('DELETE CWSource S WHERE S name "extern"') diff -r 8af09eeee130 -r aa547cf3bf0d web/facet.py --- a/web/facet.py Wed Sep 07 17:50:09 2011 +0200 +++ b/web/facet.py Thu Sep 08 18:09:36 2011 +0200 @@ -66,7 +66,7 @@ from cubicweb.utils import make_uid from cubicweb.selectors import match_context_prop, partial_relation_possible from cubicweb.appobject import AppObject -from cubicweb.web.htmlwidgets import HTMLWidget +from cubicweb.web import RequestError, htmlwidgets def rtype_facet_title(facet): @@ -431,7 +431,7 @@ values are selected. """ # OR between selected values by default - return self._cw.form.get(self.__regid__ + '_andor', 'OR') + return self._cw.form.get(xml_escape(self.__regid__) + '_andor', 'OR') def rqlexec(self, rql, args=None): """Utility method to execute some rql queries, and simply returning an @@ -720,6 +720,7 @@ return support def value_restriction(self, restrvar, rel, value): + # XXX handle rel is None case in RQLPathFacet? if self.restr_attr != 'eid': self.select.set_distinct(True) if isinstance(value, basestring): @@ -740,14 +741,19 @@ self._add_not_rel_restr(rel) self._and_restriction(rel, restrvar, value) else: - # multiple values with AND operator + # multiple values with AND operator. We've to generate a query like + # "X relation A, A eid 1, X relation B, B eid 1", hence the new + # relations at each iteration in the while loop below if '' in value: - value.remove('') - self._add_not_rel_restr(rel) + raise RequestError("this doesn't make sense") self._and_restriction(rel, restrvar, value.pop()) while value: restrvar, rtrel = _make_relation(self.select, self.filtered_variable, self.rtype, self.role) + if rel is None: + select.add_restriction(rtrel) + else: + rel.parent.replace(rel, nodes.And(rel, rtrel)) self._and_restriction(rel, restrvar, value.pop()) def _and_restriction(self, rel, restrvar, value): @@ -1331,7 +1337,7 @@ ## html widets ################################################################ -class FacetVocabularyWidget(HTMLWidget): +class FacetVocabularyWidget(htmlwidgets.HTMLWidget): def __init__(self, facet): self.facet = facet @@ -1355,7 +1361,7 @@ w(u'''''' % (facetid + '_andor', _('and/or between different values'), +''' % (xml_escape(self.facet.__regid__) + '_andor', _('and/or between different values'), _('OR'), _('AND'))) cssclass = 'facetBody' if not self.facet.start_unfolded: @@ -1369,7 +1375,7 @@ w(u'\n') -class FacetStringWidget(HTMLWidget): +class FacetStringWidget(htmlwidgets.HTMLWidget): def __init__(self, facet): self.facet = facet self.value = None @@ -1388,7 +1394,7 @@ w(u'\n') -class FacetRangeWidget(HTMLWidget): +class FacetRangeWidget(htmlwidgets.HTMLWidget): formatter = 'function (value) {return value;}' onload = u''' var _formatter = %(formatter)s; @@ -1478,7 +1484,7 @@ facet._cw.html_headers.define_var('DATE_FMT', fmt) -class FacetItem(HTMLWidget): +class FacetItem(htmlwidgets.HTMLWidget): selected_img = "black-check.png" unselected_img = "no-check-no-border.png" @@ -1506,7 +1512,7 @@ w(u'') -class CheckBoxFacetWidget(HTMLWidget): +class CheckBoxFacetWidget(htmlwidgets.HTMLWidget): selected_img = "black-check.png" unselected_img = "black-uncheck.png" @@ -1543,7 +1549,7 @@ w(u'\n') -class FacetSeparator(HTMLWidget): +class FacetSeparator(htmlwidgets.HTMLWidget): def __init__(self, label=None): self.label = label or u' ' diff -r 8af09eeee130 -r aa547cf3bf0d web/test/unittest_facet.py --- a/web/test/unittest_facet.py Wed Sep 07 17:50:09 2011 +0200 +++ b/web/test/unittest_facet.py Thu Sep 08 18:09:36 2011 +0200 @@ -50,6 +50,22 @@ self.assertEqual(f.select.as_string(), 'DISTINCT Any WHERE X is CWUser, X in_group D, D eid %s' % guests) + def test_relation_multiple_and(self): + f, (guests, managers) = self._in_group_facet() + f._cw.form[f.__regid__] = [str(guests), str(managers)] + f._cw.form[f.__regid__ + '_andor'] = 'AND' + f.add_rql_restrictions() + self.assertEqual(f.select.as_string(), + 'DISTINCT Any WHERE X is CWUser, X in_group A, B eid %s, X in_group B, A eid %s' % (guests, managers)) + + def test_relation_multiple_or(self): + f, (guests, managers) = self._in_group_facet() + f._cw.form[f.__regid__] = [str(guests), str(managers)] + f._cw.form[f.__regid__ + '_andor'] = 'OR' + f.add_rql_restrictions() + self.assertEqual(f.select.as_string(), + 'DISTINCT Any WHERE X is CWUser, X in_group A, A eid IN(%s, %s)' % (guests, managers)) + def test_relation_optional_rel(self): req = self.request() rset = self.execute('Any X,GROUP_CONCAT(GN) GROUPBY X ' diff -r 8af09eeee130 -r aa547cf3bf0d web/views/navigation.py --- a/web/views/navigation.py Wed Sep 07 17:50:09 2011 +0200 +++ b/web/views/navigation.py Thu Sep 08 18:09:36 2011 +0200 @@ -211,6 +211,14 @@ context = 'navbottom' order = 10 + @property + def prev_icon(self): + return '' % xml_escape(self._cw.data_url('go_prev.png')) + + @property + def next_icon(self): + return '' % xml_escape(self._cw.data_url('go_next.png')) + def init_rendering(self): adapter = self.entity.cw_adapt_to('IPrevNext') self.previous = adapter.previous_entity() @@ -232,16 +240,19 @@ def prevnext_entity(self, w, entity, type): textsize = self._cw.property_value('navigation.short-line-size') + content = xml_escape(cut(entity.dc_title(), textsize)) if type == 'prev': title = self._cw._('i18nprevnext_previous') - icon = u'<< ' + icon = self.prev_icon cssclass = u'previousEntity left' + content = icon + content else: title = self._cw._('i18nprevnext_next') - icon = u'>> ' + icon = self.next_icon cssclass = u'nextEntity right' + content = content + '  ' + icon self.prevnext_div(w, type, cssclass, entity.absolute_url(), - title, icon + xml_escape(cut(entity.dc_title(), textsize))) + title, content) def prevnext_div(self, w, type, cssclass, url, title, content): w(u'
' % cssclass) diff -r 8af09eeee130 -r aa547cf3bf0d web/webconfig.py --- a/web/webconfig.py Wed Sep 07 17:50:09 2011 +0200 +++ b/web/webconfig.py Thu Sep 08 18:09:36 2011 +0200 @@ -27,6 +27,7 @@ from logilab.common.decorators import cached from logilab.common.deprecation import deprecated +from cubicweb import ConfigurationError from cubicweb.toolsutils import read_config from cubicweb.cwconfig import CubicWebConfiguration, register_persistent_options, merge_options @@ -239,16 +240,20 @@ return self.repository().get_versions() def anonymous_user(self): - """return a login and password to use for anonymous users. None - may be returned for both if anonymous connections are not allowed + """return a login and password to use for anonymous users. + + None may be returned for both if anonymous connection is not + allowed or if an empty login is used in configuration """ try: - user = self['anonymous-user'] + user = self['anonymous-user'] or None passwd = self['anonymous-password'] + if user: + user = unicode(user) except KeyError: user, passwd = None, None - if user is not None: - user = unicode(user) + except UnicodeDecodeError: + raise ConfigurationError("anonymous information should only contains ascii") return user, passwd def locate_resource(self, rid):