--- a/.hgtags Thu Feb 18 09:22:04 2010 +0100
+++ b/.hgtags Fri Feb 26 17:39:33 2010 +0100
@@ -100,3 +100,7 @@
4281e1e2d76b9a37f38c0eeb1cbdcaa2fac6533c cubicweb-debian-version-3.5.12-1
5f957e351b0a60d5c5fff60c560b04e666c3a8c6 cubicweb-version-3.6.0
17e88f2485d1ea1fb8a3926a274637ce19e95d69 cubicweb-debian-version-3.6.0-1
+450804da3ab2476b7ede0c1f956235b4c239734f cubicweb-version-3.6.0
+d2ba93fcb8da95ceab08f48f8149a480215f149c cubicweb-debian-version-3.6.0-1
+4ae30c9ca11b1edad67d25b76fce672171d02023 cubicweb-version-3.6.1
+b9cdfe3341d1228687515d9af8686971ad5e6f5c cubicweb-debian-version-3.6.1-1
--- a/__pkginfo__.py Thu Feb 18 09:22:04 2010 +0100
+++ b/__pkginfo__.py Fri Feb 26 17:39:33 2010 +0100
@@ -7,7 +7,7 @@
distname = "cubicweb"
modname = "cubicweb"
-numversion = (3, 6, 0)
+numversion = (3, 6, 1)
version = '.'.join(str(num) for num in numversion)
license = 'LGPL'
--- a/appobject.py Thu Feb 18 09:22:04 2010 +0100
+++ b/appobject.py Fri Feb 26 17:39:33 2010 +0100
@@ -13,12 +13,9 @@
from logging import getLogger
from warnings import warn
-from logilab.common.decorators import classproperty
from logilab.common.deprecation import deprecated
from logilab.common.logging_ext import set_log_methods
-from cubicweb import Unauthorized, NoSelectableObject
-
# selector base classes and operations ########################################
@@ -268,7 +265,7 @@
pdef['default'] = getattr(cls, propid, pdef['default'])
pdef['sitewide'] = getattr(cls, 'site_wide', pdef.get('sitewide'))
registry.vreg.register_property(cls._cwpropkey(propid), **pdef)
- assert callable(cls.__select__), obj
+ assert callable(cls.__select__), cls
return cls
def __init__(self, req, **extra):
--- a/cwconfig.py Thu Feb 18 09:22:04 2010 +0100
+++ b/cwconfig.py Fri Feb 26 17:39:33 2010 +0100
@@ -236,7 +236,7 @@
options = (
('log-threshold',
{'type' : 'string', # XXX use a dedicated type?
- 'default': 'ERROR',
+ 'default': 'WARNING',
'help': 'server\'s log level',
'group': 'main', 'inputlevel': 1,
}),
--- a/cwctl.py Thu Feb 18 09:22:04 2010 +0100
+++ b/cwctl.py Fri Feb 26 17:39:33 2010 +0100
@@ -1,16 +1,26 @@
-"""%%prog %s [options] %s
+"""the cubicweb-ctl tool, based on logilab.common.clcommands to
+provide a pluggable commands system.
+
-The CubicWeb swiss-knife.
+:organization: Logilab
+:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
-%s"""
-
+# *ctl module should limit the number of import to be imported as quickly as
+# possible (for cubicweb-ctl reactivity, necessary for instance for usable bash
+# completion). So import locally in command helpers.
import sys
from os import remove, listdir, system, pathsep
try:
from os import kill, getpgid
except ImportError:
- def kill(*args): pass
- def getpgid(): pass
+ def kill(*args):
+ """win32 kill implementation"""
+ def getpgid():
+ """win32 getpgid implementation"""
from os.path import exists, join, isfile, isdir, dirname, abspath
@@ -158,6 +168,79 @@
# base commands ###############################################################
+def version_strictly_lower(a, b):
+ from logilab.common.changelog import Version
+ if a:
+ a = Version(a)
+ if b:
+ b = Version(b)
+ return a < b
+
+def max_version(a, b):
+ from logilab.common.changelog import Version
+ return str(max(Version(a), Version(b)))
+
+class ConfigurationProblem(object):
+ """Each cube has its own list of dependencies on other cubes/versions.
+
+ The ConfigurationProblem is used to record the loaded cubes, then to detect
+ inconsistencies in their dependencies.
+
+ See configuration management on wikipedia for litterature.
+ """
+
+ def __init__(self):
+ self.cubes = {}
+
+ def add_cube(self, name, info):
+ self.cubes[name] = info
+
+ def solve(self):
+ self.warnings = []
+ self.errors = []
+ self.read_constraints()
+ for cube, versions in sorted(self.constraints.items()):
+ oper, version = None, None
+ # simplify constraints
+ if versions:
+ for constraint in versions:
+ op, ver = constraint.split()
+ if oper is None:
+ oper = op
+ version = ver
+ elif op == '>=' and oper == '>=':
+ version = max_version(ver, version)
+ else:
+ print 'unable to handle this case', oper, version, op, ver
+ # "solve" constraint satisfaction problem
+ if cube not in self.cubes:
+ self.errors.append( ('add', cube, version) )
+ elif versions:
+ lower_strict = version_strictly_lower(self.cubes[cube].version, version)
+ if oper in ('>=','='):
+ if lower_strict:
+ self.errors.append( ('update', cube, version) )
+ else:
+ print 'unknown operator', oper
+
+ def read_constraints(self):
+ self.constraints = {}
+ self.reverse_constraints = {}
+ for cube, info in self.cubes.items():
+ if hasattr(info,'__depends_cubes__'):
+ use = info.__depends_cubes__
+ if not isinstance(use, dict):
+ use = dict((key, None) for key in use)
+ self.warnings.append('cube %s should define __depends_cubes__ as a dict not a list')
+ else:
+ self.warnings.append('cube %s should define __depends_cubes__' % cube)
+ use = dict((key, None) for key in info.__use__)
+ for name, constraint in use.items():
+ self.constraints.setdefault(name,set())
+ if constraint:
+ self.constraints[name].add(constraint)
+ self.reverse_constraints.setdefault(name, set()).add(cube)
+
class ListCommand(Command):
"""List configurations, cubes and instances.
@@ -185,6 +268,7 @@
continue
print ' ', line
print
+ cfgpb = ConfigurationProblem()
try:
cubesdir = pathsep.join(cwcfg.cubes_search_path())
namesize = max(len(x) for x in cwcfg.available_cubes())
@@ -200,6 +284,7 @@
try:
tinfo = cwcfg.cube_pkginfo(cube)
tversion = tinfo.version
+ cfgpb.add_cube(cube, tinfo)
except ConfigurationError:
tinfo = None
tversion = '[missing cube information]'
@@ -235,7 +320,21 @@
else:
print 'No instance available in %s' % regdir
print
-
+ # configuration management problem solving
+ cfgpb.solve()
+ if cfgpb.warnings:
+ print 'Warnings:\n', '\n'.join('* '+txt for txt in cfgpb.warnings)
+ if cfgpb.errors:
+ print 'Errors:'
+ for op, cube, version in cfgpb.errors:
+ if op == 'add':
+ print '* cube', cube,
+ if version:
+ print ' version', version,
+ print 'is not installed, but required by %s' % ' '.join(cfgpb.reverse_constraints[cube])
+ else:
+ print '* cube %s version %s is installed, but version %s is required by (%s)' % (
+ cube, cfgpb.cubes[cube].version, version, ', '.join(cfgpb.reverse_constraints[cube]))
class CreateInstanceCommand(Command):
"""Create an instance from a cube. This is an unified
@@ -887,7 +986,12 @@
def run(args):
"""command line tool"""
cwcfg.load_cwctl_plugins()
- main_run(args, __doc__)
+ main_run(args, """%%prog %s [options] %s
+
+The CubicWeb swiss-knife.
+
+%s"""
+)
if __name__ == '__main__':
run(sys.argv[1:])
--- a/cwvreg.py Thu Feb 18 09:22:04 2010 +0100
+++ b/cwvreg.py Fri Feb 26 17:39:33 2010 +0100
@@ -8,7 +8,7 @@
__docformat__ = "restructuredtext en"
_ = unicode
-from logilab.common.decorators import cached, clear_cache, monkeypatch
+from logilab.common.decorators import cached, clear_cache
from logilab.common.deprecation import deprecated
from logilab.common.modutils import cleanup_sys_modules
--- a/dbapi.py Thu Feb 18 09:22:04 2010 +0100
+++ b/dbapi.py Fri Feb 26 17:39:33 2010 +0100
@@ -27,15 +27,10 @@
def _fake_property_value(self, name):
try:
- return super(dbapi.DBAPIRequest, self).property_value(name)
+ return super(DBAPIRequest, self).property_value(name)
except KeyError:
return ''
-def _fix_cls_attrs(reg, appobject):
- appobject.vreg = reg.vreg
- appobject.schema = reg.schema
- appobject.config = reg.config
-
def multiple_connections_fix():
"""some monkey patching necessary when an application has to deal with
several connections to different repositories. It tries to hide buggy class
@@ -43,21 +38,6 @@
registries.
"""
defaultcls = cwvreg.VRegistry.REGISTRY_FACTORY[None]
- orig_select_best = defaultcls.orig_select_best = defaultcls._select_best
- @monkeypatch(defaultcls)
- def _select_best(self, appobjects, *args, **kwargs):
- """return an instance of the most specific object according
- to parameters
-
- raise NoSelectableObject if no object apply
- """
- for appobjectcls in appobjects:
- _fix_cls_attrs(self, appobjectcls)
- selected = orig_select_best(self, appobjects, *args, **kwargs)
- # redo the same thing on the instance so it won't use equivalent class
- # attributes (which may change)
- _fix_cls_attrs(self, selected)
- return selected
etypescls = cwvreg.VRegistry.REGISTRY_FACTORY['etypes']
orig_etype_class = etypescls.orig_etype_class = etypescls.etype_class
@@ -74,8 +54,6 @@
return usercls
def multiple_connections_unfix():
- defaultcls = cwvreg.VRegistry.REGISTRY_FACTORY[None]
- defaultcls.select_best = defaultcls.orig_select_best
etypescls = cwvreg.VRegistry.REGISTRY_FACTORY['etypes']
etypescls.etype_class = etypescls.orig_etype_class
@@ -222,7 +200,7 @@
except KeyError:
# this occurs usually during test execution
self._ = self.__ = unicode
- self.pgettext = lambda x,y: y
+ self.pgettext = lambda x, y: y
self.debug('request default language: %s', self.lang)
def decorate_rset(self, rset):
--- a/debian/changelog Thu Feb 18 09:22:04 2010 +0100
+++ b/debian/changelog Fri Feb 26 17:39:33 2010 +0100
@@ -1,3 +1,9 @@
+cubicweb (3.6.1-1) unstable; urgency=low
+
+ * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr> Fri, 26 Feb 2010 14:14:01 +0100
+
cubicweb (3.6.0-1) unstable; urgency=low
* new upstream release
--- a/debian/control Thu Feb 18 09:22:04 2010 +0100
+++ b/debian/control Fri Feb 26 17:39:33 2010 +0100
@@ -77,7 +77,7 @@
Package: cubicweb-common
Architecture: all
XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.48.1), python-yams (>= 0.27.0), python-rql (>= 0.24.0), python-lxml
+Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.48.1), python-yams (>= 0.28.0), python-rql (>= 0.24.0), python-lxml
Recommends: python-simpletal (>= 4.0), python-crypto
Conflicts: cubicweb-core
Replaces: cubicweb-core
--- a/devtools/dataimport.py Thu Feb 18 09:22:04 2010 +0100
+++ b/devtools/dataimport.py Fri Feb 26 17:39:33 2010 +0100
@@ -152,7 +152,7 @@
def confirm(question):
"""A confirm function that asks for yes/no/abort and exits on abort."""
- answer = shellutils.ASK.ask(question, ('Y','n','abort'), 'Y')
+ answer = shellutils.ASK.ask(question, ('Y', 'n', 'abort'), 'Y')
if answer == 'abort':
sys.exit(1)
return answer == 'Y'
@@ -275,11 +275,12 @@
def check_doubles(buckets):
"""Extract the keys that have more than one item in their bucket."""
- return [(key, len(value)) for key,value in buckets.items() if len(value) > 1]
+ return [(k, len(v)) for k, v in buckets.items() if len(v) > 1]
def check_doubles_not_none(buckets):
"""Extract the keys that have more than one item in their bucket."""
- return [(key, len(value)) for key,value in buckets.items() if key is not None and len(value) > 1]
+ return [(k, len(v)) for k, v in buckets.items()
+ if k is not None and len(v) > 1]
# object stores #################################################################
@@ -430,12 +431,14 @@
return entity
def _put(self, type, item):
- query = ('INSERT %s X: ' % type) + ', '.join(['X %s %%(%s)s' % (key,key) for key in item])
+ query = ('INSERT %s X: ' % type) + ', '.join('X %s %%(%s)s' % (k, k)
+ for k in item)
return self.rql(query, item)[0][0]
def relate(self, eid_from, rtype, eid_to):
# if reverse relation is found, eids are exchanged
- eid_from, rtype, eid_to = super(RQLObjectStore, self).relate(eid_from, rtype, eid_to)
+ eid_from, rtype, eid_to = super(RQLObjectStore, self).relate(
+ eid_from, rtype, eid_to)
self.rql('SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % rtype,
{'x': int(eid_from), 'y': int(eid_to)}, ('x', 'y'))
@@ -513,7 +516,7 @@
% (len(self.store.eids), len(self.store.types),
len(self.store.relations), nberrors))
if self.errors:
- if self.askerror==2 or (self.askerror and confirm('Display errors ?')):
+ if self.askerror == 2 or (self.askerror and confirm('Display errors ?')):
from pprint import pformat
for errkey, error in self.errors.items():
self.tell("\n%s (%s): %d\n" % (error[0], errkey, len(error[1])))
--- a/devtools/devctl.py Thu Feb 18 09:22:04 2010 +0100
+++ b/devtools/devctl.py Fri Feb 26 17:39:33 2010 +0100
@@ -8,32 +8,23 @@
"""
__docformat__ = "restructuredtext en"
+# *ctl module should limit the number of import to be imported as quickly as
+# possible (for cubicweb-ctl reactivity, necessary for instance for usable bash
+# completion). So import locally in command helpers.
import sys
from datetime import datetime
-from os import mkdir, chdir, getcwd
+from os import mkdir, chdir
from os.path import join, exists, abspath, basename, normpath, split, isdir
-from copy import deepcopy
from warnings import warn
-from tempfile import NamedTemporaryFile
-from subprocess import Popen
from logilab.common import STD_BLACKLIST
-from logilab.common.modutils import get_module_files
-from logilab.common.textutils import splitstrip
-from logilab.common.shellutils import ASK
from logilab.common.clcommands import register_commands, pop_arg
-from yams import schema2dot
-
from cubicweb.__pkginfo__ import version as cubicwebversion
from cubicweb import CW_SOFTWARE_ROOT as BASEDIR, BadCommandUsage
from cubicweb.toolsutils import Command, copy_skeleton, underline_title
-from cubicweb.schema import CONSTRAINTS
from cubicweb.web.webconfig import WebConfiguration
from cubicweb.server.serverconfig import ServerConfiguration
-from yams import BASE_TYPES
-from cubicweb.schema import (META_RTYPES, SCHEMA_TYPES, SYSTEM_RTYPES,
- WORKFLOW_TYPES, INTERNAL_TYPES)
class DevConfiguration(ServerConfiguration, WebConfiguration):
@@ -110,13 +101,14 @@
# set_schema triggers objects registrations
vreg.set_schema(schema)
w(DEFAULT_POT_HEAD)
- _generate_schema_pot(w, vreg, schema, libconfig=libconfig, cube=cube)
+ _generate_schema_pot(w, vreg, schema, libconfig=libconfig)
-def _generate_schema_pot(w, vreg, schema, libconfig=None, cube=None):
+def _generate_schema_pot(w, vreg, schema, libconfig=None):
+ from copy import deepcopy
from cubicweb.i18n import add_msg
from cubicweb.web import uicfg
- from cubicweb.schema import META_RTYPES, SYSTEM_RTYPES
+ from cubicweb.schema import META_RTYPES, SYSTEM_RTYPES, CONSTRAINTS
no_context_rtypes = META_RTYPES | SYSTEM_RTYPES
w('# schema pot file, generated on %s\n' % datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
w('# \n')
@@ -221,7 +213,7 @@
add_msg(w, objid)
-def _iter_vreg_objids(vreg, done, prefix=None):
+def _iter_vreg_objids(vreg, done):
for reg, objdict in vreg.items():
for objects in objdict.values():
for obj in objects:
@@ -238,21 +230,6 @@
break
-def defined_in_library(etype, rtype, tetype, role):
- """return true if the given relation definition exists in cubicweb's library
- """
- if libschema is None:
- return False
- if role == 'subject':
- subjtype, objtype = etype, tetype
- else:
- subjtype, objtype = tetype, etype
- try:
- return libschema.rschema(rtype).has_rdef(subjtype, objtype)
- except KeyError:
- return False
-
-
LANGS = ('en', 'fr', 'es')
I18NDIR = join(BASEDIR, 'i18n')
DEFAULT_POT_HEAD = r'''msgid ""
@@ -287,6 +264,7 @@
import yams
from logilab.common.fileutils import ensure_fs_mode
from logilab.common.shellutils import globfind, find, rm
+ from logilab.common.modutils import get_module_files
from cubicweb.i18n import extract_from_tal, execute
tempdir = tempfile.mkdtemp()
potfiles = [join(I18NDIR, 'static-messages.pot')]
@@ -501,6 +479,7 @@
def run(self, args):
+ from logilab.common.shellutils import ASK
if len(args) != 1:
raise BadCommandUsage("exactly one argument (cube name) is expected")
cubename, = args
@@ -558,6 +537,8 @@
copy_skeleton(skeldir, cubedir, context)
def _ask_for_dependancies(self):
+ from logilab.common.shellutils import ASK
+ from logilab.common.textutils import splitstrip
includes = []
for stdtype in ServerConfiguration.available_cubes():
answer = ASK.ask("Depends on cube %s? " % stdtype,
@@ -575,17 +556,16 @@
class ExamineLogCommand(Command):
"""Examine a rql log file.
- usage: python exlog.py < rql.log
-
will print out the following table
total execution time || number of occurences || rql query
sorted by descending total execution time
- chances are the lines at the top are the ones that will bring
- the higher benefit after optimisation. Start there.
+ chances are the lines at the top are the ones that will bring the higher
+ benefit after optimisation. Start there.
"""
+ arguments = '< rql.log'
name = 'exlog'
options = (
)
@@ -662,7 +642,12 @@
]
def run(self, args):
+ from subprocess import Popen
+ from tempfile import NamedTemporaryFile
from logilab.common.textutils import splitstrip
+ from yams import schema2dot, BASE_TYPES
+ from cubicweb.schema import (META_RTYPES, SCHEMA_TYPES, SYSTEM_RTYPES,
+ WORKFLOW_TYPES, INTERNAL_TYPES)
cubes = splitstrip(pop_arg(args, 1))
dev_conf = DevConfiguration(*cubes)
schema = dev_conf.load_schema()
--- a/devtools/fill.py Thu Feb 18 09:22:04 2010 +0100
+++ b/devtools/fill.py Fri Feb 26 17:39:33 2010 +0100
@@ -142,7 +142,7 @@
def generate_date(self, entity, attrname, index):
"""generates a random date (format is 'yyyy-mm-dd')"""
- base = date(randint(2000, 2004), randint(1, 12), randint(1, 28))
+ base = date(randint(2000, 2010), 1, 1) + timedelta(randint(1, 365))
return self._constrained_generate(entity, attrname, base, timedelta(days=1), index)
def generate_time(self, entity, attrname, index):
--- a/devtools/realdbtest.py Thu Feb 18 09:22:04 2010 +0100
+++ b/devtools/realdbtest.py Fri Feb 26 17:39:33 2010 +0100
@@ -1,4 +1,3 @@
-import logging
from cubicweb import toolsutils
from cubicweb.devtools import DEFAULT_SOURCES, BaseApptestConfiguration
--- a/devtools/repotest.py Thu Feb 18 09:22:04 2010 +0100
+++ b/devtools/repotest.py Fri Feb 26 17:39:33 2010 +0100
@@ -191,15 +191,14 @@
rqlst.solutions = remove_unused_solutions(rqlst, rqlst.solutions, {}, self.repo.schema)[0]
return rqlst
- def _user_session(self, groups=('guests',), ueid=None):
+ def user_groups_session(self, *groups):
+ """lightweight session using the current user with hi-jacked groups"""
# use self.session.user.eid to get correct owned_by relation, unless explicit eid
- if ueid is None:
- ueid = self.session.user.eid
- u = self.repo._build_user(self.session, ueid)
+ u = self.repo._build_user(self.session, self.session.user.eid)
u._groups = set(groups)
s = Session(u, self.repo)
s._threaddata.pool = self.pool
- return u, s
+ return s
def execute(self, rql, args=None, eid_key=None, build_descr=True):
return self.o.execute(self.session, rql, args, eid_key, build_descr)
--- a/devtools/test/unittest_dbfill.py Thu Feb 18 09:22:04 2010 +0100
+++ b/devtools/test/unittest_dbfill.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,6 +9,7 @@
import os.path as osp
import re
+import datetime
from logilab.common.testlib import TestCase, unittest_main
@@ -41,7 +42,6 @@
def _available_Person_firstname(self, etype, attrname):
return [f.strip() for f in file(osp.join(DATADIR, 'firstnames.txt'))]
-
def setUp(self):
config = ApptestConfiguration('data')
config.bootstrap_cubes()
@@ -52,17 +52,6 @@
self.bug_valgen = MyValueGenerator(e_schema)
self.config = config
- def _check_date(self, date):
- """checks that 'date' is well-formed"""
- year = date.year
- month = date.month
- day = date.day
- self.failUnless(day in range(1, 29), '%s not in [0;28]' % day)
- self.failUnless(month in range(1, 13), '%s not in [1;12]' % month)
- self.failUnless(year in range(2000, 2005),
- '%s not in [2000;2004]' % year)
-
-
def test_string(self):
"""test string generation"""
surname = self.person_valgen.generate_attribute_value({}, 'surname', 12)
@@ -92,15 +81,14 @@
def test_date(self):
"""test date generation"""
# Test for random index
- for index in range(5):
+ for index in range(10):
date_value = self.person_valgen.generate_attribute_value({}, 'birthday', index)
- self._check_date(date_value)
+ self.failUnless(isinstance(date_value, datetime.date))
def test_phone(self):
"""tests make_tel utility"""
self.assertEquals(make_tel(22030405), '22 03 04 05')
-
def test_customized_generation(self):
self.assertEquals(self.bug_valgen.generate_attribute_value({}, 'severity', 12),
u'dangerous')
@@ -110,7 +98,6 @@
u'yo')
-
class ConstraintInsertionTC(TestCase):
def test_writeme(self):
--- a/devtools/testlib.py Thu Feb 18 09:22:04 2010 +0100
+++ b/devtools/testlib.py Fri Feb 26 17:39:33 2010 +0100
@@ -66,24 +66,7 @@
return set(schema.entities()) - protected_entities
-def get_versions(self, checkversions=False):
- """return the a dictionary containing cubes used by this instance
- as key with their version as value, including cubicweb version. This is a
- public method, not requiring a session id.
-
- replace Repository.get_versions by this method if you don't want versions
- checking
- """
- vcconf = {'cubicweb': self.config.cubicweb_version()}
- self.config.bootstrap_cubes()
- for pk in self.config.cubes():
- version = self.config.cube_version(pk)
- vcconf[pk] = version
- self.config._cubes = None
- return vcconf
-
-
-def refresh_repo(repo):
+def refresh_repo(repo, resetschema=False, resetvreg=False):
devtools.reset_test_database(repo.config)
for pool in repo.pools:
pool.reconnect()
@@ -92,6 +75,8 @@
repo.querier._rql_cache = {}
for source in repo.sources:
source.reset_caches()
+ if resetschema:
+ repo.set_schema(repo.config.load_schema(), resetvreg=resetvreg)
# email handling, to test emails sent by an application ########################
@@ -160,6 +145,7 @@
"""
appid = 'data'
configcls = devtools.ApptestConfiguration
+ reset_schema = reset_vreg = False # reset schema / vreg between tests
@classproperty
def config(cls):
@@ -230,7 +216,7 @@
@classmethod
def _refresh_repo(cls):
- refresh_repo(cls.repo)
+ refresh_repo(cls.repo, cls.reset_schema, cls.reset_vreg)
# global resources accessors ###############################################
@@ -488,7 +474,7 @@
Redirect exception
"""
try:
- res = callback(req)
+ callback(req)
except Redirect, ex:
try:
path, params = ex.location.split('?', 1)
--- a/embedded/README Thu Feb 18 09:22:04 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-This directory contains extra libraries which are needed
-to make cubicweb work.
-
-The mx.DateTime python implementation is directly taken from
-the mx.DateTime distribution. The only modification is the
-strptime function which has been mocked using the standard
-``datetime`` module. (as provided by the python2.5's stdlib)
--- a/entities/test/unittest_wfobjs.py Thu Feb 18 09:22:04 2010 +0100
+++ b/entities/test/unittest_wfobjs.py Fri Feb 26 17:39:33 2010 +0100
@@ -98,7 +98,7 @@
trs = list(user.possible_transitions())
self.assertEquals(len(trs), 1)
self.assertEquals(trs[0].name, u'deactivate')
- self.assertEquals(trs[0].destination().name, u'deactivated')
+ self.assertEquals(trs[0].destination(None).name, u'deactivated')
# test a std user get no possible transition
cnx = self.login('member')
# fetch the entity using the new session
@@ -141,6 +141,27 @@
trinfo = self._test_manager_deactivate(user)
self.assertEquals(trinfo.transition.name, 'deactivate')
+ def test_goback_transition(self):
+ wf = self.session.user.current_workflow
+ asleep = wf.add_state('asleep')
+ wf.add_transition('rest', (wf.state_by_name('activated'), wf.state_by_name('deactivated')),
+ asleep)
+ wf.add_transition('wake up', asleep)
+ user = self.create_user('stduser')
+ user.fire_transition('rest')
+ self.commit()
+ user.fire_transition('wake up')
+ self.commit()
+ self.assertEquals(user.state, 'activated')
+ user.fire_transition('deactivate')
+ self.commit()
+ user.fire_transition('rest')
+ self.commit()
+ user.fire_transition('wake up')
+ self.commit()
+ user.clear_all_caches()
+ self.assertEquals(user.state, 'deactivated')
+
# XXX test managers can change state without matching transition
def _test_stduser_deactivate(self):
@@ -207,7 +228,7 @@
state3 = mwf.add_state(u'state3')
swftr1 = mwf.add_wftransition(u'swftr1', swf, state1,
[(swfstate2, state2), (swfstate3, state3)])
- self.assertEquals(swftr1.destination().eid, swfstate1.eid)
+ self.assertEquals(swftr1.destination(None).eid, swfstate1.eid)
# workflows built, begin test
self.group = self.request().create_entity('CWGroup', name=u'grp1')
self.commit()
--- a/entities/wfobjs.py Thu Feb 18 09:22:04 2010 +0100
+++ b/entities/wfobjs.py Fri Feb 26 17:39:33 2010 +0100
@@ -123,7 +123,7 @@
self._cw.execute('SET S allowed_transition T '
'WHERE S eid %(s)s, T eid %(t)s',
{'s': state, 't': tr.eid}, ('s', 't'))
- tr.set_transition_permissions(requiredgroups, conditions, reset=False)
+ tr.set_permissions(requiredgroups, conditions, reset=False)
return tr
def add_transition(self, name, fromstates, tostate=None,
@@ -161,9 +161,9 @@
execute = self._cw.unsafe_execute
execute('SET X in_state S WHERE S eid %(s)s', {'s': todelstate.eid}, 's')
execute('SET X from_state NS WHERE X to_state OS, OS eid %(os)s, NS eid %(ns)s',
- {'os': todelstate.eid, 'ns': newstate.eid}, 's')
+ {'os': todelstate.eid, 'ns': replacement.eid}, 's')
execute('SET X to_state NS WHERE X to_state OS, OS eid %(os)s, NS eid %(ns)s',
- {'os': todelstate.eid, 'ns': newstate.eid}, 's')
+ {'os': todelstate.eid, 'ns': replacement.eid}, 's')
todelstate.delete()
@@ -219,10 +219,9 @@
"""
if self.transition_of:
return self.transition_of[0].rest_path(), {}
- return super(Transition, self).after_deletion_path()
+ return super(BaseTransition, self).after_deletion_path()
- def set_transition_permissions(self, requiredgroups=(), conditions=(),
- reset=True):
+ def set_permissions(self, requiredgroups=(), conditions=(), reset=True):
"""set or add (if `reset` is False) groups and conditions for this
transition
"""
@@ -251,13 +250,30 @@
'T condition X WHERE T eid %(x)s',kwargs, 'x')
# XXX clear caches?
+ @deprecated('[3.6.1] use set_permission')
+ def set_transition_permissions(self, requiredgroups=(), conditions=(),
+ reset=True):
+ return self.set_permissions(requiredgroups, conditions, reset)
+
class Transition(BaseTransition):
"""customized class for Transition entities"""
__regid__ = 'Transition'
- def destination(self):
- return self.destination_state[0]
+ def destination(self, entity):
+ try:
+ return self.destination_state[0]
+ except IndexError:
+ return entity.latest_trinfo().previous_state
+
+ def potential_destinations(self):
+ try:
+ yield self.destination_state[0]
+ except IndexError:
+ for incomingstate in self.reverse_allowed_transition:
+ for tr in incomingstate.reverse_destination_state:
+ for previousstate in tr.reverse_allowed_transition:
+ yield previousstate
def parent(self):
return self.workflow
@@ -271,9 +287,12 @@
def subwf(self):
return self.subworkflow[0]
- def destination(self):
+ def destination(self, entity):
return self.subwf.initial
+ def potential_destinations(self):
+ yield self.subwf.initial
+
def add_exit_point(self, fromstate, tostate):
if hasattr(fromstate, 'eid'):
fromstate = fromstate.eid
@@ -311,7 +330,7 @@
return result
def clear_all_caches(self):
- super(WorkflowableMixIn, self).clear_all_caches()
+ super(WorkflowTransition, self).clear_all_caches()
clear_cache(self, 'exit_points')
--- a/entity.py Thu Feb 18 09:22:04 2010 +0100
+++ b/entity.py Fri Feb 26 17:39:33 2010 +0100
@@ -12,10 +12,8 @@
from logilab.common import interface
from logilab.common.compat import all
from logilab.common.decorators import cached
-from logilab.common.deprecation import deprecated
from logilab.mtconverter import TransformData, TransformError, xml_escape
-from rql import parse
from rql.utils import rqlvar_maker
from cubicweb import Unauthorized, typed_eid
@@ -638,8 +636,17 @@
rql = '%s WHERE %s' % (rql.split(' ORDERBY ', 1)[0],
rql.split(' WHERE ', 1)[1])
elif not ' ORDERBY ' in rql:
- args = tuple(rql.split(' WHERE ', 1))
- rql = '%s ORDERBY Z DESC WHERE X modification_date Z, %s' % args
+ args = rql.split(' WHERE ', 1)
+ # if modification_date already retreived, we should use it instead
+ # of adding another variable for sort. This should be be problematic
+ # but it's actually with sqlserver, see ticket #694445
+ if 'X modification_date ' in args[1]:
+ var = args[1].split('X modification_date ', 1)[1].split(',', 1)[0]
+ args.insert(1, var.strip())
+ rql = '%s ORDERBY %s DESC WHERE %s' % tuple(args)
+ else:
+ rql = '%s ORDERBY Z DESC WHERE X modification_date Z, %s' % \
+ tuple(args)
return rql
# generic vocabulary methods ##############################################
--- a/etwist/service.py Thu Feb 18 09:22:04 2010 +0100
+++ b/etwist/service.py Fri Feb 26 17:39:33 2010 +0100
@@ -1,6 +1,4 @@
import os
-import os.path as osp
-import sys
import win32serviceutil
import win32service
--- a/etwist/twconfig.py Thu Feb 18 09:22:04 2010 +0100
+++ b/etwist/twconfig.py Fri Feb 26 17:39:33 2010 +0100
@@ -16,7 +16,9 @@
from os.path import join
-from cubicweb.web.webconfig import WebConfiguration, merge_options, Method
+from logilab.common.configuration import Method
+
+from cubicweb.web.webconfig import WebConfiguration, merge_options
class TwistedConfiguration(WebConfiguration):
"""web instance (in a twisted web server) client of a RQL server"""
--- a/etwist/twctl.py Thu Feb 18 09:22:04 2010 +0100
+++ b/etwist/twctl.py Fri Feb 26 17:39:33 2010 +0100
@@ -6,8 +6,6 @@
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
-import sys
-
from cubicweb.toolsutils import CommandHandler
from cubicweb.web.webctl import WebCreateHandler
--- a/ext/rest.py Thu Feb 18 09:22:04 2010 +0100
+++ b/ext/rest.py Fri Feb 26 17:39:33 2010 +0100
@@ -147,7 +147,7 @@
try:
from pygments import highlight
- from pygments.lexers import get_lexer_by_name, LEXERS
+ from pygments.lexers import get_lexer_by_name
from pygments.formatters import HtmlFormatter
except ImportError:
pygments_directive = None
--- a/ext/xhtml2fo.py Thu Feb 18 09:22:04 2010 +0100
+++ b/ext/xhtml2fo.py Fri Feb 26 17:39:33 2010 +0100
@@ -1,4 +1,4 @@
-from xml.etree.ElementTree import QName, fromstring
+from xml.etree.ElementTree import QName
from pysixt.standard.xhtml_xslfo.transformer import XHTML2FOTransformer
from pysixt.utils.xslfo.standard import cm
from pysixt.utils.xslfo import SimplePageMaster
@@ -64,7 +64,7 @@
u'bottom': self.page_bmargin*cm,
u'left' : self.page_lmargin*cm,
u'right' : self.page_rmargin*cm })
- pm.add_peripheral_region(u"end",self.hf_height)
+ pm.add_peripheral_region(u"end", self.hf_height)
dims = {}
dims[u"bottom"] = self.hf_height + 0.25
pm.set_main_region_margins(dims)
@@ -82,41 +82,39 @@
props = { u"force-page-count": u"no-force",
u"initial-page-number": u"1",
u"format": u"1", }
- self._output_properties(ps,props)
+ self._output_properties(ps, props)
sc = self.create_staticcontent(ps, u"end")
sc_bl = self.create_block(sc)
attrs = { u"hyphenate": u"false", }
- attrs[u"font-size"] = u"%.1fpt" %(self.font_size*0.7)
+ attrs[u"font-size"] = u"%.1fpt" % (self.font_size * 0.7)
attrs[u"language"] = self.lang
attrs[u"text-align"] = u"center"
- self._output_properties(sc_bl,attrs)
+ self._output_properties(sc_bl, attrs)
sc_bl.text = u"Page" + u" " # ### Should be localised!
pn = self.create_pagenumber(sc_bl)
pn.tail = u"/"
- lpn = self.create_pagenumbercitation( sc_bl,
- u"last-block-of-report-%d" % params[u"context_pos"]
- )
+ self.create_pagenumbercitation(
+ sc_bl, u"last-block-of-report-%d" % params[u"context_pos"])
-
- fl = self.create_flow(ps,u"body")
+ fl = self.create_flow(ps, u"body")
bl = self.create_block(fl)
# Sets on the highest block element the properties of the XHTML body
# element. These properties (at the least the inheritable ones) will
# be inherited by all the future FO elements.
- bodies = list(self.in_tree.getiterator(QName(XHTML_NS,u"body")))
+ bodies = list(self.in_tree.getiterator(QName(XHTML_NS, u"body")))
if len(bodies) > 0:
attrs = self._extract_properties([bodies[0]])
else:
attrs = default_styles[u"body"].copy()
- attrs[u"font-size"] = u"%.1fpt" %self.font_size
+ attrs[u"font-size"] = u"%.1fpt" % self.font_size
attrs[u"language"] = self.lang
self._output_properties(bl,attrs)
# Processes the report content
- self._copy_text(in_elt,bl)
- self._process_nodes(in_elt.getchildren(),bl)
+ self._copy_text(in_elt, bl)
+ self._process_nodes(in_elt.getchildren(), bl)
# Inserts an empty block at the end of the report in order to be able
# to compute the last page number of this report.
@@ -130,7 +128,7 @@
"""
Visit function called when starting the process of the input tree.
"""
- content = [ d for d in self.in_tree.getiterator(QName(XHTML_NS,u"div"))
+ content = [ d for d in self.in_tree.getiterator(QName(XHTML_NS, u"div"))
if d.get(u"id") == self.section ]
# Asks the process of the report elements with a specific visit
# function
--- a/goa/goactl.py Thu Feb 18 09:22:04 2010 +0100
+++ b/goa/goactl.py Fri Feb 26 17:39:33 2010 +0100
@@ -31,7 +31,6 @@
(docutils.__path__[0], 'docutils'),
(roman.__file__.replace('.pyc', '.py'), 'roman.py'),
- (join(CW_SOFTWARE_ROOT, 'embedded', 'mx'), 'mx'),
('/usr/share/fckeditor/', 'fckeditor'),
(join(CW_SOFTWARE_ROOT, 'web', 'data'), join('cubes', 'shared', 'data')),
--- a/goa/test/pytestconf.py Thu Feb 18 09:22:04 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-"""this pytestconf automatically adds the mx's python version in the PYTHONPATH
-:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
-"""
-import sys
-import os.path as osp
-
-import cubicweb
-# remove 'mx' modules imported by cubicweb
-for modname in sys.modules.keys():
- if modname.startswith('mx'):
- sys.modules.pop(modname)
-
-# this is where mx should get imported from
-mxpath = osp.abspath(osp.join(osp.dirname(cubicweb.__file__), 'embedded'))
-sys.path.insert(1, mxpath)
-
-# make sure the correct mx is imported
-import mx
-assert osp.dirname(mx.__file__) == osp.join(mxpath, 'mx'), '%s != %s' % (osp.dirname(mx.__file__), mxpath)
--- a/hooks/email.py Thu Feb 18 09:22:04 2010 +0100
+++ b/hooks/email.py Fri Feb 26 17:39:33 2010 +0100
@@ -8,7 +8,6 @@
__docformat__ = "restructuredtext en"
from cubicweb.server import hook
-from cubicweb.server.repository import ensure_card_respected
from logilab.common.compat import any
@@ -27,11 +26,6 @@
def precommit_event(self):
if self.condition():
- # we've to handle cardinaly by ourselves since we're using unsafe_execute
- # but use session.execute and not session.unsafe_execute to check we
- # can change the relation
- ensure_card_respected(self.session.execute, self.session,
- self.entity.eid, self.rtype, self.email.eid)
self.session.unsafe_execute(
'SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % self.rtype,
{'x': self.entity.eid, 'y': self.email.eid}, 'x')
--- a/hooks/notification.py Thu Feb 18 09:22:04 2010 +0100
+++ b/hooks/notification.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,7 +9,6 @@
from logilab.common.textutils import normalize_text
-from cubicweb import RegistryException
from cubicweb.selectors import implements
from cubicweb.server import hook
from cubicweb.sobjects.supervising import SupervisionMailOp
@@ -30,8 +29,8 @@
category = 'notification'
def select_view(self, vid, rset, row=0, col=0):
- return self._cw.vreg['views'].select_or_none(vid, self._cw,
- rset=rset, row=0, col=0)
+ return self._cw.vreg['views'].select_or_none(vid, self._cw, rset=rset,
+ row=row, col=col)
class StatusChangeHook(NotificationHook):
--- a/hooks/storages.py Thu Feb 18 09:22:04 2010 +0100
+++ b/hooks/storages.py Fri Feb 26 17:39:33 2010 +0100
@@ -1,7 +1,5 @@
"""hooks to handle attributes mapped to a custom storage
"""
-from os import unlink
-
from cubicweb.server.hook import Hook
from cubicweb.server.sources.storages import ETYPE_ATTR_STORAGE
@@ -16,30 +14,28 @@
""""""
__regid__ = 'bfss_add_entity'
events = ('before_add_entity', )
- #__select__ = Hook.__select__ & implements('Repository')
def __call__(self):
- for attr in ETYPE_ATTR_STORAGE.get(self.entity.__regid__, ()):
- fpath = ETYPE_ATTR_STORAGE[self.entity.__regid__][attr].entity_added(self.entity, attr)
- if fpath is not None:
- AddFileOp(filepath=fpath)
+ etype = self.entity.__regid__
+ for attr in ETYPE_ATTR_STORAGE.get(etype, ()):
+ ETYPE_ATTR_STORAGE[etype][attr].entity_added(self.entity, attr)
class PreUpdateEntityHook(BFSSHook):
""""""
__regid__ = 'bfss_update_entity'
events = ('before_update_entity', )
- #__select__ = Hook.__select__ & implements('Repository')
def __call__(self):
- for attr in ETYPE_ATTR_STORAGE.get(self.entity.__regid__, ()):
- ETYPE_ATTR_STORAGE[self.entity.__regid__][attr].entity_updated(self.entity, attr)
+ etype = self.entity.__regid__
+ for attr in ETYPE_ATTR_STORAGE.get(etype, ()):
+ ETYPE_ATTR_STORAGE[etype][attr].entity_updated(self.entity, attr)
class PreDeleteEntityHook(BFSSHook):
""""""
__regid__ = 'bfss_delete_entity'
events = ('before_delete_entity', )
- #__select__ = Hook.__select__ & implements('Repository')
def __call__(self):
- for attr in ETYPE_ATTR_STORAGE.get(self.entity.__regid__, ()):
- ETYPE_ATTR_STORAGE[self.entity.__regid__][attr].entity_deleted(self.entity, attr)
+ etype = self.entity.__regid__
+ for attr in ETYPE_ATTR_STORAGE.get(etype, ()):
+ ETYPE_ATTR_STORAGE[etype][attr].entity_deleted(self.entity, attr)
--- a/hooks/syncschema.py Thu Feb 18 09:22:04 2010 +0100
+++ b/hooks/syncschema.py Fri Feb 26 17:39:33 2010 +0100
@@ -18,7 +18,7 @@
from logilab.common.decorators import clear_cache
-from cubicweb import ValidationError, RepositoryError
+from cubicweb import ValidationError
from cubicweb.selectors import implements
from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES, CONSTRAINTS, display_name
from cubicweb.server import hook, schemaserial as ss
@@ -85,6 +85,7 @@
session.transaction_data.setdefault('createdattrs', []).append(
'%s.%s' % (etype, rtype))
+
def check_valid_changes(session, entity, ro_attrs=('name', 'final')):
errors = {}
# don't use getattr(entity, attr), we would get the modified value if any
@@ -418,7 +419,7 @@
rdef = self.init_rdef(composite=entity.composite)
schema = session.vreg.schema
rtype = rdef.name
- rschema = session.vreg.schema.rschema(rtype)
+ rschema = schema.rschema(rtype)
# this have to be done before permissions setting
if rschema.inlined:
# need to add a column if the relation is inlined and if this is the
@@ -440,15 +441,15 @@
if not (rschema.subjects() or
rtype in session.transaction_data.get('createdtables', ())):
try:
- rschema = session.vreg.schema.rschema(rtype)
+ rschema = schema.rschema(rtype)
tablesql = rschema2sql(rschema)
except KeyError:
# fake we add it to the schema now to get a correctly
# initialized schema but remove it before doing anything
# more dangerous...
- rschema = session.vreg.schema.add_relation_type(rdef)
+ rschema = schema.add_relation_type(rdef)
tablesql = rschema2sql(rschema)
- session.vreg.schema.del_relation_type(rtype)
+ schema.del_relation_type(rtype)
# create the necessary table
for sql in tablesql.split(';'):
if sql.strip():
@@ -485,6 +486,10 @@
sql = adbh.sql_set_null_allowed(table, column, coltype,
self.values['cardinality'][0] != '1')
self.session.system_sql(sql)
+ if 'fulltextindexed' in self.values:
+ UpdateFTIndexOp(self.session)
+ self.session.transaction_data.setdefault('fti_update_etypes',
+ set()).add(etype)
class SourceDbCWConstraintAdd(hook.Operation):
@@ -690,7 +695,7 @@
erschema = self.session.vreg.schema.schema_by_eid(self.eid)
except KeyError:
# duh, schema not found, log error and skip operation
- self.error('no schema for %s', self.eid)
+ self.warning('no schema for %s', self.eid)
return
perms = list(erschema.action_permissions(self.action))
if hasattr(self, 'group_eid'):
@@ -717,7 +722,7 @@
erschema = self.session.vreg.schema.schema_by_eid(self.eid)
except KeyError:
# duh, schema not found, log error and skip operation
- self.error('no schema for %s', self.eid)
+ self.warning('no schema for %s', self.eid)
return
if isinstance(erschema, RelationSchema): # XXX 3.6 migration
return
@@ -935,18 +940,6 @@
SourceDbCWRTypeUpdate(self._cw, rschema=rschema, values=newvalues,
entity=entity)
-def check_valid_changes(session, entity, ro_attrs=('name', 'final')):
- errors = {}
- # don't use getattr(entity, attr), we would get the modified value if any
- for attr in ro_attrs:
- if attr in entity.edited_attributes:
- origval, newval = hook.entity_oldnewvalue(entity, attr)
- if newval != origval:
- errors[attr] = session._("can't change the %s attribute") % \
- display_name(session, attr)
- if errors:
- raise ValidationError(entity.eid, errors)
-
class AfterDelRelationTypeHook(SyncSchemaHook):
"""before deleting a CWAttribute or CWRelation entity:
@@ -1135,6 +1128,41 @@
expr=expr)
+
+class UpdateFTIndexOp(hook.SingleLastOperation):
+ """operation to update full text indexation of entity whose schema change
+
+ We wait after the commit to as the schema in memory is only updated after the commit.
+ """
+
+ def postcommit_event(self):
+ session = self.session
+ source = session.repo.system_source
+ to_reindex = session.transaction_data.get('fti_update_etypes', ())
+ self.info('%i etypes need full text indexed reindexation',
+ len(to_reindex))
+ schema = self.session.repo.vreg.schema
+ for etype in to_reindex:
+ rset = session.execute('Any X WHERE X is %s' % etype)
+ self.info('Reindexing full text index for %i entity of type %s',
+ len(rset), etype)
+ still_fti = list(schema[etype].indexable_attributes())
+ for entity in rset.entities():
+ try:
+ source.fti_unindex_entity(session, entity.eid)
+ for container in entity.fti_containers():
+ if still_fti or container is not entity:
+ session.repo.index_entity(session, container)
+ except Exception:
+ self.critical('Error while updating Full Text Index for'
+ ' entity %s', entity.eid, exc_info=True)
+ if len(to_reindex):
+ # Transaction have already been committed
+ session.pool.commit()
+
+
+
+
# specializes synchronization hooks ############################################
--- a/hooks/test/unittest_hooks.py Thu Feb 18 09:22:04 2010 +0100
+++ b/hooks/test/unittest_hooks.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,22 +9,8 @@
from datetime import datetime
-from cubicweb import (ConnectionError, ValidationError, AuthenticationError,
- BadConnectionId)
-from cubicweb.devtools.testlib import CubicWebTC, get_versions
-
-from cubicweb.server.sqlutils import SQL_PREFIX
-from cubicweb.server.repository import Repository
-
-orig_get_versions = Repository.get_versions
-
-def setup_module(*args):
- Repository.get_versions = get_versions
-
-def teardown_module(*args):
- Repository.get_versions = orig_get_versions
-
-
+from cubicweb import ValidationError, AuthenticationError, BadConnectionId
+from cubicweb.devtools.testlib import CubicWebTC
class CoreHooksTC(CubicWebTC):
@@ -270,238 +256,5 @@
self.assertEquals(ex.errors, {'login': 'the value "admin" is already used, use another one'})
-class SchemaModificationHooksTC(CubicWebTC):
-
- @classmethod
- def init_config(cls, config):
- super(SchemaModificationHooksTC, cls).init_config(config)
- config._cubes = None
- cls.repo.fill_schema()
-
- def index_exists(self, etype, attr, unique=False):
- self.session.set_pool()
- dbhelper = self.session.pool.source('system').dbhelper
- sqlcursor = self.session.pool['system']
- return dbhelper.index_exists(sqlcursor, SQL_PREFIX + etype, SQL_PREFIX + attr, unique=unique)
-
- def _set_perms(self, eid):
- self.execute('SET X read_permission G WHERE X eid %(x)s, G is CWGroup',
- {'x': eid}, 'x')
- self.execute('SET X add_permission G WHERE X eid %(x)s, G is CWGroup, G name "managers"',
- {'x': eid}, 'x')
- self.execute('SET X delete_permission G WHERE X eid %(x)s, G is CWGroup, G name "owners"',
- {'x': eid}, 'x')
-
- def test_base(self):
- schema = self.repo.schema
- self.session.set_pool()
- dbhelper = self.session.pool.source('system').dbhelper
- sqlcursor = self.session.pool['system']
- self.failIf(schema.has_entity('Societe2'))
- self.failIf(schema.has_entity('concerne2'))
- # schema should be update on insertion (after commit)
- eeid = self.execute('INSERT CWEType X: X name "Societe2", X description "", X final FALSE')[0][0]
- self._set_perms(eeid)
- self.execute('INSERT CWRType X: X name "concerne2", X description "", X final FALSE, X symmetric FALSE')
- self.failIf(schema.has_entity('Societe2'))
- self.failIf(schema.has_entity('concerne2'))
- # have to commit before adding definition relations
- self.commit()
- self.failUnless(schema.has_entity('Societe2'))
- self.failUnless(schema.has_relation('concerne2'))
- attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval "noname", '
- ' X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
- 'WHERE RT name "name", E name "Societe2", F name "String"')[0][0]
- self._set_perms(attreid)
- concerne2_rdef_eid = self.execute(
- 'INSERT CWRelation X: X cardinality "**", X relation_type RT, X from_entity E, X to_entity E '
- 'WHERE RT name "concerne2", E name "Societe2"')[0][0]
- self._set_perms(concerne2_rdef_eid)
- self.failIf('name' in schema['Societe2'].subject_relations())
- self.failIf('concerne2' in schema['Societe2'].subject_relations())
- self.failIf(self.index_exists('Societe2', 'name'))
- self.commit()
- self.failUnless('name' in schema['Societe2'].subject_relations())
- self.failUnless('concerne2' in schema['Societe2'].subject_relations())
- self.failUnless(self.index_exists('Societe2', 'name'))
- # now we should be able to insert and query Societe2
- s2eid = self.execute('INSERT Societe2 X: X name "logilab"')[0][0]
- self.execute('Societe2 X WHERE X name "logilab"')
- self.execute('SET X concerne2 X WHERE X name "logilab"')
- rset = self.execute('Any X WHERE X concerne2 Y')
- self.assertEquals(rset.rows, [[s2eid]])
- # check that when a relation definition is deleted, existing relations are deleted
- rdefeid = self.execute('INSERT CWRelation X: X cardinality "**", X relation_type RT, '
- ' X from_entity E, X to_entity E '
- 'WHERE RT name "concerne2", E name "CWUser"')[0][0]
- self._set_perms(rdefeid)
- self.commit()
- self.execute('DELETE CWRelation X WHERE X eid %(x)s', {'x': concerne2_rdef_eid}, 'x')
- self.commit()
- self.failUnless('concerne2' in schema['CWUser'].subject_relations())
- self.failIf('concerne2' in schema['Societe2'].subject_relations())
- self.failIf(self.execute('Any X WHERE X concerne2 Y'))
- # schema should be cleaned on delete (after commit)
- self.execute('DELETE CWEType X WHERE X name "Societe2"')
- self.execute('DELETE CWRType X WHERE X name "concerne2"')
- self.failUnless(self.index_exists('Societe2', 'name'))
- self.failUnless(schema.has_entity('Societe2'))
- self.failUnless(schema.has_relation('concerne2'))
- self.commit()
- self.failIf(self.index_exists('Societe2', 'name'))
- self.failIf(schema.has_entity('Societe2'))
- self.failIf(schema.has_entity('concerne2'))
- self.failIf('concerne2' in schema['CWUser'].subject_relations())
-
- def test_is_instance_of_insertions(self):
- seid = self.execute('INSERT Transition T: T name "subdiv"')[0][0]
- is_etypes = [etype for etype, in self.execute('Any ETN WHERE X eid %s, X is ET, ET name ETN' % seid)]
- self.assertEquals(is_etypes, ['Transition'])
- instanceof_etypes = [etype for etype, in self.execute('Any ETN WHERE X eid %s, X is_instance_of ET, ET name ETN' % seid)]
- self.assertEquals(sorted(instanceof_etypes), ['BaseTransition', 'Transition'])
- snames = [name for name, in self.execute('Any N WHERE S is BaseTransition, S name N')]
- self.failIf('subdiv' in snames)
- snames = [name for name, in self.execute('Any N WHERE S is_instance_of BaseTransition, S name N')]
- self.failUnless('subdiv' in snames)
-
-
- def test_perms_synchronization_1(self):
- schema = self.repo.schema
- self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', 'users')))
- self.failUnless(self.execute('Any X, Y WHERE X is CWEType, X name "CWUser", Y is CWGroup, Y name "users"')[0])
- self.execute('DELETE X read_permission Y WHERE X is CWEType, X name "CWUser", Y name "users"')
- self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', 'users', )))
- self.commit()
- self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', )))
- self.execute('SET X read_permission Y WHERE X is CWEType, X name "CWUser", Y name "users"')
- self.commit()
- self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', 'users',)))
-
- def test_perms_synchronization_2(self):
- schema = self.repo.schema['in_group'].rdefs[('CWUser', 'CWGroup')]
- self.assertEquals(schema.get_groups('read'), set(('managers', 'users', 'guests')))
- self.execute('DELETE X read_permission Y WHERE X relation_type RT, RT name "in_group", Y name "guests"')
- self.assertEquals(schema.get_groups('read'), set(('managers', 'users', 'guests')))
- self.commit()
- self.assertEquals(schema.get_groups('read'), set(('managers', 'users')))
- self.execute('SET X read_permission Y WHERE X relation_type RT, RT name "in_group", Y name "guests"')
- self.assertEquals(schema.get_groups('read'), set(('managers', 'users')))
- self.commit()
- self.assertEquals(schema.get_groups('read'), set(('managers', 'users', 'guests')))
-
- def test_nonregr_user_edit_itself(self):
- ueid = self.session.user.eid
- groupeids = [eid for eid, in self.execute('CWGroup G WHERE G name in ("managers", "users")')]
- self.execute('DELETE X in_group Y WHERE X eid %s' % ueid)
- self.execute('SET X surname "toto" WHERE X eid %s' % ueid)
- self.execute('SET X in_group Y WHERE X eid %s, Y name "managers"' % ueid)
- self.commit()
- eeid = self.execute('Any X WHERE X is CWEType, X name "CWEType"')[0][0]
- self.execute('DELETE X read_permission Y WHERE X eid %s' % eeid)
- self.execute('SET X final FALSE WHERE X eid %s' % eeid)
- self.execute('SET X read_permission Y WHERE X eid %s, Y eid in (%s, %s)'
- % (eeid, groupeids[0], groupeids[1]))
- self.commit()
- self.execute('Any X WHERE X is CWEType, X name "CWEType"')
-
- # schema modification hooks tests #########################################
-
- def test_uninline_relation(self):
- self.session.set_pool()
- dbhelper = self.session.pool.source('system').dbhelper
- sqlcursor = self.session.pool['system']
- self.failUnless(self.schema['state_of'].inlined)
- try:
- self.execute('SET X inlined FALSE WHERE X name "state_of"')
- self.failUnless(self.schema['state_of'].inlined)
- self.commit()
- self.failIf(self.schema['state_of'].inlined)
- self.failIf(self.index_exists('State', 'state_of'))
- rset = self.execute('Any X, Y WHERE X state_of Y')
- self.assertEquals(len(rset), 2) # user states
- finally:
- self.execute('SET X inlined TRUE WHERE X name "state_of"')
- self.failIf(self.schema['state_of'].inlined)
- self.commit()
- self.failUnless(self.schema['state_of'].inlined)
- self.failUnless(self.index_exists('State', 'state_of'))
- rset = self.execute('Any X, Y WHERE X state_of Y')
- self.assertEquals(len(rset), 2)
-
- def test_indexed_change(self):
- self.session.set_pool()
- dbhelper = self.session.pool.source('system').dbhelper
- sqlcursor = self.session.pool['system']
- try:
- self.execute('SET X indexed FALSE WHERE X relation_type R, R name "name"')
- self.failUnless(self.schema['name'].rdef('Workflow', 'String').indexed)
- self.failUnless(self.index_exists('Workflow', 'name'))
- self.commit()
- self.failIf(self.schema['name'].rdef('Workflow', 'String').indexed)
- self.failIf(self.index_exists('Workflow', 'name'))
- finally:
- self.execute('SET X indexed TRUE WHERE X relation_type R, R name "name"')
- self.failIf(self.schema['name'].rdef('Workflow', 'String').indexed)
- self.failIf(self.index_exists('Workflow', 'name'))
- self.commit()
- self.failUnless(self.schema['name'].rdef('Workflow', 'String').indexed)
- self.failUnless(self.index_exists('Workflow', 'name'))
-
- def test_unique_change(self):
- self.session.set_pool()
- dbhelper = self.session.pool.source('system').dbhelper
- sqlcursor = self.session.pool['system']
- try:
- self.execute('INSERT CWConstraint X: X cstrtype CT, DEF constrained_by X '
- 'WHERE CT name "UniqueConstraint", DEF relation_type RT, DEF from_entity E,'
- 'RT name "name", E name "Workflow"')
- self.failIf(self.schema['Workflow'].has_unique_values('name'))
- self.failIf(self.index_exists('Workflow', 'name', unique=True))
- self.commit()
- self.failUnless(self.schema['Workflow'].has_unique_values('name'))
- self.failUnless(self.index_exists('Workflow', 'name', unique=True))
- finally:
- self.execute('DELETE DEF constrained_by X WHERE X cstrtype CT, '
- 'CT name "UniqueConstraint", DEF relation_type RT, DEF from_entity E,'
- 'RT name "name", E name "Workflow"')
- self.failUnless(self.schema['Workflow'].has_unique_values('name'))
- self.failUnless(self.index_exists('Workflow', 'name', unique=True))
- self.commit()
- self.failIf(self.schema['Workflow'].has_unique_values('name'))
- self.failIf(self.index_exists('Workflow', 'name', unique=True))
-
- def test_required_change_1(self):
- self.execute('SET DEF cardinality "?1" '
- 'WHERE DEF relation_type RT, DEF from_entity E,'
- 'RT name "title", E name "Bookmark"')
- self.commit()
- # should now be able to add bookmark without title
- self.execute('INSERT Bookmark X: X path "/view"')
- self.commit()
-
- def test_required_change_2(self):
- self.execute('SET DEF cardinality "11" '
- 'WHERE DEF relation_type RT, DEF from_entity E,'
- 'RT name "surname", E name "CWUser"')
- self.commit()
- # should not be able anymore to add cwuser without surname
- self.assertRaises(ValidationError, self.create_user, "toto")
- self.execute('SET DEF cardinality "?1" '
- 'WHERE DEF relation_type RT, DEF from_entity E,'
- 'RT name "surname", E name "CWUser"')
- self.commit()
-
-
- def test_add_attribute_to_base_class(self):
- attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval "noname", X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
- 'WHERE RT name "messageid", E name "BaseTransition", F name "String"')[0][0]
- assert self.execute('SET X read_permission Y WHERE X eid %(x)s, Y name "managers"',
- {'x': attreid}, 'x')
- self.commit()
- self.schema.rebuild_infered_relations()
- self.failUnless('Transition' in self.schema['messageid'].subjects())
- self.failUnless('WorkflowTransition' in self.schema['messageid'].subjects())
- self.execute('Any X WHERE X is_instance_of BaseTransition, X messageid "hop"')
-
if __name__ == '__main__':
unittest_main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hooks/test/unittest_syncschema.py Fri Feb 26 17:39:33 2010 +0100
@@ -0,0 +1,290 @@
+from logilab.common.testlib import TestCase, unittest_main
+
+from cubicweb import ValidationError
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.server.sqlutils import SQL_PREFIX
+
+
+SCHEMA_EIDS = {}
+class SchemaModificationHooksTC(CubicWebTC):
+ reset_schema = True
+
+ @classmethod
+ def init_config(cls, config):
+ super(SchemaModificationHooksTC, cls).init_config(config)
+ # we have to read schema from the database to get eid for schema entities
+ config._cubes = None
+ cls.repo.fill_schema()
+ # remember them so we can reread it from the fs instead of the db (too
+ # costly) between tests
+ for x in cls.repo.schema.entities():
+ SCHEMA_EIDS[x] = x.eid
+ for x in cls.repo.schema.relations():
+ SCHEMA_EIDS[x] = x.eid
+ for rdef in x.rdefs.itervalues():
+ SCHEMA_EIDS[(rdef.subject, rdef.rtype, rdef.object)] = rdef.eid
+
+ @classmethod
+ def _refresh_repo(cls):
+ super(SchemaModificationHooksTC, cls)._refresh_repo()
+ # rebuild schema eid index
+ schema = cls.repo.schema
+ for x in schema.entities():
+ x.eid = SCHEMA_EIDS[x]
+ schema._eid_index[x.eid] = x
+ for x in cls.repo.schema.relations():
+ x.eid = SCHEMA_EIDS[x]
+ schema._eid_index[x.eid] = x
+ for rdef in x.rdefs.itervalues():
+ rdef.eid = SCHEMA_EIDS[(rdef.subject, rdef.rtype, rdef.object)]
+ schema._eid_index[rdef.eid] = rdef
+
+ def index_exists(self, etype, attr, unique=False):
+ self.session.set_pool()
+ dbhelper = self.session.pool.source('system').dbhelper
+ sqlcursor = self.session.pool['system']
+ return dbhelper.index_exists(sqlcursor, SQL_PREFIX + etype, SQL_PREFIX + attr, unique=unique)
+
+ def _set_perms(self, eid):
+ self.execute('SET X read_permission G WHERE X eid %(x)s, G is CWGroup',
+ {'x': eid}, 'x')
+ self.execute('SET X add_permission G WHERE X eid %(x)s, G is CWGroup, G name "managers"',
+ {'x': eid}, 'x')
+ self.execute('SET X delete_permission G WHERE X eid %(x)s, G is CWGroup, G name "owners"',
+ {'x': eid}, 'x')
+
+ def _set_attr_perms(self, eid):
+ self.execute('SET X read_permission G WHERE X eid %(x)s, G is CWGroup',
+ {'x': eid}, 'x')
+ self.execute('SET X update_permission G WHERE X eid %(x)s, G is CWGroup, G name "managers"',
+ {'x': eid}, 'x')
+
+ def test_base(self):
+ schema = self.repo.schema
+ self.session.set_pool()
+ dbhelper = self.session.pool.source('system').dbhelper
+ sqlcursor = self.session.pool['system']
+ self.failIf(schema.has_entity('Societe2'))
+ self.failIf(schema.has_entity('concerne2'))
+ # schema should be update on insertion (after commit)
+ eeid = self.execute('INSERT CWEType X: X name "Societe2", X description "", X final FALSE')[0][0]
+ self._set_perms(eeid)
+ self.execute('INSERT CWRType X: X name "concerne2", X description "", X final FALSE, X symmetric FALSE')
+ self.failIf(schema.has_entity('Societe2'))
+ self.failIf(schema.has_entity('concerne2'))
+ # have to commit before adding definition relations
+ self.commit()
+ self.failUnless(schema.has_entity('Societe2'))
+ self.failUnless(schema.has_relation('concerne2'))
+ attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval "noname", '
+ ' X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
+ 'WHERE RT name "name", E name "Societe2", F name "String"')[0][0]
+ self._set_attr_perms(attreid)
+ concerne2_rdef_eid = self.execute(
+ 'INSERT CWRelation X: X cardinality "**", X relation_type RT, X from_entity E, X to_entity E '
+ 'WHERE RT name "concerne2", E name "Societe2"')[0][0]
+ self._set_perms(concerne2_rdef_eid)
+ self.failIf('name' in schema['Societe2'].subject_relations())
+ self.failIf('concerne2' in schema['Societe2'].subject_relations())
+ self.failIf(self.index_exists('Societe2', 'name'))
+ self.commit()
+ self.failUnless('name' in schema['Societe2'].subject_relations())
+ self.failUnless('concerne2' in schema['Societe2'].subject_relations())
+ self.failUnless(self.index_exists('Societe2', 'name'))
+ # now we should be able to insert and query Societe2
+ s2eid = self.execute('INSERT Societe2 X: X name "logilab"')[0][0]
+ self.execute('Societe2 X WHERE X name "logilab"')
+ self.execute('SET X concerne2 X WHERE X name "logilab"')
+ rset = self.execute('Any X WHERE X concerne2 Y')
+ self.assertEquals(rset.rows, [[s2eid]])
+ # check that when a relation definition is deleted, existing relations are deleted
+ rdefeid = self.execute('INSERT CWRelation X: X cardinality "**", X relation_type RT, '
+ ' X from_entity E, X to_entity E '
+ 'WHERE RT name "concerne2", E name "CWUser"')[0][0]
+ self._set_perms(rdefeid)
+ self.commit()
+ self.execute('DELETE CWRelation X WHERE X eid %(x)s', {'x': concerne2_rdef_eid}, 'x')
+ self.commit()
+ self.failUnless('concerne2' in schema['CWUser'].subject_relations())
+ self.failIf('concerne2' in schema['Societe2'].subject_relations())
+ self.failIf(self.execute('Any X WHERE X concerne2 Y'))
+ # schema should be cleaned on delete (after commit)
+ self.execute('DELETE CWEType X WHERE X name "Societe2"')
+ self.execute('DELETE CWRType X WHERE X name "concerne2"')
+ self.failUnless(self.index_exists('Societe2', 'name'))
+ self.failUnless(schema.has_entity('Societe2'))
+ self.failUnless(schema.has_relation('concerne2'))
+ self.commit()
+ self.failIf(self.index_exists('Societe2', 'name'))
+ self.failIf(schema.has_entity('Societe2'))
+ self.failIf(schema.has_entity('concerne2'))
+ self.failIf('concerne2' in schema['CWUser'].subject_relations())
+
+ def test_is_instance_of_insertions(self):
+ seid = self.execute('INSERT Transition T: T name "subdiv"')[0][0]
+ is_etypes = [etype for etype, in self.execute('Any ETN WHERE X eid %s, X is ET, ET name ETN' % seid)]
+ self.assertEquals(is_etypes, ['Transition'])
+ instanceof_etypes = [etype for etype, in self.execute('Any ETN WHERE X eid %s, X is_instance_of ET, ET name ETN' % seid)]
+ self.assertEquals(sorted(instanceof_etypes), ['BaseTransition', 'Transition'])
+ snames = [name for name, in self.execute('Any N WHERE S is BaseTransition, S name N')]
+ self.failIf('subdiv' in snames)
+ snames = [name for name, in self.execute('Any N WHERE S is_instance_of BaseTransition, S name N')]
+ self.failUnless('subdiv' in snames)
+
+
+ def test_perms_synchronization_1(self):
+ schema = self.repo.schema
+ self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', 'users')))
+ self.failUnless(self.execute('Any X, Y WHERE X is CWEType, X name "CWUser", Y is CWGroup, Y name "users"')[0])
+ self.execute('DELETE X read_permission Y WHERE X is CWEType, X name "CWUser", Y name "users"')
+ self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', 'users', )))
+ self.commit()
+ self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers',)))
+ self.execute('SET X read_permission Y WHERE X is CWEType, X name "CWUser", Y name "users"')
+ self.commit()
+ self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', 'users',)))
+
+ def test_perms_synchronization_2(self):
+ schema = self.repo.schema['in_group'].rdefs[('CWUser', 'CWGroup')]
+ self.assertEquals(schema.get_groups('read'), set(('managers', 'users', 'guests')))
+ self.execute('DELETE X read_permission Y WHERE X relation_type RT, RT name "in_group", Y name "guests"')
+ self.assertEquals(schema.get_groups('read'), set(('managers', 'users', 'guests')))
+ self.commit()
+ self.assertEquals(schema.get_groups('read'), set(('managers', 'users')))
+ self.execute('SET X read_permission Y WHERE X relation_type RT, RT name "in_group", Y name "guests"')
+ self.assertEquals(schema.get_groups('read'), set(('managers', 'users')))
+ self.commit()
+ self.assertEquals(schema.get_groups('read'), set(('managers', 'users', 'guests')))
+
+ def test_nonregr_user_edit_itself(self):
+ ueid = self.session.user.eid
+ groupeids = [eid for eid, in self.execute('CWGroup G WHERE G name in ("managers", "users")')]
+ self.execute('DELETE X in_group Y WHERE X eid %s' % ueid)
+ self.execute('SET X surname "toto" WHERE X eid %s' % ueid)
+ self.execute('SET X in_group Y WHERE X eid %s, Y name "managers"' % ueid)
+ self.commit()
+ eeid = self.execute('Any X WHERE X is CWEType, X name "CWEType"')[0][0]
+ self.execute('DELETE X read_permission Y WHERE X eid %s' % eeid)
+ self.execute('SET X final FALSE WHERE X eid %s' % eeid)
+ self.execute('SET X read_permission Y WHERE X eid %s, Y eid in (%s, %s)'
+ % (eeid, groupeids[0], groupeids[1]))
+ self.commit()
+ self.execute('Any X WHERE X is CWEType, X name "CWEType"')
+
+ # schema modification hooks tests #########################################
+
+ def test_uninline_relation(self):
+ self.session.set_pool()
+ dbhelper = self.session.pool.source('system').dbhelper
+ sqlcursor = self.session.pool['system']
+ self.failUnless(self.schema['state_of'].inlined)
+ try:
+ self.execute('SET X inlined FALSE WHERE X name "state_of"')
+ self.failUnless(self.schema['state_of'].inlined)
+ self.commit()
+ self.failIf(self.schema['state_of'].inlined)
+ self.failIf(self.index_exists('State', 'state_of'))
+ rset = self.execute('Any X, Y WHERE X state_of Y')
+ self.assertEquals(len(rset), 2) # user states
+ finally:
+ self.execute('SET X inlined TRUE WHERE X name "state_of"')
+ self.failIf(self.schema['state_of'].inlined)
+ self.commit()
+ self.failUnless(self.schema['state_of'].inlined)
+ self.failUnless(self.index_exists('State', 'state_of'))
+ rset = self.execute('Any X, Y WHERE X state_of Y')
+ self.assertEquals(len(rset), 2)
+
+ def test_indexed_change(self):
+ self.session.set_pool()
+ dbhelper = self.session.pool.source('system').dbhelper
+ sqlcursor = self.session.pool['system']
+ try:
+ self.execute('SET X indexed FALSE WHERE X relation_type R, R name "name"')
+ self.failUnless(self.schema['name'].rdef('Workflow', 'String').indexed)
+ self.failUnless(self.index_exists('Workflow', 'name'))
+ self.commit()
+ self.failIf(self.schema['name'].rdef('Workflow', 'String').indexed)
+ self.failIf(self.index_exists('Workflow', 'name'))
+ finally:
+ self.execute('SET X indexed TRUE WHERE X relation_type R, R name "name"')
+ self.failIf(self.schema['name'].rdef('Workflow', 'String').indexed)
+ self.failIf(self.index_exists('Workflow', 'name'))
+ self.commit()
+ self.failUnless(self.schema['name'].rdef('Workflow', 'String').indexed)
+ self.failUnless(self.index_exists('Workflow', 'name'))
+
+ def test_unique_change(self):
+ self.session.set_pool()
+ dbhelper = self.session.pool.source('system').dbhelper
+ sqlcursor = self.session.pool['system']
+ try:
+ self.execute('INSERT CWConstraint X: X cstrtype CT, DEF constrained_by X '
+ 'WHERE CT name "UniqueConstraint", DEF relation_type RT, DEF from_entity E,'
+ 'RT name "name", E name "Workflow"')
+ self.failIf(self.schema['Workflow'].has_unique_values('name'))
+ self.failIf(self.index_exists('Workflow', 'name', unique=True))
+ self.commit()
+ self.failUnless(self.schema['Workflow'].has_unique_values('name'))
+ self.failUnless(self.index_exists('Workflow', 'name', unique=True))
+ finally:
+ self.execute('DELETE DEF constrained_by X WHERE X cstrtype CT, '
+ 'CT name "UniqueConstraint", DEF relation_type RT, DEF from_entity E,'
+ 'RT name "name", E name "Workflow"')
+ self.failUnless(self.schema['Workflow'].has_unique_values('name'))
+ self.failUnless(self.index_exists('Workflow', 'name', unique=True))
+ self.commit()
+ self.failIf(self.schema['Workflow'].has_unique_values('name'))
+ self.failIf(self.index_exists('Workflow', 'name', unique=True))
+
+ def test_required_change_1(self):
+ self.execute('SET DEF cardinality "?1" '
+ 'WHERE DEF relation_type RT, DEF from_entity E,'
+ 'RT name "title", E name "Bookmark"')
+ self.commit()
+ # should now be able to add bookmark without title
+ self.execute('INSERT Bookmark X: X path "/view"')
+ self.commit()
+
+ def test_required_change_2(self):
+ self.execute('SET DEF cardinality "11" '
+ 'WHERE DEF relation_type RT, DEF from_entity E,'
+ 'RT name "surname", E name "CWUser"')
+ self.commit()
+ # should not be able anymore to add cwuser without surname
+ self.assertRaises(ValidationError, self.create_user, "toto")
+ self.execute('SET DEF cardinality "?1" '
+ 'WHERE DEF relation_type RT, DEF from_entity E,'
+ 'RT name "surname", E name "CWUser"')
+ self.commit()
+
+
+ def test_add_attribute_to_base_class(self):
+ attreid = self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval "noname", X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
+ 'WHERE RT name "messageid", E name "BaseTransition", F name "String"')[0][0]
+ assert self.execute('SET X read_permission Y WHERE X eid %(x)s, Y name "managers"',
+ {'x': attreid}, 'x')
+ self.commit()
+ self.schema.rebuild_infered_relations()
+ self.failUnless('Transition' in self.schema['messageid'].subjects())
+ self.failUnless('WorkflowTransition' in self.schema['messageid'].subjects())
+ self.execute('Any X WHERE X is_instance_of BaseTransition, X messageid "hop"')
+
+ def test_change_fulltextindexed(self):
+ target = self.request().create_entity(u'EmailAddress', address=u'rick.roll@dance.com')
+ self.commit()
+ rset = self.execute('Any X Where X has_text "rick.roll"')
+ self.assertIn(target.eid, [item[0] for item in rset])
+
+ assert self.execute('''SET A fulltextindexed False
+ WHERE E is CWEType,
+ E name "EmailAddress",
+ A is CWAttribute,
+ A from_entity E,
+ A relation_type R,
+ R name "address"
+ ''')
+ self.commit()
+ rset = self.execute('Any X Where X has_text "rick.roll"')
+ self.assertNotIn(target.eid, [item[0] for item in rset])
+
--- a/hooks/workflow.py Thu Feb 18 09:22:04 2010 +0100
+++ b/hooks/workflow.py Fri Feb 26 17:39:33 2010 +0100
@@ -13,7 +13,6 @@
from cubicweb.interfaces import IWorkflowable
from cubicweb.selectors import implements
from cubicweb.server import hook
-from cubicweb.entities.wfobjs import WorkflowTransition
def _change_state(session, x, oldstate, newstate):
@@ -52,7 +51,6 @@
"""try to fire auto transition after state changes"""
def precommit_event(self):
- session = self.session
entity = self.entity
autotrs = list(entity.possible_transitions('auto'))
if autotrs:
@@ -232,7 +230,7 @@
raise ValidationError(entity.eid, {'by_transition': msg})
if entity.get('to_state'):
deststateeid = entity['to_state']
- if not cowpowers and deststateeid != tr.destination().eid:
+ if not cowpowers and deststateeid != tr.destination(forentity).eid:
msg = session._("transition isn't allowed")
raise ValidationError(entity.eid, {'by_transition': msg})
if swtr is None:
@@ -241,7 +239,7 @@
msg = session._("state doesn't belong to entity's workflow")
raise ValidationError(entity.eid, {'to_state': msg})
else:
- deststateeid = tr.destination().eid
+ deststateeid = tr.destination(forentity).eid
# everything is ok, add missing information on the trinfo entity
entity['from_state'] = fromstate.eid
entity['to_state'] = deststateeid
--- a/i18n.py Thu Feb 18 09:22:04 2010 +0100
+++ b/i18n.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,7 +9,6 @@
import re
import os
-import sys
from os.path import join, basename, splitext, exists
from glob import glob
--- a/i18n/en.po Thu Feb 18 09:22:04 2010 +0100
+++ b/i18n/en.po Fri Feb 26 17:39:33 2010 +0100
@@ -125,6 +125,9 @@
msgid "(UNEXISTANT EID)"
msgstr ""
+msgid "(loading ...)"
+msgstr ""
+
msgid "**"
msgstr "0..n 0..n"
@@ -191,9 +194,6 @@
msgid "Any"
msgstr ""
-msgid "Application"
-msgstr ""
-
msgid "Attributes"
msgstr ""
@@ -348,9 +348,6 @@
msgid "Entities"
msgstr ""
-msgid "Environment"
-msgstr ""
-
msgid "ExternalUri"
msgstr "External Uri"
@@ -375,6 +372,9 @@
msgid "Help"
msgstr ""
+msgid "Instance"
+msgstr ""
+
msgid "Int"
msgstr "Integer"
@@ -501,7 +501,7 @@
msgid "Relations"
msgstr ""
-msgid "Request"
+msgid "Repository"
msgstr ""
#, python-format
@@ -514,9 +514,6 @@
msgid "Search for"
msgstr ""
-msgid "Server"
-msgstr ""
-
msgid "SizeConstraint"
msgstr "size constraint"
@@ -657,6 +654,9 @@
msgid "Used by:"
msgstr ""
+msgid "Web server"
+msgstr ""
+
msgid "What's new?"
msgstr ""
@@ -917,32 +917,29 @@
msgid "add Bookmark bookmarked_by CWUser object"
msgstr "bookmark"
-msgid "add CWAttribute add_permission RQLExpression subject"
-msgstr "add rql expression"
-
msgid "add CWAttribute constrained_by CWConstraint subject"
msgstr "constraint"
-msgid "add CWAttribute delete_permission RQLExpression subject"
-msgstr "delete rql expression"
-
msgid "add CWAttribute read_permission RQLExpression subject"
msgstr "read rql expression"
msgid "add CWAttribute relation_type CWRType object"
msgstr "attribute definition"
+msgid "add CWAttribute update_permission RQLExpression subject"
+msgstr "rql expression for update permission"
+
msgid "add CWEType add_permission RQLExpression subject"
-msgstr "rql expression for the add permission"
+msgstr "rql expression for add permission"
msgid "add CWEType delete_permission RQLExpression subject"
-msgstr "rql expression for the delete permission"
+msgstr "rql expression for delete permission"
msgid "add CWEType read_permission RQLExpression subject"
-msgstr "rql expression for the read permission"
+msgstr "rql expression for read permission"
msgid "add CWEType update_permission RQLExpression subject"
-msgstr "rql expression for the update permission"
+msgstr "rql expression for update permission"
msgid "add CWProperty for_user CWUser object"
msgstr "property"
@@ -1034,10 +1031,6 @@
msgid "add_permission"
msgstr "add permission"
-msgctxt "CWAttribute"
-msgid "add_permission"
-msgstr "add permission"
-
msgctxt "CWRelation"
msgid "add_permission"
msgstr "add permission"
@@ -1083,9 +1076,6 @@
msgid "allow to set a specific workflow for an entity"
msgstr ""
-msgid "allowed transition from this state"
-msgstr ""
-
msgid "allowed transitions from this state"
msgstr ""
@@ -1489,6 +1479,12 @@
msgid "condition_object"
msgstr "condition of"
+msgid "config mode"
+msgstr ""
+
+msgid "config type"
+msgstr ""
+
msgid "confirm password"
msgstr ""
@@ -1577,24 +1573,6 @@
msgid "copy"
msgstr ""
-msgid ""
-"core relation giving to a group the permission to add an entity or relation "
-"type"
-msgstr ""
-
-msgid ""
-"core relation giving to a group the permission to delete an entity or "
-"relation type"
-msgstr ""
-
-msgid ""
-"core relation giving to a group the permission to read an entity or relation "
-"type"
-msgstr ""
-
-msgid "core relation giving to a group the permission to update an entity type"
-msgstr ""
-
msgid "core relation indicating a user's groups"
msgstr ""
@@ -1661,17 +1639,13 @@
msgstr "creating email address for user %(linkto)s"
msgid ""
-"creating RQLExpression (CWAttribute %(linkto)s add_permission RQLExpression)"
-msgstr "RQL expression granting add permission on %(linkto)s"
+"creating RQLExpression (CWAttribute %(linkto)s read_permission RQLExpression)"
+msgstr "RQL expression granting read permission on %(linkto)s"
msgid ""
-"creating RQLExpression (CWAttribute %(linkto)s delete_permission "
+"creating RQLExpression (CWAttribute %(linkto)s update_permission "
"RQLExpression)"
-msgstr "RQL expression granting delete permission on %(linkto)s"
-
-msgid ""
-"creating RQLExpression (CWAttribute %(linkto)s read_permission RQLExpression)"
-msgstr "RQL expression granting read permission on %(linkto)s"
+msgstr "RQL expression granting update permission on %(linkto)s"
msgid ""
"creating RQLExpression (CWEType %(linkto)s add_permission RQLExpression)"
@@ -1905,10 +1879,6 @@
msgid "delete_permission"
msgstr "delete permission"
-msgctxt "CWAttribute"
-msgid "delete_permission"
-msgstr "delete permission"
-
msgctxt "CWRelation"
msgid "delete_permission"
msgstr "delete_permission"
@@ -2271,6 +2241,9 @@
msgid "follow this link for more information on this %s"
msgstr ""
+msgid "follow this link if javascript is deactivated"
+msgstr ""
+
msgid "for_user"
msgstr "for user"
@@ -2385,18 +2358,6 @@
msgid "groups"
msgstr ""
-msgid "groups allowed to add entities/relations of this type"
-msgstr ""
-
-msgid "groups allowed to delete entities/relations of this type"
-msgstr ""
-
-msgid "groups allowed to read entities/relations of this type"
-msgstr ""
-
-msgid "groups allowed to update entities of this type"
-msgstr ""
-
msgid "groups grant permissions to the user"
msgstr ""
@@ -2421,9 +2382,6 @@
msgid "hide filter form"
msgstr ""
-msgid "home"
-msgstr ""
-
msgid ""
"how to format date and time in the ui (\"man strftime\" for format "
"description)"
@@ -2567,6 +2525,9 @@
msgid "inlined"
msgstr "inlined"
+msgid "instance home"
+msgstr ""
+
msgid "instance schema"
msgstr ""
@@ -2634,6 +2595,9 @@
msgid "last connection date"
msgstr ""
+msgid "last usage"
+msgstr ""
+
msgid "last_login_time"
msgstr "last login time"
@@ -2681,15 +2645,9 @@
msgid "link a workflow to one or more entity type"
msgstr ""
-msgid "link to each item in"
-msgstr ""
-
msgid "list"
msgstr ""
-msgid "loading"
-msgstr ""
-
msgid "log in"
msgstr ""
@@ -2888,6 +2846,9 @@
msgid "no related project"
msgstr ""
+msgid "no repository sessions found"
+msgstr ""
+
msgid "no selected entities"
msgstr ""
@@ -2898,6 +2859,9 @@
msgid "no version information"
msgstr ""
+msgid "no web sessions found"
+msgstr ""
+
msgid "normal"
msgstr ""
@@ -2934,6 +2898,12 @@
msgid "open all"
msgstr ""
+msgid "opened sessions"
+msgstr ""
+
+msgid "opened web sessions"
+msgstr ""
+
msgid "order"
msgstr ""
@@ -3182,6 +3152,9 @@
msgid "required field"
msgstr ""
+msgid "resources usage"
+msgstr ""
+
msgid ""
"restriction part of a rql query. For entity rql expression, X and U are "
"predefined respectivly to the current object and to the request user. For "
@@ -3195,18 +3168,6 @@
msgid "right"
msgstr ""
-msgid "rql expression allowing to add entities/relations of this type"
-msgstr ""
-
-msgid "rql expression allowing to delete entities/relations of this type"
-msgstr ""
-
-msgid "rql expression allowing to read entities/relations of this type"
-msgstr ""
-
-msgid "rql expression allowing to update entities of this type"
-msgstr ""
-
msgid "rql expressions"
msgstr ""
@@ -3300,9 +3261,6 @@
msgid "september"
msgstr ""
-msgid "server debug information"
-msgstr ""
-
msgid "server information"
msgstr ""
@@ -3744,6 +3702,10 @@
msgid "update_permission"
msgstr "can be updated by"
+msgctxt "CWAttribute"
+msgid "update_permission"
+msgstr "can be updated by"
+
msgctxt "CWGroup"
msgid "update_permission_object"
msgstr "has permission to update"
@@ -3774,7 +3736,8 @@
msgid ""
"use to define a transition from one or multiple states to a destination "
-"states in workflow's definitions."
+"states in workflow's definitions. Transition without destination state will "
+"go back to the state from which we arrived to the current state."
msgstr ""
msgid "use_email"
@@ -3847,6 +3810,9 @@
msgid "vcard"
msgstr ""
+msgid "versions configuration"
+msgstr ""
+
msgid "view"
msgstr ""
--- a/i18n/es.po Thu Feb 18 09:22:04 2010 +0100
+++ b/i18n/es.po Fri Feb 26 17:39:33 2010 +0100
@@ -130,6 +130,9 @@
msgid "(UNEXISTANT EID)"
msgstr ""
+msgid "(loading ...)"
+msgstr "(Cargando ...)"
+
msgid "**"
msgstr "0..n 0..n"
@@ -199,9 +202,6 @@
msgid "Any"
msgstr "Cualquiera"
-msgid "Application"
-msgstr "Aplicación"
-
msgid "Attributes"
msgstr "Atributos"
@@ -356,9 +356,6 @@
msgid "Entities"
msgstr "Entidades"
-msgid "Environment"
-msgstr "Ambiente"
-
msgid "ExternalUri"
msgstr ""
@@ -383,6 +380,9 @@
msgid "Help"
msgstr ""
+msgid "Instance"
+msgstr ""
+
msgid "Int"
msgstr "Número entero"
@@ -509,8 +509,8 @@
msgid "Relations"
msgstr "Relaciones"
-msgid "Request"
-msgstr "Petición"
+msgid "Repository"
+msgstr ""
#, python-format
msgid "Schema %s"
@@ -522,9 +522,6 @@
msgid "Search for"
msgstr "Buscar"
-msgid "Server"
-msgstr "Servidor"
-
msgid "SizeConstraint"
msgstr ""
@@ -665,6 +662,9 @@
msgid "Used by:"
msgstr "Utilizado por :"
+msgid "Web server"
+msgstr ""
+
msgid "What's new?"
msgstr "Lo último en el sitio"
@@ -940,21 +940,18 @@
msgid "add Bookmark bookmarked_by CWUser object"
msgstr "Agregar a los favoritos "
-msgid "add CWAttribute add_permission RQLExpression subject"
-msgstr ""
-
msgid "add CWAttribute constrained_by CWConstraint subject"
msgstr "Restricción"
-msgid "add CWAttribute delete_permission RQLExpression subject"
-msgstr ""
-
msgid "add CWAttribute read_permission RQLExpression subject"
msgstr ""
msgid "add CWAttribute relation_type CWRType object"
msgstr "Definición de atributo"
+msgid "add CWAttribute update_permission RQLExpression subject"
+msgstr ""
+
msgid "add CWEType add_permission RQLExpression subject"
msgstr "Expresión RQL de agregación"
@@ -1057,10 +1054,6 @@
msgid "add_permission"
msgstr ""
-msgctxt "CWAttribute"
-msgid "add_permission"
-msgstr ""
-
msgctxt "CWRelation"
msgid "add_permission"
msgstr ""
@@ -1106,9 +1099,6 @@
msgid "allow to set a specific workflow for an entity"
msgstr ""
-msgid "allowed transition from this state"
-msgstr "transición autorizada desde este estado"
-
msgid "allowed transitions from this state"
msgstr "transiciones autorizadas desde este estado"
@@ -1520,6 +1510,12 @@
msgid "condition_object"
msgstr "condición de"
+msgid "config mode"
+msgstr ""
+
+msgid "config type"
+msgstr ""
+
msgid "confirm password"
msgstr "Confirmar contraseña"
@@ -1610,32 +1606,6 @@
msgid "copy"
msgstr "Copiar"
-msgid ""
-"core relation giving to a group the permission to add an entity or relation "
-"type"
-msgstr ""
-"Relación sistema que otorga a un grupo la autorización de agregar una "
-"entidad o una relación"
-
-msgid ""
-"core relation giving to a group the permission to delete an entity or "
-"relation type"
-msgstr ""
-"Relación sistema que otorga a un grupo la autorización de eliminar una "
-"entidad o relación"
-
-msgid ""
-"core relation giving to a group the permission to read an entity or relation "
-"type"
-msgstr ""
-"Relación sistema que otorga a un grupo la autorización de leer una entidad o "
-"una relación "
-
-msgid "core relation giving to a group the permission to update an entity type"
-msgstr ""
-"Relación sistema que otorga a un grupo la autorización de actualizar una "
-"entidad"
-
msgid "core relation indicating a user's groups"
msgstr ""
"Relación sistema que indica los grupos a los cuales pertenece un usuario"
@@ -1708,19 +1678,15 @@
msgstr "Creación de una dirección electrónica para el usuario %(linkto)s"
msgid ""
-"creating RQLExpression (CWAttribute %(linkto)s add_permission RQLExpression)"
+"creating RQLExpression (CWAttribute %(linkto)s read_permission RQLExpression)"
msgstr ""
msgid ""
-"creating RQLExpression (CWAttribute %(linkto)s delete_permission "
+"creating RQLExpression (CWAttribute %(linkto)s update_permission "
"RQLExpression)"
msgstr ""
msgid ""
-"creating RQLExpression (CWAttribute %(linkto)s read_permission RQLExpression)"
-msgstr ""
-
-msgid ""
"creating RQLExpression (CWEType %(linkto)s add_permission RQLExpression)"
msgstr ""
"Creación de una expresión RQL para la autorización de agregar %(linkto)s"
@@ -1956,10 +1922,6 @@
msgid "delete_permission"
msgstr ""
-msgctxt "CWAttribute"
-msgid "delete_permission"
-msgstr ""
-
msgctxt "CWRelation"
msgid "delete_permission"
msgstr ""
@@ -2329,6 +2291,9 @@
msgid "follow this link for more information on this %s"
msgstr ""
+msgid "follow this link if javascript is deactivated"
+msgstr ""
+
msgid "for_user"
msgstr "Para el usuario"
@@ -2443,18 +2408,6 @@
msgid "groups"
msgstr "Grupos"
-msgid "groups allowed to add entities/relations of this type"
-msgstr "Grupos autorizados a agregar entidades/relaciones de este tipo"
-
-msgid "groups allowed to delete entities/relations of this type"
-msgstr "Grupos autorizados a eliminar entidades/relaciones de este tipo"
-
-msgid "groups allowed to read entities/relations of this type"
-msgstr "Grupos autorizados a leer entidades/relaciones de este tipo"
-
-msgid "groups allowed to update entities of this type"
-msgstr "Grupos autorizados a actualizar entidades de este tipo"
-
msgid "groups grant permissions to the user"
msgstr "Los grupos otorgan las autorizaciones al usuario"
@@ -2479,9 +2432,6 @@
msgid "hide filter form"
msgstr "Esconder el filtro"
-msgid "home"
-msgstr "Inicio"
-
msgid ""
"how to format date and time in the ui (\"man strftime\" for format "
"description)"
@@ -2634,6 +2584,9 @@
msgid "inlined"
msgstr ""
+msgid "instance home"
+msgstr ""
+
msgid "instance schema"
msgstr ""
@@ -2705,6 +2658,9 @@
msgid "last connection date"
msgstr "Ultima fecha de conexión"
+msgid "last usage"
+msgstr ""
+
msgid "last_login_time"
msgstr "Ultima fecha de conexión"
@@ -2757,15 +2713,9 @@
msgid "link a workflow to one or more entity type"
msgstr ""
-msgid "link to each item in"
-msgstr "ligar hacia cada elemento en"
-
msgid "list"
msgstr "Lista"
-msgid "loading"
-msgstr "Cargando"
-
msgid "log in"
msgstr "Identificarse"
@@ -2970,6 +2920,9 @@
msgid "no related project"
msgstr "no hay proyecto relacionado"
+msgid "no repository sessions found"
+msgstr ""
+
msgid "no selected entities"
msgstr "no hay entidades seleccionadas"
@@ -2980,6 +2933,9 @@
msgid "no version information"
msgstr "no información de version"
+msgid "no web sessions found"
+msgstr ""
+
msgid "normal"
msgstr ""
@@ -3016,6 +2972,12 @@
msgid "open all"
msgstr "abrir todos"
+msgid "opened sessions"
+msgstr ""
+
+msgid "opened web sessions"
+msgstr ""
+
msgid "order"
msgstr "orden"
@@ -3263,6 +3225,9 @@
msgid "required field"
msgstr "Campo requerido"
+msgid "resources usage"
+msgstr ""
+
msgid ""
"restriction part of a rql query. For entity rql expression, X and U are "
"predefined respectivly to the current object and to the request user. For "
@@ -3280,18 +3245,6 @@
msgid "right"
msgstr "Derecha"
-msgid "rql expression allowing to add entities/relations of this type"
-msgstr "expresion RQL permitiendo agregar entidades/relaciones de este tipo"
-
-msgid "rql expression allowing to delete entities/relations of this type"
-msgstr "expresion RQL permitiendo eliminar entidades/relaciones de este tipo"
-
-msgid "rql expression allowing to read entities/relations of this type"
-msgstr "expresion RQL permitiendo leer entidades/relaciones de este tipo"
-
-msgid "rql expression allowing to update entities of this type"
-msgstr "expresion RQL permitiendo actualizar entidades de este tipo"
-
msgid "rql expressions"
msgstr "expresiones rql"
@@ -3385,9 +3338,6 @@
msgid "september"
msgstr "septiembre"
-msgid "server debug information"
-msgstr "server debug information"
-
msgid "server information"
msgstr "server information"
@@ -3833,6 +3783,10 @@
msgid "update_permission"
msgstr ""
+msgctxt "CWAttribute"
+msgid "update_permission"
+msgstr ""
+
msgctxt "CWGroup"
msgid "update_permission_object"
msgstr ""
@@ -3863,10 +3817,9 @@
msgid ""
"use to define a transition from one or multiple states to a destination "
-"states in workflow's definitions."
-msgstr ""
-"utilizado para definir una transición desde uno o multiples estados hacia "
-"uno o varios estados destino en las definiciones del workflow"
+"states in workflow's definitions. Transition without destination state will "
+"go back to the state from which we arrived to the current state."
+msgstr ""
msgid "use_email"
msgstr "correo electrónico"
@@ -3944,6 +3897,9 @@
msgid "vcard"
msgstr "vcard"
+msgid "versions configuration"
+msgstr ""
+
msgid "view"
msgstr "ver"
@@ -4063,12 +4019,24 @@
#~ msgid "%s results matching query"
#~ msgstr "%s resultados de la demanda"
+#~ msgid "Application"
+#~ msgstr "Aplicación"
+
#~ msgid "Debug level set to %s"
#~ msgstr "Nivel de debug puesto a %s"
+#~ msgid "Environment"
+#~ msgstr "Ambiente"
+
#~ msgid "No query has been executed"
#~ msgstr "Ninguna búsqueda ha sido ejecutada"
+#~ msgid "Request"
+#~ msgstr "Petición"
+
+#~ msgid "Server"
+#~ msgstr "Servidor"
+
#~ msgid "There is no workflow defined for this entity."
#~ msgstr "No hay workflow para este entidad"
@@ -4164,6 +4132,9 @@
#~ "Relación agregada %(rtype)s de %(frometype)s #%(fromeid)s hacia %(toetype)"
#~ "s #%(toeid)s"
+#~ msgid "allowed transition from this state"
+#~ msgstr "transición autorizada desde este estado"
+
#~ msgid "button_reset"
#~ msgstr "Cancelar los cambios"
@@ -4177,6 +4148,33 @@
#~ msgstr "Edición de una copia"
#~ msgid ""
+#~ "core relation giving to a group the permission to add an entity or "
+#~ "relation type"
+#~ msgstr ""
+#~ "Relación sistema que otorga a un grupo la autorización de agregar una "
+#~ "entidad o una relación"
+
+#~ msgid ""
+#~ "core relation giving to a group the permission to delete an entity or "
+#~ "relation type"
+#~ msgstr ""
+#~ "Relación sistema que otorga a un grupo la autorización de eliminar una "
+#~ "entidad o relación"
+
+#~ msgid ""
+#~ "core relation giving to a group the permission to read an entity or "
+#~ "relation type"
+#~ msgstr ""
+#~ "Relación sistema que otorga a un grupo la autorización de leer una "
+#~ "entidad o una relación "
+
+#~ msgid ""
+#~ "core relation giving to a group the permission to update an entity type"
+#~ msgstr ""
+#~ "Relación sistema que otorga a un grupo la autorización de actualizar una "
+#~ "entidad"
+
+#~ msgid ""
#~ "creating RQLExpression (CWRType %(linkto)s add_permission RQLExpression)"
#~ msgstr ""
#~ "Creación de una expresión RQL para la autorización de agregar relaciones %"
@@ -4229,6 +4227,21 @@
#~ msgid "entity types which may use this transition"
#~ msgstr "Entidades que pueden utilizar esta transición"
+#~ msgid "groups allowed to add entities/relations of this type"
+#~ msgstr "Grupos autorizados a agregar entidades/relaciones de este tipo"
+
+#~ msgid "groups allowed to delete entities/relations of this type"
+#~ msgstr "Grupos autorizados a eliminar entidades/relaciones de este tipo"
+
+#~ msgid "groups allowed to read entities/relations of this type"
+#~ msgstr "Grupos autorizados a leer entidades/relaciones de este tipo"
+
+#~ msgid "groups allowed to update entities of this type"
+#~ msgstr "Grupos autorizados a actualizar entidades de este tipo"
+
+#~ msgid "home"
+#~ msgstr "Inicio"
+
#~ msgid "initial state for entities of this type"
#~ msgstr "Estado inicial para las entidades de este tipo"
@@ -4241,6 +4254,12 @@
#~ msgid "link a transition to one or more entity type"
#~ msgstr "liga una transición a una o mas tipos de entidad"
+#~ msgid "link to each item in"
+#~ msgstr "ligar hacia cada elemento en"
+
+#~ msgid "loading"
+#~ msgstr "Cargando"
+
#~ msgid "nothing to edit"
#~ msgstr "nada que editar"
@@ -4295,6 +4314,29 @@
#~ msgid "remove this Transition"
#~ msgstr "Eliminar esta transición"
+#~ msgid "rql expression allowing to add entities/relations of this type"
+#~ msgstr "expresion RQL permitiendo agregar entidades/relaciones de este tipo"
+
+#~ msgid "rql expression allowing to delete entities/relations of this type"
+#~ msgstr ""
+#~ "expresion RQL permitiendo eliminar entidades/relaciones de este tipo"
+
+#~ msgid "rql expression allowing to read entities/relations of this type"
+#~ msgstr "expresion RQL permitiendo leer entidades/relaciones de este tipo"
+
+#~ msgid "rql expression allowing to update entities of this type"
+#~ msgstr "expresion RQL permitiendo actualizar entidades de este tipo"
+
+#~ msgid "server debug information"
+#~ msgstr "server debug information"
+
+#~ msgid ""
+#~ "use to define a transition from one or multiple states to a destination "
+#~ "states in workflow's definitions."
+#~ msgstr ""
+#~ "utilizado para definir una transición desde uno o multiples estados hacia "
+#~ "uno o varios estados destino en las definiciones del workflow"
+
#~ msgid ""
#~ "user for which this property is applying. If this relation is not set, "
#~ "the property is considered as a global property"
--- a/i18n/fr.po Thu Feb 18 09:22:04 2010 +0100
+++ b/i18n/fr.po Fri Feb 26 17:39:33 2010 +0100
@@ -130,6 +130,9 @@
msgid "(UNEXISTANT EID)"
msgstr "(EID INTROUVABLE)"
+msgid "(loading ...)"
+msgstr "(chargement ...)"
+
msgid "**"
msgstr "0..n 0..n"
@@ -198,9 +201,6 @@
msgid "Any"
msgstr "N'importe"
-msgid "Application"
-msgstr "Application"
-
msgid "Attributes"
msgstr "Attributs"
@@ -355,9 +355,6 @@
msgid "Entities"
msgstr "entités"
-msgid "Environment"
-msgstr "Environement"
-
msgid "ExternalUri"
msgstr "Uri externe"
@@ -382,6 +379,9 @@
msgid "Help"
msgstr "Aide"
+msgid "Instance"
+msgstr "Instance"
+
msgid "Int"
msgstr "Nombre entier"
@@ -508,8 +508,8 @@
msgid "Relations"
msgstr "Relations"
-msgid "Request"
-msgstr "Requête"
+msgid "Repository"
+msgstr "Entrepôt de données"
#, python-format
msgid "Schema %s"
@@ -521,9 +521,6 @@
msgid "Search for"
msgstr "Rechercher"
-msgid "Server"
-msgstr "Serveur"
-
msgid "SizeConstraint"
msgstr "contrainte de taille"
@@ -664,6 +661,9 @@
msgid "Used by:"
msgstr "Utilisé par :"
+msgid "Web server"
+msgstr "Serveur web"
+
msgid "What's new?"
msgstr "Nouveautés"
@@ -945,21 +945,18 @@
msgid "add Bookmark bookmarked_by CWUser object"
msgstr "signet"
-msgid "add CWAttribute add_permission RQLExpression subject"
-msgstr "expression rql d'ajout"
-
msgid "add CWAttribute constrained_by CWConstraint subject"
msgstr "contrainte"
-msgid "add CWAttribute delete_permission RQLExpression subject"
-msgstr "expression rql de suppression"
-
msgid "add CWAttribute read_permission RQLExpression subject"
msgstr "expression rql de lecture"
msgid "add CWAttribute relation_type CWRType object"
msgstr "définition d'attribut"
+msgid "add CWAttribute update_permission RQLExpression subject"
+msgstr "permission de mise à jour"
+
msgid "add CWEType add_permission RQLExpression subject"
msgstr "définir une expression RQL d'ajout"
@@ -1062,10 +1059,6 @@
msgid "add_permission"
msgstr "permission d'ajout"
-msgctxt "CWAttribute"
-msgid "add_permission"
-msgstr "permission d'ajout"
-
msgctxt "CWRelation"
msgid "add_permission"
msgstr "permission d'ajout"
@@ -1113,9 +1106,6 @@
msgid "allow to set a specific workflow for an entity"
msgstr "permet de spécifier un workflow donné pour une entité"
-msgid "allowed transition from this state"
-msgstr "transition autorisée depuis cet état"
-
msgid "allowed transitions from this state"
msgstr "transitions autorisées depuis cet état"
@@ -1528,6 +1518,12 @@
msgid "condition_object"
msgstr "condition de"
+msgid "config mode"
+msgstr "mode de configuration"
+
+msgid "config type"
+msgstr "type de configuration"
+
msgid "confirm password"
msgstr "confirmer le mot de passe"
@@ -1619,32 +1615,6 @@
msgid "copy"
msgstr "copier"
-msgid ""
-"core relation giving to a group the permission to add an entity or relation "
-"type"
-msgstr ""
-"relation système donnant à un groupe la permission d'ajouter une entité ou "
-"une relation"
-
-msgid ""
-"core relation giving to a group the permission to delete an entity or "
-"relation type"
-msgstr ""
-"relation système donnant à un group la permission de supprimer une entité ou "
-"une relation"
-
-msgid ""
-"core relation giving to a group the permission to read an entity or relation "
-"type"
-msgstr ""
-"relation système donnant à un group la permission de lire une entité ou une "
-"relation"
-
-msgid "core relation giving to a group the permission to update an entity type"
-msgstr ""
-"relation système donnant à un groupe la permission de mettre à jour une "
-"entityé"
-
msgid "core relation indicating a user's groups"
msgstr ""
"relation système indiquant les groupes auxquels appartient l'utilisateur"
@@ -1717,18 +1687,14 @@
msgstr "création d'une adresse électronique pour l'utilisateur %(linkto)s"
msgid ""
-"creating RQLExpression (CWAttribute %(linkto)s add_permission RQLExpression)"
-msgstr "création d'une expression rql pour le droit d'ajout de %(linkto)s"
+"creating RQLExpression (CWAttribute %(linkto)s read_permission RQLExpression)"
+msgstr "création d'une expression rql pour le droit de lecture de %(linkto)s"
msgid ""
-"creating RQLExpression (CWAttribute %(linkto)s delete_permission "
+"creating RQLExpression (CWAttribute %(linkto)s update_permission "
"RQLExpression)"
msgstr ""
-"création d'une expression rql pour le droit de suppression de %(linkto)s"
-
-msgid ""
-"creating RQLExpression (CWAttribute %(linkto)s read_permission RQLExpression)"
-msgstr "création d'une expression rql pour le droit de lecture de %(linkto)s"
+"création d'une expression rql pour le droit de mise à jour de %(linkto)s"
msgid ""
"creating RQLExpression (CWEType %(linkto)s add_permission RQLExpression)"
@@ -1928,8 +1894,8 @@
msgid "define a relation type, used to build the instance schema"
msgstr "définit un type de relation"
-msgid "define a rql expression used to define vpermissions"
-msgstr ""
+msgid "define a rql expression used to define permissions"
+msgstr "définit une expression rql donnant une permission"
msgid "define a schema constraint"
msgstr "définit une contrainte de schema"
@@ -1972,10 +1938,6 @@
msgid "delete_permission"
msgstr "permission de supprimer"
-msgctxt "CWAttribute"
-msgid "delete_permission"
-msgstr "permission de supprimer"
-
msgctxt "CWRelation"
msgid "delete_permission"
msgstr "permission de supprimer"
@@ -2351,6 +2313,9 @@
msgid "follow this link for more information on this %s"
msgstr "suivez ce lien pour plus d'information sur ce %s"
+msgid "follow this link if javascript is deactivated"
+msgstr ""
+
msgid "for_user"
msgstr "pour l'utilisateur"
@@ -2468,18 +2433,6 @@
msgid "groups"
msgstr "groupes"
-msgid "groups allowed to add entities/relations of this type"
-msgstr "groupes autorisés à ajouter des entités/relations de ce type"
-
-msgid "groups allowed to delete entities/relations of this type"
-msgstr "groupes autorisés à supprimer des entités/relations de ce type"
-
-msgid "groups allowed to read entities/relations of this type"
-msgstr "groupes autorisés à lire des entités/relations de ce type"
-
-msgid "groups allowed to update entities of this type"
-msgstr "groupes autorisés à mettre à jour les entités de ce type"
-
msgid "groups grant permissions to the user"
msgstr "les groupes donnent des permissions à l'utilisateur"
@@ -2504,9 +2457,6 @@
msgid "hide filter form"
msgstr "cacher le filtre"
-msgid "home"
-msgstr "maison"
-
msgid ""
"how to format date and time in the ui (\"man strftime\" for format "
"description)"
@@ -2659,6 +2609,9 @@
msgid "inlined"
msgstr "mise en ligne"
+msgid "instance home"
+msgstr "répertoire de l'instance"
+
msgid "instance schema"
msgstr "schéma de l'instance"
@@ -2731,6 +2684,9 @@
msgid "last connection date"
msgstr "dernière date de connexion"
+msgid "last usage"
+msgstr "dernier usage"
+
msgid "last_login_time"
msgstr "dernière date de connexion"
@@ -2783,15 +2739,9 @@
msgid "link a workflow to one or more entity type"
msgstr "lie un workflow à un ou plusieurs types d'entité"
-msgid "link to each item in"
-msgstr "lier vers chaque élément dans"
-
msgid "list"
msgstr "liste"
-msgid "loading"
-msgstr "chargement"
-
msgid "log in"
msgstr "s'identifier"
@@ -2992,6 +2942,9 @@
msgid "no related project"
msgstr "pas de projet rattaché"
+msgid "no repository sessions found"
+msgstr "aucune session trouvée"
+
msgid "no selected entities"
msgstr "pas d'entité sélectionnée"
@@ -3002,6 +2955,9 @@
msgid "no version information"
msgstr "pas d'information de version"
+msgid "no web sessions found"
+msgstr "aucune session trouvée"
+
msgid "normal"
msgstr "normal"
@@ -3038,6 +2994,12 @@
msgid "open all"
msgstr "tout ouvrir"
+msgid "opened sessions"
+msgstr "sessions ouvertes"
+
+msgid "opened web sessions"
+msgstr "sessions web ouvertes"
+
msgid "order"
msgstr "ordre"
@@ -3287,6 +3249,9 @@
msgid "required field"
msgstr "champ requis"
+msgid "resources usage"
+msgstr "resources utilisées"
+
msgid ""
"restriction part of a rql query. For entity rql expression, X and U are "
"predefined respectivly to the current object and to the request user. For "
@@ -3305,22 +3270,6 @@
msgid "right"
msgstr "droite"
-msgid "rql expression allowing to add entities/relations of this type"
-msgstr ""
-"expression RQL donnant le droit d'ajouter des entités/relations de ce type"
-
-msgid "rql expression allowing to delete entities/relations of this type"
-msgstr ""
-"expression RQL donnant le droit de supprimer des entités/relations de ce type"
-
-msgid "rql expression allowing to read entities/relations of this type"
-msgstr ""
-"expression RQL donnant le droit de lire des entités/relations de ce type"
-
-msgid "rql expression allowing to update entities of this type"
-msgstr ""
-"expression RQL donnant le droit de modifier des entités/relations de ce type"
-
msgid "rql expressions"
msgstr "conditions rql"
@@ -3414,9 +3363,6 @@
msgid "september"
msgstr "septembre"
-msgid "server debug information"
-msgstr "informations de déboguage serveur"
-
msgid "server information"
msgstr "informations serveur"
@@ -3866,6 +3812,10 @@
msgid "update_permission"
msgstr "permission de modifier"
+msgctxt "CWAttribute"
+msgid "update_permission"
+msgstr "permission de modifier"
+
msgctxt "CWGroup"
msgid "update_permission_object"
msgstr "peut modifier"
@@ -3896,10 +3846,12 @@
msgid ""
"use to define a transition from one or multiple states to a destination "
-"states in workflow's definitions."
+"states in workflow's definitions. Transition without destination state will "
+"go back to the state from which we arrived to the current state."
msgstr ""
-"utiliser dans une définition de processus pour ajouter une transition depuis "
-"un ou plusieurs états vers un état de destination."
+"utilisé dans une définition de processus pour ajouter une transition depuis "
+"un ou plusieurs états vers un état de destination. Une transition sans état "
+"de destination retournera à l'état précédent l'état courant."
msgid "use_email"
msgstr "adresse électronique"
@@ -3975,6 +3927,9 @@
msgid "vcard"
msgstr "vcard"
+msgid "versions configuration"
+msgstr "configuration de version"
+
msgid "view"
msgstr "voir"
@@ -4072,7 +4027,7 @@
#, python-format
msgid "wrong query parameter line %s"
-msgstr ""
+msgstr "mauvais paramètre de requête ligne %s"
msgid "xbel"
msgstr "xbel"
--- a/mail.py Thu Feb 18 09:22:04 2010 +0100
+++ b/mail.py Fri Feb 26 17:39:33 2010 +0100
@@ -8,7 +8,6 @@
__docformat__ = "restructuredtext en"
from base64 import b64encode, b64decode
-from itertools import repeat
from time import time
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
--- a/migration.py Thu Feb 18 09:22:04 2010 +0100
+++ b/migration.py Fri Feb 26 17:39:33 2010 +0100
@@ -193,7 +193,7 @@
if `retry` is true the r[etry] answer may return 2
"""
- possibleanswers = ['y','n']
+ possibleanswers = ['y', 'n']
if abort:
possibleanswers.append('abort')
if shell:
--- a/misc/migration/3.6.1_Any.py Thu Feb 18 09:22:04 2010 +0100
+++ b/misc/migration/3.6.1_Any.py Fri Feb 26 17:39:33 2010 +0100
@@ -1,1 +1,2 @@
sync_schema_props_perms(syncprops=False)
+sync_schema_props_perms('destination_state', syncperms=False)
--- a/mixins.py Thu Feb 18 09:22:04 2010 +0100
+++ b/mixins.py Fri Feb 26 17:39:33 2010 +0100
@@ -10,10 +10,8 @@
from itertools import chain
-from logilab.common.deprecation import deprecated
from logilab.common.decorators import cached
-from cubicweb import typed_eid
from cubicweb.selectors import implements
from cubicweb.interfaces import IEmailable, ITree
--- a/rqlrewrite.py Thu Feb 18 09:22:04 2010 +0100
+++ b/rqlrewrite.py Fri Feb 26 17:39:33 2010 +0100
@@ -12,7 +12,6 @@
from rql import nodes as n, stmts, TypeResolverException
-from logilab.common.compat import any
from logilab.common.graph import has_path
from cubicweb import Unauthorized, typed_eid
@@ -110,7 +109,10 @@
return newsolutions
-class Unsupported(Exception): pass
+class Unsupported(Exception):
+ """raised when an rql expression can't be inserted in some rql query
+ because it create an unresolvable query (eg no solutions found)
+ """
class RQLRewriter(object):
@@ -291,7 +293,7 @@
def snippet_subquery(self, varmap, transformedsnippet):
"""introduce the given snippet in a subquery"""
subselect = stmts.Select()
- selectvar, snippetvar = varmap
+ selectvar = varmap[0]
subselect.append_selected(n.VariableRef(
subselect.get_variable(selectvar)))
aliases = [selectvar]
@@ -408,7 +410,7 @@
cardindex = 1
ttypes_func = rschema.subjects
rdef = lambda x, y: rschema.rdef(y, x)
- except KeyError, ex:
+ 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
@@ -441,9 +443,9 @@
while argname in self.kwargs:
argname = select.allocate_varname()
# insert "U eid %(u)s"
- var = select.get_variable(self.u_varname)
- select.add_constant_restriction(select.get_variable(self.u_varname),
- 'eid', unicode(argname), 'Substitute')
+ select.add_constant_restriction(
+ select.get_variable(self.u_varname),
+ 'eid', unicode(argname), 'Substitute')
self.kwargs[argname] = self.session.user.eid
return self.u_varname
key = (self.current_expr, self.varmap, vname)
--- a/schema.py Thu Feb 18 09:22:04 2010 +0100
+++ b/schema.py Fri Feb 26 17:39:33 2010 +0100
@@ -15,15 +15,14 @@
from logilab.common.decorators import cached, clear_cache, monkeypatch
from logilab.common.logging_ext import set_log_methods
-from logilab.common.deprecation import deprecated
+from logilab.common.deprecation import deprecated, class_moved
from logilab.common.graph import get_cycles
from logilab.common.compat import any
from yams import BadSchemaDefinition, buildobjs as ybo
from yams.schema import Schema, ERSchema, EntitySchema, RelationSchema, \
RelationDefinitionSchema, PermissionMixIn
-from yams.constraints import (BaseConstraint, StaticVocabularyConstraint,
- FormatConstraint)
+from yams.constraints import BaseConstraint, FormatConstraint
from yams.reader import (CONSTRAINTS, PyFileReader, SchemaLoader,
obsolete as yobsolete, cleanup_sys_modules)
@@ -462,7 +461,6 @@
"""set of entities and relations schema defining the possible data sets
used in an application
-
:type name: str
:ivar name: name of the schema, usually the instance identifier
@@ -767,7 +765,7 @@
rqlst.add_selected(objvar)
else:
colindex = selected.index(objvar.name)
- found.append((action, objvar, colindex))
+ found.append((action, colindex))
# remove U eid %(u)s if U is not used in any other relation
uvrefs = rqlst.defined_vars['U'].references()
if len(uvrefs) == 1:
@@ -835,7 +833,7 @@
# check every special has_*_permission relation is satisfied
get_eschema = session.vreg.schema.eschema
try:
- for eaction, var, col in has_perm_defs:
+ for eaction, col in has_perm_defs:
for i in xrange(len(rset)):
eschema = get_eschema(rset.description[i][col])
eschema.check_perm(session, eaction, eid=rset[i][col])
@@ -1092,6 +1090,12 @@
# XXX deprecated
from yams.buildobjs import RichString
+from yams.constraints import StaticVocabularyConstraint
+
+RichString = class_moved(RichString)
+
+StaticVocabularyConstraint = class_moved(StaticVocabularyConstraint)
+FormatConstraint = class_moved(FormatConstraint)
PyFileReader.context['ERQLExpression'] = yobsolete(ERQLExpression)
PyFileReader.context['RRQLExpression'] = yobsolete(RRQLExpression)
--- a/schemas/base.py Thu Feb 18 09:22:04 2010 +0100
+++ b/schemas/base.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,7 +9,7 @@
_ = unicode
from yams.buildobjs import (EntityType, RelationType, SubjectRelation,
- String, Boolean, Datetime, Password)
+ String, Datetime, Password)
from cubicweb.schema import (RQLConstraint, WorkflowableEntityType,
ERQLExpression, RRQLExpression)
from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS
--- a/schemas/workflow.py Thu Feb 18 09:22:04 2010 +0100
+++ b/schemas/workflow.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,7 +9,7 @@
_ = unicode
from yams.buildobjs import (EntityType, RelationType, SubjectRelation,
- ObjectRelation, RichString, String)
+ RichString, String)
from cubicweb.schema import RQLConstraint, RQLUniqueConstraint
from cubicweb.schemas import (META_ETYPE_PERMS, META_RTYPE_PERMS,
HOOKS_RTYPE_PERMS)
@@ -97,12 +97,13 @@
class Transition(BaseTransition):
"""use to define a transition from one or multiple states to a destination
- states in workflow's definitions.
+ states in workflow's definitions. Transition without destination state will
+ go back to the state from which we arrived to the current state.
"""
__specializes_schema__ = True
destination_state = SubjectRelation(
- 'State', cardinality='1*',
+ 'State', cardinality='?*',
constraints=[RQLConstraint('S transition_of WF, O state_of WF',
msg=_('state and transition don\'t belong the the same workflow'))],
description=_('destination state for this transition'))
--- a/selectors.py Thu Feb 18 09:22:04 2010 +0100
+++ b/selectors.py Fri Feb 26 17:39:33 2010 +0100
@@ -43,7 +43,7 @@
__docformat__ = "restructuredtext en"
import logging
-from warnings import warn, filterwarnings
+from warnings import warn
from logilab.common.deprecation import class_renamed
from logilab.common.compat import all, any
@@ -51,8 +51,7 @@
from yams import BASE_TYPES
-from cubicweb import (Unauthorized, NoSelectableObject, NotAnEntity,
- role, typed_eid)
+from cubicweb import Unauthorized, NoSelectableObject, NotAnEntity, role
# even if not used, let yes here so it's importable through this module
from cubicweb.appobject import Selector, objectify_selector, yes
from cubicweb.vregistry import class_regid
@@ -163,7 +162,6 @@
def score_interfaces(self, req, cls_or_inst, cls):
score = 0
etypesreg = req.vreg['etypes']
- eschema = cls_or_inst.e_schema
for iface in self.expected_ifaces:
if isinstance(iface, basestring):
# entity type
@@ -528,6 +526,9 @@
return 0
return 1
+ def score_class(self, eclass, req):
+ return 1 # necessarily true if we're there
+
class implements(ImplementsMixIn, EClassSelector):
"""Return non-zero score for entity that are of the given type(s) or
--- a/server/__init__.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/__init__.py Fri Feb 26 17:39:33 2010 +0100
@@ -96,10 +96,11 @@
def create_user(session, login, pwd, *groups):
# monkey patch this method if you want to customize admin/anon creation
# (that maybe necessary if you change CWUser's schema)
- session.create_entity('CWUser', login=login, upassword=pwd)
+ user = session.create_entity('CWUser', login=login, upassword=pwd)
for group in groups:
- session.execute('SET U in_group G WHERE G name %(group)s',
- {'group': group})
+ session.execute('SET U in_group G WHERE U eid %(u)s, G name %(group)s',
+ {'u': user.eid, 'group': group})
+ return user
def init_repository(config, interactive=True, drop=False, vreg=None):
"""initialise a repository database by creating tables add filling them
--- a/server/checkintegrity.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/checkintegrity.py Fri Feb 26 17:39:33 2010 +0100
@@ -64,7 +64,7 @@
else:
yield eschema
-def reindex_entities(schema, session):
+def reindex_entities(schema, session, withpb=True):
"""reindex all entities in the repository"""
# deactivate modification_date hook since we don't want them
# to be updated due to the reindexation
@@ -92,22 +92,27 @@
etypes.add(container)
print 'Reindexing entities of type %s' % \
', '.join(sorted(str(e) for e in etypes))
- pb = ProgressBar(len(etypes) + 1)
+ if withpb:
+ pb = ProgressBar(len(etypes) + 1)
# first monkey patch Entity.check to disable validation
from cubicweb.entity import Entity
_check = Entity.check
Entity.check = lambda self, creation=False: True
# clear fti table first
session.system_sql('DELETE FROM %s' % session.repo.system_source.dbhelper.fti_table)
- pb.update()
+ if withpb:
+ pb.update()
# reindex entities by generating rql queries which set all indexable
# attribute to their current value
for eschema in etypes:
for entity in session.execute('Any X WHERE X is %s' % eschema).entities():
FTIndexEntityOp(session, entity=entity)
- pb.update()
+ if withpb:
+ pb.update()
# restore Entity.check
Entity.check = _check
+ repo.config.disabled_hooks_categories.remove('metadata')
+ repo.config.disabled_hooks_categories.remove('integrity')
def check_schema(schema, session, eids, fix=1):
@@ -276,7 +281,7 @@
print >> sys.stderr
-def check(repo, cnx, checks, reindex, fix):
+def check(repo, cnx, checks, reindex, fix, withpb=True):
"""check integrity of instance's repository,
using given user and password to locally connect to the repository
(no running cubicweb server needed)
@@ -297,5 +302,5 @@
if reindex:
cnx.rollback()
session.set_pool()
- reindex_entities(repo.schema, session)
+ reindex_entities(repo.schema, session, withpb=withpb)
cnx.commit()
--- a/server/hook.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/hook.py Fri Feb 26 17:39:33 2010 +0100
@@ -44,7 +44,7 @@
from logilab.common.logging_ext import set_log_methods
from cubicweb.cwvreg import CWRegistry, VRegistry
-from cubicweb.selectors import (objectify_selector, lltrace, match_search_state,
+from cubicweb.selectors import (objectify_selector, lltrace, ExpectedValueSelector,
implements)
from cubicweb.appobject import AppObject
@@ -134,7 +134,7 @@
return iter(chain(*self.iterators))
-class match_rtype(match_search_state):
+class match_rtype(ExpectedValueSelector):
"""accept if parameters specified as initializer arguments are specified
in named arguments given to the selector
@@ -159,7 +159,7 @@
return 1
-class match_rtype_sets(match_search_state):
+class match_rtype_sets(ExpectedValueSelector):
"""accept if parameters specified as initializer arguments are specified
in named arguments given to the selector
"""
--- a/server/hookhelper.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/hookhelper.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,7 +9,6 @@
from logilab.common.deprecation import deprecated, class_moved
-from cubicweb import RepositoryError
from cubicweb.server import hook
@deprecated('[3.6] entity_oldnewvalue should be imported from cw.server.hook')
--- a/server/migractions.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/migractions.py Fri Feb 26 17:39:33 2010 +0100
@@ -29,12 +29,11 @@
from logilab.common.deprecation import deprecated
from logilab.common.decorators import cached, clear_cache
-from logilab.common.adbh import get_adv_func_helper
from yams.constraints import SizeConstraint
from yams.schema2sql import eschema2sql, rschema2sql
-from cubicweb import AuthenticationError, ETYPE_NAME_MAP
+from cubicweb import AuthenticationError
from cubicweb.schema import (META_RTYPES, VIRTUAL_RTYPES,
CubicWebRelationSchema, order_eschemas)
from cubicweb.dbapi import get_repository, repo_connect
@@ -156,7 +155,7 @@
else:
bkup = tarfile.open(backupfile, 'w|gz')
for filename in os.listdir(tmpdir):
- bkup.add(osp.join(tmpdir,filename), filename)
+ bkup.add(osp.join(tmpdir, filename), filename)
bkup.close()
# call hooks
repo.hm.call_hooks('server_backup', repo=repo, timestamp=timestamp)
@@ -170,7 +169,6 @@
# check
if not osp.exists(backupfile):
raise Exception("Backup file %s doesn't exist" % backupfile)
- return
if askconfirm and not self.confirm('Restore %s database from %s ?'
% (self.config.appid, backupfile)):
return
@@ -293,9 +291,6 @@
apc = osp.join(self.config.migration_scripts_dir(), '%s.py' % event)
if osp.exists(apc):
if self.config.free_wheel:
- from cubicweb.server.hooks import setowner_after_add_entity
- self.repo.hm.unregister_hook(setowner_after_add_entity,
- 'after_add_entity', '')
self.cmd_deactivate_verification_hooks()
self.info('executing %s', apc)
confirm = self.confirm
@@ -308,8 +303,6 @@
self.confirm = confirm
self.execscript_confirm = execscript_confirm
if self.config.free_wheel:
- self.repo.hm.register_hook(setowner_after_add_entity,
- 'after_add_entity', '')
self.cmd_reactivate_verification_hooks()
def install_custom_sql_scripts(self, directory, driver):
@@ -352,7 +345,7 @@
'T eid %%(x)s' % perm, {'x': teid}, 'x',
ask_confirm=False):
if not gname in newgroups:
- if not confirm or self.confirm('remove %s permission of %s to %s?'
+ if not confirm or self.confirm('Remove %s permission of %s to %s?'
% (action, erschema, gname)):
self.rqlexec('DELETE T %s G WHERE G eid %%(x)s, T eid %s'
% (perm, teid),
@@ -360,7 +353,7 @@
else:
newgroups.remove(gname)
for gname in newgroups:
- if not confirm or self.confirm('grant %s permission of %s to %s?'
+ if not confirm or self.confirm('Grant %s permission of %s to %s?'
% (action, erschema, gname)):
self.rqlexec('SET T %s G WHERE G eid %%(x)s, T eid %s'
% (perm, teid),
@@ -371,7 +364,7 @@
'T eid %s' % (perm, teid),
ask_confirm=False):
if not expression in newexprs:
- if not confirm or self.confirm('remove %s expression for %s permission of %s?'
+ if not confirm or self.confirm('Remove %s expression for %s permission of %s?'
% (expression, action, erschema)):
# deleting the relation will delete the expression entity
self.rqlexec('DELETE T %s E WHERE E eid %%(x)s, T eid %s'
@@ -381,7 +374,7 @@
newexprs.pop(expression)
for expression in newexprs.values():
expr = expression.expression
- if not confirm or self.confirm('add %s expression for %s permission of %s?'
+ if not confirm or self.confirm('Add %s expression for %s permission of %s?'
% (expr, action, erschema)):
self.rqlexec('INSERT RQLExpression X: X exprtype %%(exprtype)s, '
'X expression %%(expr)s, X mainvars %%(vars)s, T %s X '
@@ -528,7 +521,7 @@
def checkpoint(self, ask_confirm=True):
"""checkpoint action"""
- if not ask_confirm or self.confirm('commit now ?', shell=False):
+ if not ask_confirm or self.confirm('Commit now ?', shell=False):
self.commit()
def cmd_add_cube(self, cube, update_database=True):
@@ -664,7 +657,7 @@
self.cmd_add_attribute(etype, newname, attrtype, commit=True)
# skipp NULL values if the attribute is required
rql = 'SET X %s VAL WHERE X is %s, X %s VAL' % (newname, etype, oldname)
- card = eschema.rproperty(newname, 'cardinality')[0]
+ card = eschema.rdef(newname).cardinality[0]
if card == '1':
rql += ', NOT X %s NULL' % oldname
self.rqlexec(rql, ask_confirm=self.verbosity>=2)
@@ -735,10 +728,10 @@
elif role == 'object':
subjschema = tschema
objschema = spschema
- if (rschema.rproperty(subjschema, objschema, 'infered')
+ if (rschema.rdef(subjschema, objschema).infered
or (instschema.has_relation(rschema) and
- instschema[rschema].has_rdef(subjschema, objschema))):
- continue
+ (subjschema, objschema) in instschema[rschema].rdefs)):
+ continue
self.cmd_add_relation_definition(
subjschema.type, rschema.type, objschema.type)
if auto:
@@ -936,7 +929,7 @@
if syncprops:
self._synchronize_eschema(etype, syncperms=syncperms)
else:
- self._synchronize_permissions(self.fs_schema[etype], erschema.eid)
+ self._synchronize_permissions(self.fs_schema[etype], etype.eid)
if commit:
self.commit()
@@ -973,7 +966,7 @@
you usually want to use sync_schema_props_perms instead.
"""
oldvalue = None
- for constr in self.repo.schema.eschema(etype).constraints(rtype):
+ for constr in self.repo.schema.eschema(etype).rdef(rtype).constraints:
if isinstance(constr, SizeConstraint):
oldvalue = constr.max
if oldvalue == size:
@@ -1146,13 +1139,13 @@
should only be used for low level stuff undoable with existing higher
level actions
"""
- if not ask_confirm or self.confirm('execute sql: %s ?' % sql):
+ if not ask_confirm or self.confirm('Execute sql: %s ?' % sql):
self.session.set_pool() # ensure pool is set
try:
cu = self.session.system_sql(sql, args)
except:
ex = sys.exc_info()[1]
- if self.confirm('error: %s\nabort?' % ex):
+ if self.confirm('Error: %s\nabort?' % ex):
raise
return
try:
@@ -1175,11 +1168,11 @@
msg = '%s (%s)' % (rql, kwargs)
else:
msg = rql
- if not ask_confirm or self.confirm('execute rql: %s ?' % msg):
+ if not ask_confirm or self.confirm('Execute rql: %s ?' % msg):
try:
res = execute(rql, kwargs, cachekey)
except Exception, ex:
- if self.confirm('error: %s\nabort?' % ex):
+ if self.confirm('Error: %s\nabort?' % ex):
raise
return res
@@ -1264,12 +1257,12 @@
else:
msg = rql
if self.ask_confirm:
- if not self._h.confirm('execute rql: %s ?' % msg):
+ if not self._h.confirm('Execute rql: %s ?' % msg):
raise StopIteration
try:
rset = self._h._cw.execute(rql, kwargs)
except Exception, ex:
- if self._h.confirm('error: %s\nabort?' % ex):
+ if self._h.confirm('Error: %s\nabort?' % ex):
raise
else:
raise StopIteration
--- a/server/msplanner.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/msplanner.py Fri Feb 26 17:39:33 2010 +0100
@@ -90,7 +90,6 @@
from cubicweb.server.ssplanner import (SSPlanner, OneFetchStep,
add_types_restriction)
from cubicweb.server.mssteps import *
-from cubicweb.server.sources import AbstractSource
Variable._ms_table_key = lambda x: x.name
Relation._ms_table_key = lambda x: x.r_type
@@ -272,7 +271,7 @@
for source in self._sourcesterms:
print '-', source
for term, sols in self._sourcesterms[source].items():
- print ' -', term, id(term), ':' ,sols
+ print ' -', term, id(term), ':', sols
def copy_solutions(self, solindices):
return [self._solutions[solidx].copy() for solidx in solindices]
@@ -465,7 +464,7 @@
try:
lhsv = self._extern_term(lhs, termssources, inserted)
rhsv = self._extern_term(rhs, termssources, inserted)
- except KeyError, ex:
+ except KeyError:
continue
self._remove_term_sources(lhsv, rel, rhsv, termssources)
self._remove_term_sources(rhsv, rel, lhsv, termssources)
@@ -684,11 +683,11 @@
# go to the next iteration directly!
continue
if not sourceterms:
- try:
- del self._sourcesterms[source]
- except KeyError:
- # XXX already cleaned
- pass
+ try:
+ del self._sourcesterms[source]
+ except KeyError:
+ # XXX already cleaned
+ pass
# set of terms which should be additionaly selected when
# possible
needsel = set()
@@ -857,7 +856,6 @@
terms = [term]
sources = sorted(sources)
sourcesterms = self._sourcesterms
- nbunlinked = 1
linkedterms = self._linkedterms
# term has to belong to the same scope if there is more
# than the system source remaining
@@ -873,7 +871,7 @@
for source in sources:
cross_rels.update(self._crossrelations.get(source, {}))
exclude = {}
- for rel, crossvars in cross_rels.iteritems():
+ for crossvars in cross_rels.itervalues():
vars = [t for t in crossvars if isinstance(t, Variable)]
try:
exclude[vars[0]] = vars[1]
@@ -897,7 +895,6 @@
modified = False
for term in candidates[:]:
if isinstance(term, Constant):
- relation = term.relation()
if sorted(set(x[0] for x in self._term_sources(term))) != sources:
continue
terms.append(term)
@@ -1023,12 +1020,6 @@
if server.DEBUG & server.DBG_MS:
print '-'*80
print 'PLANNING', rqlst
- for select in rqlst.children:
- if len(select.solutions) > 1:
- hasmultiplesols = True
- break
- else:
- hasmultiplesols = False
# preprocess deals with security insertion and returns a new syntax tree
# which have to be executed to fulfill the query: according
# to permissions for variable's type, different rql queries may have to
@@ -1036,7 +1027,7 @@
plan.preprocess(rqlst)
ppis = [PartPlanInformation(plan, select, self.rqlhelper)
for select in rqlst.children]
- steps = self._union_plan(plan, rqlst, ppis)
+ steps = self._union_plan(plan, ppis)
if server.DEBUG & server.DBG_MS:
from pprint import pprint
for step in plan.steps:
@@ -1054,7 +1045,7 @@
for sppi in sppis:
if sppi.needsplit or sppi.part_sources != ppi.part_sources:
temptable = 'T%s' % make_uid(id(subquery))
- sstep = self._union_plan(plan, subquery.query, sppis, temptable)[0]
+ sstep = self._union_plan(plan, sppis, temptable)[0]
break
else:
sstep = None
@@ -1065,7 +1056,7 @@
ppi.plan.add_step(sstep)
return inputmap
- def _union_plan(self, plan, union, ppis, temptable=None):
+ def _union_plan(self, plan, ppis, temptable=None):
tosplit, cango, allsources = [], {}, set()
for planinfo in ppis:
if planinfo.needsplit:
@@ -1191,7 +1182,7 @@
for step in steps
for select in step.union.children):
if temptable:
- step = IntersectFetchStep(plan)
+ step = IntersectFetchStep(plan) # XXX not implemented
else:
step = IntersectStep(plan)
else:
--- a/server/pool.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/pool.py Fri Feb 26 17:39:33 2010 +0100
@@ -87,7 +87,7 @@
# implementation details of flying insert requires the system source
# first
yield self.source_cnxs['system'][0]
- for uri, (source, cursor) in self.source_cnxs.items():
+ for uri, (source, cnx) in self.source_cnxs.items():
if uri == 'system':
continue
yield source
@@ -109,6 +109,11 @@
else:
sources = (source,)
for source in sources:
+ try:
+ # properly close existing connection if any
+ self.source_cnxs[source.uri][1].close()
+ except:
+ pass
source.info('trying to reconnect')
self.source_cnxs[source.uri] = (source, source.get_connection())
self._cursors.pop(source.uri, None)
--- a/server/querier.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/querier.py Fri Feb 26 17:39:33 2010 +0100
@@ -12,7 +12,7 @@
from logilab.common.cache import Cache
from logilab.common.compat import any
-from rql import RQLHelper, RQLSyntaxError
+from rql import RQLSyntaxError
from rql.stmts import Union, Select
from rql.nodes import Relation, VariableRef, Constant, SubQuery
@@ -26,7 +26,7 @@
READ_ONLY_RTYPES = set(('eid', 'has_text', 'is', 'is_instance_of', 'identity'))
-def empty_rset(session, rql, args, rqlst=None):
+def empty_rset(rql, args, rqlst=None):
"""build an empty result set object"""
return ResultSet([], rql, args, rqlst=rqlst)
@@ -210,7 +210,6 @@
self.cache_key = None
def _insert_security(self, union, noinvariant):
- rh = self.rqlhelper
for select in union.children[:]:
for subquery in select.with_:
self._insert_security(subquery.query, noinvariant)
@@ -572,7 +571,7 @@
"""execute a rql query, return resulting rows and their description in
a `ResultSet` object
- * `rql` should be an unicode string or a plain ascii string
+ * `rql` should be an Unicode string or a plain ASCII string
* `args` the optional parameters dictionary associated to the query
* `build_descr` is a boolean flag indicating if the description should
be built on select queries (if false, the description will be en empty
@@ -580,17 +579,17 @@
* `eid_key` must be both a key in args and a substitution in the rql
query. It should be used to enhance cacheability of rql queries.
It may be a tuple for keys in args.
- eid_key must be providen in case where a eid substitution is providen
- and resolve some ambiguity in the possible solutions infered for each
+ `eid_key` must be provided in cases where a eid substitution is provided
+ and resolves ambiguities in the possible solutions inferred for each
variable in the query.
- on INSERT queries, there will be on row with the eid of each inserted
+ on INSERT queries, there will be one row with the eid of each inserted
entity
result for DELETE and SET queries is undefined yet
to maximize the rql parsing/analyzing cache performance, you should
- always use substitute arguments in queries (eg avoid query such as
+ always use substitute arguments in queries (i.e. avoid query such as
'Any X WHERE X eid 123'!)
"""
if server.DEBUG & (server.DBG_RQL | server.DBG_SQL):
@@ -613,7 +612,7 @@
except UnknownEid:
# we want queries such as "Any X WHERE X eid 9999"
# return an empty result instead of raising UnknownEid
- return empty_rset(session, rql, args)
+ return empty_rset(rql, args)
cachekey.append(etype)
# ensure eid is correctly typed in args
args[key] = typed_eid(args[key])
@@ -631,7 +630,7 @@
except UnknownEid:
# we want queries such as "Any X WHERE X eid 9999"
# return an empty result instead of raising UnknownEid
- return empty_rset(session, rql, args, rqlst)
+ return empty_rset(rql, args, rqlst)
self._rql_cache[cachekey] = rqlst
orig_rqlst = rqlst
if not rqlst.TYPE == 'select':
--- a/server/repository.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/repository.py Fri Feb 26 17:39:33 2010 +0100
@@ -19,7 +19,7 @@
import sys
import Queue
-from os.path import join, exists
+from os.path import join
from datetime import datetime
from time import time, localtime, strftime
@@ -29,7 +29,7 @@
from yams import BadSchemaDefinition
from rql import RQLSyntaxError
-from cubicweb import (CW_SOFTWARE_ROOT, CW_MIGRATION_MAP, CW_EVENT_MANAGER,
+from cubicweb import (CW_SOFTWARE_ROOT, CW_MIGRATION_MAP,
UnknownEid, AuthenticationError, ExecutionError,
ETypeNotSupportedBySources, MultiSourcesError,
BadConnectionId, Unauthorized, ValidationError,
@@ -101,14 +101,12 @@
this kind of behaviour has to be done in the repository so we don't have
hooks order hazardness
"""
- # skip delete queries (only?) if session is an internal session. This is
- # hooks responsability to ensure they do not violate relation's cardinality
- if session.is_super_session:
+ # XXX now that rql in migraction default to unsafe_execute we don't want to
+ # skip that for super session (though we can still skip it for internal
+ # sessions). Also we should imo rely on the orm to first fetch existing
+ # entity if any then delete it.
+ if session.is_internal_session:
return
- ensure_card_respected(session.unsafe_execute, session, eidfrom, rtype, eidto)
-
-
-def ensure_card_respected(execute, session, eidfrom, rtype, eidto):
card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality')
# one may be tented to check for neweids but this may cause more than one
# relation even with '1?' cardinality if thoses relations are added in the
@@ -116,14 +114,28 @@
# the web interface but may occurs during test or dbapi connection (though
# not expected for this). So: don't do it, we pretend to ensure repository
# consistency.
+ #
+ # XXX we don't want read permissions to be applied but we want delete
+ # permission to be checked
+ rschema = session.repo.schema.rschema(rtype)
if card[0] in '1?':
- rschema = session.repo.schema.rschema(rtype)
- if not rschema.inlined:
- execute('DELETE X %s Y WHERE X eid %%(x)s,NOT Y eid %%(y)s' % rtype,
- {'x': eidfrom, 'y': eidto}, 'x')
+ if not rschema.inlined: # inlined relations will be implicitly deleted
+ rset = session.unsafe_execute('Any X,Y WHERE X %s Y, X eid %%(x)s, '
+ 'NOT Y eid %%(y)s' % rtype,
+ {'x': eidfrom, 'y': eidto}, 'x')
+ if rset:
+ safe_delete_relation(session, rschema, *rset[0])
if card[1] in '1?':
- execute('DELETE X %s Y WHERE NOT X eid %%(x)s, Y eid %%(y)s' % rtype,
- {'x': eidfrom, 'y': eidto}, 'y')
+ rset = session.unsafe_execute('Any X,Y WHERE X %s Y, Y eid %%(y)s, '
+ 'NOT X eid %%(x)s' % rtype,
+ {'x': eidfrom, 'y': eidto}, 'y')
+ if rset:
+ safe_delete_relation(session, rschema, *rset[0])
+
+def safe_delete_relation(session, rschema, subject, object):
+ if not rschema.has_perm(session, 'delete', fromeid=subject, toeid=object):
+ raise Unauthorized()
+ session.repo.glob_delete_relation(session, subject, rschema.type, object)
class Repository(object):
@@ -316,8 +328,8 @@
return self._available_pools.get(True, timeout=5)
except Queue.Empty:
raise Exception('no pool available after 5 secs, probably either a '
- 'bug in code (to many uncommited/rollbacked '
- 'connections) or to much load on the server (in '
+ 'bug in code (too many uncommited/rollbacked '
+ 'connections) or too much load on the server (in '
'which case you can try to set a bigger '
'connections pools size)')
@@ -371,6 +383,23 @@
except ZeroDivisionError:
pass
+ def stats(self): # XXX restrict to managers session?
+ import threading
+ results = {}
+ for hits, misses, title in (
+ (self.querier.cache_hit, self.querier.cache_miss, 'rqlt_st'),
+ (self.system_source.cache_hit, self.system_source.cache_miss, 'sql'),
+ ):
+ results['%s_cache_hit' % title] = hits
+ results['%s_cache_miss' % title] = misses
+ results['%s_cache_hit_percent' % title] = (hits * 100) / (hits + misses)
+ results['sql_no_cache'] = self.system_source.no_cache
+ results['nb_open_sessions'] = len(self._sessions)
+ results['nb_active_threads'] = threading.activeCount()
+ results['looping_tasks'] = ', '.join(str(t) for t in self._looping_tasks)
+ results['available_pools'] = self._available_pools.qsize()
+ return results
+
def _login_from_email(self, login):
session = self.internal_session()
try:
@@ -435,7 +464,8 @@
public method, not requiring a session id.
"""
versions = self.get_versions(not (self.config.creating
- or self.config.repairing))
+ or self.config.repairing
+ or self.config.mode == 'test'))
cubes = list(versions)
cubes.remove('cubicweb')
return cubes
--- a/server/rqlannotation.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/rqlannotation.py Fri Feb 26 17:39:33 2010 +0100
@@ -30,7 +30,7 @@
need_distinct = True
# XXX could mark as not invariant
break
- for name, var in rqlst.defined_vars.items():
+ for var in rqlst.defined_vars.itervalues():
stinfo = var.stinfo
if stinfo.get('ftirels'):
has_text_query = True
@@ -130,7 +130,8 @@
-class CantSelectPrincipal(Exception): pass
+class CantSelectPrincipal(Exception):
+ """raised when no 'principal' variable can be found"""
def _select_principal(sqlscope, relations, _sort=lambda x:x):
"""given a list of rqlst relations, select one which will be used to
@@ -139,13 +140,11 @@
"""
# _sort argument is there for test
diffscope_rels = {}
- has_same_scope_rel = False
ored_rels = set()
diffscope_rels = set()
for rel in _sort(relations):
# note: only eid and has_text among all final relations may be there
if rel.r_type in ('eid', 'identity'):
- has_same_scope_rel = rel.sqlscope is sqlscope
continue
if rel.ored(traverse_scope=True):
ored_rels.add(rel)
--- a/server/schemaserial.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/schemaserial.py Fri Feb 26 17:39:33 2010 +0100
@@ -8,8 +8,6 @@
__docformat__ = "restructuredtext en"
import os
-import sys
-import os
from itertools import chain
from logilab.common.shellutils import ProgressBar
@@ -284,23 +282,19 @@
relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
return relations, values
-# XXX 2.47 migration
-HAS_FULLTEXT_CONTAINER = True
-
def rschema_relations_values(rschema):
values = _ervalues(rschema)
values['final'] = rschema.final
values['symmetric'] = rschema.symmetric
values['inlined'] = rschema.inlined
- if HAS_FULLTEXT_CONTAINER:
- if isinstance(rschema.fulltext_container, str):
- values['fulltext_container'] = unicode(rschema.fulltext_container)
- else:
- values['fulltext_container'] = rschema.fulltext_container
+ if isinstance(rschema.fulltext_container, str):
+ values['fulltext_container'] = unicode(rschema.fulltext_container)
+ else:
+ values['fulltext_container'] = rschema.fulltext_container
relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
return relations, values
-def _rdef_values(rschema, objtype, props):
+def _rdef_values(objtype, props):
amap = {'order': 'ordernum'}
values = {}
for prop, default in schemamod.RelationDefinitionSchema.rproperty_defs(objtype).iteritems():
@@ -316,13 +310,13 @@
values[amap.get(prop, prop)] = value
return values
-def nfrdef_relations_values(rschema, objtype, props):
- values = _rdef_values(rschema, objtype, props)
+def nfrdef_relations_values(objtype, props):
+ values = _rdef_values(objtype, props)
relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
return relations, values
-def frdef_relations_values(rschema, objtype, props):
- values = _rdef_values(rschema, objtype, props)
+def frdef_relations_values(objtype, props):
+ values = _rdef_values(objtype, props)
default = values['default']
del values['default']
if default is not None:
@@ -435,7 +429,7 @@
_LOCATE_RDEF_RQL1 = 'SE name %(se)s,ER name %(rt)s,OE name %(oe)s'
def frdef2rql(rschema, subjtype, objtype, props):
- relations, values = frdef_relations_values(rschema, objtype, props)
+ relations, values = frdef_relations_values(objtype, props)
relations.append(_LOCATE_RDEF_RQL0)
values.update({'se': str(subjtype), 'rt': str(rschema), 'oe': str(objtype)})
yield 'INSERT CWAttribute X: %s WHERE %s' % (','.join(relations), _LOCATE_RDEF_RQL1), values
@@ -443,7 +437,7 @@
yield rql + ', EDEF is CWAttribute', values
def nfrdef2rql(rschema, subjtype, objtype, props):
- relations, values = nfrdef_relations_values(rschema, objtype, props)
+ relations, values = nfrdef_relations_values(objtype, props)
relations.append(_LOCATE_RDEF_RQL0)
values.update({'se': str(subjtype), 'rt': str(rschema), 'oe': str(objtype)})
yield 'INSERT CWRelation X: %s WHERE %s' % (','.join(relations), _LOCATE_RDEF_RQL1), values
@@ -472,7 +466,12 @@
and CWGroup entities
"""
for action in erschema.ACTIONS:
- for group_or_rqlexpr in erschema.action_permissions(action):
+ try:
+ grantedto = erschema.action_permissions(action)
+ except KeyError:
+ # may occurs when modifying persistent schema
+ continue
+ for group_or_rqlexpr in grantedto:
if isinstance(group_or_rqlexpr, basestring):
# group
try:
@@ -505,14 +504,14 @@
return __rdef2rql(genmap, rschema, subjtype, objtype, props)
def updatefrdef2rql(rschema, subjtype, objtype, props):
- relations, values = frdef_relations_values(rschema, objtype, props)
+ relations, values = frdef_relations_values(objtype, props)
values.update({'se': subjtype, 'rt': str(rschema), 'oe': objtype})
yield 'SET %s WHERE %s, %s, X is CWAttribute' % (','.join(relations),
_LOCATE_RDEF_RQL0,
_LOCATE_RDEF_RQL1), values
def updatenfrdef2rql(rschema, subjtype, objtype, props):
- relations, values = nfrdef_relations_values(rschema, objtype, props)
+ relations, values = nfrdef_relations_values(objtype, props)
values.update({'se': subjtype, 'rt': str(rschema), 'oe': objtype})
yield 'SET %s WHERE %s, %s, X is CWRelation' % (','.join(relations),
_LOCATE_RDEF_RQL0,
--- a/server/serverconfig.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/serverconfig.py Fri Feb 26 17:39:33 2010 +0100
@@ -7,14 +7,12 @@
"""
__docformat__ = "restructuredtext en"
-import os
from os.path import join, exists
from logilab.common.configuration import REQUIRED, Method, Configuration, \
ini_format_section
from logilab.common.decorators import wproperty, cached, clear_cache
-from cubicweb import CW_SOFTWARE_ROOT, RegistryNotFound
from cubicweb.toolsutils import read_config, restrict_perms_to_user
from cubicweb.cwconfig import CubicWebConfiguration, merge_options
from cubicweb.server import SOURCE_TYPES
--- a/server/serverctl.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/serverctl.py Fri Feb 26 17:39:33 2010 +0100
@@ -7,6 +7,9 @@
"""
__docformat__ = 'restructuredtext en'
+# *ctl module should limit the number of import to be imported as quickly as
+# possible (for cubicweb-ctl reactivity, necessary for instance for usable bash
+# completion). So import locally in command helpers.
import sys
import os
@@ -17,7 +20,6 @@
from cubicweb import AuthenticationError, ExecutionError, ConfigurationError
from cubicweb.toolsutils import Command, CommandHandler, underline_title
from cubicweb.server import SOURCE_TYPES
-from cubicweb.server.utils import ask_source_config
from cubicweb.server.serverconfig import (USER_OPTIONS, ServerConfiguration,
SourceConfiguration)
@@ -132,6 +134,7 @@
"""create an instance by copying files from the given cube and by
asking information necessary to build required configuration files
"""
+ from cubicweb.server.utils import ask_source_config
config = self.config
print underline_title('Configuring the repository')
config.input_config('email', inputlevel)
@@ -443,7 +446,6 @@
def run(self, args):
"""run the command with its specific arguments"""
- from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
from cubicweb.server.utils import crypt_password, manager_userpasswd
appid = pop_arg(args, 1, msg='No instance specified !')
config = ServerConfiguration.config_for(appid)
@@ -455,13 +457,21 @@
sys.exit(1)
cnx = source_cnx(sourcescfg['system'])
cursor = cnx.cursor()
+ # check admin exists
+ cursor.execute("SELECT * FROM cw_CWUser WHERE cw_login=%(l)s",
+ {'l': adminlogin})
+ if not cursor.fetchall():
+ print ("-> error: admin user %r specified in sources doesn't exist "
+ "in the database" % adminlogin)
+ print " fix your sources file before running this command"
+ cnx.close()
+ sys.exit(1)
+ # ask for a new password
_, passwd = manager_userpasswd(adminlogin, confirm=True,
passwdmsg='new password for %s' % adminlogin)
try:
- sqlexec("UPDATE %(sp)sCWUser SET %(sp)supassword='%(p)s' WHERE %(sp)slogin='%(l)s'"
- % {'sp': SQL_PREFIX,
- 'p': crypt_password(passwd), 'l': adminlogin},
- cursor, withpb=False)
+ cursor.execute("UPDATE cw_CWUser SET cw_upassword=%(p)s WHERE cw_login=%(l)s",
+ {'p': crypt_password(passwd), 'l': adminlogin})
sconfig = Configuration(options=USER_OPTIONS)
sconfig['login'] = adminlogin
sconfig['password'] = passwd
@@ -475,6 +485,7 @@
else:
cnx.commit()
print '-> password reset, sources file regenerated.'
+ cnx.close()
class StartRepositoryCommand(Command):
--- a/server/session.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/session.py Fri Feb 26 17:39:33 2010 +0100
@@ -71,9 +71,9 @@
self._threads_in_transaction = set()
self._closed = False
- def __str__(self):
- return '<%ssession %s (%s 0x%x)>' % (self.cnxtype, self.user.login,
- self.id, id(self))
+ def __unicode__(self):
+ return '<%ssession %s (%s 0x%x)>' % (
+ self.cnxtype, unicode(self.user.login), self.id, id(self))
def hijack_user(self, user):
"""return a fake request/session using specified user"""
@@ -225,7 +225,7 @@
self.pgettext = pgettext
except KeyError:
self._ = self.__ = unicode
- self.pgettext = lambda x,y: y
+ self.pgettext = lambda x, y: y
self.lang = language
def change_property(self, prop, value):
@@ -314,7 +314,7 @@
except KeyError:
pass
pool.pool_reset()
- self._threaddata.pool = None
+ del self._threaddata.pool
# free pool once everything is done to avoid race-condition
self.repo._free_pool(pool)
@@ -410,13 +410,13 @@
@property
def super_session(self):
try:
- csession = self._threaddata.childsession
+ csession = self.childsession
except AttributeError:
if isinstance(self, (ChildSession, InternalSession)):
csession = self
else:
csession = ChildSession(self)
- self._threaddata.childsession = csession
+ self.childsession = csession
# need shared pool set
self.set_pool(checkclosed=False)
return csession
@@ -443,11 +443,24 @@
rset = self._execute(self, rql, kwargs, eid_key, build_descr)
return self.decorate_rset(rset, propagate)
+ def _clear_thread_data(self):
+ """remove everything from the thread local storage, except pool
+ which is explicitly removed by reset_pool, and mode which is set anyway
+ by _touch
+ """
+ store = self._threaddata
+ for name in ('commit_state', 'transaction_data', 'pending_operations',
+ '_rewriter'):
+ try:
+ delattr(store, name)
+ except AttributeError:
+ pass
+
def commit(self, reset_pool=True):
"""commit the current session's transaction"""
if self.pool is None:
assert not self.pending_operations
- self.transaction_data.clear()
+ self._clear_thread_data()
self._touch()
self.debug('commit session %s done (no db activity)', self.id)
return
@@ -501,10 +514,8 @@
exc_info=sys.exc_info())
self.info('%s session %s done', trstate, self.id)
finally:
+ self._clear_thread_data()
self._touch()
- self.commit_state = None
- self.pending_operations[:] = []
- self.transaction_data.clear()
if reset_pool:
self.reset_pool(ignoremode=True)
@@ -512,7 +523,7 @@
"""rollback the current session's transaction"""
if self.pool is None:
assert not self.pending_operations
- self.transaction_data.clear()
+ self._clear_thread_data()
self._touch()
self.debug('rollback session %s done (no db activity)', self.id)
return
@@ -527,9 +538,8 @@
self.pool.rollback()
self.debug('rollback for session %s done', self.id)
finally:
+ self._clear_thread_data()
self._touch()
- self.pending_operations[:] = []
- self.transaction_data.clear()
if reset_pool:
self.reset_pool(ignoremode=True)
@@ -584,6 +594,7 @@
@property
def rql_rewriter(self):
+ # in thread local storage since the rewriter isn't thread safe
try:
return self._threaddata._rewriter
except AttributeError:
--- a/server/sources/ldapuser.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/sources/ldapuser.py Fri Feb 26 17:39:33 2010 +0100
@@ -210,11 +210,11 @@
if res:
ldapemailaddr = res[0].get(ldap_emailattr)
if ldapemailaddr:
- rset = session.execute('EmailAddress X,A WHERE '
+ rset = session.execute('EmailAddress A WHERE '
'U use_email X, U eid %(u)s',
{'u': eid})
ldapemailaddr = unicode(ldapemailaddr)
- for emaileid, emailaddr in rset:
+ for emailaddr, in rset:
if emailaddr == ldapemailaddr:
break
else:
@@ -448,12 +448,12 @@
def _auth_cram_md5(self, conn, user, userpwd):
from ldap import sasl
auth_token = sasl.cram_md5(user['dn'], userpwd)
- conn.sasl_interactive_bind_s('', auth_tokens)
+ conn.sasl_interactive_bind_s('', auth_token)
def _auth_digest_md5(self, conn, user, userpwd):
from ldap import sasl
auth_token = sasl.digest_md5(user['dn'], userpwd)
- conn.sasl_interactive_bind_s('', auth_tokens)
+ conn.sasl_interactive_bind_s('', auth_token)
def _auth_gssapi(self, conn, user, userpwd):
# print XXX not proper sasl/gssapi
--- a/server/sources/native.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/sources/native.py Fri Feb 26 17:39:33 2010 +0100
@@ -421,12 +421,16 @@
# str(query) to avoid error if it's an unicode string
cursor.execute(str(query), args)
except Exception, ex:
- self.critical("sql: %r\n args: %s\ndbms message: %r",
- query, args, ex.args[0])
+ if self.repo.config.mode != 'test':
+ # during test we get those message when trying to alter sqlite
+ # db schema
+ self.critical("sql: %r\n args: %s\ndbms message: %r",
+ query, args, ex.args[0])
if rollback:
try:
session.pool.connection(self.uri).rollback()
- self.critical('transaction has been rollbacked')
+ if self.repo.config.mode != 'test':
+ self.critical('transaction has been rollbacked')
except:
pass
raise
@@ -443,11 +447,15 @@
# str(query) to avoid error if it's an unicode string
cursor.executemany(str(query), args)
except Exception, ex:
- self.critical("sql many: %r\n args: %s\ndbms message: %r",
- query, args, ex.args[0])
+ if self.repo.config.mode != 'test':
+ # during test we get those message when trying to alter sqlite
+ # db schema
+ self.critical("sql many: %r\n args: %s\ndbms message: %r",
+ query, args, ex.args[0])
try:
session.pool.connection(self.uri).rollback()
- self.critical('transaction has been rollbacked')
+ if self.repo.config.mode != 'test':
+ self.critical('transaction has been rollbacked')
except:
pass
raise
--- a/server/sources/rql2sql.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/sources/rql2sql.py Fri Feb 26 17:39:33 2010 +0100
@@ -38,7 +38,6 @@
from rql.nodes import (SortTerm, VariableRef, Constant, Function, Not,
Variable, ColumnAlias, Relation, SubQuery, Exists)
-from cubicweb import server
from cubicweb.server.sqlutils import SQL_PREFIX
from cubicweb.server.utils import cleanup_solutions
--- a/server/sources/storages.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/sources/storages.py Fri Feb 26 17:39:33 2010 +0100
@@ -62,7 +62,7 @@
else:
fpath = self.new_fs_path(entity, attr)
# bytes storage used to store file's path
- entity[attr]= Binary(fpath)
+ entity[attr] = Binary(fpath)
file(fpath, 'w').write(value.getvalue())
AddFileOp(entity._cw, filepath=fpath)
# else entity[attr] is expected to be an already existant file path
--- a/server/sqlutils.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/sqlutils.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,9 +9,7 @@
import os
import subprocess
-from os.path import exists
-from warnings import warn
-from datetime import datetime, date, timedelta
+from datetime import datetime, date
import logilab.common as lgc
from logilab.common import db
@@ -24,7 +22,6 @@
from cubicweb import Binary, ConfigurationError
from cubicweb.uilib import remove_html_tags
-from cubicweb.toolsutils import restrict_perms_to_user
from cubicweb.schema import PURE_VIRTUAL_RTYPES
from cubicweb.server import SQL_CONNECT_HOOKS
from cubicweb.server.utils import crypt_password
--- a/server/test/data/schema.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/test/data/schema.py Fri Feb 26 17:39:33 2010 +0100
@@ -137,7 +137,7 @@
class para(RelationType):
__permissions__ = {
'read': ('managers', 'users', 'guests'),
- 'update': ('managers', ERQLExpression('X in_state S, S name "todo"')),
+ 'update': ('managers', ERQLExpression('X in_state S, S name "todo"')),
}
class test(RelationType):
--- a/server/test/unittest_checkintegrity.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/test/unittest_checkintegrity.py Fri Feb 26 17:39:33 2010 +0100
@@ -20,7 +20,7 @@
sys.stderr = sys.stdout = StringIO()
try:
check(repo, cnx, ('entities', 'relations', 'text_index', 'metadata'),
- True, True)
+ reindex=True, fix=True, withpb=False)
finally:
sys.stderr = sys.__stderr__
sys.stdout = sys.__stdout__
--- a/server/test/unittest_migractions.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/test/unittest_migractions.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,26 +9,18 @@
from logilab.common.testlib import TestCase, unittest_main
from cubicweb import ConfigurationError
-from cubicweb.devtools.testlib import CubicWebTC, get_versions
+from cubicweb.devtools.testlib import CubicWebTC
from cubicweb.schema import CubicWebSchemaLoader
from cubicweb.server.sqlutils import SQL_PREFIX
-from cubicweb.server.repository import Repository
from cubicweb.server.migractions import *
-orig_get_versions = Repository.get_versions
-
-def setup_module(*args):
- Repository.get_versions = get_versions
-
-def teardown_module(*args):
- Repository.get_versions = orig_get_versions
-
class MigrationCommandsTC(CubicWebTC):
@classmethod
def init_config(cls, config):
super(MigrationCommandsTC, cls).init_config(config)
+ # we have to read schema from the database to get eid for schema entities
config._cubes = None
cls.repo.fill_schema()
cls.origschema = deepcopy(cls.repo.schema)
@@ -42,7 +34,7 @@
@classmethod
def _refresh_repo(cls):
super(MigrationCommandsTC, cls)._refresh_repo()
- cls.repo.schema = cls.vreg.schema = deepcopy(cls.origschema)
+ cls.repo.set_schema(deepcopy(cls.origschema), resetvreg=False)
def setUp(self):
CubicWebTC.setUp(self)
@@ -157,7 +149,7 @@
sorted(str(e) for e in self.schema.entities() if not e.final))
self.assertEquals(self.schema['filed_under2'].objects(), ('Folder2',))
eschema = self.schema.eschema('Folder2')
- for cstr in eschema.constraints('name'):
+ for cstr in eschema.rdef('name').constraints:
self.failUnless(hasattr(cstr, 'eid'))
def test_add_drop_entity_type(self):
@@ -281,7 +273,7 @@
def test_sync_schema_props_perms(self):
cursor = self.mh.session
cursor.set_pool()
- nbrqlexpr_start = len(cursor.execute('RQLExpression X'))
+ nbrqlexpr_start = cursor.execute('Any COUNT(X) WHERE X is RQLExpression')[0][0]
migrschema['titre'].rdefs[('Personne', 'String')].order = 7
migrschema['adel'].rdefs[('Personne', 'String')].order = 6
migrschema['ass'].rdefs[('Personne', 'String')].order = 5
@@ -345,19 +337,22 @@
self.assertEquals(len(self._erqlexpr_rset('delete', 'Affaire')), 1)
self.assertEquals(len(self._erqlexpr_rset('add', 'Affaire')), 1)
# no change for rqlexpr to add and delete concerne relation
- for rdef in self.schema['concerne'].rdefs.values():
- print rdef, rdef.permissions
self.assertEquals(len(self._rrqlexpr_rset('delete', 'concerne')), len(delete_concerne_rqlexpr))
self.assertEquals(len(self._rrqlexpr_rset('add', 'concerne')), len(add_concerne_rqlexpr))
# * migrschema involve:
- # * 8 deletion (2 in Affaire read + Societe + travaille + para rqlexprs)
+ # * 7 rqlexprs deletion (2 in (Affaire read + Societe + travaille) + 1
+ # in para attribute)
# * 1 update (Affaire update)
# * 2 new (Note add, ecrit_par add)
+ # * 2 implicit new for attributes update_permission (Note.para, Personne.test)
# remaining orphan rql expr which should be deleted at commit (composite relation)
- self.assertEquals(len(cursor.execute('RQLExpression X WHERE NOT ET1 read_permission X, NOT ET2 add_permission X, '
- 'NOT ET3 delete_permission X, NOT ET4 update_permission X')), 8+1)
+ self.assertEquals(cursor.execute('Any COUNT(X) WHERE X is RQLExpression, '
+ 'NOT ET1 read_permission X, NOT ET2 add_permission X, '
+ 'NOT ET3 delete_permission X, NOT ET4 update_permission X')[0][0],
+ 7+1)
# finally
- self.assertEquals(len(cursor.execute('RQLExpression X')), nbrqlexpr_start + 1 + 2)
+ self.assertEquals(cursor.execute('Any COUNT(X) WHERE X is RQLExpression')[0][0],
+ nbrqlexpr_start + 1 + 2 + 2)
self.mh.rollback()
--- a/server/test/unittest_msplanner.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/test/unittest_msplanner.py Fri Feb 26 17:39:33 2010 +0100
@@ -5,7 +5,6 @@
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
-from logilab.common.decorators import clear_cache
from cubicweb.devtools import init_test_database
from cubicweb.devtools.repotest import BasePlannerTC, test_plan
@@ -78,12 +77,12 @@
self.prevrqlexpr_affaire = affreadperms[-1]
# add access to type attribute so S can't be invariant
affreadperms[-1] = ERQLExpression('X concerne S?, S owned_by U, S type "X"')
- self.schema['Affaire'].permissions['read'] = tuple(affreadperms)
+ self.schema['Affaire'].set_action_permissions('read', affreadperms)
# hijack CWUser security
userreadperms = list(self.schema['CWUser'].permissions['read'])
self.prevrqlexpr_user = userreadperms[-1]
userreadperms[-1] = ERQLExpression('X owned_by U')
- self.schema['CWUser'].permissions['read'] = tuple(userreadperms)
+ self.schema['CWUser'].set_action_permissions('read', userreadperms)
self.add_source(FakeUserROSource, 'ldap')
self.add_source(FakeCardSource, 'cards')
@@ -96,9 +95,7 @@
def restore_orig_affaire_security(self):
affreadperms = list(self.schema['Affaire'].permissions['read'])
affreadperms[-1] = self.prevrqlexpr_affaire
- self.schema['Affaire'].permissions['read'] = tuple(affreadperms)
- clear_cache(self.schema['Affaire'], 'get_rqlexprs')
- #clear_cache(self.schema['Affaire'], 'get_groups')
+ self.schema['Affaire'].set_action_permissions('read', affreadperms)
def restore_orig_cwuser_security(self):
if hasattr(self, '_orig_cwuser_security_restored'):
@@ -106,9 +103,7 @@
self._orig_cwuser_security_restored = True
userreadperms = list(self.schema['CWUser'].permissions['read'])
userreadperms[-1] = self.prevrqlexpr_user
- self.schema['CWUser'].permissions['read'] = tuple(userreadperms)
- clear_cache(self.schema['CWUser'], 'get_rqlexprs')
- #clear_cache(self.schema['CWUser'], 'get_groups')
+ self.schema['CWUser'].set_action_permissions('read', userreadperms)
class PartPlanInformationTC(BaseMSPlannerTC):
@@ -762,7 +757,7 @@
def test_security_has_text(self):
# use a guest user
- self.session = self._user_session()[1]
+ self.session = self.user_groups_session('guests')
self._test('Any X WHERE X has_text "bla"',
[('FetchStep', [('Any E WHERE E type "X", E is Note', [{'E': 'Note'}])],
[self.cards, self.system], None, {'E': 'table0.C0'}, []),
@@ -789,7 +784,7 @@
def test_security_has_text_limit_offset(self):
# use a guest user
- self.session = self._user_session()[1]
+ self.session = self.user_groups_session('guests')
# note: same as the above query but because of the subquery usage, the display differs (not printing solutions for each union)
self._test('Any X LIMIT 10 OFFSET 10 WHERE X has_text "bla"',
[('FetchStep', [('Any E WHERE E type "X", E is Note', [{'E': 'Note'}])],
@@ -827,7 +822,7 @@
def test_security_user(self):
"""a guest user trying to see another user: EXISTS(X owned_by U) is automatically inserted"""
# use a guest user
- self.session = self._user_session()[1]
+ self.session = self.user_groups_session('guests')
self._test('Any X WHERE X login "bla"',
[('FetchStep',
[('Any X WHERE X login "bla", X is CWUser', [{'X': 'CWUser'}])],
@@ -838,7 +833,7 @@
def test_security_complex_has_text(self):
# use a guest user
- self.session = self._user_session()[1]
+ self.session = self.user_groups_session('guests')
self._test('Any X WHERE X has_text "bla", X firstname "bla"',
[('FetchStep', [('Any X WHERE X firstname "bla", X is CWUser', [{'X': 'CWUser'}])],
[self.ldap, self.system], None, {'X': 'table0.C0'}, []),
@@ -852,7 +847,7 @@
def test_security_complex_has_text_limit_offset(self):
# use a guest user
- self.session = self._user_session()[1]
+ self.session = self.user_groups_session('guests')
self._test('Any X LIMIT 10 OFFSET 10 WHERE X has_text "bla", X firstname "bla"',
[('FetchStep', [('Any X WHERE X firstname "bla", X is CWUser', [{'X': 'CWUser'}])],
[self.ldap, self.system], None, {'X': 'table1.C0'}, []),
@@ -869,7 +864,7 @@
def test_security_complex_aggregat(self):
# use a guest user
- self.session = self._user_session()[1]
+ self.session = self.user_groups_session('guests')
self._test('Any MAX(X)',
[('FetchStep', [('Any E WHERE E type "X", E is Note', [{'E': 'Note'}])],
[self.cards, self.system], None, {'E': 'table1.C0'}, []),
@@ -914,7 +909,7 @@
def test_security_complex_aggregat2(self):
# use a guest user
- self.session = self._user_session()[1]
+ self.session = self.user_groups_session('guests')
X_ET_ALL_SOLS = []
for s in X_ALL_SOLS:
ets = {'ET': 'CWEType'}
@@ -978,7 +973,7 @@
def test_security_3sources(self):
# use a guest user
- self.session = self._user_session()[1]
+ self.session = self.user_groups_session('guests')
self._test('Any X, XT WHERE X is Card, X owned_by U, X title XT, U login "syt"',
[('FetchStep',
[('Any X,XT WHERE X title XT, X is Card', [{'X': 'Card', 'XT': 'String'}])],
@@ -996,8 +991,7 @@
def test_security_3sources_identity(self):
self.restore_orig_cwuser_security()
# use a guest user
- self.session = self._user_session()[1]
- print self.session
+ self.session = self.user_groups_session('guests')
self._test('Any X, XT WHERE X is Card, X owned_by U, X title XT, U login "syt"',
[('FetchStep',
[('Any X,XT WHERE X title XT, X is Card', [{'X': 'Card', 'XT': 'String'}])],
@@ -1011,7 +1005,7 @@
def test_security_3sources_identity_optional_var(self):
self.restore_orig_cwuser_security()
# use a guest user
- self.session = self._user_session()[1]
+ self.session = self.user_groups_session('guests')
self._test('Any X,XT,U WHERE X is Card, X owned_by U?, X title XT, U login L',
[('FetchStep',
[('Any U,L WHERE U identity 5, U login L, U is CWUser',
@@ -1032,7 +1026,7 @@
def test_security_3sources_limit_offset(self):
# use a guest user
- self.session = self._user_session()[1]
+ self.session = self.user_groups_session('guests')
self._test('Any X, XT LIMIT 10 OFFSET 10 WHERE X is Card, X owned_by U, X title XT, U login "syt"',
[('FetchStep',
[('Any X,XT WHERE X title XT, X is Card', [{'X': 'Card', 'XT': 'String'}])],
@@ -1668,7 +1662,7 @@
])
def test_update3(self):
- anoneid = self._user_session()[1].user.eid
+ anoneid = self.user_groups_session('guests').user.eid
# since we are adding a in_state relation for an entity in the system
# source, states should only be searched in the system source as well
self._test('SET X in_state S WHERE X eid %(x)s, S name "deactivated"',
--- a/server/test/unittest_multisources.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/test/unittest_multisources.py Fri Feb 26 17:39:33 2010 +0100
@@ -30,13 +30,25 @@
repo2, cnx2 = init_test_database(config=ExternalSource1Configuration('data'))
repo3, cnx3 = init_test_database(config=ExternalSource2Configuration('data'))
-# XXX, access existing connection, no pyro connection
+# hi-jacking
from cubicweb.server.sources.pyrorql import PyroRQLSource
-PyroRQLSource.get_connection = lambda x: x.uri == 'extern-multi' and cnx3 or cnx2
-# necessary since the repository is closing its initial connections pool though
-# we want to keep cnx2 valid
from cubicweb.dbapi import Connection
-Connection.close = lambda x: None
+
+PyroRQLSource_get_connection = PyroRQLSource.get_connection
+Connection_close = Connection.close
+
+def setup_module(*args):
+ # hi-jack PyroRQLSource.get_connection to access existing connection (no
+ # pyro connection)
+ PyroRQLSource.get_connection = lambda x: x.uri == 'extern-multi' and cnx3 or cnx2
+ # also necessary since the repository is closing its initial connections
+ # pool though we want to keep cnx2 valid
+ Connection.close = lambda x: None
+
+def teardown_module(*args):
+ PyroRQLSource.get_connection = PyroRQLSource_get_connection
+ Connection.close = Connection_close
+
class TwoSourcesTC(CubicWebTC):
config = TwoSourcesConfiguration('data')
--- a/server/test/unittest_querier.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/test/unittest_querier.py Fri Feb 26 17:39:33 2010 +0100
@@ -78,7 +78,7 @@
def test_preprocess_security(self):
plan = self._prepare_plan('Any ETN,COUNT(X) GROUPBY ETN '
'WHERE X is ET, ET name ETN')
- plan.session = self._user_session(('users',))[1]
+ plan.session = self.user_groups_session('users')
union = plan.rqlst
plan.preprocess(union)
self.assertEquals(len(union.children), 1)
@@ -158,7 +158,7 @@
def test_preprocess_security_aggregat(self):
plan = self._prepare_plan('Any MAX(X)')
- plan.session = self._user_session(('users',))[1]
+ plan.session = self.user_groups_session('users')
union = plan.rqlst
plan.preprocess(union)
self.assertEquals(len(union.children), 1)
@@ -257,7 +257,7 @@
result, descr = rset.rows, rset.description
self.assertEquals(descr[0][0], 'String')
self.assertEquals(descr[0][1], 'Int')
- self.assertEquals(result[0][0], 'CWRelation')
+ self.assertEquals(result[0][0], 'RQLExpression') # XXX may change as schema evolve
def test_select_groupby_orderby(self):
rset = self.execute('Any N GROUPBY N ORDERBY N WHERE X is CWGroup, X name N')
@@ -928,7 +928,7 @@
self.assertEqual(len(rset.rows), 0, rset.rows)
def test_delete_3(self):
- u, s = self._user_session(('users',))
+ s = self.user_groups_session('users')
peid, = self.o.execute(s, "INSERT Personne P: P nom 'toto'")[0]
seid, = self.o.execute(s, "INSERT Societe S: S nom 'logilab'")[0]
self.o.execute(s, "SET P travaille S")
@@ -959,7 +959,7 @@
(using cachekey on sql generation returned always the same query for an eid,
whatever the relation)
"""
- u, s = self._user_session(('users',))
+ s = self.user_groups_session('users')
aeid, = self.o.execute(s, 'INSERT EmailAddress X: X address "toto@logilab.fr", X alias "hop"')[0]
# XXX would be nice if the rql below was enough...
#'INSERT Email X: X messageid "<1234>", X subject "test", X sender Y, X recipients Y'
--- a/server/test/unittest_repository.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/test/unittest_repository.py Fri Feb 26 17:39:33 2010 +0100
@@ -215,7 +215,7 @@
if not r.type in ('eid', 'is', 'is_instance_of', 'identity',
'creation_date', 'modification_date', 'cwuri',
'owned_by', 'created_by',
- 'add_permission', 'delete_permission', 'read_permission')],
+ 'update_permission', 'read_permission')],
['relation_type',
'from_entity', 'to_entity',
'in_basket', 'constrained_by',
--- a/server/test/unittest_schemaserial.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/test/unittest_schemaserial.py Fri Feb 26 17:39:33 2010 +0100
@@ -68,22 +68,17 @@
def test_rschema2rql2(self):
self.assertListEquals(list(rschema2rql(schema.rschema('add_permission'))),
[
- ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symmetric %(symmetric)s', {'description': u'core relation giving to a group the permission to add an entity or relation type', 'symmetric': False, 'name': u'add_permission', 'final': False, 'fulltext_container': None, 'inlined': False}),
-
- ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
- {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'CWGroup', 'ordernum': 3, 'cardinality': u'**', 'se': 'CWAttribute'}),
- ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
- {'rt': 'add_permission', 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'oe': 'RQLExpression', 'ordernum': 5, 'cardinality': u'*?', 'se': 'CWAttribute'}),
+ ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symmetric %(symmetric)s', {'description': u'', 'symmetric': False, 'name': u'add_permission', 'final': False, 'fulltext_container': None, 'inlined': False}),
('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
- {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'CWGroup', 'ordernum': 3, 'cardinality': u'**', 'se': 'CWEType'}),
+ {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'CWGroup', 'ordernum': 9999, 'cardinality': u'**', 'se': 'CWEType'}),
('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
- {'rt': 'add_permission', 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'oe': 'RQLExpression', 'ordernum': 5, 'cardinality': u'*?', 'se': 'CWEType'}),
+ {'rt': 'add_permission', 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'oe': 'RQLExpression', 'ordernum': 9999, 'cardinality': u'*?', 'se': 'CWEType'}),
('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
- {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'CWGroup', 'ordernum': 3, 'cardinality': u'**', 'se': 'CWRelation'}),
+ {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'CWGroup', 'ordernum': 9999, 'cardinality': u'**', 'se': 'CWRelation'}),
('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
- {'rt': 'add_permission', 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'oe': 'RQLExpression', 'ordernum': 5, 'cardinality': u'*?', 'se': 'CWRelation'}),
+ {'rt': 'add_permission', 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'oe': 'RQLExpression', 'ordernum': 9999, 'cardinality': u'*?', 'se': 'CWRelation'}),
])
def test_rschema2rql3(self):
@@ -133,8 +128,8 @@
expected = [
('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X name %(name)s,X symmetric %(symmetric)s WHERE X is CWRType, X name %(rt)s',
{'rt': 'add_permission', 'symmetric': False,
- 'description': u'core relation giving to a group the permission to add an entity or relation type',
- 'final': False, 'fulltext_container': None, 'inlined': False, 'name': u'add_permission'})
+ 'description': u'', 'final': False, 'fulltext_container': None,
+ 'inlined': False, 'name': u'add_permission'})
]
for i, (rql, args) in enumerate(updaterschema2rql(schema.rschema('add_permission'))):
yield self.assertEquals, (rql, args), expected[i]
@@ -172,10 +167,12 @@
[('SET X read_permission Y WHERE Y eid %(g)s, ', {'g': 0}),
('SET X read_permission Y WHERE Y eid %(g)s, ', {'g': 1}),
('SET X read_permission Y WHERE Y eid %(g)s, ', {'g': 2}),
- ('SET X add_permission Y WHERE Y eid %(g)s, ', {'g': 0}),
- ('SET X add_permission Y WHERE Y eid %(g)s, ', {'g': 1}),
- ('SET X delete_permission Y WHERE Y eid %(g)s, ', {'g': 0}),
- ('SET X delete_permission Y WHERE Y eid %(g)s, ', {'g': 1}),
+ ('SET X update_permission Y WHERE Y eid %(g)s, ', {'g': 0}),
+ ('INSERT RQLExpression E: '
+ 'E expression %(e)s, E exprtype %(t)s, E mainvars %(v)s, '
+ 'X update_permission E '
+ 'WHERE ', # completed by the outer function
+ {'e': u'U has_update_permission X', 't': u'ERQLExpression', 'v': u'X'}),
])
#def test_perms2rql(self):
--- a/server/test/unittest_security.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/test/unittest_security.py Fri Feb 26 17:39:33 2010 +0100
@@ -30,7 +30,7 @@
rqlst = self.repo.vreg.rqlhelper.parse(rql).children[0]
origgroups = self.schema['Personne'].get_groups('read')
self.schema['Personne'].set_action_permissions('read', ('users', 'managers'))
- self.repo.vreg.rqlhelper.compute_solutions(rqlst)
+ self.repo.vreg.solutions(self.session, rqlst, None)
solution = rqlst.solutions[0]
check_read_access(self.schema, self.session.user, rqlst, solution)
cnx = self.login('anon')
@@ -494,12 +494,13 @@
# needed to avoid check_perm error
session.set_pool()
# needed to remove rql expr granting update perm to the user
+ affaire_perms = self.schema['Affaire'].permissions.copy()
self.schema['Affaire'].set_action_permissions('update', self.schema['Affaire'].get_groups('update'))
- self.assertRaises(Unauthorized,
- self.schema['Affaire'].check_perm, session, 'update', eid=eid)
- cu = cnx.cursor()
- self.schema['Affaire'].set_action_permissions('read', ('users',))
try:
+ self.assertRaises(Unauthorized,
+ self.schema['Affaire'].check_perm, session, 'update', eid=eid)
+ cu = cnx.cursor()
+ self.schema['Affaire'].set_action_permissions('read', ('users',))
aff = cu.execute('Any X WHERE X ref "ARCT01"').get_entity(0, 0)
aff.fire_transition('abort')
cnx.commit()
@@ -510,7 +511,9 @@
# from the current state but Unauthorized if it exists but user can't pass it
self.assertRaises(ValidationError, user.fire_transition, 'deactivate')
finally:
- self.schema['Affaire'].set_action_permissions('read', ('managers',))
+ # restore orig perms
+ for action, perms in affaire_perms.iteritems():
+ self.schema['Affaire'].set_action_permissions(action, perms)
def test_trinfo_security(self):
aff = self.execute('INSERT Affaire X: X ref "ARCT01"').get_entity(0, 0)
--- a/server/utils.py Thu Feb 18 09:22:04 2010 +0100
+++ b/server/utils.py Fri Feb 26 17:39:33 2010 +0100
@@ -107,6 +107,9 @@
self.func = auto_restart_func
self.name = func.__name__
+ def __str__(self):
+ return '%s (%s seconds)' % (self.name, self.interval)
+
def start(self):
self._t = Timer(self.interval, self.func)
self._t.start()
--- a/setup.py Thu Feb 18 09:22:04 2010 +0100
+++ b/setup.py Fri Feb 26 17:39:33 2010 +0100
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-"""
+"""Generic Setup script, takes package info from __pkginfo__.py file
:organization: Logilab
:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
@@ -8,9 +8,6 @@
"""
# pylint: disable-msg=W0142,W0403,W0404,W0613,W0622,W0622,W0704,R0904,C0103,E0611
#
-# Copyright (c) 2003-2010 LOGILAB S.A. (Paris, FRANCE).
-# http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
@@ -23,7 +20,6 @@
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-""" Generic Setup script, takes package info from __pkginfo__.py file """
import os
import sys
--- a/skeleton/MANIFEST.in Thu Feb 18 09:22:04 2010 +0100
+++ b/skeleton/MANIFEST.in Fri Feb 26 17:39:33 2010 +0100
@@ -1,5 +1,4 @@
include *.py
-
+include */*.py
recursive-include data external_resources *.gif *.png *.css *.ico *.js
recursive-include i18n *.pot *.po
-recursive-include migration *.py
--- a/sobjects/notification.py Thu Feb 18 09:22:04 2010 +0100
+++ b/sobjects/notification.py Fri Feb 26 17:39:33 2010 +0100
@@ -11,7 +11,7 @@
from itertools import repeat
from logilab.common.textutils import normalize_text
-from logilab.common.deprecation import class_renamed, deprecated
+from logilab.common.deprecation import class_renamed, class_moved, deprecated
from cubicweb.selectors import yes
from cubicweb.view import Component
@@ -183,7 +183,6 @@
entity.eid, self.user_data['login'])
-from logilab.common.deprecation import class_renamed, class_moved, deprecated
from cubicweb.hooks.notification import RenderAndSendNotificationView
from cubicweb.mail import parse_message_id
--- a/sobjects/test/unittest_email.py Thu Feb 18 09:22:04 2010 +0100
+++ b/sobjects/test/unittest_email.py Fri Feb 26 17:39:33 2010 +0100
@@ -46,10 +46,10 @@
self.commit()
cnx = self.login('toto')
cu = cnx.cursor()
- cu.execute('SET U primary_email E WHERE E eid %(e)s, U login "toto"',
- {'e': email1})
- self.assertRaises(Unauthorized, cnx.commit)
-
+ self.assertRaises(Unauthorized,
+ cu.execute, 'SET U primary_email E WHERE E eid %(e)s, U login "toto"',
+ {'e': email1})
+ cnx.close()
if __name__ == '__main__':
from logilab.common.testlib import unittest_main
--- a/test/unittest_cwconfig.py Thu Feb 18 09:22:04 2010 +0100
+++ b/test/unittest_cwconfig.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,6 +9,7 @@
import os
from os.path import dirname, join, abspath
+from logilab.common.modutils import cleanup_sys_modules
from logilab.common.testlib import TestCase, unittest_main
from logilab.common.changelog import Version
@@ -21,8 +22,12 @@
return '/'.join(parts[i+1:])
raise Exception('duh? %s' % path)
+CUSTOM_CUBES_DIR = abspath(join(dirname(__file__), 'data', 'cubes'))
+
+
class CubicWebConfigurationTC(TestCase):
def setUp(self):
+ cleanup_sys_modules([CUSTOM_CUBES_DIR, ApptestConfiguration.CUBES_DIR])
self.config = ApptestConfiguration('data')
self.config._cubes = ('email', 'file')
@@ -86,16 +91,14 @@
# make sure we don't import the email cube, but the stdlib email package
import email
self.assertNotEquals(dirname(email.__file__), self.config.CUBES_DIR)
- os.environ['CW_CUBES_PATH'] = join(dirname(__file__), 'data', 'cubes')
+ os.environ['CW_CUBES_PATH'] = CUSTOM_CUBES_DIR
self.assertEquals(self.config.cubes_search_path(),
- [abspath(join(dirname(__file__), 'data', 'cubes')),
- self.config.CUBES_DIR])
- os.environ['CW_CUBES_PATH'] = '%s%s%s%s%s' % (join(dirname(__file__), 'data', 'cubes'),
- os.pathsep, self.config.CUBES_DIR,
- os.pathsep, 'unexistant')
+ [CUSTOM_CUBES_DIR, self.config.CUBES_DIR])
+ os.environ['CW_CUBES_PATH'] = os.pathsep.join([
+ CUSTOM_CUBES_DIR, self.config.CUBES_DIR, 'unexistant'])
# filter out unexistant and duplicates
self.assertEquals(self.config.cubes_search_path(),
- [abspath(join(dirname(__file__), 'data', 'cubes')),
+ [CUSTOM_CUBES_DIR,
self.config.CUBES_DIR])
self.failUnless('mycube' in self.config.available_cubes())
# test cubes python path
@@ -104,12 +107,12 @@
self.assertEquals(cubes.__path__, self.config.cubes_search_path())
# this import should succeed once path is adjusted
from cubes import mycube
- self.assertEquals(mycube.__path__, [abspath(join(dirname(__file__), 'data', 'cubes', 'mycube'))])
+ self.assertEquals(mycube.__path__, [join(CUSTOM_CUBES_DIR, 'mycube')])
# file cube should be overriden by the one found in data/cubes
sys.modules.pop('cubes.file', None)
del cubes.file
from cubes import file
- self.assertEquals(file.__path__, [abspath(join(dirname(__file__), 'data', 'cubes', 'file'))])
+ self.assertEquals(file.__path__, [join(CUSTOM_CUBES_DIR, 'file')])
if __name__ == '__main__':
--- a/test/unittest_entity.py Thu Feb 18 09:22:04 2010 +0100
+++ b/test/unittest_entity.py Fri Feb 26 17:39:33 2010 +0100
@@ -175,12 +175,14 @@
Personne.fetch_attrs = pfetch_attrs
Societe.fetch_attrs = sfetch_attrs
- def test_related_rql(self):
+ def test_related_rql_base(self):
Personne = self.vreg['etypes'].etype_class('Personne')
Note = self.vreg['etypes'].etype_class('Note')
+ SubNote = self.vreg['etypes'].etype_class('SubNote')
self.failUnless(issubclass(self.vreg['etypes'].etype_class('SubNote'), Note))
Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', 'type'))
Note.fetch_attrs, Note.fetch_order = fetch_config(('type',))
+ SubNote.fetch_attrs, SubNote.fetch_order = fetch_config(('type',))
p = self.request().create_entity('Personne', nom=u'pouet')
self.assertEquals(p.related_rql('evaluee'),
'Any X,AA,AB ORDERBY AA ASC WHERE E eid %(x)s, E evaluee X, '
@@ -188,20 +190,26 @@
Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', ))
# XXX
self.assertEquals(p.related_rql('evaluee'),
- 'Any X,AA ORDERBY Z DESC '
- 'WHERE X modification_date Z, E eid %(x)s, E evaluee X, '
- 'X modification_date AA')
+ 'Any X,AA ORDERBY AA DESC '
+ 'WHERE E eid %(x)s, E evaluee X, X modification_date AA')
tag = self.vreg['etypes'].etype_class('Tag')(self.request())
self.assertEquals(tag.related_rql('tags', 'subject'),
- 'Any X,AA ORDERBY Z DESC '
- 'WHERE X modification_date Z, E eid %(x)s, E tags X, '
- 'X modification_date AA')
+ 'Any X,AA ORDERBY AA DESC '
+ 'WHERE E eid %(x)s, E tags X, X modification_date AA')
self.assertEquals(tag.related_rql('tags', 'subject', ('Personne',)),
'Any X,AA,AB ORDERBY AA ASC '
'WHERE E eid %(x)s, E tags X, X is IN (Personne), X nom AA, '
'X modification_date AB')
+ def test_related_rql_ambigous_cant_use_fetch_order(self):
+ tag = self.vreg['etypes'].etype_class('Tag')(self.request())
+ for ttype in self.schema['tags'].objects():
+ self.vreg['etypes'].etype_class(ttype).fetch_attrs = ('modification_date',)
+ self.assertEquals(tag.related_rql('tags', 'subject'),
+ '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):
user = self.request().user
rql = user.unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
--- a/test/unittest_utils.py Thu Feb 18 09:22:04 2010 +0100
+++ b/test/unittest_utils.py Fri Feb 26 17:39:33 2010 +0100
@@ -6,14 +6,18 @@
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
-from logilab.common.testlib import TestCase, unittest_main
-
-import simplejson
+import re
import decimal
import datetime
-from cubicweb.utils import make_uid, UStringIO, SizeConstrainedList, CubicWebJsonEncoder
+from logilab.common.testlib import TestCase, unittest_main
+from cubicweb.utils import make_uid, UStringIO, SizeConstrainedList
+try:
+ import simplejson
+ from cubicweb.utils import CubicWebJsonEncoder
+except ImportError:
+ simplejson = None
class MakeUidTC(TestCase):
def test_1(self):
@@ -26,6 +30,9 @@
uid = make_uid('xyz')
if uid in d:
self.fail(len(d))
+ if re.match('\d', uid):
+ self.fail('make_uid must not return something begining with '
+ 'some numeric character, got %s' % uid)
d.add(uid)
@@ -53,6 +60,9 @@
yield self.assertEquals, l, expected
class JSONEncoerTests(TestCase):
+ def setUp(self):
+ if simplejson is None:
+ self.skip('simplejson not available')
def encode(self, value):
return simplejson.dumps(value, cls=CubicWebJsonEncoder)
--- a/toolsutils.py Thu Feb 18 09:22:04 2010 +0100
+++ b/toolsutils.py Fri Feb 26 17:39:33 2010 +0100
@@ -82,7 +82,7 @@
if askconfirm:
print
print diffs
- action = ASK.ask('Replace ?', ('N','y','q'), 'N')
+ action = ASK.ask('Replace ?', ('N', 'y', 'q'), 'N')
else:
action = 'y'
if action == 'y':
--- a/utils.py Thu Feb 18 09:22:04 2010 +0100
+++ b/utils.py Fri Feb 26 17:39:33 2010 +0100
@@ -7,27 +7,35 @@
"""
__docformat__ = "restructuredtext en"
-from logilab.mtconverter import xml_escape
-
-import locale
import sys
import decimal
import datetime
-from md5 import md5
-from time import time
-from random import randint, seed
+import random
+
+from logilab.mtconverter import xml_escape
+from logilab.common.deprecation import deprecated
# initialize random seed from current time
-seed()
+random.seed()
if sys.version_info[:2] < (2, 5):
+
+ from time import time
+ from md5 import md5
+ from random import randint
+
def make_uid(key):
"""forge a unique identifier
- not that unique on win32"""
- msg = str(key) + "%.10f" % time() + str(randint(0, 1000000))
- return md5(msg).hexdigest()
+ XXX not that unique on win32
+ """
+ key = str(key)
+ msg = key + "%.10f" % time() + str(randint(0, 1000000))
+ return key + md5(msg).hexdigest()
+
else:
+
from uuid import uuid4
+
def make_uid(key):
# remove dash, generated uid are used as identifier sometimes (sql table
# names at least)
@@ -328,3 +336,12 @@
# we never ever want to fail because of an unknown type,
# just return None in those cases.
return None
+
+from logilab.common import date
+_THIS_MOD_NS = globals()
+for funcname in ('date_range', 'todate', 'todatetime', 'datetime2ticks',
+ 'days_in_month', 'days_in_year', 'previous_month',
+ 'next_month', 'first_day', 'last_day', 'ustrftime',
+ 'strptime'):
+ msg = '[3.6] %s has been moved to logilab.common.date' % funcname
+ _THIS_MOD_NS[funcname] = deprecated(msg)(getattr(date, funcname))
--- a/view.py Thu Feb 18 09:22:04 2010 +0100
+++ b/view.py Fri Feb 26 17:39:33 2010 +0100
@@ -91,9 +91,9 @@
* the `category` attribute may be used in the interface to regroup related
objects together
- At instantiation time, the standard `req`, `rset`, and `cursor`
- attributes are added and the `w` attribute will be set at rendering
- time to a write function to use.
+ At instantiation time, the standard `_cw`, and `cw_rset` attributes are
+ added and the `w` attribute will be set at rendering time to a write
+ function to use.
"""
__registry__ = 'views'
--- a/vregistry.py Thu Feb 18 09:22:04 2010 +0100
+++ b/vregistry.py Fri Feb 26 17:39:33 2010 +0100
@@ -23,7 +23,7 @@
import sys
from os import listdir, stat
-from os.path import dirname, join, realpath, split, isdir, exists
+from os.path import dirname, join, realpath, isdir, exists
from logging import getLogger
from warnings import warn
--- a/web/action.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/action.py Fri Feb 26 17:39:33 2010 +0100
@@ -10,7 +10,7 @@
from cubicweb import target
from cubicweb.selectors import (partial_relation_possible, match_search_state,
- one_line_rset, yes)
+ one_line_rset)
from cubicweb.appobject import AppObject
--- a/web/application.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/application.py Fri Feb 26 17:39:33 2010 +0100
@@ -205,6 +205,8 @@
# reopening. Is it actually a problem?
self._update_last_login_time(req)
args = req.form
+ for forminternal_key in ('__form_id', '__domid', '__errorurl'):
+ args.pop(forminternal_key, None)
args['__message'] = req._('welcome %s !') % req.user.login
if 'vid' in req.form:
args['vid'] = req.form['vid']
@@ -379,7 +381,11 @@
'eidmap': req.data.get('eidmap', {})
}
req.set_session_data(req.form['__errorurl'], forminfo)
- raise Redirect(req.form['__errorurl'])
+ # XXX form session key / __error_url should be differentiated:
+ # session key is 'url + #<form dom id', though we usually don't want
+ # the browser to move to the form since it hides the global
+ # messages.
+ raise Redirect(req.form['__errorurl'].rsplit('#', 1)[0])
self.error_handler(req, ex, tb=False)
def error_handler(self, req, ex, tb=False):
@@ -389,6 +395,8 @@
req.remove_header('Etag')
req.message = None
req.reset_headers()
+ if req.json_request:
+ raise RemoteCallFailed(unicode(ex))
try:
req.data['ex'] = ex
if tb:
--- a/web/captcha.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/captcha.py Fri Feb 26 17:39:33 2010 +0100
@@ -38,7 +38,7 @@
draw = ImageDraw.Draw(img)
# draw 100 random colored boxes on the background
x, y = img.size
- for num in range(100):
+ for num in xrange(100):
draw.rectangle((randint(0, x), randint(0, y),
randint(0, x), randint(0, y)),
fill=randint(0, 0xffffff))
--- a/web/controller.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/controller.py Fri Feb 26 17:39:33 2010 +0100
@@ -1,4 +1,4 @@
-"""abstract controler classe for CubicWeb web client
+"""abstract controller classe for CubicWeb web client
:organization: Logilab
@@ -8,9 +8,6 @@
"""
__docformat__ = "restructuredtext en"
-import datetime
-
-from cubicweb import typed_eid
from cubicweb.selectors import yes
from cubicweb.appobject import AppObject
from cubicweb.web import LOGGER, Redirect, RequestError
@@ -124,6 +121,9 @@
else:
self._cw.set_message(self._cw._('entity deleted'))
+ def validate_cache(self, view):
+ view.set_http_cache_headers()
+ self._cw.validate_cache()
def reset(self):
"""reset form parameters and redirect to a view determinated by given
--- a/web/data/cubicweb.css Thu Feb 18 09:22:04 2010 +0100
+++ b/web/data/cubicweb.css Fri Feb 26 17:39:33 2010 +0100
@@ -392,7 +392,11 @@
color: #111100;
}
-
+ul.boxListing a.boxMenu:hover {
+ background: #eeedd9 url(puce_down.png) no-repeat scroll 98% 6px;
+ cursor:pointer;
+ border-top:medium none;
+ }
a.boxMenu {
background: transparent url("puce_down.png") 98% 6px no-repeat;
display: block;
--- a/web/data/cubicweb.edition.js Thu Feb 18 09:22:04 2010 +0100
+++ b/web/data/cubicweb.edition.js Fri Feb 26 17:39:33 2010 +0100
@@ -240,12 +240,13 @@
/*
* makes an AJAX request to get an inline-creation view's content
* @param peid : the parent entity eid
+ * @param petype : the parent entity type
* @param ttype : the target (inlined) entity type
* @param rtype : the relation type between both entities
*/
-function addInlineCreationForm(peid, ttype, rtype, role, i18nctx, insertBefore) {
+function addInlineCreationForm(peid, petype, ttype, rtype, role, i18nctx, insertBefore) {
insertBefore = insertBefore || getNode('add' + rtype + ':' + peid + 'link').parentNode;
- var d = asyncRemoteExec('inline_creation_form', peid, ttype, rtype, role, i18nctx);
+ var d = asyncRemoteExec('inline_creation_form', peid, petype, ttype, rtype, role, i18nctx);
d.addCallback(function (response) {
var dom = getDomFromResponse(response);
preprocessAjaxLoad(null, dom);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/data/cubicweb.iprogress.js Fri Feb 26 17:39:33 2010 +0100
@@ -0,0 +1,65 @@
+function ProgressBar() {
+ this.budget = 100;
+ this.todo = 100;
+ this.done = 100;
+ this.color_done = "green";
+ this.color_budget = "blue";
+ this.color_todo = "#cccccc"; // grey
+ this.height = 16;
+ this.middle = this.height/2;
+ this.radius = 4;
+}
+
+ProgressBar.prototype.draw_one_rect = function(ctx, pos, color, fill) {
+ ctx.beginPath();
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = color;
+ if (fill) {
+ ctx.fillStyle = color;
+ ctx.fillRect(0,0,pos,this.middle*2);
+ } else {
+ ctx.lineWidth = 2;
+ ctx.strokeStyle = "black";
+ ctx.moveTo(pos,0);
+ ctx.lineTo(pos,this.middle*2);
+ ctx.stroke();
+ }
+};
+
+ProgressBar.prototype.draw_one_circ = function(ctx, pos, color) {
+ ctx.beginPath();
+ ctx.lineWidth = 2;
+ ctx.strokeStyle = color;
+ ctx.moveTo(0,this.middle);
+ ctx.lineTo(pos,this.middle);
+ ctx.arc(pos,this.middle,this.radius,0,Math.PI*2,true);
+ ctx.stroke();
+};
+
+
+ProgressBar.prototype.draw_circ = function(ctx) {
+ this.draw_one_circ(ctx,this.budget,this.color_budget);
+ this.draw_one_circ(ctx,this.todo,this.color_todo);
+ this.draw_one_circ(ctx,this.done,this.color_done);
+};
+
+
+ProgressBar.prototype.draw_rect = function(ctx) {
+ this.draw_one_rect(ctx,this.todo,this.color_todo,true);
+ this.draw_one_rect(ctx,this.done,this.color_done,true);
+ this.draw_one_rect(ctx,this.budget,this.color_budget,false);
+};
+
+
+function draw_progressbar(cid, done, todo, budget, color) {
+ var canvas = document.getElementById(cid);
+ if (canvas.getContext) {
+ var ctx = canvas.getContext("2d");
+ var bar = new ProgressBar();
+ bar.budget = budget;
+ bar.todo = todo;
+ bar.done = done;
+ bar.color_done = color;
+ bar.draw_rect(ctx);
+ }
+}
--- a/web/facet.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/facet.py Fri Feb 26 17:39:33 2010 +0100
@@ -15,7 +15,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, ustrftime
+from logilab.common.date import datetime2ticks
from logilab.common.compat import all
from rql import parse, nodes
@@ -267,6 +267,7 @@
context = ''
needs_update = False
start_unfolded = True
+ cw_rset = None # ensure facets have a cw_rset attribute
def __init__(self, req, rqlst=None, filtered_variable=None,
**kwargs):
--- a/web/form.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/form.py Fri Feb 26 17:39:33 2010 +0100
@@ -14,8 +14,7 @@
from cubicweb.appobject import AppObject
from cubicweb.view import NOINDEX, NOFOLLOW
-from cubicweb import tags
-from cubicweb.web import stdmsgs, httpcache, formfields
+from cubicweb.web import httpcache, formfields, controller
class FormViewMixIn(object):
@@ -69,12 +68,43 @@
__metaclass__ = metafieldsform
__registry__ = 'forms'
+ internal_fields = ('__errorurl',) + controller.NAV_FORM_PARAMETERS
+
parent_form = None
force_session_key = None
+ domid = 'form'
+ copy_nav_params = False
- def __init__(self, req, rset, **kwargs):
- super(Form, self).__init__(req, rset=rset, **kwargs)
- self.restore_previous_post(self.session_key())
+ def __init__(self, req, rset=None, row=None, col=None,
+ submitmsg=None, mainform=True, **kwargs):
+ super(Form, self).__init__(req, rset=rset, row=row, col=col)
+ self.fields = list(self.__class__._fields_)
+ if mainform:
+ self.add_hidden(u'__form_id', kwargs.pop('formvid', self.__regid__))
+ for key, val in kwargs.iteritems():
+ if key in controller.NAV_FORM_PARAMETERS:
+ self.add_hidden(key, val)
+ elif key == 'redirect_path':
+ self.add_hidden(u'__redirectpath', val)
+ elif hasattr(self.__class__, key) and not key[0] == '_':
+ setattr(self, key, val)
+ else:
+ self.cw_extra_kwargs[key] = val
+ # skip other parameters, usually given for selection
+ # (else write a custom class to handle them)
+ if mainform:
+ self.add_hidden(u'__errorurl', self.session_key())
+ self.add_hidden(u'__domid', self.domid)
+ self.restore_previous_post(self.session_key())
+ # XXX why do we need two different variables (mainform and copy_nav_params ?)
+ if self.copy_nav_params:
+ for param in controller.NAV_FORM_PARAMETERS:
+ if not param in kwargs:
+ value = req.form.get(param)
+ if value:
+ self.add_hidden(param, value)
+ if submitmsg is not None:
+ self.add_hidden(u'__message', submitmsg)
@property
def root_form(self):
--- a/web/formfields.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/formfields.py Fri Feb 26 17:39:33 2010 +0100
@@ -91,7 +91,10 @@
optional fieldset to which this field belongs to
:order:
key used by automatic forms to sort fields
-
+ :ignore_req_params:
+ when true, this field won't consider value potentialy specified using
+ request's form parameters (eg you won't be able to specify a value using for
+ instance url like http://mywebsite.com/form?field=value)
"""
# default widget associated to this class of fields. May be overriden per
# instance
@@ -113,6 +116,7 @@
order = None
value = _MARKER
fallback_on_none_attribute = False
+ ignore_req_params = False
def __init__(self, name=None, label=_MARKER, widget=None, **kwargs):
for key, val in kwargs.items():
@@ -348,9 +352,9 @@
def process_form_value(self, form):
"""process posted form and return correctly typed value"""
try:
- return form.formvalues[self]
+ return form.formvalues[(self, form)]
except KeyError:
- value = form.formvalues[self] = self._process_form_value(form)
+ value = form.formvalues[(self, form)] = self._process_form_value(form)
return value
def _process_form_value(self, form):
@@ -413,12 +417,17 @@
class PasswordField(StringField):
widget = fw.PasswordInput
+ def form_init(self, form):
+ if self.eidparam and form.edited_entity.has_eid():
+ # see below: value is probably set but we can't retreive it. Ensure
+ # the field isn't show as a required field on modification
+ self.required = False
def typed_value(self, form, load_bytes=False):
if self.eidparam:
# no way to fetch actual password value with cw
if form.edited_entity.has_eid():
- return INTERNAL_FIELD_VALUE
+ return ''
return self.initial_typed_value(form, load_bytes)
return super(PasswordField, self).typed_value(form, load_bytes)
@@ -591,7 +600,7 @@
if data:
encoding = self.encoding(form)
try:
- form.formvalues[self] = unicode(data.getvalue(), encoding)
+ form.formvalues[(self, form)] = unicode(data.getvalue(), encoding)
except UnicodeError:
pass
else:
@@ -811,7 +820,7 @@
for field in form.root_form.fields_by_name('__linkto'):
if field.value in searchedvalues:
form.root_form.remove_field(field)
- form.formvalues[self] = value
+ form.formvalues[(self, form)] = value
def format_single_value(self, req, value):
return value
@@ -819,13 +828,13 @@
def process_form_value(self, form):
"""process posted form and return correctly typed value"""
try:
- return form.formvalues[self]
+ return form.formvalues[(self, form)]
except KeyError:
value = self._process_form_value(form)
# if value is None, there are some remaining pending fields, we'll
# have to recompute this later -> don't cache in formvalues
if value is not None:
- form.formvalues[self] = value
+ form.formvalues[(self, form)] = value
return value
def _process_form_value(self, form):
--- a/web/formwidgets.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/formwidgets.py Fri Feb 26 17:39:33 2010 +0100
@@ -59,21 +59,6 @@
def _render(self, form, field, renderer):
raise NotImplementedError()
- def typed_value(self, form, field):
- """return field's *typed* value specified in:
- 3. extra form values given to render()
- 4. field's typed value
- """
- qname = field.input_name(form)
- for key in (field, qname):
- try:
- return form.formvalues[key]
- except KeyError:
- continue
- if field.name != qname and field.name in form.formvalues:
- return form.formvalues[field.name]
- return field.typed_value(form)
-
def format_value(self, form, field, value):
return field.format_value(form._cw, value)
@@ -97,15 +82,20 @@
return self.values(form, field), attrs
def values(self, form, field):
- qname = field.input_name(form, self.suffix)
- if qname in form.form_previous_values:
- values = form.form_previous_values[qname]
- elif qname in form._cw.form:
- values = form._cw.form[qname]
- elif field.name != qname and field.name in form._cw.form:
- # compat: accept attr=value in req.form to specify value of attr-subject
- values = form._cw.form[field.name]
- else:
+ values = None
+ if not field.ignore_req_params:
+ qname = field.input_name(form, self.suffix)
+ # value from a previous post that has raised a validation error
+ if qname in form.form_previous_values:
+ values = form.form_previous_values[qname]
+ # value specified using form parameters
+ elif qname in form._cw.form:
+ values = form._cw.form[qname]
+ elif field.name != qname and field.name in form._cw.form:
+ # XXX compat: accept attr=value in req.form to specify value of
+ # attr-subject
+ values = form._cw.form[field.name]
+ if values is None:
values = self.typed_value(form, field)
if values != INTERNAL_FIELD_VALUE:
values = self.format_value(form, field, values)
@@ -113,6 +103,21 @@
values = (values,)
return values
+ def typed_value(self, form, field):
+ """return field's *typed* value specified in:
+ 3. extra form values given to render()
+ 4. field's typed value
+ """
+ qname = field.input_name(form)
+ for key in ((field, form), qname):
+ try:
+ return form.formvalues[key]
+ except KeyError:
+ continue
+ if field.name != qname and field.name in form.formvalues:
+ return form.formvalues[field.name]
+ return field.typed_value(form)
+
def process_field_data(self, form, field):
posted = form._cw.form
val = posted.get(field.input_name(form, self.suffix))
@@ -164,9 +169,9 @@
assert self.suffix is None, 'suffix not supported'
values, attrs = self.values_and_attributes(form, field)
assert len(values) == 1
- id = attrs.pop('id')
+ domid = attrs.pop('id')
inputs = [tags.input(name=field.input_name(form),
- value=values[0], type=self.type, id=id, **attrs),
+ value=values[0], type=self.type, id=domid, **attrs),
'<br/>',
tags.input(name=field.input_name(form, '-confirm'),
value=values[0], type=self.type, **attrs),
--- a/web/htmlwidgets.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/htmlwidgets.py Fri Feb 26 17:39:33 2010 +0100
@@ -15,7 +15,7 @@
from logilab.mtconverter import xml_escape
from cubicweb.utils import UStringIO
-from cubicweb.uilib import toggle_action, limitsize, htmlescape
+from cubicweb.uilib import toggle_action, htmlescape
from cubicweb.web import jsonize
# XXX HTMLWidgets should have access to req (for datadir / static urls,
@@ -280,7 +280,7 @@
if value is None:
return u''
elif isinstance(value, int):
- return u'%09d'%value
+ return u'%09d' % value
else:
return unicode(value)
@@ -316,7 +316,7 @@
self.w(u'<th %s>%s</th>' % (' '.join(attrs), column.name))
self.w(u'</tr>')
self.w(u'</thead><tbody>')
- for rowindex, row in enumerate(self.model.get_rows()):
+ for rowindex in xrange(len(self.model.get_rows())):
klass = (rowindex%2==1) and 'odd' or 'even'
self.w(u'<tr class="%s" %s>' % (klass, self.highlight))
for column, sortvalue in self.itercols(rowindex):
@@ -373,14 +373,10 @@
budget = self.budget
if budget == 0:
pourcent = 100
- todo_pourcent = 0
else:
pourcent = done*100./budget
- todo_pourcent = min(todo*100./budget, 100-pourcent)
- bar_pourcent = pourcent
if pourcent > 100.1:
color = 'red'
- bar_pourcent = 100
elif todo+done > self.red_threshold*budget:
color = 'red'
elif todo+done > self.orange_threshold*budget:
--- a/web/request.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/request.py Fri Feb 26 17:39:33 2010 +0100
@@ -135,7 +135,7 @@
self.user.properties['ui.language'])
self.set_language(lang)
return
- except KeyError, ex:
+ except KeyError:
pass
if vreg.config['language-negociation']:
# 2. http negociated language
@@ -671,7 +671,7 @@
try:
scorekey, scoreval = score.split('=')
if scorekey == 'q': # XXX 'level'
- score = float(score[2:]) # remove 'q='
+ score = float(scoreval)
except ValueError:
continue
values.append((score, value))
--- a/web/test/unittest_form.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/test/unittest_form.py Fri Feb 26 17:39:33 2010 +0100
@@ -207,9 +207,9 @@
upassword = PasswordField(eidparam=True, role='subject')
form = PFForm(self.req, redirect_path='perdu.com', entity=self.entity)
self.assertTextEquals(self._render_entity_field('upassword', form),
- '''<input id="upassword-subject:%(eid)s" name="upassword-subject:%(eid)s" tabindex="1" type="password" value="__cubicweb_internal_field__" />
+ '''<input id="upassword-subject:%(eid)s" name="upassword-subject:%(eid)s" tabindex="1" type="password" value="" />
<br/>
-<input name="upassword-subject-confirm:%(eid)s" tabindex="1" type="password" value="__cubicweb_internal_field__" />
+<input name="upassword-subject-confirm:%(eid)s" tabindex="1" type="password" value="" />
 
<span class="emphasis">confirm password</span>''' % {'eid': self.entity.eid})
--- a/web/test/unittest_pdf.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/test/unittest_pdf.py Fri Feb 26 17:39:33 2010 +0100
@@ -27,6 +27,8 @@
fopproc = sub(['/usr/bin/fop', foptmp.name, pdftmp.name])
fopproc.wait()
del foptmp
+ if fopproc.returncode:
+ self.skip('fop returned status %s' % fopproc.returncode)
pdftmp.seek(0) # a bit superstitious
reference = open(osp.join(DATADIR, 'sample1.pdf'), 'r').read()
output = pdftmp.read()
--- a/web/test/unittest_urlrewrite.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/test/unittest_urlrewrite.py Fri Feb 26 17:39:33 2010 +0100
@@ -39,6 +39,7 @@
('/notfound', dict(vid='404')),
('/error', dict(vid='error')),
('/sparql', dict(vid='sparql')),
+ ('/processinfo', dict(vid='processinfo')),
('/schema/([^/]+?)/?$', {'rql': r'Any X WHERE X is CWEType, X name "\1"', 'vid': 'primary'}),
('/add/([^/]+?)/?$' , dict(vid='creation', etype=r'\1')),
('/doc/images/(.+?)/?$', dict(fid='\\1', vid='wdocimages')),
--- a/web/test/unittest_views_editforms.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/test/unittest_views_editforms.py Fri Feb 26 17:39:33 2010 +0100
@@ -35,9 +35,6 @@
def test_cwuser_relations_by_category(self):
- #for (rtype, role, stype, otype), tag in AEF.rcategories._tagdefs.items():
- # if rtype == 'tags':
- # print rtype, role, stype, otype, ':', tag
e = self.vreg['etypes'].etype_class('CWUser')(self.request())
# see custom configuration in views.cwuser
self.assertEquals(rbc(e, 'main', 'attributes'),
@@ -63,7 +60,11 @@
('owned_by', 'subject'),
('bookmarked_by', 'object'),
])
- self.assertListEquals(rbc(e, 'main', 'relations'),
+ # XXX skip 'tags' relation here and in the hidden category because
+ # of some test interdependancy when pytest is launched on whole cw
+ # (appears here while expected in hidden
+ self.assertListEquals([x for x in rbc(e, 'main', 'relations')
+ if x != ('tags', 'object')],
[('primary_email', 'subject'),
('custom_workflow', 'subject'),
('connait', 'subject'),
@@ -73,10 +74,10 @@
[('use_email', 'subject'),
])
# owned_by is defined both as subject and object relations on CWUser
- self.assertListEquals(sorted(rbc(e, 'main', 'hidden')),
+ self.assertListEquals(sorted(x for x in rbc(e, 'main', 'hidden')
+ if x != ('tags', 'object')),
sorted([('has_text', 'subject'),
('identity', 'subject'),
- ('tags', 'object'),
('for_user', 'object'),
('created_by', 'object'),
('wf_info_for', 'object'),
@@ -166,14 +167,14 @@
geid = self.execute('CWGroup X LIMIT 1')[0][0]
rset = self.execute('CWUser X LIMIT 1')
self.view('inline-edition', rset, row=0, col=0, rtype='in_group',
- peid=geid, role='object', template=None, i18nctx='',
- pform=MOCKPFORM).source
+ peid=geid, role='object', i18nctx='', pform=MOCKPFORM,
+ template=None).source
def test_automatic_inline_creation_formview(self):
geid = self.execute('CWGroup X LIMIT 1')[0][0]
self.view('inline-creation', None, etype='CWUser', rtype='in_group',
- peid=geid, template=None, i18nctx='', role='object',
- pform=MOCKPFORM).source
+ peid=geid, petype='CWGroup', i18nctx='', role='object', pform=MOCKPFORM,
+ template=None)
MOCKPFORM = mock_object(form_previous_values={}, form_valerror=None)
--- a/web/test/unittest_viewselector.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/test/unittest_viewselector.py Fri Feb 26 17:39:33 2010 +0100
@@ -67,9 +67,8 @@
req = self.request()
self.assertListEqual(self.pviews(req, None),
[('changelog', wdoc.ChangeLogView),
- ('debug', debug.DebugView),
('index', startup.IndexView),
- ('info', management.ProcessInformationView),
+ ('info', debug.ProcessInformationView),
('manage', startup.ManageView),
('owl', owl.OWLView),
('propertiesform', cwproperties.CWPropertiesForm),
--- a/web/uicfg.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/uicfg.py Fri Feb 26 17:39:33 2010 +0100
@@ -112,7 +112,6 @@
from cubicweb.rtags import (RelationTags, RelationTagsBool, RelationTagsSet,
RelationTagsDict, register_rtag, _ensure_str_key)
from cubicweb.schema import META_RTYPES
-from cubicweb.web import formwidgets
# primary view configuration ##################################################
@@ -251,7 +250,7 @@
_allowed_form_types = ('main', 'inlined', 'muledit')
_allowed_values = {'main': ('attributes', 'inlined', 'relations',
'metadata', 'hidden'),
- 'inlined': ('attributes', 'hidden'),
+ 'inlined': ('attributes', 'inlined', 'hidden'),
'muledit': ('attributes', 'hidden'),
}
--- a/web/views/autoform.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/autoform.py Fri Feb 26 17:39:33 2010 +0100
@@ -169,13 +169,15 @@
:attr etype: the entity type being created in the inline form
"""
__regid__ = 'inline-creation'
- __select__ = (match_kwargs('peid', 'rtype')
+ __select__ = (match_kwargs('peid', 'petype', 'rtype')
& specified_etype_implements('Any'))
+ _select_attrs = InlineEntityEditionFormView._select_attrs + ('petype',)
@property
def removejs(self):
entity = self._entity()
- card = entity.e_schema.rdef(self.rtype, neg_role(self.role)).role_cardinality(self.role)
+ rdef = entity.e_schema.rdef(self.rtype, neg_role(self.role), self.petype)
+ card= rdef.role_cardinality(self.role)
# when one is adding an inline entity for a relation of a single card,
# the 'add a new xxx' link disappears. If the user then cancel the addition,
# we have to make this link appears back. This is done by giving add new link
@@ -207,7 +209,7 @@
:attr card: the cardinality of the relation according to role of `peid`
"""
__regid__ = 'inline-addnew-link'
- __select__ = (match_kwargs('peid', 'rtype')
+ __select__ = (match_kwargs('peid', 'petype', 'rtype')
& specified_etype_implements('Any'))
_select_attrs = InlineEntityCreationFormView._select_attrs + ('card',)
@@ -218,8 +220,8 @@
divid = "addNew%s%s%s:%s" % (self.etype, self.rtype, self.role, self.peid)
self.w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'
% divid)
- js = "addInlineCreationForm('%s', '%s', '%s', '%s', '%s')" % (
- self.peid, self.etype, self.rtype, self.role, i18nctx)
+ js = "addInlineCreationForm('%s', '%s', '%s', '%s', '%s', '%s')" % (
+ self.peid, self.petype, self.etype, self.rtype, self.role, i18nctx)
if self.pform.should_hide_add_new_relation_link(self.rtype, self.card):
js = "toggleVisibility('%s'); %s" % (divid, js)
__ = self._cw.pgettext
@@ -566,18 +568,18 @@
except f.FieldNotFound:
# meta attribute such as <attr>_format
continue
+ if self.fieldsets_in_order:
+ fsio = list(self.fieldsets_in_order)
+ else:
+ fsio = [None]
+ self.fieldsets_in_order = fsio
+ # add fields for relation whose target should have an inline form
+ for formview in self.inlined_form_views():
+ field = self._inlined_form_view_field(formview)
+ self.fields.append(field)
+ if not field.fieldset in fsio:
+ fsio.append(field.fieldset)
if self.formtype == 'main':
- if self.fieldsets_in_order:
- fsio = list(self.fieldsets_in_order)
- else:
- fsio = [None]
- self.fieldsets_in_order = fsio
- # add fields for relation whose target should have an inline form
- for formview in self.inlined_form_views():
- field = self._inlined_form_view_field(formview)
- self.fields.append(field)
- if not field.fieldset in fsio:
- fsio.append(field.fieldset)
# add the generic relation field if necessary
if entity.has_eid() and (
self.display_fields is None or
@@ -655,6 +657,8 @@
"""return a list of (relation schema, role) to edit for the entity"""
if self.display_fields is not None:
return self.display_fields
+ if self.edited_entity.has_eid() and not self.edited_entity.has_perm('update'):
+ return []
# XXX we should simply put eid in the generated section, no?
return [(rtype, role) for rtype, _, role in self._relations_by_section(
'attributes', 'update', strict) if rtype != 'eid']
@@ -706,8 +710,9 @@
if self.should_display_add_new_relation_link(rschema, formviews, card):
addnewlink = self._cw.vreg['views'].select(
'inline-addnew-link', self._cw,
- etype=ttype, rtype=rschema, role=role,
- peid=self.edited_entity.eid, pform=self, card=card)
+ etype=ttype, rtype=rschema, role=role, card=card,
+ peid=self.edited_entity.eid,
+ petype=self.edited_entity.e_schema, pform=self)
formviews.append(addnewlink)
allformviews += formviews
return allformviews
@@ -767,7 +772,9 @@
"""
yield self._cw.vreg['views'].select('inline-creation', self._cw,
etype=ttype, rtype=rschema, role=role,
- peid=self.edited_entity.eid, pform=self)
+ peid=self.edited_entity.eid,
+ petype=self.edited_entity.e_schema,
+ pform=self)
## default form ui configuration ##############################################
--- a/web/views/basecontrollers.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/basecontrollers.py Fri Feb 26 17:39:33 2010 +0100
@@ -172,7 +172,9 @@
def _validation_error(req, ex):
req.cnx.rollback()
- forminfo = req.get_session_data(req.form.get('__errorurl'), pop=True)
+ # XXX necessary to remove existant validation error?
+ # imo (syt), it's not necessary
+ req.get_session_data(req.form.get('__errorurl'), pop=True)
foreid = ex.entity
eidmap = req.data.get('eidmap', {})
for var, eid in eidmap.items():
@@ -351,6 +353,7 @@
except NoSelectableObject:
vid = req.form.get('fallbackvid', 'noresult')
view = self._cw.vreg['views'].select(vid, req, rset=rset)
+ self.validate_cache(view)
return self._call_view(view)
@xhtmlize
@@ -383,10 +386,10 @@
@check_pageid
@xhtmlize
- def js_inline_creation_form(self, peid, ttype, rtype, role, i18nctx):
+ def js_inline_creation_form(self, peid, petype, ttype, rtype, role, i18nctx):
view = self._cw.vreg['views'].select('inline-creation', self._cw,
- etype=ttype, peid=peid, rtype=rtype,
- role=role)
+ etype=ttype, rtype=rtype, role=role,
+ peid=peid, petype=petype)
return self._call_view(view, i18nctx=i18nctx)
@jsonize
@@ -399,7 +402,7 @@
@xhtmlize
def js_reledit_form(self):
- args = dict((x,self._cw.form[x])
+ args = dict((x, self._cw.form[x])
for x in frozenset(('rtype', 'role', 'reload', 'landing_zone')))
entity = self._cw.entity_from_eid(int(self._cw.form['eid']))
# note: default is reserved in js land
--- a/web/views/boxes.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/boxes.py Fri Feb 26 17:39:33 2010 +0100
@@ -24,7 +24,6 @@
from cubicweb.view import EntityView
from cubicweb.schema import display_name
from cubicweb.web.htmlwidgets import BoxWidget, BoxMenu, BoxHtml, RawBoxItem
-from cubicweb.web import uicfg
from cubicweb.web.box import BoxTemplate
--- a/web/views/cwuser.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/cwuser.py Fri Feb 26 17:39:33 2010 +0100
@@ -12,17 +12,16 @@
from cubicweb.selectors import one_line_rset, implements, match_user_groups
from cubicweb.view import EntityView
from cubicweb.web import action, uicfg
-from cubicweb.web.views import primary
-uicfg.primaryview_section.tag_attribute(('CWUser', 'login'), 'hidden')
-
-uicfg.primaryview_section.tag_attribute(('CWGroup', 'name'), 'hidden')
-uicfg.primaryview_section.tag_subject_of(('CWGroup', 'read_permission', '*'), 'relations')
-uicfg.primaryview_section.tag_subject_of(('CWGroup', 'add_permission', '*'), 'relations')
-uicfg.primaryview_section.tag_subject_of(('CWGroup', 'delete_permission', '*'), 'relations')
-uicfg.primaryview_section.tag_subject_of(('CWGroup', 'update_permission', '*'), 'relations')
-uicfg.primaryview_section.tag_object_of(('*', 'in_group', 'CWGroup'), 'relations')
-uicfg.primaryview_section.tag_object_of(('*', 'require_group', 'CWGroup'), 'relations')
+_pvs = uicfg.primaryview_section
+_pvs.tag_attribute(('CWUser', 'login'), 'hidden')
+_pvs.tag_attribute(('CWGroup', 'name'), 'hidden')
+_pvs.tag_subject_of(('CWGroup', 'read_permission', '*'), 'relations')
+_pvs.tag_subject_of(('CWGroup', 'add_permission', '*'), 'relations')
+_pvs.tag_subject_of(('CWGroup', 'delete_permission', '*'), 'relations')
+_pvs.tag_subject_of(('CWGroup', 'update_permission', '*'), 'relations')
+_pvs.tag_object_of(('*', 'in_group', 'CWGroup'), 'relations')
+_pvs.tag_object_of(('*', 'require_group', 'CWGroup'), 'relations')
class UserPreferencesEntityAction(action.Action):
__regid__ = 'prefs'
--- a/web/views/debug.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/debug.py Fri Feb 26 17:39:33 2010 +0100
@@ -25,41 +25,89 @@
w(u'</ul>')
-class DebugView(StartupView):
- __regid__ = 'debug'
+
+class ProcessInformationView(StartupView):
+ __regid__ = 'info'
__select__ = none_rset() & match_user_groups('managers')
- title = _('server debug information')
+
+ title = _('server information')
def call(self, **kwargs):
"""display server information"""
+ req = self._cw
+ dtformat = req.property_value('ui.datetime-format')
+ _ = req._
w = self.w
- w(u'<h1>server sessions</h1>')
- sessions = self._cw.cnx._repo._sessions.items()
+ # generic instance information
+ w(u'<h1>%s</h1>' % _('Instance'))
+ w(u'<table>')
+ w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
+ _('config type'), self._cw.vreg.config.name))
+ w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
+ _('config mode'), self._cw.vreg.config.mode))
+ w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
+ _('instance home'), self._cw.vreg.config.apphome))
+ w(u'</table>')
+ vcconf = req.vreg.config.vc_config()
+ w(u'<h3>%s</h3>' % _('versions configuration'))
+ w(u'<table>')
+ w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
+ 'CubicWeb', vcconf.get('cubicweb', _('no version information'))))
+ for cube in sorted(self._cw.vreg.config.cubes()):
+ cubeversion = vcconf.get(cube, _('no version information'))
+ w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
+ cube, cubeversion))
+ w(u'</table>')
+ # repository information
+ repo = req.vreg.config.repository(None)
+ w(u'<h1>%s</h1>' % _('Repository'))
+ w(u'<h3>%s</h3>' % _('resources usage'))
+ w(u'<table>')
+ stats = repo.stats()
+ for element in sorted(stats):
+ w(u'<tr><th align="left">%s</th><td>%s %s</td></tr>'
+ % (element, xml_escape(unicode(stats[element])),
+ element.endswith('percent') and '%' or '' ))
+ w(u'</table>')
+ if req.cnx._cnxtype == 'inmemory':
+ w(u'<h3>%s</h3>' % _('opened sessions'))
+ sessions = repo._sessions.values()
+ if sessions:
+ w(u'<ul>')
+ for session in sessions:
+ w(u'<li>%s (%s: %s)<br/>' % (
+ xml_escape(unicode(session)),
+ _('last usage'),
+ strftime(dtformat, localtime(session.timestamp))))
+ dict_to_html(w, session.data)
+ w(u'</li>')
+ w(u'</ul>')
+ else:
+ w(u'<p>%s</p>' % _('no repository sessions found'))
+ # web server information
+ w(u'<h1>%s</h1>' % _('Web server'))
+ w(u'<table>')
+ w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
+ _('base url'), req.base_url()))
+ w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
+ _('data directory url'), req.datadir_url))
+ w(u'</table>')
+ from cubicweb.web.application import SESSION_MANAGER
+ sessions = SESSION_MANAGER.current_sessions()
+ w(u'<h3>%s</h3>' % _('opened web sessions'))
if sessions:
w(u'<ul>')
- for sid, session in sessions:
- w(u'<li>%s (last usage: %s)<br/>' % (xml_escape(str(session)),
- strftime('%Y-%m-%d %H:%M:%S',
- localtime(session.timestamp))))
+ for session in sessions:
+ w(u'<li>%s (%s: %s)<br/>' % (
+ session.sessionid,
+ _('last usage'),
+ strftime(dtformat, localtime(session.last_usage_time))))
dict_to_html(w, session.data)
w(u'</li>')
w(u'</ul>')
else:
- w(u'<p>no server sessions found</p>')
- from cubicweb.web.application import SESSION_MANAGER
- w(u'<h1>web sessions</h1>')
- sessions = SESSION_MANAGER.current_sessions()
- if sessions:
- w(u'<ul>')
- for session in sessions:
- w(u'<li>%s (last usage: %s)<br/>' % (session.sessionid,
- strftime('%Y-%m-%d %H:%M:%S',
- localtime(session.last_usage_time))))
- dict_to_html(w, session.data)
- w(u'</li>')
- w(u'</ul>')
- else:
- w(u'<p>no web sessions found</p>')
+ w(u'<p>%s</p>' % _('no web sessions found'))
+
class RegistryView(StartupView):
@@ -74,7 +122,7 @@
self.w(u'<p>%s</p>\n' % ' - '.join('<a href="/_registry#%s">%s</a>'
% (key, key) for key in keys))
for key in keys:
- self.w(u'<h2><a name="%s">%s</a></h2>' % (key,key))
+ self.w(u'<h2><a name="%s">%s</a></h2>' % (key, key))
items = self._cw.vreg[key].items()
if items:
self.w(u'<table><tbody>')
--- a/web/views/editcontroller.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/editcontroller.py Fri Feb 26 17:39:33 2010 +0100
@@ -159,8 +159,9 @@
field = form.field_by_name(name, role, eschema=entity.e_schema)
else:
field = form.field_by_name(name, role)
- if field.has_been_modified(form):
- self.handle_formfield(form, field, rqlquery)
+ for field in field.actual_fields(form):
+ if field.has_been_modified(form):
+ self.handle_formfield(form, field, rqlquery)
if self.errors:
errors = dict((f.role_name(), unicode(ex)) for f, ex in self.errors)
raise ValidationError(entity.eid, errors)
--- a/web/views/editforms.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/editforms.py Fri Feb 26 17:39:33 2010 +0100
@@ -17,7 +17,7 @@
from logilab.common.decorators import cached
from cubicweb.selectors import (match_kwargs, one_line_rset, non_final_entity,
- specified_etype_implements, yes)
+ specified_etype_implements, implements, yes)
from cubicweb.view import EntityView
from cubicweb import tags
from cubicweb.web import uicfg, stdmsgs, eid_param, \
@@ -30,15 +30,13 @@
class DeleteConfForm(forms.CompositeForm):
__regid__ = 'deleteconf'
- __select__ = non_final_entity()
+ # XXX non_final_entity does not implement eclass_selector
+ __select__ = implements('Any')
domid = 'deleteconf'
copy_nav_params = True
form_buttons = [fw.Button(stdmsgs.BUTTON_DELETE, cwaction='delete'),
fw.Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
- @property
- def action(self):
- return self._cw.build_url('edit')
def __init__(self, *args, **kwargs):
super(DeleteConfForm, self).__init__(*args, **kwargs)
@@ -98,12 +96,10 @@
def render_form(self, entity):
"""fetch and render the form"""
self.form_title(entity)
- form = self._cw.vreg['forms'].select('edition', self._cw, rset=entity.cw_rset,
- row=entity.cw_row, col=entity.cw_col,
- entity=entity,
+ form = self._cw.vreg['forms'].select('edition', self._cw, entity=entity,
submitmsg=self.submited_message())
self.init_form(form, entity)
- self.w(form.render(formvid=u'edition'))
+ self.w(form.render())
def init_form(self, form, entity):
"""customize your form before rendering here"""
@@ -236,18 +232,18 @@
"""a view to edit multiple entities of the same type the first column
should be the eid
"""
- #self.form_title(entity)
- form = self._cw.vreg['forms'].select(self.__regid__, self._cw,
- rset=self.cw_rset,
- copy_nav_params=True)
# XXX overriding formvid (eg __form_id) necessary to make work edition:
# the edit controller try to select the form with no rset but
# entity=entity, and use this form to edit the entity. So we want
# edition form there but specifying formvid may have other undesired
- # side effect. Maybe we should provide another variable optinally
+ # side effect. Maybe we should provide another variable optionally
# telling which form the edit controller should select (eg difffers
# between html generation / post handling form)
- self.w(form.render(formvid='edition'))
+ form = self._cw.vreg['forms'].select(self.__regid__, self._cw,
+ rset=self.cw_rset,
+ copy_nav_params=True,
+ formvid='edition')
+ self.w(form.render())
# click and edit handling ('reledit') ##########################################
@@ -307,7 +303,7 @@
# compute value, checking perms, build form
if rschema.final:
form = self._build_form(entity, rtype, role, 'base', default, reload, lzone)
- if not self.should_edit_attribute(entity, rschema, role, form):
+ if not self.should_edit_attribute(entity, rschema, form):
self.w(entity.printable_value(rtype))
return
value = entity.printable_value(rtype) or default
@@ -330,14 +326,17 @@
self.relation_form(lzone, value, form,
self._build_renderer(entity, rtype, role))
- def should_edit_attribute(self, entity, rschema, role, form):
+ def should_edit_attribute(self, entity, rschema, form):
rtype = str(rschema)
- ttype = rschema.targets(entity.__regid__, role)[0]
- afs = uicfg.autoform_section.etype_get(entity.__regid__, rtype, role, ttype)
+ rdef = entity.e_schema.rdef(rtype)
+ afs = uicfg.autoform_section.etype_get(
+ entity.__regid__, rtype, 'subject', rdef.object)
if 'main_hidden' in afs or not entity.has_perm('update'):
return False
+ if not rdef.has_perm(self._cw, 'update', eid=entity.eid):
+ return False
try:
- form.field_by_name(rtype, role, entity.e_schema)
+ form.field_by_name(rtype, 'subject', entity.e_schema)
except FieldNotFound:
return False
return True
--- a/web/views/formrenderers.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/formrenderers.py Fri Feb 26 17:39:33 2010 +0100
@@ -85,10 +85,6 @@
if self.display_progress_div:
w(u'<div id="progress">%s</div>' % self._cw._('validating...'))
w(u'<fieldset>')
- w(tags.input(type=u'hidden', name=u'__form_id',
- value=values.get('formvid', form.__regid__)))
- if form.redirect_path:
- w(tags.input(type='hidden', name='__redirectpath', value=form.redirect_path))
self.render_fields(w, form, values)
self.render_buttons(w, form)
w(u'</fieldset>')
@@ -380,10 +376,6 @@
attrs_fs_label += '<div class="formBody">'
return attrs_fs_label + super(EntityFormRenderer, self).open_form(form, values)
- def _render_fields(self, fields, w, form):
- if not form.edited_entity.has_eid() or form.edited_entity.has_perm('update'):
- super(EntityFormRenderer, self)._render_fields(fields, w, form)
-
def render_buttons(self, w, form):
if len(form.form_buttons) == 3:
w("""<table width="100%%">
--- a/web/views/forms.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/forms.py Fri Feb 26 17:39:33 2010 +0100
@@ -15,9 +15,7 @@
from cubicweb import typed_eid
from cubicweb.selectors import non_final_entity, match_kwargs, one_line_rset
-from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param
from cubicweb.web import uicfg, form, formwidgets as fwdgs
-from cubicweb.web.controller import NAV_FORM_PARAMETERS
from cubicweb.web.formfields import StringField, relvoc_unrelated, guess_field
@@ -58,54 +56,20 @@
"""
__regid__ = 'base'
- internal_fields = ('__errorurl',) + NAV_FORM_PARAMETERS
# attributes overrideable by subclasses or through __init__
needs_js = ('cubicweb.ajax.js', 'cubicweb.edition.js',)
needs_css = ('cubicweb.form.css',)
- domid = 'form'
action = None
onsubmit = "return freezeFormButtons('%(domid)s');"
cssclass = None
cssstyle = None
cwtarget = None
redirect_path = None
- copy_nav_params = False
form_buttons = None
form_renderer_id = 'default'
fieldsets_in_order = None
- def __init__(self, req, rset=None, row=None, col=None,
- submitmsg=None, mainform=True,
- **kwargs):
- super(FieldsForm, self).__init__(req, rset=rset, row=row, col=col)
- self.fields = list(self.__class__._fields_)
- for key, val in kwargs.items():
- if key in NAV_FORM_PARAMETERS:
- self.add_hidden(key, val)
- elif hasattr(self.__class__, key) and not key[0] == '_':
- setattr(self, key, val)
- else:
- self.cw_extra_kwargs[key] = val
- # skip other parameters, usually given for selection
- # (else write a custom class to handle them)
- if mainform:
- self.add_hidden('__errorurl', self.session_key())
- self.add_hidden('__domid', self.domid)
- self.restore_previous_post(self.session_key())
-
- # XXX why do we need two different variables (mainform and copy_nav_params ?)
- if self.copy_nav_params:
- for param in NAV_FORM_PARAMETERS:
- if not param in kwargs:
- value = req.form.get(param)
- if value:
- self.add_hidden(param, value)
- if submitmsg is not None:
- self.add_hidden('__message', submitmsg)
- if 'domid' in kwargs:# session key changed
- self.restore_previous_post(self.session_key())
-
@property
def needs_multipart(self):
"""true if the form needs enctype=multipart/form-data"""
@@ -113,6 +77,7 @@
def add_hidden(self, name, value=None, **kwargs):
"""add an hidden field to the form"""
+ kwargs.setdefault('ignore_req_params', True)
kwargs.setdefault('widget', fwdgs.HiddenInput)
field = StringField(name=name, value=value, **kwargs)
if 'id' in kwargs:
@@ -145,7 +110,7 @@
def default_renderer(self):
return self._cw.vreg['formrenderers'].select(
self.form_renderer_id, self._cw,
- rset=self.cw_rset, row=self.cw_row, col=self.cw_col)
+ rset=self.cw_rset, row=self.cw_row, col=self.cw_col or 0)
formvalues = None
def build_context(self, formvalues=None):
@@ -178,6 +143,7 @@
renderer = self.default_renderer()
return renderer.render(self, values)
+
_AFF = uicfg.autoform_field
_AFF_KWARGS = uicfg.autoform_field_kwargs
@@ -248,6 +214,8 @@
# entity primary view
if self._cw.json_request and self.edited_entity.has_eid():
return '%s#%s' % (self.edited_entity.absolute_url(), self.domid)
+ # XXX we should not consider some url parameters that may lead to
+ # different url after a validation error
return '%s#%s' % (self._cw.url(), self.domid)
def build_context(self, formvalues=None):
@@ -258,8 +226,7 @@
for field in self.fields:
if field.eidparam:
edited.add(field.role_name())
- self.add_hidden('_cw_edited_fields', u','.join(edited),
- eidparam=True)
+ self.add_hidden('_cw_edited_fields', u','.join(edited), eidparam=True)
def default_renderer(self):
return self._cw.vreg['formrenderers'].select(
--- a/web/views/management.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/management.py Fri Feb 26 17:39:33 2010 +0100
@@ -12,7 +12,7 @@
from logilab.mtconverter import xml_escape
from cubicweb.selectors import yes, none_rset, match_user_groups, authenticated_user
-from cubicweb.view import AnyRsetView, StartupView, EntityView
+from cubicweb.view import AnyRsetView, StartupView, EntityView, View
from cubicweb.uilib import html_traceback, rest_traceback
from cubicweb.web import formwidgets as wdgs
from cubicweb.web.formfields import guess_field
@@ -273,63 +273,17 @@
return binfo
-class ProcessInformationView(StartupView):
- __regid__ = 'info'
+class CwStats(View):
+ """A textual stats output for monitoring tools such as munin """
+
+ __regid__ = 'processinfo'
+ content_type = 'text/txt'
+ templatable = False
__select__ = none_rset() & match_user_groups('users', 'managers')
- title = _('server information')
-
- def call(self, **kwargs):
- """display server information"""
- vcconf = self._cw.vreg.config.vc_config()
- req = self._cw
- _ = req._
- # display main information
- self.w(u'<h3>%s</h3>' % _('Application'))
- self.w(u'<table border="1">')
- self.w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
- 'CubicWeb', vcconf.get('cubicweb', _('no version information'))))
- for pkg in self._cw.vreg.config.cubes():
- pkgversion = vcconf.get(pkg, _('no version information'))
- self.w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
- pkg, pkgversion))
- self.w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
- _('home'), self._cw.vreg.config.apphome))
- self.w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
- _('base url'), req.base_url()))
- self.w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
- _('data directory url'), req.datadir_url))
- self.w(u'</table>')
- self.w(u'<br/>')
- # environment and request and server information
- try:
- # need to remove our adapter and then modpython-apache wrapper...
- env = req._areq._req.subprocess_env
- except AttributeError:
- return
- self.w(u'<h3>%s</h3>' % _('Environment'))
- self.w(u'<table border="1">')
- for attr in env.keys():
- self.w(u'<tr><th align="left">%s</th><td>%s</td></tr>'
- % (attr, xml_escape(env[attr])))
- self.w(u'</table>')
- self.w(u'<h3>%s</h3>' % _('Request'))
- self.w(u'<table border="1">')
- for attr in ('filename', 'form', 'hostname', 'main', 'method',
- 'path_info', 'protocol',
- 'search_state', 'the_request', 'unparsed_uri', 'uri'):
- val = getattr(req, attr)
- self.w(u'<tr><th align="left">%s</th><td>%s</td></tr>'
- % (attr, xml_escape(val)))
- self.w(u'</table>')
- server = req.server
- self.w(u'<h3>%s</h3>' % _('Server'))
- self.w(u'<table border="1">')
- for attr in dir(server):
- val = getattr(server, attr)
- if attr.startswith('_') or callable(val):
- continue
- self.w(u'<tr><th align="left">%s</th><td>%s</td></tr>'
- % (attr, xml_escape(val)))
- self.w(u'</table>')
-
+ def call(self):
+ stats = self._cw.vreg.config.repository(None).stats()
+ results = []
+ for element in stats:
+ results.append(u'%s %s' % (element, stats[element]))
+ self.w(u'\n'.join(results))
--- a/web/views/navigation.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/navigation.py Fri Feb 26 17:39:33 2010 +0100
@@ -59,7 +59,6 @@
nb_chars = 5
def display_func(self, rset, col, attrname):
- req = self._cw
if attrname is not None:
def index_display(row):
if not rset[row][col]: # outer join
@@ -159,7 +158,7 @@
context = 'navbottom'
order = 10
def call(self, view=None):
- entity = self.cw_rset.get_entity(0,0)
+ entity = self.cw_rset.get_entity(0, 0)
previous = entity.previous_entity()
next = entity.next_entity()
if previous or next:
--- a/web/views/old_calendar.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/old_calendar.py Fri Feb 26 17:39:33 2010 +0100
@@ -40,7 +40,6 @@
def nav_header(self, date, smallshift=3, bigshift=9):
"""prints shortcut links to go to previous/next steps (month|week)"""
- prev1 = next1 = prev2 = nex2 = date
prev1 = previous_month(date, smallshift)
next1 = next_month(date, smallshift)
prev2 = previous_month(date, bigshift)
@@ -110,7 +109,7 @@
self._cw.add_css('cubicweb.calendar.css')
schedule = {}
for row in xrange(len(self.cw_rset.rows)):
- entity = self.cw_rset.get_entity(row,0)
+ entity = self.cw_rset.get_entity(row, 0)
infos = u'<div class="event">'
infos += self._cw.view(itemvid, self.cw_rset, row=row)
infos += u'</div>'
@@ -137,11 +136,11 @@
end = last_day(next_month(day, shift))
return begin, end
- def _build_ampm_cells(self, daynum, events):
+ def _build_ampm_cells(self, events):
"""create a view without any hourly details.
- :param daynum: day of the built cell
- :param events: dictionnary with all events classified by hours"""
+ :param events: dictionnary with all events classified by hours
+ """
# split events according am/pm
am_events = [event for e_time, e_list in events.iteritems()
if 0 <= e_time.hour < 12
@@ -318,7 +317,7 @@
day = first_day + timedelta(daynum)
events = schedule.get(day)
if events:
- current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(daynum, events))
+ current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(events))
else:
current_row.append((AMPM_DAY % (daynum+1),
AMPM_EMPTY % ("amCell", "am"),
@@ -387,7 +386,7 @@
def format_day_events(self, day, events):
if events:
- self.w(u'\n'.join(self._build_ampm_cells(day, events)))
+ self.w(u'\n'.join(self._build_ampm_cells(events)))
else:
self.w(u'%s %s'% (AMPM_EMPTY % ("amCell", "am"),
AMPM_EMPTY % ("pmCell", "pm")))
@@ -409,7 +408,7 @@
day = first_day + timedelta(daynum)
events = schedule.get(day)
if events:
- current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(daynum, events))
+ current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(events))
else:
current_row.append((AMPM_DAY % (daynum+1),
AMPM_EMPTY % ("amCell", "am"),
--- a/web/views/plots.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/plots.py Fri Feb 26 17:39:33 2010 +0100
@@ -7,16 +7,12 @@
"""
__docformat__ = "restructuredtext en"
-import os
-import time
-
from simplejson import dumps
-from logilab.common import flatten
from logilab.common.date import datetime2ticks
from logilab.mtconverter import xml_escape
-from cubicweb.utils import make_uid, UStringIO
+from cubicweb.utils import UStringIO
from cubicweb.appobject import objectify_selector
from cubicweb.selectors import multi_columns_rset
from cubicweb.web.views import baseviews
@@ -98,7 +94,7 @@
# datetime labels on tooltips is to insert an additional column
# cf. function onPlotHover in cubicweb.flot.js
if self.timemode:
- plot = [(datetime2ticks(x), y, datetime2ticks(x)) for x,y in plot]
+ plot = [(datetime2ticks(x), y, datetime2ticks(x)) for x, y in plot]
return dumps(plot)
def _render(self, req, width=500, height=400):
--- a/web/views/sparql.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/sparql.py Fri Feb 26 17:39:33 2010 +0100
@@ -1,11 +1,11 @@
"""SPARQL integration
:organization: Logilab
-:copyright: 2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
+:copyright: 2009-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
"""
-v__docformat__ = "restructuredtext en"
+__docformat__ = "restructuredtext en"
import rql
from yams import xy
@@ -15,9 +15,9 @@
from cubicweb.view import StartupView, AnyRsetView
from cubicweb.web import Redirect, form, formfields, formwidgets as fwdgs
-from cubicweb.web.views import forms, urlrewrite
+from cubicweb.web.views import forms
try:
- from cubicweb.spa2rql import Sparql2rqlTranslator
+ from cubicweb.spa2rql import Sparql2rqlTranslator, UnsupportedQuery
except ImportError:
# fyzz not available (only a recommends)
Sparql2rqlTranslator = None
@@ -45,7 +45,7 @@
if sparql:
try:
qinfo = Sparql2rqlTranslator(self._cw.vreg.schema).translate(sparql)
- except rql.TypeResolverException, ex:
+ except rql.TypeResolverException:
self.w(self._cw._('can not resolve entity types:') + u' ' + unicode('ex'))
except UnsupportedQuery:
self.w(self._cw._('we are not yet ready to handle this query'))
--- a/web/views/tabs.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/tabs.py Fri Feb 26 17:39:33 2010 +0100
@@ -48,7 +48,10 @@
tabid or vid, xml_escape(self._cw.build_url('json', **urlparams))))
if show_spinbox:
w(u'<img src="data/loading.gif" id="%s-hole" alt="%s"/>'
- % (tabid or vid, self._cw._('loading')))
+ % (tabid or vid, self._cw._('(loading ...)')))
+ w(u'<noscript><p><a class="style: hidden" id="seo-%s" href="%s">%s</a></p></noscript>'
+ % (tabid or vid, xml_escape(self._cw.build_url(**urlparams)), xml_escape('%s (%s)') %
+ (tabid or vid, self._cw._('follow this link if javascript is deactivated'))))
w(u'</div>')
self._prepare_bindings(tabid or vid, reloadable)
--- a/web/views/treeview.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/treeview.py Fri Feb 26 17:39:33 2010 +0100
@@ -9,7 +9,6 @@
import simplejson as json
-from logilab.common.decorators import monkeypatch
from logilab.mtconverter import xml_escape
from cubicweb.utils import make_uid
from cubicweb.interfaces import ITree
@@ -115,7 +114,6 @@
def cell_call(self, row, col, vid='oneline', treeid=None, **morekwargs):
assert treeid is not None
- entity = self.cw_rset.get_entity(row, col)
itemview = self._cw.view(vid, self.cw_rset, row=row, col=col)
last_class = morekwargs['is_last'] and ' class="last"' or ''
self.w(u'<li%s>%s</li>' % (last_class, itemview))
@@ -177,8 +175,8 @@
if treeid.startswith('throw_away'):
divtail = ''
else:
- divtail = """ onclick="asyncRemoteExec('node_clicked', '%s', '%s')" """ %\
- (treeid, entity.eid)
+ divtail = """ onclick="asyncRemoteExec('node_clicked', '%s', '%s')" """ % (
+ treeid, entity.eid)
w(u'<div class="%s"%s></div>' % (u' '.join(divclasses), divtail))
# add empty <ul> because jquery's treeview plugin checks for
--- a/web/views/urlrewrite.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/urlrewrite.py Fri Feb 26 17:39:33 2010 +0100
@@ -83,6 +83,7 @@
('/notfound', dict(vid='404')),
('/error', dict(vid='error')),
('/sparql', dict(vid='sparql')),
+ ('/processinfo', dict(vid='processinfo')),
# XXX should be case insensitive as 'create', but I would like to find another way than
# relying on the etype_selector
(rgx('/schema/([^/]+?)/?'), dict(vid='primary', rql=r'Any X WHERE X is CWEType, X name "\1"')),
--- a/web/views/workflow.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/workflow.py Fri Feb 26 17:39:33 2010 +0100
@@ -69,7 +69,7 @@
entity.view('oneline')))
msg = self.req._('status will change from %(st1)s to %(st2)s') % {
'st1': entity.printable_state,
- 'st2': self._cw._(transition.destination().name)}
+ 'st2': self._cw._(transition.destination(entity).name)}
self.w(u'<p>%s</p>\n' % msg)
self.w(form.render())
@@ -318,7 +318,8 @@
for transition in self.entity.reverse_transition_of:
for incomingstate in transition.reverse_allowed_transition:
yield incomingstate.eid, transition.eid, transition
- yield transition.eid, transition.destination().eid, transition
+ for outgoingstate in transition.potential_destinations():
+ yield transition.eid, outgoingstate.eid, transition
class WorkflowImageView(TmpFileViewMixin, view.EntityView):
--- a/web/views/xbel.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/views/xbel.py Fri Feb 26 17:39:33 2010 +0100
@@ -26,8 +26,6 @@
def call(self):
"""display a list of entities by calling their <item_vid> view"""
- title = self.page_title()
- url = self._cw.build_url(rql=self._cw.form.get('rql', ''))
self.w(u'<?xml version="1.0" encoding="%s"?>\n' % self._cw.encoding)
self.w(u'<!DOCTYPE xbel PUBLIC "+//IDN python.org//DTD XML Bookmark Exchange Language 1.0//EN//XML" "http://www.python.org/topics/xml/dtds/xbel-1.0.dtd">')
self.w(u'<xbel version="1.0">')
--- a/web/webconfig.py Thu Feb 18 09:22:04 2010 +0100
+++ b/web/webconfig.py Fri Feb 26 17:39:33 2010 +0100
@@ -11,7 +11,6 @@
import os
from os.path import join, exists, split
-from logilab.common.configuration import Method
from logilab.common.decorators import cached
from cubicweb.toolsutils import read_config
--- a/wsgi/handler.py Thu Feb 18 09:22:04 2010 +0100
+++ b/wsgi/handler.py Fri Feb 26 17:39:33 2010 +0100
@@ -8,9 +8,9 @@
__docformat__ = "restructuredtext en"
-from cubicweb import ObjectNotFound, AuthenticationError
+from cubicweb import AuthenticationError
from cubicweb.web import (NotFound, Redirect, DirectResponse, StatusResponse,
- ExplicitLogin)
+ ExplicitLogin)
from cubicweb.web.application import CubicWebPublisher
from cubicweb.wsgi.request import CubicWebWsgiRequest