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