--- a/.hgtags Thu Mar 24 15:21:13 2011 +0100
+++ b/.hgtags Wed Mar 30 11:17:58 2011 +0200
@@ -186,3 +186,5 @@
8daabda9f571863e8754f8ab722744c417ba3abf cubicweb-debian-version-3.11.0-1
d0410eb4d8bbf657d7f32b0c681db09b1f8119a0 cubicweb-version-3.11.1
77318f1ec4aae3523d455e884daf3708c3c79af7 cubicweb-debian-version-3.11.1-1
+56ae3cd5f8553678a2b1d4121b61241598d0ca68 cubicweb-version-3.11.2
+954b5b51cd9278eb45d66be1967064d01ab08453 cubicweb-debian-version-3.11.2-1
--- a/__pkginfo__.py Thu Mar 24 15:21:13 2011 +0100
+++ b/__pkginfo__.py Wed Mar 30 11:17:58 2011 +0200
@@ -22,7 +22,7 @@
modname = distname = "cubicweb"
-numversion = (3, 11, 1)
+numversion = (3, 11, 2)
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.0',
+ 'logilab-common': '>= 0.55.2',
'logilab-mtconverter': '>= 0.8.0',
'rql': '>= 0.28.0',
'yams': '>= 0.30.4',
--- a/cwconfig.py Thu Mar 24 15:21:13 2011 +0100
+++ b/cwconfig.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -325,7 +325,8 @@
{'type' : 'string',
'default': '',
'help': 'Pyro name server\'s host. If not set, will be detected by a \
-broadcast query. It may contains port information using <host>:<port> notation.',
+broadcast query. It may contains port information using <host>:<port> notation. \
+Use "NO_PYRONS" to create a Pyro server but not register to a pyro nameserver',
'group': 'pyro', 'level': 1,
}),
('pyro-ns-group',
@@ -843,9 +844,8 @@
if not exists(_INSTANCES_DIR):
os.makedirs(_INSTANCES_DIR)
- # for some commands (creation...) we don't want to initialize gettext
- set_language = True
- # set this to true to allow somethings which would'nt be possible
+ # set to true during repair (shell, migration) to allow some things which
+ # wouldn't be possible otherwise
repairing = False
options = CubicWebNoAppConfiguration.options + (
@@ -900,13 +900,13 @@
return mdir
@classmethod
- def config_for(cls, appid, config=None, debugmode=False):
+ def config_for(cls, appid, config=None, debugmode=False, creating=False):
"""return a configuration instance for the given instance identifier
"""
cls.load_available_configs()
config = config or guess_configuration(cls.instance_home(appid))
configcls = configuration_cls(config)
- return configcls(appid, debugmode)
+ return configcls(appid, debugmode, creating)
@classmethod
def possible_configurations(cls, appid):
@@ -966,8 +966,6 @@
log_path = os.path.join(_INSTALL_PREFIX, 'var', 'log', 'cubicweb', '%s-%s.log')
return log_path % (self.appid, self.name)
-
-
def default_pid_file(self):
"""return default path to the pid file of the instance'server"""
if self.mode == 'system':
@@ -985,8 +983,10 @@
# instance methods used to get instance specific resources #############
- def __init__(self, appid, debugmode=False):
+ def __init__(self, appid, debugmode=False, creating=False):
self.appid = appid
+ # set to true while creating an instance
+ self.creating = creating
super(CubicWebConfiguration, self).__init__(debugmode)
fake_gettext = (unicode, lambda ctx, msgid: unicode(msgid))
for lang in self.available_languages():
@@ -1077,7 +1077,7 @@
def load_configuration(self):
"""load instance's configuration files"""
super(CubicWebConfiguration, self).load_configuration()
- if self.apphome and self.set_language:
+ if self.apphome and not self.creating:
# init gettext
self._gettext_init()
--- a/cwctl.py Thu Mar 24 15:21:13 2011 +0100
+++ b/cwctl.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -300,6 +300,11 @@
print '* cube %s version %s is installed, but version %s is required by %s' % (
cube, cfgpb.cubes[cube], version, src)
+def check_options_consistency(config):
+ if config.automatic and config.config_level > 0:
+ raise BadCommandUsage('--automatic and --config-level should not be '
+ 'used together')
+
class CreateInstanceCommand(Command):
"""Create an instance from a cube. This is an unified
command which can handle web / server / all-in-one installation
@@ -309,7 +314,7 @@
<cube>
the name of cube to use (list available cube names using
the "list" command). You can use several cubes by separating
- them using comma (e.g. 'jpl,eemail')
+ them using comma (e.g. 'jpl,email')
<instance>
an identifier for the instance to create
"""
@@ -317,34 +322,39 @@
arguments = '<cube> <instance>'
min_args = max_args = 2
options = (
- ("config-level",
+ ('automatic',
+ {'short': 'a', 'action' : 'store_true',
+ 'default': False,
+ 'help': 'automatic mode: never ask and use default answer to every '
+ 'question. this may require that your login match a database super '
+ 'user (allowed to create database & all).',
+ }),
+ ('config-level',
{'short': 'l', 'type' : 'int', 'metavar': '<level>',
'default': 0,
- 'help': 'configuration level (0..2): 0 will ask for essential \
-configuration parameters only while 2 will ask for all parameters',
- }
- ),
- ("config",
+ 'help': 'configuration level (0..2): 0 will ask for essential '
+ 'configuration parameters only while 2 will ask for all parameters',
+ }),
+ ('config',
{'short': 'c', 'type' : 'choice', 'metavar': '<install type>',
'choices': ('all-in-one', 'repository', 'twisted'),
'default': 'all-in-one',
- 'help': 'installation type, telling which part of an instance \
-should be installed. You can list available configurations using the "list" \
-command. Default to "all-in-one", e.g. an installation embedding both the RQL \
-repository and the web server.',
- }
- ),
+ 'help': 'installation type, telling which part of an instance '
+ 'should be installed. You can list available configurations using the'
+ ' "list" command. Default to "all-in-one", e.g. an installation '
+ 'embedding both the RQL repository and the web server.',
+ }),
)
def run(self, args):
"""run the command with its specific arguments"""
from logilab.common.textutils import splitstrip
+ check_options_consistency(self.config)
configname = self.config.config
cubes, appid = args
cubes = splitstrip(cubes)
# get the configuration and helper
- config = cwcfg.config_for(appid, configname)
- config.set_language = False
+ config = cwcfg.config_for(appid, configname, creating=True)
cubes = config.expand_cubes(cubes)
config.init_cubes(cubes)
helper = self.config_helper(config)
@@ -361,15 +371,19 @@
print '\n'+underline_title('Creating the instance %s' % appid)
create_dir(config.apphome)
# cubicweb-ctl configuration
- print '\n'+underline_title('Configuring the instance (%s.conf)' % configname)
- config.input_config('main', self.config.config_level)
+ if not self.config.automatic:
+ print '\n'+underline_title('Configuring the instance (%s.conf)'
+ % configname)
+ config.input_config('main', self.config.config_level)
# configuration'specific stuff
print
- helper.bootstrap(cubes, self.config.config_level)
+ helper.bootstrap(cubes, self.config.automatic, self.config.config_level)
# input for cubes specific options
- for section in set(sect.lower() for sect, opt, optdict in config.all_options()
- if optdict.get('level') <= self.config.config_level):
- if section not in ('main', 'email', 'pyro'):
+ sections = set(sect.lower() for sect, opt, odict in config.all_options()
+ if 'type' in odict
+ and odict.get('level') <= self.config.config_level)
+ for section in sections:
+ if section not in ('main', 'email', 'pyro', 'web'):
print '\n' + underline_title('%s options' % section)
config.input_config(section, self.config.config_level)
# write down configuration
@@ -384,8 +398,9 @@
errors = config.i18ncompile(langs)
if errors:
print '\n'.join(errors)
- if not ASK.confirm('error while compiling message catalogs, '
- 'continue anyway ?'):
+ if self.config.automatic \
+ or not ASK.confirm('error while compiling message catalogs, '
+ 'continue anyway ?'):
print 'creation not completed'
return
# create the additional data directory for this instance
@@ -398,7 +413,7 @@
print 'set %s as owner of the data directory' % config['uid']
chown(config.appdatahome, config['uid'])
print '\n-> creation done for %r.\n' % config.apphome
- helper.postcreate()
+ helper.postcreate(self.config.automatic)
def _handle_win32(self, config, appid):
if sys.platform != 'win32':
--- a/dataimport.py Thu Mar 24 15:21:13 2011 +0100
+++ b/dataimport.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -81,7 +81,7 @@
from logilab.common.deprecation import deprecated
from cubicweb.server.utils import eschema_eid
-from cubicweb.server.ssplanner import EditedEntity
+from cubicweb.server.edition import EditedEntity
def count_lines(stream_or_filename):
if isinstance(stream_or_filename, basestring):
@@ -473,6 +473,11 @@
self.rql('SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % rtype,
{'x': int(eid_from), 'y': int(eid_to)})
+ def find_entities(self, *args, **kwargs):
+ return self.session.find_entities(*args, **kwargs)
+
+ def find_one_entity(self, *args, **kwargs):
+ return self.session.find_one_entity(*args, **kwargs)
# the import controller ########################################################
--- a/dbapi.py Thu Mar 24 15:21:13 2011 +0100
+++ b/dbapi.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -102,11 +102,14 @@
return Repository(config, vreg=vreg)
else: # method == 'pyro'
# resolve the Pyro object
- from logilab.common.pyro_ext import ns_get_proxy
+ from logilab.common.pyro_ext import ns_get_proxy, get_proxy
pyroid = database or config['pyro-instance-id'] or config.appid
try:
- return ns_get_proxy(pyroid, defaultnsgroup=config['pyro-ns-group'],
- nshost=config['pyro-ns-host'])
+ if config['pyro-ns-host'] == 'NO_PYRONS':
+ return get_proxy(pyroid)
+ else:
+ return ns_get_proxy(pyroid, defaultnsgroup=config['pyro-ns-group'],
+ nshost=config['pyro-ns-host'])
except Exception, ex:
raise ConnectionError(str(ex))
--- a/debian/changelog Thu Mar 24 15:21:13 2011 +0100
+++ b/debian/changelog Wed Mar 30 11:17:58 2011 +0200
@@ -1,3 +1,9 @@
+cubicweb (3.11.2-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Nicolas Chauvat <nicolas.chauvat@logilab.fr> Mon, 28 Mar 2011 19:18:54 +0200
+
cubicweb (3.11.1-1) unstable; urgency=low
* new upstream release
--- a/debian/control Thu Mar 24 15:21:13 2011 +0100
+++ b/debian/control Wed Mar 30 11:17:58 2011 +0200
@@ -97,7 +97,7 @@
Package: cubicweb-common
Architecture: all
XB-Python-Version: ${python:Versions}
-Depends: ${misc:Depends}, ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.8.0), python-logilab-common (>= 0.55.0), python-yams (>= 0.30.4), python-rql (>= 0.28.0), python-lxml
+Depends: ${misc:Depends}, ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.8.0), python-logilab-common (>= 0.55.2), python-yams (>= 0.30.4), python-rql (>= 0.28.0), python-lxml
Recommends: python-simpletal (>= 4.0), python-crypto
Conflicts: cubicweb-core
Replaces: cubicweb-core
--- a/devtools/__init__.py Thu Mar 24 15:21:13 2011 +0100
+++ b/devtools/__init__.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -127,7 +127,6 @@
class TestServerConfiguration(ServerConfiguration):
mode = 'test'
- set_language = False
read_instance_schema = False
init_repository = True
@@ -497,7 +496,8 @@
@cached
def dbcnx(self):
from cubicweb.server.serverctl import _db_sys_cnx
- return _db_sys_cnx(self.system_source, 'CREATE DATABASE and / or USER', verbose=0)
+ return _db_sys_cnx(self.system_source, 'CREATE DATABASE and / or USER',
+ interactive=False)
@property
@cached
@@ -514,7 +514,8 @@
createdb(self.helper, self.system_source, self.dbcnx, self.cursor)
self.dbcnx.commit()
- cnx = system_source_cnx(self.system_source, special_privs='LANGUAGE C', verbose=0)
+ cnx = system_source_cnx(self.system_source, special_privs='LANGUAGE C',
+ interactive=False)
templcursor = cnx.cursor()
try:
# XXX factorize with db-create code
--- a/doc/book/en/devrepo/testing.rst Thu Mar 24 15:21:13 2011 +0100
+++ b/doc/book/en/devrepo/testing.rst Wed Mar 30 11:17:58 2011 +0200
@@ -59,10 +59,10 @@
def setup_database(self):
req = self.request()
- group_etype = req.execute('Any X WHERE X name "CWGroup"').get_entity(0,0)
+ group_etype = req.find_one_entity('CWEType', name='CWGroup')
c1 = req.create_entity('Classification', name=u'classif1',
classifies=group_etype)
- user_etype = req.execute('Any X WHERE X name "CWUser"').get_entity(0,0)
+ user_etype = req.find_one_entity('CWEType', name='CWUser')
c2 = req.create_entity('Classification', name=u'classif2',
classifies=user_etype)
self.kw1 = req.create_entity('Keyword', name=u'kwgroup', included_in=c1)
@@ -228,7 +228,7 @@
def test_admin(self):
req = self.request()
- rset = req.execute('Any C WHERE C is Conference')
+ rset = req.find_entities('Conference')
self.assertListEqual(self.pactions(req, rset),
[('workflow', workflow.WorkflowActions),
('edit', confactions.ModifyAction),
--- a/entities/authobjs.py Thu Mar 24 15:21:13 2011 +0100
+++ b/entities/authobjs.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -80,6 +80,20 @@
key, self.login)
return self._cw.vreg.property_value(key)
+ def set_property(self, pkey, value):
+ value = unicode(value)
+ try:
+ prop = self._cw.execute(
+ 'CWProperty X WHERE X pkey %(k)s, X for_user U, U eid %(u)s',
+ {'k': pkey, 'u': self.eid}).get_entity(0, 0)
+ except:
+ kwargs = dict(pkey=unicode(pkey), value=value)
+ if self.is_in_group('managers'):
+ kwargs['for_user'] = self
+ self._cw.create_entity('CWProperty', **kwargs)
+ else:
+ prop.set_attributes(value=value)
+
def matching_groups(self, groups):
"""return the number of the given group(s) in which the user is
--- a/entity.py Thu Mar 24 15:21:13 2011 +0100
+++ b/entity.py Wed Mar 30 11:17:58 2011 +0200
@@ -62,7 +62,6 @@
return True
-
class Entity(AppObject):
"""an entity instance has e_schema automagically set on
the class and instances has access to their issuing cursor.
@@ -808,7 +807,11 @@
else:
existant = None # instead of 'SO', improve perfs
for select in rqlst.children:
- rewriter.rewrite(select, [((searchedvar, searchedvar), rqlexprs)],
+ varmap = {}
+ for var in 'SO':
+ if var in select.defined_vars:
+ varmap[var] = var
+ rewriter.rewrite(select, [(varmap, rqlexprs)],
select.solutions, args, existant)
rql = rqlst.as_string()
return rql, args
--- a/etwist/twctl.py Thu Mar 24 15:21:13 2011 +0100
+++ b/etwist/twctl.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -51,10 +51,10 @@
"""
cfgname = 'all-in-one'
- def bootstrap(self, cubes, inputlevel=0):
+ def bootstrap(self, cubes, automatic=False, inputlevel=0):
"""bootstrap this configuration"""
- serverctl.RepositoryCreateHandler.bootstrap(self, cubes, inputlevel)
- TWCreateHandler.bootstrap(self, cubes, inputlevel)
+ serverctl.RepositoryCreateHandler.bootstrap(self, cubes, automatic, inputlevel)
+ TWCreateHandler.bootstrap(self, cubes, automatic, inputlevel)
class AllInOneStartHandler(TWStartHandler):
cmdname = 'start'
--- a/hooks/syncsession.py Thu Mar 24 15:21:13 2011 +0100
+++ b/hooks/syncsession.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
--- a/req.py Thu Mar 24 15:21:13 2011 +0100
+++ b/req.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -35,6 +35,8 @@
ONESECOND = timedelta(0, 1, 0)
CACHE_REGISTRY = {}
+class FindEntityError(Exception):
+ """raised when find_one_entity() can not return one and only one entity"""
def _check_cw_unsafe(kwargs):
if kwargs.pop('_cw_unsafe', False):
@@ -140,6 +142,33 @@
cls = self.vreg['etypes'].etype_class(etype)
return cls.cw_instantiate(self.execute, **kwargs)
+ def find_entities(self, etype, **kwargs):
+ """find entities of the given type and attribute values.
+
+ >>> users = find_entities('CWGroup', name=u'users')
+ >>> groups = find_entities('CWGroup')
+ """
+ parts = ['Any X WHERE X is %s' % etype]
+ parts.extend('X %(attr)s %%(%(attr)s)s' % {'attr': attr} for attr in kwargs)
+ return self.execute(', '.join(parts), kwargs).entities()
+
+ def find_one_entity(self, etype, **kwargs):
+ """find one entity of the given type and attribute values.
+ raise :exc:`FindEntityError` if can not return one and only one entity.
+
+ >>> users = find_one_entity('CWGroup', name=u'users')
+ >>> groups = find_one_entity('CWGroup')
+ Exception()
+ """
+ parts = ['Any X WHERE X is %s' % etype]
+ parts.extend('X %(attr)s %%(%(attr)s)s' % {'attr': attr} for attr in kwargs)
+ rql = ', '.join(parts)
+ rset = self.execute(rql, kwargs)
+ if len(rset) != 1:
+ raise FindEntityError('Found %i entitie(s) when 1 was expected (rql=%s ; %s)'
+ % (len(rset), rql, repr(kwargs)))
+ return rset.get_entity(0,0)
+
def ensure_ro_rql(self, rql):
"""raise an exception if the given rql is not a select query"""
first = rql.split(None, 1)[0].lower()
--- a/rqlrewrite.py Thu Mar 24 15:21:13 2011 +0100
+++ b/rqlrewrite.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -24,6 +24,7 @@
__docformat__ = "restructuredtext en"
from rql import nodes as n, stmts, TypeResolverException
+from rql.utils import common_parent
from yams import BadSchemaDefinition
from logilab.common.graph import has_path
@@ -180,13 +181,23 @@
def insert_snippets(self, snippets, varexistsmap=None):
self.rewritten = {}
for varmap, rqlexprs in snippets:
+ if isinstance(varmap, dict):
+ varmap = tuple(sorted(varmap.items()))
+ else:
+ assert isinstance(varmap, tuple), varmap
if varexistsmap is not None and not varmap in varexistsmap:
continue
- self.varmap = varmap
- selectvar, snippetvar = varmap
+ self.insert_varmap_snippets(varmap, rqlexprs, varexistsmap)
+
+ def insert_varmap_snippets(self, varmap, rqlexprs, varexistsmap):
+ self.varmap = varmap
+ self.revvarmap = {}
+ self.varinfos = []
+ for i, (selectvar, snippetvar) in enumerate(varmap):
assert snippetvar in 'SOX'
- self.revvarmap = {snippetvar: selectvar}
- self.varinfo = vi = {}
+ self.revvarmap[snippetvar] = (selectvar, i)
+ vi = {}
+ self.varinfos.append(vi)
try:
vi['const'] = typed_eid(selectvar) # XXX gae
vi['rhs_rels'] = vi['lhs_rels'] = {}
@@ -194,42 +205,42 @@
try:
vi['stinfo'] = sti = self.select.defined_vars[selectvar].stinfo
except KeyError:
- # variable has been moved to a newly inserted subquery
+ # variable may have been moved to a newly inserted subquery
# we should insert snippet in that subquery
subquery = self.select.aliases[selectvar].query
assert len(subquery.children) == 1
subselect = subquery.children[0]
RQLRewriter(self.session).rewrite(subselect, [(varmap, rqlexprs)],
subselect.solutions, self.kwargs)
- continue
+ return
if varexistsmap is None:
vi['rhs_rels'] = dict( (r.r_type, r) for r in sti['rhsrelations'])
vi['lhs_rels'] = dict( (r.r_type, r) for r in sti['relations']
if not r in sti['rhsrelations'])
else:
vi['rhs_rels'] = vi['lhs_rels'] = {}
- parent = None
- inserted = False
- for rqlexpr in rqlexprs:
- self.current_expr = rqlexpr
- if varexistsmap is None:
- try:
- new = self.insert_snippet(varmap, rqlexpr.snippet_rqlst, parent)
- except Unsupported:
- continue
- inserted = True
- if new is not None:
- self.exists_snippet[rqlexpr] = new
- parent = parent or new
- else:
- # called to reintroduce snippet due to ambiguity creation,
- # so skip snippets which are not introducing this ambiguity
- exists = varexistsmap[varmap]
- if self.exists_snippet[rqlexpr] is exists:
- self.insert_snippet(varmap, rqlexpr.snippet_rqlst, exists)
- if varexistsmap is None and not inserted:
- # no rql expression found matching rql solutions. User has no access right
- raise Unauthorized()
+ parent = None
+ inserted = False
+ for rqlexpr in rqlexprs:
+ self.current_expr = rqlexpr
+ if varexistsmap is None:
+ try:
+ new = self.insert_snippet(varmap, rqlexpr.snippet_rqlst, parent)
+ except Unsupported:
+ continue
+ inserted = True
+ if new is not None:
+ self.exists_snippet[rqlexpr] = new
+ parent = parent or new
+ else:
+ # called to reintroduce snippet due to ambiguity creation,
+ # so skip snippets which are not introducing this ambiguity
+ exists = varexistsmap[varmap]
+ if self.exists_snippet[rqlexpr] is exists:
+ self.insert_snippet(varmap, rqlexpr.snippet_rqlst, exists)
+ if varexistsmap is None and not inserted:
+ # no rql expression found matching rql solutions. User has no access right
+ raise Unauthorized() # XXX bad constraint when inserting constraints
def insert_snippet(self, varmap, snippetrqlst, parent=None):
new = snippetrqlst.where.accept(self)
@@ -243,16 +254,23 @@
def _insert_snippet(self, varmap, parent, new):
if new is not None:
if self._insert_scope is None:
- insert_scope = self.varinfo.get('stinfo', {}).get('scope', self.select)
+ insert_scope = None
+ for vi in self.varinfos:
+ scope = vi.get('stinfo', {}).get('scope', self.select)
+ if insert_scope is None:
+ insert_scope = scope
+ else:
+ insert_scope = common_parent(scope, insert_scope)
else:
insert_scope = self._insert_scope
- if self.varinfo.get('stinfo', {}).get('optrelations'):
+ if any(vi.get('stinfo', {}).get('optrelations') for vi in self.varinfos):
assert parent is None
self._insert_scope = self.snippet_subquery(varmap, new)
self.insert_pending()
self._insert_scope = None
return
- new = n.Exists(new)
+ if not isinstance(new, (n.Exists, n.Not)):
+ new = n.Exists(new)
if parent is None:
insert_scope.add_restriction(new)
else:
@@ -291,7 +309,7 @@
varname = self.rewritten[key]
except KeyError:
try:
- varname = self.revvarmap[key[-1]]
+ varname = self.revvarmap[key[-1]][0]
except KeyError:
# variable isn't used anywhere else, we can't insert security
raise Unauthorized()
@@ -308,45 +326,51 @@
rqlexprs = eschema.get_rqlexprs(action)
if not rqlexprs:
raise Unauthorized()
- self.insert_snippets([((varname, 'X'), rqlexprs)])
+ self.insert_snippets([({varname: 'X'}, rqlexprs)])
def snippet_subquery(self, varmap, transformedsnippet):
"""introduce the given snippet in a subquery"""
subselect = stmts.Select()
- selectvar = varmap[0]
- subselectvar = subselect.get_variable(selectvar)
- subselect.append_selected(n.VariableRef(subselectvar))
snippetrqlst = n.Exists(transformedsnippet.copy(subselect))
- aliases = [selectvar]
- stinfo = self.varinfo['stinfo']
- need_null_test = False
- for rel in stinfo['relations']:
- rschema = self.schema.rschema(rel.r_type)
- if rschema.final or (rschema.inlined and
- not rel in stinfo['rhsrelations']):
- rel.children[0].name = selectvar # XXX explain why
- subselect.add_restriction(rel.copy(subselect))
- for vref in rel.children[1].iget_nodes(n.VariableRef):
- if isinstance(vref.variable, n.ColumnAlias):
- # XXX could probably be handled by generating the subquery
- # into the detected subquery
- raise BadSchemaDefinition(
- "cant insert security because of usage two inlined "
- "relations in this query. You should probably at "
- "least uninline %s" % rel.r_type)
- subselect.append_selected(vref.copy(subselect))
- aliases.append(vref.name)
- self.select.remove_node(rel)
- # when some inlined relation has to be copied in the subquery,
- # we need to test that either value is NULL or that the snippet
- # condition is satisfied
- if rschema.inlined and rel.optional:
- need_null_test = True
- if need_null_test:
- snippetrqlst = n.Or(
- n.make_relation(subselectvar, 'is', (None, None), n.Constant,
- operator='='),
- snippetrqlst)
+ aliases = []
+ rels_done = set()
+ for i, (selectvar, snippetvar) in enumerate(varmap):
+ subselectvar = subselect.get_variable(selectvar)
+ subselect.append_selected(n.VariableRef(subselectvar))
+ aliases.append(selectvar)
+ vi = self.varinfos[i]
+ need_null_test = False
+ stinfo = vi['stinfo']
+ for rel in stinfo['relations']:
+ if rel in rels_done:
+ continue
+ rels_done.add(rel)
+ rschema = self.schema.rschema(rel.r_type)
+ if rschema.final or (rschema.inlined and
+ not rel in stinfo['rhsrelations']):
+ rel.children[0].name = selectvar # XXX explain why
+ subselect.add_restriction(rel.copy(subselect))
+ for vref in rel.children[1].iget_nodes(n.VariableRef):
+ if isinstance(vref.variable, n.ColumnAlias):
+ # XXX could probably be handled by generating the
+ # subquery into the detected subquery
+ raise BadSchemaDefinition(
+ "cant insert security because of usage two inlined "
+ "relations in this query. You should probably at "
+ "least uninline %s" % rel.r_type)
+ subselect.append_selected(vref.copy(subselect))
+ aliases.append(vref.name)
+ self.select.remove_node(rel)
+ # when some inlined relation has to be copied in the
+ # subquery, we need to test that either value is NULL or
+ # that the snippet condition is satisfied
+ if rschema.inlined and rel.optional:
+ need_null_test = True
+ if need_null_test:
+ snippetrqlst = n.Or(
+ n.make_relation(subselectvar, 'is', (None, None), n.Constant,
+ operator='='),
+ snippetrqlst)
subselect.add_restriction(snippetrqlst)
if self.u_varname:
# generate an identifier for the substitution
@@ -433,35 +457,37 @@
# no more references, undefine the variable
del self.select.defined_vars[vref.name]
- def _may_be_shared_with(self, sniprel, target, searchedvarname):
+ def _may_be_shared_with(self, sniprel, target):
"""if the snippet relation can be skipped to use a relation from the
original query, return that relation node
"""
rschema = self.schema.rschema(sniprel.r_type)
- try:
- if target == 'object':
- orel = self.varinfo['lhs_rels'][sniprel.r_type]
- cardindex = 0
- ttypes_func = rschema.objects
- rdef = rschema.rdef
- else: # target == 'subject':
- orel = self.varinfo['rhs_rels'][sniprel.r_type]
- cardindex = 1
- ttypes_func = rschema.subjects
- rdef = lambda x, y: rschema.rdef(y, x)
- except KeyError:
- # may be raised by self.varinfo['xhs_rels'][sniprel.r_type]
- return None
- # can't share neged relation or relations with different outer join
- if (orel.neged(strict=True) or sniprel.neged(strict=True)
- or (orel.optional and orel.optional != sniprel.optional)):
- return None
- # if cardinality is in '?1', we can ignore the snippet relation and use
- # variable from the original query
- for etype in self.varinfo['stinfo']['possibletypes']:
- for ttype in ttypes_func(etype):
- if rdef(etype, ttype).cardinality[cardindex] in '+*':
- return None
+ for vi in self.varinfos:
+ try:
+ if target == 'object':
+ orel = vi['lhs_rels'][sniprel.r_type]
+ cardindex = 0
+ ttypes_func = rschema.objects
+ rdef = rschema.rdef
+ else: # target == 'subject':
+ orel = vi['rhs_rels'][sniprel.r_type]
+ cardindex = 1
+ ttypes_func = rschema.subjects
+ rdef = lambda x, y: rschema.rdef(y, x)
+ except KeyError:
+ # may be raised by vi['xhs_rels'][sniprel.r_type]
+ return None
+ # can't share neged relation or relations with different outer join
+ if (orel.neged(strict=True) or sniprel.neged(strict=True)
+ or (orel.optional and orel.optional != sniprel.optional)):
+ return None
+ # if cardinality is in '?1', we can ignore the snippet relation and use
+ # variable from the original query
+ for etype in vi['stinfo']['possibletypes']:
+ for ttype in ttypes_func(etype):
+ if rdef(etype, ttype).cardinality[cardindex] in '+*':
+ return None
+ break
return orel
def _use_orig_term(self, snippet_varname, term):
@@ -560,12 +586,12 @@
if self.existingvars and not self.keep_var(rhs.name):
return
if lhs.name in self.revvarmap and rhs.name != 'U':
- orel = self._may_be_shared_with(node, 'object', lhs.name)
+ orel = self._may_be_shared_with(node, 'object')
if orel is not None:
self._use_orig_term(rhs.name, orel.children[1].children[0])
return
elif rhs.name in self.revvarmap and lhs.name != 'U':
- orel = self._may_be_shared_with(node, 'subject', rhs.name)
+ orel = self._may_be_shared_with(node, 'subject')
if orel is not None:
self._use_orig_term(lhs.name, orel.children[0])
return
@@ -600,10 +626,11 @@
def visit_variableref(self, node):
"""get the sql name for a variable reference"""
if node.name in self.revvarmap:
- if self.varinfo.get('const') is not None:
- return n.Constant(self.varinfo['const'], 'Int') # XXX gae
- return n.VariableRef(self.select.get_variable(
- self.revvarmap[node.name]))
+ selectvar, index = self.revvarmap[node.name]
+ vi = self.varinfos[index]
+ if vi.get('const') is not None:
+ return n.Constant(vi['const'], 'Int') # XXX gae
+ return n.VariableRef(self.select.get_variable(selectvar))
vname_or_term = self._get_varname_or_term(node.name)
if isinstance(vname_or_term, basestring):
return n.VariableRef(self.select.get_variable(vname_or_term))
--- a/server/__init__.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/__init__.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -129,7 +129,6 @@
# on connection
config.creating = True
config.consider_user_state = False
- config.set_language = False
# only enable the system source at initialization time
repo = Repository(config, vreg=vreg)
schema = repo.schema
@@ -210,7 +209,6 @@
# restore initial configuration
config.creating = False
config.consider_user_state = True
- config.set_language = True
print '-> database for instance %s initialized.' % config.appid
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/server/edition.py Wed Mar 30 11:17:58 2011 +0200
@@ -0,0 +1,150 @@
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
+"""helper classes to handle server-side edition of entities"""
+
+from __future__ import with_statement
+
+__docformat__ = "restructuredtext en"
+
+from copy import copy
+from yams import ValidationError
+
+
+_MARKER = object()
+
+class dict_protocol_catcher(object):
+ def __init__(self, entity):
+ self.__entity = entity
+ def __getitem__(self, attr):
+ return self.__entity.cw_edited[attr]
+ def __setitem__(self, attr, value):
+ self.__entity.cw_edited[attr] = value
+ def __getattr__(self, attr):
+ return getattr(self.__entity, attr)
+
+
+class EditedEntity(dict):
+ """encapsulate entities attributes being written by an RQL query"""
+ def __init__(self, entity, **kwargs):
+ dict.__init__(self, **kwargs)
+ self.entity = entity
+ self.skip_security = set()
+ self.querier_pending_relations = {}
+ self.saved = False
+
+ def __hash__(self):
+ # dict|set keyable
+ return hash(id(self))
+
+ def __cmp__(self, other):
+ # we don't want comparison by value inherited from dict
+ return cmp(id(self), id(other))
+
+ def __setitem__(self, attr, value):
+ assert attr != 'eid'
+ # don't add attribute into skip_security if already in edited
+ # attributes, else we may accidentaly skip a desired security check
+ if attr not in self:
+ self.skip_security.add(attr)
+ self.edited_attribute(attr, value)
+
+ def __delitem__(self, attr):
+ assert not self.saved, 'too late to modify edited attributes'
+ super(EditedEntity, self).__delitem__(attr)
+ self.entity.cw_attr_cache.pop(attr, None)
+
+ def pop(self, attr, *args):
+ # don't update skip_security by design (think to storage api)
+ assert not self.saved, 'too late to modify edited attributes'
+ value = super(EditedEntity, self).pop(attr, *args)
+ self.entity.cw_attr_cache.pop(attr, *args)
+ return value
+
+ def setdefault(self, attr, default):
+ assert attr != 'eid'
+ # don't add attribute into skip_security if already in edited
+ # attributes, else we may accidentaly skip a desired security check
+ if attr not in self:
+ self[attr] = default
+ return self[attr]
+
+ def update(self, values, skipsec=True):
+ if skipsec:
+ setitem = self.__setitem__
+ else:
+ setitem = self.edited_attribute
+ for attr, value in values.iteritems():
+ setitem(attr, value)
+
+ def edited_attribute(self, attr, value):
+ """attribute being edited by a rql query: should'nt be added to
+ skip_security
+ """
+ assert not self.saved, 'too late to modify edited attributes'
+ super(EditedEntity, self).__setitem__(attr, value)
+ self.entity.cw_attr_cache[attr] = value
+
+ def oldnewvalue(self, attr):
+ """returns the couple (old attr value, new attr value)
+
+ NOTE: will only work in a before_update_entity hook
+ """
+ assert not self.saved, 'too late to get the old value'
+ # get new value and remove from local dict to force a db query to
+ # fetch old value
+ newvalue = self.entity.cw_attr_cache.pop(attr, _MARKER)
+ oldvalue = getattr(self.entity, attr)
+ if newvalue is not _MARKER:
+ self.entity.cw_attr_cache[attr] = newvalue
+ else:
+ newvalue = oldvalue
+ return oldvalue, newvalue
+
+ def set_defaults(self):
+ """set default values according to the schema"""
+ for attr, value in self.entity.e_schema.defaults():
+ if not attr in self:
+ self[str(attr)] = value
+
+ def check(self, creation=False):
+ """check the entity edition against its schema. Only final relation
+ are checked here, constraint on actual relations are checked in hooks
+ """
+ entity = self.entity
+ if creation:
+ # on creations, we want to check all relations, especially
+ # required attributes
+ relations = [rschema for rschema in entity.e_schema.subject_relations()
+ if rschema.final and rschema.type != 'eid']
+ else:
+ relations = [entity._cw.vreg.schema.rschema(rtype)
+ for rtype in self]
+ try:
+ entity.e_schema.check(dict_protocol_catcher(entity),
+ creation=creation, _=entity._cw._,
+ relations=relations)
+ except ValidationError, ex:
+ ex.entity = self.entity
+ raise
+
+ def clone(self):
+ thecopy = EditedEntity(copy(self.entity))
+ thecopy.entity.cw_attr_cache = copy(self.entity.cw_attr_cache)
+ thecopy.entity._cw_related_cache = {}
+ thecopy.update(self, skipsec=False)
+ return thecopy
--- a/server/migractions.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/migractions.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -1307,20 +1307,32 @@
# CWProperty handling ######################################################
def cmd_property_value(self, pkey):
- rql = 'Any V WHERE X is CWProperty, X pkey %(k)s, X value V'
- rset = self.rqlexec(rql, {'k': pkey}, ask_confirm=False)
+ """retreive the site-wide persistent property value for the given key.
+
+ To get a user specific property value, use appropriate method on CWUser
+ instance.
+ """
+ rset = self.rqlexec(
+ 'Any V WHERE X is CWProperty, X pkey %(k)s, X value V, NOT X for_user U',
+ {'k': pkey}, ask_confirm=False)
return rset[0][0]
def cmd_set_property(self, pkey, value):
+ """set the site-wide persistent property value for the given key to the
+ given value.
+
+ To set a user specific property value, use appropriate method on CWUser
+ instance.
+ """
value = unicode(value)
try:
- prop = self.rqlexec('CWProperty X WHERE X pkey %(k)s', {'k': pkey},
- ask_confirm=False).get_entity(0, 0)
+ prop = self.rqlexec(
+ 'CWProperty X WHERE X pkey %(k)s, NOT X for_user U',
+ {'k': pkey}, ask_confirm=False).get_entity(0, 0)
except:
self.cmd_create_entity('CWProperty', pkey=unicode(pkey), value=value)
else:
- self.rqlexec('SET X value %(v)s WHERE X pkey %(k)s',
- {'k': pkey, 'v': value}, ask_confirm=False)
+ prop.set_attributes(value=value)
# other data migration commands ###########################################
@@ -1360,6 +1372,18 @@
self.commit()
return entity
+ def cmd_find_entities(self, etype, **kwargs):
+ """find entities of the given type and attribute values"""
+ return self._cw.find_entities(etype, **kwargs)
+
+ def cmd_find_one_entity(self, etype, **kwargs):
+ """find one entity of the given type and attribute values.
+
+ raise :exc:`cubicweb.req.FindEntityError` if can not return one and only
+ one entity.
+ """
+ return self._cw.find_one_entity(etype, **kwargs)
+
def cmd_update_etype_fti_weight(self, etype, weight):
if self.repo.system_source.dbdriver == 'postgres':
self.sqlexec('UPDATE appears SET weight=%(weight)s '
--- a/server/querier.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/querier.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -38,7 +38,8 @@
from cubicweb.server.utils import cleanup_solutions
from cubicweb.server.rqlannotation import SQLGenAnnotator, set_qdata
-from cubicweb.server.ssplanner import READ_ONLY_RTYPES, add_types_restriction, EditedEntity
+from cubicweb.server.ssplanner import READ_ONLY_RTYPES, add_types_restriction
+from cubicweb.server.edition import EditedEntity
from cubicweb.server.session import security_enabled
def empty_rset(rql, args, rqlst=None):
@@ -353,7 +354,7 @@
myrqlst = select.copy(solutions=lchecksolutions)
myunion.append(myrqlst)
# in-place rewrite + annotation / simplification
- lcheckdef = [((var, 'X'), rqlexprs) for var, rqlexprs in lcheckdef]
+ lcheckdef = [({var: 'X'}, rqlexprs) for var, rqlexprs in lcheckdef]
rewrite(myrqlst, lcheckdef, lchecksolutions, self.args)
add_noinvariant(noinvariant, restricted, myrqlst, nbtrees)
if () in localchecks:
--- a/server/repository.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/repository.py Wed Mar 30 11:17:58 2011 +0200
@@ -59,6 +59,7 @@
security_enabled
from cubicweb.server.ssplanner import EditedEntity
+
def prefill_entity_caches(entity, relations):
session = entity._cw
# prefill entity relation caches
@@ -134,6 +135,7 @@
vreg = cwvreg.CubicWebVRegistry(config)
self.vreg = vreg
self.pyro_registered = False
+ self.pyro_uri = None
self.info('starting repository from %s', self.config.apphome)
# dictionary of opened sessions
self._sessions = {}
@@ -415,7 +417,9 @@
self.exception('error while closing %s' % pool)
continue
if self.pyro_registered:
- pyro_unregister(self.config)
+ if self._use_pyrons():
+ pyro_unregister(self.config)
+ self.pyro_uri = None
hits, misses = self.querier.cache_hit, self.querier.cache_miss
try:
self.info('rql st cache hit/miss: %s/%s (%s%% hits)', hits, misses,
@@ -1419,20 +1423,32 @@
config['pyro-instance-id'] = appid
return appid
+ def _use_pyrons(self):
+ """return True if the pyro-ns-host is set to something else
+ than NO_PYRONS, meaning we want to go through a pyro
+ nameserver"""
+ return self.config['pyro-ns-host'] != 'NO_PYRONS'
+
def pyro_register(self, host=''):
"""register the repository as a pyro object"""
from logilab.common import pyro_ext as pyro
daemon = pyro.register_object(self, self.pyro_appid,
daemonhost=self.config['pyro-host'],
- nshost=self.config['pyro-ns-host'])
+ nshost=self.config['pyro-ns-host'],
+ use_pyrons=self._use_pyrons())
self.info('repository registered as a pyro object %s', self.pyro_appid)
+ self.pyro_uri = pyro.get_object_uri(self.pyro_appid)
+ self.info('pyro uri is: %s', self.pyro_uri)
self.pyro_registered = True
# register a looping task to regularly ensure we're still registered
# into the pyro name server
- self.looping_task(60*10, self._ensure_pyro_ns)
+ if self._use_pyrons():
+ self.looping_task(60*10, self._ensure_pyro_ns)
return daemon
def _ensure_pyro_ns(self):
+ if not self._use_pyrons():
+ return
from logilab.common import pyro_ext as pyro
pyro.ns_reregister(self.pyro_appid, nshost=self.config['pyro-ns-host'])
self.info('repository re-registered as a pyro object %s',
--- a/server/serverconfig.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/serverconfig.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -215,8 +215,6 @@
# read the schema from the database
read_instance_schema = True
- # set to true while creating an instance
- creating = False
# set this to true to get a minimal repository, for instance to get cubes
# information on commands such as i18ninstance, db-restore, etc...
quick_start = False
--- a/server/serverctl.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/serverctl.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -27,11 +27,11 @@
from logilab.common import nullobject
from logilab.common.configuration import Configuration
-from logilab.common.shellutils import ASK
+from logilab.common.shellutils import ASK, generate_password
from cubicweb import AuthenticationError, ExecutionError, ConfigurationError
from cubicweb.toolsutils import Command, CommandHandler, underline_title
-from cubicweb.cwctl import CWCTL
+from cubicweb.cwctl import CWCTL, check_options_consistency
from cubicweb.server import SOURCE_TYPES
from cubicweb.server.serverconfig import (
USER_OPTIONS, ServerConfiguration, SourceConfiguration,
@@ -39,7 +39,7 @@
# utility functions ###########################################################
-def source_cnx(source, dbname=None, special_privs=False, verbose=True):
+def source_cnx(source, dbname=None, special_privs=False, interactive=True):
"""open and return a connection to the system database defined in the
given server.serverconfig
"""
@@ -50,21 +50,20 @@
dbname = source['db-name']
driver = source['db-driver']
dbhelper = get_db_helper(driver)
- if verbose:
+ if interactive:
print '-> connecting to %s database' % driver,
if dbhost:
print '%s@%s' % (dbname, dbhost),
else:
print dbname,
if dbhelper.users_support:
- if not special_privs and source.get('db-user'):
- user = source['db-user']
- if verbose:
+ if not interactive or (not special_privs and source.get('db-user')):
+ user = source.get('db-user', os.environ.get('USER', ''))
+ if interactive:
print 'as', user
password = source.get('db-password')
else:
- if verbose:
- print
+ print
if special_privs:
print 'WARNING'
print ('the user will need the following special access rights '
@@ -95,7 +94,7 @@
return cnx
def system_source_cnx(source, dbms_system_base=False,
- special_privs='CREATE/DROP DATABASE', verbose=True):
+ special_privs='CREATE/DROP DATABASE', interactive=True):
"""shortcut to get a connextion to the instance system database
defined in the given config. If <dbms_system_base> is True,
connect to the dbms system database instead (for task such as
@@ -104,10 +103,12 @@
if dbms_system_base:
from logilab.database import get_db_helper
system_db = get_db_helper(source['db-driver']).system_database()
- return source_cnx(source, system_db, special_privs=special_privs, verbose=verbose)
- return source_cnx(source, special_privs=special_privs, verbose=verbose)
+ return source_cnx(source, system_db, special_privs=special_privs,
+ interactive=interactive)
+ return source_cnx(source, special_privs=special_privs,
+ interactive=interactive)
-def _db_sys_cnx(source, special_privs, verbose=True):
+def _db_sys_cnx(source, special_privs, interactive=True):
"""return a connection on the RDMS system table (to create/drop a user or a
database)
"""
@@ -118,7 +119,7 @@
helper = get_db_helper(driver)
# connect on the dbms system base to create our base
cnx = system_source_cnx(source, True, special_privs=special_privs,
- verbose=verbose)
+ interactive=interactive)
# disable autocommit (isolation_level(1)) because DROP and
# CREATE DATABASE can't be executed in a transaction
try:
@@ -153,38 +154,49 @@
cmdname = 'create'
cfgname = 'repository'
- def bootstrap(self, cubes, inputlevel=0):
+ def bootstrap(self, cubes, automatic=False, inputlevel=0):
"""create an instance by copying files from the given cube and by asking
information necessary to build required configuration files
"""
config = self.config
- print underline_title('Configuring the repository')
- config.input_config('email', inputlevel)
- # ask for pyro configuration if pyro is activated and we're not using a
- # all-in-one config, in which case this is done by the web side command
- # handler
- if config.pyro_enabled() and config.name != 'all-in-one':
- config.input_config('pyro', inputlevel)
- print '\n'+underline_title('Configuring the sources')
+ if not automatic:
+ print underline_title('Configuring the repository')
+ config.input_config('email', inputlevel)
+ # ask for pyro configuration if pyro is activated and we're not
+ # using a all-in-one config, in which case this is done by the web
+ # side command handler
+ if config.pyro_enabled() and config.name != 'all-in-one':
+ config.input_config('pyro', inputlevel)
+ print '\n'+underline_title('Configuring the sources')
sourcesfile = config.sources_file()
- # XXX hack to make Method('default_instance_id') usable in db option
- # defs (in native.py)
+ # hack to make Method('default_instance_id') usable in db option defs
+ # (in native.py)
sconfig = SourceConfiguration(config,
options=SOURCE_TYPES['native'].options)
- sconfig.input_config(inputlevel=inputlevel)
+ if not automatic:
+ sconfig.input_config(inputlevel=inputlevel)
+ print
sourcescfg = {'system': sconfig}
- print
- sconfig = Configuration(options=USER_OPTIONS)
- sconfig.input_config(inputlevel=inputlevel)
+ if automatic:
+ # XXX modify a copy
+ password = generate_password()
+ print 'Administration account is admin / %s' % password
+ USER_OPTIONS[1][1]['default'] = password
+ sconfig = Configuration(options=USER_OPTIONS)
+ else:
+ sconfig = Configuration(options=USER_OPTIONS)
+ sconfig.input_config(inputlevel=inputlevel)
sourcescfg['admin'] = sconfig
config.write_sources_file(sourcescfg)
# remember selected cubes for later initialization of the database
config.write_bootstrap_cubes_file(cubes)
- def postcreate(self):
- if ASK.confirm('Run db-create to create the system database ?'):
- verbosity = (self.config.mode == 'installed') and 'y' or 'n'
- CWCTL.run(['db-create', self.config.appid, '--verbose=%s' % verbosity])
+ def postcreate(self, automatic=False, inputlevel=0):
+ if automatic:
+ CWCTL.run(['db-create', '--automatic', self.config.appid])
+ elif ASK.confirm('Run db-create to create the system database ?'):
+ CWCTL.run(['db-create', '--config-level', str(inputlevel),
+ self.config.appid])
else:
print ('-> nevermind, you can do it later with '
'"cubicweb-ctl db-create %s".' % self.config.appid)
@@ -292,27 +304,30 @@
arguments = '<instance>'
min_args = max_args = 1
options = (
+ ('automatic',
+ {'short': 'a', 'action' : 'store_true',
+ 'default': False,
+ 'help': 'automatic mode: never ask and use default answer to every '
+ 'question. this may require that your login match a database super '
+ 'user (allowed to create database & all).',
+ }),
+ ('config-level',
+ {'short': 'l', 'type' : 'int', 'metavar': '<level>',
+ 'default': 0,
+ 'help': 'configuration level (0..2): 0 will ask for essential '
+ 'configuration parameters only while 2 will ask for all parameters',
+ }),
('create-db',
{'short': 'c', 'type': 'yn', 'metavar': '<y or n>',
'default': True,
- 'help': 'create the database (yes by default)'}),
- ('verbose',
- {'short': 'v', 'type' : 'yn', 'metavar': '<verbose>',
- 'default': 'n',
- 'help': 'verbose mode: will ask all possible configuration questions',
- }
- ),
- ('automatic',
- {'short': 'a', 'type' : 'yn', 'metavar': '<auto>',
- 'default': 'n',
- 'help': 'automatic mode: never ask and use default answer to every question',
- }
- ),
+ 'help': 'create the database (yes by default)'
+ }),
)
+
def run(self, args):
"""run the command with its specific arguments"""
from logilab.database import get_db_helper
- verbose = self.get('verbose')
+ check_options_consistency(self.config)
automatic = self.get('automatic')
appid = args.pop()
config = ServerConfiguration.config_for(appid)
@@ -329,7 +344,7 @@
print '\n'+underline_title('Creating the system database')
# connect on the dbms system base to create our base
dbcnx = _db_sys_cnx(source, 'CREATE/DROP DATABASE and / or USER',
- verbose=verbose)
+ interactive=not automatic)
cursor = dbcnx.cursor()
try:
if helper.users_support:
@@ -342,6 +357,8 @@
if automatic or ASK.confirm('Database %s already exists -- do you want to drop it ?' % dbname):
cursor.execute('DROP DATABASE %s' % dbname)
else:
+ print ('you may want to run "cubicweb-ctl db-init '
+ '--drop %s" manually to continue.' % config.appid)
return
createdb(helper, source, dbcnx, cursor)
dbcnx.commit()
@@ -350,7 +367,7 @@
dbcnx.rollback()
raise
cnx = system_source_cnx(source, special_privs='CREATE LANGUAGE',
- verbose=verbose)
+ interactive=not automatic)
cursor = cnx.cursor()
helper.init_fti_extensions(cursor)
# postgres specific stuff
@@ -363,8 +380,12 @@
cnx.commit()
print '-> database for instance %s created and necessary extensions installed.' % appid
print
- if automatic or ASK.confirm('Run db-init to initialize the system database ?'):
- CWCTL.run(['db-init', config.appid])
+ if automatic:
+ CWCTL.run(['db-init', '--automatic', '--config-level', '0',
+ config.appid])
+ elif ASK.confirm('Run db-init to initialize the system database ?'):
+ CWCTL.run(['db-init', '--config-level',
+ str(self.config.config_level), config.appid])
else:
print ('-> nevermind, you can do it later with '
'"cubicweb-ctl db-init %s".' % config.appid)
@@ -383,18 +404,27 @@
arguments = '<instance>'
min_args = max_args = 1
options = (
+ ('automatic',
+ {'short': 'a', 'action' : 'store_true',
+ 'default': False,
+ 'help': 'automatic mode: never ask and use default answer to every '
+ 'question.',
+ }),
+ ('config-level',
+ {'short': 'l', 'type': 'int', 'default': 1,
+ 'help': 'level threshold for questions asked when configuring '
+ 'another source'
+ }),
('drop',
{'short': 'd', 'action': 'store_true',
'default': False,
- 'help': 'insert drop statements to remove previously existant \
-tables, indexes... (no by default)'}),
- ('config-level',
- {'short': 'l', 'type': 'int', 'default': 1,
- 'help': 'level threshold for questions asked when configuring another source'
+ 'help': 'insert drop statements to remove previously existant '
+ 'tables, indexes... (no by default)'
}),
)
def run(self, args):
+ check_options_consistency(self.config)
print '\n'+underline_title('Initializing the system database')
from cubicweb.server import init_repository
from logilab.database import get_connection
@@ -415,8 +445,10 @@
'the %s file. Resolve this first (error: %s).'
% (config.sources_file(), str(ex).strip()))
init_repository(config, drop=self.config.drop)
- while ASK.confirm('Enter another source ?', default_is_yes=False):
- CWCTL.run(['add-source', '--config-level', self.config.config_level, config.appid])
+ if not self.config.automatic:
+ while ASK.confirm('Enter another source ?', default_is_yes=False):
+ CWCTL.run(['add-source', '--config-level',
+ str(self.config.config_level), config.appid])
class AddSourceCommand(Command):
--- a/server/session.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/session.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -37,6 +37,7 @@
from cubicweb.dbapi import ConnectionProperties
from cubicweb.utils import make_uid, RepeatList
from cubicweb.rqlrewrite import RQLRewriter
+from cubicweb.server.edition import EditedEntity
ETYPE_PYOBJ_MAP[Binary] = 'Bytes'
@@ -215,8 +216,9 @@
with security_enabled(self, False, False):
if self.vreg.schema[rtype].inlined:
entity = self.entity_from_eid(fromeid)
- entity[rtype] = toeid
- self.repo.glob_update_entity(self, entity, set((rtype,)))
+ edited = EditedEntity(entity)
+ edited.edited_attribute(rtype, toeid)
+ self.repo.glob_update_entity(self, edited)
else:
self.repo.glob_add_relation(self, fromeid, rtype, toeid)
@@ -234,7 +236,7 @@
with security_enabled(self, False, False):
if self.vreg.schema[rtype].inlined:
entity = self.entity_from_eid(fromeid)
- entity[rtype] = None
+ entity.cw_attr_cache[rtype] = None
self.repo.glob_update_entity(self, entity, set((rtype,)))
else:
self.repo.glob_delete_relation(self, fromeid, rtype, toeid)
--- a/server/sources/__init__.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/sources/__init__.py Wed Mar 30 11:17:58 2011 +0200
@@ -31,7 +31,7 @@
from cubicweb import ValidationError, set_log_methods, server
from cubicweb.schema import VIRTUAL_RTYPES
from cubicweb.server.sqlutils import SQL_PREFIX
-from cubicweb.server.ssplanner import EditedEntity
+from cubicweb.server.edition import EditedEntity
def dbg_st_search(uri, union, varmap, args, cachekey=None, prefix='rql for'):
--- a/server/sources/ldapuser.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/sources/ldapuser.py Wed Mar 30 11:17:58 2011 +0200
@@ -522,7 +522,8 @@
def _search(self, session, base, scope,
searchstr='(objectClass=*)', attrs=()):
"""make an ldap query"""
- self.debug('ldap search %s %s %s %s %s', self.uri, base, scope, searchstr, list(attrs))
+ self.debug('ldap search %s %s %s %s %s', self.uri, base, scope,
+ searchstr, list(attrs))
# XXX for now, we do not have connection pool support for LDAP, so
# this is always self._conn
cnx = session.pool.connection(self.uri).cnx
--- a/server/sources/native.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/sources/native.py Wed Mar 30 11:17:58 2011 +0200
@@ -58,7 +58,7 @@
from cubicweb.server.rqlannotation import set_qdata
from cubicweb.server.hook import CleanupDeletedEidsCacheOp
from cubicweb.server.session import hooks_control, security_enabled
-from cubicweb.server.ssplanner import EditedEntity
+from cubicweb.server.edition import EditedEntity
from cubicweb.server.sources import AbstractSource, dbg_st_search, dbg_results
from cubicweb.server.sources.rql2sql import SQLGenerator
--- a/server/sources/storages.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/sources/storages.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -24,7 +24,7 @@
from cubicweb import Binary, ValidationError
from cubicweb.server import hook
-from cubicweb.server.ssplanner import EditedEntity
+from cubicweb.server.edition import EditedEntity
def set_attribute_storage(repo, etype, attr, storage):
--- a/server/ssplanner.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/ssplanner.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -21,8 +21,6 @@
__docformat__ = "restructuredtext en"
-from copy import copy
-
from rql.stmts import Union, Select
from rql.nodes import Constant, Relation
@@ -31,6 +29,7 @@
from cubicweb.rqlrewrite import add_types_restriction
from cubicweb.server.session import security_enabled
from cubicweb.server.hook import CleanupDeletedEidsCacheOp
+from cubicweb.server.edition import EditedEntity
READ_ONLY_RTYPES = set(('eid', 'has_text', 'is', 'is_instance_of', 'identity'))
@@ -128,132 +127,6 @@
return select
-_MARKER = object()
-
-class dict_protocol_catcher(object):
- def __init__(self, entity):
- self.__entity = entity
- def __getitem__(self, attr):
- return self.__entity.cw_edited[attr]
- def __setitem__(self, attr, value):
- self.__entity.cw_edited[attr] = value
- def __getattr__(self, attr):
- return getattr(self.__entity, attr)
-
-
-class EditedEntity(dict):
- """encapsulate entities attributes being written by an RQL query"""
- def __init__(self, entity, **kwargs):
- dict.__init__(self, **kwargs)
- self.entity = entity
- self.skip_security = set()
- self.querier_pending_relations = {}
- self.saved = False
-
- def __hash__(self):
- # dict|set keyable
- return hash(id(self))
-
- def __cmp__(self, other):
- # we don't want comparison by value inherited from dict
- return cmp(id(self), id(other))
-
- def __setitem__(self, attr, value):
- assert attr != 'eid'
- # don't add attribute into skip_security if already in edited
- # attributes, else we may accidentaly skip a desired security check
- if attr not in self:
- self.skip_security.add(attr)
- self.edited_attribute(attr, value)
-
- def __delitem__(self, attr):
- assert not self.saved, 'too late to modify edited attributes'
- super(EditedEntity, self).__delitem__(attr)
- self.entity.cw_attr_cache.pop(attr, None)
-
- def pop(self, attr, *args):
- # don't update skip_security by design (think to storage api)
- assert not self.saved, 'too late to modify edited attributes'
- value = super(EditedEntity, self).pop(attr, *args)
- self.entity.cw_attr_cache.pop(attr, *args)
- return value
-
- def setdefault(self, attr, default):
- assert attr != 'eid'
- # don't add attribute into skip_security if already in edited
- # attributes, else we may accidentaly skip a desired security check
- if attr not in self:
- self[attr] = default
- return self[attr]
-
- def update(self, values, skipsec=True):
- if skipsec:
- setitem = self.__setitem__
- else:
- setitem = self.edited_attribute
- for attr, value in values.iteritems():
- setitem(attr, value)
-
- def edited_attribute(self, attr, value):
- """attribute being edited by a rql query: should'nt be added to
- skip_security
- """
- assert not self.saved, 'too late to modify edited attributes'
- super(EditedEntity, self).__setitem__(attr, value)
- self.entity.cw_attr_cache[attr] = value
-
- def oldnewvalue(self, attr):
- """returns the couple (old attr value, new attr value)
-
- NOTE: will only work in a before_update_entity hook
- """
- assert not self.saved, 'too late to get the old value'
- # get new value and remove from local dict to force a db query to
- # fetch old value
- newvalue = self.entity.cw_attr_cache.pop(attr, _MARKER)
- oldvalue = getattr(self.entity, attr)
- if newvalue is not _MARKER:
- self.entity.cw_attr_cache[attr] = newvalue
- else:
- newvalue = oldvalue
- return oldvalue, newvalue
-
- def set_defaults(self):
- """set default values according to the schema"""
- for attr, value in self.entity.e_schema.defaults():
- if not attr in self:
- self[str(attr)] = value
-
- def check(self, creation=False):
- """check the entity edition against its schema. Only final relation
- are checked here, constraint on actual relations are checked in hooks
- """
- entity = self.entity
- if creation:
- # on creations, we want to check all relations, especially
- # required attributes
- relations = [rschema for rschema in entity.e_schema.subject_relations()
- if rschema.final and rschema.type != 'eid']
- else:
- relations = [entity._cw.vreg.schema.rschema(rtype)
- for rtype in self]
- from yams import ValidationError
- try:
- entity.e_schema.check(dict_protocol_catcher(entity),
- creation=creation, _=entity._cw._,
- relations=relations)
- except ValidationError, ex:
- ex.entity = self.entity
- raise
-
- def clone(self):
- thecopy = EditedEntity(copy(self.entity))
- thecopy.entity.cw_attr_cache = copy(self.entity.cw_attr_cache)
- thecopy.entity._cw_related_cache = {}
- thecopy.update(self, skipsec=False)
- return thecopy
-
-
class SSPlanner(object):
"""SingleSourcePlanner: build execution plan for rql queries
--- a/server/test/unittest_ldapuser.py Thu Mar 24 15:21:13 2011 +0100
+++ b/server/test/unittest_ldapuser.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
--- a/test/unittest_entity.py Thu Mar 24 15:21:13 2011 +0100
+++ b/test/unittest_entity.py Wed Mar 30 11:17:58 2011 +0200
@@ -223,38 +223,48 @@
'Any X,AA ORDERBY AA DESC '
'WHERE E eid %(x)s, E tags X, X modification_date AA')
- def test_unrelated_rql_security_1(self):
+ def test_unrelated_rql_security_1_manager(self):
user = self.request().user
rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC '
- 'WHERE NOT S use_email O, S eid %(x)s, O is EmailAddress, O address AA, O alias AB, O modification_date AC')
+ 'WHERE NOT S use_email O, S eid %(x)s, '
+ 'O is EmailAddress, O address AA, O alias AB, O modification_date AC')
+
+ def test_unrelated_rql_security_1_user(self):
self.create_user('toto')
self.login('toto')
user = self.request().user
rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC '
- 'WHERE NOT S use_email O, S eid %(x)s, O is EmailAddress, O address AA, O alias AB, O modification_date AC')
+ 'WHERE NOT S use_email O, S eid %(x)s, '
+ 'O is EmailAddress, O address AA, O alias AB, O modification_date AC')
user = self.execute('Any X WHERE X login "admin"').get_entity(0, 0)
- self.assertRaises(Unauthorized, user.cw_unrelated_rql, 'use_email', 'EmailAddress', 'subject')
+ rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
+ self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
+ 'NOT EXISTS(S use_email O), S eid %(x)s, '
+ 'O is EmailAddress, O address AA, O alias AB, O modification_date AC, '
+ 'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
+
+ def test_unrelated_rql_security_1_anon(self):
self.login('anon')
user = self.request().user
- self.assertRaises(Unauthorized, user.cw_unrelated_rql, 'use_email', 'EmailAddress', 'subject')
+ rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
+ self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
+ 'NOT EXISTS(S use_email O), S eid %(x)s, '
+ 'O is EmailAddress, O address AA, O alias AB, O modification_date AC, '
+ 'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
def test_unrelated_rql_security_2(self):
email = self.execute('INSERT EmailAddress X: X address "hop"').get_entity(0, 0)
rql = email.cw_unrelated_rql('use_email', 'CWUser', 'object')[0]
self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA ASC '
'WHERE NOT S use_email O, O eid %(x)s, S is CWUser, S login AA, S firstname AB, S surname AC, S modification_date AD')
- #rql = email.cw_unrelated_rql('use_email', 'Person', 'object')[0]
- #self.assertEqual(rql, '')
self.login('anon')
email = self.execute('Any X WHERE X eid %(x)s', {'x': email.eid}).get_entity(0, 0)
rql = email.cw_unrelated_rql('use_email', 'CWUser', 'object')[0]
self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA '
'WHERE NOT EXISTS(S use_email O), O eid %(x)s, S is CWUser, S login AA, S firstname AB, S surname AC, S modification_date AD, '
'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
- #rql = email.cw_unrelated_rql('use_email', 'Person', 'object')[0]
- #self.assertEqual(rql, '')
def test_unrelated_rql_security_nonexistant(self):
self.login('anon')
--- a/test/unittest_rqlrewrite.py Thu Mar 24 15:21:13 2011 +0100
+++ b/test/unittest_rqlrewrite.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -62,15 +62,17 @@
def simplify(mainrqlst, needcopy=False):
rqlhelper.simplify(rqlst, needcopy)
rewriter = RQLRewriter(mock_object(vreg=FakeVReg, user=(mock_object(eid=1))))
- for v, snippets in snippets_map.items():
- snippets_map[v] = [isinstance(snippet, basestring)
- and mock_object(snippet_rqlst=parse('Any X WHERE '+snippet).children[0],
- expression='Any X WHERE '+snippet)
- or snippet
- for snippet in snippets]
+ snippets = []
+ for v, exprs in snippets_map.items():
+ rqlexprs = [isinstance(snippet, basestring)
+ and mock_object(snippet_rqlst=parse('Any X WHERE '+snippet).children[0],
+ expression='Any X WHERE '+snippet)
+ or snippet
+ for snippet in exprs]
+ snippets.append((dict([v]), rqlexprs))
rqlhelper.compute_solutions(rqlst.children[0], {'eid': eid_func_map}, kwargs=kwargs)
solutions = rqlst.children[0].solutions
- rewriter.rewrite(rqlst.children[0], snippets_map.items(), solutions, kwargs,
+ rewriter.rewrite(rqlst.children[0], snippets, solutions, kwargs,
existingvars)
test_vrefs(rqlst.children[0])
return rewriter.rewritten
--- a/utils.py Thu Mar 24 15:21:13 2011 +0100
+++ b/utils.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
--- a/web/data/cubicweb.ajax.js Thu Mar 24 15:21:13 2011 +0100
+++ b/web/data/cubicweb.ajax.js Wed Mar 30 11:17:58 2011 +0200
@@ -283,7 +283,7 @@
* dictionary, `reqtype` the HTTP request type (get 'GET' or 'POST').
*/
function loadRemote(url, form, reqtype, sync) {
- if (!url.toLowerCase().startswith(baseuri())) {
+ if (!url.toLowerCase().startswith(baseuri().toLowerCase())) {
url = baseuri() + url;
}
if (!sync) {
--- a/web/data/cubicweb.old.css Thu Mar 24 15:21:13 2011 +0100
+++ b/web/data/cubicweb.old.css Wed Mar 30 11:17:58 2011 +0200
@@ -283,7 +283,7 @@
}
table#mainLayout{
- margin:0px 3px;
+ padding: 0px 3px;
}
table#mainLayout td#contentColumn {
--- a/web/data/jquery.ui.datepicker-es.js Thu Mar 24 15:21:13 2011 +0100
+++ b/web/data/jquery.ui.datepicker-es.js Wed Mar 30 11:17:58 2011 +0200
@@ -10,9 +10,9 @@
'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun',
'Jul','Ago','Sep','Oct','Nov','Dic'],
- dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'],
- dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
- dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
+ dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'],
+ dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
+ dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
weekHeader: 'Sm',
dateFormat: 'dd/mm/yy',
firstDay: 1,
--- a/web/facet.py Thu Mar 24 15:21:13 2011 +0100
+++ b/web/facet.py Wed Mar 30 11:17:58 2011 +0200
@@ -53,7 +53,7 @@
from logilab.mtconverter import xml_escape
from logilab.common.graph import has_path
from logilab.common.decorators import cached
-from logilab.common.date import datetime2ticks
+from logilab.common.date import datetime2ticks, ustrftime, ticks2datetime
from logilab.common.compat import all
from rql import parse, nodes, utils
@@ -981,7 +981,11 @@
def formatvalue(self, value):
"""format `value` before in order to insert it in the RQL query"""
- return '"%s"' % date.fromtimestamp(float(value) / 1000).strftime('%Y/%m/%d')
+ try:
+ date_value = ticks2datetime(float(value))
+ except (ValueError, OverflowError):
+ return u'"date out-of-range"'
+ return '"%s"' % ustrftime(date_value, '%Y/%m/%d')
class HasRelationFacet(AbstractFacet):
--- a/web/formwidgets.py Thu Mar 24 15:21:13 2011 +0100
+++ b/web/formwidgets.py Wed Mar 30 11:17:58 2011 +0200
@@ -594,8 +594,11 @@
value = self.values(form, field)[0]
else:
value = self.datestr
+ attrs = {}
+ if self.settabindex:
+ attrs['tabindex'] = req.next_tabindex()
return tags.input(id=domid, name=domid, value=value,
- type='text', size='10')
+ type='text', size='10', **attrs)
class JQueryTimePicker(FieldWidget):
@@ -620,6 +623,9 @@
value = self.values(form, field)[0]
else:
value = self.timestr
+ attrs = {}
+ if self.settabindex:
+ attrs['tabindex'] = req.next_tabindex()
return tags.input(id=domid, name=domid, value=value,
type='text', size='5')
--- a/web/test/unittest_reledit.py Thu Mar 24 15:21:13 2011 +0100
+++ b/web/test/unittest_reledit.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -33,9 +33,9 @@
class ClickAndEditFormTC(ReleditMixinTC, CubicWebTC):
def test_default_config(self):
- reledit = {'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'title', 'subject', 'title-subject-%(eid)s', false, '');" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
- 'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue"><not specified></div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm('edition', %(eid)s, 'long_desc', 'subject', 'long_desc-subject-%(eid)s', false, 'autolimited');" title="click to add a value"><img title="click to add a value" src="data/plus.png" alt="click to add a value"/></div></div></div>""",
- 'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><not specified></div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+ reledit = {'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'title', 'subject', 'title-subject-%(eid)s', false, '');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+ 'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue"><not specified></div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm('edition', %(eid)s, 'long_desc', 'subject', 'long_desc-subject-%(eid)s', false, 'autolimited');" title="click to add a value"><img title="click to add a value" src="http://testing.fr/cubicweb/data/plus.png" alt="click to add a value"/></div></div></div>""",
+ 'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><not specified></div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
'composite_card11_2ttypes': """<not specified>""",
'concerns': """<not specified>"""}
@@ -76,7 +76,7 @@
<td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel('title-subject-%(eid)s')" tabindex="3" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
</tr></table>
</fieldset>
-</form><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'title', 'subject', 'title-subject-%(eid)s', false, '');" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+</form><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'title', 'subject', 'title-subject-%(eid)s', false, '');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue"><not specified></div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="long_desc-subject-%(eid)s-form" onsubmit="return freezeFormButtons('long_desc-subject-%(eid)s-form');" class="releditForm" cubicweb:target="eformframe">
<fieldset>
@@ -120,7 +120,7 @@
<td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel('long_desc-subject-%(eid)s')" tabindex="8" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
</tr></table>
</fieldset>
-</form><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm('edition', %(eid)s, 'long_desc', 'subject', 'long_desc-subject-%(eid)s', false, 'autolimited');" title="click to add a value"><img title="click to add a value" src="data/plus.png" alt="click to add a value"/></div></div></div>""",
+</form><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm('edition', %(eid)s, 'long_desc', 'subject', 'long_desc-subject-%(eid)s', false, 'autolimited');" title="click to add a value"><img title="click to add a value" src="http://testing.fr/cubicweb/data/plus.png" alt="click to add a value"/></div></div></div>""",
'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><not specified></div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="manager-subject-%(eid)s-form" onsubmit="return freezeFormButtons('manager-subject-%(eid)s-form');" class="releditForm" cubicweb:target="eformframe">
<fieldset>
@@ -156,7 +156,7 @@
<td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel('manager-subject-%(eid)s')" tabindex="11" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
</tr></table>
</fieldset>
-</form><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+</form><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
'composite_card11_2ttypes': """<not specified>""",
'concerns': """<not specified>"""
}
@@ -190,11 +190,11 @@
reledit_ctrl.tag_object_of(('Ticket', 'concerns', 'Project'),
{'edit_target': 'rtype'})
reledit = {
- 'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'title', 'subject', 'title-subject-%(eid)s', true, '');" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
- 'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue"><long_desc is required></div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'long_desc', 'subject', 'long_desc-subject-%(eid)s', true, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
- 'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/personne/%(toto)s" title="">Toto</a></div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('edition', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div><div id="manager-subject-%(eid)s-delete" class="editableField" onclick="cw.reledit.loadInlineEditionForm('deleteconf', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to delete this value"><img title="click to delete this value" src="data/cancel.png" alt="click to delete this value"/></div></div></div>""",
+ 'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'title', 'subject', 'title-subject-%(eid)s', true, '');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+ 'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue"><long_desc is required></div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'long_desc', 'subject', 'long_desc-subject-%(eid)s', true, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+ 'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/personne/%(toto)s" title="">Toto</a></div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('edition', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div><div id="manager-subject-%(eid)s-delete" class="editableField" onclick="cw.reledit.loadInlineEditionForm('deleteconf', %(eid)s, 'manager', 'subject', 'manager-subject-%(eid)s', false, 'autolimited');" title="click to delete this value"><img title="click to delete this value" src="http://testing.fr/cubicweb/data/cancel.png" alt="click to delete this value"/></div></div></div>""",
'composite_card11_2ttypes': """<not specified>""",
- 'concerns': """<div id="concerns-object-%(eid)s-reledit" onmouseout="jQuery('#concerns-object-%(eid)s').addClass('hidden')" onmouseover="jQuery('#concerns-object-%(eid)s').removeClass('hidden')" class="releditField"><div id="concerns-object-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/ticket/%(tick)s" title="">write the code</a></div><div id="concerns-object-%(eid)s" class="editableField hidden"><div id="concerns-object-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'concerns', 'object', 'concerns-object-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>"""
+ 'concerns': """<div id="concerns-object-%(eid)s-reledit" onmouseout="jQuery('#concerns-object-%(eid)s').addClass('hidden')" onmouseover="jQuery('#concerns-object-%(eid)s').removeClass('hidden')" class="releditField"><div id="concerns-object-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/ticket/%(tick)s" title="">write the code</a></div><div id="concerns-object-%(eid)s" class="editableField hidden"><div id="concerns-object-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm('base', %(eid)s, 'concerns', 'object', 'concerns-object-%(eid)s', false, 'autolimited');" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>"""
}
for rschema, ttypes, role in self.proj.e_schema.relation_definitions(includefinal=True):
if rschema not in reledit:
--- a/web/webconfig.py Thu Mar 24 15:21:13 2011 +0100
+++ b/web/webconfig.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -297,7 +297,7 @@
baseurl = self['base-url'] or self.default_base_url()
if baseurl and baseurl[-1] != '/':
baseurl += '/'
- if not self.repairing:
+ if not (self.repairing or self.creating):
self.global_set_option('base-url', baseurl)
httpsurl = self['https-url']
if httpsurl:
--- a/web/webctl.py Thu Mar 24 15:21:13 2011 +0100
+++ b/web/webctl.py Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
@@ -17,26 +17,29 @@
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""cubicweb-ctl commands and command handlers common to twisted/modpython
web configuration
+"""
-"""
__docformat__ = "restructuredtext en"
+from logilab.common.shellutils import ASK
+
from cubicweb.toolsutils import CommandHandler, underline_title
-from logilab.common.shellutils import ASK
class WebCreateHandler(CommandHandler):
cmdname = 'create'
- def bootstrap(self, cubes, inputlevel=0):
+ def bootstrap(self, cubes, automatic=False, inputlevel=0):
"""bootstrap this configuration"""
- print '\n' + underline_title('Generic web configuration')
- config = self.config
- if config.repo_method == 'pyro' or config.pyro_enabled():
- print '\n' + underline_title('Pyro configuration')
- config.input_config('pyro', inputlevel)
- if ASK.confirm('Allow anonymous access ?', False):
- config.global_set_option('anonymous-user', 'anon')
- config.global_set_option('anonymous-password', 'anon')
+ if not automatic:
+ print '\n' + underline_title('Generic web configuration')
+ config = self.config
+ if config.repo_method == 'pyro' or config.pyro_enabled():
+ print '\n' + underline_title('Pyro configuration')
+ config.input_config('pyro', inputlevel)
+ config.input_config('web', inputlevel)
+ if ASK.confirm('Allow anonymous access ?', False):
+ config.global_set_option('anonymous-user', 'anon')
+ config.global_set_option('anonymous-password', 'anon')
- def postcreate(self):
+ def postcreate(self, *args, **kwargs):
"""hooks called once instance's initialization has been completed"""