26 |
26 |
27 from logilab.mtconverter import TransformError |
27 from logilab.mtconverter import TransformError |
28 from logilab.common.decorators import cached |
28 from logilab.common.decorators import cached |
29 |
29 |
30 from cubicweb import ValidationError, view |
30 from cubicweb import ValidationError, view |
31 from cubicweb.predicates import (implements, is_instance, relation_possible, |
31 from cubicweb.predicates import is_instance, relation_possible, match_exception |
32 match_exception) |
|
33 from cubicweb.interfaces import IDownloadable, ITree |
|
34 |
32 |
35 |
33 |
36 class IEmailableAdapter(view.EntityAdapter): |
34 class IEmailableAdapter(view.EntityAdapter): |
37 __regid__ = 'IEmailable' |
35 __regid__ = 'IEmailable' |
38 __select__ = relation_possible('primary_email') | relation_possible('use_email') |
36 __select__ = relation_possible('primary_email') | relation_possible('use_email') |
65 return dict( (attr, getattr(self.entity, attr)) |
63 return dict( (attr, getattr(self.entity, attr)) |
66 for attr in self.allowed_massmail_keys() ) |
64 for attr in self.allowed_massmail_keys() ) |
67 |
65 |
68 |
66 |
69 class INotifiableAdapter(view.EntityAdapter): |
67 class INotifiableAdapter(view.EntityAdapter): |
70 __needs_bw_compat__ = True |
|
71 __regid__ = 'INotifiable' |
68 __regid__ = 'INotifiable' |
72 __select__ = is_instance('Any') |
69 __select__ = is_instance('Any') |
73 |
70 |
74 @view.implements_adapter_compat('INotifiableAdapter') |
|
75 def notification_references(self, view): |
71 def notification_references(self, view): |
76 """used to control References field of email send on notification |
72 """used to control References field of email send on notification |
77 for this entity. `view` is the notification view. |
73 for this entity. `view` is the notification view. |
78 |
74 |
79 Should return a list of eids which can be used to generate message |
75 Should return a list of eids which can be used to generate message |
165 for weight, words in newdict.iteritems(): |
161 for weight, words in newdict.iteritems(): |
166 maindict.setdefault(weight, []).extend(words) |
162 maindict.setdefault(weight, []).extend(words) |
167 |
163 |
168 class IDownloadableAdapter(view.EntityAdapter): |
164 class IDownloadableAdapter(view.EntityAdapter): |
169 """interface for downloadable entities""" |
165 """interface for downloadable entities""" |
170 __needs_bw_compat__ = True |
|
171 __regid__ = 'IDownloadable' |
166 __regid__ = 'IDownloadable' |
172 __select__ = implements(IDownloadable, warn=False) # XXX for bw compat, else should be abstract |
167 __abstract__ = True |
173 |
168 |
174 @view.implements_adapter_compat('IDownloadable') |
|
175 def download_url(self, **kwargs): # XXX not really part of this interface |
169 def download_url(self, **kwargs): # XXX not really part of this interface |
176 """return an url to download entity's content""" |
170 """return an url to download entity's content""" |
177 raise NotImplementedError |
171 raise NotImplementedError |
178 @view.implements_adapter_compat('IDownloadable') |
172 |
179 def download_content_type(self): |
173 def download_content_type(self): |
180 """return MIME type of the downloadable content""" |
174 """return MIME type of the downloadable content""" |
181 raise NotImplementedError |
175 raise NotImplementedError |
182 @view.implements_adapter_compat('IDownloadable') |
176 |
183 def download_encoding(self): |
177 def download_encoding(self): |
184 """return encoding of the downloadable content""" |
178 """return encoding of the downloadable content""" |
185 raise NotImplementedError |
179 raise NotImplementedError |
186 @view.implements_adapter_compat('IDownloadable') |
180 |
187 def download_file_name(self): |
181 def download_file_name(self): |
188 """return file name of the downloadable content""" |
182 """return file name of the downloadable content""" |
189 raise NotImplementedError |
183 raise NotImplementedError |
190 @view.implements_adapter_compat('IDownloadable') |
184 |
191 def download_data(self): |
185 def download_data(self): |
192 """return actual data of the downloadable content""" |
186 """return actual data of the downloadable content""" |
193 raise NotImplementedError |
187 raise NotImplementedError |
194 |
188 |
195 # XXX should propose to use two different relations for children/parent |
189 # XXX should propose to use two different relations for children/parent |
217 .. automethod: different_type_children |
211 .. automethod: different_type_children |
218 .. automethod: same_type_children |
212 .. automethod: same_type_children |
219 .. automethod: children_rql |
213 .. automethod: children_rql |
220 .. automethod: path |
214 .. automethod: path |
221 """ |
215 """ |
222 __needs_bw_compat__ = True |
|
223 __regid__ = 'ITree' |
216 __regid__ = 'ITree' |
224 __select__ = implements(ITree, warn=False) # XXX for bw compat, else should be abstract |
217 __abstract__ = True |
225 |
218 |
226 child_role = 'subject' |
219 child_role = 'subject' |
227 parent_role = 'object' |
220 parent_role = 'object' |
228 |
221 |
229 @property |
|
230 def tree_relation(self): |
|
231 warn('[3.9] tree_attribute is deprecated, define tree_relation on a custom ' |
|
232 'ITree for %s instead' % (self.entity.__class__), |
|
233 DeprecationWarning) |
|
234 return self.entity.tree_attribute |
|
235 |
|
236 # XXX should be removed from the public interface |
|
237 @view.implements_adapter_compat('ITree') |
|
238 def children_rql(self): |
222 def children_rql(self): |
239 """Returns RQL to get the children of the entity.""" |
223 """Returns RQL to get the children of the entity.""" |
240 return self.entity.cw_related_rql(self.tree_relation, self.parent_role) |
224 return self.entity.cw_related_rql(self.tree_relation, self.parent_role) |
241 |
225 |
242 @view.implements_adapter_compat('ITree') |
|
243 def different_type_children(self, entities=True): |
226 def different_type_children(self, entities=True): |
244 """Return children entities of different type as this entity. |
227 """Return children entities of different type as this entity. |
245 |
228 |
246 According to the `entities` parameter, return entity objects or the |
229 According to the `entities` parameter, return entity objects or the |
247 equivalent result set. |
230 equivalent result set. |
251 eschema = self.entity.e_schema |
234 eschema = self.entity.e_schema |
252 if entities: |
235 if entities: |
253 return [e for e in res if e.e_schema != eschema] |
236 return [e for e in res if e.e_schema != eschema] |
254 return res.filtered_rset(lambda x: x.e_schema != eschema, self.entity.cw_col) |
237 return res.filtered_rset(lambda x: x.e_schema != eschema, self.entity.cw_col) |
255 |
238 |
256 @view.implements_adapter_compat('ITree') |
|
257 def same_type_children(self, entities=True): |
239 def same_type_children(self, entities=True): |
258 """Return children entities of the same type as this entity. |
240 """Return children entities of the same type as this entity. |
259 |
241 |
260 According to the `entities` parameter, return entity objects or the |
242 According to the `entities` parameter, return entity objects or the |
261 equivalent result set. |
243 equivalent result set. |
265 eschema = self.entity.e_schema |
247 eschema = self.entity.e_schema |
266 if entities: |
248 if entities: |
267 return [e for e in res if e.e_schema == eschema] |
249 return [e for e in res if e.e_schema == eschema] |
268 return res.filtered_rset(lambda x: x.e_schema is eschema, self.entity.cw_col) |
250 return res.filtered_rset(lambda x: x.e_schema is eschema, self.entity.cw_col) |
269 |
251 |
270 @view.implements_adapter_compat('ITree') |
|
271 def is_leaf(self): |
252 def is_leaf(self): |
272 """Returns True if the entity does not have any children.""" |
253 """Returns True if the entity does not have any children.""" |
273 return len(self.children()) == 0 |
254 return len(self.children()) == 0 |
274 |
255 |
275 @view.implements_adapter_compat('ITree') |
|
276 def is_root(self): |
256 def is_root(self): |
277 """Returns true if the entity is root of the tree (e.g. has no parent). |
257 """Returns true if the entity is root of the tree (e.g. has no parent). |
278 """ |
258 """ |
279 return self.parent() is None |
259 return self.parent() is None |
280 |
260 |
281 @view.implements_adapter_compat('ITree') |
|
282 def root(self): |
261 def root(self): |
283 """Return the root entity of the tree.""" |
262 """Return the root entity of the tree.""" |
284 return self._cw.entity_from_eid(self.path()[0]) |
263 return self._cw.entity_from_eid(self.path()[0]) |
285 |
264 |
286 @view.implements_adapter_compat('ITree') |
|
287 def parent(self): |
265 def parent(self): |
288 """Returns the parent entity if any, else None (e.g. if we are on the |
266 """Returns the parent entity if any, else None (e.g. if we are on the |
289 root). |
267 root). |
290 """ |
268 """ |
291 try: |
269 try: |
292 return self.entity.related(self.tree_relation, self.child_role, |
270 return self.entity.related(self.tree_relation, self.child_role, |
293 entities=True)[0] |
271 entities=True)[0] |
294 except (KeyError, IndexError): |
272 except (KeyError, IndexError): |
295 return None |
273 return None |
296 |
274 |
297 @view.implements_adapter_compat('ITree') |
|
298 def children(self, entities=True, sametype=False): |
275 def children(self, entities=True, sametype=False): |
299 """Return children entities. |
276 """Return children entities. |
300 |
277 |
301 According to the `entities` parameter, return entity objects or the |
278 According to the `entities` parameter, return entity objects or the |
302 equivalent result set. |
279 equivalent result set. |
305 return self.same_type_children(entities) |
282 return self.same_type_children(entities) |
306 else: |
283 else: |
307 return self.entity.related(self.tree_relation, self.parent_role, |
284 return self.entity.related(self.tree_relation, self.parent_role, |
308 entities=entities) |
285 entities=entities) |
309 |
286 |
310 @view.implements_adapter_compat('ITree') |
|
311 def iterparents(self, strict=True): |
287 def iterparents(self, strict=True): |
312 """Return an iterator on the parents of the entity.""" |
288 """Return an iterator on the parents of the entity.""" |
313 def _uptoroot(self): |
289 def _uptoroot(self): |
314 curr = self |
290 curr = self |
315 while True: |
291 while True: |
320 curr = curr.cw_adapt_to('ITree') |
296 curr = curr.cw_adapt_to('ITree') |
321 if not strict: |
297 if not strict: |
322 return chain([self.entity], _uptoroot(self)) |
298 return chain([self.entity], _uptoroot(self)) |
323 return _uptoroot(self) |
299 return _uptoroot(self) |
324 |
300 |
325 @view.implements_adapter_compat('ITree') |
|
326 def iterchildren(self, _done=None): |
301 def iterchildren(self, _done=None): |
327 """Return an iterator over the item's children.""" |
302 """Return an iterator over the item's children.""" |
328 if _done is None: |
303 if _done is None: |
329 _done = set() |
304 _done = set() |
330 for child in self.children(): |
305 for child in self.children(): |
332 self.error('loop in %s tree: %s', child.cw_etype.lower(), child) |
307 self.error('loop in %s tree: %s', child.cw_etype.lower(), child) |
333 continue |
308 continue |
334 yield child |
309 yield child |
335 _done.add(child.eid) |
310 _done.add(child.eid) |
336 |
311 |
337 @view.implements_adapter_compat('ITree') |
|
338 def prefixiter(self, _done=None): |
312 def prefixiter(self, _done=None): |
339 """Return an iterator over the item's descendants in a prefixed order.""" |
313 """Return an iterator over the item's descendants in a prefixed order.""" |
340 if _done is None: |
314 if _done is None: |
341 _done = set() |
315 _done = set() |
342 if self.entity.eid in _done: |
316 if self.entity.eid in _done: |