12 from logilab.common import interface |
12 from logilab.common import interface |
13 from logilab.common.compat import all |
13 from logilab.common.compat import all |
14 from logilab.common.decorators import cached |
14 from logilab.common.decorators import cached |
15 from logilab.mtconverter import TransformData, TransformError, xml_escape |
15 from logilab.mtconverter import TransformData, TransformError, xml_escape |
16 |
16 |
|
17 from rql import parse |
17 from rql.utils import rqlvar_maker |
18 from rql.utils import rqlvar_maker |
18 |
19 |
19 from cubicweb import Unauthorized |
20 from cubicweb import Unauthorized |
20 from cubicweb.rset import ResultSet |
21 from cubicweb.rset import ResultSet |
21 from cubicweb.selectors import yes |
22 from cubicweb.selectors import yes |
22 from cubicweb.appobject import AppObject |
23 from cubicweb.appobject import AppObject |
23 from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint |
24 from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint |
|
25 from cubicweb.rqlrewrite import RQLRewriter |
24 |
26 |
25 from cubicweb.common.uilib import printable_value, soup2xhtml |
27 from cubicweb.common.uilib import printable_value, soup2xhtml |
26 from cubicweb.common.mixins import MI_REL_TRIGGERS |
28 from cubicweb.common.mixins import MI_REL_TRIGGERS |
27 from cubicweb.common.mttransforms import ENGINE |
29 from cubicweb.common.mttransforms import ENGINE |
28 |
30 |
614 # generic vocabulary methods ############################################## |
616 # generic vocabulary methods ############################################## |
615 |
617 |
616 def unrelated_rql(self, rtype, targettype, role, ordermethod=None, |
618 def unrelated_rql(self, rtype, targettype, role, ordermethod=None, |
617 vocabconstraints=True): |
619 vocabconstraints=True): |
618 """build a rql to fetch `targettype` entities unrelated to this entity |
620 """build a rql to fetch `targettype` entities unrelated to this entity |
619 using (rtype, role) relation |
621 using (rtype, role) relation. |
|
622 |
|
623 Consider relation permissions so that returned entities may be actually |
|
624 linked by `rtype`. |
620 """ |
625 """ |
621 ordermethod = ordermethod or 'fetch_unrelated_order' |
626 ordermethod = ordermethod or 'fetch_unrelated_order' |
622 if isinstance(rtype, basestring): |
627 if isinstance(rtype, basestring): |
623 rtype = self.req.vreg.schema.rschema(rtype) |
628 rtype = self.req.vreg.schema.rschema(rtype) |
624 if role == 'subject': |
629 if role == 'subject': |
627 else: |
632 else: |
628 searchedvar, evar = 'S', 'O' |
633 searchedvar, evar = 'S', 'O' |
629 objtype, subjtype = self.e_schema, targettype |
634 objtype, subjtype = self.e_schema, targettype |
630 if self.has_eid(): |
635 if self.has_eid(): |
631 restriction = ['NOT S %s O' % rtype, '%s eid %%(x)s' % evar] |
636 restriction = ['NOT S %s O' % rtype, '%s eid %%(x)s' % evar] |
|
637 args = {'x': self.eid} |
|
638 if role == 'subject': |
|
639 securitycheck_args = {'fromeid': self.eid} |
|
640 else: |
|
641 securitycheck_args = {'toeid': self.eid} |
632 else: |
642 else: |
633 restriction = [] |
643 restriction = [] |
|
644 args = {} |
|
645 securitycheck_args = {} |
|
646 insertsecurity = (rtype.has_local_role('add') and not |
|
647 rtype.has_perm(self.req, 'add', **securitycheck_args)) |
634 constraints = rtype.rproperty(subjtype, objtype, 'constraints') |
648 constraints = rtype.rproperty(subjtype, objtype, 'constraints') |
635 if vocabconstraints: |
649 if vocabconstraints: |
636 # RQLConstraint is a subclass for RQLVocabularyConstraint, so they |
650 # RQLConstraint is a subclass for RQLVocabularyConstraint, so they |
637 # will be included as well |
651 # will be included as well |
638 restriction += [cstr.restriction for cstr in constraints |
652 restriction += [cstr.restriction for cstr in constraints |
645 mainvar=searchedvar, ordermethod=ordermethod) |
659 mainvar=searchedvar, ordermethod=ordermethod) |
646 # ensure we have an order defined |
660 # ensure we have an order defined |
647 if not ' ORDERBY ' in rql: |
661 if not ' ORDERBY ' in rql: |
648 before, after = rql.split(' WHERE ', 1) |
662 before, after = rql.split(' WHERE ', 1) |
649 rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after) |
663 rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after) |
650 return rql |
664 if insertsecurity: |
|
665 rqlexprs = rtype.get_rqlexprs('add') |
|
666 rewriter = RQLRewriter(self.req) |
|
667 rqlst = self.req.vreg.parse(self.req, rql, args) |
|
668 for select in rqlst.children: |
|
669 rewriter.rewrite(select, [((searchedvar, searchedvar), rqlexprs)], |
|
670 select.solutions, args) |
|
671 rql = rqlst.as_string() |
|
672 return rql, args |
651 |
673 |
652 def unrelated(self, rtype, targettype, role='subject', limit=None, |
674 def unrelated(self, rtype, targettype, role='subject', limit=None, |
653 ordermethod=None): |
675 ordermethod=None): |
654 """return a result set of target type objects that may be related |
676 """return a result set of target type objects that may be related |
655 by a given relation, with self as subject or object |
677 by a given relation, with self as subject or object |
656 """ |
678 """ |
657 rql = self.unrelated_rql(rtype, targettype, role, ordermethod) |
679 try: |
|
680 rql, args = self.unrelated_rql(rtype, targettype, role, ordermethod) |
|
681 except Unauthorized: |
|
682 return self.req.empty_rset() |
658 if limit is not None: |
683 if limit is not None: |
659 before, after = rql.split(' WHERE ', 1) |
684 before, after = rql.split(' WHERE ', 1) |
660 rql = '%s LIMIT %s WHERE %s' % (before, limit, after) |
685 rql = '%s LIMIT %s WHERE %s' % (before, limit, after) |
661 if self.has_eid(): |
686 return self.req.execute(rql, args, tuple(args)) |
662 return self.req.execute(rql, {'x': self.eid}) |
|
663 return self.req.execute(rql) |
|
664 |
687 |
665 # relations cache handling ################################################ |
688 # relations cache handling ################################################ |
666 |
689 |
667 def relation_cached(self, rtype, role): |
690 def relation_cached(self, rtype, role): |
668 """return true if the given relation is already cached on the instance |
691 """return true if the given relation is already cached on the instance |
814 else: # if role == 'object': |
837 else: # if role == 'object': |
815 for entity in getattr(self, 'reverse_%s' % rschema.type): |
838 for entity in getattr(self, 'reverse_%s' % rschema.type): |
816 words += entity.get_words() |
839 words += entity.get_words() |
817 return words |
840 return words |
818 |
841 |
|
842 @deprecated('[3.2] see new form api') |
|
843 def vocabulary(self, rtype, role='subject', limit=None): |
|
844 """vocabulary functions must return a list of couples |
|
845 (label, eid) that will typically be used to fill the |
|
846 edition view's combobox. |
|
847 |
|
848 If `eid` is None in one of these couples, it should be |
|
849 interpreted as a separator in case vocabulary results are grouped |
|
850 """ |
|
851 from logilab.common.testlib import mock_object |
|
852 form = self.vreg.select('forms', 'edition', self.req, entity=self) |
|
853 field = mock_object(name=rtype, role=role) |
|
854 return form.form_field_vocabulary(field, limit) |
|
855 |
819 |
856 |
820 # attribute and relation descriptors ########################################## |
857 # attribute and relation descriptors ########################################## |
821 |
858 |
822 class Attribute(object): |
859 class Attribute(object): |
823 """descriptor that controls schema attribute access""" |
860 """descriptor that controls schema attribute access""" |