Make EditController edit_entity method always return an eid
In cases the entity was to be created or copied, eid was None hence the method
returned None despite the dosctring says the method should always return an
eid. Now if the eid variable is None, it is assigned to the newly created
entity eid.
Closes #3593029.
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of CubicWeb.## CubicWeb is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## CubicWeb is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with CubicWeb. If not, see <http://www.gnu.org/licenses/>."""Set of tree views / tree-building widgets, some based on jQuery treeviewplugin."""__docformat__="restructuredtext en"_=unicodefromwarningsimportwarnfromlogilab.mtconverterimportxml_escapefromcubicweb.utilsimportmake_uid,jsonfromcubicweb.predicatesimportadaptablefromcubicweb.viewimportEntityViewfromcubicweb.web.viewsimportbaseviewsfromcubicweb.web.views.ajaxcontrollerimportajaxfuncdeftreecookiename(treeid):returnstr('%s-treestate'%treeid)def_done_init(done,view,row,col):"""handle an infinite recursion safety belt"""ifdoneisNone:done=set()entity=view.cw_rset.get_entity(row,col)ifentity.eidindone:msg=entity._cw._('loop in %(rel)s relation (%(eid)s)')%{'rel':entity.cw_adapt_to('ITree').tree_relation,'eid':entity.eid}returnNone,msgdone.add(entity.eid)returndone,entityclassBaseTreeView(baseviews.ListView):"""base tree view"""__regid__='tree'__select__=adaptable('ITree')item_vid='treeitem'defcall(self,done=None,**kwargs):ifdoneisNone:done=set()super(BaseTreeView,self).call(done=done,**kwargs)defcell_call(self,row,col=0,vid=None,done=None,maxlevel=None,**kwargs):assertmaxlevelisNoneormaxlevel>0done,entity=_done_init(done,self,row,col)ifdoneisNone:# entity is actually an error messageself.w(u'<li class="badcontent">%s</li>'%entity)returnself.open_item(entity)entity.view(vidorself.item_vid,w=self.w,**kwargs)ifmaxlevelisnotNone:maxlevel-=1ifmaxlevel==0:self.close_item(entity)returnrelatedrset=entity.cw_adapt_to('ITree').children(entities=False)self.wview(self.__regid__,relatedrset,'null',done=done,maxlevel=maxlevel,**kwargs)self.close_item(entity)defopen_item(self,entity):self.w(u'<li class="%s">\n'%entity.cw_etype.lower())defclose_item(self,entity):self.w(u'</li>\n')classTreePathView(EntityView):"""a recursive path view"""__regid__='path'__select__=adaptable('ITree')item_vid='oneline'separator=u' > 'defcall(self,**kwargs):self.w(u'<div class="pathbar">')super(TreePathView,self).call(**kwargs)self.w(u'</div>')defcell_call(self,row,col=0,vid=None,done=None,**kwargs):done,entity=_done_init(done,self,row,col)ifdoneisNone:# entity is actually an error messageself.w(u'<span class="badcontent">%s</span>'%entity)returnparent=entity.cw_adapt_to('ITree').parent()ifparent:parent.view(self.__regid__,w=self.w,done=done)self.w(self.separator)entity.view(vidorself.item_vid,w=self.w)classTreeComboBoxView(TreePathView):"""display folder in edition's combobox"""__regid__='combobox'item_vid='text'separator=u' > '# XXX rename regid to ajaxtree/foldabletree or something like that (same for# treeitemview)classTreeView(EntityView):"""ajax tree view, click to expand folder"""__regid__='treeview'itemvid='treeitemview'subvid='oneline'cssclass='treeview widget'title=_('tree view')def_init_params(self,subvid,treeid,initial_load,initial_thru_ajax,morekwargs):form=self._cw.formifsubvidisNone:subvid=form.pop('treesubvid',self.subvid)# consume itiftreeidisNone:treeid=form.pop('treeid',None)iftreeidisNone:treeid='throw_away'+make_uid('uid')if'morekwargs'inself._cw.form:ajaxargs=json.loads(form.pop('morekwargs'))# got unicode & python keywords must be stringsmorekwargs.update(dict((str(k),v)fork,vinajaxargs.iteritems()))toplevel_thru_ajax=form.pop('treeview_top',False)orinitial_thru_ajaxtoplevel=toplevel_thru_ajaxor(initial_loadandnotform.get('fname'))returnsubvid,treeid,toplevel_thru_ajax,topleveldef_init_headers(self,treeid,toplevel_thru_ajax):self._cw.add_css('jquery.treeview.css')self._cw.add_js(('cubicweb.ajax.js','cubicweb.widgets.js','jquery.treeview.js'))self._cw.html_headers.add_onload(u"""jQuery("#tree-%s").treeview({toggle: toggleTree, prerendered: true});"""%treeid)defcall(self,subvid=None,treeid=None,initial_load=True,initial_thru_ajax=False,**morekwargs):subvid,treeid,toplevel_thru_ajax,toplevel=self._init_params(subvid,treeid,initial_load,initial_thru_ajax,morekwargs)ulid=' 'iftoplevel:self._init_headers(treeid,toplevel_thru_ajax)ulid=' id="tree-%s"'%treeidself.w(u'<ul%s class="%s">'%(ulid,self.cssclass))# XXX force sorting on x.sortvalue() (which return dc_title by default)# we need proper ITree & co specification to avoid this.# (pb when type ambiguity at the other side of the tree relation,# unability to provide generic implementation on eg Folder...)fori,entityinenumerate(sorted(self.cw_rset.entities(),key=lambdax:x.sortvalue())):ifi+1<len(self.cw_rset):morekwargs['is_last']=Falseelse:morekwargs['is_last']=Trueentity.view(self.itemvid,vid=subvid,parentvid=self.__regid__,treeid=treeid,w=self.w,**morekwargs)self.w(u'</ul>')defcell_call(self,*args,**allargs):""" does not makes much sense until you have to invoke somentity.view('treeview') """allargs.pop('row')allargs.pop('col')self.call(*args,**allargs)classFileTreeView(TreeView):"""specific version of the treeview to display file trees """__regid__='filetree'cssclass='treeview widget filetree'title=_('file tree view')defcall(self,subvid=None,treeid=None,initial_load=True,**kwargs):super(FileTreeView,self).call(treeid=treeid,subvid='filetree-oneline',initial_load=initial_load,**kwargs)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. """__regid__='filetree-oneline'defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)itree=entity.cw_adapt_to('ITree')ifitreeandnotitree.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 adapt to ITree"""__regid__='treeitemview'defcell_call(self,row,col,vid='oneline',treeid=None,**morekwargs):asserttreeidisnotNoneitemview=self._cw.view(vid,self.cw_rset,row=row,col=col)last_class=morekwargs['is_last']and' class="last"'or''self.w(u'<li%s>%s</li>'%(last_class,itemview))classTreeViewItemView(EntityView):"""specific treeitem view for entities which adapt to ITree (each item should be expandable if it's not a tree leaf) """__regid__='treeitemview'__select__=adaptable('ITree')default_branch_state_is_open=Falsedefopen_state(self,eeid,treeid):cookies=self._cw.get_cookie()treestate=cookies.get(treecookiename(treeid))iftreestate:returnstr(eeid)intreestate.value.split(':')returnself.default_branch_state_is_opendefcell_call(self,row,col,treeid,vid='oneline',parentvid='treeview',is_last=False,**morekwargs):w=self.wentity=self.cw_rset.get_entity(row,col)itree=entity.cw_adapt_to('ITree')liclasses=[]is_open=self.open_state(entity.eid,treeid)is_leaf=itreeisNoneoritree.is_leaf()ifis_leaf:ifis_last:liclasses.append('last')w(u'<li class="%s">'%u' '.join(liclasses))else:rql=itree.children_rql()%{'x':entity.eid}url=xml_escape(self._cw.build_url('ajax',rql=rql,vid=parentvid,pageid=self._cw.pageid,treeid=treeid,fname='view',treesubvid=vid,morekwargs=json.dumps(morekwargs)))divclasses=['hitarea']ifis_open:liclasses.append('collapsable')divclasses.append('collapsable-hitarea')else:liclasses.append('expandable')divclasses.append('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)))iftreeid.startswith('throw_away'):divtail=''else:divtail=""" onclick="asyncRemoteExec('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.cw_rset,row=row,col=col,**morekwargs)ifis_openandnotis_leaf:# => rql is definedself.wview(parentvid,itree.children(entities=False),subvid=vid,treeid=treeid,initial_load=False,**morekwargs)w(u'</li>')@ajaxfuncdefnode_clicked(self,treeid,nodeeid):"""add/remove eid in treestate cookie"""cookies=self._cw.get_cookie()statename=treecookiename(treeid)treestate=cookies.get(statename)iftreestateisNone:self._cw.set_cookie(statename,nodeeid)else:marked=set(filter(None,treestate.value.split(':')))ifnodeeidinmarked:marked.remove(nodeeid)else:marked.add(nodeeid)self._cw.set_cookie(statename,':'.join(marked))