31 # in Python2.4 |
31 # in Python2.4 |
32 from cubicweb import selectors |
32 from cubicweb import selectors |
33 selectors.TRACED_OIDS = ('calendar',) |
33 selectors.TRACED_OIDS = ('calendar',) |
34 self.view('calendar', myrset) |
34 self.view('calendar', myrset) |
35 selectors.TRACED_OIDS = () |
35 selectors.TRACED_OIDS = () |
36 |
36 |
37 |
37 |
38 :organization: Logilab |
38 :organization: Logilab |
39 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
39 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
40 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
40 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
41 """ |
41 """ |
95 or |
95 or |
96 |
96 |
97 >>> with traced_selection( ('oid1', 'oid2') ): |
97 >>> with traced_selection( ('oid1', 'oid2') ): |
98 ... # some code in which you want to debug selectors |
98 ... # some code in which you want to debug selectors |
99 ... # for objects with id 'oid1' and 'oid2' |
99 ... # for objects with id 'oid1' and 'oid2' |
100 |
100 |
101 """ |
101 """ |
102 def __init__(self, traced='all'): |
102 def __init__(self, traced='all'): |
103 self.traced = traced |
103 self.traced = traced |
104 |
104 |
105 def __enter__(self): |
105 def __enter__(self): |
106 global TRACED_OIDS |
106 global TRACED_OIDS |
107 TRACED_OIDS = self.traced |
107 TRACED_OIDS = self.traced |
108 |
108 |
109 def __exit__(self, exctype, exc, traceback): |
109 def __exit__(self, exctype, exc, traceback): |
137 # abstract selectors ########################################################## |
137 # abstract selectors ########################################################## |
138 |
138 |
139 class PartialSelectorMixIn(object): |
139 class PartialSelectorMixIn(object): |
140 """convenience mix-in for selectors that will look into the containing |
140 """convenience mix-in for selectors that will look into the containing |
141 class to find missing information. |
141 class to find missing information. |
142 |
142 |
143 cf. `cubicweb.web.action.LinkToEntityAction` for instance |
143 cf. `cubicweb.web.action.LinkToEntityAction` for instance |
144 """ |
144 """ |
145 def __call__(self, cls, *args, **kwargs): |
145 def __call__(self, cls, *args, **kwargs): |
146 self.complete(cls) |
146 self.complete(cls) |
147 return super(PartialSelectorMixIn, self).__call__(cls, *args, **kwargs) |
147 return super(PartialSelectorMixIn, self).__call__(cls, *args, **kwargs) |
155 self.expected_ifaces = expected_ifaces |
155 self.expected_ifaces = expected_ifaces |
156 |
156 |
157 def __str__(self): |
157 def __str__(self): |
158 return '%s(%s)' % (self.__class__.__name__, |
158 return '%s(%s)' % (self.__class__.__name__, |
159 ','.join(str(s) for s in self.expected_ifaces)) |
159 ','.join(str(s) for s in self.expected_ifaces)) |
160 |
160 |
161 def score_interfaces(self, cls_or_inst, cls): |
161 def score_interfaces(self, cls_or_inst, cls): |
162 score = 0 |
162 score = 0 |
163 vreg, eschema = cls_or_inst.vreg, cls_or_inst.e_schema |
163 vreg, eschema = cls_or_inst.vreg, cls_or_inst.e_schema |
164 for iface in self.expected_ifaces: |
164 for iface in self.expected_ifaces: |
165 if isinstance(iface, basestring): |
165 if isinstance(iface, basestring): |
185 - `once_is_enough` is False, in which case if score_class return 0, 0 is |
185 - `once_is_enough` is False, in which case if score_class return 0, 0 is |
186 returned |
186 returned |
187 """ |
187 """ |
188 def __init__(self, once_is_enough=False): |
188 def __init__(self, once_is_enough=False): |
189 self.once_is_enough = once_is_enough |
189 self.once_is_enough = once_is_enough |
190 |
190 |
191 @lltrace |
191 @lltrace |
192 def __call__(self, cls, req, rset, row=None, col=0, **kwargs): |
192 def __call__(self, cls, req, rset, row=None, col=0, **kwargs): |
193 if not rset: |
193 if not rset: |
194 return 0 |
194 return 0 |
195 score = 0 |
195 score = 0 |
211 |
211 |
212 def score(self, cls, req, etype): |
212 def score(self, cls, req, etype): |
213 if etype in BASE_TYPES: |
213 if etype in BASE_TYPES: |
214 return 0 |
214 return 0 |
215 return self.score_class(cls.vreg.etype_class(etype), req) |
215 return self.score_class(cls.vreg.etype_class(etype), req) |
216 |
216 |
217 def score_class(self, eclass, req): |
217 def score_class(self, eclass, req): |
218 raise NotImplementedError() |
218 raise NotImplementedError() |
219 |
219 |
220 |
220 |
221 class EntitySelector(EClassSelector): |
221 class EntitySelector(EClassSelector): |
263 def score(self, req, rset, row, col): |
263 def score(self, req, rset, row, col): |
264 try: |
264 try: |
265 return self.score_entity(rset.get_entity(row, col)) |
265 return self.score_entity(rset.get_entity(row, col)) |
266 except NotAnEntity: |
266 except NotAnEntity: |
267 return 0 |
267 return 0 |
268 |
268 |
269 def score_entity(self, entity): |
269 def score_entity(self, entity): |
270 raise NotImplementedError() |
270 raise NotImplementedError() |
271 |
271 |
272 |
272 |
273 # very basic selectors ######################################################## |
273 # very basic selectors ######################################################## |
300 def nonempty_rset(cls, req, rset, *args, **kwargs): |
300 def nonempty_rset(cls, req, rset, *args, **kwargs): |
301 """accept any non empty result set""" |
301 """accept any non empty result set""" |
302 if rset is not None and rset.rowcount: |
302 if rset is not None and rset.rowcount: |
303 return 1 |
303 return 1 |
304 return 0 |
304 return 0 |
305 |
305 |
306 @objectify_selector |
306 @objectify_selector |
307 @lltrace |
307 @lltrace |
308 def empty_rset(cls, req, rset, *args, **kwargs): |
308 def empty_rset(cls, req, rset, *args, **kwargs): |
309 """accept empty result set""" |
309 """accept empty result set""" |
310 if rset is not None and rset.rowcount == 0: |
310 if rset is not None and rset.rowcount == 0: |
339 |
339 |
340 @objectify_selector |
340 @objectify_selector |
341 @lltrace |
341 @lltrace |
342 def paginated_rset(cls, req, rset, *args, **kwargs): |
342 def paginated_rset(cls, req, rset, *args, **kwargs): |
343 """accept result set with more lines than the page size. |
343 """accept result set with more lines than the page size. |
344 |
344 |
345 Page size is searched in (respecting order): |
345 Page size is searched in (respecting order): |
346 * a page_size argument |
346 * a page_size argument |
347 * a page_size form parameters |
347 * a page_size form parameters |
348 * the navigation.page-size property |
348 * the navigation.page-size property |
349 """ |
349 """ |
440 |
440 |
441 |
441 |
442 class match_search_state(Selector): |
442 class match_search_state(Selector): |
443 """accept if the current request search state is in one of the expected |
443 """accept if the current request search state is in one of the expected |
444 states given to the initializer |
444 states given to the initializer |
445 |
445 |
446 :param expected: either 'normal' or 'linksearch' (eg searching for an |
446 :param expected: either 'normal' or 'linksearch' (eg searching for an |
447 object to create a relation with another) |
447 object to create a relation with another) |
448 """ |
448 """ |
449 def __init__(self, *expected): |
449 def __init__(self, *expected): |
450 assert expected, self |
450 assert expected, self |
451 self.expected = frozenset(expected) |
451 self.expected = frozenset(expected) |
452 |
452 |
453 def __str__(self): |
453 def __str__(self): |
454 return '%s(%s)' % (self.__class__.__name__, |
454 return '%s(%s)' % (self.__class__.__name__, |
455 ','.join(sorted(str(s) for s in self.expected))) |
455 ','.join(sorted(str(s) for s in self.expected))) |
456 |
456 |
457 @lltrace |
457 @lltrace |
458 def __call__(self, cls, req, rset, row=None, col=0, **kwargs): |
458 def __call__(self, cls, req, rset, row=None, col=0, **kwargs): |
459 try: |
459 try: |
460 if not req.search_state[0] in self.expected: |
460 if not req.search_state[0] in self.expected: |
461 return 0 |
461 return 0 |
500 |
500 |
501 |
501 |
502 class match_user_groups(match_search_state): |
502 class match_user_groups(match_search_state): |
503 """accept if logged users is in at least one of the given groups. Returned |
503 """accept if logged users is in at least one of the given groups. Returned |
504 score is the number of groups in which the user is. |
504 score is the number of groups in which the user is. |
505 |
505 |
506 If the special 'owners' group is given: |
506 If the special 'owners' group is given: |
507 * if row is specified check the entity at the given row/col is owned by the |
507 * if row is specified check the entity at the given row/col is owned by the |
508 logged user |
508 logged user |
509 * if row is not specified check all entities in col are owned by the logged |
509 * if row is not specified check all entities in col are owned by the logged |
510 user |
510 user |
511 |
511 |
512 :param *required_groups: name of groups (`basestring`) in which the logged |
512 :param *required_groups: name of groups (`basestring`) in which the logged |
513 user should be |
513 user should be |
514 """ |
514 """ |
515 |
515 |
516 @lltrace |
516 @lltrace |
517 def __call__(self, cls, req, rset=None, row=None, col=0, **kwargs): |
517 def __call__(self, cls, req, rset=None, row=None, col=0, **kwargs): |
518 user = req.user |
518 user = req.user |
519 if user is None: |
519 if user is None: |
520 return int('guests' in self.expected) |
520 return int('guests' in self.expected) |
548 :param oid: an object identifier (`basestring`) |
548 :param oid: an object identifier (`basestring`) |
549 """ |
549 """ |
550 def __init__(self, registry, oid): |
550 def __init__(self, registry, oid): |
551 self.registry = registry |
551 self.registry = registry |
552 self.oid = oid |
552 self.oid = oid |
553 |
553 |
554 def __call__(self, cls, req, rset, *args, **kwargs): |
554 def __call__(self, cls, req, rset, *args, **kwargs): |
555 try: |
555 try: |
556 cls.vreg.select_object(self.registry, self.oid, req, rset, *args, **kwargs) |
556 cls.vreg.select_object(self.registry, self.oid, req, rset, *args, **kwargs) |
557 return 1 |
557 return 1 |
558 except NoSelectableObject: |
558 except NoSelectableObject: |
570 |
570 |
571 :param *expected_ifaces: expected interfaces. An interface may be a class |
571 :param *expected_ifaces: expected interfaces. An interface may be a class |
572 or an entity type (e.g. `basestring`) in which case |
572 or an entity type (e.g. `basestring`) in which case |
573 the associated class will be searched in the |
573 the associated class will be searched in the |
574 registry (at selection time) |
574 registry (at selection time) |
575 |
575 |
576 note: when interface is an entity class, the score will reflect class |
576 note: when interface is an entity class, the score will reflect class |
577 proximity so the most specific object'll be selected |
577 proximity so the most specific object'll be selected |
578 """ |
578 """ |
579 def score_class(self, eclass, req): |
579 def score_class(self, eclass, req): |
580 return self.score_interfaces(eclass, eclass) |
580 return self.score_interfaces(eclass, eclass) |
587 |
587 |
588 :param *expected_ifaces: expected interfaces. An interface may be a class |
588 :param *expected_ifaces: expected interfaces. An interface may be a class |
589 or an entity type (e.g. `basestring`) in which case |
589 or an entity type (e.g. `basestring`) in which case |
590 the associated class will be searched in the |
590 the associated class will be searched in the |
591 registry (at selection time) |
591 registry (at selection time) |
592 |
592 |
593 note: when interface is an entity class, the score will reflect class |
593 note: when interface is an entity class, the score will reflect class |
594 proximity so the most specific object'll be selected |
594 proximity so the most specific object'll be selected |
595 """ |
595 """ |
596 |
596 |
597 @lltrace |
597 @lltrace |
598 def __call__(self, cls, req, *args, **kwargs): |
598 def __call__(self, cls, req, *args, **kwargs): |
599 try: |
599 try: |
600 etype = req.form['etype'] |
600 etype = req.form['etype'] |
601 except KeyError: |
601 except KeyError: |
615 |
615 |
616 :param *expected_ifaces: expected interfaces. An interface may be a class |
616 :param *expected_ifaces: expected interfaces. An interface may be a class |
617 or an entity type (e.g. `basestring`) in which case |
617 or an entity type (e.g. `basestring`) in which case |
618 the associated class will be searched in the |
618 the associated class will be searched in the |
619 registry (at selection time) |
619 registry (at selection time) |
620 |
620 |
621 note: when interface is an entity class, the score will reflect class |
621 note: when interface is an entity class, the score will reflect class |
622 proximity so the most specific object'll be selected |
622 proximity so the most specific object'll be selected |
623 """ |
623 """ |
624 def score_entity(self, entity): |
624 def score_entity(self, entity): |
625 return self.score_interfaces(entity, entity.__class__) |
625 return self.score_interfaces(entity, entity.__class__) |
626 |
626 |
627 |
627 |
628 class relation_possible(EClassSelector): |
628 class relation_possible(EClassSelector): |
653 if not (rschema.has_perm(req, self.action) |
653 if not (rschema.has_perm(req, self.action) |
654 or rschema.has_local_role(self.action)): |
654 or rschema.has_local_role(self.action)): |
655 return 0 |
655 return 0 |
656 score = super(relation_possible, self).__call__(cls, req, *args, **kwargs) |
656 score = super(relation_possible, self).__call__(cls, req, *args, **kwargs) |
657 return score |
657 return score |
658 |
658 |
659 def score_class(self, eclass, req): |
659 def score_class(self, eclass, req): |
660 eschema = eclass.e_schema |
660 eschema = eclass.e_schema |
661 try: |
661 try: |
662 if self.role == 'object': |
662 if self.role == 'object': |
663 rschema = eschema.object_relation(self.rtype) |
663 rschema = eschema.object_relation(self.rtype) |
682 The selector will look for class attributes to find its missing |
682 The selector will look for class attributes to find its missing |
683 information. The list of attributes required on the class |
683 information. The list of attributes required on the class |
684 for this selector are: |
684 for this selector are: |
685 |
685 |
686 - `rtype`: same as `rtype` parameter of the `relation_possible` selector |
686 - `rtype`: same as `rtype` parameter of the `relation_possible` selector |
687 |
687 |
688 - `role`: this attribute will be passed to the `cubicweb.role` function |
688 - `role`: this attribute will be passed to the `cubicweb.role` function |
689 to determine the role of class in the relation |
689 to determine the role of class in the relation |
690 |
690 |
691 - `etype` (optional): the entity type on the other side of the relation |
691 - `etype` (optional): the entity type on the other side of the relation |
692 |
692 |
693 :param action: a relation schema action (one of 'read', 'add', 'delete') |
693 :param action: a relation schema action (one of 'read', 'add', 'delete') |
694 which must be granted to the logged user, else a 0 score will |
694 which must be granted to the logged user, else a 0 score will |
695 be returned |
695 be returned |
696 """ |
696 """ |
697 def __init__(self, action='read', once_is_enough=False): |
697 def __init__(self, action='read', once_is_enough=False): |
705 |
705 |
706 |
706 |
707 class may_add_relation(EntitySelector): |
707 class may_add_relation(EntitySelector): |
708 """accept if the relation can be added to an entity found in the result set |
708 """accept if the relation can be added to an entity found in the result set |
709 by the logged user. |
709 by the logged user. |
710 |
710 |
711 See `EntitySelector` documentation for behaviour when row is not specified. |
711 See `EntitySelector` documentation for behaviour when row is not specified. |
712 |
712 |
713 :param rtype: a relation type (`basestring`) |
713 :param rtype: a relation type (`basestring`) |
714 :param role: the role of the result set entity in the relation. 'subject' or |
714 :param role: the role of the result set entity in the relation. 'subject' or |
715 'object', default to 'subject'. |
715 'object', default to 'subject'. |
716 """ |
716 """ |
717 |
717 |
718 def __init__(self, rtype, role='subject', once_is_enough=False): |
718 def __init__(self, rtype, role='subject', once_is_enough=False): |
719 super(may_add_relation, self).__init__(once_is_enough) |
719 super(may_add_relation, self).__init__(once_is_enough) |
720 self.rtype = rtype |
720 self.rtype = rtype |
721 self.role = role |
721 self.role = role |
722 |
722 |
723 def score_entity(self, entity): |
723 def score_entity(self, entity): |
724 rschema = entity.schema.rschema(self.rtype) |
724 rschema = entity.schema.rschema(self.rtype) |
725 if self.role == 'subject': |
725 if self.role == 'subject': |
726 if not rschema.has_perm(entity.req, 'add', fromeid=entity.eid): |
726 if not rschema.has_perm(entity.req, 'add', fromeid=entity.eid): |
727 return 0 |
727 return 0 |
736 The selector will look for class attributes to find its missing |
736 The selector will look for class attributes to find its missing |
737 information. The list of attributes required on the class |
737 information. The list of attributes required on the class |
738 for this selector are: |
738 for this selector are: |
739 |
739 |
740 - `rtype`: same as `rtype` parameter of the `relation_possible` selector |
740 - `rtype`: same as `rtype` parameter of the `relation_possible` selector |
741 |
741 |
742 - `role`: this attribute will be passed to the `cubicweb.role` function |
742 - `role`: this attribute will be passed to the `cubicweb.role` function |
743 to determine the role of class in the relation. |
743 to determine the role of class in the relation. |
744 |
744 |
745 :param action: a relation schema action (one of 'read', 'add', 'delete') |
745 :param action: a relation schema action (one of 'read', 'add', 'delete') |
746 which must be granted to the logged user, else a 0 score will |
746 which must be granted to the logged user, else a 0 score will |
747 be returned |
747 be returned |
748 """ |
748 """ |
749 def __init__(self, once_is_enough=False): |
749 def __init__(self, once_is_enough=False): |
751 |
751 |
752 def complete(self, cls): |
752 def complete(self, cls): |
753 self.rtype = cls.rtype |
753 self.rtype = cls.rtype |
754 self.role = role(cls) |
754 self.role = role(cls) |
755 |
755 |
756 |
756 |
757 class has_related_entities(EntitySelector): |
757 class has_related_entities(EntitySelector): |
758 """accept if entity found in the result set has some linked entities using |
758 """accept if entity found in the result set has some linked entities using |
759 the specified relation (optionaly filtered according to the specified target |
759 the specified relation (optionaly filtered according to the specified target |
760 type). Checks first if the relation is possible. |
760 type). Checks first if the relation is possible. |
761 |
761 |
762 See `EntitySelector` documentation for behaviour when row is not specified. |
762 See `EntitySelector` documentation for behaviour when row is not specified. |
763 |
763 |
764 :param rtype: a relation type (`basestring`) |
764 :param rtype: a relation type (`basestring`) |
765 :param role: the role of the result set entity in the relation. 'subject' or |
765 :param role: the role of the result set entity in the relation. 'subject' or |
766 'object', default to 'subject'. |
766 'object', default to 'subject'. |
771 once_is_enough=False): |
771 once_is_enough=False): |
772 super(has_related_entities, self).__init__(once_is_enough) |
772 super(has_related_entities, self).__init__(once_is_enough) |
773 self.rtype = rtype |
773 self.rtype = rtype |
774 self.role = role |
774 self.role = role |
775 self.target_etype = target_etype |
775 self.target_etype = target_etype |
776 |
776 |
777 def score_entity(self, entity): |
777 def score_entity(self, entity): |
778 relpossel = relation_possible(self.rtype, self.role, self.target_etype) |
778 relpossel = relation_possible(self.rtype, self.role, self.target_etype) |
779 if not relpossel.score_class(entity.__class__, entity.req): |
779 if not relpossel.score_class(entity.__class__, entity.req): |
780 return 0 |
780 return 0 |
781 rset = entity.related(self.rtype, self.role) |
781 rset = entity.related(self.rtype, self.role) |
790 The selector will look for class attributes to find its missing |
790 The selector will look for class attributes to find its missing |
791 information. The list of attributes required on the class |
791 information. The list of attributes required on the class |
792 for this selector are: |
792 for this selector are: |
793 |
793 |
794 - `rtype`: same as `rtype` parameter of the `relation_possible` selector |
794 - `rtype`: same as `rtype` parameter of the `relation_possible` selector |
795 |
795 |
796 - `role`: this attribute will be passed to the `cubicweb.role` function |
796 - `role`: this attribute will be passed to the `cubicweb.role` function |
797 to determine the role of class in the relation. |
797 to determine the role of class in the relation. |
798 |
798 |
799 - `etype` (optional): the entity type on the other side of the relation |
799 - `etype` (optional): the entity type on the other side of the relation |
800 |
800 |
801 :param action: a relation schema action (one of 'read', 'add', 'delete') |
801 :param action: a relation schema action (one of 'read', 'add', 'delete') |
802 which must be granted to the logged user, else a 0 score will |
802 which must be granted to the logged user, else a 0 score will |
803 be returned |
803 be returned |
804 """ |
804 """ |
805 def __init__(self, once_is_enough=False): |
805 def __init__(self, once_is_enough=False): |
820 * else return a positive score if user has the permission for every entity |
820 * else return a positive score if user has the permission for every entity |
821 in the found in the specified column |
821 in the found in the specified column |
822 |
822 |
823 note: None values (resulting from some outer join in the query) are not |
823 note: None values (resulting from some outer join in the query) are not |
824 considered. |
824 considered. |
825 |
825 |
826 :param action: an entity schema action (eg 'read'/'add'/'delete'/'update') |
826 :param action: an entity schema action (eg 'read'/'add'/'delete'/'update') |
827 """ |
827 """ |
828 def __init__(self, action, once_is_enough=False): |
828 def __init__(self, action, once_is_enough=False): |
829 super(has_permission, self).__init__(once_is_enough) |
829 super(has_permission, self).__init__(once_is_enough) |
830 self.action = action |
830 self.action = action |
831 |
831 |
832 @lltrace |
832 @lltrace |
833 def __call__(self, cls, req, rset, row=None, col=0, **kwargs): |
833 def __call__(self, cls, req, rset, row=None, col=0, **kwargs): |
834 if rset is None: |
834 if rset is None: |
835 return 0 |
835 return 0 |
836 user = req.user |
836 user = req.user |
837 action = self.action |
837 action = self.action |
838 if row is None: |
838 if row is None: |
839 score = 0 |
839 score = 0 |
840 need_local_check = [] |
840 need_local_check = [] |
841 geteschema = cls.schema.eschema |
841 geteschema = cls.schema.eschema |
842 for etype in rset.column_types(0): |
842 for etype in rset.column_types(0): |
843 if etype in BASE_TYPES: |
843 if etype in BASE_TYPES: |
844 return 0 |
844 return 0 |
845 eschema = geteschema(etype) |
845 eschema = geteschema(etype) |
860 if not self.score(req, rset, i, col): |
860 if not self.score(req, rset, i, col): |
861 return 0 |
861 return 0 |
862 score += 1 |
862 score += 1 |
863 return score |
863 return score |
864 return self.score(req, rset, row, col) |
864 return self.score(req, rset, row, col) |
865 |
865 |
866 def score_entity(self, entity): |
866 def score_entity(self, entity): |
867 if entity.has_perm(self.action): |
867 if entity.has_perm(self.action): |
868 return 1 |
868 return 1 |
869 return 0 |
869 return 0 |
870 |
870 |
885 |
885 |
886 class rql_condition(EntitySelector): |
886 class rql_condition(EntitySelector): |
887 """accept if an arbitrary rql return some results for an eid found in the |
887 """accept if an arbitrary rql return some results for an eid found in the |
888 result set. Returned score is the number of items returned by the rql |
888 result set. Returned score is the number of items returned by the rql |
889 condition. |
889 condition. |
890 |
890 |
891 See `EntitySelector` documentation for behaviour when row is not specified. |
891 See `EntitySelector` documentation for behaviour when row is not specified. |
892 |
892 |
893 :param expression: basestring containing an rql expression, which should use |
893 :param expression: basestring containing an rql expression, which should use |
894 X variable to represent the context entity and may use U |
894 X variable to represent the context entity and may use U |
895 to represent the logged user |
895 to represent the logged user |
902 if 'U' in frozenset(split_expression(expression)): |
902 if 'U' in frozenset(split_expression(expression)): |
903 rql = 'Any X WHERE X eid %%(x)s, U eid %%(u)s, %s' % expression |
903 rql = 'Any X WHERE X eid %%(x)s, U eid %%(u)s, %s' % expression |
904 else: |
904 else: |
905 rql = 'Any X WHERE X eid %%(x)s, %s' % expression |
905 rql = 'Any X WHERE X eid %%(x)s, %s' % expression |
906 self.rql = rql |
906 self.rql = rql |
907 |
907 |
908 def score(self, req, rset, row, col): |
908 def score(self, req, rset, row, col): |
909 try: |
909 try: |
910 return len(req.execute(self.rql, {'x': rset[row][col], |
910 return len(req.execute(self.rql, {'x': rset[row][col], |
911 'u': req.user.eid}, 'x')) |
911 'u': req.user.eid}, 'x')) |
912 except Unauthorized: |
912 except Unauthorized: |
913 return 0 |
913 return 0 |
914 |
914 |
915 |
915 |
916 class but_etype(EntitySelector): |
916 class but_etype(EntitySelector): |
917 """accept if the given entity types are not found in the result set. |
917 """accept if the given entity types are not found in the result set. |
918 |
918 |
919 See `EntitySelector` documentation for behaviour when row is not specified. |
919 See `EntitySelector` documentation for behaviour when row is not specified. |
920 |
920 |
921 :param *etypes: entity types (`basestring`) which should be refused |
921 :param *etypes: entity types (`basestring`) which should be refused |
922 """ |
922 """ |
923 def __init__(self, *etypes): |
923 def __init__(self, *etypes): |
924 super(but_etype, self).__init__() |
924 super(but_etype, self).__init__() |
925 self.but_etypes = etypes |
925 self.but_etypes = etypes |
926 |
926 |
927 def score(self, req, rset, row, col): |
927 def score(self, req, rset, row, col): |
928 if rset.description[row][col] in self.but_etypes: |
928 if rset.description[row][col] in self.but_etypes: |
929 return 0 |
929 return 0 |
930 return 1 |
930 return 1 |
931 |
931 |
932 |
932 |
933 class score_entity(EntitySelector): |
933 class score_entity(EntitySelector): |
934 """accept if some arbitrary function return a positive score for an entity |
934 """accept if some arbitrary function return a positive score for an entity |
935 found in the result set. |
935 found in the result set. |
936 |
936 |
937 See `EntitySelector` documentation for behaviour when row is not specified. |
937 See `EntitySelector` documentation for behaviour when row is not specified. |
938 |
938 |
939 :param scorefunc: callable expected to take an entity as argument and to |
939 :param scorefunc: callable expected to take an entity as argument and to |
940 return a score >= 0 |
940 return a score >= 0 |
941 """ |
941 """ |
942 def __init__(self, scorefunc, once_is_enough=False): |
942 def __init__(self, scorefunc, once_is_enough=False): |
943 super(score_entity, self).__init__(once_is_enough) |
943 super(score_entity, self).__init__(once_is_enough) |
944 self.score_entity = scorefunc |
944 self.score_entity = scorefunc |
945 |
945 |
1020 return 1 |
1020 return 1 |
1021 _rqlcondition_selector = deprecated_function(_rql_condition) |
1021 _rqlcondition_selector = deprecated_function(_rql_condition) |
1022 |
1022 |
1023 rqlcondition_selector = deprecated_function(chainall(non_final_entity(), one_line_rset, _rql_condition, |
1023 rqlcondition_selector = deprecated_function(chainall(non_final_entity(), one_line_rset, _rql_condition, |
1024 name='rql_condition')) |
1024 name='rql_condition')) |
1025 |
1025 |
1026 def but_etype_selector(cls, req, rset, row=None, col=0, **kwargs): |
1026 def but_etype_selector(cls, req, rset, row=None, col=0, **kwargs): |
1027 return but_etype(cls.etype)(cls, req, rset, row, col) |
1027 return but_etype(cls.etype)(cls, req, rset, row, col) |
1028 but_etype_selector = deprecated_function(but_etype_selector) |
1028 but_etype_selector = deprecated_function(but_etype_selector) |
1029 |
1029 |
1030 @lltrace |
1030 @lltrace |
1077 registered = registered.im_func |
1077 registered = registered.im_func |
1078 def _deprecate(cls, vreg): |
1078 def _deprecate(cls, vreg): |
1079 warn(msg, DeprecationWarning) |
1079 warn(msg, DeprecationWarning) |
1080 return registered(cls, vreg) |
1080 return registered(cls, vreg) |
1081 return _deprecate |
1081 return _deprecate |
1082 |
1082 |
1083 @unbind_method |
1083 @unbind_method |
1084 def require_group_compat(registered): |
1084 def require_group_compat(registered): |
1085 def plug_selector(cls, vreg): |
1085 def plug_selector(cls, vreg): |
1086 cls = registered(cls, vreg) |
1086 cls = registered(cls, vreg) |
1087 if getattr(cls, 'require_groups', None): |
1087 if getattr(cls, 'require_groups', None): |
1121 warn('use "use rql_condition(expression)" instead of using condition', |
1121 warn('use "use rql_condition(expression)" instead of using condition', |
1122 DeprecationWarning) |
1122 DeprecationWarning) |
1123 cls.__select__ &= rql_condition(cls.condition) |
1123 cls.__select__ &= rql_condition(cls.condition) |
1124 return cls |
1124 return cls |
1125 return plug_selector |
1125 return plug_selector |
1126 |
1126 |
1127 @unbind_method |
1127 @unbind_method |
1128 def has_relation_compat(registered): |
1128 def has_relation_compat(registered): |
1129 def plug_selector(cls, vreg): |
1129 def plug_selector(cls, vreg): |
1130 cls = registered(cls, vreg) |
1130 cls = registered(cls, vreg) |
1131 if getattr(cls, 'etype', None): |
1131 if getattr(cls, 'etype', None): |