web/views/treeview.py
branchtls-sprint
changeset 985 6a25c58a1c23
parent 981 d86d1ee3b60e
parent 923 7c184924d492
child 1006 92a0601b2523
equal deleted inserted replaced
984:536e421b082b 985:6a25c58a1c23
    16 
    16 
    17 def treecookiename(treeid):
    17 def treecookiename(treeid):
    18     return str('treestate-%s' % treeid)
    18     return str('treestate-%s' % treeid)
    19 
    19 
    20 
    20 
       
    21 from cubicweb.web.views.baseviews import OneLineView
       
    22 
    21 class TreeView(EntityView):
    23 class TreeView(EntityView):
    22     id = 'treeview'
    24     id = 'treeview'
    23     itemvid = 'treeitemview'
    25     itemvid = 'treeitemview'
    24     css_classes = 'treeview widget'
    26     css_classes = 'treeview widget'
    25     title = _('tree view')
    27     title = _('tree view')
    26 
    28 
    27     def call(self, subvid=None, treeid=None, initial_load=True):
    29     def call(self, subvid=None):
    28         if subvid is None and 'subvid' in self.req.form:
    30         if subvid is None and 'subvid' in self.req.form:
    29             subvid = self.req.form.pop('subvid') # consume it
    31             subvid = self.req.form.pop('subvid') # consume it
    30         if subvid is None:
    32         if subvid is None:
    31             subvid = 'oneline'
    33             subvid = 'oneline'
    32         if treeid is None and 'treeid' in self.req.form:
    34         self.req.add_css('jquery.treeview.css')
    33             treeid = self.req.form.pop('treeid')
    35         self.req.add_js(('cubicweb.ajax.js', 'jquery.treeview.js', 'cubicweb.widgets.js'))
    34         assert treeid is not None
    36         # XXX noautoload is a quick hack to avoid treeview to be rebuilt
    35         if initial_load:
    37         #     after a json query and avoid double toggling bugs.
    36             self.req.add_css('jquery.treeview.css')
    38         #     Need to find a way to do that cleanly.
    37             self.req.add_js(('cubicweb.ajax.js', 'jquery.treeview.js'))
    39         if 'noautoload' in self.req.form:
    38             self.req.html_headers.add_onload(u"""
    40             self.w(u'<ul class="%s" cubicweb:wdgtype="TreeView">' % self.css_classes)
    39 jQuery("#tree-%s").treeview({toggle: toggleTree, prerendered: true});""" % treeid)
    41         else:
    40         self.w(u'<ul id="tree-%s" class="%s">' % (treeid, self.css_classes))
    42             self.w(u'<ul class="%s" cubicweb:loadtype="auto" cubicweb:wdgtype="TreeView">'
       
    43                    % self.css_classes)
    41         for rowidx in xrange(len(self.rset)):
    44         for rowidx in xrange(len(self.rset)):
    42             self.wview(self.itemvid, self.rset, row=rowidx, col=0,
    45             self.wview(self.itemvid, self.rset, row=rowidx, col=0,
    43                        vid=subvid, parentvid=self.id, treeid=treeid)
    46                        vid=subvid, parentvid=self.id, treeid=treeid)
    44         self.w(u'</ul>')
    47         self.w(u'</ul>')
    45         
    48         
    52     title = _('file tree view')
    55     title = _('file tree view')
    53 
    56 
    54     def call(self, subvid=None, treeid=None, initial_load=True):
    57     def call(self, subvid=None, treeid=None, initial_load=True):
    55         super(FileTreeView, self).call(treeid=treeid, subvid='filetree-oneline', initial_load=initial_load)
    58         super(FileTreeView, self).call(treeid=treeid, subvid='filetree-oneline', initial_load=initial_load)
    56 
    59 
       
    60 
       
    61 
    57 class FileItemInnerView(EntityView):
    62 class FileItemInnerView(EntityView):
    58     """inner view used by the TreeItemView instead of oneline view
    63     """inner view used by the TreeItemView instead of oneline view
    59 
    64 
    60     This view adds an enclosing <span> with some specific CSS classes
    65     This view adds an enclosing <span> with some specific CSS classes
    61     around the oneline view. This is needed by the jquery treeview plugin.
    66     around the oneline view. This is needed by the jquery treeview plugin.
    63     id = 'filetree-oneline'
    68     id = 'filetree-oneline'
    64 
    69 
    65     def cell_call(self, row, col):
    70     def cell_call(self, row, col):
    66         entity = self.entity(row, col)
    71         entity = self.entity(row, col)
    67         if ITree.is_implemented_by(entity.__class__) and not entity.is_leaf():
    72         if ITree.is_implemented_by(entity.__class__) and not entity.is_leaf():
    68             self.w(u'<div class="folder">%s</div>\n' % entity.view('oneline'))
    73             self.w(u'<div class="folder">%s</div>' % entity.view('oneline'))
    69         else:
    74         else:
    70             # XXX define specific CSS classes according to mime types
    75             # XXX define specific CSS classes according to mime types
    71             self.w(u'<div class="file">%s</div>\n' % entity.view('oneline'))
    76             self.w(u'<div class="file">%s</div>' % entity.view('oneline'))
    72 
    77 
    73 
    78 
    74 class DefaultTreeViewItemView(EntityView):
    79 class DefaultTreeViewItemView(EntityView):
    75     """default treeitem view for entities which don't implement ITree"""
    80     """default treeitem view for entities which don't implement ITree"""
    76     id = 'treeitemview'
    81     id = 'treeitemview'
    77 
    82     
    78     def cell_call(self, row, col, vid='oneline', parentvid='treeview', treeid=None):
    83     def cell_call(self, row, col, vid='oneline', parentvid='treeview'):
    79         assert treeid is not None
       
    80         entity = self.entity(row, col)
    84         entity = self.entity(row, col)
    81         itemview = self.view(vid, self.rset, row=row, col=col)
    85         itemview = self.view(vid, self.rset, row=row, col=col)
    82         if row == len(self.rset) - 1:
    86         if row == len(self.rset) - 1:
    83             self.w(u'<li class="last">%s</li>' % itemview)
    87             self.w(u'<li class="last">%s</li>' % itemview)
    84         else:
    88         else:
    90 
    94 
    91     (each item should be expandable if it's not a tree leaf)
    95     (each item should be expandable if it's not a tree leaf)
    92     """
    96     """
    93     id = 'treeitemview'
    97     id = 'treeitemview'
    94     __select_ = implements(ITree)
    98     __select_ = implements(ITree)
    95 
    99     
    96     def open_state(self, eeid, treeid):
   100     def cell_call(self, row, col, vid='oneline', parentvid='treeview'):
    97         cookies = self.req.get_cookie()
       
    98         treestate = cookies.get(treecookiename(treeid))
       
    99         if treestate:
       
   100             return str(eeid) in treestate.value.split(';')
       
   101         return False
       
   102 
       
   103     def cell_call(self, row, col, treeid, vid='oneline', parentvid='treeview'):
       
   104         w = self.w
       
   105         entity = self.entity(row, col)
   101         entity = self.entity(row, col)
   106         liclasses = []
   102         cssclasses = []
   107         is_leaf = False
   103         is_leaf = False
   108         is_last = row == len(self.rset) - 1
   104         if row == len(self.rset) - 1:
   109         is_open = self.open_state(entity.eid, treeid)
   105             is_leaf = True
   110         if not hasattr(entity, 'is_leaf') or entity.is_leaf():
   106         if not hasattr(entity, 'is_leaf') or entity.is_leaf():
   111             if is_last:
   107             if is_leaf : cssclasses.append('last')
   112                 liclasses.append('last')
   108             self.w(u'<li class="%s">' % u' '.join(cssclasses))
   113             w(u'<li class="%s">' % u' '.join(liclasses))
       
   114         else:
   109         else:
   115             rql = entity.children_rql() % {'x': entity.eid}
   110             rql = entity.children_rql() % {'x': entity.eid}
   116             url = html_escape(self.build_url('json', rql=rql, vid=parentvid,
   111             url = html_escape(self.build_url('json', rql=rql, vid=parentvid,
   117                                              pageid=self.req.pageid,
   112                                              pageid=self.req.pageid,
   118                                              treeid=treeid,
   113                                              subvid=vid,
   119                                              subvid=vid))
   114                                              noautoload=True))
   120             divclasses = ['hitarea']
   115             cssclasses.append('expandable')
   121             if is_open:
   116             divclasses = ['hitarea expandable-hitarea']
   122                 liclasses.append('collapsable')
   117             if is_leaf :
   123                 divclasses.append('collapsable-hitarea')
   118                 cssclasses.append('lastExpandable')
   124             else:
   119                 divclasses.append('lastExpandable-hitarea')
   125                 liclasses.append('expandable')
   120             self.w(u'<li cubicweb:loadurl="%s" class="%s">' % (url, u' '.join(cssclasses)))
   126                 divclasses.append('closed-hitarea expandable-hitarea')
   121             self.w(u'<div class="%s"> </div>' % u' '.join(divclasses))
   127             if is_last:
       
   128                 if is_open:
       
   129                     liclasses.append('lastCollapsable')
       
   130                     divclasses.append('lastCollapsable-hitarea')
       
   131                 else:
       
   132                     liclasses.append('lastExpandable')
       
   133                     divclasses.append('lastExpandable-hitarea')
       
   134             if is_open:
       
   135                 w(u'<li class="%s">' % u' '.join(liclasses))
       
   136             else:
       
   137                 w(u'<li cubicweb:loadurl="%s" class="%s">' % (url, u' '.join(liclasses)))
       
   138             if is_leaf:
       
   139                 divtail = ''
       
   140             else:
       
   141                 divtail = ''' onclick="async_remote_exec('node_clicked', '%s', '%s')"''' % \
       
   142                     (treeid, entity.eid)
       
   143             w(u'<div class="%s"%s></div>' % (u' '.join(divclasses), divtail))
       
   144 
       
   145             # add empty <ul> because jquery's treeview plugin checks for
   122             # add empty <ul> because jquery's treeview plugin checks for
   146             # sublists presence
   123             # sublists presence
   147             if not is_open:
   124             self.w(u'<ul class="placeholder"><li>place holder</li></ul>')
   148                 w(u'<ul class="placeholder"><li>place holder</li></ul>')
       
   149         # the local node info
       
   150         self.wview(vid, self.rset, row=row, col=col)
   125         self.wview(vid, self.rset, row=row, col=col)
   151         if is_open: # recurse if needed
   126         self.w(u'</li>')
   152             self.wview(parentvid, self.req.execute(rql), treeid=treeid, initial_load=False)
       
   153         w(u'</li>')
       
   154 
   127 
   155 
       
   156 @monkeypatch(JSonController)
       
   157 def js_node_clicked(self, treeid, nodeeid):
       
   158     """add/remove eid in treestate cookie"""
       
   159     cookies = self.req.get_cookie()
       
   160     statename = treecookiename(treeid)
       
   161     treestate = cookies.get(statename)
       
   162     if treestate is None:
       
   163         cookies[statename] = nodeeid
       
   164         self.req.set_cookie(cookies, statename)
       
   165     else:
       
   166         marked = set(filter(None, treestate.value.split(';')))
       
   167         if nodeeid in marked:
       
   168             marked.remove(nodeeid)
       
   169         else:
       
   170             marked.add(nodeeid)
       
   171         cookies[statename] = ';'.join(marked)
       
   172         self.req.set_cookie(cookies, statename)