[facets] reenable facet hiding (was lost in the previous refactoring)
# 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/>."""workflow views:* IWorkflowable views and forms* workflow entities views (State, Transition, TrInfo)"""__docformat__="restructuredtext en"_=unicodeimportosfromwarningsimportwarnfromlogilab.mtconverterimportxml_escapefromlogilab.common.graphimportescapefromcubicwebimportUnauthorizedfromcubicweb.selectorsimport(has_related_entities,one_line_rset,relation_possible,match_form_params,score_entity,is_instance,adaptable)fromcubicweb.viewimportEntityViewfromcubicweb.schemaimportdisplay_namefromcubicweb.webimportuicfg,stdmsgs,action,component,form,actionfromcubicweb.webimportformfieldsasff,formwidgetsasfwdgsfromcubicweb.web.viewsimportTmpFileViewMixinfromcubicweb.web.viewsimportforms,primary,ibreadcrumbsfromcubicweb.web.views.tabsimportTabbedPrimaryView,PrimaryTabfromcubicweb.web.views.dotgraphviewimportDotGraphView,DotPropsHandler_pvs=uicfg.primaryview_section_pvs.tag_subject_of(('Workflow','initial_state','*'),'hidden')_pvs.tag_object_of(('*','state_of','Workflow'),'hidden')_pvs.tag_object_of(('*','transition_of','Workflow'),'hidden')_pvs.tag_object_of(('*','wf_info_for','*'),'hidden')forrtypein('in_state','by_transition','from_state','to_state'):_pvs.tag_subject_of(('*',rtype,'*'),'hidden')_pvs.tag_object_of(('*',rtype,'*'),'hidden')_pvs.tag_object_of(('*','wf_info_for','*'),'hidden')_abaa=uicfg.actionbox_appearsin_addmenu_abaa.tag_subject_of(('BaseTransition','condition','RQLExpression'),False)_abaa.tag_subject_of(('State','allowed_transition','BaseTransition'),False)_abaa.tag_object_of(('SubWorkflowExitPoint','destination_state','State'),False)_abaa.tag_subject_of(('*','wf_info_for','*'),False)_abaa.tag_object_of(('*','wf_info_for','*'),False)_abaa.tag_object_of(('*','state_of','CWEType'),True)_abaa.tag_object_of(('*','transition_of','CWEType'),True)_abaa.tag_subject_of(('Transition','destination_state','*'),True)_abaa.tag_object_of(('*','allowed_transition','Transition'),True)_abaa.tag_object_of(('*','destination_state','State'),True)_abaa.tag_subject_of(('State','allowed_transition','*'),True)_abaa.tag_object_of(('State','state_of','Workflow'),True)_abaa.tag_object_of(('Transition','transition_of','Workflow'),True)_abaa.tag_object_of(('WorkflowTransition','transition_of','Workflow'),True)_afs=uicfg.autoform_section_affk=uicfg.autoform_field_kwargs# IWorkflowable views #########################################################classChangeStateForm(forms.CompositeEntityForm):# set dom id to ensure there is no conflict with edition form (see# session_key() implementation)__regid__=domid='changestate'form_renderer_id='base'# don't want EntityFormRendererform_buttons=[fwdgs.SubmitButton(),fwdgs.Button(stdmsgs.BUTTON_CANCEL,cwaction='cancel')]classChangeStateFormView(form.FormViewMixIn,EntityView):__regid__='statuschange'title=_('status change')__select__=(one_line_rset()&match_form_params('treid')&adaptable('IWorkflowable'))defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)transition=self._cw.entity_from_eid(self._cw.form['treid'])form=self.get_form(entity,transition)self.w(u'<h4>%s%s</h4>\n'%(self._cw._(transition.name),entity.view('oneline')))msg=self._cw._('status will change from %(st1)s to %(st2)s')%{'st1':entity.cw_adapt_to('IWorkflowable').printable_state,'st2':self._cw._(transition.destination(entity).name)}self.w(u'<p>%s</p>\n'%msg)form.render(w=self.w)defredirectpath(self,entity):returnentity.rest_path()defget_form(self,entity,transition,**kwargs):# XXX used to specify both rset/row/col and entity in case implements# selector (and not is_instance) is used on custom formform=self._cw.vreg['forms'].select('changestate',self._cw,entity=entity,transition=transition,redirect_path=self.redirectpath(entity),**kwargs)trinfo=self._cw.vreg['etypes'].etype_class('TrInfo')(self._cw)trinfo.eid=self._cw.varmaker.next()subform=self._cw.vreg['forms'].select('edition',self._cw,entity=trinfo,mainform=False)subform.field_by_name('wf_info_for','subject').value=entity.eidtrfield=subform.field_by_name('by_transition','subject')trfield.widget=fwdgs.HiddenInput()trfield.value=transition.eidform.add_subform(subform)returnformclassWFHistoryView(EntityView):__regid__='wfhistory'__select__=relation_possible('wf_info_for',role='object')& \score_entity(lambdax:x.cw_adapt_to('IWorkflowable').workflow_history)title=_('Workflow history')defcell_call(self,row,col,view=None,title=title):_=self._cw._eid=self.cw_rset[row][col]sel='Any FS,TS,C,D'rql=' ORDERBY D DESC WHERE WF wf_info_for X,'\'WF from_state FS, WF to_state TS, WF comment C,'\'WF creation_date D'ifself._cw.vreg.schema.eschema('CWUser').has_perm(self._cw,'read'):sel+=',U,WF'rql+=', WF owned_by U?'headers=(_('from_state'),_('to_state'),_('comment'),_('date'),_('CWUser'))else:sel+=',WF'headers=(_('from_state'),_('to_state'),_('comment'),_('date'))rql='%s%s, X eid %%(x)s'%(sel,rql)try:rset=self._cw.execute(rql,{'x':eid})exceptUnauthorized:returnifrset:iftitle:self.w(u'<h2>%s</h2>\n'%_(title))self.wview('table',rset,headers=headers,cellvids={2:'editable-final'})classWFHistoryVComponent(component.EntityCtxComponent):"""display the workflow history for entities supporting it"""__regid__='wfhistory'__select__=component.EntityCtxComponent.__select__&WFHistoryView.__select__context='navcontentbottom'title=_('Workflow history')defrender_body(self,w):ifhasattr(self,'cell_call'):warn('[3.10] %s should now implement render_body instead of cell_call'%self.__class__,DeprecationWarning)self.w=w# pylint: disable=E1101self.cell_call(self.entity.cw_row,self.entity.cw_col)else:self.entity.view('wfhistory',w=w,title=None)classInContextWithStateView(EntityView):"""display incontext view for an entity as well as its current state"""__regid__='incontext-state'__select__=adaptable('IWorkflowable')defentity_call(self,entity):iwf=entity.cw_adapt_to('IWorkflowable')self.w(u'%s [%s]'%(entity.view('incontext'),iwf.printable_state))# workflow actions #############################################################classWorkflowActions(action.Action):"""fill 'workflow' sub-menu of the actions box"""__regid__='workflow'__select__=(action.Action.__select__&one_line_rset()&relation_possible('in_state'))submenu=_('workflow')order=10deffill_menu(self,box,menu):entity=self.cw_rset.get_entity(self.cw_rowor0,self.cw_color0)menu.label=u'%s: %s'%(self._cw._('state'),entity.cw_adapt_to('IWorkflowable').printable_state)menu.append_anyway=Truesuper(WorkflowActions,self).fill_menu(box,menu)defactual_actions(self):entity=self.cw_rset.get_entity(self.cw_rowor0,self.cw_color0)iworkflowable=entity.cw_adapt_to('IWorkflowable')hastr=Falsefortriniworkflowable.possible_transitions():url=entity.absolute_url(vid='statuschange',treid=tr.eid)yieldself.build_action(self._cw._(tr.name),url)hastr=True# don't propose to see wf if user can't pass any transitionifhastr:wfurl=iworkflowable.current_workflow.absolute_url()yieldself.build_action(self._cw._('view workflow'),wfurl)ifiworkflowable.workflow_history:wfurl=entity.absolute_url(vid='wfhistory')yieldself.build_action(self._cw._('view history'),wfurl)# workflow entity types views ##################################################_pvs=uicfg.primaryview_section_pvs.tag_subject_of(('Workflow','initial_state','*'),'hidden')_pvs.tag_object_of(('*','state_of','Workflow'),'hidden')_pvs.tag_object_of(('*','transition_of','Workflow'),'hidden')_pvs.tag_object_of(('*','default_workflow','Workflow'),'hidden')_abaa=uicfg.actionbox_appearsin_addmenu_abaa.tag_subject_of(('BaseTransition','condition','RQLExpression'),False)_abaa.tag_subject_of(('State','allowed_transition','BaseTransition'),False)_abaa.tag_object_of(('SubWorkflowExitPoint','destination_state','State'),False)_abaa.tag_object_of(('State','state_of','Workflow'),True)_abaa.tag_object_of(('BaseTransition','transition_of','Workflow'),False)_abaa.tag_object_of(('Transition','transition_of','Workflow'),True)_abaa.tag_object_of(('WorkflowTransition','transition_of','Workflow'),True)classWorkflowPrimaryView(TabbedPrimaryView):__select__=is_instance('Workflow')tabs=[_('wf_tab_info'),_('wfgraph'),]default_tab='wf_tab_info'classStateInContextView(EntityView):"""convenience trick, State's incontext view should not be clickable"""__regid__='incontext'__select__=is_instance('State')defcell_call(self,row,col):self.w(xml_escape(self._cw.view('textincontext',self.cw_rset,row=row,col=col)))classWorkflowTabTextView(PrimaryTab):__regid__='wf_tab_info'__select__=PrimaryTab.__select__&one_line_rset()&is_instance('Workflow')defrender_entity_attributes(self,entity):_=self._cw._self.w(u'<div>%s</div>'%(entity.printable_value('description')))self.w(u'<span>%s%s</span>'%(_("workflow_of").capitalize(),_(" :")))html=[]foreinentity.workflow_of:view=e.view('outofcontext')ifentity.eid==e.default_workflow[0].eid:view+=u' <span>[%s]</span>'%_('default_workflow')html.append(view)self.w(', '.join(vforvinhtml))self.w(u'<h2>%s</h2>'%_("Transition_plural"))rset=self._cw.execute('Any T,T,DS,T,TT ORDERBY TN WHERE T transition_of WF, WF eid %(x)s,''T type TT, T name TN, T destination_state DS?',{'x':entity.eid})self.wview('table',rset,'null',cellvids={1:'trfromstates',2:'outofcontext',3:'trsecurity',},headers=(_('Transition'),_('from_state'),_('to_state'),_('permissions'),_('type')),)classTransitionSecurityTextView(EntityView):__regid__='trsecurity'__select__=is_instance('Transition')defcell_call(self,row,col):_=self._cw._entity=self.cw_rset.get_entity(self.cw_row,self.cw_col)ifentity.require_group:self.w(u'<div>%s%s%s</div>'%(_('groups'),_(" :"),u', '.join((g.view('incontext')forginentity.require_group))))ifentity.condition:self.w(u'<div>%s%s%s</div>'%(_('conditions'),_(" :"),u'<br/>'.join((e.dc_title()foreinentity.condition))))classTransitionAllowedTextView(EntityView):__regid__='trfromstates'__select__=is_instance('Transition')defcell_call(self,row,col):entity=self.cw_rset.get_entity(self.cw_row,self.cw_col)self.w(u', '.join((e.view('outofcontext')foreinentity.reverse_allowed_transition)))# workflow entity types edition ################################################def_wf_items_for_relation(req,wfeid,wfrelation,field):wf=req.entity_from_eid(wfeid)rschema=req.vreg.schema[field.name]param='toeid'iffield.role=='subject'else'fromeid'returnsorted((e.view('combobox'),e.eid)foreingetattr(wf,'reverse_%s'%wfrelation)ifrschema.has_perm(req,'add',**{param:e.eid}))# TrInfo_afs.tag_subject_of(('TrInfo','to_state','*'),'main','hidden')_afs.tag_subject_of(('TrInfo','from_state','*'),'main','hidden')_afs.tag_attribute(('TrInfo','tr_count'),'main','hidden')# BaseTransition# XXX * allowed_transition BaseTransition# XXX BaseTransition destination_state *deftransition_states_vocabulary(form,field):entity=form.edited_entityifnotentity.has_eid():eids=form.linked_to.get(('transition_of','subject'))ifnoteids:return[]return_wf_items_for_relation(form._cw,eids[0],'state_of',field)returnfield.relvoc_unrelated(form)_afs.tag_subject_of(('*','destination_state','*'),'main','attributes')_affk.tag_subject_of(('*','destination_state','*'),{'choices':transition_states_vocabulary})_afs.tag_object_of(('*','allowed_transition','*'),'main','attributes')_affk.tag_object_of(('*','allowed_transition','*'),{'choices':transition_states_vocabulary})# Statedefstate_transitions_vocabulary(form,field):entity=form.edited_entityifnotentity.has_eid():eids=form.linked_to.get(('state_of','subject'))ifeids:return_wf_items_for_relation(form._cw,eids[0],'transition_of',field)return[]returnfield.relvoc_unrelated(form)_afs.tag_subject_of(('State','allowed_transition','*'),'main','attributes')_affk.tag_subject_of(('State','allowed_transition','*'),{'choices':state_transitions_vocabulary})# adaptaters ###################################################################classWorkflowIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):__select__=is_instance('Workflow')# XXX what if workflow of multiple types?defparent_entity(self):returnself.entity.workflow_ofandself.entity.workflow_of[0]orNoneclassWorkflowItemIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):__select__=is_instance('BaseTransition','State')defparent_entity(self):returnself.entity.workflowclassTransitionItemIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):__select__=is_instance('SubWorkflowExitPoint')defparent_entity(self):returnself.entity.reverse_subworkflow_exit[0]classTrInfoIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter):__select__=is_instance('TrInfo')defparent_entity(self):returnself.entity.for_entity# workflow images ##############################################################classWorkflowDotPropsHandler(DotPropsHandler):defnode_properties(self,stateortransition):"""return default DOT drawing options for a state or transition"""props=super(WorkflowDotPropsHandler,self).node_properties(stateortransition)ifhasattr(stateortransition,'state_of'):props['shape']='box'props['style']='filled'ifstateortransition.reverse_initial_state:props['fillcolor']='#88CC88'else:props['shape']='ellipse'descr=[]tr=stateortransitionifdescr:props['label']+=escape('\n'.join(descr))returnpropsclassWorkflowVisitor(object):def__init__(self,entity):self.entity=entitydefnodes(self):forstateinself.entity.reverse_state_of:state.complete()yieldstate.eid,statefortransitioninself.entity.reverse_transition_of:transition.complete()yieldtransition.eid,transitiondefedges(self):fortransitioninself.entity.reverse_transition_of:forincomingstateintransition.reverse_allowed_transition:yieldincomingstate.eid,transition.eid,transitionforoutgoingstateintransition.potential_destinations():yieldtransition.eid,outgoingstate.eid,transitionclassWorkflowGraphView(DotGraphView):__regid__='wfgraph'__select__=EntityView.__select__&one_line_rset()&is_instance('Workflow')defbuild_visitor(self,entity):returnWorkflowVisitor(entity)defbuild_dotpropshandler(self):returnWorkflowDotPropsHandler(self._cw)classTmpPngView(TmpFileViewMixin,EntityView):__regid__='tmppng'__select__=match_form_params('tmpfile')content_type='image/png'binary=Truedefcell_call(self,row=0,col=0):tmpfile=self._cw.session.data[self._cw.form['tmpfile']]self.w(open(tmpfile,'rb').read())os.unlink(tmpfile)