author | sylvain.thenault@logilab.fr |
Wed, 15 Apr 2009 14:06:08 +0200 | |
branch | tls-sprint |
changeset 1356 | 7b4802822f40 |
parent 1263 | 01152fffd593 |
child 1451 | 982e8616d9a2 |
permissions | -rw-r--r-- |
0 | 1 |
"""Set of HTML generic base views: |
2 |
||
3 |
* noresult, final |
|
4 |
* primary, sidebox |
|
5 |
* secondary, oneline, incontext, outofcontext, text |
|
6 |
* list |
|
7 |
||
8 |
||
9 |
:organization: Logilab |
|
479 | 10 |
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
0 | 11 |
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
12 |
""" |
|
964
a711c7c185d1
gargl, this shouldn't be there in case we're running py 2.4
sylvain.thenault@logilab.fr
parents:
958
diff
changeset
|
13 |
#from __future__ import with_statement |
958
6053622aae81
fix name error, filter out non empty rset
sylvain.thenault@logilab.fr
parents:
956
diff
changeset
|
14 |
|
0 | 15 |
__docformat__ = "restructuredtext en" |
16 |
||
528
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
17 |
from warnings import warn |
0 | 18 |
|
19 |
from rql import nodes |
|
20 |
||
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
21 |
from logilab.mtconverter import TransformError, html_escape |
0 | 22 |
|
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
23 |
from cubicweb import Unauthorized, NoSelectableObject |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
24 |
from cubicweb.selectors import yes, empty_rset |
756 | 25 |
from cubicweb.view import EntityView, AnyRsetView, View |
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
26 |
from cubicweb.common.uilib import cut, printable_value |
0 | 27 |
|
28 |
_ = unicode |
|
29 |
||
30 |
class NullView(AnyRsetView): |
|
31 |
"""default view when no result has been found""" |
|
32 |
id = 'null' |
|
758
0c0dfd33a76d
instantiate selectors wherever needed
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
742
diff
changeset
|
33 |
__select__ = yes() |
0 | 34 |
def call(self, **kwargs): |
35 |
pass |
|
36 |
cell_call = call |
|
37 |
||
38 |
||
632 | 39 |
class NoResultView(View): |
0 | 40 |
"""default view when no result has been found""" |
742
99115e029dca
replaced most of __selectors__ assignments with __select__
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
647
diff
changeset
|
41 |
__select__ = empty_rset() |
0 | 42 |
id = 'noresult' |
43 |
||
44 |
def call(self, **kwargs): |
|
45 |
self.w(u'<div class="searchMessage"><strong>%s</strong></div>\n' |
|
46 |
% self.req._('No result matching query')) |
|
47 |
||
48 |
||
49 |
class FinalView(AnyRsetView): |
|
50 |
"""display values without any transformation (i.e. get a number for |
|
51 |
entities) |
|
52 |
""" |
|
53 |
id = 'final' |
|
978
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
54 |
# record generated i18n catalog messages |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
55 |
_('%d years') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
56 |
_('%d months') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
57 |
_('%d weeks') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
58 |
_('%d days') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
59 |
_('%d hours') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
60 |
_('%d minutes') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
61 |
_('%d seconds') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
62 |
_('%d years') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
63 |
_('%d months') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
64 |
_('%d weeks') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
65 |
_('%d days') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
66 |
_('%d hours') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
67 |
_('%d minutes') |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
68 |
_('%d seconds') |
0 | 69 |
|
573
9c8fd72ba6c1
try to get raw text values in rset csv view
sylvain.thenault@logilab.fr
parents:
570
diff
changeset
|
70 |
def cell_call(self, row, col, props=None, displaytime=False, format='text/html'): |
0 | 71 |
etype = self.rset.description[row][col] |
72 |
value = self.rset.rows[row][col] |
|
73 |
if etype == 'String': |
|
74 |
entity, rtype = self.rset.related_entity(row, col) |
|
75 |
if entity is not None: |
|
76 |
# yes ! |
|
573
9c8fd72ba6c1
try to get raw text values in rset csv view
sylvain.thenault@logilab.fr
parents:
570
diff
changeset
|
77 |
self.w(entity.printable_value(rtype, value, format=format)) |
0 | 78 |
return |
79 |
if etype in ('Time', 'Interval'): |
|
80 |
# value is DateTimeDelta but we have no idea about what is the |
|
81 |
# reference date here, so we can only approximate years and months |
|
978
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
82 |
if format == 'text/html': |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
83 |
space = ' ' |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
84 |
else: |
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
85 |
space = ' ' |
0 | 86 |
if value.days > 730: # 2 years |
978
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
87 |
self.w(self.req.__('%%d%syears' % space) % (value.days // 365)) |
0 | 88 |
elif value.days > 60: # 2 months |
978
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
89 |
self.w(self.req.__('%%d%smonths' % space) % (value.days // 30)) |
0 | 90 |
elif value.days > 14: # 2 weeks |
978
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
91 |
self.w(self.req.__('%%d%sweeks' % space) % (value.days // 7)) |
0 | 92 |
elif value.days > 2: |
978
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
93 |
self.w(self.req.__('%%d%sdays' % space) % int(value.days)) |
0 | 94 |
elif value.hours > 2: |
978
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
95 |
self.w(self.req.__('%%d%shours' % space) % int(value.hours)) |
0 | 96 |
elif value.minutes >= 2: |
978
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
97 |
self.w(self.req.__('%%d%sminutes' % space) % int(value.minutes)) |
0 | 98 |
else: |
978
536c748e7b3f
use non-breakable space in time display
sylvain.thenault@logilab.fr
parents:
964
diff
changeset
|
99 |
self.w(self.req.__('%%d%sseconds' % space) % int(value.seconds)) |
0 | 100 |
return |
101 |
self.wdata(printable_value(self.req, etype, value, props, displaytime=displaytime)) |
|
102 |
||
103 |
||
104 |
PRIMARY_SKIP_RELS = set(['is', 'is_instance_of', 'identity', |
|
105 |
'owned_by', 'created_by', |
|
106 |
'in_state', 'wf_info_for', 'require_permission', |
|
107 |
'from_entity', 'to_entity', |
|
108 |
'see_also']) |
|
109 |
||
110 |
class PrimaryView(EntityView): |
|
111 |
"""the full view of an non final entity""" |
|
112 |
id = 'primary' |
|
113 |
title = _('primary') |
|
114 |
show_attr_label = True |
|
115 |
show_rel_label = True |
|
116 |
skip_none = True |
|
117 |
skip_attrs = ('eid', 'creation_date', 'modification_date') |
|
118 |
skip_rels = () |
|
119 |
main_related_section = True |
|
120 |
||
121 |
def html_headers(self): |
|
122 |
"""return a list of html headers (eg something to be inserted between |
|
123 |
<head> and </head> of the returned page |
|
124 |
||
125 |
by default primary views are indexed |
|
126 |
""" |
|
127 |
return [] |
|
128 |
||
129 |
def cell_call(self, row, col): |
|
130 |
self.row = row |
|
213
6842c3dee34b
adding files (formely appearing in jpl) specific to cubicweb
Laure Bourgois <Laure.Bourgois@logilab.fr>
parents:
179
diff
changeset
|
131 |
# XXX move render_entity implementation here |
0 | 132 |
self.render_entity(self.complete_entity(row, col)) |
133 |
||
134 |
def render_entity(self, entity): |
|
135 |
"""return html to display the given entity""" |
|
136 |
siderelations = [] |
|
137 |
self.render_entity_title(entity) |
|
138 |
self.render_entity_metadata(entity) |
|
139 |
# entity's attributes and relations, excluding meta data |
|
140 |
# if the entity isn't meta itself |
|
988
b73f3cd23d43
remplacing tables by div for right (and left) box layout
Laure Bourgois <Laure.Bourgois@logilab.fr>
parents:
978
diff
changeset
|
141 |
self.w(u'<div>') |
0 | 142 |
self.w(u'<div class="mainInfo">') |
143 |
self.render_entity_attributes(entity, siderelations) |
|
144 |
self.w(u'</div>') |
|
528
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
145 |
self.content_navigation_components('navcontenttop') |
0 | 146 |
if self.main_related_section: |
147 |
self.render_entity_relations(entity, siderelations) |
|
988
b73f3cd23d43
remplacing tables by div for right (and left) box layout
Laure Bourgois <Laure.Bourgois@logilab.fr>
parents:
978
diff
changeset
|
148 |
self.w(u'</div>') |
0 | 149 |
# side boxes |
988
b73f3cd23d43
remplacing tables by div for right (and left) box layout
Laure Bourgois <Laure.Bourgois@logilab.fr>
parents:
978
diff
changeset
|
150 |
self.w(u'<div class="primaryRight">') |
0 | 151 |
self.render_side_related(entity, siderelations) |
988
b73f3cd23d43
remplacing tables by div for right (and left) box layout
Laure Bourgois <Laure.Bourgois@logilab.fr>
parents:
978
diff
changeset
|
152 |
self.w(u'</div>') |
991
746fed01d1e0
correction (removing comments)
Laure Bourgois <Laure.Bourgois@logilab.fr>
parents:
988
diff
changeset
|
153 |
self.w(u'<div class="clear"></div>') |
528
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
154 |
self.content_navigation_components('navcontentbottom') |
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
155 |
|
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
156 |
def content_navigation_components(self, context): |
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
157 |
self.w(u'<div class="%s">' % context) |
0 | 158 |
for comp in self.vreg.possible_vobjects('contentnavigation', |
528
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
159 |
self.req, self.rset, row=self.row, |
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
160 |
view=self, context=context): |
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
161 |
try: |
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
162 |
comp.dispatch(w=self.w, row=self.row, view=self) |
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
163 |
except NotImplementedError: |
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
164 |
warn('component %s doesnt implement cell_call, please update' |
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
165 |
% comp.__class__, DeprecationWarning) |
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
166 |
comp.dispatch(w=self.w, view=self) |
0 | 167 |
self.w(u'</div>') |
528
60bd171ecd04
give row when selecting/dispatching content navigation component
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
168 |
|
0 | 169 |
def iter_attributes(self, entity): |
170 |
for rschema, targetschema in entity.e_schema.attribute_definitions(): |
|
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
171 |
if rschema.type in self.skip_attrs: |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
172 |
continue |
0 | 173 |
yield rschema, targetschema |
174 |
||
175 |
def iter_relations(self, entity): |
|
176 |
skip = set(self.skip_rels) |
|
177 |
skip.update(PRIMARY_SKIP_RELS) |
|
178 |
for rschema, targetschemas, x in entity.e_schema.relation_definitions(): |
|
179 |
if rschema.type in skip: |
|
180 |
continue |
|
181 |
yield rschema, targetschemas, x |
|
182 |
||
183 |
def render_entity_title(self, entity): |
|
184 |
title = self.content_title(entity) # deprecate content_title? |
|
185 |
if title: |
|
186 |
self.w(u'<h1><span class="etype">%s</span> %s</h1>' |
|
187 |
% (entity.dc_type().capitalize(), title)) |
|
188 |
||
189 |
def content_title(self, entity): |
|
190 |
"""default implementation return an empty string""" |
|
191 |
return u'' |
|
192 |
||
193 |
def render_entity_metadata(self, entity): |
|
194 |
entity.view('metadata', w=self.w) |
|
195 |
summary = self.summary(entity) # deprecate summary? |
|
196 |
if summary: |
|
197 |
self.w(u'<div class="summary">%s</div>' % summary) |
|
198 |
||
199 |
def summary(self, entity): |
|
200 |
"""default implementation return an empty string""" |
|
179 | 201 |
return u'' |
0 | 202 |
|
203 |
def render_entity_attributes(self, entity, siderelations): |
|
204 |
for rschema, targetschema in self.iter_attributes(entity): |
|
205 |
attr = rschema.type |
|
206 |
if targetschema.type in ('Password', 'Bytes'): |
|
207 |
continue |
|
208 |
try: |
|
209 |
wdg = entity.get_widget(attr) |
|
210 |
except Exception, ex: |
|
211 |
value = entity.printable_value(attr, entity[attr], targetschema.type) |
|
212 |
else: |
|
213 |
value = wdg.render(entity) |
|
214 |
if self.skip_none and (value is None or value == ''): |
|
215 |
continue |
|
216 |
if rschema.meta: |
|
217 |
continue |
|
218 |
self._render_related_entities(entity, rschema, value) |
|
219 |
||
220 |
def render_entity_relations(self, entity, siderelations): |
|
221 |
if hasattr(self, 'get_side_boxes_defs'): |
|
222 |
return |
|
223 |
eschema = entity.e_schema |
|
224 |
maxrelated = self.req.property_value('navigation.related-limit') |
|
225 |
for rschema, targetschemas, x in self.iter_relations(entity): |
|
226 |
try: |
|
227 |
related = entity.related(rschema.type, x, limit=maxrelated+1) |
|
228 |
except Unauthorized: |
|
229 |
continue |
|
230 |
if not related: |
|
231 |
continue |
|
232 |
if self.is_side_related(rschema, eschema): |
|
233 |
siderelations.append((rschema, related, x)) |
|
234 |
continue |
|
235 |
self._render_related_entities(entity, rschema, related, x) |
|
236 |
||
237 |
def render_side_related(self, entity, siderelations): |
|
238 |
"""display side related relations: |
|
239 |
non-meta in a first step, meta in a second step |
|
240 |
""" |
|
241 |
if hasattr(self, 'get_side_boxes_defs'): |
|
958
6053622aae81
fix name error, filter out non empty rset
sylvain.thenault@logilab.fr
parents:
956
diff
changeset
|
242 |
sideboxes = [(label, rset) for label, rset in self.get_side_boxes_defs(entity) |
6053622aae81
fix name error, filter out non empty rset
sylvain.thenault@logilab.fr
parents:
956
diff
changeset
|
243 |
if rset] |
6053622aae81
fix name error, filter out non empty rset
sylvain.thenault@logilab.fr
parents:
956
diff
changeset
|
244 |
if sideboxes: |
6053622aae81
fix name error, filter out non empty rset
sylvain.thenault@logilab.fr
parents:
956
diff
changeset
|
245 |
for label, rset in sideboxes: |
0 | 246 |
self.w(u'<div class="sideRelated">') |
247 |
self.wview('sidebox', rset, title=label) |
|
248 |
self.w(u'</div>') |
|
249 |
elif siderelations: |
|
250 |
self.w(u'<div class="sideRelated">') |
|
251 |
for relatedinfos in siderelations: |
|
252 |
# if not relatedinfos[0].meta: |
|
253 |
# continue |
|
254 |
self._render_related_entities(entity, *relatedinfos) |
|
255 |
self.w(u'</div>') |
|
953
ded89d49c0b4
xhtml fixes: table has no align attribute, avoid inserting empty table
sylvain.thenault@logilab.fr
parents:
925
diff
changeset
|
256 |
boxes = list(self.vreg.possible_vobjects('boxes', self.req, self.rset, |
ded89d49c0b4
xhtml fixes: table has no align attribute, avoid inserting empty table
sylvain.thenault@logilab.fr
parents:
925
diff
changeset
|
257 |
row=self.row, view=self, |
ded89d49c0b4
xhtml fixes: table has no align attribute, avoid inserting empty table
sylvain.thenault@logilab.fr
parents:
925
diff
changeset
|
258 |
context='incontext')) |
ded89d49c0b4
xhtml fixes: table has no align attribute, avoid inserting empty table
sylvain.thenault@logilab.fr
parents:
925
diff
changeset
|
259 |
if boxes: |
ded89d49c0b4
xhtml fixes: table has no align attribute, avoid inserting empty table
sylvain.thenault@logilab.fr
parents:
925
diff
changeset
|
260 |
for box in boxes: |
ded89d49c0b4
xhtml fixes: table has no align attribute, avoid inserting empty table
sylvain.thenault@logilab.fr
parents:
925
diff
changeset
|
261 |
try: |
ded89d49c0b4
xhtml fixes: table has no align attribute, avoid inserting empty table
sylvain.thenault@logilab.fr
parents:
925
diff
changeset
|
262 |
box.dispatch(w=self.w, row=self.row) |
ded89d49c0b4
xhtml fixes: table has no align attribute, avoid inserting empty table
sylvain.thenault@logilab.fr
parents:
925
diff
changeset
|
263 |
except NotImplementedError: |
ded89d49c0b4
xhtml fixes: table has no align attribute, avoid inserting empty table
sylvain.thenault@logilab.fr
parents:
925
diff
changeset
|
264 |
# much probably a context insensitive box, which only implements |
ded89d49c0b4
xhtml fixes: table has no align attribute, avoid inserting empty table
sylvain.thenault@logilab.fr
parents:
925
diff
changeset
|
265 |
# .call() and not cell_call() |
991
746fed01d1e0
correction (removing comments)
Laure Bourgois <Laure.Bourgois@logilab.fr>
parents:
988
diff
changeset
|
266 |
box.dispatch(w=self.w) |
0 | 267 |
|
268 |
def is_side_related(self, rschema, eschema): |
|
269 |
return rschema.meta and \ |
|
270 |
not rschema.schema_relation() == eschema.schema_entity() |
|
271 |
||
272 |
def _render_related_entities(self, entity, rschema, related, |
|
273 |
role='subject'): |
|
274 |
if rschema.is_final(): |
|
275 |
value = related |
|
276 |
show_label = self.show_attr_label |
|
277 |
else: |
|
278 |
if not related: |
|
279 |
return |
|
280 |
show_label = self.show_rel_label |
|
281 |
# if not too many entities, show them all in a list |
|
282 |
maxrelated = self.req.property_value('navigation.related-limit') |
|
283 |
if related.rowcount <= maxrelated: |
|
284 |
if related.rowcount == 1: |
|
285 |
value = self.view('incontext', related, row=0) |
|
286 |
elif 1 < related.rowcount <= 5: |
|
287 |
value = self.view('csv', related) |
|
288 |
else: |
|
289 |
value = '<div>' + self.view('simplelist', related) + '</div>' |
|
290 |
# else show links to display related entities |
|
291 |
else: |
|
292 |
rql = related.printable_rql() |
|
293 |
related.limit(maxrelated) |
|
294 |
value = '<div>' + self.view('simplelist', related) |
|
295 |
value += '[<a href="%s">%s</a>]' % (self.build_url(rql=rql), |
|
296 |
self.req._('see them all')) |
|
297 |
value += '</div>' |
|
298 |
label = display_name(self.req, rschema.type, role) |
|
299 |
self.field(label, value, show_label=show_label, w=self.w, tr=False) |
|
300 |
||
301 |
||
302 |
class SecondaryView(EntityView): |
|
303 |
id = 'secondary' |
|
304 |
title = _('secondary') |
|
305 |
||
306 |
def cell_call(self, row, col): |
|
307 |
"""the secondary view for an entity |
|
308 |
secondary = icon + view(oneline) |
|
309 |
""" |
|
310 |
entity = self.entity(row, col) |
|
311 |
self.w(u' ') |
|
312 |
self.wview('oneline', self.rset, row=row, col=col) |
|
313 |
||
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
314 |
|
0 | 315 |
class OneLineView(EntityView): |
316 |
id = 'oneline' |
|
317 |
title = _('oneline') |
|
318 |
||
319 |
def cell_call(self, row, col): |
|
320 |
"""the one line view for an entity: linked text view |
|
321 |
""" |
|
322 |
entity = self.entity(row, col) |
|
323 |
self.w(u'<a href="%s">' % html_escape(entity.absolute_url())) |
|
324 |
self.w(html_escape(self.view('text', self.rset, row=row, col=col))) |
|
325 |
self.w(u'</a>') |
|
326 |
||
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
327 |
|
0 | 328 |
class TextView(EntityView): |
532
ce3e94cbb074
specify content type for text views
sylvain.thenault@logilab.fr
parents:
531
diff
changeset
|
329 |
"""the simplest text view for an entity""" |
0 | 330 |
id = 'text' |
331 |
title = _('text') |
|
532
ce3e94cbb074
specify content type for text views
sylvain.thenault@logilab.fr
parents:
531
diff
changeset
|
332 |
content_type = 'text/plain' |
647
49bb57a9606b
some selectors update + drop EntityRelationView (only used by jpl)
sylvain.thenault@logilab.fr
parents:
632
diff
changeset
|
333 |
|
0 | 334 |
def call(self, **kwargs): |
335 |
"""the view is called for an entire result set, by default loop |
|
336 |
other rows of the result set and call the same view on the |
|
337 |
particular row |
|
338 |
||
339 |
Views applicable on None result sets have to override this method |
|
340 |
""" |
|
341 |
rset = self.rset |
|
342 |
if rset is None: |
|
343 |
raise NotImplementedError, self |
|
344 |
for i in xrange(len(rset)): |
|
345 |
self.wview(self.id, rset, row=i, **kwargs) |
|
346 |
if len(rset) > 1: |
|
347 |
self.w(u"\n") |
|
348 |
||
349 |
def cell_call(self, row, col=0, **kwargs): |
|
350 |
entity = self.entity(row, col) |
|
351 |
self.w(cut(entity.dc_title(), |
|
352 |
self.req.property_value('navigation.short-line-size'))) |
|
353 |
||
137
7e45cf48c2f1
don't display additional owners in metadata, this is confusing
Sylvain Thenault <sylvain.thenault@logilab.fr>
parents:
0
diff
changeset
|
354 |
|
0 | 355 |
class MetaDataView(EntityView): |
356 |
"""paragraph view of some metadata""" |
|
357 |
id = 'metadata' |
|
358 |
show_eid = True |
|
359 |
||
360 |
def cell_call(self, row, col): |
|
361 |
_ = self.req._ |
|
362 |
entity = self.entity(row, col) |
|
363 |
self.w(u'<div class="metadata">') |
|
364 |
if self.show_eid: |
|
365 |
self.w(u'#%s - ' % entity.eid) |
|
366 |
if entity.modification_date != entity.creation_date: |
|
367 |
self.w(u'<span>%s</span> ' % _('latest update on')) |
|
368 |
self.w(u'<span class="value">%s</span>, ' |
|
369 |
% self.format_date(entity.modification_date)) |
|
370 |
# entities from external source may not have a creation date (eg ldap) |
|
371 |
if entity.creation_date: |
|
372 |
self.w(u'<span>%s</span> ' % _('created on')) |
|
373 |
self.w(u'<span class="value">%s</span>' |
|
374 |
% self.format_date(entity.creation_date)) |
|
375 |
if entity.creator: |
|
376 |
self.w(u' <span>%s</span> ' % _('by')) |
|
377 |
self.w(u'<span class="value">%s</span>' % entity.creator.name()) |
|
378 |
self.w(u'</div>') |
|
379 |
||
380 |
||
381 |
# new default views for finner control in general views , to use instead of |
|
382 |
# oneline / secondary |
|
383 |
||
384 |
class InContextTextView(TextView): |
|
385 |
id = 'textincontext' |
|
386 |
title = None # not listed as a possible view |
|
387 |
def cell_call(self, row, col): |
|
388 |
entity = self.entity(row, col) |
|
389 |
self.w(entity.dc_title()) |
|
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
390 |
|
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
391 |
|
0 | 392 |
class OutOfContextTextView(InContextTextView): |
393 |
id = 'textoutofcontext' |
|
394 |
||
395 |
def cell_call(self, row, col): |
|
396 |
entity = self.entity(row, col) |
|
397 |
self.w(entity.dc_long_title()) |
|
398 |
||
399 |
||
400 |
class InContextView(EntityView): |
|
401 |
id = 'incontext' |
|
402 |
||
403 |
def cell_call(self, row, col): |
|
404 |
entity = self.entity(row, col) |
|
405 |
desc = cut(entity.dc_description(), 50) |
|
406 |
self.w(u'<a href="%s" title="%s">' % (html_escape(entity.absolute_url()), |
|
407 |
html_escape(desc))) |
|
408 |
self.w(html_escape(self.view('textincontext', self.rset, row=row, col=col))) |
|
409 |
self.w(u'</a>') |
|
410 |
||
411 |
||
412 |
class OutOfContextView(EntityView): |
|
413 |
id = 'outofcontext' |
|
414 |
||
415 |
def cell_call(self, row, col): |
|
416 |
self.w(u'<a href="%s">' % self.entity(row, col).absolute_url()) |
|
417 |
self.w(html_escape(self.view('textoutofcontext', self.rset, row=row, col=col))) |
|
418 |
self.w(u'</a>') |
|
419 |
||
420 |
||
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
421 |
# list views ################################################################## |
0 | 422 |
|
423 |
class ListView(EntityView): |
|
424 |
id = 'list' |
|
425 |
title = _('list') |
|
426 |
item_vid = 'listitem' |
|
427 |
||
428 |
def call(self, klass=None, title=None, subvid=None, listid=None, **kwargs): |
|
429 |
"""display a list of entities by calling their <item_vid> view |
|
430 |
|
|
431 |
:param listid: the DOM id to use for the root element |
|
432 |
""" |
|
433 |
if subvid is None and 'subvid' in self.req.form: |
|
434 |
subvid = self.req.form.pop('subvid') # consume it |
|
435 |
if listid: |
|
436 |
listid = u' id="%s"' % listid |
|
437 |
else: |
|
438 |
listid = u'' |
|
439 |
if title: |
|
440 |
self.w(u'<div%s class="%s"><h4>%s</h4>\n' % (listid, klass or 'section', title)) |
|
441 |
self.w(u'<ul>\n') |
|
442 |
else: |
|
443 |
self.w(u'<ul%s class="%s">\n' % (listid, klass or 'section')) |
|
444 |
for i in xrange(self.rset.rowcount): |
|
445 |
self.cell_call(row=i, col=0, vid=subvid, **kwargs) |
|
446 |
self.w(u'</ul>\n') |
|
447 |
if title: |
|
448 |
self.w(u'</div>\n') |
|
449 |
||
450 |
def cell_call(self, row, col=0, vid=None, **kwargs): |
|
451 |
self.w(u'<li>') |
|
452 |
self.wview(self.item_vid, self.rset, row=row, col=col, vid=vid, **kwargs) |
|
453 |
self.w(u'</li>\n') |
|
454 |
||
455 |
def url(self): |
|
456 |
"""overrides url method so that by default, the view list is called |
|
457 |
with sorted entities |
|
458 |
""" |
|
459 |
coltypes = self.rset.column_types(0) |
|
460 |
# don't want to generate the rql if there is some restriction on |
|
461 |
# something else than the entity type |
|
462 |
if len(coltypes) == 1: |
|
463 |
# XXX norestriction is not correct here. For instance, in cases like |
|
464 |
# Any P,N WHERE P is Project, P name N |
|
465 |
# norestriction should equal True |
|
466 |
restr = self.rset.syntax_tree().children[0].where |
|
467 |
norestriction = (isinstance(restr, nodes.Relation) and |
|
468 |
restr.is_types_restriction()) |
|
469 |
if norestriction: |
|
470 |
etype = iter(coltypes).next() |
|
471 |
return self.build_url(etype.lower(), vid=self.id) |
|
472 |
if len(self.rset) == 1: |
|
473 |
entity = self.rset.get_entity(0, 0) |
|
474 |
return self.build_url(entity.rest_path(), vid=self.id) |
|
475 |
return self.build_url(rql=self.rset.printable_rql(), vid=self.id) |
|
476 |
||
477 |
||
478 |
class ListItemView(EntityView): |
|
479 |
id = 'listitem' |
|
480 |
||
481 |
@property |
|
482 |
def redirect_vid(self): |
|
483 |
if self.req.search_state[0] == 'normal': |
|
484 |
return 'outofcontext' |
|
485 |
return 'outofcontext-search' |
|
486 |
||
487 |
def cell_call(self, row, col, vid=None, **kwargs): |
|
488 |
if not vid: |
|
489 |
vid = self.redirect_vid |
|
490 |
try: |
|
491 |
self.wview(vid, self.rset, row=row, col=col, **kwargs) |
|
492 |
except NoSelectableObject: |
|
493 |
if vid == self.redirect_vid: |
|
494 |
raise |
|
495 |
self.wview(self.redirect_vid, self.rset, row=row, col=col, **kwargs) |
|
496 |
||
497 |
||
498 |
class SimpleListView(ListItemView): |
|
499 |
"""list without bullets""" |
|
500 |
id = 'simplelist' |
|
501 |
redirect_vid = 'incontext' |
|
502 |
||
503 |
||
504 |
class CSVView(SimpleListView): |
|
505 |
id = 'csv' |
|
506 |
redirect_vid = 'incontext' |
|
507 |
||
508 |
def call(self, **kwargs): |
|
509 |
rset = self.rset |
|
510 |
for i in xrange(len(rset)): |
|
511 |
self.cell_call(i, 0, vid=kwargs.get('vid')) |
|
512 |
if i < rset.rowcount-1: |
|
513 |
self.w(u", ") |
|
514 |
||
515 |
||
516 |
class TreeItemView(ListItemView): |
|
517 |
id = 'treeitem' |
|
518 |
||
519 |
def cell_call(self, row, col): |
|
520 |
self.wview('incontext', self.rset, row=row, col=col) |
|
521 |
||
522 |
||
523 |
class TextSearchResultView(EntityView): |
|
524 |
"""this view is used to display full-text search |
|
525 |
||
526 |
It tries to highlight part of data where the search word appears. |
|
527 |
||
528 |
XXX: finish me (fixed line width, fixed number of lines, CSS, etc.) |
|
529 |
""" |
|
530 |
id = 'tsearch' |
|
531 |
||
532 |
def cell_call(self, row, col, **kwargs): |
|
533 |
entity = self.complete_entity(row, col) |
|
534 |
self.w(entity.view('incontext')) |
|
535 |
searched = self.rset.searched_text() |
|
536 |
if searched is None: |
|
537 |
return |
|
538 |
searched = searched.lower() |
|
539 |
highlighted = '<b>%s</b>' % searched |
|
540 |
for attr in entity.e_schema.indexable_attributes(): |
|
541 |
try: |
|
542 |
value = html_escape(entity.printable_value(attr, format='text/plain').lower()) |
|
543 |
except TransformError, ex: |
|
544 |
continue |
|
545 |
except: |
|
546 |
continue |
|
547 |
if searched in value: |
|
548 |
contexts = [] |
|
549 |
for ctx in value.split(searched): |
|
550 |
if len(ctx) > 30: |
|
551 |
contexts.append(u'...' + ctx[-30:]) |
|
552 |
else: |
|
553 |
contexts.append(ctx) |
|
554 |
value = u'\n' + highlighted.join(contexts) |
|
555 |
self.w(value.replace('\n', '<br/>')) |
|
556 |
||
557 |
||
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
558 |
class TooltipView(EntityView): |
0 | 559 |
"""A entity view used in a tooltip""" |
560 |
id = 'tooltip' |
|
561 |
def cell_call(self, row, col): |
|
562 |
self.wview('oneline', self.rset, row=row, col=col) |
|
563 |
||
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
564 |
|
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
565 |
# XXX bw compat |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
566 |
|
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
567 |
from logilab.common.deprecation import class_moved |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
568 |
|
0 | 569 |
try: |
570 |
from cubicweb.web.views.tableview import TableView |
|
571 |
TableView = class_moved(TableView) |
|
572 |
except ImportError: |
|
573 |
pass # gae has no tableview module (yet) |
|
824
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
574 |
|
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
575 |
from cubicweb.web.views import boxes, xmlrss |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
576 |
SideBoxView = class_moved(boxes.SideBoxView) |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
577 |
XmlView = class_moved(xmlrss.XmlView) |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
578 |
XmlItemView = class_moved(xmlrss.XmlItemView) |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
579 |
XmlRsetView = class_moved(xmlrss.XmlRsetView) |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
580 |
RssView = class_moved(xmlrss.RssView) |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
581 |
RssItemView = class_moved(xmlrss.RssItemView) |
a5e6acffde30
merge, split baseviews (new csvexport, xmlrss and editviews modules)
sylvain.thenault@logilab.fr
parents:
795
diff
changeset
|
582 |