[web/facet] Use an Exists node in HasRelationFacet
The generated RQL now includes an EXISTS clause. For instance, one gets:
::
DISTINCT Any WHERE X is CWUser, EXISTS(X in_group A)
instead of:
::
DISTINCT Any WHERE X is CWUser, X in_group A
for a HasRelationFacet with ``rtype = 'in_group'``, which is more appropriate
for testing the existence of the relation.
Add a test for this facet along the way.
# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
"""core CubicWeb schema, but not necessary at bootstrap time"""
__docformat__ = "restructuredtext en"
_ = unicode
from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
SubjectRelation,
String, TZDatetime, Datetime, Password, Interval,
Boolean)
from cubicweb.schema import (
RQLConstraint, WorkflowableEntityType, ERQLExpression, RRQLExpression,
PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, PUB_SYSTEM_ATTR_PERMS)
class CWUser(WorkflowableEntityType):
"""define a CubicWeb user"""
__permissions__ = {
'read': ('managers', 'users', ERQLExpression('X identity U')),
'add': ('managers',),
'delete': ('managers',),
'update': ('managers', ERQLExpression('X identity U, NOT U in_group G, G name "guests"'),),
}
login = String(required=True, unique=True, maxsize=64,
description=_('unique identifier used to connect to the application'))
upassword = Password(required=True) # password is a reserved word for mysql
firstname = String(maxsize=64)
surname = String(maxsize=64)
last_login_time = TZDatetime(description=_('last connection date'))
in_group = SubjectRelation('CWGroup', cardinality='+*',
constraints=[RQLConstraint('NOT O name "owners"')],
description=_('groups grant permissions to the user'))
class EmailAddress(EntityType):
"""an electronic mail address associated to a short alias"""
__permissions__ = {
# application that wishes public email, or use it for something else
# than users (eg Company, Person), should explicitly change permissions
'read': ('managers', ERQLExpression('U use_email X')),
'add': ('managers', 'users',),
'delete': ('managers', 'owners', ERQLExpression('P use_email X, U has_update_permission P')),
'update': ('managers', 'owners', ERQLExpression('P use_email X, U has_update_permission P')),
}
alias = String(fulltextindexed=True, maxsize=56)
address = String(required=True, fulltextindexed=True,
indexed=True, unique=True, maxsize=128)
prefered_form = SubjectRelation('EmailAddress', cardinality='?*',
description=_('when multiple addresses are equivalent \
(such as python-projects@logilab.org and python-projects@lists.logilab.org), set this \
to indicate which is the preferred form.'))
class use_email(RelationType):
fulltext_container = 'subject'
class use_email_relation(RelationDefinition):
"""user's email account"""
name = "use_email"
__permissions__ = {
'read': ('managers', 'users', 'guests',),
'add': ('managers', RRQLExpression('U has_update_permission S'),),
'delete': ('managers', RRQLExpression('U has_update_permission S'),),
}
subject = "CWUser"
object = "EmailAddress"
cardinality = '*?'
composite = 'subject'
class primary_email(RelationDefinition):
"""the prefered email"""
__permissions__ = {
'read': ('managers', 'users', 'guests',),
'add': ('managers', RRQLExpression('U has_update_permission S'),),
'delete': ('managers', RRQLExpression('U has_update_permission S'),),
}
subject = "CWUser"
object = "EmailAddress"
cardinality = '??'
constraints= [RQLConstraint('S use_email O')]
class prefered_form(RelationType):
__permissions__ = {
'read': ('managers', 'users', 'guests',),
# XXX should have update __permissions__ on both subject and object,
# though by doing this we will probably have no way to add
# this relation in the web ui. The easiest way to acheive this
# is probably to be able to have "U has_update_permission O" as
# RQLConstraint of the relation definition, though this is not yet
# possible
'add': ('managers', RRQLExpression('U has_update_permission S'),),
'delete': ('managers', RRQLExpression('U has_update_permission S'),),
}
class in_group(RelationType):
"""core relation indicating a user's groups"""
__permissions__ = PUB_SYSTEM_REL_PERMS
class owned_by(RelationType):
"""core relation indicating owners of an entity. This relation
implicitly put the owner into the owners group for the entity
"""
__permissions__ = {
'read': ('managers', 'users', 'guests'),
'add': ('managers', RRQLExpression('S owned_by U'),),
'delete': ('managers', RRQLExpression('S owned_by U'),),
}
# 0..n cardinality for entities created by internal session (no attached user)
# and to support later deletion of a user which has created some entities
cardinality = '**'
subject = '*'
object = 'CWUser'
class created_by(RelationType):
"""core relation indicating the original creator of an entity"""
__permissions__ = {
'read': ('managers', 'users', 'guests'),
'add': ('managers',),
'delete': ('managers',),
}
# 0..1 cardinality for entities created by internal session (no attached user)
# and to support later deletion of a user which has created some entities
cardinality = '?*'
subject = '*'
object = 'CWUser'
class creation_date(RelationType):
"""creation time of an entity"""
__permissions__ = PUB_SYSTEM_ATTR_PERMS
cardinality = '11'
subject = '*'
object = 'Datetime'
class modification_date(RelationType):
"""latest modification time of an entity"""
__permissions__ = PUB_SYSTEM_ATTR_PERMS
cardinality = '11'
subject = '*'
object = 'Datetime'
class cwuri(RelationType):
"""internal entity uri"""
__permissions__ = PUB_SYSTEM_ATTR_PERMS
cardinality = '11'
subject = '*'
object = 'String'
# XXX find a better relation name
class for_user(RelationType):
"""link a property to the user which want this property customization. Unless
you're a site manager, this relation will be handled automatically.
"""
__permissions__ = {
'read': ('managers', 'users', 'guests'),
'add': ('managers',),
'delete': ('managers',),
}
inlined = True
subject = 'CWProperty'
object = 'CWUser'
composite = 'object'
cardinality = '?*'
class ExternalUri(EntityType):
"""a URI representing an object in external data store"""
uri = String(required=True, unique=True, maxsize=256,
description=_('the URI of the object'))
class same_as(RelationType):
"""generic relation to specify that an external entity represent the same
object as a local one:
http://www.w3.org/TR/owl-ref/#sameAs-def
"""
#NOTE: You'll have to explicitly declare which entity types can have a
#same_as relation
__permissions__ = {
'read': ('managers', 'users', 'guests',),
'add': ('managers', 'users'),
'delete': ('managers', 'owners'),
}
cardinality = '**'
symmetric = True
# NOTE: the 'object = ExternalUri' declaration will still be mandatory
# in the cube's schema.
object = 'ExternalUri'
class CWCache(EntityType):
"""a simple cache entity characterized by a name and
a validity date.
The target application is responsible for updating timestamp
when necessary to invalidate the cache (typically in hooks).
Also, checkout the AppObject.get_cache() method.
"""
# XXX only handle by hooks, shouldn't be readable/editable at all through
# the ui and so no permissions should be granted, no?
__permissions__ = {
'read': ('managers', 'users', 'guests'),
'add': ('managers',),
'update': ('managers', 'users',), # XXX
'delete': ('managers',),
}
name = String(required=True, unique=True, maxsize=128,
description=_('name of the cache'))
timestamp = TZDatetime(default='NOW')
class CWSource(EntityType):
__permissions__ = {
'read': ('managers', 'users', 'guests'),
'add': ('managers',),
'update': ('managers',),
'delete': ('managers',),
}
name = String(required=True, unique=True, maxsize=128,
description=_('name of the source'))
type = String(required=True, maxsize=20, description=_('type of the source'))
config = String(description=_('source\'s configuration. One key=value per '
'line, authorized keys depending on the '
'source\'s type'),
__permissions__={
'read': ('managers',),
'add': ('managers',),
'update': ('managers',),
})
# put this here and not in a subclass even if it's only for some sources
# since having subclasses on generic relation (cw_source) double the number
# of rdef in the schema, and make ms planning harder since queries solutions
# may changes when sources are specified
url = String(description=_('URLs from which content will be imported. You can put one url per line'))
parser = String(description=_('parser to use to extract entities from content retrieved at given URLs.'))
latest_retrieval = TZDatetime(description=_('latest synchronization time'))
in_synchronization = TZDatetime(description=_('start timestamp of the currently in synchronization, or NULL when no synchronization in progress.'))
ENTITY_MANAGERS_PERMISSIONS = {
'read': ('managers',),
'add': ('managers',),
'update': ('managers',),
'delete': ('managers',),
}
RELATION_MANAGERS_PERMISSIONS = {
'read': ('managers',),
'add': ('managers',),
'delete': ('managers',),
}
class CWSourceHostConfig(EntityType):
__permissions__ = ENTITY_MANAGERS_PERMISSIONS
__unique_together__ = [('match_host', 'cw_host_config_of')]
match_host = String(required=True, maxsize=128,
description=_('regexp matching host(s) to which this config applies'))
config = String(required=True,
description=_('Source\'s configuration for a particular host. '
'One key=value per line, authorized keys '
'depending on the source\'s type, overriding '
'values defined on the source.'),
__permissions__={
'read': ('managers',),
'add': ('managers',),
'update': ('managers',),
})
class cw_host_config_of(RelationDefinition):
__permissions__ = RELATION_MANAGERS_PERMISSIONS
subject = 'CWSourceHostConfig'
object = 'CWSource'
cardinality = '1*'
composite = 'object'
inlined = True
class cw_source(RelationDefinition):
__permissions__ = {
'read': ('managers', 'users', 'guests'),
'add': ('managers',),
'delete': ('managers',),
}
subject = '*'
object = 'CWSource'
cardinality = '1*'
composite = 'object'
class CWDataImport(EntityType):
__permissions__ = ENTITY_MANAGERS_PERMISSIONS
start_timestamp = TZDatetime()
end_timestamp = TZDatetime()
log = String()
status = String(required=True, internationalizable=True, indexed=True,
default='in progress',
vocabulary=[_('in progress'), _('success'), _('failed')])
class cw_import_of(RelationDefinition):
__permissions__ = RELATION_MANAGERS_PERMISSIONS
subject = 'CWDataImport'
object = 'CWSource'
cardinality = '1*'
composite = 'object'
class CWSourceSchemaConfig(EntityType):
__permissions__ = ENTITY_MANAGERS_PERMISSIONS
cw_for_source = SubjectRelation(
'CWSource', inlined=True, cardinality='1*', composite='object',
__permissions__=RELATION_MANAGERS_PERMISSIONS)
options = String(description=_('allowed options depends on the source type'))
class rtype_cw_schema(RelationDefinition):
__permissions__ = RELATION_MANAGERS_PERMISSIONS
name = 'cw_schema'
subject = 'CWSourceSchemaConfig'
object = ('CWEType', 'CWRType')
inlined = True
cardinality = '1*'
composite = 'object'
constraints = [RQLConstraint('NOT O final TRUE')]
class rdef_cw_schema(RelationDefinition):
__permissions__ = RELATION_MANAGERS_PERMISSIONS
name = 'cw_schema'
subject = 'CWSourceSchemaConfig'
object = 'CWRelation'
inlined = True
cardinality = '1*'
composite = 'object'
# "abtract" relation types, no definition in cubicweb itself ###################
class identical_to(RelationType):
"""identical to"""
symmetric = True
__permissions__ = {
'read': ('managers', 'users', 'guests',),
# XXX should have update __permissions__ on both subject and object,
# though by doing this we will probably have no way to add
# this relation in the web ui. The easiest way to acheive this
# is probably to be able to have "U has_update_permission O" as
# RQLConstraint of the relation definition, though this is not yet
# possible
'add': ('managers', RRQLExpression('U has_update_permission S'),),
'delete': ('managers', RRQLExpression('U has_update_permission S'),),
}
class see_also(RelationType):
"""generic relation to link one entity to another"""
symmetric = True
__permissions__ = {
'read': ('managers', 'users', 'guests',),
'add': ('managers', RRQLExpression('U has_update_permission S'),),
'delete': ('managers', RRQLExpression('U has_update_permission S'),),
}