2 |
2 |
3 :organization: Logilab |
3 :organization: Logilab |
4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
6 """ |
6 """ |
|
7 __docformat__ = "restructuredtext en" |
7 |
8 |
8 from warnings import warn |
9 from warnings import warn |
|
10 |
|
11 from logilab.mtconverter import html_escape |
9 |
12 |
10 from cubicweb import Unauthorized |
13 from cubicweb import Unauthorized |
11 from cubicweb.view import EntityView |
14 from cubicweb.view import EntityView |
|
15 from cubicweb.web.uicfg import rdisplay |
12 |
16 |
13 _ = unicode |
17 _ = unicode |
14 |
18 |
15 PRIMARY_SKIP_RELS = set(['is', 'is_instance_of', 'identity', |
|
16 'owned_by', 'created_by', |
|
17 'in_state', 'wf_info_for', 'require_permission', |
|
18 'from_entity', 'to_entity', |
|
19 'see_also']) |
|
20 |
19 |
21 class PrimaryView(EntityView): |
20 class PrimaryView(EntityView): |
22 """the full view of an non final entity""" |
21 """the full view of an non final entity""" |
23 id = 'primary' |
22 id = 'primary' |
24 title = _('primary') |
23 title = _('primary') |
25 show_attr_label = True |
24 show_attr_label = True |
26 show_rel_label = True |
25 show_rel_label = True |
27 skip_none = True |
26 skip_none = True |
28 skip_attrs = ('eid', 'creation_date', 'modification_date') |
27 rdisplay = rdisplay |
29 skip_rels = () |
|
30 main_related_section = True |
28 main_related_section = True |
|
29 |
|
30 @classmethod |
|
31 def vreg_initialization_completed(cls): |
|
32 """set default category tags for relations where it's not yet defined in |
|
33 the category relation tags |
|
34 """ |
|
35 for eschema in cls.schema.entities(): |
|
36 for rschema, tschemas, role in eschema.relation_definitions(True): |
|
37 for tschema in tschemas: |
|
38 if role == 'subject': |
|
39 X, Y = eschema, tschema |
|
40 card = rschema.rproperty(X, Y, 'cardinality')[0] |
|
41 composed = rschema.rproperty(X, Y, 'composite') == 'object' |
|
42 else: |
|
43 X, Y = tschema, eschema |
|
44 card = rschema.rproperty(X, Y, 'cardinality')[1] |
|
45 composed = rschema.rproperty(X, Y, 'composite') == 'subject' |
|
46 displayinfo = cls.rdisplay.get(rschema, role, X, Y) |
|
47 if displayinfo is None: |
|
48 if rschema.is_final(): |
|
49 if rschema.meta or tschema.type in ('Password', 'Bytes'): |
|
50 where = None |
|
51 else: |
|
52 where = 'attributes' |
|
53 elif card in '1+': |
|
54 where = 'attributes' |
|
55 elif composed: |
|
56 where = 'relations' |
|
57 else: |
|
58 where = 'sideboxes' |
|
59 displayinfo = {'where': where, |
|
60 'order': cls.rdisplay.get_timestamp()} |
|
61 cls.rdisplay.tag_relation(displayinfo, (X, rschema, Y), |
|
62 role) |
|
63 displayinfo.setdefault('label', '%s_%s' % (rschema, role)) |
31 |
64 |
32 def html_headers(self): |
65 def html_headers(self): |
33 """return a list of html headers (eg something to be inserted between |
66 """return a list of html headers (eg something to be inserted between |
34 <head> and </head> of the returned page |
67 <head> and </head> of the returned page |
35 |
68 |
95 warn('component %s doesnt implement cell_call, please update' |
127 warn('component %s doesnt implement cell_call, please update' |
96 % comp.__class__, DeprecationWarning) |
128 % comp.__class__, DeprecationWarning) |
97 comp.dispatch(w=self.w, view=self) |
129 comp.dispatch(w=self.w, view=self) |
98 self.w(u'</div>') |
130 self.w(u'</div>') |
99 |
131 |
100 def iter_attributes(self, entity): |
|
101 for rschema, targetschema in entity.e_schema.attribute_definitions(): |
|
102 if rschema.type in self.skip_attrs: |
|
103 continue |
|
104 yield rschema, targetschema |
|
105 |
|
106 def iter_relations(self, entity): |
|
107 skip = set(self.skip_rels) |
|
108 skip.update(PRIMARY_SKIP_RELS) |
|
109 for rschema, targetschemas, x in entity.e_schema.relation_definitions(): |
|
110 if rschema.type in skip: |
|
111 continue |
|
112 yield rschema, targetschemas, x |
|
113 |
|
114 def render_entity_title(self, entity): |
132 def render_entity_title(self, entity): |
115 title = self.content_title(entity) # deprecate content_title? |
133 title = self.content_title(entity) # deprecate content_title? |
116 if title: |
134 if title: |
117 self.w(u'<h1><span class="etype">%s</span> %s</h1>' |
135 self.w(u'<h1><span class="etype">%s</span> %s</h1>' |
118 % (entity.dc_type().capitalize(), title)) |
136 % (entity.dc_type().capitalize(), title)) |
119 |
137 |
|
138 |
120 def content_title(self, entity): |
139 def content_title(self, entity): |
121 """default implementation return an empty string""" |
140 """default implementation return dc_title""" |
122 return u'' |
141 return html_escape(entity.dc_title()) |
123 |
142 |
124 def render_entity_metadata(self, entity): |
143 def render_entity_metadata(self, entity): |
125 entity.view('metadata', w=self.w) |
144 entity.view('metadata', w=self.w) |
126 summary = self.summary(entity) # deprecate summary? |
145 summary = self.summary(entity) # deprecate summary? |
127 if summary: |
146 if summary: |
130 def summary(self, entity): |
149 def summary(self, entity): |
131 """default implementation return an empty string""" |
150 """default implementation return an empty string""" |
132 return u'' |
151 return u'' |
133 |
152 |
134 def render_entity_attributes(self, entity, siderelations=None): |
153 def render_entity_attributes(self, entity, siderelations=None): |
135 for rschema, targetschema in self.iter_attributes(entity): |
154 for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'attributes'): |
136 attr = rschema.type |
155 vid = displayinfo.get('vid', 'reledit') |
137 if targetschema.type in ('Password', 'Bytes'): |
156 if rschema.is_final(): |
138 continue |
157 value = entity.view('reledit', rtype=rschema.type) |
139 try: |
158 else: |
140 wdg = entity.get_widget(attr) |
159 vid = displayinfo.get('vid', 'reledit') |
141 except Exception, ex: |
160 rset = self._relation_rset(entity, rschema, role, displayinfo) |
142 value = entity.printable_value(attr, entity[attr], targetschema.type) |
161 if rset: |
143 else: |
162 value = self.view(rset, vid) |
144 value = wdg.render(entity) |
163 else: |
|
164 value = None |
145 if self.skip_none and (value is None or value == ''): |
165 if self.skip_none and (value is None or value == ''): |
146 continue |
166 continue |
147 if rschema.meta: |
167 self._render_attribute(rschema, value) |
148 continue |
|
149 self._render_related_entities(entity, rschema, value) |
|
150 |
|
151 def _preinit_side_related(self, entity): |
|
152 self._sideboxes = [] |
|
153 if hasattr(self, 'get_side_boxes_defs'): |
|
154 self._sideboxes = [(label, rset, 'sidebox') for label, rset in self.get_side_boxes_defs(entity) |
|
155 if rset] |
|
156 else: |
|
157 eschema = entity.e_schema |
|
158 maxrelated = self.req.property_value('navigation.related-limit') |
|
159 for rschema, targetschemas, role in self.iter_relations(entity): |
|
160 if self.is_side_related(rschema, eschema): |
|
161 try: |
|
162 related = entity.related(rschema.type, role, limit=maxrelated+1) |
|
163 except Unauthorized: |
|
164 continue |
|
165 if not related: |
|
166 continue |
|
167 label = display_name(self.req, rschema.type, role) |
|
168 self._sideboxes.append((label, related, 'autolimited')) |
|
169 self._contextboxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset, |
|
170 row=self.row, view=self, |
|
171 context='incontext')) |
|
172 return self._sideboxes or self._contextboxes |
|
173 |
168 |
174 def render_entity_relations(self, entity, siderelations=None): |
169 def render_entity_relations(self, entity, siderelations=None): |
175 eschema = entity.e_schema |
170 for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'relations'): |
176 for rschema, targetschemas, x in self.iter_relations(entity): |
171 rset = self._relation_rset(entity, rschema, role, displayinfo) |
177 if not self.is_side_related(rschema, eschema): |
172 if rset: |
178 try: |
173 self._render_relation(rset, displayinfo, 'autolimited', |
179 related = entity.related(rschema.type, x, limit=maxrelated+1) |
174 self.show_rel_label) |
180 except Unauthorized: |
175 |
181 continue |
176 def render_side_boxes(self, boxes): |
182 self._render_related_entities(entity, rschema, related, x) |
|
183 |
|
184 |
|
185 def render_side_related(self, entity, siderelations=None): |
|
186 """display side related relations: |
177 """display side related relations: |
187 non-meta in a first step, meta in a second step |
178 non-meta in a first step, meta in a second step |
188 """ |
179 """ |
189 if self._sideboxes: |
180 for box in boxes: |
190 for label, rset, vid in self._sideboxes: |
181 if isinstance(box, tuple): |
|
182 label, rset, vid, _ = box |
191 self.w(u'<div class="sideRelated">') |
183 self.w(u'<div class="sideRelated">') |
192 self.wview(vid, rset, title=label) |
184 self.wview(vid, rset, title=label) |
193 self.w(u'</div>') |
185 self.w(u'</div>') |
194 if self._contextboxes: |
186 else: |
195 for box in self._contextboxes: |
|
196 try: |
187 try: |
197 box.dispatch(w=self.w, row=self.row) |
188 box.dispatch(w=self.w, row=self.row) |
198 except NotImplementedError: |
189 except NotImplementedError: |
199 # much probably a context insensitive box, which only implements |
190 # much probably a context insensitive box, which only implements |
200 # .call() and not cell_call() |
191 # .call() and not cell_call() |
201 box.dispatch(w=self.w) |
192 box.dispatch(w=self.w) |
202 |
193 |
203 def is_side_related(self, rschema, eschema): |
194 def _prepare_side_boxes(self, entity): |
204 return rschema.meta and \ |
195 sideboxes = [] |
205 not rschema.schema_relation() == eschema.schema_entity() |
196 for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'sideboxes'): |
206 |
197 rset = self._relation_rset(entity, rschema, role, displayinfo) |
207 def _render_related_entities(self, entity, rschema, related, |
198 if not rset: |
208 role='subject'): |
199 continue |
|
200 label = display_name(self.req, rschema.type, role) |
|
201 vid = displayinfo.get('vid', 'autolimited') |
|
202 sideboxes.append((label, rset, vid, displayinfo.get('order'))) |
|
203 sideboxes = sorted(sideboxes, key=lambda x: x[-1]) |
|
204 sideboxes += list(self.vreg.possible_vobjects('boxes', self.req, self.rset, |
|
205 row=self.row, view=self, |
|
206 context='incontext')) |
|
207 return sideboxes |
|
208 |
|
209 def _iter_display(self, entity, where): |
|
210 eschema = entity.e_schema |
|
211 for rschema, tschemas, role in eschema.relation_definitions(True): |
|
212 matchtschemas = [] |
|
213 for tschema in tschemas: |
|
214 displayinfo = self.rdisplay.etype_get(eschema, rschema, role, |
|
215 tschema) |
|
216 assert displayinfo is not None, (str(rschema), role, |
|
217 str(eschema), str(tschema)) |
|
218 if displayinfo.get('where') == where: |
|
219 matchtschemas.append(tschema) |
|
220 if matchtschemas: |
|
221 # XXX pick the latest displayinfo |
|
222 yield rschema, matchtschemas, role, displayinfo |
|
223 |
|
224 def _relation_rset(self, entity, rschema, role, displayinfo): |
|
225 try: |
|
226 if displayinfo.get('limit'): |
|
227 rset = entity.related(rschema.type, role, |
|
228 limit=self.maxrelated+1) |
|
229 else: |
|
230 rset = entity.related(rschema.type, role) |
|
231 except Unauthorized: |
|
232 return |
|
233 if 'filter' in displayinfo: |
|
234 rset = displayinfo['filter'](rset) |
|
235 return rset |
|
236 |
|
237 def _render_relation(self, rset, displayinfo, defaultvid, showlabel): |
|
238 self.w('<div class="section">') |
|
239 if showlabel: |
|
240 label = self.req._(displayinfo['label']) |
|
241 self.w('<h4>%s</h4>' % label) |
|
242 self.wview(displayinfo.get('vid', defaultvid), rset) |
|
243 self.w('</div>') |
|
244 |
|
245 def _render_attribute(self, rschema, value, role='subject'): |
209 if rschema.is_final(): |
246 if rschema.is_final(): |
210 value = related |
|
211 show_label = self.show_attr_label |
247 show_label = self.show_attr_label |
212 else: |
248 else: |
213 if not related: |
249 show_label = self.show_rel_label |
214 return |
|
215 value = self.view('autolimited', related) |
|
216 label = display_name(self.req, rschema.type, role) |
250 label = display_name(self.req, rschema.type, role) |
217 self.field(label, value, show_label=show_label, tr=False) |
251 self.field(label, value, show_label=show_label, tr=False) |
218 |
252 |
219 |
253 |
220 class RelatedView(EntityView): |
254 class RelatedView(EntityView): |
221 id = 'autolimited' |
255 id = 'autolimited' |
222 def call(self): |
256 def call(self, title=None, **kwargs): |
223 # if not too many entities, show them all in a list |
257 # if not too many entities, show them all in a list |
224 maxrelated = self.req.property_value('navigation.related-limit') |
258 maxrelated = self.req.property_value('navigation.related-limit') |
|
259 if title: |
|
260 self.w(u'<div class="title"><span>%s</span></div>' % title) |
225 if self.rset.rowcount <= maxrelated: |
261 if self.rset.rowcount <= maxrelated: |
226 if self.rset.rowcount == 1: |
262 if self.rset.rowcount == 1: |
227 self.wview('incontext', self.rset, row=0) |
263 self.wview('incontext', self.rset, row=0) |
228 elif 1 < self.rset.rowcount <= 5: |
264 elif 1 < self.rset.rowcount <= 5: |
229 self.wview('csv', self.rset) |
265 self.wview('csv', self.rset) |