# HG changeset patch # User sylvain.thenault@logilab.fr # Date 1234870437 -3600 # Node ID 75b4c661f71baa2b9812b4d8afbe8cc9fb495986 # Parent e5956e9ebef1e27c526b4958814a870e9d9e56db proper documentation, some bug fixes, upgrade some selectors diff -r e5956e9ebef1 -r 75b4c661f71b selectors.py --- a/selectors.py Tue Feb 17 09:59:15 2009 +0100 +++ b/selectors.py Tue Feb 17 12:33:57 2009 +0100 @@ -105,183 +105,24 @@ TRACED_OIDS = () return traceback is None -# very basic selectors ######################################################## - -def yes(cls, *args, **kwargs): - """accept everything""" - return 1 -yes_selector = deprecated_function(yes) - -@lltrace -def none_rset(cls, req, rset, *args, **kwargs): - """accept no result set""" - if rset is None: - return 1 - return 0 -norset_selector = deprecated_function(none_rset) - -@lltrace -def any_rset(cls, req, rset, *args, **kwargs): - """accept result set, whatever the number of result""" - if rset is not None: - return 1 - return 0 -rset_selector = deprecated_function(any_rset) - -@lltrace -def nonempty_rset(cls, req, rset, *args, **kwargs): - """accept any non empty result set""" - if rset is not None and rset.rowcount: - return 1 - return 0 -anyrset_selector = deprecated_function(nonempty_rset) - -@lltrace -def empty_rset(cls, req, rset, *args, **kwargs): - """accept empty result set""" - if rset is not None and rset.rowcount == 0: - return 1 - return 0 -emptyrset_selector = deprecated_function(empty_rset) - -@lltrace -def one_line_rset(cls, req, rset, row=None, *args, **kwargs): - """accept result set with a single line of result""" - if rset is not None and (row is not None or rset.rowcount == 1): - return 1 - return 0 -onelinerset_selector = deprecated_function(one_line_rset) - -@lltrace -def two_lines_rset(cls, req, rset, *args, **kwargs): - """accept result set with *at least* two lines of result""" - if rset is not None and rset.rowcount > 1: - return 1 - return 0 -twolinerset_selector = deprecated_function(two_lines_rset) - -@lltrace -def two_cols_rset(cls, req, rset, *args, **kwargs): - """accept result set with at least one line and two columns of result""" - if rset is not None and rset.rowcount > 0 and len(rset.rows[0]) > 1: - return 1 - return 0 -twocolrset_selector = deprecated_function(two_cols_rset) - -@lltrace -def paginated_rset(cls, req, rset, *args, **kwargs): - """accept result sets with more rows than the page size - """ - page_size = kwargs.get('page_size') - if page_size is None: - page_size = req.form.get('page_size') - if page_size is None: - page_size = req.property_value('navigation.page-size') - else: - page_size = int(page_size) - if rset is None or len(rset) <= page_size: - return 0 - return 1 -largerset_selector = deprecated_function(paginated_rset) - -@lltrace -def sorted_rset(cls, req, rset, row=None, col=0, **kwargs): - """accept sorted result set""" - rqlst = rset.syntax_tree() - if len(rqlst.children) > 1 or not rqlst.children[0].orderby: - return 0 - return 2 -sortedrset_selector = deprecated_function(sorted_rset) - -@lltrace -def one_etype_rset(cls, req, rset, *args, **kwargs): - """accept result set where entities in the first columns are all of the - same type - """ - if len(rset.column_types(0)) != 1: - return 0 - return 1 -oneetyperset_selector = deprecated_function(one_etype_rset) - -@lltrace -def two_etypes_rset(cls, req, rset, **kwargs): - """accepts resultsets containing several entity types""" - if rset: - etypes = rset.column_types(0) - if len(etypes) > 1: - return 1 - return 0 -multitype_selector = deprecated_function(two_etypes_rset) - - -class match_search_state(Selector): - def __init__(self, *expected): - self.expected = expected - - @lltrace - def __call__(self, cls, req, rset, row=None, col=0, **kwargs): - """checks if the current request search state is in one of the expected states - the wrapped class - - search state should be either 'normal' or 'linksearch' (eg searching for an - object to create a relation with another) - """ - try: - if not req.search_state[0] in self.expected: - return 0 - except AttributeError: - return 1 # class doesn't care about search state, accept it - return 1 - - -class match_form_params(match_search_state): - """check if parameters specified as initializer arguments are specified - in request form parameters - """ - @lltrace - def __call__(self, cls, req, *args, **kwargs): - score = 0 - for param in self.expected: - val = req.form.get(param) - if not val: - return 0 - score += 1 - return len(self.expected) - - -class match_kwargs(match_search_state): - """check if parameters specified as initializer arguments are specified - in named parameters - """ - @lltrace - def __call__(self, cls, req, *args, **kwargs): - for arg in self.expected: - if not arg in kwargs: - return 0 - return len(self.expected) - - -@lltrace -def anonymous_user(cls, req, *args, **kwargs): - """accept if user is anonymous""" - if req.cnx.anonymous_connection: - return 1 - return 0 -anonymous_selector = deprecated_function(anonymous_user) - -@lltrace -def authenticated_user(cls, req, *args, **kwargs): - """accept if user is authenticated""" - return not anonymous_user(cls, req, *args, **kwargs) -not_anonymous_selector = deprecated_function(authenticated_user) # abstract selectors ########################################################## class EClassSelector(Selector): """abstract class for selectors working on the entity classes of the result - set + set. Its __call__ method has the following behaviour: + + * if row is specified, return the score returned by the score_class method + called with the entity class found in the specified cell + * else return the sum of score returned by the score_class method for each + entity type found in the specified column, unless: + - `once_is_enough` is True, in which case the first non-zero score is + returned + - `once_is_enough` is False, in which case if score_class return 0, 0 is + returned """ - once_is_enough = False + def __init__(self, once_is_enough=False): + self.once_is_enough = once_is_enough @lltrace def __call__(self, cls, req, rset, row=None, col=0, **kwargs): @@ -292,28 +133,44 @@ for etype in rset.column_types(col): if etype is None: # outer join continue - if etype in BASE_TYPES: - return 0 - escore = self.score_class(cls.vreg.etype_class(etype), req) - if not escore: + escore = self.score(cls, req, etype) + if not escore and not self.once_is_enough: return 0 elif self.once_is_enough: return escore score += escore else: etype = rset.description[row][col] - if etype is not None and not etype in BASE_TYPES: - score = self.score_class(cls.vreg.etype_class(etype), req) + if etype is not None: + score = self.score(cls, req, etype) return score and (score + 1) + def score(self, cls, req, etype): + if etype in BASE_TYPES: + return 0 + return self.score_class(cls.vreg.etype_class(etype), req) + def score_class(self, eclass, req): raise NotImplementedError() -class EntitySelector(Selector): +class EntitySelector(EClassSelector): """abstract class for selectors working on the entity instances of the - result set + result set. Its __call__ method has the following behaviour: + + * if row is specified, return the score returned by the score_entity method + called with the entity instance found in the specified cell + * else return the sum of score returned by the score_entity method for each + entity found in the specified column, unless: + - `once_is_enough` is True, in which case the first non-zero score is + returned + - `once_is_enough` is False, in which case if score_class return 0, 0 is + returned + + note: None values (resulting from some outer join in the query) are not + considered. """ + @lltrace def __call__(self, cls, req, rset, row=None, col=0, **kwargs): if not rset: @@ -323,12 +180,11 @@ for row, rowvalue in enumerate(rset.rows): if rowvalue[col] is None: # outer join continue - try: - escore = self.score(req, rset, row, col)) - except NotAnEntity: + escore = self.score(req, rset, row, col)) + if not escore and not self.once_is_enough: return 0 - if not escore: - return 0 + elif self.once_is_enough: + return escore score += escore else: etype = rset.description[row][col] @@ -345,21 +201,284 @@ def score_entity(self, entity): raise NotImplementedError() + +# very basic selectors ######################################################## + +def yes(cls, *args, **kwargs): + """accept everything""" + return 1 + +@lltrace +def none_rset(cls, req, rset, *args, **kwargs): + """accept no result set (e.g. given rset is None)""" + if rset is None: + return 1 + return 0 + +@lltrace +def any_rset(cls, req, rset, *args, **kwargs): + """accept result set, whatever the number of result it contains""" + if rset is not None: + return 1 + return 0 + +@lltrace +def nonempty_rset(cls, req, rset, *args, **kwargs): + """accept any non empty result set""" + if rset is not None and rset.rowcount: + return 1 + return 0 + +@lltrace +def empty_rset(cls, req, rset, *args, **kwargs): + """accept empty result set""" + if rset is not None and rset.rowcount == 0: + return 1 + return 0 + +@lltrace +def one_line_rset(cls, req, rset, row=None, *args, **kwargs): + """if row is specified, accept result set with a single line of result, + else accepts anyway + """ + if rset is not None and (row is not None or rset.rowcount == 1): + return 1 + return 0 + +@lltrace +def two_lines_rset(cls, req, rset, *args, **kwargs): + """accept result set with *at least* two lines of result""" + if rset is not None and rset.rowcount > 1: + return 1 + return 0 + +@lltrace +def two_cols_rset(cls, req, rset, *args, **kwargs): + """accept result set with at least one line and two columns of result""" + if rset is not None and rset.rowcount and len(rset.rows[0]) > 1: + return 1 + return 0 + +@lltrace +def paginated_rset(cls, req, rset, *args, **kwargs): + """accept result set with more lines than the page size. + + Page size is searched in (respecting order): + * a page_size argument + * a page_size form parameters + * the navigation.page-size property + """ + page_size = kwargs.get('page_size') + if page_size is None: + page_size = req.form.get('page_size') + if page_size is None: + page_size = req.property_value('navigation.page-size') + else: + page_size = int(page_size) + if rset is None or rset.rowcount <= page_size: + return 0 + return 1 + +@lltrace +def sorted_rset(cls, req, rset, row=None, col=0, **kwargs): + """accept sorted result set""" + rqlst = rset.syntax_tree() + if len(rqlst.children) > 1 or not rqlst.children[0].orderby: + return 0 + return 2 + +@lltrace +def one_etype_rset(cls, req, rset, row=None, col=0, *args, **kwargs): + """accept result set where entities in the specified column (or 0) are all + of the same type + """ + if len(rset.column_types(col)) != 1: + return 0 + return 1 + +@lltrace +def two_etypes_rset(cls, req, rset, row=None, col=0, **kwargs): + """accept result set where entities in the specified column (or 0) are not + of the same type + """ + if rset: + etypes = rset.column_types(col) + if len(etypes) > 1: + return 1 + return 0 + +class non_final_entity(EClassSelector): + """accept if entity type found in the result set is non final. + + See `EClassSelector` documentation for behaviour when row is not specified. + """ + def score(self, cls, req, etype): + if etype in BASE_TYPES: + return 0 + return 1 + +@lltrace +def anonymous_user(cls, req, *args, **kwargs): + """accept if user is anonymous""" + if req.cnx.anonymous_connection: + return 1 + return 0 + +@lltrace +def authenticated_user(cls, req, *args, **kwargs): + """accept if user is authenticated""" + return not anonymous_user(cls, req, *args, **kwargs) + +@lltrace +def primary_view(cls, req, rset, row=None, col=0, view=None, **kwargs): + """accept if view given as named argument is a primary view, or if no view + is given + """ + if view is not None and not view.is_primary(): + return 0 + return 1 + +@lltrace +def match_context_prop(cls, req, rset, row=None, col=0, context=None, + **kwargs): + """accept if: + * no context given + * context (`basestring`) is matching the context property value for the + given cls + """ + propval = req.property_value('%s.%s.context' % (cls.__registry__, cls.id)) + if not propval: + propval = cls.context + if context is not None and propval and context != propval: + return 0 + return 1 + + +class match_search_state(Selector): + """accept if the current request search state is in one of the expected + states given to the initializer + + :param expected: either 'normal' or 'linksearch' (eg searching for an + object to create a relation with another) + """ + def __init__(self, *expected): + self.expected = expected + + @lltrace + def __call__(self, cls, req, rset, row=None, col=0, **kwargs): + try: + if not req.search_state[0] in self.expected: + return 0 + except AttributeError: + return 1 # class doesn't care about search state, accept it + return 1 + + +class match_form_params(match_search_state): + """accept if parameters specified as initializer arguments are specified + in request's form parameters + + :param *expected: parameters (eg `basestring`) which are expected to be + found in request's form parameters + """ + + @lltrace + def __call__(self, cls, req, *args, **kwargs): + score = 0 + for param in self.expected: + val = req.form.get(param) + if not val: + return 0 + score += 1 + return len(self.expected) + + +class match_kwargs(match_search_state): + """accept if parameters specified as initializer arguments are specified + in named arguments given to the selector + + :param *expected: parameters (eg `basestring`) which are expected to be + found in named arguments (kwargs) + """ + + @lltrace + def __call__(self, cls, req, *args, **kwargs): + for arg in self.expected: + if not arg in kwargs: + return 0 + return len(self.expected) + + +class match_user_groups(Selector): + """accept if logged users is in at least one of the given groups. Returned + score is the number of groups in which the user is. + + If the special 'owners' group is given: + * if row is specified check the entity at the given row/col is owned by the + logged user + * if row is not specified check all entities in col are owned by the logged + user + + :param *required_groups: name of groups (`basestring`) in which the logged + user should be + """ + + def __init__(self, *required_groups): + self.required_groups = required_groups + + @lltrace + def __call__(self, cls, req, rset=None, row=None, col=0, **kwargs): + user = req.user + if user is None: + return int('guests' in self.required_groups) + score = user.matching_groups(self.required_groups) + if not score and 'owners' in self.required_groups and rset: + nbowned = 0 + if row is not None: + if not user.owns(rset[row][col]): + return 0 + score = 1 + else: + score = all(user.owns(r[col or 0]) for r in rset) + return 0 + + +class appobject_selectable(Selector): + """accept with another appobject is selectable using selector's input + context. + + :param registry: a registry name (`basestring`) + :param oid: an object identifier (`basestring`) + """ + def __init__(self, registry, oid): + self.registry = registry + self.oid = oid + + def __call__(self, cls, req, rset, *args, **kwargs): + try: + cls.vreg.select_object(self.registry, self.oid, req, rset, *args, **kwargs) + return 1 + except NoSelectableObject: + return 0 + + # not so basic selectors ###################################################### class implements(EClassSelector): - """initializer takes a list of interfaces or entity types as argument - - * if row is None, return the number of implemented interfaces for each - entity's class in the result set at the specified column (or column 0). - If any class has no matching interface, return 0. - * if row is specified, return number of implemented interfaces by the - entity's class at this row (and column) + """accept if entity class found in the result set implements at least one + of the interfaces given as argument. Returned score is the number of + implemented interfaces. + + See `EClassSelector` documentation for behaviour when row is not specified. - if some interface is an entity class, the score will reflect class - proximity so the most specific object'll be selected + :param *expected_ifaces: expected interfaces. An interface may be a class + or an entity type (e.g. `basestring`) in which case + the associated class will be searched in the + registry (at selection time) + + note: when interface is an entity class, the score will reflect class + proximity so the most specific object'll be selected """ - def __init__(self, *expected_ifaces): self.expected_ifaces = expected_ifaces @@ -376,21 +495,30 @@ # adjust score if the interface is an entity class if iface is eclass: score += len(eclass.e_schema.ancestors()) - print 'is majoration', len(eclass.e_schema.ancestors()) +# print 'is majoration', len(eclass.e_schema.ancestors()) else: parents = [e.type for e in eclass.e_schema.ancestors()] for index, etype in enumerate(reversed(parents)): basecls = eclass.vreg.etype_class(etype) if iface is basecls: score += index - print 'etype majoration', index +# print 'etype majoration', index break return score class specified_etype_implements(implements): - """return the "interface score" for class associated to 'etype' (expected in - request form or arguments) + """accept if entity class specified using an 'etype' parameters in name + argument or request form implements at least one of the interfaces given as + argument. Returned score is the number of implemented interfaces. + + :param *expected_ifaces: expected interfaces. An interface may be a class + or an entity type (e.g. `basestring`) in which case + the associated class will be searched in the + registry (at selection time) + + note: when interface is an entity class, the score will reflect class + proximity so the most specific object'll be selected """ @lltrace @@ -406,29 +534,32 @@ class relation_possible(EClassSelector): - """initializer takes relation name as argument and an optional role (default - as subject) and target type (default to unspecified) - - * if row is None, return 1 if every entity's class in the result set at the - specified column (or column 0) may have this relation (as role). If target - type is specified, check the relation's end may be of this target type. - - * if row is specified, check relation is supported by the entity's class at - this row (and column) + """accept if entity class found in the result set support the relation. + + See `EClassSelector` documentation for behaviour when row is not specified. + + :param rtype: a relation type (`basestring`) + :param role: the role of the result set entity in the relation. 'subject' or + 'object', default to 'subject'. + :param target_type: if specified, check the relation's end may be of this + target type (`basestring`) + :param action: a relation schema action (one of 'read', 'add', 'delete') + which must be granted to the logged user, else a 0 score will + be returned """ def __init__(self, rtype, role='subject', target_etype=None, - permission='read', once_is_enough=False): + action='read', once_is_enough=False): + super(relation_possible, self).__init__(once_is_enough) self.rtype = rtype self.role = role self.target_etype = target_etype - self.permission = permission - self.once_is_enough = once_is_enough + self.action = action @lltrace def __call__(self, cls, *args, **kwargs): rschema = cls.schema.rschema(self.rtype) - if not (rschema.has_perm(req, self.permission) - or rschema.has_local_role(self.permission)): + if not (rschema.has_perm(req, self.action) + or rschema.has_local_role(self.action)): return 0 return super(relation_possible, self)(cls, *args, **kwargs) @@ -452,56 +583,11 @@ return 1 -class non_final_entity(EClassSelector): - """initializer takes no argument - - * if row is None, return 1 if there are only non final entity's class in the - result set at the specified column (or column 0) - * if row is specified, return 1 if entity's class at this row (and column) - isn't final - """ - def score_class(self, eclass, req): - return int(not eclass.e_schema.is_final()) - - -class match_user_groups(Selector): - """initializer takes users group as argument +class has_editable_relation(EntitySelector): + """accept if some relations for an entity found in the result set is + editable by the logged user. - * check logged user is in one of the given groups. If special 'owners' group - given: - - if row is specified check the entity at the given row/col is owned by - the logged user - - if row is not specified check all entities in col are owned by the - logged user - """ - - def __init__(self, *required_groups): - self.required_groups = required_groups - - @lltrace - def __call__(self, cls, req, rset=None, row=None, col=0, **kwargs): - user = req.user - if user is None: - return int('guests' in self.require_groups) - score = user.matching_groups(self.require_groups) - if not score and 'owners' in self.require_groups and rset: - nbowned = 0 - if row is not None: - if not user.owns(rset[row][col]): - return 0 - score = 1 - else: - score = all(user.owns(r[col or 0]) for r in rset) - return 0 - - -class has_editable_relation(EntitySelector): - """initializer takes no argument - - * if row is specified check the entity at the given row/col has some - relation editable by the logged user - * if row is not specified check all entities in col are owned have some - relation editable by the logged userlogged user + See `EntitySelector` documentation for behaviour when row is not specified. """ def score_entity(self, entity): @@ -518,12 +604,14 @@ class may_add_relation(EntitySelector): - """initializer takes a relation type and optional role (default to - 'subject') as argument + """accept if the relation can be added to an entity found in the result set + by the logged user. + + See `EntitySelector` documentation for behaviour when row is not specified. - if row is specified check the relation may be added to the entity at the - given row/col (if row specified) or to every entities in the given col (if - row is not specified) + :param rtype: a relation type (`basestring`) + :param role: the role of the result set entity in the relation. 'subject' or + 'object', default to 'subject'. """ def __init__(self, rtype, role='subject'): @@ -534,28 +622,60 @@ rschema = entity.schema.rschema(self.rtype) if self.role == 'subject': if not rschema.has_perm(req, 'add', fromeid=entity.eid): - return False + return 0 elif not rschema.has_perm(req, 'add', toeid=entity.eid): - return False - return True + return 0 + return 1 + + +class has_related_entities(EntitySelector): + """accept if entity found in the result set has some linked entities using + the specified relation (optionaly filtered according to the specified target + type). + + See `EntitySelector` documentation for behaviour when row is not specified. + + :param rtype: a relation type (`basestring`) + :param role: the role of the result set entity in the relation. 'subject' or + 'object', default to 'subject'. + :param target_type: if specified, check the relation's end may be of this + target type (`basestring`) + """ + def __init__(self, rtype, role='subject', target_etype=None, + once_is_enough=False): + self.rtype = rtype + self.role = role + self.target_etype = target_etype + self.once_is_enough = once_is_enough + + def score_entity(self, entity): + rset = entity.related(self.rtype, self.role) + if self.target_etype: + return any(x for x, in rset.description if x == self.target_etype) + return bool(rset) class has_permission(EntitySelector): - """initializer takes a schema action (eg 'read'/'add'/'delete'/'update') as - argument + """accept if user has the permission to do the requested action on a result + set entity. + + * if row is specified, return 1 if user has the permission on the entity + instance found in the specified cell + * else return a positive score if user has the permission for every entity + in the found in the specified column - * if row is specified check user has permission to do the requested action - on the entity at the given row/col - * if row is specified check user has permission to do the requested action - on all entities in the given col + note: None values (resulting from some outer join in the query) are not + considered. + + :param action: an entity schema action (eg 'read'/'add'/'delete'/'update') """ - def __init__(self, schema_action): - self.schema_action = schema_action + def __init__(self, action): + self.action = action @lltrace def __call__(self, cls, req, rset, row=None, col=0, **kwargs): user = req.user - action = self.schema_action + action = self.action if row is None: score = 0 need_local_check = [] @@ -572,53 +692,48 @@ else: # even a local role won't be enough return 0 - score += accepted + score += 1 if need_local_check: # check local role for entities of necessary types for i, row in enumerate(rset): if not rset.description[i][0] in need_local_check: continue - if not self.score_entity(rset.get_entity(i, col)): + if not self.score(req, rset, i, col)): return 0 - score += 1 + score += 1 return score - if rset.description[row][col] in BASE_TYPES: - return 0 - return self.score_entity(rset.get_entity(row, col)) + return self.score(req, rset, i, col) def score_entity(self, entity): - if entity.has_perm(self.schema_action): + if entity.has_perm(self.action): return 1 return 0 class has_add_permission(EClassSelector): - """return 1 if the user may add some entity of the types found in the - result set (0 else) + """accept if logged user has the add permission on entity class found in the + result set, and class is not a strict subobject. + + See `EClassSelector` documentation for behaviour when row is not specified. """ - def score_class(self, eclass, req): - eschema = eclass.e_schema + def score(self, cls, req, etype): + eschema = cls.schema.eschema(etype) if not (eschema.is_final() or eschema.is_subobject(strict=True)) \ and eschema.has_perm(req, 'add'): return 1 return 0 - -class score_entity(EntitySelector): - """initializer takes a function as argument (which is expected to take an - entity as argument) - - return the score returned by the function on the entity at the given row/col - (if row specified) or the sum of the score for every entities in the given - col (if row is not specified). Return 0 at the first entity scoring to zero. - """ - def __init__(self, scorefunc): - self.score_entity = scorefunc - class rql_condition(EntitySelector): - """initializer takes a rql expression as argument (which should use X - variable to represent the context entity). + """accept if an arbitrary rql return some results for an eid found in the + result set. Returned score is the number of items returned by the rql + condition. + + See `EntitySelector` documentation for behaviour when row is not specified. + + :param expression: basestring containing an rql expression, which should use + X variable to represent the context entity and may use U + to represent the logged user return the sum of the number of items returned by the rql condition as score or 0 at the first entity scoring to zero. @@ -638,93 +753,53 @@ class but_etype(EntitySelector): - """initializer takes an entity type as argument. + """accept if the given entity types are not found in the result set. - return 0 if an entity type is this type, else 1. + See `EntitySelector` documentation for behaviour when row is not specified. + + :param *etypes: entity types (`basestring`) which should be refused """ - def __init__(self, etype): - self.etype = etype + def __init__(self, *etypes): + self.but_etypes = etypes def score(self, req, rset, row, col): - if rset.description[row][col] == self.etype: + if rset.description[row][col] in self.but_etypes: return 0 return 1 - -class appobject_selectable(Selector): - """initializer takes a registry and oid of another vobject + +class score_entity(EntitySelector): + """accept if some arbitrary function return a positive score for an entity + found in the result set. - return 1 if the given registry and object is selectable using selector's - input context, else 0 - """ - def __init__(self, registry, oid): - self.registry = registry - self.oid = oid - - def __call__(self, cls, req, rset, *args, **kwargs): - try: - cls.vreg.select_object(self.registry, self.oid, req, rset, *args, **kwargs) - return 1 - except NoSelectableObject: - return 0 + See `EntitySelector` documentation for behaviour when row is not specified. - -# XXX not so basic selectors ###################################################### - -@lltrace -def etype_rtype_selector(cls, req, rset, row=None, col=0, **kwargs): - """only check if the user has read access on the entity's type refered - by the .etype attribute and on the relations's type refered by the - .rtype attribute if set. + :param scorefunc: callable expected to take an entity as argument and to + return a score >= 0 """ - schema = cls.schema - perm = getattr(cls, 'require_permission', 'read') - if hasattr(cls, 'etype'): - eschema = schema.eschema(cls.etype) - if not (eschema.has_perm(req, perm) or eschema.has_local_role(perm)): - return 0 - if hasattr(cls, 'rtype'): - rschema = schema.rschema(cls.rtype) - if not (rschema.has_perm(req, perm) or rschema.has_local_role(perm)): - return 0 - return 1 - -@lltrace -def has_related_entities(cls, req, rset, row=None, col=0, **kwargs): - return bool(rset.get_entity(row or 0, col or 0).related(cls.rtype, role(cls))) - -@lltrace -def user_can_add_etype(cls, req, rset, row=None, col=0, **kwargs): - """only check if the user has add access on the entity's type refered - by the .etype attribute. - """ - if not cls.schema.eschema(cls.etype).has_perm(req, 'add'): - return 0 - return 1 -add_etype_selector = deprecated_function(user_can_add_etype) - -@lltrace -def match_context_prop(cls, req, rset, row=None, col=0, context=None, - **kwargs): - propval = req.property_value('%s.%s.context' % (cls.__registry__, cls.id)) - if not propval: - propval = cls.context - if context is not None and propval and context != propval: - return 0 - return 1 -contextprop_selector = deprecated_function(match_context_prop) - -@lltrace -def primary_view(cls, req, rset, row=None, col=0, view=None, **kwargs): - if view is not None and not view.is_primary(): - return 0 - return 1 -primaryview_selector = deprecated_function(primary_view) - + def __init__(self, scorefunc): + self.score_entity = scorefunc # XXX DEPRECATED ############################################################## +yes_selector = deprecated_function(yes) +norset_selector = deprecated_function(none_rset) +rset_selector = deprecated_function(any_rset) +anyrset_selector = deprecated_function(nonempty_rset) +emptyrset_selector = deprecated_function(empty_rset) +onelinerset_selector = deprecated_function(one_line_rset) +twolinerset_selector = deprecated_function(two_lines_rset) +twocolrset_selector = deprecated_function(two_cols_rset) +largerset_selector = deprecated_function(paginated_rset) +sortedrset_selector = deprecated_function(sorted_rset) +oneetyperset_selector = deprecated_function(one_etype_rset) +multitype_selector = deprecated_function(two_etypes_rset) +anonymous_selector = deprecated_function(anonymous_user) +not_anonymous_selector = deprecated_function(authenticated_user) +primaryview_selector = deprecated_function(primary_view) +contextprop_selector = deprecated_function(match_context_prop) + def nfentity_selector(cls, req, rset, row=None, col=0, **kwargs): return non_final_entity()(cls, req, rset, row, col) nfentity_selector = deprecated_function(nfentity_selector) @@ -789,6 +864,24 @@ return but_etype(cls.etype)(cls, req, rset, row, col) but_etype_selector = deprecated_function(but_etype_selector) +@lltrace +def etype_rtype_selector(cls, req, rset, row=None, col=0, **kwargs): + """only check if the user has read access on the entity's type refered + by the .etype attribute and on the relations's type refered by the + .rtype attribute if set. + """ + schema = cls.schema + perm = getattr(cls, 'require_permission', 'read') + if hasattr(cls, 'etype'): + eschema = schema.eschema(cls.etype) + if not (eschema.has_perm(req, perm) or eschema.has_local_role(perm)): + return 0 + if hasattr(cls, 'rtype'): + rschema = schema.rschema(cls.rtype) + if not (rschema.has_perm(req, perm) or rschema.has_local_role(perm)): + return 0 + return 1 +etype_rtype_selector = deprecated_function(etype_rtype_selector) #req_form_params_selector = deprecated_function(match_form_params) # form_params #kwargs_selector = deprecated_function(match_kwargs) # expected_kwargs @@ -804,10 +897,8 @@ name='searchstate_accept_one') searchstate_accept_one_selector = deprecated_function(searchstate_accept_one) -searchstate_accept_one_but_etype = chainall(searchstate_accept_one, but_etype, - name='searchstate_accept_one_but_etype') -searchstate_accept_one_but_etype_selector = deprecated_function( - searchstate_accept_one_but_etype) +searchstate_accept = deprecated_function(searchstate_accept) +searchstate_accept_one = deprecated_function(searchstate_accept_one) def require_group_compat(registered):