"""Set of HTML automatic forms to create, delete, copy or edit a single entityor a list of entities of the same type:organization: Logilab:copyright: 2001-2010 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"_=unicodefromcopyimportcopyfromsimplejsonimportdumpsfromlogilab.mtconverterimportxml_escapefromlogilab.common.decoratorsimportcachedfromcubicweb.selectorsimport(match_kwargs,one_line_rset,non_final_entity,specified_etype_implements,implements,yes)fromcubicweb.viewimportEntityViewfromcubicwebimporttagsfromcubicweb.webimportuicfg,stdmsgs,eid_param, \formfieldsasff,formwidgetsasfwfromcubicweb.web.formimportFormViewMixIn,FieldNotFoundfromcubicweb.web.viewsimportforms_pvdc=uicfg.primaryview_display_ctrlclassDeleteConfForm(forms.CompositeForm):__regid__='deleteconf'# XXX non_final_entity does not implement eclass_selector__select__=implements('Any')domid='deleteconf'copy_nav_params=Trueform_buttons=[fw.Button(stdmsgs.BUTTON_DELETE,cwaction='delete'),fw.Button(stdmsgs.BUTTON_CANCEL,cwaction='cancel')]def__init__(self,*args,**kwargs):super(DeleteConfForm,self).__init__(*args,**kwargs)done=set()forentityinself.cw_rset.entities():ifentity.eidindone:continuedone.add(entity.eid)subform=self._cw.vreg['forms'].select('base',self._cw,entity=entity,mainform=False)self.add_subform(subform)classDeleteConfFormView(FormViewMixIn,EntityView):"""form used to confirm deletion of some entities"""__regid__='deleteconf'title=_('delete')# don't use navigation, all entities asked to be deleted should be displayed# else we will only delete the displayed pagepaginable=Falsedefcall(self,onsubmit=None):"""ask for confirmation before real deletion"""req,w=self._cw,self.w_=req._w(u'<script type="text/javascript">updateMessage(\'%s\');</script>\n'%_('this action is not reversible!'))# XXX above message should have style of a warningw(u'<h4>%s</h4>\n'%_('Do you want to delete the following element(s) ?'))form=self._cw.vreg['forms'].select(self.__regid__,req,rset=self.cw_rset,onsubmit=onsubmit)w(u'<ul>\n')forentityinself.cw_rset.entities():# don't use outofcontext view or any other that may contain inline# edition formw(u'<li>%s</li>'%tags.a(entity.view('textoutofcontext'),href=entity.absolute_url()))w(u'</ul>\n')w(form.render())classEditionFormView(FormViewMixIn,EntityView):"""display primary entity edition form"""__regid__='edition'# add yes() so it takes precedence over deprecated views in baseforms,# though not baseforms based customized view__select__=one_line_rset()&non_final_entity()&yes()title=_('edition')defcell_call(self,row,col,**kwargs):entity=self.cw_rset.complete_entity(row,col)self.render_form(entity)defrender_form(self,entity):"""fetch and render the form"""self.form_title(entity)form=self._cw.vreg['forms'].select('edition',self._cw,entity=entity,submitmsg=self.submited_message())self.init_form(form,entity)self.w(form.render())definit_form(self,form,entity):"""customize your form before rendering here"""passdefform_title(self,entity):"""the form view title"""ptitle=self._cw._(self.title)self.w(u'<div class="formTitle"><span>%s%s</span></div>'%(entity.dc_type(),ptitleand'(%s)'%ptitle))defsubmited_message(self):"""return the message that will be displayed on successful edition"""returnself._cw._('entity edited')classCreationFormView(EditionFormView):"""display primary entity creation form"""__regid__='creation'__select__=specified_etype_implements('Any')&yes()title=_('creation')defcall(self,**kwargs):"""creation view for an entity"""# at this point we know etype is a valid entity type, thanks to our# selectoretype=kwargs.pop('etype',self._cw.form.get('etype'))entity=self._cw.vreg['etypes'].etype_class(etype)(self._cw)entity.eid=self._cw.varmaker.next()self.render_form(entity)defform_title(self,entity):"""the form view title"""if'__linkto'inself._cw.form:ifisinstance(self._cw.form['__linkto'],list):# XXX which one should be considered (case: add a ticket to a# version in jpl)rtype,linkto_eid,role=self._cw.form['__linkto'][0].split(':')else:rtype,linkto_eid,role=self._cw.form['__linkto'].split(':')linkto_rset=self._cw.eid_rset(linkto_eid)linkto_type=linkto_rset.description[0][0]ifrole=='subject':title=self._cw.__('creating %s (%s%s%s%%(linkto)s)'%(entity.e_schema,entity.e_schema,rtype,linkto_type))else:title=self._cw.__('creating %s (%s%%(linkto)s %s%s)'%(entity.e_schema,linkto_type,rtype,entity.e_schema))msg=title%{'linkto':self._cw.view('incontext',linkto_rset)}self.w(u'<div class="formTitle notransform"><span>%s</span></div>'%msg)else:super(CreationFormView,self).form_title(entity)defurl(self):"""return the url associated with this view"""returnself.create_url(self._cw.form.get('etype'))defsubmited_message(self):"""return the message that will be displayed on successful edition"""returnself._cw._('entity created')classCopyFormView(EditionFormView):"""display primary entity creation form initialized with values from another entity """__regid__='copy'title=_('copy')warning_message=_('Please note that this is only a shallow copy')defrender_form(self,entity):"""fetch and render the form"""# make a copy of entity to avoid altering the entity in the# request's cache.entity.complete()self.newentity=copy(entity)self.copying=entityself.newentity.eid=self._cw.varmaker.next()self.w(u'<script type="text/javascript">updateMessage("%s");</script>\n'%self._cw._(self.warning_message))super(CopyFormView,self).render_form(self.newentity)delself.newentitydefinit_form(self,form,entity):"""customize your form before rendering here"""super(CopyFormView,self).init_form(form,entity)ifentity.eid==self.newentity.eid:form.add_hidden(eid_param('__cloned_eid',entity.eid),self.copying.eid)forrschema,roleinform.editable_attributes():ifnotrschema.final:# ensure relation cache is filedrset=self.copying.related(rschema,role)self.newentity.set_related_cache(rschema,role,rset)defsubmited_message(self):"""return the message that will be displayed on successful edition"""returnself._cw._('entity copied')classTableEditForm(forms.CompositeForm):__regid__='muledit'domid='entityForm'onsubmit="return validateForm('%s', null);"%domidform_buttons=[fw.SubmitButton(_('validate modifications on selected items')),fw.ResetButton(_('revert changes'))]def__init__(self,req,rset,**kwargs):kwargs.setdefault('__redirectrql',rset.printable_rql())super(TableEditForm,self).__init__(req,rset=rset,**kwargs)forrowinxrange(len(self.cw_rset)):form=self._cw.vreg['forms'].select('edition',self._cw,rset=self.cw_rset,row=row,formtype='muledit',copy_nav_params=False,mainform=False)# XXX rely on the EntityCompositeFormRenderer to put the eid inputform.remove_field(form.field_by_name('eid'))self.add_subform(form)classTableEditFormView(FormViewMixIn,EntityView):__regid__='muledit'__select__=EntityView.__select__&yes()title=_('multiple edit')defcall(self,**kwargs):"""a view to edit multiple entities of the same type the first column should be the eid """# XXX overriding formvid (eg __form_id) necessary to make work edition:# the edit controller try to select the form with no rset but# entity=entity, and use this form to edit the entity. So we want# edition form there but specifying formvid may have other undesired# side effect. Maybe we should provide another variable optionally# telling which form the edit controller should select (eg difffers# between html generation / post handling form)form=self._cw.vreg['forms'].select(self.__regid__,self._cw,rset=self.cw_rset,copy_nav_params=True,formvid='edition')self.w(form.render())# click and edit handling ('reledit') ##########################################classDummyForm(object):__slots__=('event_args',)defform_render(self,**_args):returnu''defrender(self,**_args):returnu''defappend_field(self,*args):passdeffield_by_name(self,rtype,role,eschema=None):returnNoneclassClickAndEditFormView(FormViewMixIn,EntityView):"""form used to permit ajax edition of a relation or attribute of an entity in a view, if logged user have the permission to edit it. (double-click on the field to see an appropriate edition widget). """__regid__='doreledit'__select__=non_final_entity()&match_kwargs('rtype')# FIXME editableField class could be toggleable from userprefs_onclick=u"showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')"_onsubmit=("return inlineValidateRelationForm('%(rtype)s', '%(role)s', '%(eid)s', ""'%(divid)s', %(reload)s, '%(vid)s', '%(default)s', '%(lzone)s');")_cancelclick="hideInlineEdit(%s,\'%s\',\'%s\')"_defaultlandingzone=(u'<img title="%(msg)s" src="data/pen_icon.png" ''alt="%(msg)s"/>')_landingzonemsg=_('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 changervid=None,# vid to be applied to other side of rtype (non final relations only)default=None,# default valuelanding_zone=None# prepend value with a separate html element to click onto# (esp. needed when values are links)):"""display field to edit entity's `rtype` relation on click"""assertrtypeassertrolein('subject','object'),'%s is not an acceptable role value'%roleself._cw.add_js('cubicweb.edition.js')self._cw.add_css('cubicweb.form.css')ifdefaultisNone:default=xml_escape(self._cw._('<no value>'))schema=self._cw.vreg.schemaentity=self.cw_rset.get_entity(row,col)rschema=schema.rschema(rtype)lzone=self._build_landing_zone(landing_zone)# compute value, checking perms, build formifrschema.final:form=self._build_form(entity,rtype,role,'base',default,reload,lzone)ifnotself.should_edit_attribute(entity,rschema,form):self.w(entity.printable_value(rtype))returnvalue=entity.printable_value(rtype)ordefaultelse:rvid=self._compute_best_vid(entity.e_schema,rschema,role)rset=entity.related(rtype,role)ifrset:value=self._cw.view(rvid,rset)else:value=defaultifnotself.should_edit_relation(entity,rschema,role,rvid):ifrset:self.w(value)return# XXX do we really have to give lzone twice?form=self._build_form(entity,rtype,role,'base',default,reload,lzone,dict(vid=rvid,lzone=lzone))field=form.field_by_name(rtype,role,entity.e_schema)form.append_field(field)self.relation_form(lzone,value,form,self._build_renderer(entity,rtype,role))defshould_edit_attribute(self,entity,rschema,form):rtype=str(rschema)rdef=entity.e_schema.rdef(rtype)afs=uicfg.autoform_section.etype_get(entity.__regid__,rtype,'subject',rdef.object)if'main_hidden'inafsornotentity.has_perm('update'):returnFalseifnotrdef.has_perm(self._cw,'update',eid=entity.eid):returnFalsetry:form.field_by_name(rtype,'subject',entity.e_schema)exceptFieldNotFound:returnFalsereturnTruedefshould_edit_relation(self,entity,rschema,role,rvid):if((role=='subject'andnotrschema.has_perm(self._cw,'add',fromeid=entity.eid))or(role=='object'andnotrschema.has_perm(self._cw,'add',toeid=entity.eid))):returnFalsereturnTruedefrelation_form(self,lzone,value,form,renderer):"""xxx-reledit div (class=field) +-xxx div (class="editableField") | +-landing zone +-xxx-value div +-xxx-form div """w=self.wdivid=form.event_args['divid']w(u'<div id="%s-reledit" class="field" 'u'onmouseout="addElementClass(jQuery(\'#%s\'), \'hidden\')" 'u'onmouseover="removeElementClass(jQuery(\'#%s\'), \'hidden\')">'%(divid,divid,divid))w(u'<div id="%s-value" class="editableFieldValue">%s</div>'%(divid,value))w(form.render(renderer=renderer))w(u'<div id="%s" class="editableField hidden" onclick="%s" title="%s">'%(divid,xml_escape(self._onclick%form.event_args),self._cw._(self._landingzonemsg)))w(lzone)w(u'</div>')w(u'</div>')def_compute_best_vid(self,eschema,rschema,role):dispctrl=_pvdc.etype_get(eschema,rschema,role)ifdispctrl.get('rvid'):returndispctrl['rvid']ifeschema.rdef(rschema,role).role_cardinality(role)in'+*':returnself._many_rvidreturnself._one_rviddef_build_landing_zone(self,lzone):returnlzoneorself._defaultlandingzone%{'msg':xml_escape(self._cw._(self._landingzonemsg))}def_build_renderer(self,entity,rtype,role):returnself._cw.vreg['formrenderers'].select('base',self._cw,entity=entity,display_label=False,display_help=False,table_class='',button_bar_class='buttonbar',display_progress_div=False)def_build_args(self,entity,rtype,role,formid,default,reload,lzone,extradata=None):divid='%s-%s-%s'%(rtype,role,entity.eid)event_args={'divid':divid,'eid':entity.eid,'rtype':rtype,'reload':dumps(reload),'default':default,'role':role,'vid':u'','lzone':lzone}ifextradata:event_args.update(extradata)returndivid,event_argsdef_build_form(self,entity,rtype,role,formid,default,reload,lzone,extradata=None,**formargs):divid,event_args=self._build_args(entity,rtype,role,formid,default,reload,lzone,extradata)onsubmit=self._onsubmit%event_argscancelclick=self._cancelclick%(entity.eid,rtype,divid)form=self._cw.vreg['forms'].select(formid,self._cw,entity=entity,domid='%s-form'%divid,cssstyle='display: none',onsubmit=onsubmit,action='#',form_buttons=[fw.SubmitButton(),fw.Button(stdmsgs.BUTTON_CANCEL,onclick=cancelclick)],**formargs)form.event_args=event_argsreturnformclassAutoClickAndEditFormView(ClickAndEditFormView):"""same as ClickAndEditFormView but checking if the view *should* be applied by checking uicfg configuration and composite relation property. """__regid__='reledit'_onclick=(u"loadInlineEditionForm(%(eid)s, '%(rtype)s', '%(role)s', ""'%(divid)s', %(reload)s, '%(vid)s', '%(default)s', '%(lzone)s');")defshould_edit_relation(self,entity,rschema,role,rvid):eschema=entity.e_schemartype=str(rschema)# XXX check autoform_section. what if 'generic'?dispctrl=_pvdc.etype_get(eschema,rtype,role)vid=dispctrl.get('vid','reledit')ifvid!='reledit':# reledit explicitly disabledreturnFalseifeschema.rdef(rschema,role).composite==role:returnFalsereturnsuper(AutoClickAndEditFormView,self).should_edit_relation(entity,rschema,role,rvid)def_build_form(self,entity,rtype,role,formid,default,reload,lzone,extradata=None,**formargs):_divid,event_args=self._build_args(entity,rtype,role,formid,default,reload,lzone,extradata)form=DummyForm()form.event_args=event_argsreturnformdef_build_renderer(self,entity,rtype,role):pass