|
1 """The default primary view |
|
2 |
|
3 :organization: Logilab |
|
4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
6 """ |
|
7 |
|
8 from warnings import warn |
|
9 |
|
10 from cubicweb import Unauthorized |
|
11 from cubicweb.view import EntityView |
|
12 |
|
13 _ = unicode |
|
14 |
|
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 |
|
21 class PrimaryView(EntityView): |
|
22 """the full view of an non final entity""" |
|
23 id = 'primary' |
|
24 title = _('primary') |
|
25 show_attr_label = True |
|
26 show_rel_label = True |
|
27 skip_none = True |
|
28 skip_attrs = ('eid', 'creation_date', 'modification_date') |
|
29 skip_rels = () |
|
30 main_related_section = True |
|
31 |
|
32 def html_headers(self): |
|
33 """return a list of html headers (eg something to be inserted between |
|
34 <head> and </head> of the returned page |
|
35 |
|
36 by default primary views are indexed |
|
37 """ |
|
38 return [] |
|
39 |
|
40 def cell_call(self, row, col): |
|
41 self.row = row |
|
42 # XXX move render_entity implementation here |
|
43 self.render_entity(self.complete_entity(row, col)) |
|
44 |
|
45 def render_entity(self, entity): |
|
46 """return html to display the given entity""" |
|
47 siderelations = [] |
|
48 self.render_entity_title(entity) |
|
49 self.render_entity_metadata(entity) |
|
50 # entity's attributes and relations, excluding meta data |
|
51 # if the entity isn't meta itself |
|
52 boxes = self._preinit_side_related(entity, siderelations) |
|
53 if boxes: |
|
54 self.w(u'<table width="100%"><tr><td width="75%">') |
|
55 self.w(u'<div>') |
|
56 self.w(u'<div class="mainInfo">') |
|
57 self.render_entity_attributes(entity, siderelations) |
|
58 self.w(u'</div>') |
|
59 self.content_navigation_components('navcontenttop') |
|
60 if self.main_related_section: |
|
61 self.render_entity_relations(entity, siderelations) |
|
62 self.w(u'</div>') |
|
63 if boxes: |
|
64 self.w(u'</td><td>') |
|
65 # side boxes |
|
66 self.w(u'<div class="primaryRight">') |
|
67 self.render_side_related(entity, siderelations) |
|
68 self.w(u'</div>') |
|
69 self.w(u'</td></tr></table>') |
|
70 self.content_navigation_components('navcontentbottom') |
|
71 |
|
72 |
|
73 def content_navigation_components(self, context): |
|
74 self.w(u'<div class="%s">' % context) |
|
75 for comp in self.vreg.possible_vobjects('contentnavigation', |
|
76 self.req, self.rset, row=self.row, |
|
77 view=self, context=context): |
|
78 try: |
|
79 comp.dispatch(w=self.w, row=self.row, view=self) |
|
80 except NotImplementedError: |
|
81 warn('component %s doesnt implement cell_call, please update' |
|
82 % comp.__class__, DeprecationWarning) |
|
83 comp.dispatch(w=self.w, view=self) |
|
84 self.w(u'</div>') |
|
85 |
|
86 def iter_attributes(self, entity): |
|
87 for rschema, targetschema in entity.e_schema.attribute_definitions(): |
|
88 if rschema.type in self.skip_attrs: |
|
89 continue |
|
90 yield rschema, targetschema |
|
91 |
|
92 def iter_relations(self, entity): |
|
93 skip = set(self.skip_rels) |
|
94 skip.update(PRIMARY_SKIP_RELS) |
|
95 for rschema, targetschemas, x in entity.e_schema.relation_definitions(): |
|
96 if rschema.type in skip: |
|
97 continue |
|
98 yield rschema, targetschemas, x |
|
99 |
|
100 def render_entity_title(self, entity): |
|
101 title = self.content_title(entity) # deprecate content_title? |
|
102 if title: |
|
103 self.w(u'<h1><span class="etype">%s</span> %s</h1>' |
|
104 % (entity.dc_type().capitalize(), title)) |
|
105 |
|
106 def content_title(self, entity): |
|
107 """default implementation return an empty string""" |
|
108 return u'' |
|
109 |
|
110 def render_entity_metadata(self, entity): |
|
111 entity.view('metadata', w=self.w) |
|
112 summary = self.summary(entity) # deprecate summary? |
|
113 if summary: |
|
114 self.w(u'<div class="summary">%s</div>' % summary) |
|
115 |
|
116 def summary(self, entity): |
|
117 """default implementation return an empty string""" |
|
118 return u'' |
|
119 |
|
120 def render_entity_attributes(self, entity, siderelations): |
|
121 for rschema, targetschema in self.iter_attributes(entity): |
|
122 attr = rschema.type |
|
123 if targetschema.type in ('Password', 'Bytes'): |
|
124 continue |
|
125 try: |
|
126 wdg = entity.get_widget(attr) |
|
127 except Exception, ex: |
|
128 value = entity.printable_value(attr, entity[attr], targetschema.type) |
|
129 else: |
|
130 value = wdg.render(entity) |
|
131 if self.skip_none and (value is None or value == ''): |
|
132 continue |
|
133 if rschema.meta: |
|
134 continue |
|
135 self._render_related_entities(entity, rschema, value) |
|
136 |
|
137 def _preinit_side_related(self, entity, siderelations): |
|
138 self._sideboxes = None |
|
139 self._related_entities = [] |
|
140 if hasattr(self, 'get_side_boxes_defs'): |
|
141 self._sideboxes = [(label, rset) for label, rset in self.get_side_boxes_defs(entity) |
|
142 if rset] |
|
143 else: |
|
144 eschema = entity.e_schema |
|
145 maxrelated = self.req.property_value('navigation.related-limit') |
|
146 for rschema, targetschemas, x in self.iter_relations(entity): |
|
147 try: |
|
148 related = entity.related(rschema.type, x, limit=maxrelated+1) |
|
149 except Unauthorized: |
|
150 continue |
|
151 if not related: |
|
152 continue |
|
153 if self.is_side_related(rschema, eschema): |
|
154 siderelations.append((rschema, related, x)) |
|
155 continue |
|
156 self._related_entities.append((rschema, related, x)) |
|
157 self._boxes_in_context = list(self.vreg.possible_vobjects('boxes', self.req, self.rset, |
|
158 row=self.row, view=self, |
|
159 context='incontext')) |
|
160 return self._sideboxes or self._boxes_in_context or self._related_entities or siderelations |
|
161 |
|
162 def render_entity_relations(self, entity, siderelations): |
|
163 if self._related_entities: |
|
164 for rschema, related, x in self._related_entities: |
|
165 self._render_related_entities(entity, rschema, related, x) |
|
166 |
|
167 |
|
168 def render_side_related(self, entity, siderelations): |
|
169 """display side related relations: |
|
170 non-meta in a first step, meta in a second step |
|
171 """ |
|
172 if self._sideboxes: |
|
173 for label, rset in self._sideboxes: |
|
174 self.w(u'<div class="sideRelated">') |
|
175 self.wview('sidebox', rset, title=label) |
|
176 self.w(u'</div>') |
|
177 elif siderelations: |
|
178 self.w(u'<div class="sideRelated">') |
|
179 for relatedinfos in siderelations: |
|
180 # if not relatedinfos[0].meta: |
|
181 # continue |
|
182 self._render_related_entities(entity, *relatedinfos) |
|
183 self.w(u'</div>') |
|
184 |
|
185 if self._boxes_in_context: |
|
186 for box in self._boxes_in_context: |
|
187 try: |
|
188 box.dispatch(w=self.w, row=self.row) |
|
189 except NotImplementedError: |
|
190 # much probably a context insensitive box, which only implements |
|
191 # .call() and not cell_call() |
|
192 box.dispatch(w=self.w) |
|
193 |
|
194 def is_side_related(self, rschema, eschema): |
|
195 return rschema.meta and \ |
|
196 not rschema.schema_relation() == eschema.schema_entity() |
|
197 |
|
198 def _render_related_entities(self, entity, rschema, related, |
|
199 role='subject'): |
|
200 if rschema.is_final(): |
|
201 value = related |
|
202 show_label = self.show_attr_label |
|
203 else: |
|
204 if not related: |
|
205 return |
|
206 show_label = self.show_rel_label |
|
207 # if not too many entities, show them all in a list |
|
208 maxrelated = self.req.property_value('navigation.related-limit') |
|
209 if related.rowcount <= maxrelated: |
|
210 if related.rowcount == 1: |
|
211 value = self.view('incontext', related, row=0) |
|
212 elif 1 < related.rowcount <= 5: |
|
213 value = self.view('csv', related) |
|
214 else: |
|
215 value = '<div>' + self.view('simplelist', related) + '</div>' |
|
216 # else show links to display related entities |
|
217 else: |
|
218 rql = related.printable_rql() |
|
219 related.limit(maxrelated) |
|
220 value = '<div>' + self.view('simplelist', related) |
|
221 value += '[<a href="%s">%s</a>]' % (self.build_url(rql=rql), |
|
222 self.req._('see them all')) |
|
223 value += '</div>' |
|
224 label = display_name(self.req, rschema.type, role) |
|
225 self.field(label, value, show_label=show_label, tr=False) |
|
226 |