1 # copyright 2010-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
1 # copyright 2010-2015 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
3 # |
3 # |
4 # This file is part of CubicWeb. |
4 # This file is part of CubicWeb. |
5 # |
5 # |
6 # CubicWeb is free software: you can redistribute it and/or modify it under the |
6 # CubicWeb is free software: you can redistribute it and/or modify it under the |
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 """some basic entity adapter implementations, for interfaces used in the |
18 """some basic entity adapter implementations, for interfaces used in the |
19 framework itself. |
19 framework itself. |
20 """ |
20 """ |
21 |
|
22 __docformat__ = "restructuredtext en" |
|
23 from cubicweb import _ |
21 from cubicweb import _ |
24 |
22 |
25 from itertools import chain |
23 from itertools import chain |
26 from warnings import warn |
|
27 from hashlib import md5 |
24 from hashlib import md5 |
28 |
25 |
29 from logilab.mtconverter import TransformError |
26 from logilab.mtconverter import TransformError |
30 from logilab.common.decorators import cached |
27 from logilab.common.decorators import cached |
31 |
28 |
32 from cubicweb import ValidationError, view, ViolatedConstraint |
29 from cubicweb import ValidationError, view, ViolatedConstraint, UniqueTogetherError |
33 from cubicweb.schema import CONSTRAINTS |
|
34 from cubicweb.predicates import is_instance, relation_possible, match_exception |
30 from cubicweb.predicates import is_instance, relation_possible, match_exception |
35 |
31 |
36 |
32 |
37 class IEmailableAdapter(view.EntityAdapter): |
33 class IEmailableAdapter(view.EntityAdapter): |
38 __regid__ = 'IEmailable' |
34 __regid__ = 'IEmailable' |
61 build email bodies. |
57 build email bodies. |
62 |
58 |
63 NOTE: the dictionary keys should match the list returned by the |
59 NOTE: the dictionary keys should match the list returned by the |
64 `allowed_massmail_keys` method. |
60 `allowed_massmail_keys` method. |
65 """ |
61 """ |
66 return dict( (attr, getattr(self.entity, attr)) |
62 return dict((attr, getattr(self.entity, attr)) |
67 for attr in self.allowed_massmail_keys() ) |
63 for attr in self.allowed_massmail_keys()) |
68 |
64 |
69 |
65 |
70 class INotifiableAdapter(view.EntityAdapter): |
66 class INotifiableAdapter(view.EntityAdapter): |
71 __regid__ = 'INotifiable' |
67 __regid__ = 'INotifiable' |
72 __select__ = is_instance('Any') |
68 __select__ = is_instance('Any') |
154 words.setdefault(weight, []).extend(tokenize(value)) |
150 words.setdefault(weight, []).extend(tokenize(value)) |
155 for rschema, role in entity.e_schema.fulltext_relations(): |
151 for rschema, role in entity.e_schema.fulltext_relations(): |
156 if role == 'subject': |
152 if role == 'subject': |
157 for entity_ in getattr(entity, rschema.type): |
153 for entity_ in getattr(entity, rschema.type): |
158 merge_weight_dict(words, entity_.cw_adapt_to('IFTIndexable').get_words()) |
154 merge_weight_dict(words, entity_.cw_adapt_to('IFTIndexable').get_words()) |
159 else: # if role == 'object': |
155 else: # if role == 'object': |
160 for entity_ in getattr(entity, 'reverse_%s' % rschema.type): |
156 for entity_ in getattr(entity, 'reverse_%s' % rschema.type): |
161 merge_weight_dict(words, entity_.cw_adapt_to('IFTIndexable').get_words()) |
157 merge_weight_dict(words, entity_.cw_adapt_to('IFTIndexable').get_words()) |
162 return words |
158 return words |
163 |
159 |
|
160 |
164 def merge_weight_dict(maindict, newdict): |
161 def merge_weight_dict(maindict, newdict): |
165 for weight, words in newdict.items(): |
162 for weight, words in newdict.items(): |
166 maindict.setdefault(weight, []).extend(words) |
163 maindict.setdefault(weight, []).extend(words) |
|
164 |
167 |
165 |
168 class IDownloadableAdapter(view.EntityAdapter): |
166 class IDownloadableAdapter(view.EntityAdapter): |
169 """interface for downloadable entities""" |
167 """interface for downloadable entities""" |
170 __regid__ = 'IDownloadable' |
168 __regid__ = 'IDownloadable' |
171 __abstract__ = True |
169 __abstract__ = True |
172 |
170 |
173 def download_url(self, **kwargs): # XXX not really part of this interface |
171 def download_url(self, **kwargs): # XXX not really part of this interface |
174 """return a URL to download entity's content |
172 """return a URL to download entity's content |
175 |
173 |
176 It should be a unicode object containing url-encoded ASCII.""" |
174 It should be a unicode object containing url-encoded ASCII. |
|
175 """ |
177 raise NotImplementedError |
176 raise NotImplementedError |
178 |
177 |
179 def download_content_type(self): |
178 def download_content_type(self): |
180 """return MIME type (unicode) of the downloadable content""" |
179 """return MIME type (unicode) of the downloadable content""" |
181 raise NotImplementedError |
180 raise NotImplementedError |
189 raise NotImplementedError |
188 raise NotImplementedError |
190 |
189 |
191 def download_data(self): |
190 def download_data(self): |
192 """return actual data (bytes) of the downloadable content""" |
191 """return actual data (bytes) of the downloadable content""" |
193 raise NotImplementedError |
192 raise NotImplementedError |
|
193 |
194 |
194 |
195 # XXX should propose to use two different relations for children/parent |
195 # XXX should propose to use two different relations for children/parent |
196 class ITreeAdapter(view.EntityAdapter): |
196 class ITreeAdapter(view.EntityAdapter): |
197 """This adapter provides a tree interface. |
197 """This adapter provides a tree interface. |
198 |
198 |
337 break |
337 break |
338 path.append(entity.eid) |
338 path.append(entity.eid) |
339 try: |
339 try: |
340 # check we are not jumping to another tree |
340 # check we are not jumping to another tree |
341 if (adapter.tree_relation != self.tree_relation or |
341 if (adapter.tree_relation != self.tree_relation or |
342 adapter.child_role != self.child_role): |
342 adapter.child_role != self.child_role): |
343 break |
343 break |
344 entity = adapter.parent() |
344 entity = adapter.parent() |
345 adapter = entity.cw_adapt_to('ITree') |
345 adapter = entity.cw_adapt_to('ITree') |
346 except AttributeError: |
346 except AttributeError: |
347 break |
347 break |
406 |
405 |
407 class IUserFriendlyCheckConstraint(IUserFriendlyError): |
406 class IUserFriendlyCheckConstraint(IUserFriendlyError): |
408 __select__ = match_exception(ViolatedConstraint) |
407 __select__ = match_exception(ViolatedConstraint) |
409 |
408 |
410 def raise_user_exception(self): |
409 def raise_user_exception(self): |
411 _ = self._cw._ |
|
412 cstrname = self.exc.cstrname |
410 cstrname = self.exc.cstrname |
413 eschema = self.entity.e_schema |
411 eschema = self.entity.e_schema |
414 for rschema, attrschema in eschema.attribute_definitions(): |
412 for rschema, attrschema in eschema.attribute_definitions(): |
415 rdef = rschema.rdef(eschema, attrschema) |
413 rdef = rschema.rdef(eschema, attrschema) |
416 for constraint in rdef.constraints: |
414 for constraint in rdef.constraints: |
417 if cstrname == 'cstr' + md5((eschema.type + rschema.type + constraint.type() + (constraint.serialize() or '')).encode('ascii')).hexdigest(): |
415 if cstrname == 'cstr' + md5( |
|
416 (eschema.type + rschema.type + constraint.type() + |
|
417 (constraint.serialize() or '')).encode('ascii')).hexdigest(): |
418 break |
418 break |
419 else: |
419 else: |
420 continue |
420 continue |
421 break |
421 break |
422 else: |
422 else: |