--- 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
--- 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',
--- 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 <sylvain.thenault@logilab.fr> Thu, 08 Sep 2011 16:53:13 +0200
+
cubicweb (3.13.4-1) unstable; urgency=low
* new upstream release
--- 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 <Adrien.DiMascio@logilab.fr>,
Aurélien Campéas <aurelien.campeas@logilab.fr>,
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
-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.
--- 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
--- 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')
--- 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)
--- 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
--- 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"
--- 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 ""
--- 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"
--- 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"
--- 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
--- 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()
--- 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:
--- 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"')
--- 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'''<select name="%s" class="radio facetOperator" title="%s">
<option value="OR">%s</option>
<option value="AND">%s</option>
-</select>''' % (facetid + '_andor', _('and/or between different values'),
+</select>''' % (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'</div>\n')
-class FacetStringWidget(HTMLWidget):
+class FacetStringWidget(htmlwidgets.HTMLWidget):
def __init__(self, facet):
self.facet = facet
self.value = None
@@ -1388,7 +1394,7 @@
w(u'</div>\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'</div>')
-class CheckBoxFacetWidget(HTMLWidget):
+class CheckBoxFacetWidget(htmlwidgets.HTMLWidget):
selected_img = "black-check.png"
unselected_img = "black-uncheck.png"
@@ -1543,7 +1549,7 @@
w(u'</div>\n')
-class FacetSeparator(HTMLWidget):
+class FacetSeparator(htmlwidgets.HTMLWidget):
def __init__(self, label=None):
self.label = label or u' '
--- 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 '
--- 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 '<img src="%s"/>' % xml_escape(self._cw.data_url('go_prev.png'))
+
+ @property
+ def next_icon(self):
+ return '<img src="%s"/>' % 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'<div class="%s">' % cssclass)
--- 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):