751 return rql |
752 return rql |
752 |
753 |
753 # generic vocabulary methods ############################################## |
754 # generic vocabulary methods ############################################## |
754 |
755 |
755 def cw_unrelated_rql(self, rtype, targettype, role, ordermethod=None, |
756 def cw_unrelated_rql(self, rtype, targettype, role, ordermethod=None, |
756 vocabconstraints=True): |
757 vocabconstraints=True): |
757 """build a rql to fetch `targettype` entities unrelated to this entity |
758 """build a rql to fetch `targettype` entities unrelated to this entity |
758 using (rtype, role) relation. |
759 using (rtype, role) relation. |
759 |
760 |
760 Consider relation permissions so that returned entities may be actually |
761 Consider relation permissions so that returned entities may be actually |
761 linked by `rtype`. |
762 linked by `rtype`. |
762 """ |
763 """ |
763 ordermethod = ordermethod or 'fetch_unrelated_order' |
764 ordermethod = ordermethod or 'fetch_unrelated_order' |
764 if isinstance(rtype, basestring): |
765 if isinstance(rtype, basestring): |
765 rtype = self._cw.vreg.schema.rschema(rtype) |
766 rtype = self._cw.vreg.schema.rschema(rtype) |
|
767 rdef = rtype.role_rdef(self.e_schema, targettype, role) |
|
768 rewriter = RQLRewriter(self._cw) |
|
769 # initialize some variables according to the `role` of `self` in the |
|
770 # relation: |
|
771 # * variable for myself (`evar`) and searched entities (`searchvedvar`) |
|
772 # * entity type of the subject (`subjtype`) and of the object |
|
773 # (`objtype`) of the relation |
766 if role == 'subject': |
774 if role == 'subject': |
767 evar, searchedvar = 'S', 'O' |
775 evar, searchedvar = 'S', 'O' |
768 subjtype, objtype = self.e_schema, targettype |
776 subjtype, objtype = self.e_schema, targettype |
769 else: |
777 else: |
770 searchedvar, evar = 'S', 'O' |
778 searchedvar, evar = 'S', 'O' |
771 objtype, subjtype = self.e_schema, targettype |
779 objtype, subjtype = self.e_schema, targettype |
772 rdef = rtype.role_rdef(self.e_schema, targettype, role) |
780 # initialize some variables according to `self` existance |
773 if self.has_eid(): |
781 if self.has_eid(): |
774 restriction = ['NOT S %s O' % rtype] |
782 restriction = ['NOT S %s O' % rtype] |
775 if rdef.role_cardinality(role) not in '?1': |
783 if rdef.role_cardinality(role) not in '?1': |
776 # if cardinality in '1?', don't add restriction on eid |
784 # if cardinality in '1?', don't add restriction on eid |
777 restriction.append('%s eid %%(x)s' % evar) |
785 restriction.append('%s eid %%(x)s' % evar) |
778 args = {'x': self.eid} |
786 args = {'x': self.eid} |
779 if role == 'subject': |
787 if role == 'subject': |
780 securitycheck_args = {'fromeid': self.eid} |
788 sec_check_args = {'fromeid': self.eid} |
781 else: |
789 else: |
782 securitycheck_args = {'toeid': self.eid} |
790 sec_check_args = {'toeid': self.eid} |
|
791 existant = None # instead of 'SO', improve perfs |
783 else: |
792 else: |
784 if rdef.role_cardinality(role) in '?1': |
793 if rdef.role_cardinality(role) in '?1': |
785 restriction = ['NOT S %s O' % rtype] |
794 restriction = ['NOT S %s O' % rtype] |
786 else: |
795 else: |
787 restriction = [] |
796 restriction = [] |
788 args = {} |
797 args = {} |
789 securitycheck_args = {} |
798 sec_check_args = {} |
790 insertsecurity = (rdef.has_local_role('add') and not |
799 existant = searchedvar |
791 rdef.has_perm(self._cw, 'add', **securitycheck_args)) |
800 # retreive entity class for targettype to compute base rql |
792 # XXX consider constraint.mainvars to check if constraint apply |
801 etypecls = self._cw.vreg['etypes'].etype_class(targettype) |
|
802 rql = etypecls.fetch_rql(self._cw.user, restriction, |
|
803 mainvar=searchedvar, ordermethod=ordermethod) |
|
804 select = self._cw.vreg.parse(self._cw, rql, args).children[0] |
|
805 # insert RQL expressions for schema constraints into the rql syntax tree |
793 if vocabconstraints: |
806 if vocabconstraints: |
794 # RQLConstraint is a subclass for RQLVocabularyConstraint, so they |
807 # RQLConstraint is a subclass for RQLVocabularyConstraint, so they |
795 # will be included as well |
808 # will be included as well |
796 cstrcls = RQLVocabularyConstraint |
809 cstrcls = RQLVocabularyConstraint |
797 else: |
810 else: |
798 cstrcls = RQLConstraint |
811 cstrcls = RQLConstraint |
799 for cstr in rdef.constraints: |
812 for cstr in rdef.constraints: |
800 if isinstance(cstr, RQLVocabularyConstraint) and searchedvar in cstr.mainvars: |
813 # consider constraint.mainvars to check if constraint apply |
|
814 if isinstance(cstr, cstrcls) and searchedvar in cstr.mainvars: |
801 if not self.has_eid() and evar in cstr.mainvars: |
815 if not self.has_eid() and evar in cstr.mainvars: |
802 continue |
816 continue |
803 restriction.append(cstr.expression) |
817 # compute a varmap suitable to RQLRewriter.rewrite argument |
804 etypecls = self._cw.vreg['etypes'].etype_class(targettype) |
818 varmap = dict((v, v) for v in 'SO' if v in select.defined_vars |
805 rql = etypecls.fetch_rql(self._cw.user, restriction, |
819 and v in cstr.mainvars) |
806 mainvar=searchedvar, ordermethod=ordermethod) |
820 # rewrite constraint by constraint since we want a AND between |
|
821 # expressions. |
|
822 rewriter.rewrite(select, [(varmap, (cstr,))], select.solutions, |
|
823 args, existant) |
|
824 # insert security RQL expressions granting the permission to 'add' the |
|
825 # relation into the rql syntax tree, if necessary |
|
826 rqlexprs = rdef.get_rqlexprs('add') |
|
827 if rqlexprs and not rdef.has_perm(self._cw, 'add', **sec_check_args): |
|
828 # compute a varmap suitable to RQLRewriter.rewrite argument |
|
829 varmap = dict((v, v) for v in 'SO' if v in select.defined_vars) |
|
830 # rewrite all expressions at once since we want a OR between them. |
|
831 rewriter.rewrite(select, [(varmap, rqlexprs)], select.solutions, |
|
832 args, existant) |
807 # ensure we have an order defined |
833 # ensure we have an order defined |
808 if not ' ORDERBY ' in rql: |
834 if not select.orderby: |
809 before, after = rql.split(' WHERE ', 1) |
835 select.add_sort_var(select.defined_vars[searchedvar]) |
810 rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after) |
836 # we're done, turn the rql syntax tree as a string |
811 if insertsecurity: |
837 rql = select.as_string() |
812 rqlexprs = rdef.get_rqlexprs('add') |
|
813 rewriter = RQLRewriter(self._cw) |
|
814 rqlst = self._cw.vreg.parse(self._cw, rql, args) |
|
815 if not self.has_eid(): |
|
816 existant = searchedvar |
|
817 else: |
|
818 existant = None # instead of 'SO', improve perfs |
|
819 for select in rqlst.children: |
|
820 varmap = {} |
|
821 for var in 'SO': |
|
822 if var in select.defined_vars: |
|
823 varmap[var] = var |
|
824 rewriter.rewrite(select, [(varmap, rqlexprs)], |
|
825 select.solutions, args, existant) |
|
826 rql = rqlst.as_string() |
|
827 return rql, args |
838 return rql, args |
828 |
839 |
829 def unrelated(self, rtype, targettype, role='subject', limit=None, |
840 def unrelated(self, rtype, targettype, role='subject', limit=None, |
830 ordermethod=None): # XXX .cw_unrelated |
841 ordermethod=None): # XXX .cw_unrelated |
831 """return a result set of target type objects that may be related |
842 """return a result set of target type objects that may be related |