Commit 54d3e35f authored by Sylvain Thénault's avatar Sylvain Thénault
Browse files

backport stable branch (one more time painfully)

......@@ -10,7 +10,7 @@ modname = "cubicweb"
numversion = (3, 5, 5)
version = '.'.join(str(num) for num in numversion)
license = 'LGPL v2'
license = 'LGPL'
copyright = '''Copyright (c) 2003-2009 LOGILAB S.A. (Paris, FRANCE).
http://www.logilab.fr/ -- mailto:contact@logilab.fr'''
......
......@@ -180,7 +180,9 @@ class EmailableMixIn(object):
For instance, the Person class might want to return a `companyname`
key.
"""
return set(rs.type for rs, _ in cls.e_schema.attribute_definitions())
return set(rschema.type
for rschema, attrtype in cls.e_schema.attribute_definitions()
if attrtype.type not in ('Password', 'Bytes'))
def as_email_context(self):
"""returns the dictionary as used by the sendmail controller to
......
......@@ -18,6 +18,7 @@ class tag(object):
attrs.setdefault('escapecontent', self.escapecontent)
return simple_sgml_tag(self.name, __content, **attrs)
button = tag('button')
input = tag('input')
textarea = tag('textarea')
a = tag('a')
......@@ -31,6 +32,9 @@ h2 = tag('h2')
h3 = tag('h3')
h4 = tag('h4')
h5 = tag('h5')
tr = tag('tr')
th = tag('th')
td = tag('td')
def select(name, id=None, multiple=False, options=[], **attrs):
if multiple:
......
......@@ -499,6 +499,8 @@ class Connection(object):
DBAPIRequest.relative_path = fake
DBAPIRequest.url = fake
DBAPIRequest.next_tabindex = fake
DBAPIRequest.get_page_data = fake
DBAPIRequest.set_page_data = fake
DBAPIRequest.add_js = fake #cwrb.add_js.im_func
DBAPIRequest.add_css = fake #cwrb.add_css.im_func
# XXX could ask the repo for it's base-url configuration
......
......@@ -180,6 +180,8 @@ def _generate_schema_pot(w, vreg, schema, libconfig=None, cube=None):
tschema, tschema, rschema, eschema)
add_msg(w, label)
add_msg(w, label2)
# XXX also generate "creating ...' messages for actions in the
# addrelated submenu
w('# subject and object forms for each relation type\n')
w('# (no object form for final or symetric relation types)\n')
w('\n')
......
......@@ -9,10 +9,6 @@ When you run CubicWeb from source, either by downloading the tarball or
cloning the mercurial forest, here is the list of tools and libraries you need
to have installed in order for CubicWeb to work:
* mxDateTime - http://www.egenix.com/products/python/mxBase/mxDateTime/ - http://pypi.python.org/pypi/egenix-mx-base
* pyro - http://pyro.sourceforge.net/ - http://pypi.python.org/pypi/Pyro
* yapps - http://theory.stanford.edu/~amitp/yapps/ -
http://pypi.python.org/pypi/Yapps2
......@@ -22,6 +18,8 @@ to have installed in order for CubicWeb to work:
* simplejson - http://code.google.com/p/simplejson/ -
http://pypi.python.org/pypi/simplejson
* docsutils - http://docutils.sourceforge.net/ - http://pypi.python.org/pypi/docutils
* lxml - http://codespeak.net/lxml - http://pypi.python.org/pypi/lxml
* twisted - http://twistedmatrix.com/ - http://pypi.python.org/pypi/Twisted
......@@ -44,12 +42,17 @@ to have installed in order for CubicWeb to work:
* indexer - http://www.logilab.org/project/indexer -
http://pypi.python.org/pypi/indexer - included in the forest
To activate Sparql querying:
* fyzz - http://www.logilab.org/project/fyzz - http://pypi.python.org/pypi/fyzz
- included in the forest
* psycopg2 - http://initd.org/projects/psycopg2 - http://pypi.python.org/pypi/psycopg2
To use network communication between cubicweb instances / clients:
* docsutils - http://docutils.sourceforge.net/ - http://pypi.python.org/pypi/docutils
* Pyro - http://pyro.sourceforge.net/ - http://pypi.python.org/pypi/Pyro
If you're using a Postgres database (recommended):
* psycopg2 - http://initd.org/projects/psycopg2 - http://pypi.python.org/pypi/psycopg2
For the google-appengine extension to be available, you also need:
......@@ -59,5 +62,6 @@ For the google-appengine extension to be available, you also need:
* vobject - http://vobject.skyhouseconsulting.com/ -
http://pypi.python.org/pypi/vobject
Any help with the packaging of CubicWeb for more than Debian/Ubuntu (including
eggs, buildouts, etc) will be greatly appreciated.
......@@ -223,7 +223,7 @@ It is possible to define RQL expression to provide update permission
RQL expression for entity type permission :
* you have to use the class `RQLExpression`
* you have to use the class `ERQLExpression`
* the used expression corresponds to the WHERE statement of an RQL query
......@@ -240,16 +240,16 @@ RQL expression for entity type permission :
For RQL expressions on a relation type, the principles are the same except
for the following :
* you have to use the class `RQLExpression` in the case of a non-final relation
* you have to use the class `RRQLExpression` in the case of a non-final relation
* in the expression, the variables S, O and U are pre-defined references
to respectively the subject and the object of the current relation (on
which the action is being verified) and the user who executed the query
* we can also defined rights on attributes of an entity (non-final relation),
* we can also define rights over attributes of an entity (non-final relation),
knowing that :
- to define RQL expression, we have to use the class `RQLExpression`
- to define RQL expression, we have to use the class `ERQLExpression`
in which X represents the entity the attribute belongs to
- the permissions `add` and `delete` are equivalent. Only `add`/`read`
......
......@@ -44,38 +44,24 @@ class MetadataTC(BaseEntityTC):
{'description_format': ('format', 'description')})
class CWUserTC(BaseEntityTC):
def test_dc_title_and_name(self):
e = self.entity('CWUser U WHERE U login "member"')
self.assertEquals(e.dc_title(), 'member')
self.assertEquals(e.name(), 'member')
self.execute(u'SET X firstname "bouah" WHERE X is CWUser, X login "member"')
self.assertEquals(e.dc_title(), 'member')
self.assertEquals(e.name(), u'bouah')
self.execute(u'SET X surname "lôt" WHERE X is CWUser, X login "member"')
self.assertEquals(e.dc_title(), 'member')
self.assertEquals(e.name(), u'bouah lôt')
class EmailAddressTC(BaseEntityTC):
def test_canonical_form(self):
email1 = self.execute('INSERT EmailAddress X: X address "maarten.ter.huurne@philips.com"').get_entity(0, 0)
email2 = self.execute('INSERT EmailAddress X: X address "maarten@philips.com"').get_entity(0, 0)
email3 = self.execute('INSERT EmailAddress X: X address "toto@logilab.fr"').get_entity(0, 0)
self.execute('SET X prefered_form Y WHERE X eid %s, Y eid %s' % (email1.eid, email2.eid))
email1.set_relations(prefered_form=email2)
self.assertEquals(email1.prefered.eid, email2.eid)
self.assertEquals(email2.prefered.eid, email2.eid)
self.assertEquals(email3.prefered.eid, email3.eid)
def test_mangling(self):
eid = self.execute('INSERT EmailAddress X: X address "maarten.ter.huurne@philips.com"')[0][0]
email = self.entity('Any X WHERE X eid %(x)s', {'x':eid}, 'x')
email = self.execute('INSERT EmailAddress X: X address "maarten.ter.huurne@philips.com"').get_entity(0, 0)
self.assertEquals(email.display_address(), 'maarten.ter.huurne@philips.com')
self.assertEquals(email.printable_value('address'), 'maarten.ter.huurne@philips.com')
self.vreg.config.global_set_option('mangle-emails', True)
self.assertEquals(email.display_address(), 'maarten.ter.huurne at philips dot com')
self.assertEquals(email.printable_value('address'), 'maarten.ter.huurne at philips dot com')
eid = self.execute('INSERT EmailAddress X: X address "syt"')[0][0]
email = self.entity('Any X WHERE X eid %(x)s', {'x':eid}, 'x')
email = self.execute('INSERT EmailAddress X: X address "syt"').get_entity(0, 0)
self.assertEquals(email.display_address(), 'syt')
self.assertEquals(email.printable_value('address'), 'syt')
......@@ -93,6 +79,25 @@ class CWUserTC(BaseEntityTC):
self.failUnless(e.matching_groups(('xyz', 'managers')))
self.failIf(e.matching_groups(('xyz', 'abcd')))
def test_dc_title_and_name(self):
e = self.entity('CWUser U WHERE U login "member"')
self.assertEquals(e.dc_title(), 'member')
self.assertEquals(e.name(), 'member')
e.set_attributes(firstname=u'bouah')
self.assertEquals(e.dc_title(), 'member')
self.assertEquals(e.name(), u'bouah')
e.set_attributes(surname=u'lôt')
self.assertEquals(e.dc_title(), 'member')
self.assertEquals(e.name(), u'bouah lôt')
def test_allowed_massmail_keys(self):
e = self.entity('CWUser U WHERE U login "member"')
# Bytes/Password attributes should be omited
self.assertEquals(e.allowed_massmail_keys(),
set(('surname', 'firstname', 'login', 'last_login_time',
'creation_date', 'modification_date', 'cwuri', 'eid'))
)
class InterfaceTC(CubicWebTC):
......
......@@ -482,12 +482,21 @@ class WorkflowHooksTC(CubicWebTC):
# self.commit()
# test that the workflow is correctly enforced
def _cleanup_msg(self, msg):
"""remove the variable part of one specific error message"""
lmsg = msg.split()
lmsg.pop(1)
lmsg.pop()
return ' '.join(lmsg)
def test_transition_checking1(self):
cnx = self.login('stduser')
user = cnx.user(self.session)
ex = self.assertRaises(ValidationError,
user.fire_transition, 'activate')
self.assertEquals(ex.errors, {'by_transition': u"transition isn't allowed"})
self.assertEquals(self._cleanup_msg(ex.errors['by_transition']),
u"transition isn't allowed from")
cnx.close()
def test_transition_checking2(self):
......@@ -495,7 +504,8 @@ class WorkflowHooksTC(CubicWebTC):
user = cnx.user(self.session)
ex = self.assertRaises(ValidationError,
user.fire_transition, 'dummy')
self.assertEquals(ex.errors, {'by_transition': u"transition isn't allowed"})
self.assertEquals(self._cleanup_msg(ex.errors['by_transition']),
u"transition isn't allowed from")
cnx.close()
def test_transition_checking3(self):
......@@ -507,7 +517,8 @@ class WorkflowHooksTC(CubicWebTC):
session.set_pool()
ex = self.assertRaises(ValidationError,
user.fire_transition, 'deactivate')
self.assertEquals(ex.errors, {'by_transition': u"transition isn't allowed"})
self.assertEquals(self._cleanup_msg(ex.errors['by_transition']),
u"transition isn't allowed from")
# get back now
user.fire_transition('activate')
cnx.commit()
......
......@@ -34,7 +34,7 @@ _marker = object()
def greater_card(rschema, subjtypes, objtypes, index):
for subjtype in subjtypes:
for objtype in objtypes:
card = rschema.rproperty(subjtype, objtype, 'cardinality')[index]
card = rschema.rdef(subjtype, objtype).cardinality[index]
if card in '+*':
return card
return '1'
......@@ -144,7 +144,8 @@ class Entity(AppObject, dict):
cls.warning('skipping fetch_attr %s defined in %s (not found in schema)',
attr, cls.__regid__)
continue
if not user.matching_groups(rschema.get_groups('read')):
rdef = eschema.rdef(attr)
if not user.matching_groups(rdef.get_groups('read')):
continue
var = varmaker.next()
selection.append(var)
......@@ -153,7 +154,7 @@ class Entity(AppObject, dict):
if not rschema.final:
# XXX this does not handle several destination types
desttype = rschema.objects(eschema.type)[0]
card = rschema.rproperty(eschema, desttype, 'cardinality')[0]
card = rdef.cardinality[0]
if card not in '?1':
cls.warning('bad relation %s specified in fetch attrs for %s',
attr, cls)
......@@ -256,10 +257,10 @@ class Entity(AppObject, dict):
self._cw.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None)
def check_perm(self, action):
self.e_schema.check_perm(self._cw, action, self.eid)
self.e_schema.check_perm(self._cw, action, eid=self.eid)
def has_perm(self, action):
return self.e_schema.has_perm(self._cw, action, self.eid)
return self.e_schema.has_perm(self._cw, action, eid=self.eid)
def view(self, vid, __registry='views', **kwargs):
"""shortcut to apply a view on this entity"""
......@@ -339,11 +340,11 @@ class Entity(AppObject, dict):
return u''
if attrtype is None:
attrtype = self.e_schema.destination(attr)
props = self.e_schema.rproperties(attr)
props = self.e_schema.rdef(attr)
if attrtype == 'String':
# internalinalized *and* formatted string such as schema
# description...
if props.get('internationalizable'):
if props.internationalizable:
value = self._cw._(value)
attrformat = self.attr_metadata(attr, 'format')
if attrformat:
......@@ -391,11 +392,12 @@ class Entity(AppObject, dict):
if rschema.type in self.skip_copy_for:
continue
# skip composite relation
if self.e_schema.subjrproperty(rschema, 'composite'):
rdef = self.e_schema.rdef(rschema)
if rdef.composite:
continue
# skip relation with card in ?1 else we either change the copied
# object (inlined relation) or inserting some inconsistency
if self.e_schema.subjrproperty(rschema, 'cardinality')[1] in '?1':
if rdef.cardinality[1] in '?1':
continue
rql = 'SET X %s V WHERE X eid %%(x)s, Y eid %%(y)s, Y %s V' % (
rschema.type, rschema.type)
......@@ -405,14 +407,15 @@ class Entity(AppObject, dict):
if rschema.meta:
continue
# skip already defined relations
if getattr(self, 'reverse_%s' % rschema.type):
if self.related(rschema.type, 'object'):
continue
rdef = self.e_schema.rdef(rschema, 'object')
# skip composite relation
if self.e_schema.objrproperty(rschema, 'composite'):
if rdef.composite:
continue
# skip relation with card in ?1 else we either change the copied
# object (inlined relation) or inserting some inconsistency
if self.e_schema.objrproperty(rschema, 'cardinality')[0] in '?1':
if rdef.cardinality[0] in '?1':
continue
rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % (
rschema.type, rschema.type)
......@@ -433,15 +436,16 @@ class Entity(AppObject, dict):
for rschema in self.e_schema.subject_relations():
if rschema.final:
continue
if len(rschema.objects(self.e_schema)) > 1:
targets = rschema.objects(self.e_schema)
if len(targets) > 1:
# ambigous relations, the querier doesn't handle
# outer join correctly in this case
continue
if rschema.inlined:
matching_groups = self._cw.user.matching_groups
if matching_groups(rschema.get_groups('read')) and \
all(matching_groups(es.get_groups('read'))
for es in rschema.objects(self.e_schema)):
rdef = rschema.rdef(self.e_schema, targets[0])
if matching_groups(rdef.get_groups('read')) and \
all(matching_groups(e.get_groups('read')) for e in targets):
yield rschema, 'subject'
def to_complete_attributes(self, skip_bytes=True):
......@@ -453,7 +457,8 @@ class Entity(AppObject, dict):
if attr == 'eid':
continue
# password retreival is blocked at the repository server level
if not self._cw.user.matching_groups(rschema.get_groups('read')) \
rdef = rschema.rdef(self.e_schema, attrschema)
if not self._cw.user.matching_groups(rdef.get_groups('read')) \
or attrschema.type == 'Password':
self[attr] = None
continue
......@@ -489,24 +494,21 @@ class Entity(AppObject, dict):
if self.relation_cached(rtype, role):
continue
var = varmaker.next()
targettype = rschema.targets(self.e_schema, role)[0]
rdef = rschema.role_rdef(self.e_schema, targettype, role)
card = rdef.role_cardinality(role)
assert card in '1?', '%s %s %s %s' % (self.e_schema, rtype,
role, card)
if role == 'subject':
targettype = rschema.objects(self.e_schema)[0]
card = rschema.rproperty(self.e_schema, targettype,
'cardinality')[0]
if card == '1':
rql.append('%s %s %s' % (V, rtype, var))
else: # '?"
else:
rql.append('%s %s %s?' % (V, rtype, var))
else:
targettype = rschema.subjects(self.e_schema)[1]
card = rschema.rproperty(self.e_schema, targettype,
'cardinality')[1]
if card == '1':
rql.append('%s %s %s' % (var, rtype, V))
else: # '?"
else:
rql.append('%s? %s %s' % (var, rtype, V))
assert card in '1?', '%s %s %s %s' % (self.e_schema, rtype,
role, card)
selected.append(((rtype, role), var))
if selected:
# select V, we need it as the left most selected variable
......@@ -652,16 +654,16 @@ class Entity(AppObject, dict):
restriction = []
args = {}
securitycheck_args = {}
insertsecurity = (rtype.has_local_role('add') and not
rtype.has_perm(self._cw, 'add', **securitycheck_args))
constraints = rtype.rproperty(subjtype, objtype, 'constraints')
rdef = rtype.role_rdef(self.e_schema, targettype, role)
insertsecurity = (rdef.has_local_role('add') and not
rdef.has_perm(self._cw, 'add', **securitycheck_args))
if vocabconstraints:
# RQLConstraint is a subclass for RQLVocabularyConstraint, so they
# will be included as well
restriction += [cstr.restriction for cstr in constraints
restriction += [cstr.restriction for cstr in rdef.constraints
if isinstance(cstr, RQLVocabularyConstraint)]
else:
restriction += [cstr.restriction for cstr in constraints
restriction += [cstr.restriction for cstr in rdef.constraints
if isinstance(cstr, RQLConstraint)]
etypecls = self._cw.vreg['etypes'].etype_class(targettype)
rql = etypecls.fetch_rql(self._cw.user, restriction,
......@@ -671,12 +673,16 @@ class Entity(AppObject, dict):
before, after = rql.split(' WHERE ', 1)
rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after)
if insertsecurity:
rqlexprs = rtype.get_rqlexprs('add')
rqlexprs = rdef.get_rqlexprs('add')
rewriter = RQLRewriter(self._cw)
rqlst = self._cw.vreg.parse(self._cw, rql, args)
if not self.has_eid():
existant = searchedvar
else:
existant = None # instead of 'SO', improve perfs
for select in rqlst.children:
rewriter.rewrite(select, [((searchedvar, searchedvar), rqlexprs)],
select.solutions, args)
select.solutions, args, existant)
rql = rqlst.as_string()
return rql, args
......@@ -719,12 +725,10 @@ class Entity(AppObject, dict):
related = list(rset.entities(col))
rschema = self._cw.vreg.schema.rschema(rtype)
if role == 'subject':
rcard = rschema.rproperty(self.e_schema, related[0].e_schema,
'cardinality')[1]
rcard = rschema.rdef(self.e_schema, related[0].e_schema).cardinality[1]
target = 'object'
else:
rcard = rschema.rproperty(related[0].e_schema, self.e_schema,
'cardinality')[0]
rcard = rschema.rdef(related[0].e_schema, self.e_schema).cardinality[0]
target = 'subject'
if rcard in '?1':
for rentity in related:
......
......@@ -103,11 +103,13 @@ class CubicWebRootResource(resource.PostableResource):
assert self.base_url[-1] == '/'
self.https_url = config['https-url']
assert not self.https_url or self.https_url[-1] == '/'
# instantiate publisher here and not in init_publisher to get some
# checks done before daemonization (eg versions consistency)
self.appli = CubicWebPublisher(config, debug=self.debugmode)
self.versioned_datadir = 'data%s' % config.instance_md5_version()
def init_publisher(self):
config = self.config
self.appli = CubicWebPublisher(config, debug=self.debugmode)
self.versioned_datadir = 'data%s' % config.instance_md5_version()
# when we have an in-memory repository, clean unused sessions every XX
# seconds and properly shutdown the server
if config.repo_method == 'inmemory':
......@@ -382,14 +384,13 @@ def run(config, debug):
port = config['port'] or 8080
reactor.listenTCP(port, channel.HTTPFactory(website))
logger = getLogger('cubicweb.twisted')
logger.info('instance started on %s', root_resource.base_url)
if not debug:
print 'instance starting in the background'
if daemonize():
return # child process
if config['pid-file']:
# ensure the directory where the pid-file should be set exists (for
# instance /var/run/cubicweb may be deleted on computer restart)
# instance /var/run/cubicweb may be deleted on computer restart)
piddir = os.path.dirname(config['pid-file'])
if not os.path.exists(piddir):
os.makedirs(piddir)
......@@ -403,6 +404,7 @@ def run(config, debug):
uid = getpwnam(config['uid']).pw_uid
os.setuid(uid)
root_resource.start_service()
logger.info('instance started on %s', root_resource.base_url)
if config['profile']:
prof = hotshot.Profile(config['profile'])
prof.runcall(reactor.run)
......
......@@ -9,10 +9,14 @@
import sys
from cubicweb.toolsutils import CommandHandler
from cubicweb.web.webctl import WebCreateHandler
# trigger configuration registration
import cubicweb.etwist.twconfig # pylint: disable-msg=W0611
class TWCreateHandler(WebCreateHandler):
cfgname = 'twisted'
class TWStartHandler(CommandHandler):
cmdname = 'start'
cfgname = 'twisted'
......@@ -28,8 +32,8 @@ class TWStopHandler(CommandHandler):
try:
from cubicweb.server import serverctl
class AllInOneCreateHandler(serverctl.RepositoryCreateHandler):
class AllInOneCreateHandler(serverctl.RepositoryCreateHandler,
TWCreateHandler):
"""configuration to get an instance running in a twisted web server
integrating a repository server in the same process
"""
......@@ -38,6 +42,7 @@ try:
def bootstrap(self, cubes, inputlevel=0):
"""bootstrap this configuration"""
serverctl.RepositoryCreateHandler.bootstrap(self, cubes, inputlevel)
TWCreateHandler.bootstrap(self, cubes, inputlevel)
class AllInOneStartHandler(TWStartHandler):
cmdname = 'start'
......
......@@ -33,6 +33,8 @@ class _CheckRequiredRelationOperation(hook.LateOperation):
# recheck pending eids
if self.session.deleted_in_transaction(self.eid):
return
if self.rtype in self.session.transaction_data.get('pendingrtypes', ()):
return
if self.session.unsafe_execute(*self._rql()).rowcount < 1:
etype = self.session.describe(self.eid)[0]
_ = self.session._
......@@ -87,22 +89,16 @@ class CheckCardinalityHook(UserIntegrityHook):
def after_add_entity(self):
eid = self.entity.eid
eschema = self.entity.e_schema
for rschema, targetschemas, x in eschema.relation_definitions():
for rschema, targetschemas, role in eschema.relation_definitions():
# skip automatically handled relations
if rschema.type in DONT_CHECK_RTYPES_ON_ADD:
continue
if x == 'subject':
subjtype = eschema
objtype = targetschemas[0].type
cardindex = 0
if role == 'subject':
opcls = _CheckSRelationOp
else:
subjtype = targetschemas[0].type
objtype = eschema
cardindex = 1
opcls = _CheckORelationOp
card = rschema.rproperty(subjtype, objtype, 'cardinality')
if card[cardindex] in '1+':
rdef = rschema.role_rdef(eschema, targetschemas[0], role)
if rdef.role_cardinality(role) in '1+':
self.checkrel_if_necessary(opcls, rschema.type, eid)
def before_delete_relation(self):
......@@ -173,7 +169,7 @@ class CheckAttributeConstraintHook(UserIntegrityHook):
entity = self.entity
for attr in entity.edited_attributes:
if schema.rschema(attr).final:
constraints = [c for c in entity.e_schema.constraints(attr)
constraints = [c for c in entity.rdef(attr).constraints
if isinstance(c, RQLVocabularyConstraint)]
if constraints:
_CheckConstraintsOp(self._cw, constraints=constraints,
......
......@@ -25,10 +25,10 @@ def check_entity_attributes(session, entity):
for attr in editedattrs:
if attr in defaults:
continue
rschema = eschema.subjrels[attr]
if rschema.final: # non final relation are checked by other hooks
rdef = eschema.rdef(attr)
if rdef.final: # non final relation are checked by other hooks
# add/delete should be equivalent (XXX: unify them into 'update' ?)
rschema.check_perm(session, 'add', eid)
rdef.check_perm(session, 'add', eid=eid)
class _CheckEntityPermissionOp(hook.LateOperation):
......@@ -43,7 +43,10 @@ class _CheckEntityPermissionOp(hook.LateOperation):
class _CheckRelationPermissionOp(hook.LateOperation):
def precommit_event(self):
self.rschema.check_perm(self.session, self.action, self.eidfrom, self.eidto)
rdef = self.rschema.rdef(self.session.describe(self.eidfrom)[0],
self.session.describe(self.eidto)[0])
rdef.check_perm(self.session, self.action,
fromeid=self.eidfrom, toeid=self.eidto)
def commit_event(self):
pass
......@@ -95,7 +98,9 @@ class BeforeAddRelationSecurityHook(SecurityHook):
if (self.eidfrom, self.rtype, self.eidto) in nocheck:
return
rschema = self._cw.repo.schema[self.rtype]
rschema.check_perm(self._cw, 'add', self.eidfrom, self.eidto)
rdef = rschema.rdef(self._cw.describe(self.eidfrom)[0],
self._cw.describe(self.eidto)[0])
rdef.check_perm(session, 'add', fromeid=self.eidfrom, toeid=self.eidto)