--- a/.hgtags Thu Mar 04 18:57:13 2010 +0100
+++ b/.hgtags Fri Mar 05 18:20:18 2010 +0100
@@ -103,4 +103,5 @@
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
0a16f07112b90fb61d2e905855fece77e5a7e39c cubicweb-debian-version-3.6.1-2
--- a/cwctl.py Thu Mar 04 18:57:13 2010 +0100
+++ b/cwctl.py Fri Mar 05 18:20:18 2010 +0100
@@ -204,7 +204,7 @@
# simplify constraints
if versions:
for constraint in versions:
- op, ver = constraint.split()
+ op, ver = constraint
if oper is None:
oper = op
version = ver
@@ -238,7 +238,12 @@
for name, constraint in use.items():
self.constraints.setdefault(name,set())
if constraint:
- self.constraints[name].add(constraint)
+ try:
+ oper, version = constraint.split()
+ self.constraints[name].add( (oper, version) )
+ except:
+ self.warnings.append('cube %s depends on %s but constraint badly formatted: %s'
+ % (cube, name, constraint))
self.reverse_constraints.setdefault(name, set()).add(cube)
class ListCommand(Command):
--- a/devtools/__init__.py Thu Mar 04 18:57:13 2010 +0100
+++ b/devtools/__init__.py Fri Mar 05 18:20:18 2010 +0100
@@ -30,8 +30,8 @@
SYSTEM_RELATIONS = schema.META_RTYPES | set((
# workflow related
- 'workflow_of', 'state_of', 'transition_of', 'initial_state', 'allowed_transition',
- 'destination_state', 'from_state', 'to_state',
+ 'workflow_of', 'state_of', 'transition_of', 'initial_state', 'default_workflow',
+ 'allowed_transition', 'destination_state', 'from_state', 'to_state',
'condition', 'subworkflow', 'subworkflow_state', 'subworkflow_exit',
'custom_workflow', 'in_state', 'wf_info_for',
# cwproperty
--- a/devtools/dataimport.py Thu Mar 04 18:57:13 2010 +0100
+++ b/devtools/dataimport.py Fri Mar 05 18:20:18 2010 +0100
@@ -59,10 +59,14 @@
import traceback
import os.path as osp
from StringIO import StringIO
+from copy import copy
from logilab.common import shellutils
+from logilab.common.date import strptime
+from logilab.common.decorators import cached
from logilab.common.deprecation import deprecated
+
def ucsvreader_pb(filepath, encoding='utf-8', separator=',', quote='"',
skipfirst=False, withpb=True):
"""same as ucsvreader but a progress bar is displayed as we iter on rows"""
@@ -90,8 +94,6 @@
for row in it:
yield [item.decode(encoding) for item in row]
-utf8csvreader = deprecated('use ucsvreader instead')(ucsvreader)
-
def commit_every(nbit, store, it):
for i, x in enumerate(it):
yield x
@@ -129,19 +131,16 @@
assert isinstance(row, dict)
assert isinstance(map, list)
for src, dest, funcs in map:
- assert not (required in funcs and optional in funcs), "optional and required checks are exclusive"
+ assert not (required in funcs and optional in funcs), \
+ "optional and required checks are exclusive"
res[dest] = row[src]
try:
for func in funcs:
res[dest] = func(res[dest])
- if res[dest] is None:
- raise AssertionError('undetermined value')
- except AssertionError, err:
- if optional in funcs:
- # Forget this field if exception is coming from optional function
- del res[dest]
- else:
- raise AssertionError('error with "%s" field: %s' % (src, err))
+ if res[dest] is None:
+ break
+ except ValueError, err:
+ raise ValueError('error with %r field: %s' % (src, err))
return res
@@ -178,98 +177,49 @@
return True # silent
-# base sanitizing functions ####################################################
-
-def capitalize_if_unicase(txt):
- if txt.isupper() or txt.islower():
- return txt.capitalize()
- return txt
-
-def uppercase(txt):
- return txt.upper()
-
-def lowercase(txt):
- return txt.lower()
-
-def no_space(txt):
- return txt.replace(' ','')
-
-def no_uspace(txt):
- return txt.replace(u'\xa0','')
-
-def no_dash(txt):
- return txt.replace('-','')
-
-def decimal(value):
- """cast to float but with comma replacement
-
- We take care of some locale format as replacing ',' by '.'"""
- value = value.replace(',', '.')
- try:
- return float(value)
- except Exception, err:
- raise AssertionError(err)
-
-def integer(value):
- try:
- return int(value)
- except Exception, err:
- raise AssertionError(err)
-
-def strip(txt):
- return txt.strip()
-
-def yesno(value):
- """simple heuristic that returns boolean value
-
- >>> yesno("Yes")
- True
- >>> yesno("oui")
- True
- >>> yesno("1")
- True
- >>> yesno("11")
- True
- >>> yesno("")
- False
- >>> yesno("Non")
- False
- >>> yesno("blablabla")
- False
- """
- if value:
- return value.lower()[0] in 'yo1'
- return False
-
-def isalpha(value):
- if value.isalpha():
- return value
- raise AssertionError("not all characters in the string alphabetic")
+# base sanitizing/coercing functions ###########################################
def optional(value):
"""validation error will not been raised if you add this checker in chain"""
- return value
+ if value:
+ return value
+ return None
def required(value):
- """raise AssertionError is value is empty
+ """raise ValueError is value is empty
This check should be often found in last position in the chain.
"""
- if bool(value):
+ if value:
return value
- raise AssertionError("required")
+ raise ValueError("required")
-@deprecated('use required(value)')
-def nonempty(value):
- return required(value)
+def todatetime(format='%d/%m/%Y'):
+ """return a transformation function to turn string input value into a
+ `datetime.datetime` instance, using given format.
+
+ Follow it by `todate` or `totime` functions from `logilab.common.date` if
+ you want a `date`/`time` instance instead of `datetime`.
+ """
+ def coerce(value):
+ return strptime(value, format)
+ return coerce
-@deprecated('use integer(value)')
-def alldigits(txt):
- if txt.isdigit():
- return txt
- else:
- return u''
+def call_transform_method(methodname, *args, **kwargs):
+ """return value returned by calling the given method on input"""
+ def coerce(value):
+ return getattr(value, methodname)(*args, **kwargs)
+ return coerce
+def call_check_method(methodname, *args, **kwargs):
+ """check value returned by calling the given method on input is true,
+ else raise ValueError
+ """
+ def check(value):
+ if getattr(value, methodname)(*args, **kwargs):
+ return value
+ raise ValueError('%s not verified on %r' % (methodname, value))
+ return check
# base integrity checking functions ############################################
@@ -316,7 +266,7 @@
self.eids[eid] = item
self.types.setdefault(type, []).append(eid)
- def relate(self, eid_from, rtype, eid_to):
+ def relate(self, eid_from, rtype, eid_to, inlined=False):
"""Add new relation (reverse type support is available)
>>> 1,2 = eid_from, eid_to
@@ -359,14 +309,6 @@
func = lambda x: x[key]
self.build_index(name, type, func)
- @deprecated('get_many() deprecated. Use fetch() instead')
- def get_many(self, name, key):
- return self.fetch(name, key, unique=False)
-
- @deprecated('get_one() deprecated. Use fetch(..., unique=True) instead')
- def get_one(self, name, key):
- return self.fetch(name, key, unique=True)
-
def fetch(self, name, key, unique=False, decorator=None):
"""
decorator is a callable method or an iterator of callable methods (usually a lambda function)
@@ -398,6 +340,24 @@
def checkpoint(self):
pass
+ @property
+ def nb_inserted_entities(self):
+ return len(self.eids)
+ @property
+ def nb_inserted_types(self):
+ return len(self.types)
+ @property
+ def nb_inserted_relations(self):
+ return len(self.relations)
+
+ @deprecated('[3.6] get_many() deprecated. Use fetch() instead')
+ def get_many(self, name, key):
+ return self.fetch(name, key, unique=False)
+
+ @deprecated('[3.6] get_one() deprecated. Use fetch(..., unique=True) instead')
+ def get_one(self, name, key):
+ return self.fetch(name, key, unique=True)
+
class RQLObjectStore(ObjectStore):
"""ObjectStore that works with an actual RQL repository (production mode)"""
@@ -412,19 +372,24 @@
session = session.request()
session.set_pool = lambda : None
checkpoint = checkpoint or cnx.commit
+ else:
+ session.set_pool()
self.session = session
- self.checkpoint = checkpoint or session.commit
+ self._checkpoint = checkpoint or session.commit
elif checkpoint is not None:
- self.checkpoint = checkpoint
+ self._checkpoint = checkpoint
+ # XXX .session
+
+ def checkpoint(self):
+ self._checkpoint()
+ self.session.set_pool()
def rql(self, *args):
if self._rql is not None:
return self._rql(*args)
- self.session.set_pool()
return self.session.execute(*args)
def create_entity(self, *args, **kwargs):
- self.session.set_pool()
entity = self.session.create_entity(*args, **kwargs)
self.eids[entity.eid] = entity
self.types.setdefault(args[0], []).append(entity.eid)
@@ -435,7 +400,7 @@
for k in item)
return self.rql(query, item)[0][0]
- def relate(self, eid_from, rtype, eid_to):
+ def relate(self, eid_from, rtype, eid_to, inlined=False):
# if reverse relation is found, eids are exchanged
eid_from, rtype, eid_to = super(RQLObjectStore, self).relate(
eid_from, rtype, eid_to)
@@ -513,8 +478,10 @@
self.store.checkpoint()
nberrors = sum(len(err[1]) for err in self.errors.values())
self.tell('\nImport completed: %i entities, %i types, %i relations and %i errors'
- % (len(self.store.eids), len(self.store.types),
- len(self.store.relations), nberrors))
+ % (self.store.nb_inserted_entities,
+ self.store.nb_inserted_types,
+ self.store.nb_inserted_relations,
+ nberrors))
if self.errors:
if self.askerror == 2 or (self.askerror and confirm('Display errors ?')):
from pprint import pformat
@@ -545,3 +512,241 @@
def iter_and_commit(self, datakey):
"""iter rows, triggering commit every self.commitevery iterations"""
return commit_every(self.commitevery, self.store, self.get_data(datakey))
+
+
+
+from datetime import datetime
+from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES
+
+
+class NoHookRQLObjectStore(RQLObjectStore):
+ """ObjectStore that works with an actual RQL repository (production mode)"""
+ _rql = None # bw compat
+
+ def __init__(self, session, metagen=None, baseurl=None):
+ super(NoHookRQLObjectStore, self).__init__(session)
+ self.source = session.repo.system_source
+ self.rschema = session.repo.schema.rschema
+ self.add_relation = self.source.add_relation
+ if metagen is None:
+ metagen = MetaGenerator(session, baseurl)
+ self.metagen = metagen
+ self._nb_inserted_entities = 0
+ self._nb_inserted_types = 0
+ self._nb_inserted_relations = 0
+ self.rql = session.unsafe_execute
+
+ def create_entity(self, etype, **kwargs):
+ for k, v in kwargs.iteritems():
+ kwargs[k] = getattr(v, 'eid', v)
+ entity, rels = self.metagen.base_etype_dicts(etype)
+ entity = copy(entity)
+ entity._related_cache = {}
+ self.metagen.init_entity(entity)
+ entity.update(kwargs)
+ session = self.session
+ self.source.add_entity(session, entity)
+ self.source.add_info(session, entity, self.source, complete=False)
+ for rtype, targeteids in rels.iteritems():
+ # targeteids may be a single eid or a list of eids
+ inlined = self.rschema(rtype).inlined
+ try:
+ for targeteid in targeteids:
+ self.add_relation(session, entity.eid, rtype, targeteid,
+ inlined)
+ except TypeError:
+ self.add_relation(session, entity.eid, rtype, targeteids,
+ inlined)
+ self._nb_inserted_entities += 1
+ return entity
+
+ def relate(self, eid_from, rtype, eid_to):
+ assert not rtype.startswith('reverse_')
+ self.add_relation(self.session, eid_from, rtype, eid_to,
+ self.rschema(rtype).inlined)
+ self._nb_inserted_relations += 1
+
+ @property
+ def nb_inserted_entities(self):
+ return self._nb_inserted_entities
+ @property
+ def nb_inserted_types(self):
+ return self._nb_inserted_types
+ @property
+ def nb_inserted_relations(self):
+ return self._nb_inserted_relations
+
+ def _put(self, type, item):
+ raise RuntimeError('use create entity')
+
+
+class MetaGenerator(object):
+ def __init__(self, session, baseurl=None):
+ self.session = session
+ self.source = session.repo.system_source
+ self.time = datetime.now()
+ if baseurl is None:
+ config = session.vreg.config
+ baseurl = config['base-url'] or config.default_base_url()
+ if not baseurl[-1] == '/':
+ baseurl += '/'
+ self.baseurl = baseurl
+ # attributes/relations shared by all entities of the same type
+ self.etype_attrs = []
+ self.etype_rels = []
+ # attributes/relations specific to each entity
+ self.entity_attrs = ['eid', 'cwuri']
+ #self.entity_rels = [] XXX not handled (YAGNI?)
+ schema = session.vreg.schema
+ rschema = schema.rschema
+ for rtype in META_RTYPES:
+ if rtype in ('eid', 'cwuri') or rtype in VIRTUAL_RTYPES:
+ continue
+ if rschema(rtype).final:
+ self.etype_attrs.append(rtype)
+ else:
+ self.etype_rels.append(rtype)
+ if not schema._eid_index:
+ # test schema loaded from the fs
+ self.gen_is = self.test_gen_is
+ self.gen_is_instance_of = self.test_gen_is_instanceof
+
+ @cached
+ def base_etype_dicts(self, etype):
+ entity = self.session.vreg['etypes'].etype_class(etype)(self.session)
+ # entity are "surface" copied, avoid shared dict between copies
+ del entity.cw_extra_kwargs
+ for attr in self.etype_attrs:
+ entity[attr] = self.generate(entity, attr)
+ rels = {}
+ for rel in self.etype_rels:
+ rels[rel] = self.generate(entity, rel)
+ return entity, rels
+
+ def init_entity(self, entity):
+ for attr in self.entity_attrs:
+ entity[attr] = self.generate(entity, attr)
+ entity.eid = entity['eid']
+
+ def generate(self, entity, rtype):
+ return getattr(self, 'gen_%s' % rtype)(entity)
+
+ def gen_eid(self, entity):
+ return self.source.create_eid(self.session)
+
+ def gen_cwuri(self, entity):
+ return u'%seid/%s' % (self.baseurl, entity['eid'])
+
+ def gen_creation_date(self, entity):
+ return self.time
+ def gen_modification_date(self, entity):
+ return self.time
+
+ def gen_is(self, entity):
+ return entity.e_schema.eid
+ def gen_is_instance_of(self, entity):
+ eids = []
+ for etype in entity.e_schema.ancestors() + [entity.e_schema]:
+ eids.append(entity.e_schema.eid)
+ return eids
+
+ def gen_created_by(self, entity):
+ return self.session.user.eid
+ def gen_owned_by(self, entity):
+ return self.session.user.eid
+
+ # implementations of gen_is / gen_is_instance_of to use during test where
+ # schema has been loaded from the fs (hence entity type schema eids are not
+ # known)
+ def test_gen_is(self, entity):
+ from cubicweb.hooks.metadata import eschema_eid
+ return eschema_eid(self.session, entity.e_schema)
+ def test_gen_is_instanceof(self, entity):
+ from cubicweb.hooks.metadata import eschema_eid
+ eids = []
+ for eschema in entity.e_schema.ancestors() + [entity.e_schema]:
+ eids.append(eschema_eid(self.session, eschema))
+ return eids
+
+
+################################################################################
+
+utf8csvreader = deprecated('[3.6] use ucsvreader instead')(ucsvreader)
+
+@deprecated('[3.6] use required')
+def nonempty(value):
+ return required(value)
+
+@deprecated("[3.6] use call_check_method('isdigit')")
+def alldigits(txt):
+ if txt.isdigit():
+ return txt
+ else:
+ return u''
+
+@deprecated("[3.7] too specific, will move away, copy me")
+def capitalize_if_unicase(txt):
+ if txt.isupper() or txt.islower():
+ return txt.capitalize()
+ return txt
+
+@deprecated("[3.7] too specific, will move away, copy me")
+def yesno(value):
+ """simple heuristic that returns boolean value
+
+ >>> yesno("Yes")
+ True
+ >>> yesno("oui")
+ True
+ >>> yesno("1")
+ True
+ >>> yesno("11")
+ True
+ >>> yesno("")
+ False
+ >>> yesno("Non")
+ False
+ >>> yesno("blablabla")
+ False
+ """
+ if value:
+ return value.lower()[0] in 'yo1'
+ return False
+
+@deprecated("[3.7] use call_check_method('isalpha')")
+def isalpha(value):
+ if value.isalpha():
+ return value
+ raise ValueError("not all characters in the string alphabetic")
+
+@deprecated("[3.7] use call_transform_method('upper')")
+def uppercase(txt):
+ return txt.upper()
+
+@deprecated("[3.7] use call_transform_method('lower')")
+def lowercase(txt):
+ return txt.lower()
+
+@deprecated("[3.7] use call_transform_method('replace', ' ', '')")
+def no_space(txt):
+ return txt.replace(' ','')
+
+@deprecated("[3.7] use call_transform_method('replace', u'\xa0', '')")
+def no_uspace(txt):
+ return txt.replace(u'\xa0','')
+
+@deprecated("[3.7] use call_transform_method('replace', '-', '')")
+def no_dash(txt):
+ return txt.replace('-','')
+
+@deprecated("[3.7] use call_transform_method('strip')")
+def strip(txt):
+ return txt.strip()
+
+@deprecated("[3.7] use call_transform_method('replace', ',', '.'), float")
+def decimal(value):
+ return comma_float(value)
+
+@deprecated('[3.7] use int builtin')
+def integer(value):
+ return int(value)
--- a/doc/book/en/B000-development.en.txt Thu Mar 04 18:57:13 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-=====================
-Part II - Development
-=====================
-
-This part is about developing web applications with the *CubicWeb* framework.
-
-.. toctree::
- :maxdepth: 1
-
- B0-data-model.en.txt
- B1-web-interface.en.txt
- B2-repository-customization.en.txt
- B3-test.en.txt
- B4-advanced.en.txt
-
-
-
--- a/doc/book/en/B0015-define-permissions.en.txt Thu Mar 04 18:57:13 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,208 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-The security model
-------------------
-
-The security model of `cubicWeb` is based on `Access Control List`.
-The main principles are:
-
-* users and groups of users
-* a user belongs to at least one group of user
-* permissions (read, update, create, delete)
-* permissions are assigned to groups (and not to users)
-
-For *CubicWeb* in particular:
-
-* we associate rights at the enttities/relations schema level
-* for each entity, we distinguish four kind of permissions: read,
- add, update and delete
-* for each relation, we distinguish three king of permissions: read,
- add and delete (we can not modify a relation)
-* the basic groups are: Administrators, Users and Guests
-* by default, users belongs to the group Users
-* there is a virtual group called `Owners users` to which we
- can associate only deletion and update permissions
-* we can not add users to the `Owners users` group, they are
- implicetely added to it according to the context of the objects
- they own
-* the permissions of this group are only be checked on update/deletion
- actions if all the other groups the user belongs does not provide
- those permissions
-
-
-Permissions definition
-``````````````````````
-
-Setting permissions is done with the attribute `permissions` of entities and
-relation types. It defines a dictionary where the keys are the access types
-(action), and the values are the authorized groups or expressions.
-
-For an entity type, the possible actions are `read`, `add`, `update` and
-`delete`.
-
-For a relation type, the possible actions are `read`, `add`, and `delete`.
-
-For each access type, a tuple indicates the name of the authorized groups and/or
-one or multiple RQL expressions to satisfy to grant access. The access is
-provided once the user is in the listed groups or one of the RQL condition is
-satisfied.
-
-The standard groups are :
-
-* `guests`
-
-* `users`
-
-* `managers`
-
-* `owners` : virtual group corresponding to the entity's owner.
- This can only be used for the actions `update` and `delete` of an entity
- type.
-
-It is also possible to use specific groups if they are defined in the precreate
-of the cube (``migration/precreate.py``).
-
-
-Use of RQL expression for writing rights
-````````````````````````````````````````
-
-It is possible to define RQL expression to provide update permission
-(`add`, `delete` and `update`) on relation and entity types.
-
-RQL expression for entity type permission :
-
-* you have to use the class `ERQLExpression`
-
-* the used expression corresponds to the WHERE statement of an RQL query
-
-* in this expression, the variables X and U are pre-defined references
- respectively on the current entity (on which the action is verified) and
- on the user who send the request
-
-* it is possible to use, in this expression, a special relation
- "has_<ACTION>_permission" where the subject is the user and the
- object is a any variable, meaning that the user needs to have
- permission to execute the action <ACTION> on the entities related
- to this variable
-
-For RQL expressions on a relation type, the principles are the same except
-for the following :
-
-* 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 define rights on attributes of an entity (non-final
- relation), knowing that :
-
- - 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`
- are actually taken in consideration.
-
-In addition to that the entity type `EPermission` from the standard library
-allows to build very complex and dynamic security architecture. The schema of
-this entity type is as follow : ::
-
- class CWPermission(EntityType):
- """entity type that may be used to construct some advanced security configuration
- """
- permissions = META_ETYPE_PERMS
-
- name = String(required=True, indexed=True, internationalizable=True, maxsize=100,
- description=_('name or identifier of the permission'))
- label = String(required=True, internationalizable=True, maxsize=100,
- description=_('distinct label to distinguate between other permission entity of the same name'))
- require_group = SubjectRelation('CWGroup',
- description=_('groups to which the permission is granted'))
-
- # explicitly add X require_permission CWPermission for each entity that should have
- # configurable security
- class require_permission(RelationType):
- """link a permission to the entity. This permission should be used in the
- security definition of the entity's type to be useful.
- """
- permissions = {
- 'read': ('managers', 'users', 'guests'),
- 'add': ('managers',),
- 'delete': ('managers',),
- }
-
- class require_group(RelationType):
- """used to grant a permission to a group"""
- permissions = {
- 'read': ('managers', 'users', 'guests'),
- 'add': ('managers',),
- 'delete': ('managers',),
- }
-
-
-Example of configuration ::
-
-
- ...
-
- class Version(EntityType):
- """a version is defining the content of a particular project's release"""
-
- permissions = {'read': ('managers', 'users', 'guests',),
- 'update': ('managers', 'logilab', 'owners',),
- 'delete': ('managers', ),
- 'add': ('managers', 'logilab',
- ERQLExpression('X version_of PROJ, U in_group G,'
- 'PROJ require_permission P, P name "add_version",'
- 'P require_group G'),)}
-
- ...
-
- class version_of(RelationType):
- """link a version to its project. A version is necessarily linked to one and only one project.
- """
- permissions = {'read': ('managers', 'users', 'guests',),
- 'delete': ('managers', ),
- 'add': ('managers', 'logilab',
- RRQLExpression('O require_permission P, P name "add_version",'
- 'U in_group G, P require_group G'),)
- }
- inlined = True
-
-This configuration indicates that an entity `CWPermission` named
-"add_version" can be associated to a project and provides rights to create
-new versions on this project to specific groups. It is important to notice that :
-
-* in such case, we have to protect both the entity type "Version" and the relation
- associating a version to a project ("version_of")
-
-* because of the genericity of the entity type `CWPermission`, we have to execute
- a unification with the groups and/or the states if necessary in the expression
- ("U in_group G, P require_group G" in the above example)
-
-Use of RQL expression for reading rights
-````````````````````````````````````````
-
-The principles are the same but with the following restrictions :
-
-* we can not use `RRQLExpression` on relation types for reading
-
-* special relations "has_<ACTION>_permission" can not be used
-
-
-Note on the use of RQL expression for `add` permission
-``````````````````````````````````````````````````````
-Potentially, the use of an RQL expression to add an entity or a relation
-can cause problems for the user interface, because if the expression uses
-the entity or the relation to create, then we are not able to verify the
-permissions before we actually add the entity (please note that this is
-not a problem for the RQL server at all, because the permissions checks are
-done after the creation). In such case, the permission check methods
-(check_perm, has_perm) can indicate that the user is not allowed to create
-this entity but can obtain the permission.
-
-To compensate this problem, it is usually necessary, for such case,
-to use an action that reflects the schema permissions but which enables
-to check properly the permissions so that it would show up if necessary.
-
--- a/doc/book/en/B4040-rss-xml.en.txt Thu Mar 04 18:57:13 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _rss:
-
-RSS Channel
------------
-
-Assuming you have several blog entries, click on the title of the
-search box in the left column. A larger search box should appear. Enter::
-
- Any X ORDERBY D WHERE X is BlogEntry, X creation_date D
-
-and you get a list of blog entries.
-
-Click on your login at the top right corner. Chose "user preferences",
-then "boxes", then "possible views box" and check "visible = yes"
-before validating your changes.
-
-Enter the same query in the search box and you will see the same list,
-plus a box titled "possible views" in the left column. Click on
-"entityview", then "RSS".
-
-You just applied the "RSS" view to the RQL selection you requested.
-
-That's it, you have a RSS channel for your blog.
-
-Try again with::
-
- Any X ORDERBY D WHERE X is BlogEntry, X creation_date D,
- X entry_of B, B title "MyLife"
-
-Another RSS channel, but a bit more focused.
-
-A last one for the road::
-
- Any C ORDERBY D WHERE C is Comment, C creation_date D LIMIT 15
-
-displayed with the RSS view, that's a channel for the last fifteen
-comments posted.
-
-[WRITE ME]
-
-* show that the RSS view can be used to display an ordered selection
- of blog entries, thus providing a RSS channel
-
-* show that a different selection (by category) means a different channel
-
-
--- a/doc/book/en/D060-modules-stdlib.en.txt Thu Mar 04 18:57:13 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-================
-Standard library
-================
-
-:mod:`cubes.addressbook`
-========================
-
-.. automodule:: cubes.addressbook
- :members:
-
-:mod:`cubes.basket`
-========================
-
-.. automodule:: cubes.basket
- :members:
-
-:mod:`cubes.blog`
-========================
-
-.. automodule:: cubes.blog
- :members:
-
-:mod:`cubes.book`
-========================
-
-.. automodule:: cubes.book
- :members:
-
-:mod:`cubes.comment`
-========================
-
-.. automodule:: cubes.comment
- :members:
-
-:mod:`cubes.company`
-========================
-
-.. automodule:: cubes.company
- :members:
-
-
-:mod:`cubes.conference`
-========================
-
-.. automodule:: cubes.conference
- :members:
-
-:mod:`cubes.email`
-========================
-
-.. automodule:: cubes.email
- :members:
-
-:mod:`cubes.event`
-========================
-
-.. automodule:: cubes.event
- :members:
-
-:mod:`cubes.expense`
-========================
-
-.. automodule:: cubes.expense
- :members:
-
-
-:mod:`cubes.file`
-========================
-
-.. automodule:: cubes.file
- :members:
-
-:mod:`cubes.folder`
-========================
-
-.. automodule:: cubes.folder
- :members:
-
-:mod:`cubes.i18ncontent`
-========================
-
-.. automodule:: cubes.i18ncontent
- :members:
-
-:mod:`cubes.invoice`
-========================
-
-.. automodule:: cubes.invoice
- :members:
-
-:mod:`cubes.keyword`
-========================
-
-.. automodule:: cubes.keyword
- :members:
-
-:mod:`cubes.link`
-========================
-
-.. automodule:: cubes.link
- :members:
-
-:mod:`cubes.mailinglist`
-========================
-
-.. automodule:: cubes.mailinglist
- :members:
-
-:mod:`cubes.person`
-========================
-
-.. automodule:: cubes.person
- :members:
-
-:mod:`cubes.shopcart`
-========================
-
-.. automodule:: cubes.shopcart
- :members:
-
-:mod:`cubes.skillmat`
-========================
-
-.. automodule:: cubes.skillmat
- :members:
-
-:mod:`cubes.tag`
-========================
-
-.. automodule:: cubes.tag
- :members:
-
-:mod:`cubes.task`
-========================
-
-.. automodule:: cubes.task
- :members:
-
-:mod:`cubes.workcase`
-========================
-
-.. automodule:: cubes.workcase
- :members:
-
-:mod:`cubes.workorder`
-========================
-
-.. automodule:: cubes.workorder
- :members:
-
-:mod:`cubes.zone`
-========================
-
-.. automodule:: cubes.zone
- :members:
-
--- a/doc/book/en/D070-modules-cbw-api.en.txt Thu Mar 04 18:57:13 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1961 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-============
-CubicWeb API
-============
-
-:mod:`cubicweb.hercule`
-=======================
-
-.. automodule:: cubicweb.hercule
- :members:
-
-:mod:`cubicweb.cwctl`
-=====================
-
-.. automodule:: cubicweb.cwctl
- :members:
-
-:mod:`cubicweb.schema`
-======================
-
-.. automodule:: cubicweb.schema
- :members:
-
-:mod:`cubicweb.cwconfig`
-========================
-
-.. automodule:: cubicweb.cwconfig
- :members:
-
-:mod:`cubicweb.schemaviewer`
-============================
-
-.. automodule:: cubicweb.schemaviewer
- :members:
-
-:mod:`cubicweb._exceptions`
-===========================
-
-.. automodule:: cubicweb._exceptions
- :members:
-
-:mod:`cubicweb.dbapi`
-=====================
-
-.. automodule:: cubicweb.dbapi
- :members:
-
-:mod:`cubicweb.toolsutils`
-==========================
-
-.. automodule:: cubicweb.toolsutils
- :members:
-
-:mod:`cubicweb.cwvreg`
-======================
-
-.. automodule:: cubicweb.cwvreg
- :members:
-
-:mod:`cubicweb.md5crypt`
-========================
-
-.. automodule:: cubicweb.md5crypt
- :members:
-
-:mod:`cubicweb.rset`
-====================
-
-.. automodule:: cubicweb.rset
- :members:
-
-:mod:`cubicweb`
-===============
-
-.. automodule:: cubicweb
- :members:
-
-:mod:`cubicweb.setup`
-=====================
-
-.. automodule:: cubicweb.setup
- :members:
-
-:mod:`cubicweb.gettext`
-=======================
-
-.. automodule:: cubicweb.gettext
- :members:
-
-:mod:`cubicweb.interfaces`
-==========================
-
-.. automodule:: cubicweb.interfaces
- :members:
-
-:mod:`cubicweb.vregistry`
-=========================
-
-.. automodule:: cubicweb.vregistry
- :members:
-
-:mod:`cubicweb.web.httpcache`
-=============================
-
-.. automodule:: cubicweb.web.httpcache
- :members:
-
-:mod:`cubicweb.web.webconfig`
-=============================
-
-.. automodule:: cubicweb.web.webconfig
- :members:
-
-:mod:`cubicweb.web.request`
-===========================
-
-.. automodule:: cubicweb.web.request
- :members:
-
-:mod:`cubicweb.web._exceptions`
-===============================
-
-.. automodule:: cubicweb.web._exceptions
- :members:
-
-:mod:`cubicweb.web.webctl`
-==========================
-
-.. automodule:: cubicweb.web.webctl
- :members:
-
-:mod:`cubicweb.web.application`
-===============================
-
-.. automodule:: cubicweb.web.application
- :members:
-
-:mod:`cubicweb.web.controller`
-==============================
-
-.. automodule:: cubicweb.web.controller
- :members:
-
-:mod:`cubicweb.web.widgets`
-===========================
-
-.. automodule:: cubicweb.web.widgets
- :members:
-
-:mod:`cubicweb.web.htmlwidgets`
-===============================
-
-.. automodule:: cubicweb.web.htmlwidgets
- :members:
-
-:mod:`cubicweb.web`
-===================
-
-.. automodule:: cubicweb.web
- :members:
-
-:mod:`cubicweb.web.form`
-========================
-
-.. automodule:: cubicweb.web.form
- :members:
-
-:mod:`cubicweb.web.box`
-=======================
-
-.. automodule:: cubicweb.web.box
- :members:
-
-:mod:`cubicweb.web.component`
-=============================
-
-.. automodule:: cubicweb.web.component
- :members:
-
-:mod:`cubicweb.web.action`
-==========================
-
-.. automodule:: cubicweb.web.action
- :members:
-
-:mod:`cubicweb.web.facet`
-=========================
-
-.. automodule:: cubicweb.web.facet
- :members:
-
-:mod:`cubicweb.web.views.plots`
-===============================
-
-.. automodule:: cubicweb.web.views.plots
- :members:
-
-:mod:`cubicweb.web.views.error`
-===============================
-
-.. automodule:: cubicweb.web.views.error
- :members:
-
-:mod:`cubicweb.web.views.magicsearch`
-=====================================
-
-.. automodule:: cubicweb.web.views.magicsearch
- :members:
-
-:mod:`cubicweb.web.views.basetemplates`
-=======================================
-
-.. automodule:: cubicweb.web.views.basetemplates
- :members:
-
-:mod:`cubicweb.web.views.idownloadable`
-=======================================
-
-.. automodule:: cubicweb.web.views.idownloadable
- :members:
-
-:mod:`cubicweb.web.views.ajaxedit`
-==================================
-
-.. automodule:: cubicweb.web.views.ajaxedit
- :members:
-
-:mod:`cubicweb.web.views.wfentities`
-====================================
-
-.. automodule:: cubicweb.web.views.wfentities
- :members:
-
-:mod:`cubicweb.web.views.navigation`
-====================================
-
-.. automodule:: cubicweb.web.views.navigation
- :members:
-
-:mod:`cubicweb.web.views.schemaentities`
-========================================
-
-.. automodule:: cubicweb.web.views.schemaentities
- :members:
-
-:mod:`cubicweb.web.views.treeview`
-==================================
-
-.. automodule:: cubicweb.web.views.treeview
- :members:
-
-:mod:`cubicweb.web.views.startup`
-=================================
-
-.. automodule:: cubicweb.web.views.startup
- :members:
-
-:mod:`cubicweb.web.views.iprogress`
-===================================
-
-.. automodule:: cubicweb.web.views.iprogress
- :members:
-
-:mod:`cubicweb.web.views.euser`
-===============================
-
-.. automodule:: cubicweb.web.views.euser
- :members:
-
-:mod:`cubicweb.web.views.facets`
-================================
-
-.. automodule:: cubicweb.web.views.facets
- :members:
-
-:mod:`cubicweb.web.views.emailaddress`
-======================================
-
-.. automodule:: cubicweb.web.views.emailaddress
- :members:
-
-:mod:`cubicweb.web.views.sessions`
-==================================
-
-.. automodule:: cubicweb.web.views.sessions
- :members:
-
-:mod:`cubicweb.web.views.timetable`
-===================================
-
-.. automodule:: cubicweb.web.views.timetable
- :members:
-
-:mod:`cubicweb.web.views.timeline`
-==================================
-
-.. automodule:: cubicweb.web.views.timeline
- :members:
-
-:mod:`cubicweb.web.views.baseviews`
-===================================
-
-.. automodule:: cubicweb.web.views.baseviews
- :members:
-
-:mod:`cubicweb.web.views.boxes`
-===============================
-
-.. automodule:: cubicweb.web.views.boxes
- :members:
-
-:mod:`cubicweb.web.views.old_calendar`
-======================================
-
-.. automodule:: cubicweb.web.views.old_calendar
- :members:
-
-:mod:`cubicweb.web.views.card`
-==============================
-
-.. automodule:: cubicweb.web.views.card
- :members:
-
-:mod:`cubicweb.web.views.ibreadcrumbs`
-======================================
-
-.. automodule:: cubicweb.web.views.ibreadcrumbs
- :members:
-
-:mod:`cubicweb.web.views.basecontrollers`
-=========================================
-
-.. automodule:: cubicweb.web.views.basecontrollers
- :members:
-
-:mod:`cubicweb.web.views.embedding`
-===================================
-
-.. automodule:: cubicweb.web.views.embedding
- :members:
-
-:mod:`cubicweb.web.views.actions`
-=================================
-
-.. automodule:: cubicweb.web.views.actions
- :members:
-
-:mod:`cubicweb.web.views.editcontroller`
-========================================
-
-.. automodule:: cubicweb.web.views.editcontroller
- :members:
-
-:mod:`cubicweb.web.views.debug`
-===============================
-
-.. automodule:: cubicweb.web.views.debug
- :members:
-
-:mod:`cubicweb.web.views.urlpublishing`
-=======================================
-
-.. automodule:: cubicweb.web.views.urlpublishing
- :members:
-
-:mod:`cubicweb.web.views.baseforms`
-===================================
-
-.. automodule:: cubicweb.web.views.baseforms
- :members:
-
-:mod:`cubicweb.web.views.urlrewrite`
-====================================
-
-.. automodule:: cubicweb.web.views.urlrewrite
- :members:
-
-:mod:`cubicweb.web.views.massmailing`
-=====================================
-
-.. automodule:: cubicweb.web.views.massmailing
- :members:
-
-:mod:`cubicweb.web.views`
-=========================
-
-.. automodule:: cubicweb.web.views
- :members:
-
-:mod:`cubicweb.web.views.eproperties`
-=====================================
-
-.. automodule:: cubicweb.web.views.eproperties
- :members:
-
-:mod:`cubicweb.web.views.tabs`
-==============================
-
-.. automodule:: cubicweb.web.views.tabs
- :members:
-
-:mod:`cubicweb.web.views.vcard`
-===============================
-
-.. automodule:: cubicweb.web.views.vcard
- :members:
-
-:mod:`cubicweb.web.views.wdoc`
-==============================
-
-.. automodule:: cubicweb.web.views.wdoc
- :members:
-
-:mod:`cubicweb.web.views.authentication`
-========================================
-
-.. automodule:: cubicweb.web.views.authentication
- :members:
-
-:mod:`cubicweb.web.views.tableview`
-===================================
-
-.. automodule:: cubicweb.web.views.tableview
- :members:
-
-:mod:`cubicweb.web.views.management`
-====================================
-
-.. automodule:: cubicweb.web.views.management
- :members:
-
-:mod:`cubicweb.web.views.igeocodable`
-=====================================
-
-.. automodule:: cubicweb.web.views.igeocodable
- :members:
-
-:mod:`cubicweb.web.views.xbel`
-==============================
-
-.. automodule:: cubicweb.web.views.xbel
- :members:
-
-:mod:`cubicweb.web.views.bookmark`
-==================================
-
-.. automodule:: cubicweb.web.views.bookmark
- :members:
-
-:mod:`cubicweb.web.views.apacherewrite`
-=======================================
-
-.. automodule:: cubicweb.web.views.apacherewrite
- :members:
-
-:mod:`cubicweb.web.views.dynimages`
-===================================
-
-.. automodule:: cubicweb.web.views.dynimages
- :members:
-
-:mod:`cubicweb.web.views.searchrestriction`
-===========================================
-
-.. automodule:: cubicweb.web.views.searchrestriction
- :members:
-
-:mod:`cubicweb.web.views.basecomponents`
-========================================
-
-.. automodule:: cubicweb.web.views.basecomponents
- :members:
-
-:mod:`cubicweb.web.views.calendar`
-==================================
-
-.. automodule:: cubicweb.web.views.calendar
- :members:
-
-:mod:`cubicweb.sobjects.supervising`
-====================================
-
-.. automodule:: cubicweb.sobjects.supervising
- :members:
-
-:mod:`cubicweb.sobjects.hooks`
-==============================
-
-.. automodule:: cubicweb.sobjects.hooks
- :members:
-
-:mod:`cubicweb.sobjects.email`
-==============================
-
-.. automodule:: cubicweb.sobjects.email
- :members:
-
-:mod:`cubicweb.sobjects`
-========================
-
-.. automodule:: cubicweb.sobjects
- :members:
-
-:mod:`cubicweb.sobjects.notification`
-=====================================
-
-.. automodule:: cubicweb.sobjects.notification
- :members:
-
-:mod:`cubicweb.wsgi.request`
-============================
-
-.. automodule:: cubicweb.wsgi.request
- :members:
-
-:mod:`cubicweb.wsgi`
-====================
-
-.. automodule:: cubicweb.wsgi
- :members:
-
-:mod:`cubicweb.wsgi.handler`
-============================
-
-.. automodule:: cubicweb.wsgi.handler
- :members:
-
-:mod:`cubicweb.etwist.server`
-=============================
-
-.. automodule:: cubicweb.etwist.server
- :members:
-
-:mod:`cubicweb.etwist.request`
-==============================
-
-.. automodule:: cubicweb.etwist.request
- :members:
-
-:mod:`cubicweb.etwist.twconfig`
-===============================
-
-.. automodule:: cubicweb.etwist.twconfig
- :members:
-
-:mod:`cubicweb.etwist`
-======================
-
-.. automodule:: cubicweb.etwist
- :members:
-
-:mod:`cubicweb.etwist.twctl`
-============================
-
-.. automodule:: cubicweb.etwist.twctl
- :members:
-
-:mod:`cubicweb.goa.goaconfig`
-=============================
-
-.. automodule:: cubicweb.goa.goaconfig
- :members:
-
-:mod:`cubicweb.goa.rqlinterpreter`
-==================================
-
-.. automodule:: cubicweb.goa.rqlinterpreter
- :members:
-
-:mod:`cubicweb.goa.dbmyams`
-===========================
-
-.. automodule:: cubicweb.goa.dbmyams
- :members:
-
-:mod:`cubicweb.goa.db`
-======================
-
-.. automodule:: cubicweb.goa.db
- :members:
-
-:mod:`cubicweb.goa.goactl`
-==========================
-
-.. automodule:: cubicweb.goa.goactl
- :members:
-
-:mod:`cubicweb.goa.goavreg`
-===========================
-
-.. automodule:: cubicweb.goa.goavreg
- :members:
-
-:mod:`cubicweb.goa`
-===================
-
-.. automodule:: cubicweb.goa
- :members:
-
-:mod:`cubicweb.goa.gaesource`
-=============================
-
-.. automodule:: cubicweb.goa.gaesource
- :members:
-
-:mod:`cubicweb.goa.dbinit`
-==========================
-
-.. automodule:: cubicweb.goa.dbinit
- :members:
-
-:mod:`cubicweb.goa.testlib`
-===========================
-
-.. automodule:: cubicweb.goa.testlib
- :members:
-
-:mod:`cubicweb.goa.appobjects.dbmgmt`
-=====================================
-
-.. automodule:: cubicweb.goa.appobjects.dbmgmt
- :members:
-
-:mod:`cubicweb.goa.appobjects.gauthservice`
-===========================================
-
-.. automodule:: cubicweb.goa.appobjects.gauthservice
- :members:
-
-:mod:`cubicweb.goa.appobjects.sessions`
-=======================================
-
-.. automodule:: cubicweb.goa.appobjects.sessions
- :members:
-
-:mod:`cubicweb.goa.appobjects`
-==============================
-
-.. automodule:: cubicweb.goa.appobjects
- :members:
-
-:mod:`cubicweb.goa.appobjects.components`
-=========================================
-
-.. automodule:: cubicweb.goa.appobjects.components
- :members:
-
-:mod:`cubicweb.goa.tools.laxctl`
-================================
-
-.. automodule:: cubicweb.goa.tools.laxctl
- :members:
-
-:mod:`cubicweb.goa.tools.generate_schema_img`
-=============================================
-
-.. automodule:: cubicweb.goa.tools.generate_schema_img
- :members:
-
-:mod:`cubicweb.goa.tools`
-=========================
-
-.. automodule:: cubicweb.goa.tools
- :members:
-
-:mod:`cubicweb.goa.tools.i18n`
-==============================
-
-.. automodule:: cubicweb.goa.tools.i18n
- :members:
-
-:mod:`cubicweb.goa.overrides.mttransforms`
-==========================================
-
-.. automodule:: cubicweb.goa.overrides.mttransforms
- :members:
-
-:mod:`cubicweb.goa.overrides.rqlannotation`
-===========================================
-
-.. automodule:: cubicweb.goa.overrides.rqlannotation
- :members:
-
-:mod:`cubicweb.goa.overrides.toolsutils`
-========================================
-
-.. automodule:: cubicweb.goa.overrides.toolsutils
- :members:
-
-:mod:`cubicweb.goa.overrides`
-=============================
-
-.. automodule:: cubicweb.goa.overrides
- :members:
-
-:mod:`cubicweb.goa.overrides.server__init__`
-============================================
-
-.. automodule:: cubicweb.goa.overrides.server__init__
- :members:
-
-:mod:`cubicweb.goa.overrides.server_utils`
-==========================================
-
-.. automodule:: cubicweb.goa.overrides.server_utils
- :members:
-
-:mod:`cubicweb.common.mttransforms`
-===================================
-
-.. automodule:: cubicweb.common.mttransforms
- :members:
-
-:mod:`cubicweb.common.utils`
-============================
-
-.. automodule:: cubicweb.common.utils
- :members:
-
-:mod:`cubicweb.common.schema`
-=============================
-
-.. automodule:: cubicweb.common.schema
- :members:
-
-:mod:`cubicweb.common.tal`
-==========================
-
-.. automodule:: cubicweb.common.tal
- :members:
-
-:mod:`cubicweb.common.appobject`
-================================
-
-.. automodule:: cubicweb.common.appobject
- :members:
-
-:mod:`cubicweb.common.migration`
-================================
-
-.. automodule:: cubicweb.common.migration
- :members:
-
-:mod:`cubicweb.common.rest`
-===========================
-
-.. automodule:: cubicweb.common.rest
- :members:
-
-:mod:`cubicweb.common.html4zope`
-================================
-
-.. automodule:: cubicweb.common.html4zope
- :members:
-
-:mod:`cubicweb.common.view`
-===========================
-
-.. automodule:: cubicweb.common.view
- :members:
-
-:mod:`cubicweb.common.selectors`
-================================
-
-.. automodule:: cubicweb.common.selectors
- :members:
-
-:mod:`cubicweb.common.entity`
-=============================
-
-.. automodule:: cubicweb.common.entity
- :members:
-
-:mod:`cubicweb.common.mail`
-===========================
-
-.. automodule:: cubicweb.common.mail
- :members:
-
-:mod:`cubicweb.common.mixins`
-=============================
-
-.. automodule:: cubicweb.common.mixins
- :members:
-
-:mod:`cubicweb.common`
-======================
-
-.. automodule:: cubicweb.common
- :members:
-
-:mod:`cubicweb.common.uilib`
-============================
-
-.. automodule:: cubicweb.common.uilib
- :members:
-
-:mod:`cubicweb.common.registerers`
-==================================
-
-.. automodule:: cubicweb.common.registerers
- :members:
-
-:mod:`cubicweb.common.i18n`
-===========================
-
-.. automodule:: cubicweb.common.i18n
- :members:
-
-:mod:`cubicweb.entities.schemaobjs`
-===================================
-
-.. automodule:: cubicweb.entities.schemaobjs
- :members:
-
-:mod:`cubicweb.entities.wfobjs`
-===============================
-
-.. automodule:: cubicweb.entities.wfobjs
- :members:
-
-:mod:`cubicweb.entities`
-========================
-
-.. automodule:: cubicweb.entities
- :members:
-
-:mod:`cubicweb.entities.authobjs`
-=================================
-
-.. automodule:: cubicweb.entities.authobjs
- :members:
-
-:mod:`cubicweb.entities.lib`
-============================
-
-.. automodule:: cubicweb.entities.lib
- :members:
-
-:mod:`cubicweb.server.server`
-=============================
-
-.. automodule:: cubicweb.server.server
- :members:
-
-:mod:`cubicweb.server.utils`
-============================
-
-.. automodule:: cubicweb.server.utils
- :members:
-
-:mod:`cubicweb.server.checkintegrity`
-=====================================
-
-.. automodule:: cubicweb.server.checkintegrity
- :members:
-
-:mod:`cubicweb.server.rqlrewrite`
-=================================
-
-.. automodule:: cubicweb.server.rqlrewrite
- :members:
-
-:mod:`cubicweb.server.rqlannotation`
-====================================
-
-.. automodule:: cubicweb.server.rqlannotation
- :members:
-
-:mod:`cubicweb.server.hooks`
-============================
-
-.. automodule:: cubicweb.server.hooks
- :members:
-
-:mod:`cubicweb.server.hooksmanager`
-===================================
-
-.. automodule:: cubicweb.server.hooksmanager
- :members:
-
-:mod:`cubicweb.server.securityhooks`
-====================================
-
-.. automodule:: cubicweb.server.securityhooks
- :members:
-
-:mod:`cubicweb.server.schemahooks`
-==================================
-
-.. automodule:: cubicweb.server.schemahooks
- :members:
-
-:mod:`cubicweb.server.session`
-==============================
-
-.. automodule:: cubicweb.server.session
- :members:
-
-:mod:`cubicweb.server.serverctl`
-================================
-
-.. automodule:: cubicweb.server.serverctl
- :members:
-
-:mod:`cubicweb.server.serverconfig`
-===================================
-
-.. automodule:: cubicweb.server.serverconfig
- :members:
-
-:mod:`cubicweb.server.pool`
-===========================
-
-.. automodule:: cubicweb.server.pool
- :members:
-
-:mod:`cubicweb.server.mssteps`
-==============================
-
-.. automodule:: cubicweb.server.mssteps
- :members:
-
-:mod:`cubicweb.server.hookhelper`
-=================================
-
-.. automodule:: cubicweb.server.hookhelper
- :members:
-
-:mod:`cubicweb.server`
-======================
-
-.. automodule:: cubicweb.server
- :members:
-
-:mod:`cubicweb.server.sqlutils`
-===============================
-
-.. automodule:: cubicweb.server.sqlutils
- :members:
-
-:mod:`cubicweb.server.schemaserial`
-===================================
-
-.. automodule:: cubicweb.server.schemaserial
- :members:
-
-:mod:`cubicweb.server.repository`
-=================================
-
-.. automodule:: cubicweb.server.repository
- :members:
-
-:mod:`cubicweb.server.ssplanner`
-================================
-
-.. automodule:: cubicweb.server.ssplanner
- :members:
-
-:mod:`cubicweb.server.msplanner`
-================================
-
-.. automodule:: cubicweb.server.msplanner
- :members:
-
-:mod:`cubicweb.server.querier`
-==============================
-
-.. automodule:: cubicweb.server.querier
- :members:
-
-:mod:`cubicweb.server.migractions`
-==================================
-
-.. automodule:: cubicweb.server.migractions
- :members:
-
-:mod:`cubicweb.server.sources.rql2sql`
-======================================
-
-.. automodule:: cubicweb.server.sources.rql2sql
- :members:
-
-:mod:`cubicweb.server.sources.ldapuser`
-=======================================
-
-.. automodule:: cubicweb.server.sources.ldapuser
- :members:
-
-:mod:`cubicweb.server.sources`
-==============================
-
-.. automodule:: cubicweb.server.sources
- :members:
-
-:mod:`cubicweb.server.sources.pyrorql`
-======================================
-
-.. automodule:: cubicweb.server.sources.pyrorql
- :members:
-
-:mod:`cubicweb.server.sources.native`
-=====================================
-
-.. automodule:: cubicweb.server.sources.native
- :members:
-
-:mod:`cubicweb.server.sources.extlite`
-======================================
-
-.. automodule:: cubicweb.server.sources.extlite
- :members:
-
-:mod:`cubicweb.devtools.devctl`
-===============================
-
-.. automodule:: cubicweb.devtools.devctl
- :members:
-
-:mod:`cubicweb.devtools.pkginfo`
-================================
-
-.. automodule:: cubicweb.devtools.pkginfo
- :members:
-
-:mod:`cubicweb.devtools.migrtest`
-=================================
-
-.. automodule:: cubicweb.devtools.migrtest
- :members:
-
-:mod:`cubicweb.devtools.htmlparser`
-===================================
-
-.. automodule:: cubicweb.devtools.htmlparser
- :members:
-
-:mod:`cubicweb.devtools`
-========================
-
-.. automodule:: cubicweb.devtools
- :members:
-
-:mod:`cubicweb.devtools.fill`
-=============================
-
-.. automodule:: cubicweb.devtools.fill
- :members:
-
-:mod:`cubicweb.devtools._apptest`
-=================================
-
-.. automodule:: cubicweb.devtools._apptest
- :members:
-
-:mod:`cubicweb.devtools.stresstester`
-=====================================
-
-.. automodule:: cubicweb.devtools.stresstester
- :members:
-
-:mod:`cubicweb.devtools.fake`
-=============================
-
-.. automodule:: cubicweb.devtools.fake
- :members:
-
-:mod:`cubicweb.devtools.apptest`
-================================
-
-.. automodule:: cubicweb.devtools.apptest
- :members:
-
-:mod:`cubicweb.devtools.livetest`
-=================================
-
-.. automodule:: cubicweb.devtools.livetest
- :members:
-
-:mod:`cubicweb.devtools.testlib`
-================================
-
-.. automodule:: cubicweb.devtools.testlib
- :members:
-
-:mod:`cubicweb.devtools.repotest`
-=================================
-
-.. automodule:: cubicweb.devtools.repotest
- :members:
-
-:mod:`cubicweb.devtools.cwtwill`
-================================
-
-.. automodule:: cubicweb.devtools.cwtwill
- :members:
-
-:mod:`cubicweb.misc.cwdesklets.rqlsensor`
-=========================================
-
-.. automodule:: cubicweb.misc.cwdesklets.rqlsensor
- :members:
-
-:mod:`cubicweb.embedded.mx`
-===========================
-
-.. automodule:: cubicweb.embedded.mx
- :members:
-
-:mod:`cubicweb.embedded.mx.DateTime.mxDateTime_python`
-======================================================
-
-.. automodule:: cubicweb.embedded.mx.DateTime.mxDateTime_python
- :members:
-
-:mod:`cubicweb.embedded.mx.DateTime.ARPA`
-=========================================
-
-.. automodule:: cubicweb.embedded.mx.DateTime.ARPA
- :members:
-
-:mod:`cubicweb.embedded.mx.DateTime.ISO`
-========================================
-
-.. automodule:: cubicweb.embedded.mx.DateTime.ISO
- :members:
-
-:mod:`cubicweb.embedded.mx.DateTime.Parser`
-===========================================
-
-.. automodule:: cubicweb.embedded.mx.DateTime.Parser
- :members:
-
-:mod:`cubicweb.embedded.mx.DateTime`
-====================================
-
-.. automodule:: cubicweb.embedded.mx.DateTime
- :members:
-
-:mod:`cubicweb.embedded.mx.DateTime.Timezone`
-=============================================
-
-.. automodule:: cubicweb.embedded.mx.DateTime.Timezone
- :members:
-
-:mod:`cubicweb.embedded.mx.DateTime.DateTime`
-=============================================
-
-.. automodule:: cubicweb.embedded.mx.DateTime.DateTime
- :members:
-
-:mod:`indexer`
-==============
-
-.. automodule:: indexer
- :members:
-
-:mod:`indexer.indexable_objects`
-================================
-
-.. automodule:: indexer.indexable_objects
- :members:
-
-:mod:`indexer.search`
-=====================
-
-.. automodule:: indexer.search
- :members:
-
-:mod:`indexer.query_objects`
-============================
-
-.. automodule:: indexer.query_objects
- :members:
-
-:mod:`indexer._exceptions`
-==========================
-
-.. automodule:: indexer._exceptions
- :members:
-
-:mod:`indexer.setup`
-====================
-
-.. automodule:: indexer.setup
- :members:
-
-:mod:`indexer.query`
-====================
-
-.. automodule:: indexer.query
- :members:
-
-:mod:`logilab`
-==============
-
-.. automodule:: logilab
- :members:
-
-:mod:`logilab.constraint.propagation`
-=====================================
-
-.. automodule:: logilab.constraint.propagation
- :members:
-
-:mod:`logilab.constraint.psyco_wrapper`
-=======================================
-
-.. automodule:: logilab.constraint.psyco_wrapper
- :members:
-
-:mod:`logilab.constraint.fd`
-============================
-
-.. automodule:: logilab.constraint.fd
- :members:
-
-:mod:`logilab.constraint.fi`
-============================
-
-.. automodule:: logilab.constraint.fi
- :members:
-
-:mod:`logilab.constraint`
-=========================
-
-.. automodule:: logilab.constraint
- :members:
-
-:mod:`logilab.constraint.setup`
-===============================
-
-.. automodule:: logilab.constraint.setup
- :members:
-
-:mod:`logilab.constraint.interfaces`
-====================================
-
-.. automodule:: logilab.constraint.interfaces
- :members:
-
-:mod:`logilab.constraint.distributors`
-======================================
-
-.. automodule:: logilab.constraint.distributors
- :members:
-
-:mod:`logilab.common.clcommands`
-================================
-
-.. automodule:: logilab.common.clcommands
- :members:
-
-:mod:`logilab.common.table`
-===========================
-
-.. automodule:: logilab.common.table
- :members:
-
-:mod:`logilab.common.interface`
-===============================
-
-.. automodule:: logilab.common.interface
- :members:
-
-:mod:`logilab.common.logger`
-============================
-
-.. automodule:: logilab.common.logger
- :members:
-
-:mod:`logilab.common.cli`
-=========================
-
-.. automodule:: logilab.common.cli
- :members:
-
-:mod:`logilab.common.xmlrpcutils`
-=================================
-
-.. automodule:: logilab.common.xmlrpcutils
- :members:
-
-:mod:`logilab.common.corbautils`
-================================
-
-.. automodule:: logilab.common.corbautils
- :members:
-
-:mod:`logilab.common.cache`
-===========================
-
-.. automodule:: logilab.common.cache
- :members:
-
-:mod:`logilab.common.astutils`
-==============================
-
-.. automodule:: logilab.common.astutils
- :members:
-
-:mod:`logilab.common.daemon`
-============================
-
-.. automodule:: logilab.common.daemon
- :members:
-
-:mod:`logilab.common.tree`
-==========================
-
-.. automodule:: logilab.common.tree
- :members:
-
-:mod:`logilab.common.textutils`
-===============================
-
-.. automodule:: logilab.common.textutils
- :members:
-
-:mod:`logilab.common.modutils`
-==============================
-
-.. automodule:: logilab.common.modutils
- :members:
-
-:mod:`logilab.common.fileutils`
-===============================
-
-.. automodule:: logilab.common.fileutils
- :members:
-
-:mod:`logilab.common.patricia`
-==============================
-
-.. automodule:: logilab.common.patricia
- :members:
-
-:mod:`logilab.common.date`
-==========================
-
-.. automodule:: logilab.common.date
- :members:
-
-:mod:`logilab.common.optparser`
-===============================
-
-.. automodule:: logilab.common.optparser
- :members:
-
-:mod:`logilab.common.twisted_distutils`
-=======================================
-
-.. automodule:: logilab.common.twisted_distutils
- :members:
-
-:mod:`logilab.common.decorators`
-================================
-
-.. automodule:: logilab.common.decorators
- :members:
-
-:mod:`logilab.common.db`
-========================
-
-.. automodule:: logilab.common.db
- :members:
-
-:mod:`logilab.common.deprecation`
-=================================
-
-.. automodule:: logilab.common.deprecation
- :members:
-
-:mod:`logilab.common.tasksqueue`
-================================
-
-.. automodule:: logilab.common.tasksqueue
- :members:
-
-:mod:`logilab.common.changelog`
-===============================
-
-.. automodule:: logilab.common.changelog
- :members:
-
-:mod:`logilab.common.shellutils`
-================================
-
-.. automodule:: logilab.common.shellutils
- :members:
-
-:mod:`logilab.common.sqlgen`
-============================
-
-.. automodule:: logilab.common.sqlgen
- :members:
-
-:mod:`logilab.common.optik_ext`
-===============================
-
-.. automodule:: logilab.common.optik_ext
- :members:
-
-:mod:`logilab.common.configuration`
-===================================
-
-.. automodule:: logilab.common.configuration
- :members:
-
-:mod:`logilab.common.visitor`
-=============================
-
-.. automodule:: logilab.common.visitor
- :members:
-
-:mod:`logilab.common.pytest`
-============================
-
-.. automodule:: logilab.common.pytest
- :members:
-
-:mod:`logilab.common`
-=====================
-
-.. automodule:: logilab.common
- :members:
-
-:mod:`logilab.common.setup`
-===========================
-
-.. automodule:: logilab.common.setup
- :members:
-
-:mod:`logilab.common.logservice`
-================================
-
-.. automodule:: logilab.common.logservice
- :members:
-
-:mod:`logilab.common.debugger`
-==============================
-
-.. automodule:: logilab.common.debugger
- :members:
-
-:mod:`logilab.common.html`
-==========================
-
-.. automodule:: logilab.common.html
- :members:
-
-:mod:`logilab.common.vcgutils`
-==============================
-
-.. automodule:: logilab.common.vcgutils
- :members:
-
-:mod:`logilab.common.compat`
-============================
-
-.. automodule:: logilab.common.compat
- :members:
-
-:mod:`logilab.common.logging_ext`
-=================================
-
-.. automodule:: logilab.common.logging_ext
- :members:
-
-:mod:`logilab.common.umessage`
-==============================
-
-.. automodule:: logilab.common.umessage
- :members:
-
-:mod:`logilab.common.proc`
-==========================
-
-.. automodule:: logilab.common.proc
- :members:
-
-:mod:`logilab.common.monclient`
-===============================
-
-.. automodule:: logilab.common.monclient
- :members:
-
-:mod:`logilab.common.bind`
-==========================
-
-.. automodule:: logilab.common.bind
- :members:
-
-:mod:`logilab.common.graph`
-===========================
-
-.. automodule:: logilab.common.graph
- :members:
-
-:mod:`logilab.common.testlib`
-=============================
-
-.. automodule:: logilab.common.testlib
- :members:
-
-:mod:`logilab.common.contexts`
-==============================
-
-.. automodule:: logilab.common.contexts
- :members:
-
-:mod:`logilab.common.adbh`
-==========================
-
-.. automodule:: logilab.common.adbh
- :members:
-
-:mod:`logilab.common.pdf_ext`
-=============================
-
-.. automodule:: logilab.common.pdf_ext
- :members:
-
-:mod:`logilab.common.monserver`
-===============================
-
-.. automodule:: logilab.common.monserver
- :members:
-
-:mod:`logilab.common.ureports.nodes`
-====================================
-
-.. automodule:: logilab.common.ureports.nodes
- :members:
-
-:mod:`logilab.common.ureports`
-==============================
-
-.. automodule:: logilab.common.ureports
- :members:
-
-:mod:`logilab.common.ureports.html_writer`
-==========================================
-
-.. automodule:: logilab.common.ureports.html_writer
- :members:
-
-:mod:`logilab.common.ureports.text_writer`
-==========================================
-
-.. automodule:: logilab.common.ureports.text_writer
- :members:
-
-:mod:`logilab.common.ureports.docbook_writer`
-=============================================
-
-.. automodule:: logilab.common.ureports.docbook_writer
- :members:
-
-:mod:`logilab.mtconverter.engine`
-=================================
-
-.. automodule:: logilab.mtconverter.engine
- :members:
-
-:mod:`logilab.mtconverter.transform`
-====================================
-
-.. automodule:: logilab.mtconverter.transform
- :members:
-
-:mod:`logilab.mtconverter`
-==========================
-
-.. automodule:: logilab.mtconverter
- :members:
-
-:mod:`logilab.mtconverter.setup`
-================================
-
-.. automodule:: logilab.mtconverter.setup
- :members:
-
-:mod:`logilab.mtconverter.transforms.html2text`
-===============================================
-
-.. automodule:: logilab.mtconverter.transforms.html2text
- :members:
-
-:mod:`logilab.mtconverter.transforms.cmdtransforms`
-===================================================
-
-.. automodule:: logilab.mtconverter.transforms.cmdtransforms
- :members:
-
-:mod:`logilab.mtconverter.transforms.python`
-============================================
-
-.. automodule:: logilab.mtconverter.transforms.python
- :members:
-
-:mod:`logilab.mtconverter.transforms.pygmentstransforms`
-========================================================
-
-.. automodule:: logilab.mtconverter.transforms.pygmentstransforms
- :members:
-
-:mod:`logilab.mtconverter.transforms`
-=====================================
-
-.. automodule:: logilab.mtconverter.transforms
- :members:
-
-:mod:`logilab.mtconverter.transforms.piltransforms`
-===================================================
-
-.. automodule:: logilab.mtconverter.transforms.piltransforms
- :members:
-
-:mod:`logilab.devtools.cvstatus`
-================================
-
-.. automodule:: logilab.devtools.cvstatus
- :members:
-
-:mod:`logilab.devtools.changelog`
-=================================
-
-.. automodule:: logilab.devtools.changelog
- :members:
-
-:mod:`logilab.devtools`
-=======================
-
-.. automodule:: logilab.devtools
- :members:
-
-:mod:`logilab.devtools.setup`
-=============================
-
-.. automodule:: logilab.devtools.setup
- :members:
-
-:mod:`logilab.devtools.cvslog`
-==============================
-
-.. automodule:: logilab.devtools.cvslog
- :members:
-
-:mod:`logilab.devtools.lgp.utils`
-=================================
-
-.. automodule:: logilab.devtools.lgp.utils
- :members:
-
-:mod:`logilab.devtools.lgp.tag`
-===============================
-
-.. automodule:: logilab.devtools.lgp.tag
- :members:
-
-:mod:`logilab.devtools.lgp.setupinfo`
-=====================================
-
-.. automodule:: logilab.devtools.lgp.setupinfo
- :members:
-
-:mod:`logilab.devtools.lgp.changelog`
-=====================================
-
-.. automodule:: logilab.devtools.lgp.changelog
- :members:
-
-:mod:`logilab.devtools.lgp.preparedist`
-=======================================
-
-.. automodule:: logilab.devtools.lgp.preparedist
- :members:
-
-:mod:`logilab.devtools.lgp.build`
-=================================
-
-.. automodule:: logilab.devtools.lgp.build
- :members:
-
-:mod:`logilab.devtools.lgp.clean`
-=================================
-
-.. automodule:: logilab.devtools.lgp.clean
- :members:
-
-:mod:`logilab.devtools.lgp`
-===========================
-
-.. automodule:: logilab.devtools.lgp
- :members:
-
-:mod:`logilab.devtools.lgp.setup`
-=================================
-
-.. automodule:: logilab.devtools.lgp.setup
- :members:
-
-:mod:`logilab.devtools.lgp.check`
-=================================
-
-.. automodule:: logilab.devtools.lgp.check
- :members:
-
-:mod:`logilab.devtools.lgp.exceptions`
-======================================
-
-.. automodule:: logilab.devtools.lgp.exceptions
- :members:
-
-:mod:`logilab.devtools.templates`
-=================================
-
-.. automodule:: logilab.devtools.templates
- :members:
-
-:mod:`logilab.devtools.templates.setup`
-=======================================
-
-.. automodule:: logilab.devtools.templates.setup
- :members:
-
-:mod:`logilab.devtools.lib.coverage`
-====================================
-
-.. automodule:: logilab.devtools.lib.coverage
- :members:
-
-:mod:`logilab.devtools.lib.manifest`
-====================================
-
-.. automodule:: logilab.devtools.lib.manifest
- :members:
-
-:mod:`logilab.devtools.lib.pkginfo`
-===================================
-
-.. automodule:: logilab.devtools.lib.pkginfo
- :members:
-
-:mod:`logilab.devtools.lib`
-===========================
-
-.. automodule:: logilab.devtools.lib
- :members:
-
-:mod:`logilab.devtools.vcslib.cvsparse`
-=======================================
-
-.. automodule:: logilab.devtools.vcslib.cvsparse
- :members:
-
-:mod:`logilab.devtools.vcslib.svn`
-==================================
-
-.. automodule:: logilab.devtools.vcslib.svn
- :members:
-
-:mod:`logilab.devtools.vcslib.node`
-===================================
-
-.. automodule:: logilab.devtools.vcslib.node
- :members:
-
-:mod:`logilab.devtools.vcslib`
-==============================
-
-.. automodule:: logilab.devtools.vcslib
- :members:
-
-:mod:`logilab.devtools.vcslib.interfaces`
-=========================================
-
-.. automodule:: logilab.devtools.vcslib.interfaces
- :members:
-
-:mod:`logilab.devtools.vcslib.cvs`
-==================================
-
-.. automodule:: logilab.devtools.vcslib.cvs
- :members:
-
-:mod:`logilab.devtools.vcslib.hg`
-=================================
-
-.. automodule:: logilab.devtools.vcslib.hg
- :members:
-
-:mod:`rql.nodes`
-================
-
-.. automodule:: rql.nodes
- :members:
-
-:mod:`rql.undo`
-===============
-
-.. automodule:: rql.undo
- :members:
-
-:mod:`rql.utils`
-================
-
-.. automodule:: rql.utils
- :members:
-
-:mod:`rql.base`
-===============
-
-.. automodule:: rql.base
- :members:
-
-:mod:`rql.analyze`
-==================
-
-.. automodule:: rql.analyze
- :members:
-
-:mod:`rql._exceptions`
-======================
-
-.. automodule:: rql._exceptions
- :members:
-
-:mod:`rql.compare`
-==================
-
-.. automodule:: rql.compare
- :members:
-
-:mod:`rql.stmts`
-================
-
-.. automodule:: rql.stmts
- :members:
-
-:mod:`rql.parser_main`
-======================
-
-.. automodule:: rql.parser_main
- :members:
-
-:mod:`rql.stcheck`
-==================
-
-.. automodule:: rql.stcheck
- :members:
-
-:mod:`rql.parser`
-=================
-
-.. automodule:: rql.parser
- :members:
-
-:mod:`rql`
-==========
-
-.. automodule:: rql
- :members:
-
-:mod:`rql.setup`
-================
-
-.. automodule:: rql.setup
- :members:
-
-:mod:`rql.interfaces`
-=====================
-
-.. automodule:: rql.interfaces
- :members:
-
-:mod:`rql.editextensions`
-=========================
-
-.. automodule:: rql.editextensions
- :members:
-
-:mod:`rql.fol`
-==============
-
-.. automodule:: rql.fol
- :members:
-
-:mod:`rqlgen`
-=============
-
-.. automodule:: rqlgen
- :members:
-
-:mod:`yams.schema`
-==================
-
-.. automodule:: yams.schema
- :members:
-
-:mod:`yams.reader`
-==================
-
-.. automodule:: yams.reader
- :members:
-
-:mod:`yams.schema2sql`
-======================
-
-.. automodule:: yams.schema2sql
- :members:
-
-:mod:`yams._exceptions`
-=======================
-
-.. automodule:: yams._exceptions
- :members:
-
-:mod:`yams.sqlreader`
-=====================
-
-.. automodule:: yams.sqlreader
- :members:
-
-:mod:`yams.schema2dot`
-======================
-
-.. automodule:: yams.schema2dot
- :members:
-
-:mod:`yams`
-===========
-
-.. automodule:: yams
- :members:
-
-:mod:`yams.setup`
-=================
-
-.. automodule:: yams.setup
- :members:
-
-:mod:`yams.interfaces`
-======================
-
-.. automodule:: yams.interfaces
- :members:
-
-:mod:`yams.buildobjs`
-=====================
-
-.. automodule:: yams.buildobjs
- :members:
-
-:mod:`yams.constraints`
-=======================
-
-.. automodule:: yams.constraints
- :members:
--- a/doc/book/en/Z010-beginners.en.txt Thu Mar 04 18:57:13 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-.. _QuickInstall:
-
-Quick Installation of a *CubicWeb* instance
-===========================================
-
-.. include:: C010-setup.en.txt
-.. include:: Z012-create-instance.en.txt
-
-
--- a/doc/book/en/Z012-create-instance.en.txt Thu Mar 04 18:57:13 2010 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-.. -*- coding: utf-8 -*-
-
-===============================
-Creation of your first instance
-===============================
-
-What is an instance?
---------------------
-
-A *CubicWeb* instance is a container that
-refers to cubes and configuration parameters for your web instance.
-Each instance is stored as a directory in ``~/etc/cubicweb.d`` which enables
-us to run your instance.
-
-What is a cube?
----------------
-
-Cubes represent data and basic building bricks of your web instances :
-blogs, person, date, addressbook and a lot more.
-
-.. XXX They related to each other by a 'Schema' which is also the PostGres representation.
-
-Each cube defines entities, their views, their schemas and workflows
-in an independant directory located in ``/path/to/forest/cubicweb/cubes/``
-for a Mercurial installation or in ``/usr/share/cubicweb/cubes`` for
-a debian package installation. For example, the 'blog' cube defines the entities
-blogs and blogentries.
-
-When an *CubicWeb* instance is created, you list the cubes that you want to use.
-Using a cube means having the entities defined in your cube's schema
-available in your instance as well as their views and workflows.
-
-
-Creating a basic *CubicWeb* Instance
-------------------------------------
-
-We can create an instance to view our
-instance in a web browser. ::
-
- cubicweb-ctl create blog myblog
-
-.. XXX or ::
-
-.. XXX cubicweb-ctl create forge myforge
-
-
-.. note::
- The commands used below are more detailled in the section dedicated to
- :ref:`cubicweb-ctl`.
-
-A series of questions will be prompted to you, the default answer is usually
-sufficient. You can allways modify the parameters later by editing
-configuration files. When a user/psswd is requested to access the database
-please use the login you create at the time you configured the database
-(:ref:`ConfigurationPostgresql`).
-
-It is important to distinguish here the user used to access the database and
-the user used to login to the cubicweb instance. When a *CubicWeb* instance
-starts, it uses the login/psswd for the database to get the schema and handle
-low level transaction. But, when ``cubicweb-ctl create`` asks for
-a manager login/psswd of *CubicWeb*, it refers to an instance user
-to administrate your web instance.
-The configuration files are stored in *~/etc/cubicweb.d/myblog/*.
-
-To launch the web instance, you just type ::
-
- cubicweb-ctl start myblog
-
-You can see how it looks by
-visiting the URL `http://localhost:8080`.
-To login, please use the cubicweb administrator login/psswd you
-defined when you created the instance.
-
-To shutdown the instance ::
-
- cubicweb-ctl stop myinstance
-
-.. XXX something like `cubicweb-ctl live-server intra` would be nice
-
-
--- a/doc/book/en/admin/additional-tips.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/admin/additional-tips.rst Fri Mar 05 18:20:18 2010 +0100
@@ -28,7 +28,7 @@
Simply use the pg_dump in a cron ::
- pg_dump -Fc --username=cubicweb --no-owner --file=/var/lib/cubicweb/backup/<instance>-$(date '+%Y-%m-%d_%H:%M:%S').dump
+ su -c "pg_dump -Fc --username=cubicweb --no-owner" postgres > <your-instance>-$(date '+%Y-%m-%d_%H:%M:%S').dump
**CubicWeb way**
--- a/doc/book/en/admin/index.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/admin/index.rst Fri Mar 05 18:20:18 2010 +0100
@@ -19,6 +19,7 @@
site-config
multisources
ldap
+ pyro
gae
additional-tips
--- a/doc/book/en/admin/instance-config.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/admin/instance-config.rst Fri Mar 05 18:20:18 2010 +0100
@@ -12,7 +12,8 @@
/etc/cubicweb.d/myblog/all-in-one.conf
-It is a simple text file format INI. In the following description,
+It is a simple text file in the INI format
+(http://en.wikipedia.org/wiki/INI_file). In the following description,
each option name is prefixed with its own section and followed by its
default value if necessary, e.g. "`<section>.<option>` [value]."
@@ -64,8 +65,8 @@
base-url = http://localhost/demo
https-url = `https://localhost/demo`
-Setting up the web
-------------------
+Setting up the web client
+-------------------------
:`web.embed-allowed`:
regular expression matching sites which could be "embedded" in
the site (controllers 'embed')
@@ -92,22 +93,26 @@
-----------------------------------
Web server side:
-:`pyro-client.pyro-instance-id`:
+:`pyro.pyro-instance-id`:
pyro identifier of RQL server (e.g. the instance name)
RQL server side:
-:`pyro-server.pyro-port`:
- pyro port number. If none is specified, a port is assigned
+:`main.pyro-server`:
+ boolean to switch on/off pyro server-side
+
+:`pyro.pyro-host`:
+ pyro host:port number. If no port is specified, it is assigned
automatically.
RQL and web servers side:
-:`pyro-name-server.pyro-ns-host`:
+:`pyro.pyro-ns-host`:
hostname hosting pyro server name. If no value is
specified, it is located by a request from broadcast
-:`pyro-name-server.pyro-ns-group` [cubicweb]:
- pyro group in which to save the instance
+
+:`pyro.pyro-ns-group`:
+ pyro group in which to save the instance (will default to 'cubicweb')
Configuring e-mail
--- a/doc/book/en/admin/ldap.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/admin/ldap.rst Fri Mar 05 18:20:18 2010 +0100
@@ -1,4 +1,68 @@
LDAP integration
================
-XXX feed me
\ No newline at end of file
+Overview
+--------
+
+Using LDAP as a source for user credentials and information is quite
+easy. The most difficult part lies in building an LDAP schema or
+using an existing one.
+
+At cube creation time, one is asked if more sources are wanted. LDAP
+is one possible option at this time. Of course, it is always possible
+to set it up later in the `source` configuration file, which we
+discuss there.
+
+It is possible to add as many LDAP sources as wanted, which translates
+in as many [ldapxxx] sections in the `source` configuration file.
+
+The general principle of the LDAP source is, given a proper
+configuration, to create local users matching the users available in
+the directory, deriving local user attributes from directory users
+attributes. Then a periodic task ensures local user information
+synchronization with the directory.
+
+Credential checks are _always_ done against the LDAP server.
+
+The base functionality for this is in
+cubicweb/server/sources/ldapuser.py.
+
+Configurations options
+----------------------
+
+Let us enumerate the options (but please keep in mind that the
+authoritative source for these is in the aforementioned python
+module), by categories (LDAP server connection, LDAP schema mapping
+information, LDAP source internal configuration).
+
+LDAP server connection options:
+
+* host: may contain port information using <host>:<port> notation.
+* protocol (choices are ldap, ldaps, ldapi)
+* auth-mode (choices are simple, cram_md5, digest_md5, gssapi, support
+ for the later being partial as of now)
+* auth-realm, realm to use when using gssapi/kerberos authentication
+* data-cnx-dn, user dn to use to open data connection to the ldap (eg
+ used to respond to rql queries)
+* data-cnx-password, password to use to open data connection to the
+ ldap (eg used to respond to rql queries)
+
+If the LDAP server accepts anonymous binds, then it is possible to
+leave data-cnx-dn and data-cnx-password empty. This is, however, quite
+unlikely in practice.
+
+LDAP schema mapping:
+
+* user-base-dn, base DN to lookup for users
+* user-scope, user search scope
+* user-classes, classes of user
+* user-attrs-map, map from ldap user attributes to cubicweb attributes
+* user-login-attr, attribute used as login on authentication
+
+LDAP source internal configuration:
+
+* user-default-group, name of a group in which ldap users will be by
+ default. You can set multiple groups by separating them by a comma
+* synchronization-interval, interval between synchronization with the
+ ldap directory in seconds (default to once a day)
+* life time of query cache in minutes (default to two hours).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/book/en/admin/pyro.rst Fri Mar 05 18:20:18 2010 +0100
@@ -0,0 +1,39 @@
+Working with a distributed client (using Pyro)
+==============================================
+
+In some circumstances, it is practical to split the repository and
+web-client parts of the application, for load-balancing reasons. Or
+one wants to access the repository from independant scripts to consult
+or update the database.
+
+For this to work, several steps have to be taken in order.
+
+You must first ensure that the apropriate software is installed and
+running (see ref:`setup`)::
+
+ pyro-nsd -x -p 6969
+
+Then you have to set appropriate options in your configuration. For
+instance::
+
+ pyro-server=yes
+ pyro-ns-host=localhost:6969
+
+ pyro-instance-id=myinstancename
+
+Finally, the client (for instance in the case of a script) must
+connect specifically, as in the following example code:
+
+.. sourcecode:: python
+
+ from cubicweb import dbapi
+
+ def pyro_connect(instname, login, password, pyro_ns_host):
+ cnx = dbapi.connect(instname, login, password, pyro_ns_host)
+ cnx.load_appobjects()
+ return cnx
+
+The 'cnx.load_appobjects()' line is optional. Without it you will get
+data through the connection roughly as you would from a DBAPI
+connection. With it, provided the cubicweb-client part is installed
+and accessible, you get the ORM goodies.
--- a/doc/book/en/admin/setup.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/admin/setup.rst Fri Mar 05 18:20:18 2010 +0100
@@ -132,7 +132,7 @@
Please be careful to select the right python (2.5) and postgres (8.4)
versions.
-Pyro enable remote access to cubicweb repository instances. Get it
+Pyro enables remote access to cubicweb repository instances. Get it
there::
http://sourceforge.net/projects/pyro/files/
@@ -373,7 +373,7 @@
```````````````````
Yout must add the following lines in ``/etc/mysql/my.cnf`` file::
- transaction-isolation = READ-COMMITTED
+ transaction-isolation=READ-COMMITTED
default-storage-engine=INNODB
default-character-set=utf8
max_allowed_packet = 128M
@@ -382,6 +382,26 @@
It is unclear whether mysql supports indexed string of arbitrary lenght or
not.
+SQLServer configuration
+-----------------------
+
+As of this writing, sqlserver support is in progress. You should be
+able to connect, create a database and go quite far, but some of the
+generated SQL is still currently not accepted by the backend.
+
+The `source` configuration file may look like this (specific parts
+only are shown)::
+
+ [system]
+ db-driver=sqlserver2005
+ db-user=someuser
+ # database password not needed
+ #db-password=toto123
+ #db-create/init may ask for a pwd: just say anything
+ db-extra-arguments=Trusted_Connection
+ db-encoding=utf8
+
+
Pyro configuration
------------------
--- a/doc/book/en/annexes/cubicweb-ctl.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/annexes/cubicweb-ctl.rst Fri Mar 05 18:20:18 2010 +0100
@@ -22,45 +22,71 @@
cubicweb-ctl <command> --help
-Command to create a cube
-------------------------
+Listing available cubes and instance
+-------------------------------------
-* ``newcube``, create a new cube on the file system based on the name
- given in the parameters. This command create a cube from a skeleton
- that includes default files required for debian packaging.
+* ``list``, provides a list of the available configuration, cubes
+ and instances.
-Command to create an instance
------------------------------
-* ``create``, creates the files for the instance configuration
+Creation of a new cube
+-----------------------
+
+Create your new cube cube ::
+
+ cubicweb-ctl newcube
+
+This will create a new cube in
+``/path/to/forest/cubicweb/cubes/<mycube>`` for a Mercurial forest
+installation, or in ``/usr/share/cubicweb/cubes`` for a debian
+packages installation.
+
+Create an instance
+-------------------
+
+You must ensure `~/cubicweb.d/` exists prior to this. On windows, the
+'~' part will probably expand to 'Documents and Settings/user'.
+
+To create an instance from an existing cube, execute the following
+command ::
+
+ cubicweb-ctl create <cube_name> <instance_name>
+
+This command will create the configuration files of an instance in
+``~/etc/cubicweb.d/<instance_name>``.
+
+The tool ``cubicweb-ctl`` executes the command ``db-create`` and
+``db-init`` when you run ``create`` so that you can complete an
+instance creation in a single command. But of course it is possible
+to issue these separate commands separately, at a later stage.
+
+Command to create/initialize an instance database
+-------------------------------------------------
+
* ``db-create``, creates the system database of an instance (tables and
extensions only)
* ``db-init``, initializes the system database of an instance
(schema, groups, users, workflows...)
-By default, those three commandes are encapsulated in ``create`` so
-that they can be executed consecutively.
-
-Command to create an instance for Google AppEngine datastore source
--------------------------------------------------------------------
-* ``newgapp``, creates the configuration files for an instance
-
-This command needs to be followed by the commands responsible for
-the database initialization. As those are specific to the `datastore`,
-specific Google AppEgine database, they are not available for now
-in cubicweb-ctl, but they are available in the instance created.
-
-For more details, please see :ref:`gaecontents` .
-
Commands to control instances
-----------------------------
+
* ``start``, starts one or more or all instances
+
+of special interest::
+
+ start -D
+
+will start in debug mode (under windows, starting without -D will not
+work; you need instead to setup your instance as a service).
+
* ``stop``, stops one or more or all instances
* ``restart``, restarts one or more or all instances
-* ``status``, returns the status of the instance
+* ``status``, returns the status of the instance(s)
Commands to maintain instances
------------------------------
+
* ``upgrade``, launches the existing instances migration when a new version
of *CubicWeb* or the cubes installed is available
* ``shell``, opens a migration shell for manual maintenance of the instance
@@ -82,41 +108,15 @@
Other commands
--------------
-* ``list``, provides a list of the available configuration, cubes
- and instances.
* ``delete``, deletes an instance (configuration files and database)
-
-Create an instance from an existing cube
-````````````````````````````````````````
-
-To create an instance from an existing cube, execute the following
-command ::
-
- cubicweb-ctl create <cube_name> <instance_name>
-
-This command will create the configuration files of an instance in
-``~/etc/cubicweb.d/<instance_name>``.
-The tool ``cubicweb-ctl`` allows you to execute the command ``db-create``
-and ``db-init`` when you run ``create`` so that you can complete an
-instance creation in a single command.
+Command to create an instance for Google AppEngine datastore source
+-------------------------------------------------------------------
+* ``newgapp``, creates the configuration files for an instance
-If you decide not to execut those commands while ``cubicweb-ctl create``,
-then you will have to execute them seperately(``cubicweb-ctl db-create``,
-``cubicweb-ctl db-init`` ) otherwise your installation will not be complete
-and you will not be able to launch your instance.
-
-
-Creation of an instance from a new cube
-```````````````````````````````````````
+This command needs to be followed by the commands responsible for
+the database initialization. As those are specific to the `datastore`,
+specific Google AppEgine database, they are not available for now
+in cubicweb-ctl, but they are available in the instance created.
-Create first your new cube cube ::
-
- cubicweb-ctl newcube <mycube>
-
-This will create a new cube in ``/path/to/forest/cubicweb/cubes/<mycube>``
-for a Mercurial forest installation, or in ``/usr/share/cubicweb/cubes``
-for a debian packages installation, and then create an instance as
-explained just above.
-
-
+For more details, please see :ref:`gaecontents` .
--- a/doc/book/en/annexes/faq.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/annexes/faq.rst Fri Mar 05 18:20:18 2010 +0100
@@ -44,6 +44,10 @@
learning a new dialect. By using Python, we use standard OOP techniques and
this is a key factor in a robust application.
+The `cwtags` (http://www.cubicweb.org/project/cwtags) package can be
+used in cubes to help generate html from Python with more comfort than
+raw strings.
+
Why do you use the LGPL license to prevent me from doing X ?
------------------------------------------------------------
@@ -88,19 +92,22 @@
component). Google App Engine is yet another supported target for
RQL.
-[copy answer from forum, explain why similar to sparql and why better
- than django and SQL]
-
-which ajax library is CubicWeb using ?
+Which ajax library is CubicWeb using ?
--------------------------------------
-[CubicWeb uses jQuery and adds a thin layer on top of that]
+CubicWeb uses jQuery and provides a few helpers on top of
+that. Additionally, some jQuery plugins are provided (some are
+provided in specific cubes).
How is security implemented ?
------------------------------
-This is an example of how it works in our framework:
+The basis for security is a mapping from operations to groups or
+arbitrary RQL expressions. These mappings are scoped to entities and
+relations.
+
+This is an example for an Entity Type definition:
.. sourcecode:: python
@@ -108,36 +115,43 @@
"""a version is defining the content of a particular project's
release"""
# definition of attributes is voluntarily missing
- permissions = {'read': ('managers', 'users', 'guests',),
- 'update': ('managers', 'logilab', 'owners',),
- 'delete': ('managers', ),
- 'add': ('managers', 'logilab',
- ERQLExpression('X version_of PROJ, U in_group G, '
- 'PROJ require_permission P, '
- 'P name "add_version", P require_group G'),)}
+ __permissions__ = {'read': ('managers', 'users', 'guests',),
+ 'update': ('managers', 'logilab', 'owners'),
+ 'delete': ('managers',),
+ 'add': ('managers', 'logilab',
+ ERQLExpression('X version_of PROJ, U in_group G, '
+ 'PROJ require_permission P, '
+ 'P name "add_version", P require_group G'),)}
The above means that permission to read a Version is granted to any
user that is part of one of the groups 'managers', 'users', 'guests'.
The 'add' permission is granted to users in group 'managers' or
-'logilab' and to users in group G, if G is linked by a permission
+'logilab' or to users in group G, if G is linked by a permission
entity named "add_version" to the version's project.
+An example for a Relation Definition (RelationType both defines a
+relation type and implicitly one relation definition, on which the
+permissions actually apply):
+
.. sourcecode:: python
class version_of(RelationType):
"""link a version to its project. A version is necessarily linked
to one and only one project. """
# some lines voluntarily missing
- permissions = {'read': ('managers', 'users', 'guests',),
- 'delete': ('managers', ),
- 'add': ('managers', 'logilab',
- RRQLExpression('O require_permission P, P name "add_version", '
- 'U in_group G, P require_group G'),) }
+ __permissions__ = {'read': ('managers', 'users', 'guests',),
+ 'delete': ('managers', ),
+ 'add': ('managers', 'logilab',
+ RRQLExpression('O require_permission P, P name "add_version", '
+ 'U in_group G, P require_group G'),) }
+
+The main difference lies in the basic available operations (there is
+no 'update' operation) and the usage of an RRQLExpression (rql
+expression for a relation) instead of an ERQLExpression (rql
+expression for an entity).
You can find additional information in the section :ref:`security`.
-[XXX what does the second example means in addition to the first one?]
-
What is `Error while publishing rest text ...` ?
------------------------------------------------
@@ -270,6 +284,8 @@
Any change applied to configuration file requires to restart your
instance.
+You can find additional information in the section :ref:`LDAP`.
+
I get NoSelectableObject exceptions, how do I debug selectors ?
---------------------------------------------------------------
--- a/doc/book/en/conf.py Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/conf.py Fri Mar 05 18:20:18 2010 +0100
@@ -61,7 +61,7 @@
today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
-unused_docs = ['D070-modules-cbw-api.en',]
+unused_docs = []
# List of directories, relative to source directories, that shouldn't be searched
# for source files.
--- a/doc/book/en/development/datamodel/definition.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/datamodel/definition.rst Fri Mar 05 18:20:18 2010 +0100
@@ -266,8 +266,11 @@
This can only be used for the actions `update` and `delete` of an entity
type.
-It is also possible to use specific groups if they are defined in the precreate
-of the cube (``migration/precreate.py``).
+It is also possible to use specific groups if they are defined in the
+precreate of the cube (``migration/precreate.py``). Defining groups in
+postcreate or even later makes them NOT available for security
+purposes (in this case, an `sync_schema_props_perms` command have to
+be issued in a CubicWeb shell).
Use of RQL expression for write permissions
--- a/doc/book/en/development/devweb/controllers.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/devweb/controllers.rst Fri Mar 05 18:20:18 2010 +0100
@@ -1,4 +1,79 @@
Controllers
-----------
-XXX the view controller, other controllers
\ No newline at end of file
+Overview
+++++++++
+
+Controllers are responsible for taking action upon user requests
+(loosely following the terminology of the MVC meta pattern).
+
+The following controllers are provided out-of-the box in CubicWeb. We
+list them by category.
+
+Browsing:
+
+* the View controller (web/views/basecontrollers.py) is associated
+ with most browsing actions within a CubicWeb application: it always
+ instantiates a `main template` and lets the ResultSet/Views dispatch
+ system build up the whole content; it handles ObjectNotFound and
+ NoSelectableObject errors that may bubble up to its entry point, in
+ an end-user-friendly way (but other programming errors will slip
+ through)
+
+* the JSon controller (web/views/basecontrollers.py) provides services
+ for Ajax calls, typically using JSON as a serialization format for
+ input, and sometimes using either JSON or XML for output;
+
+* the Login/Logout controllers (web/views/basecontrollers.py) make
+ effective user login or logout requests
+
+Edition:
+
+* the Edit controller (web/views/editcontroller.py) handles CRUD
+ operations in response to a form being submitted; it works in close
+ association with the Forms, to which it delegates some of the work
+
+* the Form validator controller (web/views/basecontrollers.py)
+ provides form validation from Ajax context, using the Edit
+ controller, to implement the classic form handling loop (user edits,
+ hits 'submit/apply', validation occurs server-side by way of the
+ Form validator controller, and the UI is decorated with failure
+ information, either global or per-field , until it is valid)
+
+Other:
+
+* the SendMail controller (web/views/basecontrollers.py) is reponsible
+ for outgoing email notifications
+
+* the MailBugReport controller (web/views/basecontrollers.py) allows
+ to quickly have a `repotbug` feature in one's application
+
+Registration
+++++++++++++
+
+All controllers (should) live in the 'controllers' namespace within
+the global registry.
+
+API
++++
+
+Most API details should be resolved by source code inspection, as the
+various controllers have differing goals.
+
+`web/controller.py` contains the top-level abstract Controller class and
+its (NotImplemented) entry point `publish(rset=None)` method.
+
+A handful of helpers are also provided there:
+
+* process_rql builds a result set from an rql query typically issued
+ from the browser (and available through _cw.form['rql'])
+
+* validate_cache will force cache validation handling with respect to
+ the HTTP Cache directives (that were typically originally issued
+ from a previous server -> client response); concrete Controller
+ implementations dealing with HTTP (thus, for instance, not the
+ SendMail controller) may very well call this in their publication
+ process.
+
+
+
--- a/doc/book/en/development/devweb/facets.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/devweb/facets.rst Fri Mar 05 18:20:18 2010 +0100
@@ -23,10 +23,10 @@
The two other entity types defined in the schema are `Visit` and `Agency` but we
can also guess from the above that this application uses the two cubes
`comment`_ and
-`addressbook`_ (remember, cubicweb is only a game where you assemble cubes !).
+`addressbook`_ (remember, cubicweb is only a game where you assemble cubes !).
While we know that just defining the schema in enough to have a full, usable,
-(testable !) application, we also know that every application needs to be
+(testable !) application, we also know that every application needs to be
customized to fulfill the needs it was built for. So in this case, what we
needed most was some custom filters that would let us restrict searches
according
@@ -36,9 +36,9 @@
.. sourcecode:: python
- class PostalCodeFacet(RelationFacet):
- id = 'postalcode-facet' # every registered class must have an id
- __select__ = implements('Office') # this facet should only be selected when
+ class PostalCodeFacet(RelationFacet):
+ __regid__ = 'postalcode-facet' # every registered class must have an id
+ __select__ = implements('Office') # this facet should only be selected when
# visualizing offices
rtype = 'has_address' # this facet is a filter on the entity linked to
# the office thrhough the relation
@@ -57,18 +57,18 @@
.. sourcecode:: python
class SurfaceFacet(AttributeFacet):
- id = 'surface-facet' # every registered class must have an id
- __select__ = implements('Office') # this facet should only be selected when
+ __regid__ = 'surface-facet' # every registered class must have an id
+ __select__ = implements('Office') # this facet should only be selected when
# visualizing offices
- rtype = 'surface' # the filter's key is the attribute "surface"
- comparator = '>=' # override the default value of operator since
+ rtype = 'surface' # the filter's key is the attribute "surface"
+ comparator = '>=' # override the default value of operator since
# we want to filter according to a
- # minimal
+ # minimal
# value, not an exact one
def rset_vocabulary(self, ___):
"""override the default vocabulary method since we want to hard-code
- our threshold values.
+ our threshold values.
Not overriding would generate a filter box with all existing surfaces
defined in the database.
"""
@@ -93,7 +93,7 @@
We've just added two new kind of facets in CubicWeb :
- The **RangeFacet** which displays a slider using `jquery`_
- to choose a lower bound and an upper bound. The **RangeWidget**
+ to choose a lower bound and an upper bound. The **RangeWidget**
works with either numerical values or date values
- The **HasRelationFacet** which displays a simple checkbox and
@@ -103,7 +103,7 @@
.. image :: http://www.cubicweb.org/Image/343498?vid=download
-Here's an example of code that defines a facet to filter
+Here's an example of code that defines a facet to filter
musical works according to their composition date:
.. sourcecode:: python
@@ -112,7 +112,7 @@
# 1. make sure this facet is displayed only on Track selection
__select__ = DateRangeFacet.__select__ & implements('Track')
# 2. give the facet an id required by CubicWeb)
- id = 'compdate-facet'
+ __regid__ = 'compdate-facet'
# 3. specify the attribute name that actually stores the date in the DB
rtype = 'composition_date'
--- a/doc/book/en/development/devweb/form.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/devweb/form.rst Fri Mar 05 18:20:18 2010 +0100
@@ -1,19 +1,22 @@
Form construction
------------------
-CubicWeb provides usual form/field/widget/renderer abstraction to provde
-some generic building blocks which will greatly help you in building forms
-properly integrated with |cubicweb| (coherent display, error handling, etc...)
+
+CubicWeb provides usual form/field/widget/renderer abstraction to
+provde some generic building blocks which will greatly help you in
+building forms properly integrated with CubicWeb (coherent display,
+error handling, etc...).
-A form basically only hold a set of fields, and is bound to a renderer that is
-responsible to layout them. Each field is bound to a widget that will be used
-to fill in value(s) for that field.
+A form basically only holds a set of fields, and has te be bound to a
+renderer which is responsible to layout them. Each field is bound to a
+widget that will be used to fill in value(s) for that field (at form
+generation time) and 'decode' (fetch and give a proper Python type to)
+values sent back by the browser.
The Field class and basic fields
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: cubicweb.web.formfields.Field
-
Existing field types are:
.. autoclass:: cubicweb.web.formfields.StringField
@@ -28,7 +31,6 @@
.. autoclass:: cubicweb.web.formfields.DateTimeField
.. autoclass:: cubicweb.web.formfields.TimeField
.. autoclass:: cubicweb.web.formfields.RelationField
-.. XXX still necessary?
.. autoclass:: cubicweb.web.formfields.CompoundField
@@ -57,9 +59,7 @@
.. autoclass:: cubicweb.web.formwidgets.AutoCompletionWidget
.. autoclass:: cubicweb.web.formwidgets.EditableURLWidget
-.. XXX StaticFileAutoCompletionWidget, RestrictedAutoCompletionWidget, AddComboBoxWidget, IntervalWidget, HorizontalLayoutWidget
-
-The following classes, which are not proper widget (they are not associated to
+Other classes in this module, which are not proper widget (they are not associated to
field) but are used as form controls, may also be useful: Button, SubmitButton,
ResetButton, ImgButton,
@@ -68,4 +68,10 @@
Renderers
~~~~~~~~~
-XXX feed me
+
+.. autoclass:: cubicweb.web.views.formrenderers.BaseFormRenderer
+.. autoclass:: cubicweb.web.views.formrenderers.HTableFormRenderer
+.. autoclass:: cubicweb.web.views.formrenderers.EntityCompositeFormRenderer
+.. autoclass:: cubicweb.web.views.formrenderers.EntityFormRenderer
+.. autoclass:: cubicweb.web.views.formrenderers.EntityInlinedFormRenderer
+
--- a/doc/book/en/development/devweb/internationalization.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/devweb/internationalization.rst Fri Mar 05 18:20:18 2010 +0100
@@ -30,7 +30,7 @@
class PrimaryView(EntityView):
"""the full view of an non final entity"""
- id = 'primary'
+ __regid__ = 'primary'
title = _('primary')
OR
@@ -39,7 +39,7 @@
class NoResultView(EmptyRsetView):
"""default view when no result has been found"""
- id = 'noresult'
+ __regid__ = 'noresult'
def call(self, **kwargs):
self.w(u'<div class="searchMessage"><strong>%s</strong></div>\n'
--- a/doc/book/en/development/devweb/views.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/devweb/views.rst Fri Mar 05 18:20:18 2010 +0100
@@ -88,7 +88,7 @@
.. sourcecode:: python
class RSSView(XMLView):
- id = 'rss'
+ __regid__ = 'rss'
title = _('rss')
templatable = False
content_type = 'text/xml'
@@ -104,7 +104,7 @@
"""view called by the edition view when the user asks
to search for something to link to the edited eid
"""
- id = 'search-associate'
+ __regid__ = 'search-associate'
title = _('search for association')
__select__ = one_line_rset() & match_search_state('linksearch') & implements('Any')
@@ -152,7 +152,7 @@
from cubicweb.web.views.primary import Primaryview
class BlogPrimaryView(PrimaryView):
- id = 'primary'
+ __regid__ = 'primary'
__select__ = PrimaryView.__select__ & implements('Blog')
rql = 'Any BE ORDERBY D DESC WHERE BE entry_of B, BE publish_date D, B eid %(b)s'
@@ -162,7 +162,7 @@
self.w(u'<p>%s</p>' % entry.view('inblogcontext'))
class BlogEntryInBlogView(EntityView):
- id = 'inblogcontext'
+ __regid__ = 'inblogcontext'
__select__ = implements('BlogEntry')
def cell_call(self, row, col):
--- a/doc/book/en/development/entityclasses/interfaces.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/entityclasses/interfaces.rst Fri Mar 05 18:20:18 2010 +0100
@@ -3,18 +3,61 @@
Same thing as object-oriented programming interfaces.
-XXX how to define a cw interface
+Definition of an interface is quite trivial. An example from cubicweb
+itself (found in cubicweb/interfaces.py):
+
+.. sourcecode:: python
+
+ class ITree(Interface):
+
+ def parent(self):
+ """returns the parent entity"""
+
+ def children(self):
+ """returns the item's children"""
+
+ def children_rql(self):
+ """XXX returns RQL to get children"""
+
+ def iterchildren(self):
+ """iterates over the item's children"""
+
+ def is_leaf(self):
+ """returns true if this node as no child"""
+
+ def is_root(self):
+ """returns true if this node has no parent"""
+
+ def root(self):
+ """returns the root object"""
+
Declaration of interfaces implemented by a class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-XXX __implements__
+.. sourcecode:: python
+ from cubicweb.interfaces import ITree
+ from cubicweb.mixins import TreeMixIn
+
+ class MyEntity(TreeMixIn, AnyEntity):
+ __regid__ = 'MyEntity'
+ __implements__ = AnyEntity.__implements__ + ('ITree',)
-Interfaces defined in the library
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ tree_attribute = 'filed_under'
+
+The TreeMixIn here provides a default implementation for the
+interface. The tree_attribute class attribute is actually used by this
+implementation to help implement correct behaviour.
+
+Interfaces (and some implementations as mixins) defined in the library
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: cubicweb.interface
:members:
+.. automodule:: cubicweb.mixins
+ :members:
+
+
--- a/doc/book/en/development/entityclasses/load-sort.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/entityclasses/load-sort.rst Fri Mar 05 18:20:18 2010 +0100
@@ -24,6 +24,7 @@
on the name attribute): ::
class MyEntity(AnyEntity):
+ __regid__ = 'MyEntity'
fetch_attrs = ('modification_date', 'name')
@classmethod
@@ -45,7 +46,7 @@
class Transition(AnyEntity):
"""..."""
- id = 'Transition'
+ __regid__ = 'Transition'
fetch_attrs, fetch_order = fetch_config(['name'])
Indicates that for the entity type "Transition", you have to pre-load
--- a/doc/book/en/development/webstdlib/basetemplates.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/webstdlib/basetemplates.rst Fri Mar 05 18:20:18 2010 +0100
@@ -130,7 +130,7 @@
view=view, context=context))
if boxes:
for box in boxes:
- if box.id == 'search_box':
+ if box.__regid__ == 'search_box':
box.dispatch(w=self.w, view=view)
@@ -159,7 +159,7 @@
.. _TheMainTemplate:
TheMainTemplate is responsible for the general layout of the entire application.
-It defines the template of ``id = main`` that is used by the instance.
+It defines the template of ``__regid__ = main`` that is used by the instance.
The default main template (`cubicweb.web.views.basetemplates.TheMainTemplate`)
builds the page based on the following pattern:
--- a/doc/book/en/development/webstdlib/primary.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/webstdlib/primary.rst Fri Mar 05 18:20:18 2010 +0100
@@ -27,7 +27,7 @@
class PrimaryView(EntityView):
"""the full view of an non final entity"""
- id = 'primary'
+ __regid__ = 'primary'
title = _('primary')
show_attr_label = True
show_rel_label = True
--- a/doc/book/en/development/webstdlib/xmlrss.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/development/webstdlib/xmlrss.rst Fri Mar 05 18:20:18 2010 +0100
@@ -3,6 +3,9 @@
XML and RSS views (:mod:`cubicweb.web.views.xmlrss`)
----------------------------------------------------
+Overview
++++++++++
+
*rss*
Creates a RSS/XML view and call the view `rssitem` for each entity of
the result set.
@@ -10,3 +13,50 @@
*rssitem*
Create a RSS/XML view for each entity based on the results of the dublin core
methods of the entity (`dc_*`)
+
+
+RSS Channel Example
+++++++++++++++++++++
+
+Assuming you have several blog entries, click on the title of the
+search box in the left column. A larger search box should appear. Enter::
+
+ Any X ORDERBY D WHERE X is BlogEntry, X creation_date D
+
+and you get a list of blog entries.
+
+Click on your login at the top right corner. Chose "user preferences",
+then "boxes", then "possible views box" and check "visible = yes"
+before validating your changes.
+
+Enter the same query in the search box and you will see the same list,
+plus a box titled "possible views" in the left column. Click on
+"entityview", then "RSS".
+
+You just applied the "RSS" view to the RQL selection you requested.
+
+That's it, you have a RSS channel for your blog.
+
+Try again with::
+
+ Any X ORDERBY D WHERE X is BlogEntry, X creation_date D,
+ X entry_of B, B title "MyLife"
+
+Another RSS channel, but a bit more focused.
+
+A last one for the road::
+
+ Any C ORDERBY D WHERE C is Comment, C creation_date D LIMIT 15
+
+displayed with the RSS view, that's a channel for the last fifteen
+comments posted.
+
+[WRITE ME]
+
+* show that the RSS view can be used to display an ordered selection
+ of blog entries, thus providing a RSS channel
+
+* show that a different selection (by category) means a different channel
+
+
+
--- a/doc/book/en/intro/tutorial/maintemplate.rst Thu Mar 04 18:57:13 2010 +0100
+++ b/doc/book/en/intro/tutorial/maintemplate.rst Fri Mar 05 18:20:18 2010 +0100
@@ -116,7 +116,7 @@
our application.
TheMainTemplate is responsible for the general layout of the entire application.
-It defines the template of ``id = main`` that is used by the application. Is
+It defines the template of ``__regid__ = main`` that is used by the application. Is
also defined in ``cubicweb/web/views/basetemplates.py`` another template that can
be used based on TheMainTemplate called SimpleMainTemplate which does not have
a top section.
--- a/entity.py Thu Mar 04 18:57:13 2010 +0100
+++ b/entity.py Fri Mar 05 18:20:18 2010 +0100
@@ -797,7 +797,7 @@
del self.__unique
except AttributeError:
pass
-
+
# raw edition utilities ###################################################
def set_attributes(self, _cw_unsafe=False, **kwargs):
@@ -876,7 +876,6 @@
_done.add(self.eid)
containers = tuple(self.e_schema.fulltext_containers())
if containers:
- yielded = False
for rschema, target in containers:
if target == 'object':
targets = getattr(self, rschema.type)
@@ -888,8 +887,6 @@
for container in entity.fti_containers(_done):
yield container
yielded = True
- if not yielded:
- yield self
else:
yield self
@@ -919,7 +916,6 @@
continue
if value:
words += tokenize(value)
-
for rschema, role in self.e_schema.fulltext_relations():
if role == 'subject':
for entity in getattr(self, rschema.type):
--- a/hooks/metadata.py Thu Mar 04 18:57:13 2010 +0100
+++ b/hooks/metadata.py Fri Mar 05 18:20:18 2010 +0100
@@ -12,12 +12,10 @@
from cubicweb.selectors import implements
from cubicweb.server import hook
-from cubicweb.server.repository import FTIndexEntityOp
-def eschema_type_eid(session, etype):
+def eschema_eid(session, eschema):
"""get eid of the CWEType entity for the given yams type"""
- eschema = session.repo.schema.eschema(etype)
# eschema.eid is None if schema has been readen from the filesystem, not
# from the database (eg during tests)
if eschema.eid is None:
@@ -76,7 +74,10 @@
class SetIsHook(MetaDataHook):
- """create a new entity -> set is relation"""
+ """create a new entity -> set is and is_instance_of relations
+
+ those relations are inserted using sql so they are not hookable.
+ """
__regid__ = 'setis'
events = ('after_add_entity',)
@@ -86,18 +87,14 @@
session = self._cw
entity = self.entity
try:
- #session.add_relation(entity.eid, 'is',
- # eschema_type_eid(session, entity.__regid__))
session.system_sql('INSERT INTO is_relation(eid_from,eid_to) VALUES (%s,%s)'
- % (entity.eid, eschema_type_eid(session, entity.__regid__)))
+ % (entity.eid, eschema_eid(session, entity.e_schema)))
except IndexError:
# during schema serialization, skip
return
- for etype in entity.e_schema.ancestors() + [entity.e_schema]:
- #session.add_relation(entity.eid, 'is_instance_of',
- # eschema_type_eid(session, etype))
+ for eschema in entity.e_schema.ancestors() + [entity.e_schema]:
session.system_sql('INSERT INTO is_instance_of_relation(eid_from,eid_to) VALUES (%s,%s)'
- % (entity.eid, eschema_type_eid(session, etype)))
+ % (entity.eid, eschema_eid(session, eschema)))
class SetOwnershipHook(MetaDataHook):
@@ -150,7 +147,8 @@
class UpdateFTIHook(MetaDataHook):
- """sync fulltext index when relevant relation is added / removed
+ """sync fulltext index text index container when a relation with
+ fulltext_container set is added / removed
"""
__regid__ = 'updateftirel'
events = ('after_add_relation', 'after_delete_relation')
@@ -158,15 +156,19 @@
def __call__(self):
rtype = self.rtype
session = self._cw
+ ftcontainer = session.vreg.schema.rschema(rtype).fulltext_container
if self.event == 'after_add_relation':
- # Reindexing the contained entity is enough since it will implicitly
- # reindex the container entity.
- ftcontainer = session.vreg.schema.rschema(rtype).fulltext_container
if ftcontainer == 'subject':
- FTIndexEntityOp(session, entity=session.entity_from_eid(self.eidto))
+ session.repo.system_source.index_entity(
+ session, session.entity_from_eid(self.eidfrom))
elif ftcontainer == 'object':
- FTIndexEntityOp(session, entity=session.entity_from_eid(self.eidfrom))
- elif session.repo.schema.rschema(rtype).fulltext_container:
- FTIndexEntityOp(session, entity=session.entity_from_eid(self.eidto))
- FTIndexEntityOp(session, entity=session.entity_from_eid(self.eidfrom))
+ session.repo.system_source.index_entity(
+ session, session.entity_from_eid(self.eidto))
+ # after delete relation
+ elif ftcontainer == 'subject':
+ session.repo.system_source.index_entity(
+ session, entity=session.entity_from_eid(self.eidfrom))
+ elif ftcontainer == 'object':
+ session.repo.system_source.index_entity(
+ session, entity=session.entity_from_eid(self.eidto))
--- a/hooks/syncschema.py Thu Mar 04 18:57:13 2010 +0100
+++ b/hooks/syncschema.py Fri Mar 05 18:20:18 2010 +0100
@@ -463,17 +463,18 @@
rschema = values = None # make pylint happy
def precommit_event(self):
+ session = self.session
etype = self.kobj[0]
table = SQL_PREFIX + etype
column = SQL_PREFIX + self.rschema.type
if 'indexed' in self.values:
- sysource = self.session.pool.source('system')
+ sysource = session.pool.source('system')
if self.values['indexed']:
- sysource.create_index(self.session, table, column)
+ sysource.create_index(session, table, column)
else:
- sysource.drop_index(self.session, table, column)
+ sysource.drop_index(session, table, column)
if 'cardinality' in self.values and self.rschema.final:
- adbh = self.session.pool.source('system').dbhelper
+ adbh = session.pool.source('system').dbhelper
if not adbh.alter_column_support:
# not supported (and NOT NULL not set by yams in that case, so
# no worry)
@@ -485,11 +486,17 @@
# XXX check self.values['cardinality'][0] actually changed?
sql = adbh.sql_set_null_allowed(table, column, coltype,
self.values['cardinality'][0] != '1')
- self.session.system_sql(sql)
+ session.system_sql(sql)
if 'fulltextindexed' in self.values:
- UpdateFTIndexOp(self.session)
- self.session.transaction_data.setdefault('fti_update_etypes',
- set()).add(etype)
+ UpdateFTIndexOp(session)
+ session.transaction_data.setdefault(
+ 'fti_update_etypes', set()).add(etype)
+ elif 'fulltext_container' in self.values:
+ ftiupdates = session.transaction_data.setdefault(
+ 'fti_update_etypes', set())
+ ftiupdates.add(etype)
+ ftiupdates.add(self.kobj[1])
+ UpdateFTIndexOp(session)
class SourceDbCWConstraintAdd(hook.Operation):
@@ -1152,7 +1159,8 @@
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)
+ source.fti_unindex_entity(session, entity.eid)
+ source.fti_index_entity(session, container)
except Exception:
self.critical('Error while updating Full Text Index for'
' entity %s', entity.eid, exc_info=True)
--- a/schema.py Thu Mar 04 18:57:13 2010 +0100
+++ b/schema.py Fri Mar 05 18:20:18 2010 +0100
@@ -50,8 +50,9 @@
))
WORKFLOW_TYPES = set(('Transition', 'State', 'TrInfo', 'Workflow',
- 'WorkflowTransition', 'BaseTransition',
- 'SubWorkflowExitPoint'))
+ 'WorkflowTransition', 'BaseTransition',
+ 'SubWorkflowExitPoint'))
+
INTERNAL_TYPES = set(('CWProperty', 'CWPermission', 'CWCache', 'ExternalUri'))
@@ -63,6 +64,31 @@
ybo.RDEF_PROPERTIES += ('eid',)
+PUB_SYSTEM_ENTITY_PERMS = {
+ 'read': ('managers', 'users', 'guests',),
+ 'add': ('managers',),
+ 'delete': ('managers',),
+ 'update': ('managers',),
+ }
+PUB_SYSTEM_REL_PERMS = {
+ 'read': ('managers', 'users', 'guests',),
+ 'add': ('managers',),
+ 'delete': ('managers',),
+ }
+PUB_SYSTEM_ATTR_PERMS = {
+ 'read': ('managers', 'users', 'guests',),
+ 'update': ('managers',),
+ }
+RO_REL_PERMS = {
+ 'read': ('managers', 'users', 'guests',),
+ 'add': (),
+ 'delete': (),
+ }
+RO_ATTR_PERMS = {
+ 'read': ('managers', 'users', 'guests',),
+ 'update': (),
+ }
+
# XXX same algorithm as in reorder_cubes and probably other place,
# may probably extract a generic function
def order_eschemas(eschemas):
@@ -369,7 +395,8 @@
if need_has_text is None:
need_has_text = may_need_has_text
if need_has_text and not has_has_text and not deletion:
- rdef = ybo.RelationDefinition(self.type, 'has_text', 'String')
+ rdef = ybo.RelationDefinition(self.type, 'has_text', 'String',
+ __permissions__=RO_ATTR_PERMS)
self.schema.add_relation_def(rdef)
elif not need_has_text and has_has_text:
self.schema.del_relation_def(self.type, 'has_text', 'String')
@@ -491,9 +518,11 @@
if not eschema.final:
# automatically add the eid relation to non final entity types
rdef = ybo.RelationDefinition(eschema.type, 'eid', 'Int',
- cardinality='11', uid=True)
+ cardinality='11', uid=True,
+ __permissions__=RO_ATTR_PERMS)
self.add_relation_def(rdef)
- rdef = ybo.RelationDefinition(eschema.type, 'identity', eschema.type)
+ rdef = ybo.RelationDefinition(eschema.type, 'identity', eschema.type,
+ __permissions__=RO_REL_PERMS)
self.add_relation_def(rdef)
self._eid_index[eschema.eid] = eschema
return eschema
@@ -1054,8 +1083,16 @@
cw = entity._cw
elif form is not None:
cw = form._cw
- if cw is not None and cw.user.has_permission(PERM_USE_TEMPLATE_FORMAT):
- return self.regular_formats + tuple(NEED_PERM_FORMATS)
+ if cw is not None:
+ if hasattr(cw, 'is_super_session'):
+ # cw is a server session
+ hasperm = cw.is_super_session or \
+ not cw.vreg.config.is_hook_category_activated('integrity') or \
+ cw.user.has_permission(PERM_USE_TEMPLATE_FORMAT)
+ else:
+ hasperm = cw.user.has_permission(PERM_USE_TEMPLATE_FORMAT)
+ if hasperm:
+ return self.regular_formats + tuple(NEED_PERM_FORMATS)
return self.regular_formats
# XXX monkey patch PyFileReader.import_erschema until bw_normalize_etype is
--- a/schemas/__init__.py Thu Mar 04 18:57:13 2010 +0100
+++ b/schemas/__init__.py Fri Mar 05 18:20:18 2010 +0100
@@ -1,38 +1,25 @@
"""some utilities to define schema permissions
:organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"
from rql.utils import quote
-from cubicweb.schema import ERQLExpression, RRQLExpression
+from cubicweb.schema import RO_REL_PERMS, RO_ATTR_PERMS, \
+ PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, \
+ ERQLExpression, RRQLExpression
# permissions for "meta" entity type (readable by anyone, can only be
# added/deleted by managers)
-META_ETYPE_PERMS = {
- 'read': ('managers', 'users', 'guests',),
- 'add': ('managers',),
- 'delete': ('managers',),
- 'update': ('managers', 'owners',),
- }
-
+META_ETYPE_PERMS = PUB_SYSTEM_ENTITY_PERMS # XXX deprecates
# permissions for "meta" relation type (readable by anyone, can only be
# added/deleted by managers)
-META_RTYPE_PERMS = {
- 'read': ('managers', 'users', 'guests',),
- 'add': ('managers',),
- 'delete': ('managers',),
- }
-
+META_RTYPE_PERMS = PUB_SYSTEM_REL_PERMS # XXX deprecates
# permissions for relation type that should only set by hooks using unsafe
# execute, readable by anyone
-HOOKS_RTYPE_PERMS = {
- 'read': ('managers', 'users', 'guests',),
- 'add': (),
- 'delete': (),
- }
+HOOKS_RTYPE_PERMS = RO_REL_PERMS # XXX deprecates
def _perm(names):
if isinstance(names, (list, tuple)):
--- a/schemas/base.py Thu Mar 04 18:57:13 2010 +0100
+++ b/schemas/base.py Fri Mar 05 18:20:18 2010 +0100
@@ -10,9 +10,9 @@
from yams.buildobjs import (EntityType, RelationType, SubjectRelation,
String, Datetime, Password)
-from cubicweb.schema import (RQLConstraint, WorkflowableEntityType,
- ERQLExpression, RRQLExpression)
-from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS
+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"""
@@ -85,7 +85,7 @@
class in_group(RelationType):
"""core relation indicating a user's groups"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
class owned_by(RelationType):
"""core relation indicating owners of an entity. This relation
@@ -118,18 +118,21 @@
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'
@@ -155,7 +158,7 @@
class CWPermission(EntityType):
"""entity type that may be used to construct some advanced security configuration
"""
- __permissions__ = META_ETYPE_PERMS
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
name = String(required=True, indexed=True, internationalizable=True, maxsize=100,
description=_('name or identifier of the permission'))
@@ -170,11 +173,11 @@
"""link a permission to the entity. This permission should be used in the
security definition of the entity's type to be useful.
"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
class require_group(RelationType):
"""used to grant a permission to a group"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
class ExternalUri(EntityType):
@@ -209,6 +212,8 @@
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',),
--- a/schemas/bootstrap.py Thu Mar 04 18:57:13 2010 +0100
+++ b/schemas/bootstrap.py Fri Mar 05 18:20:18 2010 +0100
@@ -10,14 +10,16 @@
from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
SubjectRelation, RichString, String, Boolean, Int)
-from cubicweb.schema import RQLConstraint
-from cubicweb.schemas import META_ETYPE_PERMS, META_RTYPE_PERMS
+from cubicweb.schema import (
+ RQLConstraint,
+ PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, PUB_SYSTEM_ATTR_PERMS
+ )
# not restricted since as "is" is handled as other relations, guests need
# access to this
class CWEType(EntityType):
"""define an entity type, used to build the instance schema"""
- __permissions__ = META_ETYPE_PERMS
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
name = String(required=True, indexed=True, internationalizable=True,
unique=True, maxsize=64)
description = RichString(internationalizable=True,
@@ -28,7 +30,7 @@
class CWRType(EntityType):
"""define a relation type, used to build the instance schema"""
- __permissions__ = META_ETYPE_PERMS
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
name = String(required=True, indexed=True, internationalizable=True,
unique=True, maxsize=64)
description = RichString(internationalizable=True,
@@ -48,7 +50,7 @@
used to build the instance schema
"""
- __permissions__ = META_ETYPE_PERMS
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
relation_type = SubjectRelation('CWRType', cardinality='1*',
constraints=[RQLConstraint('O final TRUE')],
composite='object')
@@ -85,7 +87,7 @@
used to build the instance schema
"""
- __permissions__ = META_ETYPE_PERMS
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
relation_type = SubjectRelation('CWRType', cardinality='1*',
constraints=[RQLConstraint('O final FALSE')],
composite='object')
@@ -116,7 +118,7 @@
# not restricted since it has to be read when checking allowed transitions
class RQLExpression(EntityType):
"""define a rql expression used to define permissions"""
- __permissions__ = META_ETYPE_PERMS
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
exprtype = String(required=True, vocabulary=['ERQLExpression', 'RRQLExpression'])
mainvars = String(maxsize=8,
description=_('name of the main variables which should be '
@@ -134,14 +136,14 @@
class CWConstraint(EntityType):
"""define a schema constraint"""
- __permissions__ = META_ETYPE_PERMS
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
cstrtype = SubjectRelation('CWConstraintType', cardinality='1*')
value = String(description=_('depends on the constraint type'))
class CWConstraintType(EntityType):
"""define a schema constraint type"""
- __permissions__ = META_ETYPE_PERMS
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
name = String(required=True, indexed=True, internationalizable=True,
unique=True, maxsize=64)
@@ -149,7 +151,7 @@
# not restricted since it has to be read when checking allowed transitions
class CWGroup(EntityType):
"""define a CubicWeb users group"""
- __permissions__ = META_ETYPE_PERMS
+ __permissions__ = PUB_SYSTEM_ENTITY_PERMS
name = String(required=True, indexed=True, internationalizable=True,
unique=True, maxsize=64)
@@ -173,32 +175,32 @@
class relation_type(RelationType):
"""link a relation definition to its relation type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
inlined = True
class from_entity(RelationType):
"""link a relation definition to its subject entity type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
inlined = True
class to_entity(RelationType):
"""link a relation definition to its object entity type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
inlined = True
class constrained_by(RelationType):
"""constraints applying on this relation"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
class cstrtype(RelationType):
"""constraint factory"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
inlined = True
class read_permission_cwgroup(RelationDefinition):
"""groups allowed to read entities/relations of this type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
name = 'read_permission'
subject = ('CWEType', 'CWAttribute', 'CWRelation')
object = 'CWGroup'
@@ -206,7 +208,7 @@
class add_permission_cwgroup(RelationDefinition):
"""groups allowed to add entities/relations of this type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
name = 'add_permission'
subject = ('CWEType', 'CWRelation')
object = 'CWGroup'
@@ -214,7 +216,7 @@
class delete_permission_cwgroup(RelationDefinition):
"""groups allowed to delete entities/relations of this type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
name = 'delete_permission'
subject = ('CWEType', 'CWRelation')
object = 'CWGroup'
@@ -222,7 +224,7 @@
class update_permission_cwgroup(RelationDefinition):
"""groups allowed to update entities/relations of this type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
name = 'update_permission'
subject = ('CWEType', 'CWAttribute')
object = 'CWGroup'
@@ -230,7 +232,7 @@
class read_permission_rqlexpr(RelationDefinition):
"""rql expression allowing to read entities/relations of this type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
name = 'read_permission'
subject = ('CWEType', 'CWAttribute', 'CWRelation')
object = 'RQLExpression'
@@ -239,7 +241,7 @@
class add_permission_rqlexpr(RelationDefinition):
"""rql expression allowing to add entities/relations of this type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
name = 'add_permission'
subject = ('CWEType', 'CWRelation')
object = 'RQLExpression'
@@ -248,7 +250,7 @@
class delete_permission_rqlexpr(RelationDefinition):
"""rql expression allowing to delete entities/relations of this type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
name = 'delete_permission'
subject = ('CWEType', 'CWRelation')
object = 'RQLExpression'
@@ -257,7 +259,7 @@
class update_permission_rqlexpr(RelationDefinition):
"""rql expression allowing to update entities/relations of this type"""
- __permissions__ = META_RTYPE_PERMS
+ __permissions__ = PUB_SYSTEM_REL_PERMS
name = 'update_permission'
subject = ('CWEType', 'CWAttribute')
object = 'RQLExpression'
@@ -305,3 +307,13 @@
cardinality = '?*'
subject = 'CWEType'
object = 'CWEType'
+
+def post_build_callback(schema):
+ """set attributes permissions for schema/workflow entities"""
+ from cubicweb.schema import SCHEMA_TYPES, WORKFLOW_TYPES, META_RTYPES
+ for eschema in schema.entities():
+ if eschema in SCHEMA_TYPES or eschema in WORKFLOW_TYPES:
+ for rschema in eschema.subject_relations():
+ if rschema.final and not rschema in META_RTYPES:
+ rdef = eschema.rdef(rschema)
+ rdef.permissions = PUB_SYSTEM_ATTR_PERMS
--- a/schemas/workflow.py Thu Mar 04 18:57:13 2010 +0100
+++ b/schemas/workflow.py Fri Mar 05 18:20:18 2010 +0100
@@ -19,7 +19,7 @@
name = String(required=True, indexed=True, internationalizable=True,
maxsize=256)
- description = RichString(fulltextindexed=True, default_format='text/rest',
+ description = RichString(default_format='text/rest',
description=_('semantic description of this workflow'))
workflow_of = SubjectRelation('CWEType', cardinality='+*',
@@ -53,7 +53,7 @@
maxsize=256,
constraints=[RQLUniqueConstraint('S name N, S state_of WF, Y state_of WF, Y name N', 'Y',
_('workflow already have a state of that name'))])
- description = RichString(fulltextindexed=True, default_format='text/rest',
+ description = RichString(default_format='text/rest',
description=_('semantic description of this state'))
# XXX should be on BaseTransition w/ AND/OR selectors when we will
@@ -77,8 +77,7 @@
constraints=[RQLUniqueConstraint('S name N, S transition_of WF, Y transition_of WF, Y name N', 'Y',
_('workflow already have a transition of that name'))])
type = String(vocabulary=(_('normal'), _('auto')), default='normal')
- description = RichString(fulltextindexed=True,
- description=_('semantic description of this transition'))
+ description = RichString(description=_('semantic description of this transition'))
condition = SubjectRelation('RQLExpression', cardinality='*?', composite='subject',
description=_('a RQL expression which should return some results, '
'else the transition won\'t be available. '
--- a/server/__init__.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/__init__.py Fri Mar 05 18:20:18 2010 +0100
@@ -186,6 +186,7 @@
handler.install_custom_sql_scripts(join(CW_SOFTWARE_ROOT, 'schemas'), driver)
for directory in reversed(config.cubes_path()):
handler.install_custom_sql_scripts(join(directory, 'schema'), driver)
+ # serialize the schema
initialize_schema(config, schema, handler)
# yoo !
cnx.commit()
--- a/server/checkintegrity.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/checkintegrity.py Fri Mar 05 18:20:18 2010 +0100
@@ -68,7 +68,6 @@
"""reindex all entities in the repository"""
# deactivate modification_date hook since we don't want them
# to be updated due to the reindexation
- from cubicweb.server.repository import FTIndexEntityOp
repo = session.repo
cursor = session.pool['system']
if not repo.system_source.indexer.has_fti_table(cursor):
@@ -80,7 +79,7 @@
cursor.execute(indexer.sql_init_fti())
repo.config.disabled_hooks_categories.add('metadata')
repo.config.disabled_hooks_categories.add('integrity')
- repo.do_fti = True # ensure full-text indexation is activated
+ repo.system_source.do_fti = True # ensure full-text indexation is activated
etypes = set()
for eschema in schema.entities():
if eschema.final:
@@ -104,9 +103,11 @@
pb.update()
# reindex entities by generating rql queries which set all indexable
# attribute to their current value
+ source = repo.system_source
for eschema in etypes:
for entity in session.execute('Any X WHERE X is %s' % eschema).entities():
- FTIndexEntityOp(session, entity=entity)
+ source.fti_unindex_entity(session, entity.eid)
+ source.fti_index_entity(session, entity)
if withpb:
pb.update()
# restore Entity.check
--- a/server/querier.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/querier.py Fri Mar 05 18:20:18 2010 +0100
@@ -358,6 +358,7 @@
self.preprocess(rqlst, security=False)
return rqlst
+
class InsertPlan(ExecutionPlan):
"""an execution model specific to the INSERT rql query
"""
--- a/server/repository.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/repository.py Fri Mar 05 18:20:18 2010 +0100
@@ -71,27 +71,6 @@
pass
-class FTIndexEntityOp(hook.LateOperation):
- """operation to delay entity full text indexation to commit
-
- since fti indexing may trigger discovery of other entities, it should be
- triggered on precommit, not commit, and this should be done after other
- precommit operation which may add relations to the entity
- """
-
- def precommit_event(self):
- session = self.session
- entity = self.entity
- if entity.eid in session.transaction_data.get('pendingeids', ()):
- return # entity added and deleted in the same transaction
- session.repo.system_source.fti_unindex_entity(session, entity.eid)
- for container in entity.fti_containers():
- session.repo.index_entity(session, container)
-
- def commit_event(self):
- pass
-
-
def del_existing_rel_if_needed(session, eidfrom, rtype, eidto):
"""delete existing relation when adding a new one if card is 1 or ?
@@ -105,7 +84,8 @@
# 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:
+ if session.is_internal_session \
+ or not session.vreg.config.is_hook_category_activated('integrity'):
return
card = session.schema_rproperty(rtype, eidfrom, eidto, 'cardinality')
# one may be tented to check for neweids but this may cause more than one
@@ -132,6 +112,7 @@
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()
@@ -163,8 +144,6 @@
self.vreg.schema = self.schema # until actual schema is loaded...
# querier helper, need to be created after sources initialization
self.querier = querier.QuerierHelper(self, self.schema)
- # should we reindex in changes?
- self.do_fti = not config['delay-full-text-indexation']
# sources
self.sources = []
self.sources_by_uri = {}
@@ -776,7 +755,6 @@
# data sources handling ###################################################
# * correspondance between eid and (type, source)
# * correspondance between eid and local id (i.e. specific to a given source)
- # * searchable text indexes
def type_and_source_from_eid(self, eid, session=None):
"""return a tuple (type, source, extid) for the entity with id <eid>"""
@@ -903,14 +881,9 @@
and index the entity with the full text index
"""
# begin by inserting eid/type/source/extid into the entities table
- self.system_source.add_info(session, entity, source, extid)
- if complete:
- entity.complete(entity.e_schema.indexable_attributes())
new = session.transaction_data.setdefault('neweids', set())
new.add(entity.eid)
- # now we can update the full text index
- if self.do_fti:
- FTIndexEntityOp(session, entity=entity)
+ self.system_source.add_info(session, entity, source, extid, complete)
CleanupEidTypeCacheOp(session)
def delete_info(self, session, eid):
@@ -960,15 +933,6 @@
# he can delete all its relations without security checking
session.unsafe_execute(rql, {'x': eid}, 'x', build_descr=False)
- def index_entity(self, session, entity):
- """full text index a modified entity"""
- alreadydone = session.transaction_data.setdefault('indexedeids', set())
- if entity.eid in alreadydone:
- self.debug('skipping reindexation of %s, already done', entity.eid)
- return
- alreadydone.add(entity.eid)
- self.system_source.fti_index_entity(session, entity)
-
def locate_relation_source(self, session, subject, rtype, object):
subjsource = self.source_from_eid(subject, session)
objsource = self.source_from_eid(object, session)
@@ -1104,14 +1068,10 @@
if not only_inline_rels:
self.hm.call_hooks('before_update_entity', session, entity=entity)
source.update_entity(session, entity)
- if not only_inline_rels:
- if need_fti_update and self.do_fti:
- # reindex the entity only if this query is updating at least
- # one indexable attribute
- FTIndexEntityOp(session, entity=entity)
- if source.should_call_hooks:
+ self.system_source.update_info(session, entity, need_fti_update)
+ if source.should_call_hooks:
+ if not only_inline_rels:
self.hm.call_hooks('after_update_entity', session, entity=entity)
- if source.should_call_hooks:
for attr, value, prevvalue in relations:
# if the relation is already cached, update existant cache
relcache = entity.relation_cached(attr, 'subject')
--- a/server/schemaserial.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/schemaserial.py Fri Mar 05 18:20:18 2010 +0100
@@ -51,9 +51,6 @@
return res
# schema / perms deserialization ##############################################
-OLD_SCHEMA_TYPES = frozenset(('EFRDef', 'ENFRDef', 'ERType', 'EEType',
- 'EConstraintType', 'EConstraint', 'EGroup',
- 'EUser', 'ECache', 'EPermission', 'EProperty'))
def deserialize_schema(schema, session):
"""return a schema according to information stored in an rql database
@@ -90,12 +87,6 @@
{'x': eid, 'n': netype})
session.system_sql('UPDATE entities SET type=%(n)s WHERE type=%(x)s',
{'x': etype, 'n': netype})
- # XXX should be donne as well on sqlite based sources
- if not etype in OLD_SCHEMA_TYPES and \
- (getattr(dbhelper, 'case_sensitive', False)
- or etype.lower() != netype.lower()):
- session.system_sql('ALTER TABLE %s%s RENAME TO %s%s' % (
- sqlutils.SQL_PREFIX, etype, sqlutils.SQL_PREFIX, netype))
session.commit(False)
try:
session.system_sql('UPDATE deleted_entities SET type=%(n)s WHERE type=%(x)s',
@@ -182,6 +173,17 @@
res.setdefault(eid, {}).setdefault(action, []).append( (expr, mainvars, expreid) )
return res
+def deserialize_rdef_constraints(session):
+ """return the list of relation definition's constraints as instances"""
+ res = {}
+ for rdefeid, ceid, ct, val in session.execute(
+ 'Any E, X,TN,V WHERE E constrained_by X, X is CWConstraint, '
+ 'X cstrtype T, T name TN, X value V', build_descr=False):
+ cstr = CONSTRAINTS[ct].deserialize(val)
+ cstr.eid = ceid
+ res.setdefault(rdefeid, []).append(cstr)
+ return res
+
def set_perms(erschema, permsdict):
"""set permissions on the given erschema according to the permission
definition dictionary as built by deserialize_ertype_permissions for a
@@ -202,21 +204,9 @@
for p in somethings)
-def deserialize_rdef_constraints(session):
- """return the list of relation definition's constraints as instances"""
- res = {}
- for rdefeid, ceid, ct, val in session.execute(
- 'Any E, X,TN,V WHERE E constrained_by X, X is CWConstraint, '
- 'X cstrtype T, T name TN, X value V', build_descr=False):
- cstr = CONSTRAINTS[ct].deserialize(val)
- cstr.eid = ceid
- res.setdefault(rdefeid, []).append(cstr)
- return res
-
-
# schema / perms serialization ################################################
-def serialize_schema(cursor, schema, verbose=False):
+def serialize_schema(cursor, schema):
"""synchronize schema and permissions in the database according to
current schema
"""
@@ -224,37 +214,43 @@
if not quiet:
_title = '-> storing the schema in the database '
print _title,
- execute = cursor.execute
+ execute = cursor.unsafe_execute
eschemas = schema.entities()
- aller = eschemas + schema.relations()
- if not verbose and not quiet:
- pb_size = len(aller) + len(CONSTRAINTS) + len([x for x in eschemas if x.specializes()])
+ if not quiet:
+ pb_size = (len(eschemas + schema.relations())
+ + len(CONSTRAINTS)
+ + len([x for x in eschemas if x.specializes()]))
pb = ProgressBar(pb_size, title=_title)
else:
pb = None
+ groupmap = group_mapping(cursor, interactive=False)
+ # serialize all entity types, assuring CWEType is serialized first for proper
+ # is / is_instance_of insertion
+ eschemas.remove(schema.eschema('CWEType'))
+ eschemas.insert(0, schema.eschema('CWEType'))
+ for eschema in eschemas:
+ for rql, kwargs in eschema2rql(eschema, groupmap):
+ execute(rql, kwargs, build_descr=False)
+ if pb is not None:
+ pb.update()
+ # serialize constraint types
rql = 'INSERT CWConstraintType X: X name %(ct)s'
for cstrtype in CONSTRAINTS:
- if verbose:
- print rql
execute(rql, {'ct': unicode(cstrtype)}, build_descr=False)
if pb is not None:
pb.update()
- groupmap = group_mapping(cursor, interactive=False)
- for ertype in aller:
- # skip eid and has_text relations
- if ertype in VIRTUAL_RTYPES:
+ # serialize relations
+ for rschema in schema.relations():
+ # skip virtual relations such as eid, has_text and identity
+ if rschema in VIRTUAL_RTYPES:
if pb is not None:
pb.update()
continue
- for rql, kwargs in erschema2rql(schema[ertype], groupmap):
- if verbose:
- print rql % kwargs
+ for rql, kwargs in rschema2rql(rschema, groupmap=groupmap):
execute(rql, kwargs, build_descr=False)
if pb is not None:
pb.update()
for rql, kwargs in specialize2rql(schema):
- if verbose:
- print rql % kwargs
execute(rql, kwargs, build_descr=False)
if pb is not None:
pb.update()
--- a/server/serverconfig.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/serverconfig.py Fri Mar 05 18:20:18 2010 +0100
@@ -185,6 +185,8 @@
# check user's state at login time
consider_user_state = True
+ # XXX hooks control stuff should probably be on the session, not on the config
+
# hooks activation configuration
# all hooks should be activated during normal execution
disabled_hooks_categories = set()
@@ -232,9 +234,13 @@
@classmethod
def is_hook_activated(cls, hook):
+ return cls.is_hook_category_activated(hook.category)
+
+ @classmethod
+ def is_hook_category_activated(cls, category):
if cls.hooks_mode is cls.DENY_ALL:
- return hook.category in cls.enabled_hooks_categories
- return hook.category not in cls.disabled_hooks_categories
+ return category in cls.enabled_hooks_categories
+ return category not in cls.disabled_hooks_categories
# should some hooks be deactivated during [pre|post]create script execution
free_wheel = False
--- a/server/session.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/session.py Fri Mar 05 18:20:18 2010 +0100
@@ -499,7 +499,7 @@
operation.handle_event('revert%s_event' % trstate)
# XXX use slice notation since self.pending_operations is a
# read-only property.
- self.pending_operations[:] = processed + self.pending_operations
+ self.pending_operations[:] = processed + self.pending_operations
self.rollback(reset_pool)
raise
self.pool.commit()
--- a/server/sources/__init__.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/sources/__init__.py Fri Mar 05 18:20:18 2010 +0100
@@ -382,6 +382,22 @@
"""
raise NotImplementedError()
+ def modified_entities(self, session, etypes, mtime):
+ """return a 2-uple:
+ * list of (etype, eid) of entities of the given types which have been
+ modified since the given timestamp (actually entities whose full text
+ index content has changed)
+ * list of (etype, eid) of entities of the given types which have been
+ deleted since the given timestamp
+ """
+ raise NotImplementedError()
+
+ def index_entity(self, session, entity):
+ """create an operation to [re]index textual content of the given entity
+ on commit
+ """
+ raise NotImplementedError()
+
def fti_unindex_entity(self, session, eid):
"""remove text content for entity with the given eid from the full text
index
@@ -393,16 +409,6 @@
"""
raise NotImplementedError()
- def modified_entities(self, session, etypes, mtime):
- """return a 2-uple:
- * list of (etype, eid) of entities of the given types which have been
- modified since the given timestamp (actually entities whose full text
- index content has changed)
- * list of (etype, eid) of entities of the given types which have been
- deleted since the given timestamp
- """
- raise NotImplementedError()
-
# sql system source interface #############################################
def sqlexec(self, session, sql, args=None):
--- a/server/sources/native.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/sources/native.py Fri Mar 05 18:20:18 2010 +0100
@@ -17,7 +17,9 @@
from datetime import datetime
from base64 import b64decode, b64encode
+from logilab.common.compat import any
from logilab.common.cache import Cache
+from logilab.common.decorators import cached, clear_cache
from logilab.common.configuration import Method
from logilab.common.adbh import get_adv_func_helper
from logilab.common.shellutils import getlogin
@@ -26,6 +28,7 @@
from cubicweb import UnknownEid, AuthenticationError, Binary, server
from cubicweb.cwconfig import CubicWebNoAppConfiguration
+from cubicweb.server import hook
from cubicweb.server.utils import crypt_password
from cubicweb.server.sqlutils import SQL_PREFIX, SQLAdapterMixIn
from cubicweb.server.rqlannotation import set_qdata
@@ -150,12 +153,14 @@
self._rql_sqlgen = self.sqlgen_class(appschema, self.dbhelper,
self.encoding, ATTR_MAP.copy())
# full text index helper
- self.indexer = get_indexer(self.dbdriver, self.encoding)
- # advanced functionality helper
- self.dbhelper.fti_uid_attr = self.indexer.uid_attr
- self.dbhelper.fti_table = self.indexer.table
- self.dbhelper.fti_restriction_sql = self.indexer.restriction_sql
- self.dbhelper.fti_need_distinct_query = self.indexer.need_distinct
+ self.do_fti = not repo.config['delay-full-text-indexation']
+ if self.do_fti:
+ self.indexer = get_indexer(self.dbdriver, self.encoding)
+ # XXX should go away with logilab.db
+ self.dbhelper.fti_uid_attr = self.indexer.uid_attr
+ self.dbhelper.fti_table = self.indexer.table
+ self.dbhelper.fti_restriction_sql = self.indexer.restriction_sql
+ self.dbhelper.fti_need_distinct_query = self.indexer.need_distinct
# sql queries cache
self._cache = Cache(repo.config['rql-cache-size'])
self._temp_table_data = {}
@@ -201,9 +206,11 @@
pool = self.repo._get_pool()
pool.pool_set()
# check full text index availibility
- if not self.indexer.has_fti_table(pool['system']):
- self.error('no text index table')
- self.indexer = None
+ if self.do_fti:
+ if not self.indexer.has_fti_table(pool['system']):
+ if not repo.config.creating:
+ self.critical('no text index table')
+ self.do_fti = False
pool.pool_reset()
self.repo._free_pool(pool)
@@ -255,6 +262,7 @@
pass # __init__
for authentifier in self.authentifiers:
authentifier.set_schema(self.schema)
+ clear_cache(self, 'need_fti_indexation')
def support_entity(self, etype, write=False):
"""return true if the given entity's type is handled by this adapter
@@ -378,7 +386,8 @@
def update_entity(self, session, entity):
"""replace an entity in the source"""
attrs = self.preprocess_entity(entity)
- sql = self.sqlgen.update(SQL_PREFIX + str(entity.e_schema), attrs, [SQL_PREFIX + 'eid'])
+ sql = self.sqlgen.update(SQL_PREFIX + str(entity.e_schema), attrs,
+ [SQL_PREFIX + 'eid'])
self.doexec(session, sql, attrs)
def delete_entity(self, session, etype, eid):
@@ -387,10 +396,16 @@
sql = self.sqlgen.delete(SQL_PREFIX + etype, attrs)
self.doexec(session, sql, attrs)
- def add_relation(self, session, subject, rtype, object):
+ def add_relation(self, session, subject, rtype, object, inlined=False):
"""add a relation to the source"""
- attrs = {'eid_from': subject, 'eid_to': object}
- sql = self.sqlgen.insert('%s_relation' % rtype, attrs)
+ if inlined is False:
+ attrs = {'eid_from': subject, 'eid_to': object}
+ sql = self.sqlgen.insert('%s_relation' % rtype, attrs)
+ else: # used by data import
+ etype = session.describe(subject)[0]
+ attrs = {SQL_PREFIX + 'eid': subject, SQL_PREFIX + rtype: object}
+ sql = self.sqlgen.update(SQL_PREFIX + etype, attrs,
+ [SQL_PREFIX + 'eid'])
self.doexec(session, sql, attrs)
def delete_relation(self, session, subject, rtype, object):
@@ -524,7 +539,7 @@
finally:
self._eid_creation_lock.release()
- def add_info(self, session, entity, source, extid=None):
+ def add_info(self, session, entity, source, extid=None, complete=True):
"""add type and source info for an eid into the system table"""
# begin by inserting eid/type/source/extid into the entities table
if extid is not None:
@@ -533,6 +548,20 @@
attrs = {'type': entity.__regid__, 'eid': entity.eid, 'extid': extid,
'source': source.uri, 'mtime': datetime.now()}
session.system_sql(self.sqlgen.insert('entities', attrs), attrs)
+ # now we can update the full text index
+ if self.do_fti and self.need_fti_indexation(entity.__regid__):
+ if complete:
+ entity.complete(entity.e_schema.indexable_attributes())
+ FTIndexEntityOp(session, entity=entity)
+
+ def update_info(self, session, entity, need_fti_update):
+ if self.do_fti and need_fti_update:
+ # reindex the entity only if this query is updating at least
+ # one indexable attribute
+ FTIndexEntityOp(session, entity=entity)
+ # update entities.mtime
+ attrs = {'eid': entity.eid, 'mtime': datetime.now()}
+ session.system_sql(self.sqlgen.update('entities', attrs, ['eid']), attrs)
def delete_info(self, session, eid, etype, uri, extid):
"""delete system information on deletion of an entity by transfering
@@ -547,30 +576,6 @@
'source': uri, 'dtime': datetime.now()}
session.system_sql(self.sqlgen.insert('deleted_entities', attrs), attrs)
- def fti_unindex_entity(self, session, eid):
- """remove text content for entity with the given eid from the full text
- index
- """
- try:
- self.indexer.cursor_unindex_object(eid, session.pool['system'])
- except Exception: # let KeyboardInterrupt / SystemExit propagate
- if self.indexer is not None:
- self.exception('error while unindexing %s', eid)
-
- def fti_index_entity(self, session, entity):
- """add text content of a created/modified entity to the full text index
- """
- self.debug('reindexing %r', entity.eid)
- try:
- self.indexer.cursor_reindex_object(entity.eid, entity,
- session.pool['system'])
- except Exception: # let KeyboardInterrupt / SystemExit propagate
- if self.indexer is not None:
- self.exception('error while reindexing %s', entity)
- # update entities.mtime
- attrs = {'eid': entity.eid, 'mtime': datetime.now()}
- session.system_sql(self.sqlgen.update('entities', attrs, ['eid']), attrs)
-
def modified_entities(self, session, etypes, mtime):
"""return a 2-uple:
* list of (etype, eid) of entities of the given types which have been
@@ -587,6 +592,71 @@
delentities = cursor.fetchall()
return modentities, delentities
+ # full text index handling #################################################
+
+ @cached
+ def need_fti_indexation(self, etype):
+ eschema = self.schema.eschema(etype)
+ if any(eschema.indexable_attributes()):
+ return True
+ if any(eschema.fulltext_containers()):
+ return True
+ return False
+
+ def index_entity(self, session, entity):
+ """create an operation to [re]index textual content of the given entity
+ on commit
+ """
+ FTIndexEntityOp(session, entity=entity)
+
+ def fti_unindex_entity(self, session, eid):
+ """remove text content for entity with the given eid from the full text
+ index
+ """
+ try:
+ self.indexer.cursor_unindex_object(eid, session.pool['system'])
+ except Exception: # let KeyboardInterrupt / SystemExit propagate
+ self.exception('error while unindexing %s', eid)
+
+ def fti_index_entity(self, session, entity):
+ """add text content of a created/modified entity to the full text index
+ """
+ self.debug('reindexing %r', entity.eid)
+ try:
+ # use cursor_index_object, not cursor_reindex_object since
+ # unindexing done in the FTIndexEntityOp
+ self.indexer.cursor_index_object(entity.eid, entity,
+ session.pool['system'])
+ except Exception: # let KeyboardInterrupt / SystemExit propagate
+ self.exception('error while reindexing %s', entity)
+
+
+class FTIndexEntityOp(hook.LateOperation):
+ """operation to delay entity full text indexation to commit
+
+ since fti indexing may trigger discovery of other entities, it should be
+ triggered on precommit, not commit, and this should be done after other
+ precommit operation which may add relations to the entity
+ """
+
+ def precommit_event(self):
+ session = self.session
+ entity = self.entity
+ if entity.eid in session.transaction_data.get('pendingeids', ()):
+ return # entity added and deleted in the same transaction
+ alreadydone = session.transaction_data.setdefault('indexedeids', set())
+ if entity.eid in alreadydone:
+ self.debug('skipping reindexation of %s, already done', entity.eid)
+ return
+ alreadydone.add(entity.eid)
+ source = session.repo.system_source
+ for container in entity.fti_containers():
+ source.fti_unindex_entity(session, container.eid)
+ source.fti_index_entity(session, container)
+
+ def commit_event(self):
+ pass
+
def sql_schema(driver):
helper = get_adv_func_helper(driver)
--- a/server/sources/pyrorql.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/sources/pyrorql.py Fri Mar 05 18:20:18 2010 +0100
@@ -182,6 +182,7 @@
self._query_cache.clear()
repo = self.repo
session = repo.internal_session()
+ source = repo.system_source
try:
for etype, extid in modified:
try:
@@ -191,7 +192,7 @@
rset = session.eid_rset(eid, etype)
entity = rset.get_entity(0, 0)
entity.complete(entity.e_schema.indexable_attributes())
- repo.index_entity(session, entity)
+ source.index_entity(session, entity)
except:
self.exception('while updating %s with external id %s of source %s',
etype, extid, self.uri)
--- a/server/sources/rql2sql.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/sources/rql2sql.py Fri Mar 05 18:20:18 2010 +0100
@@ -41,6 +41,8 @@
from cubicweb.server.sqlutils import SQL_PREFIX
from cubicweb.server.utils import cleanup_solutions
+ColumnAlias._q_invariant = False # avoid to check for ColumnAlias / Variable
+
def _new_var(select, varname):
newvar = select.get_variable(varname)
if not 'relations' in newvar.stinfo:
@@ -711,7 +713,7 @@
return '%s=%s' % (lhssql, rhsvar.accept(self))
def _process_relation_term(self, relation, rid, termvar, termconst, relfield):
- if termconst or isinstance(termvar, ColumnAlias) or not termvar._q_invariant:
+ if termconst or not termvar._q_invariant:
termsql = termconst and termconst.accept(self) or termvar.accept(self)
yield '%s.%s=%s' % (rid, relfield, termsql)
elif termvar._q_invariant:
--- a/server/test/unittest_msplanner.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/test/unittest_msplanner.py Fri Mar 05 18:20:18 2010 +0100
@@ -771,13 +771,12 @@
[{'X': 'Basket'}]),
('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is CWUser',
[{'X': 'CWUser'}]),
- ('Any X WHERE X has_text "bla", X is IN(BaseTransition, Card, Comment, Division, Email, EmailThread, File, Folder, Image, Note, Personne, Societe, State, SubDivision, Tag, Transition, Workflow, WorkflowTransition)',
- [{'X': 'BaseTransition'}, {'X': 'Card'}, {'X': 'Comment'},
+ ('Any X WHERE X has_text "bla", X is IN(Card, Comment, Division, Email, EmailThread, File, Folder, Image, Note, Personne, Societe, SubDivision, Tag)',
+ [{'X': 'Card'}, {'X': 'Comment'},
{'X': 'Division'}, {'X': 'Email'}, {'X': 'EmailThread'},
{'X': 'File'}, {'X': 'Folder'}, {'X': 'Image'},
{'X': 'Note'}, {'X': 'Personne'}, {'X': 'Societe'},
- {'X': 'State'}, {'X': 'SubDivision'}, {'X': 'Tag'},
- {'X': 'Transition'}, {'X': 'Workflow'}, {'X': 'WorkflowTransition'}]),],
+ {'X': 'SubDivision'}, {'X': 'Tag'}]),],
None, None, [self.system], {}, []),
])
])
@@ -798,24 +797,22 @@
[{'X': 'Basket'}]),
('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is CWUser',
[{'X': 'CWUser'}]),
- ('Any X WHERE X has_text "bla", X is IN(BaseTransition, Card, Comment, Division, Email, EmailThread, File, Folder, Image, Note, Personne, Societe, State, SubDivision, Tag, Transition, Workflow, WorkflowTransition)',
- [{'X': 'BaseTransition'}, {'X': 'Card'}, {'X': 'Comment'},
+ ('Any X WHERE X has_text "bla", X is IN(Card, Comment, Division, Email, EmailThread, File, Folder, Image, Note, Personne, Societe, SubDivision, Tag)',
+ [{'X': 'Card'}, {'X': 'Comment'},
{'X': 'Division'}, {'X': 'Email'}, {'X': 'EmailThread'},
{'X': 'File'}, {'X': 'Folder'}, {'X': 'Image'},
{'X': 'Note'}, {'X': 'Personne'}, {'X': 'Societe'},
- {'X': 'State'}, {'X': 'SubDivision'}, {'X': 'Tag'},
- {'X': 'Transition'}, {'X': 'Workflow'}, {'X': 'WorkflowTransition'}])],
+ {'X': 'SubDivision'}, {'X': 'Tag'}])],
[self.system], {}, {'X': 'table0.C0'}, []),
]),
('OneFetchStep',
[('Any X LIMIT 10 OFFSET 10',
- [{'X': 'Affaire'}, {'X': 'BaseTransition'}, {'X': 'Basket'},
+ [{'X': 'Affaire'}, {'X': 'Basket'},
{'X': 'CWUser'}, {'X': 'Card'}, {'X': 'Comment'},
{'X': 'Division'}, {'X': 'Email'}, {'X': 'EmailThread'},
{'X': 'File'}, {'X': 'Folder'}, {'X': 'Image'},
{'X': 'Note'}, {'X': 'Personne'}, {'X': 'Societe'},
- {'X': 'State'}, {'X': 'SubDivision'}, {'X': 'Tag'},
- {'X': 'Transition'}, {'X': 'Workflow'}, {'X': 'WorkflowTransition'}])],
+ {'X': 'SubDivision'}, {'X': 'Tag'}])],
10, 10, [self.system], {'X': 'table0.C0'}, [])
])
--- a/server/test/unittest_querier.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/test/unittest_querier.py Fri Mar 05 18:20:18 2010 +0100
@@ -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], 'RQLExpression') # XXX may change as schema evolve
+ self.assertEquals(result[0][0], 'CWRelation') # 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')
@@ -862,6 +862,14 @@
self.assert_(rset.rows)
self.assertEquals(rset.description, [('Personne', 'Societe',)])
+ def test_insert_5bis(self):
+ peid = self.execute("INSERT Personne X: X nom 'bidule'")[0][0]
+ self.execute("INSERT Societe Y: Y nom 'toto', X travaille Y WHERE X eid %(x)s",
+ {'x': peid}, 'x')
+ rset = self.execute('Any X, Y WHERE X nom "bidule", Y nom "toto", X travaille Y')
+ self.assert_(rset.rows)
+ self.assertEquals(rset.description, [('Personne', 'Societe',)])
+
def test_insert_6(self):
self.execute("INSERT Personne X, Societe Y: X nom 'bidule', Y nom 'toto', X travaille Y")
rset = self.execute('Any X, Y WHERE X nom "bidule", Y nom "toto", X travaille Y')
--- a/server/test/unittest_repository.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/test/unittest_repository.py Fri Mar 05 18:20:18 2010 +0100
@@ -26,7 +26,7 @@
from cubicweb.devtools.repotest import tuplify
from cubicweb.server import repository, hook
from cubicweb.server.sqlutils import SQL_PREFIX
-
+from cubicweb.server.sources import native
# start name server anyway, process will fail if already running
os.system('pyro-ns >/dev/null 2>/dev/null &')
@@ -424,25 +424,39 @@
self.assertEquals(modified, [])
self.assertEquals(deleted, [('Personne', eidp)])
- def test_composite_entity(self):
+ def test_fulltext_container_entity(self):
assert self.schema.rschema('use_email').fulltext_container == 'subject'
- eid = self.request().create_entity('EmailAddress', address=u'toto@logilab.fr').eid
+ req = self.request()
+ toto = req.create_entity('EmailAddress', address=u'toto@logilab.fr')
self.commit()
- rset = self.execute('Any X WHERE X has_text %(t)s', {'t': 'toto'})
- self.assertEquals(rset.rows, [[eid]])
- self.execute('SET X use_email Y WHERE X login "admin", Y eid %(y)s', {'y': eid})
+ rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'toto'})
+ self.assertEquals(rset.rows, [])
+ req.user.set_relations(use_email=toto)
+ self.commit()
+ rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'toto'})
+ self.assertEquals(rset.rows, [[req.user.eid]])
+ req.execute('DELETE X use_email Y WHERE X login "admin", Y eid %(y)s',
+ {'y': toto.eid})
self.commit()
- rset = self.execute('Any X WHERE X has_text %(t)s', {'t': 'toto'})
- self.assertEquals(rset.rows, [[self.session.user.eid]])
- self.execute('DELETE X use_email Y WHERE X login "admin", Y eid %(y)s', {'y': eid})
- self.commit()
- rset = self.execute('Any X WHERE X has_text %(t)s', {'t': 'toto'})
+ rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'toto'})
self.assertEquals(rset.rows, [])
- eid = self.request().create_entity('EmailAddress', address=u'tutu@logilab.fr').eid
- self.execute('SET X use_email Y WHERE X login "admin", Y eid %(y)s', {'y': eid})
+ tutu = req.create_entity('EmailAddress', address=u'tutu@logilab.fr')
+ req.user.set_relations(use_email=tutu)
+ self.commit()
+ rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'tutu'})
+ self.assertEquals(rset.rows, [[req.user.eid]])
+ tutu.set_attributes(address=u'hip@logilab.fr')
self.commit()
- rset = self.execute('Any X WHERE X has_text %(t)s', {'t': 'tutu'})
- self.assertEquals(rset.rows, [[self.session.user.eid]])
+ rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'tutu'})
+ self.assertEquals(rset.rows, [])
+ rset = req.execute('Any X WHERE X has_text %(t)s', {'t': 'hip'})
+ self.assertEquals(rset.rows, [[req.user.eid]])
+
+ def test_no_uncessary_ftiindex_op(self):
+ req = self.request()
+ req.create_entity('Workflow', name=u'dummy workflow', description=u'huuuuu')
+ self.failIf(any(x for x in self.session.pending_operations
+ if isinstance(x, native.FTIndexEntityOp)))
class DBInitTC(CubicWebTC):
--- a/server/test/unittest_schemaserial.py Thu Mar 04 18:57:13 2010 +0100
+++ b/server/test/unittest_schemaserial.py Fri Mar 05 18:20:18 2010 +0100
@@ -149,7 +149,6 @@
('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 update_permission Y WHERE Y eid %(g)s, ', {'g': 0}),
- ('SET X update_permission Y WHERE Y eid %(g)s, ', {'g': 3}),
('SET X delete_permission Y WHERE Y eid %(g)s, ', {'g': 0}),
])
@@ -168,11 +167,6 @@
('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 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 Mar 04 18:57:13 2010 +0100
+++ b/server/test/unittest_security.py Fri Mar 05 18:20:18 2010 +0100
@@ -257,6 +257,26 @@
self.assertEquals(rset.rows, [[aff2]])
rset = cu.execute('Affaire X WHERE NOT X eid %(x)s', {'x': aff2}, 'x')
self.assertEquals(rset.rows, [])
+ # test can't update an attribute of an entity that can't be readen
+ self.assertRaises(Unauthorized, cu.execute, 'SET X sujet "hacked" WHERE X eid %(x)s', {'x': eid}, 'x')
+
+
+ def test_entity_created_in_transaction(self):
+ affschema = self.schema['Affaire']
+ origperms = affschema.permissions['read']
+ affschema.set_action_permissions('read', affschema.permissions['add'])
+ try:
+ cnx = self.login('iaminusersgrouponly')
+ cu = cnx.cursor()
+ aff2 = cu.execute("INSERT Affaire X: X sujet 'cool'")[0][0]
+ # entity created in transaction are readable *by eid*
+ self.failUnless(cu.execute('Any X WHERE X eid %(x)s', {'x':aff2}, 'x'))
+ # XXX would be nice if it worked
+ rset = cu.execute("Affaire X WHERE X sujet 'cool'")
+ self.assertEquals(len(rset), 0)
+ finally:
+ affschema.set_action_permissions('read', origperms)
+ cnx.close()
def test_read_erqlexpr_has_text1(self):
aff1 = self.execute("INSERT Affaire X: X sujet 'cool'")[0][0]
--- a/web/controller.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/controller.py Fri Mar 05 18:20:18 2010 +0100
@@ -61,12 +61,10 @@
self._edited_entity = None
def publish(self, rset=None):
- """publish the current request, with an option input rql string
- (already processed if necessary)
- """
+ """publish the current request, with an optional input rset"""
raise NotImplementedError
- # generic methods useful for concret implementations ######################
+ # generic methods useful for concrete implementations ######################
def process_rql(self, rql):
"""execute rql if specified"""
@@ -102,6 +100,7 @@
if not self._edited_entity:
self._edited_entity = entity
+ # XXX move to EditController (only customer)
def delete_entities(self, eidtypes):
"""delete entities from the repository"""
redirect_info = set()
@@ -125,6 +124,7 @@
view.set_http_cache_headers()
self._cw.validate_cache()
+ # XXX is that used AT ALL ?
def reset(self):
"""reset form parameters and redirect to a view determinated by given
parameters
@@ -141,6 +141,7 @@
self._return_to_original_view(newparams)
+ # XXX is that used AT ALL ?
def _return_to_original_view(self, newparams):
"""validate-button case"""
# transforms __redirect[*] parameters into regular form parameters
@@ -173,7 +174,7 @@
url = append_url_params(url, self._cw.form.get('__redirectparams'))
raise Redirect(url)
-
+ # XXX is that used AT ALL ?
def _return_to_edition_view(self, newparams):
"""apply-button case"""
form = self._cw.form
@@ -197,6 +198,7 @@
raise Redirect(self._cw.build_url(path, **newparams))
+ # XXX is that used AT ALL ?
def _return_to_lastpage(self, newparams):
"""cancel-button case: in this case we are always expecting to go back
where we came from, and this is not easy. Currently we suppose that
--- a/web/data/cubicweb.widgets.js Thu Mar 04 18:57:13 2010 +0100
+++ b/web/data/cubicweb.widgets.js Fri Mar 05 18:20:18 2010 +0100
@@ -170,8 +170,8 @@
function toggleTree(event) {
var linode = jQuery(this);
var url = linode.attr('cubicweb:loadurl');
- linode.find('ul.placeholder').remove();
if (url) {
+ linode.find('ul.placeholder').remove();
linode.loadxhtml(url, {callback: function(domnode) {
linode.removeAttr('cubicweb:loadurl');
jQuery(domnode).treeview({toggle: toggleTree,
--- a/web/data/jquery.corner.js Thu Mar 04 18:57:13 2010 +0100
+++ b/web/data/jquery.corner.js Fri Mar 05 18:20:18 2010 +0100
@@ -140,7 +140,7 @@
this.style.position = 'relative';
ds.position = 'absolute';
ds.bottom = ds.left = ds.padding = ds.margin = '0';
- if ($.browser.msie)
+ if (($.browser.msie) && ($.browser.version < 8.0))
ds.setExpression('width', 'this.parentNode.offsetWidth');
else
ds.width = '100%';
--- a/web/form.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/form.py Fri Mar 05 18:20:18 2010 +0100
@@ -186,6 +186,11 @@
# deleting validation errors here breaks form reloading (errors are
# no more available), they have to be deleted by application's publish
# method on successful commit
+ if hasattr(self, '_form_previous_values'):
+ # XXX behaviour changed in 3.6.1, warn
+ warn('[3.6.1] restore_previous_post already called, remove this call',
+ DeprecationWarning, stacklevel=2)
+ return
forminfo = self._cw.get_session_data(sessionkey, pop=True)
if forminfo:
self._form_previous_values = forminfo['values']
--- a/web/htmlwidgets.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/htmlwidgets.py Fri Mar 05 18:20:18 2010 +0100
@@ -336,88 +336,3 @@
yield column, self.model.sortvalue(rowindex, column.rset_sortcol)
-class ProgressBarWidget(HTMLWidget):
- """display a progress bar widget"""
- precision = 0.1
- red_threshold = 1.1
- orange_threshold = 1.05
- yellow_threshold = 1
-
- def __init__(self, done, todo, total):
- self.done = done
- self.todo = todo
- self.budget = total
-
- @property
- def overrun(self):
- """overrun = done + todo - """
- if self.done + self.todo > self.budget:
- overrun = self.done + self.todo - self.budget
- else:
- overrun = 0
- if overrun < self.precision:
- overrun = 0
- return overrun
-
- @property
- def overrun_percentage(self):
- """pourcentage overrun = overrun / budget"""
- if self.budget == 0:
- return 0
- else:
- return self.overrun * 100. / self.budget
-
- def _render(self):
- done = self.done
- todo = self.todo
- budget = self.budget
- if budget == 0:
- pourcent = 100
- else:
- pourcent = done*100./budget
- if pourcent > 100.1:
- color = 'red'
- elif todo+done > self.red_threshold*budget:
- color = 'red'
- elif todo+done > self.orange_threshold*budget:
- color = 'orange'
- elif todo+done > self.yellow_threshold*budget:
- color = 'yellow'
- else:
- color = 'green'
- if pourcent < 0:
- pourcent = 0
-
- if floor(done) == done or done>100:
- done_str = '%i' % done
- else:
- done_str = '%.1f' % done
- if floor(budget) == budget or budget>100:
- budget_str = '%i' % budget
- else:
- budget_str = '%.1f' % budget
-
- title = u'%s/%s = %i%%' % (done_str, budget_str, pourcent)
- short_title = title
- if self.overrun_percentage:
- title += u' overrun +%sj (+%i%%)' % (self.overrun,
- self.overrun_percentage)
- overrun = self.overrun
- if floor(overrun) == overrun or overrun>100:
- overrun_str = '%i' % overrun
- else:
- overrun_str = '%.1f' % overrun
- short_title += u' +%s' % overrun_str
- # write bars
- maxi = max(done+todo, budget)
- if maxi == 0:
- maxi = 1
-
- cid = random.randint(0, 100000)
- self.w(u'%s<br/>'
- u'<canvas class="progressbar" id="canvas%i" width="100" height="10"></canvas>'
- u'<script type="application/x-javascript">'
- u'draw_progressbar("canvas%i", %i, %i, %i, "%s");</script>'
- % (short_title.replace(' ',' '), cid, cid,
- int(100.*done/maxi), int(100.*(done+todo)/maxi),
- int(100.*budget/maxi), color))
--- a/web/test/unittest_views_editforms.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/test/unittest_views_editforms.py Fri Mar 05 18:20:18 2010 +0100
@@ -43,13 +43,11 @@
('firstname', 'subject'),
('surname', 'subject'),
('in_group', 'subject'),
- ('eid', 'subject'),
])
self.assertListEquals(rbc(e, 'muledit', 'attributes'),
[('login', 'subject'),
('upassword', 'subject'),
('in_group', 'subject'),
- ('eid', 'subject'),
])
self.assertListEquals(rbc(e, 'main', 'metadata'),
[('last_login_time', 'subject'),
@@ -76,13 +74,10 @@
# owned_by is defined both as subject and object relations on CWUser
self.assertListEquals(sorted(x for x in rbc(e, 'main', 'hidden')
if x != ('tags', 'object')),
- sorted([('has_text', 'subject'),
- ('identity', 'subject'),
- ('for_user', 'object'),
+ sorted([('for_user', 'object'),
('created_by', 'object'),
('wf_info_for', 'object'),
('owned_by', 'object'),
- ('identity', 'object'),
]))
def test_inlined_view(self):
@@ -106,11 +101,9 @@
('test', 'subject'),
('description', 'subject'),
('salary', 'subject'),
- ('eid', 'subject')
])
self.assertListEquals(rbc(e, 'muledit', 'attributes'),
[('nom', 'subject'),
- ('eid', 'subject')
])
self.assertListEquals(rbc(e, 'main', 'metadata'),
[('creation_date', 'subject'),
@@ -124,10 +117,7 @@
('connait', 'object')
])
self.assertListEquals(rbc(e, 'main', 'hidden'),
- [('has_text', 'subject'),
- ('identity', 'subject'),
- ('identity', 'object'),
- ])
+ [])
def test_edition_form(self):
rset = self.execute('CWUser X LIMIT 1')
--- a/web/uicfg.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/uicfg.py Fri Mar 05 18:20:18 2010 +0100
@@ -455,7 +455,7 @@
"""XXX for < 3.6 bw compat"""
def tag_relation(self, key, tag):
warn('autoform_is_inlined is deprecated, use autoform_section '
- 'with formtype="inlined", section="attributes" or section="hidden"',
+ 'with formtype="main", section="inlined"',
DeprecationWarning, stacklevel=3)
section = tag and 'inlined' or 'hidden'
autoform_section.tag_relation(key, 'main', section)
--- a/web/views/autoform.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/views/autoform.py Fri Mar 05 18:20:18 2010 +0100
@@ -661,7 +661,7 @@
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']
+ 'attributes', 'update', strict)]
def editable_relations(self):
"""return a sorted list of (relation's label, relation'schema, role) for
--- a/web/views/basecontrollers.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/views/basecontrollers.py Fri Mar 05 18:20:18 2010 +0100
@@ -375,12 +375,12 @@
rset = self._exec(rql)
else:
rset = None
- comp = self._cw.vreg[registry].select(compid, self._cw, rset=rset)
if extraargs is None:
extraargs = {}
else: # we receive unicode keys which is not supported by the **syntax
extraargs = dict((str(key), value)
for key, value in extraargs.items())
+ comp = self._cw.vreg[registry].select(compid, self._cw, rset=rset, **extraargs)
extraargs = extraargs or {}
return comp.render(**extraargs)
@@ -402,13 +402,23 @@
@xhtmlize
def js_reledit_form(self):
+ req = self._cw
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
args['default'] = self._cw.form['default_value']
args['reload'] = simplejson.loads(args['reload'])
- return entity.view('doreledit', **args)
+ rset = req.eid_rset(int(self._cw.form['eid']))
+ view = req.vreg['views'].select('doreledit', req, rset=rset, rtype=args['rtype'])
+ stream = view.set_stream()
+ view.render(**args)
+ extresources = req.html_headers.getvalue(skiphead=True)
+ if extresources:
+ stream.write(u'<div class="ajaxHtmlHead">\n')
+ stream.write(extresources)
+ stream.write(u'</div>\n')
+ return stream.getvalue()
@jsonize
def js_i18n(self, msgids):
--- a/web/views/basetemplates.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/views/basetemplates.py Fri Mar 05 18:20:18 2010 +0100
@@ -391,8 +391,9 @@
self.w(u'<td id="lastcolumn">')
self.w(u'</td>\n')
self.w(u'</tr></table>\n')
- self.wview('logform', rset=self.cw_rset, id='popupLoginBox', klass='hidden',
- title=False, showmessage=False)
+ if self._cw.cnx.anonymous_connection:
+ self.wview('logform', rset=self.cw_rset, id='popupLoginBox',
+ klass='hidden', title=False, showmessage=False)
def state_header(self):
state = self._cw.search_state
--- a/web/views/formrenderers.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/views/formrenderers.py Fri Mar 05 18:20:18 2010 +0100
@@ -44,6 +44,7 @@
+--------------+--------------+
| field1 label | field2 input |
+--------------+--------------+
+
+---------+
| buttons |
+---------+
@@ -131,9 +132,9 @@
errors = form.remaining_errors()
if errors:
if len(errors) > 1:
- templstr = '<li>%s</li>\n'
+ templstr = u'<li>%s</li>\n'
else:
- templstr = ' %s\n'
+ templstr = u' %s\n'
for field, err in errors:
if field is None:
errormsg += templstr % err
@@ -168,7 +169,7 @@
return tag + '>'
def close_form(self, form, values):
- """seem dump but important for consistency w/ close form, and necessary
+ """seems dumb but important for consistency w/ close form, and necessary
for form renderers overriding open_form to use something else or more than
and <form>
"""
--- a/web/views/ibreadcrumbs.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/views/ibreadcrumbs.py Fri Mar 05 18:20:18 2010 +0100
@@ -104,7 +104,7 @@
class BreadCrumbView(EntityView):
__regid__ = 'breadcrumbs'
- def cell_call(self, row, col):
+ def cell_call(self, row, col, **kwargs):
entity = self.cw_rset.get_entity(row, col)
desc = xml_escape(uilib.cut(entity.dc_description(), 50))
# XXX remember camember : tags.a autoescapes !
@@ -115,7 +115,7 @@
class BreadCrumbTextView(EntityView):
__regid__ = 'breadcrumbtext'
- def cell_call(self, row, col):
+ def cell_call(self, row, col, **kwargs):
entity = self.cw_rset.get_entity(row, col)
textsize = self._cw.property_value('navigation.short-line-size')
self.w(uilib.cut(entity.dc_title(), textsize))
--- a/web/views/iprogress.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/views/iprogress.py Fri Mar 05 18:20:18 2010 +0100
@@ -8,13 +8,15 @@
__docformat__ = "restructuredtext en"
_ = unicode
+from math import floor
+
from logilab.mtconverter import xml_escape
+from cubicweb.utils import make_uid
from cubicweb.selectors import implements
from cubicweb.interfaces import IProgress, IMileStone
from cubicweb.schema import display_name
from cubicweb.view import EntityView
-from cubicweb.web.htmlwidgets import ProgressBarWidget
class ProgressTableView(EntityView):
@@ -182,11 +184,85 @@
title = _('progress bar')
__select__ = implements(IProgress)
+ precision = 0.1
+ red_threshold = 1.1
+ orange_threshold = 1.05
+ yellow_threshold = 1
+
+ @classmethod
+ def overrun(cls, entity):
+ """overrun = done + todo - """
+ if entity.done + entity.todo > entity.revised_cost:
+ overrun = entity.done + entity.todo - entity.revised_cost
+ else:
+ overrun = 0
+ if overrun < cls.precision:
+ overrun = 0
+ return overrun
+
+ @classmethod
+ def overrun_percentage(cls, entity):
+ """pourcentage overrun = overrun / budget"""
+ if entity.revised_cost == 0:
+ return 0
+ else:
+ return cls.overrun(entity) * 100. / entity.revised_cost
+
def cell_call(self, row, col):
self._cw.add_css('cubicweb.iprogress.css')
self._cw.add_js('cubicweb.iprogress.js')
entity = self.cw_rset.get_entity(row, col)
- widget = ProgressBarWidget(entity.done, entity.todo,
- entity.revised_cost)
- self.w(widget.render())
+ done = entity.done
+ todo = entity.todo
+ budget = entity.revised_cost
+ if budget == 0:
+ pourcent = 100
+ else:
+ pourcent = done*100./budget
+ if pourcent > 100.1:
+ color = 'red'
+ elif todo+done > self.red_threshold*budget:
+ color = 'red'
+ elif todo+done > self.orange_threshold*budget:
+ color = 'orange'
+ elif todo+done > self.yellow_threshold*budget:
+ color = 'yellow'
+ else:
+ color = 'green'
+ if pourcent < 0:
+ pourcent = 0
+ if floor(done) == done or done>100:
+ done_str = '%i' % done
+ else:
+ done_str = '%.1f' % done
+ if floor(budget) == budget or budget>100:
+ budget_str = '%i' % budget
+ else:
+ budget_str = '%.1f' % budget
+
+ title = u'%s/%s = %i%%' % (done_str, budget_str, pourcent)
+ short_title = title
+ if self.overrun_percentage(entity):
+ title += u' overrun +%sj (+%i%%)' % (self.overrun(entity),
+ self.overrun_percentage(entity))
+ overrun = self.overrun(entity)
+ if floor(overrun) == overrun or overrun>100:
+ overrun_str = '%i' % overrun
+ else:
+ overrun_str = '%.1f' % overrun
+ short_title += u' +%s' % overrun_str
+ # write bars
+ maxi = max(done+todo, budget)
+ if maxi == 0:
+ maxi = 1
+
+ cid = make_uid('progress_bar')
+ self._cw.html_headers.add_onload('draw_progressbar("canvas%s", %i, %i, %i, "%s");' %
+ (cid,
+ int(100.*done/maxi), int(100.*(done+todo)/maxi),
+ int(100.*budget/maxi), color),
+ jsoncall=self._cw.json_request)
+ self.w(u'%s<br/>'
+ u'<canvas class="progressbar" id="canvas%s" width="100" height="10"></canvas>'
+ % (short_title.replace(' ',' '), cid))
--- a/web/views/primary.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/views/primary.py Fri Mar 05 18:20:18 2010 +0100
@@ -62,6 +62,7 @@
self.render_entity_attributes(entity)
if self.main_related_section:
self.render_entity_relations(entity)
+ self.content_navigation_components('navcontentbottom')
self.w(u'</div>')
# side boxes
if boxes or hasattr(self, 'render_side_related'):
@@ -73,7 +74,6 @@
self.render_side_boxes(boxes)
self.w(u'</div>')
self.w(u'</td></tr></table>')
- self.content_navigation_components('navcontentbottom')
def content_navigation_components(self, context):
self.w(u'<div class="%s">' % context)
--- a/web/views/workflow.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/views/workflow.py Fri Mar 05 18:20:18 2010 +0100
@@ -67,7 +67,7 @@
form = self.get_form(entity, transition)
self.w(u'<h4>%s %s</h4>\n' % (self._cw._(transition.name),
entity.view('oneline')))
- msg = self.req._('status will change from %(st1)s to %(st2)s') % {
+ msg = self._cw._('status will change from %(st1)s to %(st2)s') % {
'st1': entity.printable_state,
'st2': self._cw._(transition.destination(entity).name)}
self.w(u'<p>%s</p>\n' % msg)
--- a/web/views/xmlrss.py Thu Mar 04 18:57:13 2010 +0100
+++ b/web/views/xmlrss.py Fri Mar 05 18:20:18 2010 +0100
@@ -188,11 +188,14 @@
self.w(u'<guid isPermaLink="true">%s</guid>\n'
% xml_escape(entity.absolute_url()))
self.render_title_link(entity)
- self._marker('description', entity.dc_description(format='text/html'))
+ self.render_description(entity)
self._marker('dc:date', entity.dc_date(self.date_format))
self.render_entity_creator(entity)
self.w(u'</item>\n')
+ def render_description(self, entity):
+ self._marker('description', entity.dc_description(format='text/html'))
+
def render_title_link(self, entity):
self._marker('title', entity.dc_long_title())
self._marker('link', entity.absolute_url())