[formfields,formwidgets] move default size and maxlength from field to widget init (that is quite still unsatisfactorily)
"""workflow views:* IWorkflowable views and forms* workflow entities views (State, Transition, TrInfo):organization: Logilab:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"_=unicodefromlogilab.mtconverterimportxml_escapefromlogilab.common.graphimportescape,GraphGenerator,DotBackendfromcubicwebimportUnauthorized,viewfromcubicweb.selectorsimport(implements,has_related_entities,relation_possible,match_form_params)fromcubicweb.interfacesimportIWorkflowablefromcubicweb.webimportstdmsgs,action,component,formfromcubicweb.web.formimportFormViewMixInfromcubicweb.web.formfieldsimportStringField,RichTextFieldfromcubicweb.web.formwidgetsimportHiddenInput,SubmitButton,Buttonfromcubicweb.web.viewsimportTmpFileViewMixin,forms# IWorkflowable views #########################################################classChangeStateForm(forms.EntityFieldsForm):id='changestate'form_renderer_id='base'# don't want EntityFormRendererform_buttons=[SubmitButton(stdmsgs.YES),Button(stdmsgs.NO,cwaction='cancel')]__method=StringField(name='__method',initial='set_state',widget=HiddenInput)state=StringField(eidparam=True,widget=HiddenInput)trcomment=RichTextField(label=_('comment:'),eidparam=True)classChangeStateFormView(FormViewMixIn,view.EntityView):id='statuschange'title=_('status change')__select__=implements(IWorkflowable)&match_form_params('treid')defcell_call(self,row,col):entity=self.entity(row,col)state=entity.in_state[0]transition=self.req.eid_rset(self.req.form['treid']).get_entity(0,0)dest=transition.destination()_=self.req._form=self.vreg.select_object('forms','changestate',self.req,self.rset,row=row,col=col,entity=entity,redirect_path=self.redirectpath(entity))self.w(form.error_message())self.w(u'<h4>%s%s</h4>\n'%(_(transition.name),entity.view('oneline')))msg=_('status will change from %(st1)s to %(st2)s')%{'st1':_(state.name),'st2':_(dest.name)}self.w(u'<p>%s</p>\n'%msg)self.w(form.form_render(state=dest.eid,trcomment=u''))defredirectpath(self,entity):returnentity.rest_path()classWFHistoryVComponent(component.EntityVComponent):"""display the workflow history for entities supporting it"""id='wfhistory'__select__=(component.EntityVComponent.__select__&relation_possible('wf_info_for',role='object'))context='navcontentbottom'title=_('Workflow history')defcell_call(self,row,col,view=None):_=self.req._eid=self.rset[row][col]sel='Any FS,TS,WF,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.vreg.schema.eschema('CWUser').has_perm(self.req,'read'):sel+=',U,C'rql+=', WF owned_by U?'displaycols=range(5)headers=(_('from_state'),_('to_state'),_('comment'),_('date'),_('CWUser'))else:sel+=',C'displaycols=range(4)headers=(_('from_state'),_('to_state'),_('comment'),_('date'))rql='%s%s, X eid %%(x)s'%(sel,rql)try:rset=self.req.execute(rql,{'x':eid},'x')exceptUnauthorized:returnifrset:self.wview('table',rset,title=_(self.title),displayactions=False,displaycols=displaycols,headers=headers)# workflow entity types views #################################################classCellView(view.EntityView):id='cell'__select__=implements('TrInfo')defcell_call(self,row,col,cellvid=None):self.w(self.entity(row,col).printable_value('comment'))classStateInContextView(view.EntityView):"""convenience trick, State's incontext view should not be clickable"""id='incontext'__select__=implements('State')defcell_call(self,row,col):self.w(xml_escape(self.view('textincontext',self.rset,row=row,col=col)))# workflow images #############################################################classViewWorkflowAction(action.Action):id='workflow'__select__=implements('CWEType')&has_related_entities('state_of','object')category='mainactions'title=_('view workflow')defurl(self):entity=self.rset.get_entity(self.rowor0,self.color0)returnentity.absolute_url(vid='workflow')classCWETypeWorkflowView(view.EntityView):id='workflow'__select__=implements('CWEType')cache_max_age=60*60*2# stay in http cache for 2 hours by defaultdefcell_call(self,row,col,**kwargs):entity=self.entity(row,col)self.w(u'<h1>%s</h1>'%(self.req._('workflow for %s')%display_name(self.req,entity.name)))self.w(u'<img src="%s" alt="%s"/>'%(xml_escape(entity.absolute_url(vid='ewfgraph')),xml_escape(self.req._('graphical workflow for %s')%entity.name)))classWorkflowDotPropsHandler(object):def__init__(self,req):self._=req._defnode_properties(self,stateortransition):"""return default DOT drawing options for a state or transition"""props={'label':stateortransition.name,'fontname':'Courier'}ifhasattr(stateortransition,'state_of'):props['shape']='box'props['style']='filled'ifstateortransition.reverse_initial_state:props['color']='#88CC88'else:props['shape']='ellipse'descr=[]tr=stateortransitioniftr.require_group:descr.append('%s%s'%(self._('groups:'),','.join(g.nameforgintr.require_group)))iftr.condition:descr.append('%s%s'%(self._('condition:'),tr.condition))ifdescr:props['label']+=escape('\n'.join(descr))returnpropsdefedge_properties(self,transition,fromstate,tostate):return{'label':'','dir':'forward','color':'black','style':'filled'}classWorkflowVisitor: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,transitionyieldtransition.eid,transition.destination().eid,transitionclassCWETypeWorkflowImageView(TmpFileViewMixin,view.EntityView):id='ewfgraph'content_type='image/png'__select__=implements('CWEType')def_generate(self,tmpfile):"""display schema information for an entity"""entity=self.entity(self.row,self.col)visitor=WorkflowVisitor(entity)prophdlr=WorkflowDotPropsHandler(self.req)generator=GraphGenerator(DotBackend('workflow','LR',ratio='compress',size='30,12'))returngenerator.generate(visitor,prophdlr,tmpfile)