# HG changeset patch # User Sylvain Thénault # Date 1309520385 -7200 # Node ID 75d208ab8444df54dc82544f3b223eeeca9475fd # Parent 9cbf4c86f57aa361e37bb452a02c534868e8a73a [facets] factorize table filter form / facets box generation, moving out filter form from the table view so it's usable from other views. Closes #1794009 diff -r 9cbf4c86f57a -r 75d208ab8444 web/facet.py --- a/web/facet.py Fri Jul 01 12:48:39 2011 +0200 +++ b/web/facet.py Fri Jul 01 13:39:45 2011 +0200 @@ -55,6 +55,7 @@ from logilab.common.decorators import cached from logilab.common.date import datetime2ticks, ustrftime, ticks2datetime from logilab.common.compat import all +from logilab.common.deprecation import deprecated from rql import parse, nodes, utils @@ -73,6 +74,21 @@ context=iter(ptypes).next()) return display_name(facet._cw, facet.rtype, form=facet.role) +def filtered_variable(rqlst): + vref = rqlst.selection[0].iget_nodes(nodes.VariableRef).next() + return vref.variable + +def get_facet(req, facetid, rqlst, mainvar): + return req.vreg['facets'].object_by_id(facetid, req, rqlst=rqlst, + filtered_variable=mainvar) + +@deprecated('[3.13] filter_hiddens moved to cubicweb.web.views.facets with ' + 'slightly modified prototype') +def filter_hiddens(w, **kwargs): + from cubicweb.web.views.facets import filter_hiddens + return filter_hiddens(w, wdgs=kwargs.pop('facets')) + + ## rqlst manipulation functions used by facets ################################ def prepare_facets_rqlst(rqlst, args=None): @@ -84,8 +100,7 @@ * set DISTINCT * unset LIMIT/OFFSET """ - if len(rqlst.children) > 1: - raise NotImplementedError('FIXME: union not yet supported') + assert len(rqlst.children) == 1, 'FIXME: union not yet supported' select = rqlst.children[0] mainvar = filtered_variable(select) select.set_limit(None) @@ -104,20 +119,102 @@ select.set_distinct(True) return mainvar, baserql -def filtered_variable(rqlst): - vref = rqlst.selection[0].iget_nodes(nodes.VariableRef).next() - return vref.variable + +def prepare_vocabulary_rqlst(rqlst, mainvar, rtype, role, + select_target_entity=True): + """prepare a syntax tree to generate a filter vocabulary rql using the given + relation: + * create a variable to filter on this relation + * add the relation + * add the new variable to GROUPBY clause if necessary + * add the new variable to the selection + """ + newvar = _add_rtype_relation(rqlst, mainvar, rtype, role)[0] + if select_target_entity: + if rqlst.groupby: + rqlst.add_group_var(newvar) + rqlst.add_selected(newvar) + # add is restriction if necessary + if mainvar.stinfo['typerel'] is None: + etypes = frozenset(sol[mainvar.name] for sol in rqlst.solutions) + rqlst.add_type_restriction(mainvar, etypes) + return newvar -def get_facet(req, facetid, rqlst, mainvar): - return req.vreg['facets'].object_by_id(facetid, req, rqlst=rqlst, - filtered_variable=mainvar) +def insert_attr_select_relation(rqlst, mainvar, rtype, role, attrname, + sortfuncname=None, sortasc=True, + select_target_entity=True): + """modify a syntax tree to : + * link a new variable to `mainvar` through `rtype` (where mainvar has `role`) + * retrieve only the newly inserted variable and its `attrname` + + Sorting: + * on `attrname` ascendant (`sortasc`=True) or descendant (`sortasc`=False) + * on `sortfuncname`(`attrname`) if `sortfuncname` is specified + * no sort if `sortasc` is None + """ + cleanup_rqlst(rqlst, mainvar) + var = prepare_vocabulary_rqlst(rqlst, mainvar, rtype, role, + select_target_entity) + attrvar = rqlst.make_variable() + rqlst.add_relation(var, attrname, attrvar) + # if query is grouped, we have to add the attribute variable + if rqlst.groupby: + if not attrvar in rqlst.groupby: + rqlst.add_group_var(attrvar) + if sortasc is not None: + _set_orderby(rqlst, attrvar, sortasc, sortfuncname) + # add attribute variable to selection + rqlst.add_selected(attrvar) + return var -def filter_hiddens(w, **kwargs): - for key, val in kwargs.items(): - w(u'' % ( - key, xml_escape(val))) +def cleanup_rqlst(rqlst, mainvar): + """cleanup tree from unnecessary restrictions: + * attribute selection + * optional relations linked to the main variable + * mandatory relations linked to the main variable + """ + if rqlst.where is None: + return + schema = rqlst.root.schema + toremove = set() + vargraph = deepcopy(rqlst.vargraph) # graph representing links between variable + for rel in rqlst.where.get_nodes(nodes.Relation): + ovar = _may_be_removed(rel, schema, mainvar) + if ovar is not None: + toremove.add(ovar) + removed = set() + while toremove: + trvar = toremove.pop() + trvarname = trvar.name + # remove paths using this variable from the graph + linkedvars = vargraph.pop(trvarname) + for ovarname in linkedvars: + vargraph[ovarname].remove(trvarname) + # remove relation using this variable + for rel in trvar.stinfo['relations']: + if rel in removed: + # already removed + continue + rqlst.remove_node(rel) + removed.add(rel) + rel = trvar.stinfo['typerel'] + if rel is not None and not rel in removed: + rqlst.remove_node(rel) + removed.add(rel) + # cleanup groupby clause + if rqlst.groupby: + for vref in rqlst.groupby[:]: + if vref.name == trvarname: + rqlst.remove_group_var(vref) + # we can also remove all variables which are linked to this variable + # and have no path to the main variable + for ovarname in linkedvars: + if ovarname == mainvar.name: + continue + if not has_path(vargraph, ovarname, mainvar.name): + toremove.add(rqlst.defined_vars[ovarname]) def _may_be_removed(rel, schema, mainvar): @@ -188,26 +285,6 @@ rrel = nodes.make_constant_restriction(restrvar, 'eid', value, 'Int') rel.parent.replace(rel, nodes.And(rel, rrel)) -def _prepare_vocabulary_rqlst(rqlst, mainvar, rtype, role, - select_target_entity=True): - """prepare a syntax tree to generate a filter vocabulary rql using the given - relation: - * create a variable to filter on this relation - * add the relation - * add the new variable to GROUPBY clause if necessary - * add the new variable to the selection - """ - newvar = _add_rtype_relation(rqlst, mainvar, rtype, role)[0] - if select_target_entity: - if rqlst.groupby: - rqlst.add_group_var(newvar) - rqlst.add_selected(newvar) - # add is restriction if necessary - if mainvar.stinfo['typerel'] is None: - etypes = frozenset(sol[mainvar.name] for sol in rqlst.solutions) - rqlst.add_type_restriction(mainvar, etypes) - return newvar - def _remove_relation(rqlst, rel, var): """remove a constraint relation from the syntax tree""" # remove the relation @@ -235,79 +312,10 @@ term = nodes.SortTerm(sortfunc, sortasc) rqlst.add_sort_term(term) -def insert_attr_select_relation(rqlst, mainvar, rtype, role, attrname, - sortfuncname=None, sortasc=True, - select_target_entity=True): - """modify a syntax tree to : - * link a new variable to `mainvar` through `rtype` (where mainvar has `role`) - * retrieve only the newly inserted variable and its `attrname` - Sorting: - * on `attrname` ascendant (`sortasc`=True) or descendant (`sortasc`=False) - * on `sortfuncname`(`attrname`) if `sortfuncname` is specified - * no sort if `sortasc` is None - """ - _cleanup_rqlst(rqlst, mainvar) - var = _prepare_vocabulary_rqlst(rqlst, mainvar, rtype, role, - select_target_entity) - attrvar = rqlst.make_variable() - rqlst.add_relation(var, attrname, attrvar) - # if query is grouped, we have to add the attribute variable - if rqlst.groupby: - if not attrvar in rqlst.groupby: - rqlst.add_group_var(attrvar) - if sortasc is not None: - _set_orderby(rqlst, attrvar, sortasc, sortfuncname) - # add attribute variable to selection - rqlst.add_selected(attrvar) - return var - -def _cleanup_rqlst(rqlst, mainvar): - """cleanup tree from unnecessary restriction: - * attribute selection - * optional relations linked to the main variable - * mandatory relations linked to the main variable - """ - if rqlst.where is None: - return - schema = rqlst.root.schema - toremove = set() - vargraph = deepcopy(rqlst.vargraph) # graph representing links between variable - for rel in rqlst.where.get_nodes(nodes.Relation): - ovar = _may_be_removed(rel, schema, mainvar) - if ovar is not None: - toremove.add(ovar) - removed = set() - while toremove: - trvar = toremove.pop() - trvarname = trvar.name - # remove paths using this variable from the graph - linkedvars = vargraph.pop(trvarname) - for ovarname in linkedvars: - vargraph[ovarname].remove(trvarname) - # remove relation using this variable - for rel in trvar.stinfo['relations']: - if rel in removed: - # already removed - continue - rqlst.remove_node(rel) - removed.add(rel) - rel = trvar.stinfo['typerel'] - if rel is not None and not rel in removed: - rqlst.remove_node(rel) - removed.add(rel) - # cleanup groupby clause - if rqlst.groupby: - for vref in rqlst.groupby[:]: - if vref.name == trvarname: - rqlst.remove_group_var(vref) - # we can also remove all variables which are linked to this variable - # and have no path to the main variable - for ovarname in linkedvars: - if ovarname == mainvar.name: - continue - if not has_path(vargraph, ovarname, mainvar.name): - toremove.add(rqlst.defined_vars[ovarname]) +_prepare_vocabulary_rqlst = deprecated('[3.13] renamed prepare_vocabulary_rqlst ')( + prepare_vocabulary_rqlst) +_cleanup_rqlst = deprecated('[3.13] renamed to cleanup_rqlst')(cleanup_rqlst) ## base facet classes ########################################################## @@ -605,10 +613,10 @@ rqlst = self.rqlst rqlst.save_state() try: - _cleanup_rqlst(rqlst, self.filtered_variable) + cleanup_rqlst(rqlst, self.filtered_variable) if self._select_target_entity: - _prepare_vocabulary_rqlst(rqlst, self.filtered_variable, self.rtype, - self.role, select_target_entity=True) + prepare_vocabulary_rqlst(rqlst, self.filtered_variable, self.rtype, + self.role, select_target_entity=True) else: insert_attr_select_relation( rqlst, self.filtered_variable, self.rtype, self.role, self.target_attr, @@ -877,8 +885,8 @@ rqlst.save_state() try: mainvar = self.filtered_variable - _cleanup_rqlst(rqlst, mainvar) - newvar = _prepare_vocabulary_rqlst(rqlst, mainvar, self.rtype, self.role) + cleanup_rqlst(rqlst, mainvar) + newvar = prepare_vocabulary_rqlst(rqlst, mainvar, self.rtype, self.role) _set_orderby(rqlst, newvar, self.sortasc, self.sortfunc) try: rset = self.rqlexec(rqlst.as_string(), self.cw_rset.args) diff -r 9cbf4c86f57a -r 75d208ab8444 web/views/facets.py --- a/web/views/facets.py Fri Jul 01 12:48:39 2011 +0200 +++ b/web/views/facets.py Fri Jul 01 13:39:45 2011 +0200 @@ -26,7 +26,41 @@ from cubicweb.selectors import (non_final_entity, multi_lines_rset, match_context_prop, yes, relation_possible) from cubicweb.utils import json_dumps -from cubicweb.web import component, facet +from cubicweb.web import component, facet as facetbase + +def facets(req, rset, context): + """return the base rql and a list of widgets for facets applying to the + given rset/context (cached version) + """ + try: + cache = req.__rset_facets + except AttributeError: + cache = req.__rset_facets = {} + try: + return cache[(rset, context)] + except KeyError: + facets = cache[(rset, context)] = _facets(req, rset, context) + return facets + +def _facets(req, rset, context): + """return the base rql and a list of widgets for facets applying to the + given rset/context + """ + # XXX done by selectors, though maybe necessary when rset has been hijacked + # (e.g. contextview_selector matched) + rqlst = rset.syntax_tree() + # union not yet supported + if len(rqlst.children) != 1: + return None, () + rqlst = rqlst.copy() + vreg = req.vreg + vreg.rqlhelper.annotate(rqlst) + mainvar, baserql = facetbase.prepare_facets_rqlst(rqlst, rset.args) + wdgs = [facet.get_widget() for facet in vreg['facets'].poss_visible_objects( + req, rset=rset, rqlst=rqlst.children[0], context=context, + filtered_variable=mainvar)] + return baserql, [wdg for wdg in wdgs if wdg is not None] + @objectify_selector def contextview_selector(cls, req, rset=None, row=None, col=None, view=None, @@ -35,28 +69,92 @@ return 1 return 0 +@objectify_selector +def has_facets(cls, req, rset=None, **kwargs): + if rset is None: + return 0 + return len(facets(req, rset, cls.__regid__)[1]) -class FilterBox(component.CtxComponent): + +def filter_hiddens(w, baserql, wdgs, **kwargs): + kwargs['facets'] = ','.join(wdg.facet.__regid__ for wdg in wdgs) + kwargs['baserql'] = baserql + for key, val in kwargs.items(): + w(u'' % ( + key, xml_escape(val))) + + +class FacetFilterMixIn(object): + needs_js = ['cubicweb.ajax.js', 'cubicweb.facets.js'] + needs_css = ['cubicweb.facets.css'] + roundcorners = True + + def generate_form(self, w, rset, divid, vid, vidargs, + paginate=False, cssclass='', **hiddens): + """display a form to filter some view's content""" + baserql, wdgs = facets(self._cw, rset, self.__regid__) + if not wdgs: # may happen in contextview_selector matched + return + self._cw.add_js(self.needs_js) + self._cw.add_css(self.needs_css) + self._cw.html_headers.define_var('facetLoadingMsg', + self._cw._('facet-loading-msg')) + if self.roundcorners: + self._cw.html_headers.add_onload( + 'jQuery(".facet").corner("tl br 10px");') + # drop False / None values from vidargs + vidargs = dict((k, v) for k, v in vidargs.iteritems() if v) + facetargs = xml_escape(json_dumps([divid, vid, paginate, vidargs])) + w(u'
' % (divid, cssclass, facetargs)) + w(u'
') + filter_hiddens(w, baserql, wdgs, **hiddens) + self.layout_widgets(w, self.sorted_widgets(wdgs)) + w(u'
\n') + w(u'
\n') + + def sorted_widgets(self, wdgs): + """sort widgets: by default sort by widget height, then according to + widget.order (the original widgets order) + """ + return sorted(wdgs, key=lambda x: x.height()) + + def layout_widgets(self, w, wdgs): + """layout widgets: by default simply render each of them + (i.e. succession of
) + """ + for wdg in wdgs: + wdg.render(w=w) + + +class FilterBox(FacetFilterMixIn, component.CtxComponent): """filter results of a query""" - __regid__ = 'facet.filters' - __select__ = ((non_final_entity() & multi_lines_rset()) - | contextview_selector()) + __regid__ = 'facet.filterbox' + __select__ = ((non_final_entity() & has_facets()) + | contextview_selector()) # can't use has_facets because of + # contextview mecanism context = 'left' # XXX doesn't support 'incontext', only 'left' or 'right' title = _('facet.filters') visible = True # functionality provided by the search box by default order = 1 - roundcorners = True - - needs_css = 'cubicweb.facets.css' - needs_js = ('cubicweb.ajax.js', 'cubicweb.facets.js') bk_linkbox_template = u'
%s
' - def facetargs(self): - """this method returns the list of extra arguments that should - be used by the facet - """ - return {} + def render(self, w, **kwargs): + req = self._cw + rset, vid, divid, paginate = self._get_context() + if len(rset) < 2: + return + if vid is None: + vid = req.form.get('vid') + if self.bk_linkbox_template and req.vreg.schema['Bookmark'].has_perm(req, 'add'): + w(self.bookmark_link(rset)) + hiddens = {} + for param in ('subvid', 'vtitle'): + if param in req.form: + hiddens[param] = req.form[param] + self.generate_form(w, rset, divid, vid, self.vidargs(), + paginate=paginate, **hiddens) def _get_context(self): view = self.cw_extra_kwargs.get('view') @@ -69,48 +167,6 @@ paginate = view and view.paginable return rset, vid, divid, paginate - def render(self, w, **kwargs): - req = self._cw - req.add_js( self.needs_js ) - req.add_css( self.needs_css) - req.html_headers.define_var('facetLoadingMsg', req._('facet-loading-msg')) - if self.roundcorners: - req.html_headers.add_onload('jQuery(".facet").corner("tl br 10px");') - rset, vid, divid, paginate = self._get_context() - # XXX done by selectors, though maybe necessary when rset has been hijacked - if rset.rowcount < 2: - return - rqlst = rset.syntax_tree() - # union not yet supported - if len(rqlst.children) != 1: - return () - rqlst = rqlst.copy() - req.vreg.rqlhelper.annotate(rqlst) - mainvar, baserql = facet.prepare_facets_rqlst(rqlst, rset.args) - widgets = [] - for facetobj in self.get_facets(rset, rqlst.children[0], mainvar): - wdg = facetobj.get_widget() - if wdg is not None: - widgets.append(wdg) - if not widgets: - return - if vid is None: - vid = req.form.get('vid') - if self.bk_linkbox_template and req.vreg.schema['Bookmark'].has_perm(req, 'add'): - w(self.bookmark_link(rset)) - w(u'
' % ( - divid, xml_escape(json_dumps([divid, vid, paginate, self.facetargs()])))) - w(u'
') - hiddens = {'facets': ','.join(wdg.facet.__regid__ for wdg in widgets), - 'baserql': baserql} - for param in ('subvid', 'vtitle'): - if param in req.form: - hiddens[param] = req.form[param] - facet.filter_hiddens(w, **hiddens) - for wdg in widgets: - wdg.render(w=w) - w(u'
\n
\n') - def bookmark_link(self, rset): req = self._cw bk_path = u'rql=%s' % req.url_quote(rset.printable_rql()) @@ -128,35 +184,79 @@ req._('bookmark this search')) return self.bk_linkbox_template % bk_link - def get_facets(self, rset, rqlst, mainvar): - return self._cw.vreg['facets'].poss_visible_objects( - self._cw, rset=rset, rqlst=rqlst, - context='facetbox', filtered_variable=mainvar) + def vidargs(self): + """this method returns the list of extra arguments that should be used + by the filter or the view using it + """ + return {} + + +from cubicweb.view import AnyRsetView + +class FilterTable(FacetFilterMixIn, AnyRsetView): + __regid__ = 'facet.filtertable' + __select__ = non_final_entity() & has_facets() + wdg_stack_size = 8 + + def call(self, vid, divid, vidargs, cssclass=''): + self.generate_form(self.w, self.cw_rset, divid, vid, vidargs, + cssclass=cssclass, fromformfilter='1', + # divid=divid XXX + ) + + def layout_widgets(self, w, wdgs): + """layout widgets: put them in a table where each column should have + sum(wdg.height()) < wdg_stack_size. + """ + w(u'\n') + widget_queue = [] + queue_height = 0 + w(u'\n') + for wdg in wdgs: + height = wdg.height() + if queue_height + height <= self.wdg_stack_size: + widget_queue.append(wdg) + queue_height += height + continue + w(u'') + widget_queue = [wdg] + queue_height = height + if widget_queue: + w(u'') + w(u'\n') + w(u'
') + for queued in widget_queue: + queued.render(w=w) + w(u'') + for queued in widget_queue: + queued.render(w=w) + w(u'
\n') + # facets ###################################################################### -class CWSourceFacet(facet.RelationFacet): +class CWSourceFacet(facetbase.RelationFacet): __regid__ = 'cw_source-facet' rtype = 'cw_source' target_attr = 'name' -class CreatedByFacet(facet.RelationFacet): +class CreatedByFacet(facetbase.RelationFacet): __regid__ = 'created_by-facet' rtype = 'created_by' target_attr = 'login' -class InGroupFacet(facet.RelationFacet): +class InGroupFacet(facetbase.RelationFacet): __regid__ = 'in_group-facet' rtype = 'in_group' target_attr = 'name' -class InStateFacet(facet.RelationAttributeFacet): +class InStateFacet(facetbase.RelationAttributeFacet): __regid__ = 'in_state-facet' rtype = 'in_state' target_attr = 'name' # inherit from RelationFacet to benefit from its possible_values implementation -class ETypeFacet(facet.RelationFacet): +class ETypeFacet(facetbase.RelationFacet): __regid__ = 'etype-facet' __select__ = yes() order = 1 @@ -187,8 +287,8 @@ rqlst = self.rqlst rqlst.save_state() try: - facet._cleanup_rqlst(rqlst, self.filtered_variable) - etype_var = facet._prepare_vocabulary_rqlst( + facetbase.cleanup_rqlst(rqlst, self.filtered_variable) + etype_var = facetbase.prepare_vocabulary_rqlst( rqlst, self.filtered_variable, self.rtype, self.role) attrvar = rqlst.make_variable() rqlst.add_selected(attrvar) @@ -197,7 +297,7 @@ finally: rqlst.recover() -class HasTextFacet(facet.AbstractFacet): +class HasTextFacet(facetbase.AbstractFacet): __select__ = relation_possible('has_text', 'subject') & match_context_prop() __regid__ = 'has_text-facet' rtype = 'has_text' @@ -206,7 +306,7 @@ @property def wdgclass(self): - return facet.FacetStringWidget + return facetbase.FacetStringWidget @property def title(self): diff -r 9cbf4c86f57a -r 75d208ab8444 web/views/tableview.py --- a/web/views/tableview.py Fri Jul 01 12:48:39 2011 +0200 +++ b/web/views/tableview.py Fri Jul 01 13:39:45 2011 +0200 @@ -15,110 +15,48 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -"""generic table view, including filtering abilities""" +"""generic table view, including filtering abilities using facets""" __docformat__ = "restructuredtext en" _ = unicode from logilab.mtconverter import xml_escape +from cubicweb import NoSelectableObject, tags from cubicweb.selectors import nonempty_rset from cubicweb.utils import make_uid, json_dumps from cubicweb.view import EntityView, AnyRsetView -from cubicweb import tags from cubicweb.uilib import toggle_action, limitsize, htmlescape -from cubicweb.web import jsonize -from cubicweb.web.component import Link +from cubicweb.web import jsonize, component, facet from cubicweb.web.htmlwidgets import (TableWidget, TableColumn, MenuWidget, PopupBoxMenu) -from cubicweb.web import facet -from cubicweb.web.facet import prepare_facets_rqlst, filter_hiddens + class TableView(AnyRsetView): - """The table view accepts any non-empty rset. It uses - introspection on the result set to compute column names and the - proper way to display the cells. + """The table view accepts any non-empty rset. It uses introspection on the + result set to compute column names and the proper way to display the cells. + It is however highly configurable and accepts a wealth of options. """ __regid__ = 'table' title = _('table') finalview = 'final' - wdg_stack_size = 8 def form_filter(self, divid, displaycols, displayactions, displayfilter, paginate, hidden=True): - rqlst = self.cw_rset.syntax_tree() - # union not yet supported - if len(rqlst.children) != 1: + try: + filterform = self._cw.vreg['views'].select( + 'facet.filtertable', self._cw, rset=self.cw_rset) + except NoSelectableObject: return () - rqlst = rqlst.copy() - self._cw.vreg.rqlhelper.annotate(rqlst) - mainvar, baserql = prepare_facets_rqlst(rqlst, self.cw_rset.args) - wdgs = [facet.get_widget() for facet in self._cw.vreg['facets'].poss_visible_objects( - self._cw, rset=self.cw_rset, rqlst=rqlst.children[0], context='tablefilter', - filtered_variable=mainvar)] - wdgs = [wdg for wdg in wdgs if wdg is not None] - if wdgs: - self._generate_form(divid, baserql, wdgs, hidden, - vidargs={'paginate': paginate, - 'displaycols': displaycols, - 'displayactions': displayactions, - 'displayfilter': displayfilter}) - return self.show_hide_actions(divid, not hidden) - return () - - def _generate_form(self, divid, baserql, fwidgets, hidden=True, vidargs={}): - """display a form to filter table's content. This should only - occur when a context eid is given - """ - w = self.w - self._cw.add_css('cubicweb.facets.css') - self._cw.add_js( ('cubicweb.ajax.js', 'cubicweb.facets.js')) - self._cw.html_headers.define_var('facetLoadingMsg', - self._cw._('facet-loading-msg')) - # drop False / None values from vidargs - vidargs = dict((k, v) for k, v in vidargs.iteritems() if v) - w(u'
' % - xml_escape(json_dumps([divid, self.__regid__, False, vidargs]))) - w(u'
' % (divid, hidden and 'hidden' or '')) - w(u'' % divid) - w(u'') - filter_hiddens(w, facets=','.join(wdg.facet.__regid__ for wdg in fwidgets), - baserql=baserql) - self._build_form_table(fwidgets) - - def _facet_widget_sort(self, fwidgets): - fwidgets.sort(key=lambda x: x.height()) - - def _build_form_table(self, fwidgets): - # sort by widget height - w = self.w - self._facet_widget_sort(fwidgets) - w(u'\n') - widget_queue = [] - queue_size = 0 - w(u'\n') - for wdg in fwidgets: - height = wdg.height() - if queue_size + height <= self.wdg_stack_size: - widget_queue.append(wdg) - queue_size += height - continue - w(u'') - widget_queue = [wdg] - queue_size = height - if widget_queue: - w(u'') - w(u'\n') - w(u'
') - for queued in widget_queue: - queued.render(w=w) - w(u'') - for queued in widget_queue: - queued.render(w=w) - w(u'
\n') - w(u'
\n') - w(u'
\n') + vidargs = {'paginate': paginate, + 'displaycols': displaycols, + 'displayactions': displayactions, + 'displayfilter': displayfilter} + cssclass = hidden and 'hidden' or '' + filterform.render(self.w, vid=self.__regid__, divid=divid, + vidargs=vidargs, cssclass=cssclass) + return self.show_hide_actions(divid, not hidden) def main_var_index(self): """returns the index of the first non-attribute variable among the RQL @@ -241,7 +179,7 @@ ident='%sActions' % divid) box.append(menu) for url, label, klass, ident in actions: - menu.append(Link(url, label, klass=klass, id=ident)) + menu.append(component.Link(url, label, klass=klass, id=ident)) box.render(w=self.w) self.w(u'
')