26 title = _('table') |
26 title = _('table') |
27 finalview = 'final' |
27 finalview = 'final' |
28 |
28 |
29 def form_filter(self, divid, displaycols, displayactions, displayfilter, |
29 def form_filter(self, divid, displaycols, displayactions, displayfilter, |
30 hidden=True): |
30 hidden=True): |
31 rqlst = self.rset.syntax_tree() |
31 rqlst = self.cw_rset.syntax_tree() |
32 # union not yet supported |
32 # union not yet supported |
33 if len(rqlst.children) != 1: |
33 if len(rqlst.children) != 1: |
34 return () |
34 return () |
35 rqlst.save_state() |
35 rqlst.save_state() |
36 mainvar, baserql = prepare_facets_rqlst(rqlst, self.rset.args) |
36 mainvar, baserql = prepare_facets_rqlst(rqlst, self.cw_rset.args) |
37 wdgs = [facet.get_widget() for facet in self.vreg['facets'].poss_visible_objects( |
37 wdgs = [facet.get_widget() for facet in self._cw.vreg['facets'].poss_visible_objects( |
38 self.req, rset=self.rset, context='tablefilter', |
38 self._cw, rset=self.cw_rset, context='tablefilter', |
39 filtered_variable=mainvar)] |
39 filtered_variable=mainvar)] |
40 wdgs = [wdg for wdg in wdgs if wdg is not None] |
40 wdgs = [wdg for wdg in wdgs if wdg is not None] |
41 rqlst.recover() |
41 rqlst.recover() |
42 if wdgs: |
42 if wdgs: |
43 self._generate_form(divid, baserql, wdgs, hidden, |
43 self._generate_form(divid, baserql, wdgs, hidden, |
49 |
49 |
50 def _generate_form(self, divid, baserql, fwidgets, hidden=True, vidargs={}): |
50 def _generate_form(self, divid, baserql, fwidgets, hidden=True, vidargs={}): |
51 """display a form to filter table's content. This should only |
51 """display a form to filter table's content. This should only |
52 occurs when a context eid is given |
52 occurs when a context eid is given |
53 """ |
53 """ |
54 self.req.add_css('cubicweb.facets.css') |
54 self._cw.add_css('cubicweb.facets.css') |
55 self.req.add_js( ('cubicweb.ajax.js', 'cubicweb.facets.js')) |
55 self._cw.add_js( ('cubicweb.ajax.js', 'cubicweb.facets.js')) |
56 # drop False / None values from vidargs |
56 # drop False / None values from vidargs |
57 vidargs = dict((k, v) for k, v in vidargs.iteritems() if v) |
57 vidargs = dict((k, v) for k, v in vidargs.iteritems() if v) |
58 self.w(u'<form method="post" cubicweb:facetargs="%s" action="">' % |
58 self.w(u'<form method="post" cubicweb:facetargs="%s" action="">' % |
59 xml_escape(dumps([divid, 'table', False, vidargs]))) |
59 xml_escape(dumps([divid, 'table', False, vidargs]))) |
60 self.w(u'<fieldset id="%sForm" class="%s">' % (divid, hidden and 'hidden' or '')) |
60 self.w(u'<fieldset id="%sForm" class="%s">' % (divid, hidden and 'hidden' or '')) |
75 |
75 |
76 def main_var_index(self): |
76 def main_var_index(self): |
77 """returns the index of the first non-attribute variable among the RQL |
77 """returns the index of the first non-attribute variable among the RQL |
78 selected variables |
78 selected variables |
79 """ |
79 """ |
80 eschema = self.vreg.schema.eschema |
80 eschema = self._cw.vreg.schema.eschema |
81 for i, etype in enumerate(self.rset.description[0]): |
81 for i, etype in enumerate(self.cw_rset.description[0]): |
82 try: |
82 try: |
83 if not eschema(etype).is_final(): |
83 if not eschema(etype).is_final(): |
84 return i |
84 return i |
85 except KeyError: # XXX possible? |
85 except KeyError: # XXX possible? |
86 continue |
86 continue |
87 return None |
87 return None |
88 |
88 |
89 def displaycols(self, displaycols): |
89 def displaycols(self, displaycols): |
90 if displaycols is None: |
90 if displaycols is None: |
91 if 'displaycols' in self.req.form: |
91 if 'displaycols' in self._cw.form: |
92 displaycols = [int(idx) for idx in self.req.form['displaycols']] |
92 displaycols = [int(idx) for idx in self._cw.form['displaycols']] |
93 else: |
93 else: |
94 displaycols = range(len(self.rset.syntax_tree().children[0].selection)) |
94 displaycols = range(len(self.cw_rset.syntax_tree().children[0].selection)) |
95 return displaycols |
95 return displaycols |
96 |
96 |
97 def call(self, title=None, subvid=None, displayfilter=None, headers=None, |
97 def call(self, title=None, subvid=None, displayfilter=None, headers=None, |
98 displaycols=None, displayactions=None, actions=(), divid=None, |
98 displaycols=None, displayactions=None, actions=(), divid=None, |
99 cellvids=None, cellattrs=None, mainindex=None): |
99 cellvids=None, cellattrs=None, mainindex=None): |
102 :param title: title added before table |
102 :param title: title added before table |
103 :param subvid: cell view |
103 :param subvid: cell view |
104 :param displayfilter: filter that selects rows to display |
104 :param displayfilter: filter that selects rows to display |
105 :param headers: columns' titles |
105 :param headers: columns' titles |
106 """ |
106 """ |
107 req = self.req |
107 req = self._cw |
108 req.add_js('jquery.tablesorter.js') |
108 req.add_js('jquery.tablesorter.js') |
109 req.add_css(('cubicweb.tablesorter.css', 'cubicweb.tableview.css')) |
109 req.add_css(('cubicweb.tablesorter.css', 'cubicweb.tableview.css')) |
110 # compute label first since the filter form may remove some necessary |
110 # compute label first since the filter form may remove some necessary |
111 # information from the rql syntax tree |
111 # information from the rql syntax tree |
112 if mainindex is None: |
112 if mainindex is None: |
113 mainindex = self.main_var_index() |
113 mainindex = self.main_var_index() |
114 computed_labels = self.columns_labels(mainindex) |
114 computed_labels = self.columns_labels(mainindex) |
115 hidden = True |
115 hidden = True |
116 if not subvid and 'subvid' in req.form: |
116 if not subvid and 'subvid' in req.form: |
117 subvid = req.form.pop('subvid') |
117 subvid = req.form.pop('subvid') |
118 divid = divid or req.form.get('divid') or 'rs%s' % make_uid(id(self.rset)) |
118 divid = divid or req.form.get('divid') or 'rs%s' % make_uid(id(self.cw_rset)) |
119 actions = list(actions) |
119 actions = list(actions) |
120 if mainindex is None: |
120 if mainindex is None: |
121 displayfilter, displayactions = False, False |
121 displayfilter, displayactions = False, False |
122 else: |
122 else: |
123 if displayfilter is None and 'displayfilter' in req.form: |
123 if displayfilter is None and 'displayfilter' in req.form: |
142 displayactions) |
142 displayactions) |
143 elif displayfilter: |
143 elif displayfilter: |
144 actions += self.show_hide_actions(divid, True) |
144 actions += self.show_hide_actions(divid, True) |
145 self.w(u'<div id="%s"' % divid) |
145 self.w(u'<div id="%s"' % divid) |
146 if displayactions: |
146 if displayactions: |
147 actionsbycat = self.vreg['actions'].possible_actions(req, self.rset) |
147 actionsbycat = self._cw.vreg['actions'].possible_actions(req, self.cw_rset) |
148 for action in actionsbycat.get('mainactions', ()): |
148 for action in actionsbycat.get('mainactions', ()): |
149 for action in action.actual_actions(): |
149 for action in action.actual_actions(): |
150 actions.append( (action.url(), req._(action.title), |
150 actions.append( (action.url(), req._(action.title), |
151 action.html_class(), None) ) |
151 action.html_class(), None) ) |
152 self.w(u' cubicweb:displayactions="1">') # close <div tag |
152 self.w(u' cubicweb:displayactions="1">') # close <div tag |
168 |
168 |
169 def show_hide_actions(self, divid, currentlydisplayed=False): |
169 def show_hide_actions(self, divid, currentlydisplayed=False): |
170 showhide = u';'.join(toggle_action('%s%s' % (divid, what))[11:] |
170 showhide = u';'.join(toggle_action('%s%s' % (divid, what))[11:] |
171 for what in ('Form', 'Show', 'Hide', 'Actions')) |
171 for what in ('Form', 'Show', 'Hide', 'Actions')) |
172 showhide = 'javascript:' + showhide |
172 showhide = 'javascript:' + showhide |
173 showlabel = self.req._('show filter form') |
173 showlabel = self._cw._('show filter form') |
174 hidelabel = self.req._('hide filter form') |
174 hidelabel = self._cw._('hide filter form') |
175 if currentlydisplayed: |
175 if currentlydisplayed: |
176 return [(showhide, showlabel, 'hidden', '%sShow' % divid), |
176 return [(showhide, showlabel, 'hidden', '%sShow' % divid), |
177 (showhide, hidelabel, None, '%sHide' % divid)] |
177 (showhide, hidelabel, None, '%sHide' % divid)] |
178 return [(showhide, showlabel, None, '%sShow' % divid), |
178 return [(showhide, showlabel, None, '%sShow' % divid), |
179 (showhide, hidelabel, 'hidden', '%sHide' % divid)] |
179 (showhide, hidelabel, 'hidden', '%sHide' % divid)] |
180 |
180 |
181 def render_actions(self, divid, actions): |
181 def render_actions(self, divid, actions): |
182 box = MenuWidget('', 'tableActionsBox', _class='', islist=False) |
182 box = MenuWidget('', 'tableActionsBox', _class='', islist=False) |
183 label = '<img src="%s" alt="%s"/>' % ( |
183 label = '<img src="%s" alt="%s"/>' % ( |
184 self.req.datadir_url + 'liveclipboard-icon.png', |
184 self._cw.datadir_url + 'liveclipboard-icon.png', |
185 xml_escape(self.req._('action(s) on this selection'))) |
185 xml_escape(self._cw._('action(s) on this selection'))) |
186 menu = PopupBoxMenu(label, isitem=False, link_class='actionsBox', |
186 menu = PopupBoxMenu(label, isitem=False, link_class='actionsBox', |
187 ident='%sActions' % divid) |
187 ident='%sActions' % divid) |
188 box.append(menu) |
188 box.append(menu) |
189 for url, label, klass, ident in actions: |
189 for url, label, klass, ident in actions: |
190 menu.append(BoxLink(url, label, klass, ident=ident, escape=True)) |
190 menu.append(BoxLink(url, label, klass, ident=ident, escape=True)) |
199 continue |
199 continue |
200 # compute column header |
200 # compute column header |
201 if headers is not None: |
201 if headers is not None: |
202 label = headers[displaycols.index(colindex)] |
202 label = headers[displaycols.index(colindex)] |
203 if colindex == mainindex: |
203 if colindex == mainindex: |
204 label += ' (%s)' % self.rset.rowcount |
204 label += ' (%s)' % self.cw_rset.rowcount |
205 column = TableColumn(label, colindex) |
205 column = TableColumn(label, colindex) |
206 coltype = self.rset.description[0][colindex] |
206 coltype = self.cw_rset.description[0][colindex] |
207 # compute column cell view (if coltype is None, it's a left outer |
207 # compute column cell view (if coltype is None, it's a left outer |
208 # join, use the default non final subvid) |
208 # join, use the default non final subvid) |
209 if cellvids and colindex in cellvids: |
209 if cellvids and colindex in cellvids: |
210 column.append_renderer(cellvids[colindex], colindex) |
210 column.append_renderer(cellvids[colindex], colindex) |
211 elif coltype is not None and self.schema.eschema(coltype).is_final(): |
211 elif coltype is not None and self._cw.schema.eschema(coltype).is_final(): |
212 column.append_renderer(self.finalview, colindex) |
212 column.append_renderer(self.finalview, colindex) |
213 else: |
213 else: |
214 column.append_renderer(subvid or 'incontext', colindex) |
214 column.append_renderer(subvid or 'incontext', colindex) |
215 if cellattrs and colindex in cellattrs: |
215 if cellattrs and colindex in cellattrs: |
216 for name, value in cellattrs[colindex].iteritems(): |
216 for name, value in cellattrs[colindex].iteritems(): |
219 columns.append(column) |
219 columns.append(column) |
220 return columns |
220 return columns |
221 |
221 |
222 |
222 |
223 def render_cell(self, cellvid, row, col, w): |
223 def render_cell(self, cellvid, row, col, w): |
224 self.view('cell', self.rset, row=row, col=col, cellvid=cellvid, w=w) |
224 self._cw.view('cell', self.cw_rset, row=row, col=col, cellvid=cellvid, w=w) |
225 |
225 |
226 def get_rows(self): |
226 def get_rows(self): |
227 return self.rset |
227 return self.cw_rset |
228 |
228 |
229 @htmlescape |
229 @htmlescape |
230 @jsonize |
230 @jsonize |
231 @limitsize(10) |
231 @limitsize(10) |
232 def sortvalue(self, row, col): |
232 def sortvalue(self, row, col): |
233 # XXX it might be interesting to try to limit value's |
233 # XXX it might be interesting to try to limit value's |
234 # length as much as possible (e.g. by returning the 10 |
234 # length as much as possible (e.g. by returning the 10 |
235 # first characters of a string) |
235 # first characters of a string) |
236 val = self.rset[row][col] |
236 val = self.cw_rset[row][col] |
237 if val is None: |
237 if val is None: |
238 return u'' |
238 return u'' |
239 etype = self.rset.description[row][col] |
239 etype = self.cw_rset.description[row][col] |
240 if self.schema.eschema(etype).is_final(): |
240 if self._cw.schema.eschema(etype).is_final(): |
241 entity, rtype = self.rset.related_entity(row, col) |
241 entity, rtype = self.cw_rset.related_entity(row, col) |
242 if entity is None: |
242 if entity is None: |
243 return val # remove_html_tags() ? |
243 return val # remove_html_tags() ? |
244 return entity.sortvalue(rtype) |
244 return entity.sortvalue(rtype) |
245 entity = self.rset.get_entity(row, col) |
245 entity = self.cw_rset.get_entity(row, col) |
246 return entity.sortvalue() |
246 return entity.sortvalue() |
247 |
247 |
248 |
248 |
249 class EditableTableView(TableView): |
249 class EditableTableView(TableView): |
250 __regid__ = 'editable-table' |
250 __regid__ = 'editable-table' |
259 def cell_call(self, row, col, cellvid=None): |
259 def cell_call(self, row, col, cellvid=None): |
260 """ |
260 """ |
261 :param row, col: indexes locating the cell value in view's result set |
261 :param row, col: indexes locating the cell value in view's result set |
262 :param cellvid: cell view (defaults to 'outofcontext') |
262 :param cellvid: cell view (defaults to 'outofcontext') |
263 """ |
263 """ |
264 etype, val = self.rset.description[row][col], self.rset[row][col] |
264 etype, val = self.cw_rset.description[row][col], self.cw_rset[row][col] |
265 if val is not None and not self.schema.eschema(etype).is_final(): |
265 if val is not None and not self._cw.schema.eschema(etype).is_final(): |
266 e = self.rset.get_entity(row, col) |
266 e = self.cw_rset.get_entity(row, col) |
267 e.view(cellvid or 'outofcontext', w=self.w) |
267 e.view(cellvid or 'outofcontext', w=self.w) |
268 elif val is None: |
268 elif val is None: |
269 # This is usually caused by a left outer join and in that case, |
269 # This is usually caused by a left outer join and in that case, |
270 # regular views will most certainly fail if they don't have |
270 # regular views will most certainly fail if they don't have |
271 # a real eid |
271 # a real eid |
272 self.wview('final', self.rset, row=row, col=col) |
272 self.wview('final', self.cw_rset, row=row, col=col) |
273 else: |
273 else: |
274 self.wview(cellvid or 'final', self.rset, 'null', row=row, col=col) |
274 self.wview(cellvid or 'final', self.cw_rset, 'null', row=row, col=col) |
275 |
275 |
276 |
276 |
277 class InitialTableView(TableView): |
277 class InitialTableView(TableView): |
278 """same display as table view but consider two rql queries : |
278 """same display as table view but consider two rql queries : |
279 |
279 |
293 title = None |
293 title = None |
294 |
294 |
295 def call(self, title=None, subvid=None, headers=None, divid=None, |
295 def call(self, title=None, subvid=None, headers=None, divid=None, |
296 displaycols=None, displayactions=None, mainindex=None): |
296 displaycols=None, displayactions=None, mainindex=None): |
297 """Dumps a table displaying a composite query""" |
297 """Dumps a table displaying a composite query""" |
298 actrql = self.req.form['actualrql'] |
298 actrql = self._cw.form['actualrql'] |
299 self.req.ensure_ro_rql(actrql) |
299 self._cw.ensure_ro_rql(actrql) |
300 displaycols = self.displaycols(displaycols) |
300 displaycols = self.displaycols(displaycols) |
301 if displayactions is None and 'displayactions' in self.req.form: |
301 if displayactions is None and 'displayactions' in self._cw.form: |
302 displayactions = True |
302 displayactions = True |
303 if divid is None and 'divid' in self.req.form: |
303 if divid is None and 'divid' in self._cw.form: |
304 divid = self.req.form['divid'] |
304 divid = self._cw.form['divid'] |
305 self.w(u'<div class="section">') |
305 self.w(u'<div class="section">') |
306 if not title and 'title' in self.req.form: |
306 if not title and 'title' in self._cw.form: |
307 # pop title so it's not displayed by the table view as well |
307 # pop title so it's not displayed by the table view as well |
308 title = self.req.form.pop('title') |
308 title = self._cw.form.pop('title') |
309 if title: |
309 if title: |
310 self.w(u'<h2>%s</h2>\n' % title) |
310 self.w(u'<h2>%s</h2>\n' % title) |
311 if mainindex is None: |
311 if mainindex is None: |
312 mainindex = self.main_var_index() |
312 mainindex = self.main_var_index() |
313 if mainindex is not None: |
313 if mainindex is not None: |
314 actions = self.form_filter(divid, displaycols, displayactions, True) |
314 actions = self.form_filter(divid, displaycols, displayactions, True) |
315 else: |
315 else: |
316 actions = () |
316 actions = () |
317 if not subvid and 'subvid' in self.req.form: |
317 if not subvid and 'subvid' in self._cw.form: |
318 subvid = self.req.form.pop('subvid') |
318 subvid = self._cw.form.pop('subvid') |
319 self.view('table', self.req.execute(actrql), |
319 self._cw.view('table', self._cw.execute(actrql), |
320 'noresult', w=self.w, displayfilter=False, subvid=subvid, |
320 'noresult', w=self.w, displayfilter=False, subvid=subvid, |
321 displayactions=displayactions, displaycols=displaycols, |
321 displayactions=displayactions, displaycols=displaycols, |
322 actions=actions, headers=headers, divid=divid) |
322 actions=actions, headers=headers, divid=divid) |
323 self.w(u'</div>\n') |
323 self.w(u'</div>\n') |
324 |
324 |
325 |
325 |
326 class EditableInitialTableTableView(InitialTableView): |
326 class EditableInitialTableTableView(InitialTableView): |
327 __regid__ = 'editable-initialtable' |
327 __regid__ = 'editable-initialtable' |