common/selectors.py
branchtls-sprint
changeset 634 0badd061ce0f
parent 633 087e3f1e87c8
child 646 8a9551089912
equal deleted inserted replaced
633:087e3f1e87c8 634:0badd061ce0f
     1 """This file contains some basic selectors required by application objects.
     1 from warnings import warn
     2 
     2 warn('moved to cubicweb.selectors', DeprecationWarning, stacklevel=2)
     3 A selector is responsible to score how well an object may be used with a
     3 from cubicweb.selectors import *
     4 given result set (publishing time selection)
     4 from cubicweb.selectors import _rql_condition
     5 
       
     6 If you have trouble with selectors, especially if the objet (typically
       
     7 a view or a component) you want to use is not selected and you want to
       
     8 know which one(s) of its selectors fail (e.g. returns 0), you can use
       
     9 `traced_selection` or even direclty `TRACED_OIDS`.
       
    10 
       
    11 `TRACED_OIDS` is a tuple of traced object ids. The special value
       
    12 'all' may be used to log selectors for all objects.
       
    13 
       
    14 For instance, say that the following code yields a `NoSelectableObject`
       
    15 exception::
       
    16 
       
    17     self.view('calendar', myrset)
       
    18 
       
    19 You can log the selectors involved for *calendar* by replacing the line
       
    20 above by::
       
    21 
       
    22     # in Python2.5
       
    23     from cubicweb.common.selectors import traced_selection
       
    24     with traced_selection():
       
    25         self.view('calendar', myrset)
       
    26 
       
    27     # in Python2.4
       
    28     from cubicweb.common import selectors
       
    29     selectors.TRACED_OIDS = ('calendar',)
       
    30     self.view('calendar', myrset)
       
    31     selectors.TRACED_OIDS = ()
       
    32  
       
    33 
       
    34 
       
    35 :organization: Logilab
       
    36 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
    37 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
    38 """
       
    39 
       
    40 __docformat__ = "restructuredtext en"
       
    41 
       
    42 import logging
       
    43 
       
    44 from logilab.common.compat import all
       
    45 from logilab.common.deprecation import deprecated_function
       
    46 from logilab.common.interface import implements as implements_iface
       
    47 
       
    48 from yams import BASE_TYPES
       
    49 
       
    50 from cubicweb import Unauthorized, NoSelectableObject, role
       
    51 from cubicweb.vregistry import NoSelectableObject, Selector, chainall, chainfirst
       
    52 from cubicweb.cwvreg import DummyCursorError
       
    53 from cubicweb.cwconfig import CubicWebConfiguration
       
    54 from cubicweb.schema import split_expression
       
    55 
       
    56 # helpers for debugging selectors
       
    57 SELECTOR_LOGGER = logging.getLogger('cubicweb.selectors')
       
    58 TRACED_OIDS = ()
       
    59 
       
    60 def lltrace(selector):
       
    61     # don't wrap selectors if not in development mode
       
    62     if CubicWebConfiguration.mode == 'installed':
       
    63         return selector
       
    64     def traced(cls, *args, **kwargs):
       
    65         if isinstance(cls, Selector):
       
    66             selname = cls.__class__.__name__
       
    67             oid = args[0].id
       
    68         else:
       
    69             selname = selector.__name__
       
    70             oid = cls.id
       
    71         ret = selector(cls, *args, **kwargs)
       
    72         if TRACED_OIDS == 'all' or oid in TRACED_OIDS:
       
    73             #SELECTOR_LOGGER.warning('selector %s returned %s for %s', selname, ret, cls)
       
    74             print 'selector %s returned %s for %s' % (selname, ret, cls)
       
    75         return ret
       
    76     traced.__name__ = selector.__name__
       
    77     return traced
       
    78 
       
    79 class traced_selection(object):
       
    80     """selector debugging helper.
       
    81 
       
    82     Typical usage is :
       
    83 
       
    84     >>> with traced_selection():
       
    85     ...     # some code in which you want to debug selectors
       
    86     ...     # for all objects
       
    87 
       
    88     or
       
    89 
       
    90     >>> with traced_selection( ('oid1', 'oid2') ):
       
    91     ...     # some code in which you want to debug selectors
       
    92     ...     # for objects with id 'oid1' and 'oid2'
       
    93     
       
    94     """
       
    95     def __init__(self, traced='all'):
       
    96         self.traced = traced
       
    97         
       
    98     def __enter__(self):
       
    99         global TRACED_OIDS
       
   100         TRACED_OIDS = self.traced
       
   101 
       
   102     def __exit__(self, exctype, exc, traceback):
       
   103         global TRACED_OIDS
       
   104         TRACED_OIDS = ()
       
   105         return traceback is None
       
   106 
       
   107 # very basic selectors ########################################################
       
   108 
       
   109 def yes(cls, *args, **kwargs):
       
   110     """accept everything"""
       
   111     return 1
       
   112 yes_selector = deprecated_function(yes)
       
   113 
       
   114 @lltrace
       
   115 def none_rset(cls, req, rset, *args, **kwargs):
       
   116     """accept no result set"""
       
   117     if rset is None:
       
   118         return 1
       
   119     return 0
       
   120 norset_selector = deprecated_function(none_rset)
       
   121 
       
   122 @lltrace
       
   123 def any_rset(cls, req, rset, *args, **kwargs):
       
   124     """accept result set, whatever the number of result"""
       
   125     if rset is not None:
       
   126         return 1
       
   127     return 0
       
   128 rset_selector = deprecated_function(any_rset)
       
   129 
       
   130 @lltrace
       
   131 def nonempty_rset(cls, req, rset, *args, **kwargs):
       
   132     """accept any non empty result set"""
       
   133     if rset is not None and rset.rowcount:
       
   134         return 1
       
   135     return 0
       
   136 anyrset_selector = deprecated_function(nonempty_rset)
       
   137     
       
   138 @lltrace
       
   139 def empty_rset(cls, req, rset, *args, **kwargs):
       
   140     """accept empty result set"""
       
   141     if rset is not None and rset.rowcount == 0:
       
   142         return 1
       
   143     return 0
       
   144 emptyrset_selector = deprecated_function(empty_rset)
       
   145 
       
   146 @lltrace
       
   147 def one_line_rset(cls, req, rset, row=None, *args, **kwargs):
       
   148     """accept result set with a single line of result"""
       
   149     if rset is not None and (row is not None or rset.rowcount == 1):
       
   150         return 1
       
   151     return 0
       
   152 onelinerset_selector = deprecated_function(one_line_rset)
       
   153 
       
   154 @lltrace
       
   155 def two_lines_rset(cls, req, rset, *args, **kwargs):
       
   156     """accept result set with *at least* two lines of result"""
       
   157     if rset is not None and rset.rowcount > 1:
       
   158         return 1
       
   159     return 0
       
   160 twolinerset_selector = deprecated_function(two_lines_rset)
       
   161 
       
   162 @lltrace
       
   163 def two_cols_rset(cls, req, rset, *args, **kwargs):
       
   164     """accept result set with at least one line and two columns of result"""
       
   165     if rset is not None and rset.rowcount > 0 and len(rset.rows[0]) > 1:
       
   166         return 1
       
   167     return 0
       
   168 twocolrset_selector = deprecated_function(two_cols_rset)
       
   169 
       
   170 @lltrace
       
   171 def paginated_rset(cls, req, rset, *args, **kwargs):
       
   172     """accept result sets with more rows than the page size
       
   173     """
       
   174     page_size = kwargs.get('page_size')
       
   175     if page_size is None:
       
   176         page_size = req.form.get('page_size')
       
   177         if page_size is None:
       
   178             page_size = req.property_value('navigation.page-size')
       
   179         else:
       
   180             page_size = int(page_size)
       
   181     if rset is None or len(rset) <= page_size:
       
   182         return 0
       
   183     return 1
       
   184 largerset_selector = deprecated_function(paginated_rset)
       
   185 
       
   186 @lltrace
       
   187 def sorted_rset(cls, req, rset, row=None, col=0, **kwargs):
       
   188     """accept sorted result set"""
       
   189     rqlst = rset.syntax_tree()
       
   190     if len(rqlst.children) > 1 or not rqlst.children[0].orderby:
       
   191         return 0
       
   192     return 2
       
   193 sortedrset_selector = deprecated_function(sorted_rset)
       
   194 
       
   195 @lltrace
       
   196 def one_etype_rset(cls, req, rset, *args, **kwargs):
       
   197     """accept result set where entities in the first columns are all of the
       
   198     same type
       
   199     """
       
   200     if len(rset.column_types(0)) != 1:
       
   201         return 0
       
   202     return 1
       
   203 oneetyperset_selector = deprecated_function(one_etype_rset)
       
   204 
       
   205 @lltrace
       
   206 def two_etypes_rset(cls, req, rset, **kwargs):
       
   207     """accepts resultsets containing several entity types"""
       
   208     if rset:
       
   209         etypes = rset.column_types(0)
       
   210         if len(etypes) > 1:
       
   211             return 1
       
   212     return 0
       
   213 multitype_selector = deprecated_function(two_etypes_rset)
       
   214 
       
   215 
       
   216 class match_search_state(Selector):
       
   217     def __init__(self, *expected):
       
   218         self.expected = expected
       
   219         
       
   220     @lltrace
       
   221     def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
       
   222         """checks if the current request search state is in one of the expected states
       
   223         the wrapped class
       
   224 
       
   225         search state should be either 'normal' or 'linksearch' (eg searching for an
       
   226         object to create a relation with another)
       
   227         """
       
   228         try:
       
   229             if not req.search_state[0] in self.expected:
       
   230                 return 0
       
   231         except AttributeError:
       
   232             return 1 # class doesn't care about search state, accept it
       
   233         return 1
       
   234 
       
   235 
       
   236 class match_form_params(match_search_state):
       
   237     """check if parameters specified as initializer arguments are specified
       
   238     in request form parameters
       
   239     """
       
   240     @lltrace
       
   241     def __call__(self, cls, req, *args, **kwargs):
       
   242         score = 0
       
   243         for param in self.expected:
       
   244             val = req.form.get(param)
       
   245             if not val:
       
   246                 return 0
       
   247             score += 1
       
   248         return len(self.expected)
       
   249 
       
   250 
       
   251 class match_kwargs(match_search_state):
       
   252     """check if parameters specified as initializer arguments are specified
       
   253     in named parameters
       
   254     """
       
   255     @lltrace
       
   256     def __call__(self, cls, req, *args, **kwargs):
       
   257         for arg in self.expected:
       
   258             if not arg in kwargs:
       
   259                 return 0
       
   260         return len(self.expected)
       
   261 
       
   262 
       
   263 @lltrace
       
   264 def anonymous_user(cls, req, *args, **kwargs):
       
   265     """accept if user is anonymous"""
       
   266     if req.cnx.anonymous_connection:
       
   267         return 1
       
   268     return 0
       
   269 anonymous_selector = deprecated_function(anonymous_user)
       
   270 
       
   271 @lltrace
       
   272 def authenticated_user(cls, req, *args, **kwargs):
       
   273     """accept if user is authenticated"""
       
   274     return not anonymous_user(cls, req, *args, **kwargs)
       
   275 not_anonymous_selector = deprecated_function(authenticated_user)
       
   276 
       
   277 # abstract selectors ##########################################################
       
   278 
       
   279 class EClassSelector(Selector):
       
   280     """abstract class for selectors working on the entity classes of the result
       
   281     set
       
   282     """
       
   283     once_is_enough = False
       
   284     
       
   285     @lltrace
       
   286     def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
       
   287         if not rset:
       
   288             return 0
       
   289         score = 0
       
   290         if row is None:
       
   291             for etype in rset.column_types(col):
       
   292                 if etype is None: # outer join
       
   293                     continue
       
   294                 if etype in BASE_TYPES:
       
   295                     return 0
       
   296                 escore = self.score_class(cls.vreg.etype_class(etype), req)
       
   297                 if not escore:
       
   298                     return 0
       
   299                 elif self.once_is_enough:
       
   300                     return escore
       
   301                 score += escore
       
   302         else:
       
   303             etype = rset.description[row][col]
       
   304             if etype is not None and not etype in BASE_TYPES:
       
   305                 score = self.score_class(cls.vreg.etype_class(etype), req)
       
   306         return score and (score + 1)
       
   307 
       
   308     def score_class(self, eclass, req):
       
   309         raise NotImplementedError()
       
   310 
       
   311 
       
   312 class EntitySelector(Selector):
       
   313     """abstract class for selectors working on the entity instances of the
       
   314     result set
       
   315     """
       
   316     @lltrace
       
   317     def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
       
   318         if not rset:
       
   319             return 0
       
   320         score = 0
       
   321         if row is None:
       
   322             for row, rowvalue in enumerate(rset.rows):
       
   323                 if rowvalue[col] is None: # outer join
       
   324                     continue
       
   325                 try:
       
   326                     escore = self.score_entity(rset.get_entity(row, col))
       
   327                 except NotAnEntity:
       
   328                     return 0
       
   329                 if not escore:
       
   330                     return 0
       
   331                 score += escore
       
   332         else:
       
   333             etype = rset.description[row][col]
       
   334             if etype is not None: # outer join
       
   335                 try:
       
   336                     score = self.score_entity(rset.get_entity(row, col))
       
   337                 except NotAnEntity:
       
   338                     return 0
       
   339         return score and (score + 1)
       
   340 
       
   341     def score_entity(self, entity):
       
   342         raise NotImplementedError()
       
   343 
       
   344 # not so basic selectors ######################################################
       
   345 
       
   346 class implements(EClassSelector):
       
   347     """initializer takes a list of interfaces or entity types as argument
       
   348     
       
   349     * if row is None, return the number of implemented interfaces for each
       
   350       entity's class in the result set at the specified column (or column 0).
       
   351       If any class has no matching interface, return 0.
       
   352     * if row is specified, return number of implemented interfaces by the
       
   353       entity's class at this row (and column)
       
   354 
       
   355     if some interface is an entity class, the score will reflect class
       
   356     proximity so the most specific object'll be selected
       
   357     """
       
   358 
       
   359     def __init__(self, *expected_ifaces):
       
   360         self.expected_ifaces = expected_ifaces
       
   361 
       
   362     def score_class(self, eclass, req):
       
   363         score = 0
       
   364         for iface in self.expected_ifaces:
       
   365             if isinstance(iface, basestring):
       
   366                 # entity type
       
   367                 iface = eclass.vreg.etype_class(iface)
       
   368             if implements_iface(eclass, iface):
       
   369                 score += 1
       
   370                 if getattr(iface, '__registry__', None) == 'etypes':
       
   371                     score += 1
       
   372                     # adjust score if the interface is an entity class
       
   373                     if iface is eclass:
       
   374                         score += len(eclass.e_schema.ancestors())
       
   375                         print 'is majoration', len(eclass.e_schema.ancestors()) 
       
   376                     else:
       
   377                         parents = [e.type for e in eclass.e_schema.ancestors()]
       
   378                         for index, etype in enumerate(reversed(parents)):
       
   379                             basecls = eclass.vreg.etype_class(etype)
       
   380                             if iface is basecls:
       
   381                                 score += index
       
   382                                 print 'etype majoration', index
       
   383                                 break
       
   384         return score
       
   385 
       
   386 
       
   387 class specified_etype_implements(implements):
       
   388     """return the "interface score" for class associated to 'etype' (expected in
       
   389     request form or arguments)
       
   390     """
       
   391     
       
   392     @lltrace
       
   393     def __call__(cls, req, *args, **kwargs):
       
   394         try:
       
   395             etype = req.form['etype']
       
   396         except KeyError:
       
   397             try:
       
   398                 etype = kwargs['etype']
       
   399             except KeyError:
       
   400                 return 0
       
   401         return self.score_class(cls.vreg.etype_class(etype), req)
       
   402 
       
   403 
       
   404 class relation_possible(EClassSelector):
       
   405     """initializer takes relation name as argument and an optional role (default
       
   406       as subject) and target type (default to unspecified)
       
   407       
       
   408     * if row is None, return 1 if every entity's class in the result set at the
       
   409       specified column (or column 0) may have this relation (as role). If target
       
   410       type is specified, check the relation's end may be of this target type.
       
   411       
       
   412     * if row is specified, check relation is supported by the entity's class at
       
   413       this row (and column)
       
   414     """
       
   415     def __init__(self, rtype, role='subject', target_etype=None,
       
   416                  permission='read', once_is_enough=False):
       
   417         self.rtype = rtype
       
   418         self.role = role
       
   419         self.target_etype = target_etype
       
   420         self.permission = permission
       
   421         self.once_is_enough = once_is_enough
       
   422 
       
   423     @lltrace
       
   424     def __call__(self, cls, *args, **kwargs):
       
   425         rschema = cls.schema.rschema(self.rtype)
       
   426         if not (rschema.has_perm(req, self.permission)
       
   427                 or rschema.has_local_role(self.permission)):
       
   428             return 0
       
   429         return super(relation_possible, self)(cls, *args, **kwargs)
       
   430         
       
   431     def score_class(self, eclass, req):
       
   432         eschema = eclass.e_schema
       
   433         try:
       
   434             if self.role == 'object':
       
   435                 rschema = eschema.object_relation(self.rtype)
       
   436             else:
       
   437                 rschema = eschema.subject_relation(self.rtype)
       
   438         except KeyError:
       
   439             return 0
       
   440         if self.target_etype is not None:
       
   441             try:
       
   442                 if self.role == 'object':
       
   443                     return self.target_etype in rschema.objects(eschema)
       
   444                 else:
       
   445                     return self.target_etype in rschema.subjects(eschema)
       
   446             except KeyError, ex:
       
   447                 return 0
       
   448         return 1
       
   449 
       
   450 
       
   451 class non_final_entity(EClassSelector):
       
   452     """initializer takes no argument
       
   453 
       
   454     * if row is None, return 1 if there are only non final entity's class in the
       
   455       result set at the specified column (or column 0)
       
   456     * if row is specified, return 1 if entity's class at this row (and column)
       
   457       isn't final
       
   458     """
       
   459     def score_class(self, eclass, req):
       
   460         return int(not eclass.e_schema.is_final())
       
   461 
       
   462 
       
   463 class match_user_groups(Selector):
       
   464     """initializer takes users group as argument
       
   465 
       
   466     * check logged user is in one of the given groups. If special 'owners' group
       
   467       given:
       
   468       - if row is specified check the entity at the given row/col is owned by
       
   469         the logged user
       
   470       - if row is not specified check all entities in col are owned by the
       
   471         logged user
       
   472     """
       
   473     
       
   474     def __init__(self, *required_groups):
       
   475         self.required_groups = required_groups
       
   476     
       
   477     @lltrace
       
   478     def __call__(self, cls, req, rset=None, row=None, col=0, **kwargs):
       
   479         user = req.user
       
   480         if user is None:
       
   481             return int('guests' in self.require_groups)
       
   482         score = user.matching_groups(self.require_groups)
       
   483         if not score and 'owners' in self.require_groups and rset:
       
   484             nbowned = 0
       
   485             if row is not None:
       
   486                 if not user.owns(rset[row][col]):
       
   487                     return 0
       
   488                 score = 1
       
   489             else:
       
   490                 score = all(user.owns(r[col or 0]) for r in rset)
       
   491         return 0
       
   492 
       
   493 
       
   494 class has_editable_relation(EntitySelector):
       
   495     """initializer takes no argument
       
   496 
       
   497     * if row is specified check the entity at the given row/col has some
       
   498       relation editable by the logged user
       
   499     * if row is not specified check all entities in col are owned have some
       
   500       relation editable by the logged userlogged user
       
   501     """
       
   502         
       
   503     def score_entity(self, entity):
       
   504         # if user has no update right but it can modify some relation,
       
   505         # display action anyway
       
   506         for dummy in entity.srelations_by_category(('generic', 'metadata'),
       
   507                                                    'add'):
       
   508             return 1
       
   509         for rschema, targetschemas, role in entity.relations_by_category(
       
   510             ('primary', 'secondary'), 'add'):
       
   511             if not rschema.is_final():
       
   512                 return 1
       
   513         return 0
       
   514 
       
   515 
       
   516 class may_add_relation(EntitySelector):
       
   517     """initializer takes a relation type and optional role (default to
       
   518     'subject') as argument
       
   519 
       
   520     if row is specified check the relation may be added to the entity at the
       
   521     given row/col (if row specified) or to every entities in the given col (if
       
   522     row is not specified)
       
   523     """
       
   524     
       
   525     def __init__(self, rtype, role='subject'):
       
   526         self.rtype = rtype
       
   527         self.role = role
       
   528         
       
   529     def score_entity(self, entity):
       
   530         rschema = entity.schema.rschema(self.rtype)
       
   531         if self.role == 'subject':
       
   532             if not rschema.has_perm(req, 'add', fromeid=entity.eid):
       
   533                 return False
       
   534         elif not rschema.has_perm(req, 'add', toeid=entity.eid):
       
   535             return False
       
   536         return True
       
   537 
       
   538         
       
   539 class has_permission(EntitySelector):
       
   540     """initializer takes a schema action (eg 'read'/'add'/'delete'/'update') as
       
   541     argument
       
   542 
       
   543     * if row is specified check user has permission to do the requested action
       
   544       on the entity at the given row/col
       
   545     * if row is specified check user has permission to do the requested action
       
   546       on all entities in the given col
       
   547     """
       
   548     def __init__(self, schema_action):
       
   549         self.schema_action = schema_action
       
   550         
       
   551     @lltrace
       
   552     def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
       
   553         user = req.user
       
   554         action = self.schema_action
       
   555         if row is None:
       
   556             score = 0
       
   557             need_local_check = [] 
       
   558             geteschema = cls.schema.eschema
       
   559             for etype in rset.column_types(0):
       
   560                 if etype in BASE_TYPES:
       
   561                     return 0
       
   562                 eschema = geteschema(etype)
       
   563                 if not user.matching_groups(eschema.get_groups(action)):
       
   564                     if eschema.has_local_role(action):
       
   565                         # have to ckeck local roles
       
   566                         need_local_check.append(eschema)
       
   567                         continue
       
   568                     else:
       
   569                         # even a local role won't be enough
       
   570                         return 0
       
   571                 score += accepted
       
   572             if need_local_check:
       
   573                 # check local role for entities of necessary types
       
   574                 for i, row in enumerate(rset):
       
   575                     if not rset.description[i][0] in need_local_check:
       
   576                         continue
       
   577                     if not self.score_entity(rset.get_entity(i, col)):
       
   578                         return 0
       
   579                     score += 1
       
   580             return score
       
   581         if rset.description[row][col] in BASE_TYPES:
       
   582             return 0
       
   583         return self.score_entity(rset.get_entity(row, col))
       
   584     
       
   585     def score_entity(self, entity):
       
   586         if entity.has_perm(self.schema_action):
       
   587             return 1
       
   588         return 0
       
   589 
       
   590 
       
   591 class has_add_permission(EClassSelector):
       
   592     """return 1 if the user may add some entity of the types found in the
       
   593     result set (0 else)
       
   594     """
       
   595     def score_class(self, eclass, req):
       
   596         eschema = eclass.e_schema
       
   597         if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
       
   598                and eschema.has_perm(req, 'add'):
       
   599             return 1
       
   600         return 0
       
   601 
       
   602         
       
   603 class score_entity(EntitySelector):
       
   604     """initializer takes a function as argument (which is expected to take an
       
   605     entity as argument)
       
   606 
       
   607     return the score returned by the function on the entity at the given row/col
       
   608     (if row specified) or the sum of the score for every entities in the given
       
   609     col (if row is not specified). Return 0 at the first entity scoring to zero.
       
   610     """
       
   611     def __init__(self, scorefunc):
       
   612         self.score_entity = scorefunc
       
   613 
       
   614     
       
   615 # XXX not so basic selectors ######################################################
       
   616 
       
   617 @lltrace
       
   618 def _rql_condition(cls, req, rset, row=None, col=0, **kwargs):
       
   619     """accept single entity result set if the entity match an rql condition
       
   620     """
       
   621     if cls.condition:
       
   622         eid = rset[row or 0][col or 0]
       
   623         if 'U' in frozenset(split_expression(cls.condition)):
       
   624             rql = 'Any X WHERE X eid %%(x)s, U eid %%(u)s, %s' % cls.condition
       
   625         else:
       
   626             rql = 'Any X WHERE X eid %%(x)s, %s' % cls.condition
       
   627         try:
       
   628             return len(req.execute(rql, {'x': eid, 'u': req.user.eid}, 'x'))
       
   629         except Unauthorized:
       
   630             return 0
       
   631         
       
   632     return 1
       
   633 _rqlcondition_selector = deprecated_function(_rql_condition)
       
   634         
       
   635 @lltrace
       
   636 def but_etype(cls, req, rset, row=None, col=0, **kwargs):
       
   637     """restrict the searchstate_accept_one_selector to exclude entity's type
       
   638     refered by the .etype attribute
       
   639     """
       
   640     if rset.description[row or 0][col or 0] == cls.etype:
       
   641         return 0
       
   642     return 1
       
   643 but_etype_selector = deprecated_function(but_etype)
       
   644 
       
   645 @lltrace
       
   646 def etype_rtype_selector(cls, req, rset, row=None, col=0, **kwargs):
       
   647     """only check if the user has read access on the entity's type refered
       
   648     by the .etype attribute and on the relations's type refered by the
       
   649     .rtype attribute if set.
       
   650     """
       
   651     schema = cls.schema
       
   652     perm = getattr(cls, 'require_permission', 'read')
       
   653     if hasattr(cls, 'etype'):
       
   654         eschema = schema.eschema(cls.etype)
       
   655         if not (eschema.has_perm(req, perm) or eschema.has_local_role(perm)):
       
   656             return 0
       
   657     if hasattr(cls, 'rtype'):
       
   658         rschema = schema.rschema(cls.rtype)
       
   659         if not (rschema.has_perm(req, perm) or rschema.has_local_role(perm)):
       
   660             return 0
       
   661     return 1
       
   662 
       
   663 @lltrace
       
   664 def has_related_entities(cls, req, rset, row=None, col=0, **kwargs):
       
   665     return bool(rset.get_entity(row or 0, col or 0).related(cls.rtype, role(cls)))
       
   666 
       
   667 @lltrace
       
   668 def user_can_add_etype(cls, req, rset, row=None, col=0, **kwargs):
       
   669     """only check if the user has add access on the entity's type refered
       
   670     by the .etype attribute.
       
   671     """
       
   672     if not cls.schema.eschema(cls.etype).has_perm(req, 'add'):
       
   673         return 0
       
   674     return 1
       
   675 add_etype_selector = deprecated_function(user_can_add_etype)
       
   676 
       
   677 @lltrace
       
   678 def match_context_prop(cls, req, rset, row=None, col=0, context=None,
       
   679                        **kwargs):
       
   680     propval = req.property_value('%s.%s.context' % (cls.__registry__, cls.id))
       
   681     if not propval:
       
   682         propval = cls.context
       
   683     if context is not None and propval and context != propval:
       
   684         return 0
       
   685     return 1
       
   686 contextprop_selector = deprecated_function(match_context_prop)
       
   687 
       
   688 @lltrace
       
   689 def primary_view(cls, req, rset, row=None, col=0, view=None,
       
   690                           **kwargs):
       
   691     if view is not None and not view.is_primary():
       
   692         return 0
       
   693     return 1
       
   694 primaryview_selector = deprecated_function(primary_view)
       
   695 
       
   696 def appobject_selectable(registry, oid):
       
   697     """return a selector that will have a positive score if an object for the
       
   698     given registry and object id is selectable for the input context
       
   699     """
       
   700     @lltrace
       
   701     def selector(cls, req, rset, *args, **kwargs):
       
   702         try:
       
   703             cls.vreg.select_object(registry, oid, req, rset, *args, **kwargs)
       
   704             return 1
       
   705         except NoSelectableObject:
       
   706             return 0
       
   707     return selector
       
   708 
       
   709 
       
   710 
       
   711 # XXX DEPRECATED ##############################################################
       
   712 
       
   713 def nfentity_selector(cls, req, rset, row=None, col=0, **kwargs):
       
   714     return non_final_entity()(cls, req, rset, row, col)
       
   715 nfentity_selector = deprecated_function(nfentity_selector)
       
   716 
       
   717 def implement_interface(cls, req, rset, row=None, col=0, **kwargs):
       
   718     return implements(*cls.accepts_interfaces)(cls, req, rset, row, col)
       
   719 _interface_selector = deprecated_function(implement_interface)
       
   720 interface_selector = deprecated_function(implement_interface)
       
   721 implement_interface = deprecated_function(implement_interface, 'use implements')
       
   722 
       
   723 def accept_etype(cls, req, *args, **kwargs):
       
   724     """check etype presence in request form *and* accepts conformance"""
       
   725     return specified_etype_implements(*cls.accepts)(cls, req, *args)
       
   726 etype_form_selector = deprecated_function(accept_etype)
       
   727 accept_etype = deprecated_function(accept_etype, 'use specified_etype_implements')
       
   728 
       
   729 def searchstate_selector(cls, req, rset, row=None, col=0, **kwargs):
       
   730     return match_search_state(cls.search_states)(cls, req, rset, row, col)
       
   731 searchstate_selector = deprecated_function(searchstate_selector)
       
   732 
       
   733 def match_user_group(cls, req, rset=None, row=None, col=0, **kwargs):
       
   734     return match_user_groups(*cls.require_groups)(cls, req, rset, row, col, **kwargs)
       
   735 in_group_selector = deprecated_function(match_user_group)
       
   736 match_user_group = deprecated_function(match_user_group)
       
   737 
       
   738 def has_relation(cls, req, rset, row=None, col=0, **kwargs):
       
   739     return relation_possible(cls.rtype, role(cls), cls.etype,
       
   740                              getattr(cls, 'require_permission', 'read'))(cls, req, rset, row, col, **kwargs)
       
   741 has_relation = deprecated_function(has_relation)
       
   742 
       
   743 def one_has_relation(cls, req, rset, row=None, col=0, **kwargs):
       
   744     return relation_possible(cls.rtype, role(cls), cls.etype,
       
   745                              getattr(cls, 'require_permission', 'read',
       
   746                                      once_is_enough=True))(cls, req, rset, row, col, **kwargs)
       
   747 one_has_relation = deprecated_function(one_has_relation, 'use relation_possible selector')
       
   748 
       
   749 def accept_rset(cls, req, rset, row=None, col=0, **kwargs):
       
   750     """simply delegate to cls.accept_rset method"""
       
   751     return implements(*cls.accepts)(cls, req, rset, row=row, col=col)
       
   752 accept_rset_selector = deprecated_function(accept_rset)
       
   753 accept_rset = deprecated_function(accept_rset, 'use implements selector')
       
   754 
       
   755 accept = chainall(non_final_entity(), accept_rset, name='accept')
       
   756 accept_selector = deprecated_function(accept)
       
   757 accept = deprecated_function(accept, 'use implements selector')
       
   758 
       
   759 # compound selectors ##########################################################
       
   760 
       
   761 accept_one = deprecated_function(chainall(one_line_rset, accept,
       
   762                                           name='accept_one'))
       
   763 accept_one_selector = deprecated_function(accept_one)
       
   764 
       
   765 rql_condition = chainall(non_final_entity(), one_line_rset, _rql_condition,
       
   766                          name='rql_condition')
       
   767 rqlcondition_selector = deprecated_function(rql_condition)
       
   768 
       
   769 
       
   770 searchstate_accept = chainall(nonempty_rset, match_search_state, accept,
       
   771                               name='searchstate_accept')
       
   772 searchstate_accept_selector = deprecated_function(searchstate_accept)
       
   773 
       
   774 searchstate_accept_one = chainall(one_line_rset, match_search_state,
       
   775                                   accept, _rql_condition,
       
   776                                   name='searchstate_accept_one')
       
   777 searchstate_accept_one_selector = deprecated_function(searchstate_accept_one)
       
   778 
       
   779 searchstate_accept_one_but_etype = chainall(searchstate_accept_one, but_etype,
       
   780                                             name='searchstate_accept_one_but_etype')
       
   781 searchstate_accept_one_but_etype_selector = deprecated_function(
       
   782     searchstate_accept_one_but_etype)
       
   783 
       
   784 #req_form_params_selector = deprecated_function(match_form_params) # form_params
       
   785 #kwargs_selector = deprecated_function(match_kwargs) # expected_kwargs