15 # |
15 # |
16 # You should have received a copy of the GNU Lesser General Public License along |
16 # You should have received a copy of the GNU Lesser General Public License along |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
18 """classes to define schemas for CubicWeb""" |
18 """classes to define schemas for CubicWeb""" |
19 |
19 |
20 from __future__ import print_function |
|
21 |
|
22 from functools import wraps |
20 from functools import wraps |
23 import re |
21 import re |
24 from os.path import join |
22 from os.path import join |
25 from hashlib import md5 |
23 from hashlib import md5 |
26 from logging import getLogger |
24 from logging import getLogger |
27 |
|
28 from six import PY2, text_type, string_types, add_metaclass |
|
29 from six.moves import range |
|
30 |
25 |
31 from logilab.common.decorators import cached, clear_cache, monkeypatch, cachedproperty |
26 from logilab.common.decorators import cached, clear_cache, monkeypatch, cachedproperty |
32 from logilab.common.logging_ext import set_log_methods |
27 from logilab.common.logging_ext import set_log_methods |
33 from logilab.common.textutils import splitstrip |
28 from logilab.common.textutils import splitstrip |
34 from logilab.common.graph import get_cycles |
29 from logilab.common.graph import get_cycles |
143 """normalize an rql expression to ease schema synchronization (avoid |
138 """normalize an rql expression to ease schema synchronization (avoid |
144 suppressing and reinserting an expression if only a space has been |
139 suppressing and reinserting an expression if only a space has been |
145 added/removed for instance) |
140 added/removed for instance) |
146 """ |
141 """ |
147 union = parse(u'Any 1 WHERE %s' % rqlstring).as_string() |
142 union = parse(u'Any 1 WHERE %s' % rqlstring).as_string() |
148 if PY2 and isinstance(union, str): |
|
149 union = union.decode('utf-8') |
|
150 return union.split(' WHERE ', 1)[1] |
143 return union.split(' WHERE ', 1)[1] |
151 |
144 |
152 |
145 |
153 def _check_valid_formula(rdef, formula_rqlst): |
146 def _check_valid_formula(rdef, formula_rqlst): |
154 """Check the formula is a valid RQL query with some restriction (no union, |
147 """Check the formula is a valid RQL query with some restriction (no union, |
216 :param mainvars: names of the variables being selected. |
209 :param mainvars: names of the variables being selected. |
217 |
210 |
218 """ |
211 """ |
219 self.eid = eid # eid of the entity representing this rql expression |
212 self.eid = eid # eid of the entity representing this rql expression |
220 assert mainvars, 'bad mainvars %s' % mainvars |
213 assert mainvars, 'bad mainvars %s' % mainvars |
221 if isinstance(mainvars, string_types): |
214 if isinstance(mainvars, str): |
222 mainvars = set(splitstrip(mainvars)) |
215 mainvars = set(splitstrip(mainvars)) |
223 elif not isinstance(mainvars, set): |
216 elif not isinstance(mainvars, set): |
224 mainvars = set(mainvars) |
217 mainvars = set(mainvars) |
225 self.mainvars = mainvars |
218 self.mainvars = mainvars |
226 self.expression = normalize_expression(expression) |
219 self.expression = normalize_expression(expression) |
577 form = '' |
570 form = '' |
578 if form: |
571 if form: |
579 key = key + '_' + form |
572 key = key + '_' + form |
580 # ensure unicode |
573 # ensure unicode |
581 if context is not None: |
574 if context is not None: |
582 return text_type(req.pgettext(context, key)) |
575 return req.pgettext(context, key) |
583 else: |
576 else: |
584 return text_type(req._(key)) |
577 return req._(key) |
585 |
578 |
586 |
579 |
587 def _override_method(cls, method_name=None, pass_original=False): |
580 def _override_method(cls, method_name=None, pass_original=False): |
588 """Override (or set) a method on `cls`.""" |
581 """Override (or set) a method on `cls`.""" |
589 def decorator(func): |
582 def decorator(func): |
625 :rtype: tuple |
618 :rtype: tuple |
626 :return: names of the groups with the given permission |
619 :return: names of the groups with the given permission |
627 """ |
620 """ |
628 assert action in self.ACTIONS, action |
621 assert action in self.ACTIONS, action |
629 try: |
622 try: |
630 return frozenset(g for g in self.permissions[action] if isinstance(g, string_types)) |
623 return frozenset(g for g in self.permissions[action] if isinstance(g, str)) |
631 except KeyError: |
624 except KeyError: |
632 return () |
625 return () |
633 |
626 |
634 |
627 |
635 @_override_method(PermissionMixIn) |
628 @_override_method(PermissionMixIn) |
644 :rtype: tuple |
637 :rtype: tuple |
645 :return: the rql expressions with the given permission |
638 :return: the rql expressions with the given permission |
646 """ |
639 """ |
647 assert action in self.ACTIONS, action |
640 assert action in self.ACTIONS, action |
648 try: |
641 try: |
649 return tuple(g for g in self.permissions[action] if not isinstance(g, string_types)) |
642 return tuple(g for g in self.permissions[action] if not isinstance(g, str)) |
650 except KeyError: |
643 except KeyError: |
651 return () |
644 return () |
652 |
645 |
653 |
646 |
654 @_override_method(PermissionMixIn, pass_original=True) |
647 @_override_method(PermissionMixIn, pass_original=True) |
1331 if not abstract: |
1324 if not abstract: |
1332 make_workflowable(cls) |
1325 make_workflowable(cls) |
1333 return cls |
1326 return cls |
1334 |
1327 |
1335 |
1328 |
1336 @add_metaclass(workflowable_definition) |
1329 class WorkflowableEntityType(ybo.EntityType, metaclass=workflowable_definition): |
1337 class WorkflowableEntityType(ybo.EntityType): |
|
1338 """Use this base class instead of :class:`EntityType` to have workflow |
1330 """Use this base class instead of :class:`EntityType` to have workflow |
1339 relations (i.e. `in_state`, `wf_info_for` and `custom_workflow`) on your |
1331 relations (i.e. `in_state`, `wf_info_for` and `custom_workflow`) on your |
1340 entity type. |
1332 entity type. |
1341 """ |
1333 """ |
1342 __abstract__ = True |
1334 __abstract__ = True |