"""Set of tree-building widgets, based on jQuery treeview plugin:organization: Logilab:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr"""__docformat__="restructuredtext en"fromlogilab.mtconverterimporthtml_escapefromcubicweb.interfacesimportITreefromcubicweb.common.selectorsimportimplement_interface,yesfromcubicweb.common.utilsimportmake_uidfromcubicweb.common.viewimportEntityViewdeftreecookiename(treeid):returnstr('treestate-%s'%treeid)classTreeView(EntityView):id='treeview'accepts=('Any',)itemvid='treeitemview'css_classes='treeview widget'title=_('tree view')defcall(self,subvid=None,treeid=None,initial_load=True):ifsubvidisNone:if'subvid'inself.req.form:subvid=self.req.form.pop('subvid')# consume itelse:subvid='oneline'iftreeidisNone:if'treeid'inself.req.form:treeid=self.req.form.pop('treeid')else:treeid=make_uid('throw away uid')self.warning('Tree state won\'t be properly restored after next reload')ifinitial_load:self.req.add_css('jquery.treeview.css')self.req.add_js(('cubicweb.ajax.js','jquery.treeview.js'))self.req.html_headers.add_onload(u""" jQuery("#tree-%s").treeview({toggle: toggleTree, prerendered: true});"""%treeid)self.w(u'<ul id="tree-%s" class="%s">'%(treeid,self.css_classes))forrowidxinxrange(len(self.rset)):self.wview(self.itemvid,self.rset,row=rowidx,col=0,vid=subvid,parentvid=self.id,treeid=treeid)self.w(u'</ul>')classFileTreeView(TreeView):"""specific version of the treeview to display file trees """id='filetree'css_classes='treeview widget filetree'title=_('file tree view')defcall(self,subvid=None,treeid=None,initial_load=True):super(FileTreeView,self).call(treeid=treeid,subvid='filetree-oneline',initial_load=initial_load)classFileItemInnerView(EntityView):"""inner view used by the TreeItemView instead of oneline view This view adds an enclosing <span> with some specific CSS classes around the oneline view. This is needed by the jquery treeview plugin. """id='filetree-oneline'defcell_call(self,row,col):entity=self.entity(row,col)ifITree.is_implemented_by(entity.__class__)andnotentity.is_leaf():self.w(u'<div class="folder">%s</div>\n'%entity.view('oneline'))else:# XXX define specific CSS classes according to mime typesself.w(u'<div class="file">%s</div>\n'%entity.view('oneline'))classDefaultTreeViewItemView(EntityView):"""default treeitem view for entities which don't implement ITree """id='treeitemview'accepts=('Any',)defcell_call(self,row,col,vid='oneline',parentvid='treeview',treeid=None):asserttreeidisnotNoneentity=self.entity(row,col)itemview=self.view(vid,self.rset,row=row,col=col)ifrow==len(self.rset)-1:self.w(u'<li class="last">%s</li>'%itemview)else:self.w(u'<li>%s</li>'%itemview)classTreeViewItemView(EntityView):"""specific treeitem view for entities which implement ITree (each item should be exandable if it's not a tree leaf) """id='treeitemview'# XXX append yes to make sure we get an higher score than# the default treeitem view__selectors__=(implement_interface,yes)accepts_interfaces=(ITree,)defopen_state(self,eeid,treeid):cookies=self.req.get_cookie()treestate=cookies.get(treecookiename(treeid))iftreestate:returnstr(eeid)intreestate.value.split(';')returnFalsedefcell_call(self,row,col,treeid,vid='oneline',parentvid='treeview'):w=self.wentity=self.entity(row,col)liclasses=[]is_leaf=Falseis_last=row==len(self.rset)-1is_open=self.open_state(entity.eid,treeid)ifnothasattr(entity,'is_leaf')orentity.is_leaf():ifis_last:liclasses.append('last')w(u'<li class="%s">'%u' '.join(liclasses))else:rql=entity.children_rql()%{'x':entity.eid}url=html_escape(self.build_url('json',rql=rql,vid=parentvid,pageid=self.req.pageid,treeid=treeid,subvid=vid))divclasses=['hitarea']ifis_open:liclasses.append('collapsable')divclasses.append('collapsable-hitarea')else:liclasses.append('expandable')divclasses.append('closed-hitarea expandable-hitarea')ifis_last:ifis_open:liclasses.append('lastCollapsable')divclasses.append('lastCollapsable-hitarea')else:liclasses.append('lastExpandable')divclasses.append('lastExpandable-hitarea')ifis_open:w(u'<li class="%s">'%u' '.join(liclasses))else:w(u'<li cubicweb:loadurl="%s" class="%s">'%(url,u' '.join(liclasses)))ifis_leaf:divtail=''else:divtail=''' onclick="async_remote_exec('node_clicked', '%s', '%s')"'''% \(treeid,entity.eid)w(u'<div class="%s"%s></div>'%(u' '.join(divclasses),divtail))# add empty <ul> because jquery's treeview plugin checks for# sublists presenceifnotis_open:w(u'<ul class="placeholder"><li>place holder</li></ul>')# the local node infoself.wview(vid,self.rset,row=row,col=col)ifis_open:# recurse if neededself.wview(parentvid,self.req.execute(rql),treeid=treeid,initial_load=False)w(u'</li>')fromlogilab.common.decoratorsimportmonkeypatchfromcubicweb.web.views.basecontrollersimportJSonController@monkeypatch(JSonController)defjs_node_clicked(self,treeid,nodeeid):"""add/remove eid in treestate cookie"""cookies=self.req.get_cookie()statename=treecookiename(treeid)treestate=cookies.get(statename)iftreestateisNone:cookies[statename]=nodeeidself.req.set_cookie(cookies,statename)else:marked=set(filter(None,treestate.value.split(';')))ifnodeeidinmarked:marked.remove(nodeeid)else:marked.add(nodeeid)cookies[statename]=';'.join(marked)self.req.set_cookie(cookies,statename)