importcopyfromlogilab.mtconverterimportxml_escapefromcubicwebimportneg_rolefromcubicweb.schemaimportdisplay_namefromcubicweb.utilsimportjsonfromcubicweb.selectorsimportnon_final_entity,match_kwargsfromcubicweb.viewimportEntityViewfromcubicweb.webimportuicfg,stdmsgsfromcubicweb.web.formimportFormViewMixIn,FieldNotFoundfromcubicweb.web.formwidgetsimportButton,SubmitButtonclassDummyForm(object):__slots__=('event_args',)defform_render(self,**_args):returnu''defrender(self,*_args,**_kwargs):returnu''defappend_field(self,*args):passdeffield_by_name(self,rtype,role,eschema=None):returnNoneclassClickAndEditFormView(FormViewMixIn,EntityView):__regid__='doreledit'__select__=non_final_entity()&match_kwargs('rtype')# ui side continuations_onclick=(u"cw.reledit.loadInlineEditionForm('%(formid)s', %(eid)s, '%(rtype)s', '%(role)s', ""'%(divid)s', %(reload)s, '%(vid)s', '%(default_value)s');")_cancelclick="cw.reledit.cleanupAfterCancel('%s')"# ui side actions/buttons_addzone=(u'<img title="%(msg)s" src="data/plus.png" 'u'alt="%(msg)s"/>')_addmsg=_('click to add a value')_deletezone=(u'<img title="%(msg)s" src="data/cancel.png" alt="%(msg)s"/>')_deletemsg=_('click to delete this value')_editzone=(u'<img title="%(msg)s" src="data/pen_icon.png" alt="%(msg)s"/>')_editzonemsg=_('click to edit this field')# default relation vids according to cardinality_one_rvid='incontext'_many_rvid='csv'defcell_call(self,row,col,rtype=None,role='subject',reload=False,# controls reloading the whole page after change# boolean, eid (to redirect), or# function taking the subject entity & returning a boolean or an eidrvid=None,# vid to be applied to other side of rtype (non final relations only)default_value=None,formid=None):"""display field to edit entity's `rtype` relation on click"""assertrtypeassertrolein('subject','object'),'%s is not an acceptable role value'%roleifself.__regid__=='doreledit':assertformidself._cw.add_js('cubicweb.reledit.js')ifformid:self._cw.add_js('cubicweb.edition.js')self._cw.add_css('cubicweb.form.css')entity=self.cw_rset.get_entity(row,col)rschema=self._cw.vreg.schema[rtype]reload=self._compute_reload(entity,rschema,role,reload)default_value=self._compute_default_value(entity,rschema,role,default_value)# compute value, checking perms, build & display formdivid=self._build_divid(rtype,role,entity.eid)ifrschema.final:value=entity.printable_value(rtype)form,renderer=self._build_form(entity,rtype,role,divid,'base',default_value,reload)ifnotself._should_edit_attribute(entity,rschema,form):self.w(value)returnvalue=valueordefault_valuefield=form.field_by_name(rtype,role,entity.e_schema)form.append_field(field)self.view_form(divid,value,form,renderer)else:rvid=self._compute_best_vid(entity.e_schema,rschema,role)related_rset=entity.related(rtype,role)ifrelated_rset:value=self._cw.view(rvid,related_rset)else:value=default_valuettypes=self._compute_ttypes(rschema,role)ifnotself._should_edit_relation(entity,rschema,role):self.w(value)return# this is for attribute-like composites (1 target type, 1 related entity at most)add_related=self._may_add_related(related_rset,entity,rschema,role,ttypes)edit_related=self._may_edit_related_entity(related_rset,entity,rschema,role,ttypes)delete_related=edit_relatedandself._may_delete_related(related_rset,entity,rschema,role)# compute formidiflen(ttypes)>1:# redundant safety beltformid='base'else:afs=uicfg.autoform_section.etype_get(entity.e_schema,rschema,role,ttypes[0])# is there an afs spec that says we should edit# the rschema as an attribute ?ifafsand'main_attributes'inafs:formid='base'form,renderer=self._build_form(entity,rtype,role,divid,formid,default_value,reload,dict(vid=rvid),edit_related,add_relatedandttypes[0])self.view_form(divid,value,form,renderer,edit_related,delete_related,add_related)def_compute_best_vid(self,eschema,rschema,role):ifeschema.rdef(rschema,role).role_cardinality(role)in'+*':returnself._many_rvidreturnself._one_rviddef_compute_ttypes(self,rschema,role):dual_role=neg_role(role)returngetattr(rschema,'%ss'%dual_role)()def_compute_reload(self,entity,rschema,role,reload):rule=uicfg.reledit_ctrl.etype_get(entity.e_schema.type,rschema.type,role,'*')ctrl_reload=rule.get('reload',reload)ifcallable(ctrl_reload):ctrl_reload=ctrl_reload(entity)ifisinstance(ctrl_reload,int)andctrl_reload>1:# not True/Falsectrl_reload=self._cw.build_url(ctrl_reload)returnctrl_reloaddef_compute_default_value(self,entity,rschema,role,default_value):etype=entity.e_schema.typerule=uicfg.reledit_ctrl.etype_get(etype,rschema.type,role,'*')ctrl_default=rule.get('default_value',default_value)ifctrl_default:returnctrl_defaultifdefault_valueisNone:returnxml_escape(self._cw._('<%s not specified>')%display_name(self._cw,rschema.type,role))returndefault_valuedef_is_composite(self,eschema,rschema,role):returneschema.rdef(rschema,role).composite==roledef_may_add_related(self,related_rset,entity,rschema,role,ttypes):""" ok for attribute-like composite entities """ifself._is_composite(entity.e_schema,rschema,role):iflen(ttypes)>1:# wrong cardinality: do not handlereturnFalsettype=ttypes[0]card=rschema.rdef(entity.e_schema,ttype).role_cardinality(role)ifrelated_rsetandcardin'?1':returnFalseifrschema.has_perm(self._cw,'add',toetype=ttype):returnTruereturnFalsedef_may_edit_related_entity(self,related_rset,entity,rschema,role,ttypes):""" controls the edition of the related entity """ifentity.e_schema.rdef(rschema,role).role_cardinality(role)notin'?1':returnFalseiflen(related_rset.rows)!=1:returnFalseiflen(ttypes)>1:returnFalseifnotself._is_composite(entity.e_schema,rschema,role):returnFalsereturnrelated_rset.get_entity(0,0).cw_has_perm('update')def_may_delete_related(self,related_rset,entity,rschema,role):# we assume may_edit_relatedkwargs={'fromeid':entity.eid}ifrole=='subject'else{'toeid':entity.eid}ifnotrschema.has_perm(self._cw,'delete',**kwargs):returnFalseforrelated_entityinrelated_rset.entities():ifnotrelated_entity.cw_has_perm('delete'):returnFalsereturnTruedef_build_edit_zone(self):returnself._editzone%{'msg':xml_escape(_(self._cw._(self._editzonemsg)))}def_build_delete_zone(self):returnself._deletezone%{'msg':xml_escape(self._cw._(self._deletemsg))}def_build_add_zone(self):returnself._addzone%{'msg':xml_escape(self._cw._(self._addmsg))}def_build_divid(self,rtype,role,entity_eid):""" builds an id for the root div of a reledit widget """return'%s-%s-%s'%(rtype,role,entity_eid)def_build_args(self,entity,rtype,role,formid,default_value,reload,extradata=None):divid=self._build_divid(rtype,role,entity.eid)event_args={'divid':divid,'eid':entity.eid,'rtype':rtype,'formid':formid,'reload':json.dumps(reload),'default_value':default_value,'role':role,'vid':u''}ifextradata:event_args.update(extradata)returnevent_argsdef_build_form(self,entity,rtype,role,divid,formid,default_value,reload,extradata=None,edit_related=False,add_related=False,**formargs):event_args=self._build_args(entity,rtype,role,formid,default_value,reload,extradata)cancelclick=self._cancelclick%dividifedit_relatedandnotadd_related:display_fields=Nonedisplay_label=Truerelated_entity=entity.related(rtype,role).get_entity(0,0)self._cw.form['eid']=related_entity.eidelifadd_related:display_fields=Nonedisplay_label=True_new_entity=self._cw.vreg['etypes'].etype_class(add_related)(self._cw)_new_entity.eid=self._cw.varmaker.next()related_entity=_new_entityself._cw.form['__linkto']='%s:%s:%s'%(rtype,entity.eid,neg_role(role))else:# base case: edition/attribute relationdisplay_fields=[(rtype,role)]display_label=Falserelated_entity=entityform=self._cw.vreg['forms'].select(formid,self._cw,rset=related_entity.as_rset(),entity=related_entity,domid='%s-form'%divid,display_fields=display_fields,formtype='inlined',action=self._cw.build_url('validateform?__onsuccess=window.parent.cw.reledit.onSuccess'),cwtarget='eformframe',cssstyle='display: none',**formargs)# pass reledit argumentsforpname,pvalueinevent_args.iteritems():form.add_hidden('__reledit|'+pname,pvalue)# handle buttonsifform.form_buttons:# edition, deleteform_buttons=[]forbuttoninform.form_buttons:ifnotbutton.label.endswith('apply'):ifbutton.label.endswith('cancel'):button=copy.deepcopy(button)button.cwaction=Nonebutton.onclick=cancelclickform_buttons.append(button)form.form_buttons=form_buttonselse:# baseform.form_buttons=[SubmitButton(),Button(stdmsgs.BUTTON_CANCEL,onclick=cancelclick)]form.event_args=event_argsrenderer=self._cw.vreg['formrenderers'].select('base',self._cw,entity=related_entity,display_label=display_label,display_help=False,table_class='',button_bar_class='buttonbar',display_progress_div=False)returnform,rendererdef_should_edit_attribute(self,entity,rschema,form):# examine rtagsnoedit=uicfg.reledit_ctrl.etype_get(entity.e_schema,rschema.type,'subject').get('noedit',False)ifnoedit:returnFalserdef=entity.e_schema.rdef(rschema)afs=uicfg.autoform_section.etype_get(entity.__regid__,rschema,'subject',rdef.object)if'main_hidden'inafs:returnFalse# check permissionsifnotentity.cw_has_perm('update'):returnFalserdef=entity.e_schema.rdef(rschema)ifnotrdef.has_perm(self._cw,'update',eid=entity.eid):returnFalse# XXX ?try:form.field_by_name(str(rschema),'subject',entity.e_schema)exceptFieldNotFound:returnFalsereturnTruedef_should_edit_relation(self,entity,rschema,role):# examine rtagsrtype=rschema.typenoedit=uicfg.reledit_ctrl.etype_get(entity.e_schema,rtype,role).get('noedit',False)ifnoedit:returnFalserdef=entity.e_schema.rdef(rschema,role)afs=uicfg.autoform_section.etype_get(entity.__regid__,rschema,role,rdef.object)if'main_hidden'inafs:returnFalseperm_args={'fromeid':entity.eid}ifrole=='subject'else{'toeid':entity.eid}returnrschema.has_perm(self._cw,'add',**perm_args)defview_form(self,divid,value,form=None,renderer=None,edit_related=False,delete_related=False,add_related=False):w=self.ww(u'<div id="%(id)s-reledit" onmouseout="%(out)s" onmouseover="%(over)s">'%{'id':divid,'out':"jQuery('#%s').addClass('hidden')"%divid,'over':"jQuery('#%s').removeClass('hidden')"%divid})w(u'<div id="%s-value" class="editableFieldValue">'%divid)w(value)w(u'</div>')w(form.render(renderer=renderer))w(u'<div id="%s" class="editableField hidden">'%divid)args=form.event_args.copy()ifnotadd_related:# excludes editionargs['formid']='edition'w(u'<div id="%s-update" class="editableField" onclick="%s" title="%s">'%(divid,xml_escape(self._onclick%args),self._cw._(self._editzonemsg)))w(self._build_edit_zone())w(u'</div>')else:args['formid']='edition'w(u'<div id="%s-add" class="editableField" onclick="%s" title="%s">'%(divid,xml_escape(self._onclick%args),self._cw._(self._addmsg)))w(self._build_add_zone())w(u'</div>')ifdelete_related:args['formid']='deleteconf'w(u'<div id="%s-delete" class="editableField" onclick="%s" title="%s">'%(divid,xml_escape(self._onclick%args),self._cw._(self._deletemsg)))w(self._build_delete_zone())w(u'</div>')w(u'</div>')w(u'</div>')classAutoClickAndEditFormView(ClickAndEditFormView):__regid__='reledit'def_build_form(self,entity,rtype,role,divid,formid,default_value,reload,extradata=None,edit_related=False,add_related=False,**formargs):event_args=self._build_args(entity,rtype,role,'base',default_value,reload,extradata)form=DummyForm()form.event_args=event_argsreturnform,None