merge 3.16.x fix in 3.17.x branch

authorPierre-Yves David <pierre-yves.david@logilab.fr>
changeset6ad000b91347
branchdefault
phasepublic
hiddenno
parent revision#64f24ecad177 merge with 3.15.x fix, #59a29405688c [ext/rest] add directive bookmark to rest (closes #2545595)
child revision#38e7de60ddc4 allow negative intervals for add_looping_task (closes #2818280), #8a55a6f89da1 allow negative intervals for add_looping_task (closes #2818280), #896bd5fad05c skeleton: add RPM spec template (closes #2800884), #49158d73b440 Use correct API for logging message, #b3409c1dc012 [LDAP] rename unittest ldapuser to ldapsource (prepares #2528116), #87856f0eaf6e typo in logging message, #deddc43788a1 Add support of LDAP groups and convert them to CWGroups (closes #2528116), #140af5f56623 [server/test] remove dependency on a pyro name server (closes #2801737)
files modified by this revision
.hgtags
__pkginfo__.py
debian/changelog
devtools/devctl.py
predicates.py
server/querier.py
server/ssplanner.py
server/test/unittest_querier.py
test/unittest_predicates.py
web/facet.py
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@logilab.fr>
# Date 1365425110 -7200
# Mon Apr 08 14:45:10 2013 +0200
# Node ID 6ad000b91347fb7780f3514aa19f2d7fa1ff8790
# Parent 59a29405688c4f71fe5ecf19da7d5ab9ed672052
# Parent 64f24ecad1777df46ae8c52b25f95592575cc536
merge 3.16.x fix in 3.17.x branch

diff --git a/.hgtags b/.hgtags
@@ -256,10 +256,12 @@
1  20ee573bd2379a00f29ff27bb88a8a3344d4cdfe cubicweb-debian-version-3.14.7-1
2  15fe07ff687238f8cc09d8e563a72981484085b3 cubicweb-version-3.14.8
3  81394043ad226942ac0019b8e1d4f7058d67a49f cubicweb-debian-version-3.14.8-1
4  9337812cef6b949eee89161190e0c3d68d7f32ea cubicweb-version-3.14.9
5  68c762adf2d5a2c338910ef1091df554370586f0 cubicweb-debian-version-3.14.9-1
6 +0ff798f80138ca8f50a59f42284380ce8f6232e8 cubicweb-version-3.14.10
7 +197bcd087c87cd3de9f21f5bf40bd6203c074f1f cubicweb-debian-version-3.14.10-1
8  783a5df54dc742e63c8a720b1582ff08366733bd cubicweb-version-3.15.1
9  fe5e60862b64f1beed2ccdf3a9c96502dfcd811b cubicweb-debian-version-3.15.1-1
10  2afc157ea9b2b92eccb0f2d704094e22ce8b5a05 cubicweb-version-3.15.2
11  9aa5553b26520ceb68539e7a32721b5cd5393e16 cubicweb-debian-version-3.15.2-1
12  0e012eb80990ca6f91aa9a8ad3324fbcf51435b1 cubicweb-version-3.15.3
@@ -276,9 +278,11 @@
13  4ef457479337396f63bf00c87cedcbb7cb5a6eee cubicweb-debian-version-3.15.8-1
14  8bfc0753f1daa37a6a268287dd2848931fca1f95 cubicweb-version-3.15.9
15  29fbc632a69667840294d7b38b0ca00e5f66ec19 cubicweb-debian-version-3.15.9-1
16  89bdb5444cd20213d5af03c2612ceb28340cb760 cubicweb-version-3.15.10
17  feca12e4a6188fbaae0cc48c6f8cc5f4202e1662 cubicweb-debian-version-3.15.10-1
18 +38c6a3ea8252e1a40452aad05e4aa25b43f66cd8 cubicweb-version-3.15.11
19 +09d65bc1f0253eacef4b480716b7c139ab9efc35 cubicweb-debian-version-3.15.11-1
20  6c7c2a02c9a0ca870accfc8ed1bb120e9c858d5d cubicweb-version-3.16.0
21  853237d1daf6710af94cc2ec8ee12aa7dba16934 cubicweb-debian-version-3.16.0-1
22  d95cbb7349f01b9e02e5da65d55a92582bbee6db cubicweb-version-3.16.1
23  84fbcdc8021c9c198fef3c6a9ad90c298ee12566 cubicweb-debian-version-3.16.1-1
diff --git a/debian/changelog b/debian/changelog
@@ -8,10 +8,16 @@
24 
25    * New upstream release
26 
27   -- Aurélien Campéas <aurelien.campeas@logilab.fr>  Wed, 23 Jan 2013 17:40:00 +0100
28 
29 +cubicweb (3.15.11-1) squeeze; urgency=low
30 +
31 +  * New upstream release
32 +
33 + -- Pierre-Yves David <pierre-yves.david@logilab.fr>  Mon, 08 Apr 2013 12:43:02 +0200
34 +
35  cubicweb (3.15.10-1) squeeze; urgency=low
36 
37    * New upstream release
38 
39   -- Aurélien Campéas <aurelien.campeas@logilab.fr>  Tue, 19 Mar 2013 16:56:00 +0100
@@ -83,10 +89,16 @@
40 
41    * new upstream release
42 
43   -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Thu, 12 Apr 2012 13:52:05 +0200
44 
45 +cubicweb (3.14.10-1) unstable; urgency=low
46 +
47 +  * new upstream release
48 +
49 + -- Pierre-Yves David <pierre-yves.david@logilab.fr>  Mon, 08 Apr 2013 12:18:20 +0200
50 +
51  cubicweb (3.14.9-1) unstable; urgency=low
52 
53    * new upstream release
54 
55   -- Pierre-Yves David <pierre-yves.david@logilab.fr>  Tue, 31 Jul 2012 16:16:28 +0200
diff --git a/devtools/devctl.py b/devtools/devctl.py
@@ -673,17 +673,17 @@
56 
57 
58  class ExamineLogCommand(Command):
59      """Examine a rql log file.
60 
61 -    will print out the following table
62 +    Will print out the following table
63 
64 -      total execution time || number of occurences || rql query
65 +      Percentage; Cumulative Time (clock); Cumulative Time (CPU); Occurences; Query
66 
67 -    sorted by descending total execution time
68 +    sorted by descending cumulative time (clock). Time are expressed in seconds.
69 
70 -    chances are the lines at the top are the ones that will bring the higher
71 +    Chances are the lines at the top are the ones that will bring the higher
72      benefit after optimisation. Start there.
73      """
74      arguments = 'rql.log'
75      name = 'exlog'
76      options = ()
diff --git a/predicates.py b/predicates.py
@@ -1,6 +1,6 @@
77 -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
78 +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
79  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
80  #
81  # This file is part of CubicWeb.
82  #
83  # CubicWeb is free software: you can redistribute it and/or modify it under the
@@ -477,15 +477,12 @@
84          self.registry = registry
85          self.regids = regids
86 
87      def __call__(self, cls, req, **kwargs):
88          for regid in self.regids:
89 -            try:
90 -                req.vreg[self.registry].select(regid, req, **kwargs)
91 +            if req.vreg[self.registry].select_or_none(regid, req, **kwargs) is not None:
92                  return self.selectable_score
93 -            except NoSelectableObject:
94 -                continue
95          return 0
96 
97 
98  class adaptable(appobject_selectable):
99      """Return 1 if another appobject is selectable using the same input context.
@@ -499,15 +496,34 @@
100      def __init__(self, *regids):
101          super(adaptable, self).__init__('adapters', *regids)
102 
103      def __call__(self, cls, req, **kwargs):
104          kwargs.setdefault('accept_none', False)
105 -        # being adaptable to an interface should takes precedence other is_instance('Any'),
106 -        # but not other explicit is_instance('SomeEntityType'), and:
107 +        score = super(adaptable, self).__call__(cls, req, **kwargs)
108 +        if score == 0 and kwargs.get('rset') and len(kwargs['rset']) > 1 and not 'row' in kwargs:
109 +            # on rset containing several entity types, each row may be
110 +            # individually adaptable, while the whole rset won't be if the
111 +            # same adapter can't be used for each type
112 +            for row in xrange(len(kwargs['rset'])):
113 +                kwargs.setdefault('col', 0)
114 +                _score = super(adaptable, self).__call__(cls, req, row=row, **kwargs)
115 +                if not _score:
116 +                    return 0
117 +                # adjust score per row as expected by default adjust_score
118 +                # implementation
119 +                score += self.adjust_score(_score)
120 +        else:
121 +            score = self.adjust_score(score)
122 +        return score
123 +
124 +    @staticmethod
125 +    def adjust_score(score):
126 +        # being adaptable to an interface should takes precedence other
127 +        # is_instance('Any'), but not other explicit
128 +        # is_instance('SomeEntityType'), and, for **a single entity**:
129          # * is_instance('Any') score is 1
130          # * is_instance('SomeEntityType') score is at least 2
131 -        score = super(adaptable, self).__call__(cls, req, **kwargs)
132          if score >= 2:
133              return score - 0.5
134          if score == 1:
135              return score + 0.5
136          return score
diff --git a/server/querier.py b/server/querier.py
@@ -61,12 +61,13 @@
137  # permission utilities ########################################################
138 
139  def check_no_password_selected(rqlst):
140      """check that Password entities are not selected"""
141      for solution in rqlst.solutions:
142 -        if 'Password' in solution.itervalues():
143 -            raise Unauthorized('Password selection is not allowed')
144 +        for var, etype in solution.iteritems():
145 +            if etype == 'Password':
146 +                raise Unauthorized('Password selection is not allowed (%s)' % var)
147 
148  def term_etype(session, term, solution, args):
149      """return the entity type for the given term (a VariableRef or a Constant
150      node)
151      """
diff --git a/server/ssplanner.py b/server/ssplanner.py
@@ -74,11 +74,16 @@
152      eidconsts = {}
153      neweids = session.transaction_data.get('neweids', ())
154      checkread = session.read_security
155      eschema = session.vreg.schema.eschema
156      for rel in rqlst.where.get_nodes(Relation):
157 -        if rel.r_type == 'eid' and not rel.neged(strict=True):
158 +        # only care for 'eid' relations ...
159 +        if (rel.r_type == 'eid'
160 +            # ... that are not part of a NOT clause ...
161 +            and not rel.neged(strict=True)
162 +            # ... and where eid is specified by '=' operator.
163 +            and rel.children[1].operator == '='):
164              lhs, rhs = rel.get_variable_parts()
165              if isinstance(rhs, Constant):
166                  eid = int(rhs.eval(plan.args))
167                  # check read permission here since it may not be done by
168                  # the generated select substep if not emited (eg nothing
diff --git a/server/test/unittest_querier.py b/server/test/unittest_querier.py
@@ -1560,7 +1560,30 @@
169              res = self.execute('Any X WHERE X has_text %(text)s', {'text': 'aff1'})
170              self.assertEqual(res.rows, [[aff1.eid]])
171              res = self.execute('Any X WHERE X has_text %(text)s', {'text': 'aff2'})
172              self.assertEqual(res.rows, [[aff2.eid]])
173 
174 +    def test_set_relations_eid(self):
175 +        req = self.request()
176 +        # create 3 email addresses
177 +        a1 = req.create_entity('EmailAddress', address=u'a1')
178 +        a2 = req.create_entity('EmailAddress', address=u'a2')
179 +        a3 = req.create_entity('EmailAddress', address=u'a3')
180 +        # SET relations using '>=' operator on eids
181 +        req.execute('SET U use_email A WHERE U login "admin", A eid >= %s' % a2.eid)
182 +        self.assertEqual(
183 +            [[a2.eid], [a3.eid]],
184 +            req.execute('Any A ORDERBY A WHERE U use_email A, U login "admin"').rows)
185 +        # DELETE
186 +        req.execute('DELETE U use_email A WHERE U login "admin", A eid > %s' % a2.eid)
187 +        self.assertEqual(
188 +            [[a2.eid]],
189 +            req.execute('Any A ORDERBY A WHERE U use_email A, U login "admin"').rows)
190 +        req.execute('DELETE U use_email A WHERE U login "admin"')
191 +        # SET relations using '<' operator on eids
192 +        req.execute('SET U use_email A WHERE U login "admin", A eid < %s' % a2.eid)
193 +        self.assertEqual(
194 +            [[a1.eid]],
195 +            req.execute('Any A ORDERBY A WHERE U use_email A, U login "admin"').rows)
196 +
197  if __name__ == '__main__':
198      unittest_main()
diff --git a/test/unittest_predicates.py b/test/unittest_predicates.py
@@ -1,6 +1,6 @@
199 -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
200 +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
201  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
202  #
203  # This file is part of CubicWeb.
204  #
205  # CubicWeb is free software: you can redistribute it and/or modify it under the
@@ -24,10 +24,11 @@
206  from cubicweb.devtools.testlib import CubicWebTC
207  from cubicweb.predicates import (is_instance, adaptable, match_kwargs, match_user_groups,
208                                  multi_lines_rset, score_entity, is_in_state,
209                                  rql_condition, relation_possible)
210  from cubicweb.selectors import on_transition # XXX on_transition is deprecated
211 +from cubicweb.view import EntityAdapter
212  from cubicweb.web import action
213 
214 
215 
216  class ImplementsSelectorTC(CubicWebTC):
@@ -334,8 +335,24 @@
217          selector = rql_condition('U login "admin"', user_condition=True)
218          self.assertEqual(selector(None, req), 1)
219          selector = rql_condition('U login "toto"', user_condition=True)
220          self.assertEqual(selector(None, req), 0)
221 
222 +
223 +class AdaptablePredicateTC(CubicWebTC):
224 +
225 +    def test_multiple_entity_types_rset(self):
226 +        class CWUserIWhatever(EntityAdapter):
227 +            __regid__ = 'IWhatever'
228 +            __select__ = is_instance('CWUser')
229 +        class CWGroupIWhatever(EntityAdapter):
230 +            __regid__ = 'IWhatever'
231 +            __select__ = is_instance('CWGroup')
232 +        with self.temporary_appobjects(CWUserIWhatever, CWGroupIWhatever):
233 +            req = self.request()
234 +            selector = adaptable('IWhatever')
235 +            rset = req.execute('Any X WHERE X is IN(CWGroup, CWUser)')
236 +            self.assertTrue(selector(None, req, rset=rset))
237 +
238  if __name__ == '__main__':
239      unittest_main()
240 
diff --git a/web/facet.py b/web/facet.py
@@ -64,11 +64,11 @@
241 
242  from rql import nodes, utils
243 
244  from cubicweb import Unauthorized
245  from cubicweb.schema import display_name
246 -from cubicweb.uilib import css_em_num_value
247 +from cubicweb.uilib import css_em_num_value, domid
248  from cubicweb.utils import make_uid
249  from cubicweb.predicates import match_context_prop, partial_relation_possible
250  from cubicweb.appobject import AppObject
251  from cubicweb.web import RequestError, htmlwidgets
252 
@@ -1447,11 +1447,11 @@
253      scrollbar_padding_factor = 4
254 
255      def _render(self):
256          w = self.w
257          title = xml_escape(self.facet.title)
258 -        facetid = make_uid(self.facet.__regid__)
259 +        facetid = domid(make_uid(self.facet.__regid__))
260          w(u'<div id="%s" class="facet">\n' % facetid)
261          cssclass = 'facetTitle'
262          if self.facet.allow_hide:
263              cssclass += ' hideFacetBody'
264          w(u'<div class="%s" cubicweb:facetName="%s">%s</div>\n' %