web/views/tableview.py
changeset 3451 6b46d73823f5
parent 3377 dd9d292b6a6d
child 3457 0924d0d08d60
equal deleted inserted replaced
3448:495862266785 3451:6b46d73823f5
    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'