13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
14 # details. |
14 # details. |
15 # |
15 # |
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 """base application's entities class implementation: `AnyEntity` |
18 """base application's entities class implementation: `AnyEntity`""" |
19 |
19 |
20 """ |
|
21 __docformat__ = "restructuredtext en" |
20 __docformat__ = "restructuredtext en" |
22 |
21 |
23 from warnings import warn |
22 from warnings import warn |
24 |
23 |
25 from logilab.common.deprecation import deprecated |
24 from logilab.common.deprecation import deprecated |
26 from logilab.common.decorators import cached |
25 from logilab.common.decorators import cached |
27 |
26 |
28 from cubicweb import Unauthorized, typed_eid |
27 from cubicweb import Unauthorized, typed_eid |
29 from cubicweb.entity import Entity |
28 from cubicweb.entity import Entity |
30 |
29 |
31 from cubicweb.interfaces import IBreadCrumbs, IFeed |
|
32 |
|
33 |
30 |
34 class AnyEntity(Entity): |
31 class AnyEntity(Entity): |
35 """an entity instance has e_schema automagically set on the class and |
32 """an entity instance has e_schema automagically set on the class and |
36 instances have access to their issuing cursor |
33 instances have access to their issuing cursor |
37 """ |
34 """ |
38 __regid__ = 'Any' |
35 __regid__ = 'Any' |
39 __implements__ = (IBreadCrumbs, IFeed) |
36 __implements__ = () |
40 |
|
41 fetch_attrs = ('modification_date',) |
|
42 @classmethod |
|
43 def fetch_order(cls, attr, var): |
|
44 """class method used to control sort order when multiple entities of |
|
45 this type are fetched |
|
46 """ |
|
47 return cls.fetch_unrelated_order(attr, var) |
|
48 |
|
49 @classmethod |
|
50 def fetch_unrelated_order(cls, attr, var): |
|
51 """class method used to control sort order when multiple entities of |
|
52 this type are fetched to use in edition (eg propose them to create a |
|
53 new relation on an edited entity). |
|
54 """ |
|
55 if attr == 'modification_date': |
|
56 return '%s DESC' % var |
|
57 return None |
|
58 |
37 |
59 # meta data api ########################################################### |
38 # meta data api ########################################################### |
60 |
39 |
61 def dc_title(self): |
40 def dc_title(self): |
62 """return a suitable *unicode* title for this entity""" |
41 """return a suitable *unicode* title for this entity""" |
63 for rschema, attrschema in self.e_schema.attribute_definitions(): |
42 for rschema, attrschema in self.e_schema.attribute_definitions(): |
64 if rschema.meta: |
43 if rschema.meta: |
65 continue |
44 continue |
66 value = self.get_value(rschema.type) |
45 value = self.cw_attr_value(rschema.type) |
67 if value: |
46 if value: |
68 # make the value printable (dates, floats, bytes, etc.) |
47 # make the value printable (dates, floats, bytes, etc.) |
69 return self.printable_value(rschema.type, value, attrschema.type, |
48 return self.printable_value(rschema.type, value, attrschema.type, |
70 format='text/plain') |
49 format='text/plain') |
71 return u'%s #%s' % (self.dc_type(), self.eid) |
50 return u'%s #%s' % (self.dc_type(), self.eid) |
118 try: |
97 try: |
119 return self.created_by[0] |
98 return self.created_by[0] |
120 except (Unauthorized, IndexError): |
99 except (Unauthorized, IndexError): |
121 return None |
100 return None |
122 |
101 |
123 def breadcrumbs(self, view=None, recurs=False): |
|
124 path = [self] |
|
125 if hasattr(self, 'parent'): |
|
126 parent = self.parent() |
|
127 if parent is not None: |
|
128 try: |
|
129 path = parent.breadcrumbs(view, True) + [self] |
|
130 except TypeError: |
|
131 warn("breadcrumbs method's now takes two arguments " |
|
132 "(view=None, recurs=False), please update", |
|
133 DeprecationWarning) |
|
134 path = parent.breadcrumbs(view) + [self] |
|
135 if not recurs: |
|
136 if view is None: |
|
137 if 'vtitle' in self._cw.form: |
|
138 # embeding for instance |
|
139 path.append( self._cw.form['vtitle'] ) |
|
140 elif view.__regid__ != 'primary' and hasattr(view, 'title'): |
|
141 path.append( self._cw._(view.title) ) |
|
142 return path |
|
143 |
|
144 ## IFeed interface ######################################################## |
|
145 |
|
146 def rss_feed_url(self): |
|
147 return self.absolute_url(vid='rss') |
|
148 |
|
149 # abstractions making the whole things (well, some at least) working ###### |
102 # abstractions making the whole things (well, some at least) working ###### |
150 |
103 |
151 def sortvalue(self, rtype=None): |
104 def sortvalue(self, rtype=None): |
152 """return a value which can be used to sort this entity or given |
105 """return a value which can be used to sort this entity or given |
153 entity's attribute |
106 entity's attribute |
154 """ |
107 """ |
155 if rtype is None: |
108 if rtype is None: |
156 return self.dc_title().lower() |
109 return self.dc_title().lower() |
157 value = self.get_value(rtype) |
110 value = self.cw_attr_value(rtype) |
158 # do not restrict to `unicode` because Bytes will return a `str` value |
111 # do not restrict to `unicode` because Bytes will return a `str` value |
159 if isinstance(value, basestring): |
112 if isinstance(value, basestring): |
160 return self.printable_value(rtype, format='text/plain').lower() |
113 return self.printable_value(rtype, format='text/plain').lower() |
161 return value |
114 return value |
162 |
115 |
187 self._cw.form['__linkto'] = linktos |
140 self._cw.form['__linkto'] = linktos |
188 linkedto.append(typed_eid(eid)) |
141 linkedto.append(typed_eid(eid)) |
189 self.__linkto[(rtype, role)] = linkedto |
142 self.__linkto[(rtype, role)] = linkedto |
190 return linkedto |
143 return linkedto |
191 |
144 |
192 # edit controller callbacks ############################################### |
|
193 |
|
194 def after_deletion_path(self): |
|
195 """return (path, parameters) which should be used as redirect |
|
196 information when this entity is being deleted |
|
197 """ |
|
198 if hasattr(self, 'parent') and self.parent(): |
|
199 return self.parent().rest_path(), {} |
|
200 return str(self.e_schema).lower(), {} |
|
201 |
|
202 def pre_web_edit(self): |
|
203 """callback called by the web editcontroller when an entity will be |
|
204 created/modified, to let a chance to do some entity specific stuff. |
|
205 |
|
206 Do nothing by default. |
|
207 """ |
|
208 pass |
|
209 |
|
210 # server side helpers ##################################################### |
145 # server side helpers ##################################################### |
211 |
|
212 def notification_references(self, view): |
|
213 """used to control References field of email send on notification |
|
214 for this entity. `view` is the notification view. |
|
215 |
|
216 Should return a list of eids which can be used to generate message ids |
|
217 of previously sent email |
|
218 """ |
|
219 return () |
|
220 |
146 |
221 # XXX: store a reference to the AnyEntity class since it is hijacked in goa |
147 # XXX: store a reference to the AnyEntity class since it is hijacked in goa |
222 # configuration and we need the actual reference to avoid infinite loops |
148 # configuration and we need the actual reference to avoid infinite loops |
223 # in mro |
149 # in mro |
224 ANYENTITY = AnyEntity |
150 ANYENTITY = AnyEntity |