"""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-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr"""__docformat__="restructuredtext en"fromcopyimportcopyfromsimplejsonimportdumpsfromlogilab.mtconverterimporthtml_escapefromlogilab.common.decoratorsimportcachedfromcubicweb.interfacesimportIWorkflowablefromcubicweb.common.utilsimportmake_uidfromcubicweb.common.uilibimportcutfromcubicweb.common.selectorsimport(etype_form_selector,kwargs_selector,one_line_rset,interface_selector,req_form_params_selector,accept_selector)fromcubicweb.common.viewimportEntityViewfromcubicweb.webimportINTERNAL_FIELD_VALUE,stdmsgs,eid_paramfromcubicweb.web.controllerimportNAV_FORM_PARAMETERSfromcubicweb.web.widgetsimportcheckbox,InputWidget,ComboBoxWidgetfromcubicweb.web.formimportEntityForm,relation_id_=unicodeclassDeleteConfForm(EntityForm):id='deleteconf'title=_('delete')domid='deleteconf'onsubmit=Nonedefcall(self):"""ask for confirmation before real deletion"""_=self.req._self.req.add_js('cubicweb.edition.js')self.w(u'<script type="text/javascript">updateMessage(\'%s\');</script>\n'%_('this action is not reversible!'))# XXX above message should have style of a warningself.w(u'<h4>%s</h4>\n'%_('Do you want to delete the following element(s) ?'))ifself.onsubmit:self.w(u'<form id="deleteconf" action="%s" onsubmit="%s" method="post">'%(self.build_url(),self.onsubmit))else:self.w(u'<form id="deleteconf" action="%s" method="post">'%(self.build_url()))self.w(u'<fieldset>\n')self.display_rset()#self.w(u'<input type="hidden" name="rql" value="%s"/>' % self.req.form['rql'])self.w(u'<input type="hidden" name="__form_id" value="%s"/>'%self.id)self.w(self.button_delete(label=stdmsgs.YES))self.w(self.button_cancel(label=stdmsgs.NO))forparaminNAV_FORM_PARAMETERS:value=self.req.form.get(param)ifvalue:self.w(u'<input type="hidden" name="%s" value="%s"/>'%(param,value))self.w(u'</fieldset></form>\n')defdisplay_rset(self):self.w(u'<ul>\n')done=set()foriinxrange(self.rset.rowcount):ifself.rset[i][0]indone:continuedone.add(self.rset[i][0])self.cell_call(i,0)self.w(u'</ul>\n')defcell_call(self,row,col):entity=self.entity(row,col)self.w(u'<li>')self.w(u'<input type="hidden" name="eid" value="%s" />'%entity.eid)self.w(u'<input type="hidden" name="%s" value="%s"/>\n'%(eid_param('__type',entity.eid),self.rset.description[row][0]))self.w(u'<a href="%s">'%html_escape(entity.absolute_url()))# don't use outofcontext view or any other that may contain inline edition formself.w(html_escape(entity.view('textoutofcontext')))self.w(u'</a>')self.w(u'</li>')classChangeStateForm(EntityForm):id='statuschange'title=_('status change')__selectors__=(interface_selector,req_form_params_selector)accepts_interfaces=(IWorkflowable,)form_params=('treid',)defcell_call(self,row,col,vid='secondary'):entity=self.entity(row,col)eid=entity.eidstate=entity.in_state[0]transition=self.req.eid_rset(self.req.form['treid']).get_entity(0,0)dest=transition.destination()self.req.add_js('cubicweb.edition.js')_=self.req._self.w(self.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(u'<form action="%s" onsubmit="return freezeFormButtons(\'entityForm\');" method="post" id="entityForm">\n'%self.build_url('edit'))self.w(u'<div id="progress">%s</div>'%_('validating...'))self.w(u'<fieldset>\n')#self.w(u'<input id="errorurl" type="hidden" name="__errorurl" value="%s"/>\n'# % html_escape(self.req.url()))self.w(u'<input type="hidden" name="__form_id" value="%s"/>\n'%self.id)self.w(u'<input type="hidden" name="eid" value="%s" />'%eid)self.w(u'<input type="hidden" name="%s" value="%s"/>\n'%(eid_param('__type',eid),entity.e_schema))self.w(u'<input type="hidden" name="%s" value="%s"/>\n'%(eid_param('state',eid),dest.eid))self.w(u'<input type="hidden" name="__redirectpath" value="%s"/>\n'%html_escape(self.redirectpath(entity)))self.fill_form(entity,state,dest)self.w(u'<input type="hidden" name="__method" value="set_state"/>\n')self.w(self.button_ok(label=stdmsgs.YES,tabindex=self.req.next_tabindex()))self.w(self.button_cancel(label=stdmsgs.NO,tabindex=self.req.next_tabindex()))self.w(u'</fieldset>\n')self.w(u'</form>')deffill_form(self,entity,state,dest):# hack to use the widget for comment_formattrinfo=self.vreg.etype_class('TrInfo')(self.req,None)# widget are cached, copy it since we want to modify its name attributewdg=trinfo.get_widget('comment_format')wdg.name='trcommentformat'# set a value in entity to avoid lookup for a non existant attribute...trinfo['trcommentformat']=u''# comment format/content have to be grouped using the original entity eidwdg.rname=eid_param('trcommentformat',entity.eid)self.w(wdg.render_label(trinfo))self.w(wdg._edit_render(trinfo))self.w(u'<br/>\n')cformname=eid_param('trcomment',entity.eid)self.w(u'<label for="%s">%s</label>\n'%(cformname,self.req._('comment:')))self.w(u'<textarea rows="10" cols="80" name="%s" tabindex="%s"></textarea><br/>\n'%(cformname,self.req.next_tabindex()))defredirectpath(self,entity):returnentity.rest_path()classClickAndEditForm(EntityForm):id='reledit'__selectors__=(kwargs_selector,)expected_kwargs=('rtype',)#FIXME editableField class could be toggleable from userprefsEDITION_BODY='''<div class="editableField" id="%(divid)s" ondblclick="showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')">%(value)s</div><form style="display: none;" onsubmit="return inlineValidateForm('%(divid)s-form', '%(rtype)s', '%(eid)s', '%(divid)s', %(reload)s);" id="%(divid)s-form" action="#"><fieldset><input type="hidden" name="eid" value="%(eid)s" /><input type="hidden" name="__maineid" value="%(eid)s" /><input type="hidden" name="__type:%(eid)s" value="%(etype)s" />%(attrform)s</fieldset><div class="buttonbar">%(ok)s%(cancel)s</div></form>'''defcell_call(self,row,col,rtype=None,role='subject',reload=False):entity=self.entity(row,col)ifgetattr(entity,rtype)isNone:value=self.req._('not specified')else:value=entity.printable_value(rtype)ifnotentity.has_perm('update'):self.w(value)returnself.req.add_js(('cubicweb.ajax.js','cubicweb.edition.js'))eid=entity.eidedit_key=make_uid('%s-%s'%(rtype,eid))divid='d%s'%edit_keywidget=entity.get_widget(rtype,'subject')eschema=entity.e_schemaattrform=widget.edit_render(entity,useid='i%s'%edit_key)ok=(u'<input class="validateButton" type="submit" name="__action_apply" value="%s" tabindex="%s" />'%(self.req._(stdmsgs.BUTTON_OK),self.req.next_tabindex()))cancel=(u'<input class="validateButton" type="button" ''value="%s" onclick="cancelInlineEdit(%s, \'%s\', \'%s\')" tabindex="%s" />'%(self.req._(stdmsgs.BUTTON_CANCEL),eid,rtype,divid,self.req.next_tabindex()))self.w(self.EDITION_BODY%{'eid':eid,'rtype':rtype,'etype':entity.e_schema,'attrform':attrform,'action':self.build_url('edit'),# NOTE: actually never gets called'ok':ok,'cancel':cancel,'value':value,'reload':dumps(reload),'divid':divid,})classEditionForm(EntityForm):"""primary entity edition form When generating a new attribute_input, the editor will look for a method named 'default_ATTRNAME' on the entity instance, where ATTRNAME is the name of the attribute being edited. You may use this feature to compute dynamic default values such as the 'tomorrow' date or the user's login being connected """__selectors__=(one_line_rset,accept_selector)id='edition'title=_('edition')controller='edit'skip_relations=EntityForm.skip_relations.copy()EDITION_BODY=u'''\%(errormsg)s<form id="%(formid)s" class="entityForm" cubicweb:target="eformframe" method="post" onsubmit="%(onsubmit)s" enctype="%(enctype)s" action="%(action)s">%(title)s <div id="progress">%(inprogress)s</div> <div class="iformTitle"><span>%(mainattrs_label)s</span></div> <div class="formBody"><fieldset>%(base)s%(attrform)s%(relattrform)s</fieldset>%(relform)s </div> <table width="100%%"> <tbody> <tr><td align="center">%(validate)s </td><td style="align: right; width: 50%%;">%(apply)s%(cancel)s </td></tr> </tbody> </table></form>'''defcell_call(self,row,col,**kwargs):self.req.add_js(('cubicweb.ajax.js','cubicweb.edition.js'))self.req.add_css('cubicweb.form.css')entity=self.complete_entity(row,col)self.edit_form(entity,kwargs)defedit_form(self,entity,kwargs):varmaker=self.req.get_page_data('rql_varmaker')ifvarmakerisNone:varmaker=self.req.varmakerself.req.set_page_data('rql_varmaker',varmaker)self.varmaker=varmakerself.w(self.EDITION_BODY%self.form_context(entity,kwargs))defform_context(self,entity,kwargs):"""returns the dictionnary used to fill the EDITION_BODY template If you create your own edition form, you can probably just override `EDITION_BODY` and `form_context` """ifself.need_multipart(entity):enctype='multipart/form-data'else:enctype='application/x-www-form-urlencoded'self._hiddens=[]ifentity.eidisNone:entity.eid=self.varmaker.next()# XXX (hack) action_title might need __linkto req's original value# and widgets such as DynamicComboWidget might change it# so we need to compute title before calling atttributes_formformtitle=self.action_title(entity)# be sure to call .*_form first so tabindexes are correct and inlined# fields errors are consumedifnotentity.has_eid()orentity.has_perm('update'):attrform=self.attributes_form(entity,kwargs)else:attrform=''inlineform=self.inline_entities_form(entity,kwargs)relform=self.relations_form(entity,kwargs)vindex=self.req.next_tabindex()aindex=self.req.next_tabindex()cindex=self.req.next_tabindex()self.add_hidden_web_behaviour_params(entity)_=self.req._return{'formid':self.domid,'onsubmit':self.on_submit(entity),'enctype':enctype,'errormsg':self.error_message(),'action':self.build_url('validateform'),'eids':entity.has_eid()and[entity.eid]or[],'inprogress':_('validating...'),'title':formtitle,'mainattrs_label':_('main informations'),'reseturl':self.redirect_url(entity),'attrform':attrform,'relform':relform,'relattrform':inlineform,'base':self.base_form(entity,kwargs),'validate':self.button_ok(tabindex=vindex),'apply':self.button_apply(tabindex=aindex),'cancel':self.button_cancel(tabindex=cindex),}@propertydefformid(self):returnself.iddefaction_title(self,entity):"""form's title"""ptitle=self.req._(self.title)returnu'<div class="formTitle"><span>%s%s</span></div>'%(entity.dc_type(),ptitleand'(%s)'%ptitle)defbase_form(self,entity,kwargs):output=[]forname,value,iidinself._hiddens:ifisinstance(value,basestring):value=html_escape(value)ifiid:output.append(u'<input id="%s" type="hidden" name="%s" value="%s" />'%(iid,name,value))else:output.append(u'<input type="hidden" name="%s" value="%s" />'%(name,value))returnu'\n'.join(output)defadd_hidden_web_behaviour_params(self,entity):"""inserts hidden params controlling how errors and redirection should be handled """req=self.reqself._hiddens.append((u'__maineid',entity.eid,u''))self._hiddens.append((u'__errorurl',req.url(),u'errorurl'))self._hiddens.append((u'__form_id',self.formid,u''))forparaminNAV_FORM_PARAMETERS:value=req.form.get(param)ifvalue:self._hiddens.append((param,value,u''))msg=self.submited_message()# If we need to directly attach the new object to another oneforlinktoinreq.list_form_param('__linkto'):self._hiddens.append(('__linkto',linkto,''))msg='%s%s'%(msg,self.req._('and linked'))self._hiddens.append(('__message',msg,''))defattributes_form(self,entity,kwargs,include_eid=True):"""create a form to edit entity's attributes"""html=[]w=html.appendeid=entity.eidwdg=entity.get_widgetlines=(wdg(rschema,x)forrschema,xinself.editable_attributes(entity))ifinclude_eid:self._hiddens.append(('eid',entity.eid,''))self._hiddens.append((eid_param('__type',eid),entity.e_schema,''))w(u'<table id="%s" class="%s" style="width:100%%;">'%(kwargs.get('tab_id','entityForm%s'%eid),kwargs.get('tab_class','attributeForm')))forwidgetinlines:w(u'<tr>\n<th class="labelCol">%s</th>'%widget.render_label(entity))error=widget.render_error(entity)iferror:w(u'<td class="error" style="width:100%;">')else:w(u'<td style="width:100%;">')iferror:w(error)w(widget.edit_render(entity))w(widget.render_help(entity))w(u'</td>\n</tr>')w(u'</table>')returnu'\n'.join(html)defeditable_attributes(self,entity):# XXX both (add, delete)return[(rschema,x)forrschema,_,xinentity.relations_by_category(('primary','secondary'),'add')ifrschema!='eid']defrelations_form(self,entity,kwargs):req=self.req_=self.req._label=u'%s :'%_('This %s'%entity.e_schema).capitalize()eid=entity.eidhtml=[]pendings=list(self.restore_pending_inserts(entity))w=html.appendw(u'<fieldset class="subentity">')w(u'<legend class="iformTitle">%s</legend>'%label)w(u'<table id="relatedEntities">')forrowinself.relations_table(entity):ifrow[2]:w(u'<tr><th class="labelCol">%s</th>'%row[0].display_name(req,row[1]))w(u'<td>')w(u'<ul>')forviewparamsinrow[2]:w(u'<li class="invisible">%s<div id="span%s" class="%s">%s</div></li>'%(viewparams[1],viewparams[0],viewparams[2],viewparams[3]))ifnotself.force_displayandself.maxrelitems<len(row[2]):w(u'<li class="invisible">%s</li>'%self.force_display_link())w(u'</ul>')w(u'</td>')w(u'</tr>')ifnotpendings:w(u'<tr><th> </th><td> </td></tr>')else:forrowinpendings:w(u'<tr id="tr%s">'%row[1])w(u'<th>%s</th>'%row[3])w(u'<td>')w(u'<a class="handle" title="%s" href="%s">[x]</a>'%(_('cancel this insert'),row[2]))w(u'<a id="a%s" class="editionPending" href="%s">%s</a>'%(row[1],row[4],html_escape(row[5])))w(u'</td>')w(u'</tr>')w(u'<tr id="relationSelectorRow_%s" class="separator">'%eid)w(u'<th class="labelCol">')w(u'<span>%s</span>'%_('add relation'))w(u'<select id="relationSelector_%s" tabindex="%s" onchange="javascript:showMatchingSelect(this.options[this.selectedIndex].value,%s);">'%(eid,req.next_tabindex(),html_escape(dumps(eid))))w(u'<option value="">%s</option>'%_('select a relation'))fori18nrtype,rschema,targetinentity.srelations_by_category(('generic','metadata'),'add'):w(u'<option value="%s_%s">%s</option>'%(rschema,target,i18nrtype))w(u'</select>')w(u'</th>')w(u'<td id="unrelatedDivs_%s"></td>'%eid)w(u'</tr>')w(u'</table>')w(u'</fieldset>')return'\n'.join(html)definline_entities_form(self,entity,kwargs):"""create a form to edit entity's inlined relations"""result=[]_=self.req._forrschema,targettypes,xinentity.relations_by_category('inlineview','add'):# show inline forms only if there's one possible target type# for rschemaiflen(targettypes)!=1:self.warning('entity related by the %s relation should have ''inlined form but there is multiple target types, ''dunno what to do',rschema)continuetargettype=targettypes[0].typeifself.should_inline_relation_form(entity,rschema,targettype,x):result.append(u'<div id="inline%sslot">'%rschema)existant=entity.has_eid()andentity.related(rschema)ifexistant:# display inline-edition view for all existing related entitiesresult.append(self.view('inline-edition',existant,ptype=entity.e_schema,peid=entity.eid,rtype=rschema,role=x,**kwargs))ifx=='subject':card=rschema.rproperty(entity.e_schema,targettype,'cardinality')[0]else:card=rschema.rproperty(targettype,entity.e_schema,'cardinality')[1]# there is no related entity and we need at least one : we need to# display one explicit inline-creation viewifself.should_display_inline_relation_form(rschema,existant,card):result.append(self.view('inline-creation',None,etype=targettype,peid=entity.eid,ptype=entity.e_schema,rtype=rschema,role=x,**kwargs))# we can create more than one related entity, we thus display a link# to add new related entitiesifself.should_display_add_inline_relation_link(rschema,existant,card):divid="addNew%s%s%s:%s"%(targettype,rschema,x,entity.eid)result.append(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'%divid)js="addInlineCreationForm('%s', '%s', '%s', '%s', '%s')"%(entity.eid,entity.e_schema,targettype,rschema,x)ifcardin'1?':js="toggleVisibility('%s'); %s"%(divid,js)result.append(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'%(rschema,entity.eid,js,self.req.__('add a %s'%targettype)))result.append(u'</div>')result.append(u'<div class="trame_grise"> </div>')result.append(u'</div>')return'\n'.join(result)# should_* method extracted to allow overridingdefshould_inline_relation_form(self,entity,rschema,targettype,role):returnentity.rtags.is_inlined(rschema,targettype,role)defshould_display_inline_relation_form(self,rschema,existant,card):returnnotexistantandcardin'1+'defshould_display_add_inline_relation_link(self,rschema,existant,card):returnnotexistantorcardin'+*'defreset_url(self,entity):returnentity.absolute_url()defon_submit(self,entity):returnu'return freezeFormButtons(\'%s\')'%(self.domid)defsubmited_message(self):returnself.req._('element edited')classCreationForm(EditionForm):__selectors__=(etype_form_selector,)id='creation'title=_('creation')defcall(self,**kwargs):"""creation view for an entity"""self.req.add_js(('cubicweb.ajax.js','cubicweb.edition.js'))self.req.add_css('cubicweb.form.css')etype=kwargs.pop('etype',self.req.form.get('etype'))try:entity=self.vreg.etype_class(etype)(self.req,None,None)except:self.w(self.req._('no such entity type %s')%etype)else:self.edit_form(entity,kwargs)defaction_title(self,entity):"""custom form title if creating a entity with __linkto"""if'__linkto'inself.req.form:ifisinstance(self.req.form['__linkto'],list):# XXX which one should be considered (case: add a ticket to a version in jpl)rtype,linkto_eid,role=self.req.form['__linkto'][0].split(':')else:rtype,linkto_eid,role=self.req.form['__linkto'].split(':')linkto_rset=self.req.eid_rset(linkto_eid)linkto_type=linkto_rset.description[0][0]ifrole=='subject':title=self.req.__('creating %s (%s%s%s%%(linkto)s)'%(entity.e_schema,entity.e_schema,rtype,linkto_type))else:title=self.req.__('creating %s (%s%%(linkto)s %s%s)'%(entity.e_schema,linkto_type,rtype,entity.e_schema))msg=title%{'linkto':self.view('incontext',linkto_rset)}returnu'<div class="formTitle notransform"><span>%s</span></div>'%msgelse:returnsuper(CreationForm,self).action_title(entity)@propertydefformid(self):return'edition'defrelations_form(self,entity,kwargs):returnu''defreset_url(self,entity=None):returnself.build_url(self.req.form.get('etype','').lower())defsubmited_message(self):returnself.req._('element created')defurl(self):"""return the url associated with this view"""returnself.create_url(self.req.form.get('etype'))classInlineFormMixIn(object):@cacheddefcard(self,etype):returnself.rschema.rproperty(self.parent_schema,etype,'cardinality')[0]defaction_title(self,entity):returnself.rschema.display_name(self.req,self.role)defadd_hidden_web_behaviour_params(self,entity):passdefedit_form(self,entity,ptype,peid,rtype,role='subject',**kwargs):self.rschema=self.schema.rschema(rtype)self.role=roleself.parent_schema=self.schema.eschema(ptype)self.parent_eid=peidsuper(InlineFormMixIn,self).edit_form(entity,kwargs)defshould_inline_relation_form(self,entity,rschema,targettype,role):ifrschema==self.rschema:returnFalsereturnentity.rtags.is_inlined(rschema,targettype,role)@cacheddefkeep_entity(self,entity):req=self.req# are we regenerating form because of a validation error ?erroneous_post=req.data.get('formvalues')iferroneous_post:cdvalues=req.list_form_param('%s:%s'%(self.rschema,self.parent_eid),erroneous_post)ifunicode(entity.eid)notincdvalues:returnFalsereturnTruedefform_context(self,entity,kwargs):ctx=super(InlineFormMixIn,self).form_context(entity,kwargs)_=self.req._local_ctx={'createmsg':self.req.__('add a %s'%entity.e_schema),'so':self.role[0],# 's' for subject, 'o' for object'eid':entity.eid,'rtype':self.rschema,'parenteid':self.parent_eid,'parenttype':self.parent_schema,'etype':entity.e_schema,'novalue':INTERNAL_FIELD_VALUE,'removemsg':self.req.__('remove this %s'%entity.e_schema),'notice':self.req._('click on the box to cancel the deletion'),}ctx.update(local_ctx)returnctxclassInlineEntityCreationForm(InlineFormMixIn,CreationForm):id='inline-creation'__selectors__=(kwargs_selector,etype_form_selector)expected_kwargs=('ptype','peid','rtype')EDITION_BODY=u'''\<div id="div-%(parenteid)s-%(rtype)s-%(eid)s" class="inlinedform"> <div class="iformBody"> <div class="iformTitle"><span>%(title)s</span> #<span class="icounter">1</span> [<a href="javascript: removeInlineForm('%(parenteid)s', '%(rtype)s', '%(eid)s'); noop();">%(removemsg)s</a>]</div> <fieldset class="subentity">%(attrform)s%(relattrform)s </fieldset> </div> <fieldset class="hidden" id="fs-%(parenteid)s-%(rtype)s-%(eid)s">%(base)s <input type="hidden" value="%(novalue)s" name="edit%(so)s-%(rtype)s:%(parenteid)s" /> <input id="rel-%(parenteid)s-%(rtype)s-%(eid)s" type="hidden" value="%(eid)s" name="%(rtype)s:%(parenteid)s" /> </fieldset></div>'''# do not insert trailing space or \n here !defcall(self,etype,ptype,peid,rtype,role='subject',**kwargs):""" :param etype: the entity type being created in the inline form :param parent: the parent entity hosting the inline form :param rtype: the relation bridging `etype` and `parent` :param role: the role played by the `parent` in the relation """self.req.add_css('cubicweb.form.css')try:entity=self.vreg.etype_class(etype)(self.req,None,None)except:self.w(self.req._('no such entity type %s')%etype)returnself.edit_form(entity,ptype,peid,rtype,role,**kwargs)classInlineEntityEditionForm(InlineFormMixIn,EditionForm):id='inline-edition'__selectors__=(accept_selector,kwargs_selector)expected_kwargs=('ptype','peid','rtype')EDITION_BODY=u'''\<div onclick="restoreInlinedEntity('%(parenteid)s', '%(rtype)s', '%(eid)s')" id="div-%(parenteid)s-%(rtype)s-%(eid)s" class="inlinedform"> <div id="notice-%(parenteid)s-%(rtype)s-%(eid)s" class="notice">%(notice)s</div><div class="iformTitle"><span>%(title)s</span> #<span class="icounter">%(count)s</span> [<a href="javascript: removeInlinedEntity('%(parenteid)s', '%(rtype)s', '%(eid)s'); noop();">%(removemsg)s</a>]</div> <div class="iformBody"> <fieldset class="subentity">%(attrform)s </fieldset>%(relattrform)s </div> <fieldset id="fs-%(parenteid)s-%(rtype)s-%(eid)s">%(base)s <input type="hidden" value="%(eid)s" name="edit%(so)s-%(rtype)s:%(parenteid)s" />%(rinput)s </fieldset></div>'''# do not insert trailing space or \n here !rel_input=u'''<input id="rel-%(parenteid)s-%(rtype)s-%(eid)s" type="hidden" value="%(eid)s" name="%(rtype)s:%(parenteid)s" />'''defcall(self,**kwargs):"""redefine default View.call() method to avoid automatic insertions of <div class="section"> between each row of the resultset """self.req.add_css('cubicweb.form.css')rset=self.rsetforiinxrange(len(rset)):self.wview(self.id,rset,row=i,**kwargs)defcell_call(self,row,col,ptype,peid,rtype,role='subject',**kwargs):""" :param parent: the parent entity hosting the inline form :param rtype: the relation bridging `etype` and `parent` :param role: the role played by the `parent` in the relation """entity=self.entity(row,col)self.edit_form(entity,ptype,peid,rtype,role,**kwargs)defform_context(self,entity,kwargs):ctx=super(InlineEntityEditionForm,self).form_context(entity,kwargs)ifself.keep_entity(entity):ctx['rinput']=self.rel_input%ctxctx['todelete']=u''else:ctx['rinput']=u''ctx['todelete']=u'checked="checked"'ctx['count']=entity.row+1returnctxclassCopyEditionForm(EditionForm):id='copy'title=_('copy edition')defcell_call(self,row,col,**kwargs):self.req.add_js(('cubicweb.ajax.js','cubicweb.edition.js'))self.req.add_css('cubicweb.form.css')entity=self.complete_entity(row,col,skip_bytes=True)# make a copy of entity to avoid altering the entity in the# request's cache. self.newentity=copy(entity)self.copying=self.newentity.eidself.newentity.eid=Noneself.edit_form(self.newentity,kwargs)delself.newentitydefaction_title(self,entity):"""form's title"""msg=super(CopyEditionForm,self).action_title(entity)returnmsg+(u'<script type="text/javascript">updateMessage("%s");</script>\n'%self.req._('Please note that this is only a shallow copy'))# XXX above message should have style of a warning@propertydefformid(self):return'edition'defrelations_form(self,entity,kwargs):returnu''defreset_url(self,entity):returnself.build_url('view',rql='Any X WHERE X eid %s'%self.copying)defattributes_form(self,entity,kwargs,include_eid=True):# we don't want __clone_eid on inlined edited entitiesifentity.eid==self.newentity.eid:self._hiddens.append((eid_param('__cloned_eid',entity.eid),self.copying,''))returnEditionForm.attributes_form(self,entity,kwargs,include_eid)defsubmited_message(self):returnself.req._('element copied')classTableEditForm(EntityForm):id='muledit'title=_('multiple edit')EDITION_BODY=u'''<form method="post" id="entityForm" onsubmit="return validateForm('entityForm', null);" action="%(action)s">%(error)s <div id="progress">%(progress)s</div> <fieldset> <input type="hidden" name="__errorurl" value="%(url)s" /> <input type="hidden" name="__form_id" value="%(formid)s" /> <input type="hidden" name="__redirectvid" value="%(redirectvid)s" /> <input type="hidden" name="__redirectrql" value="%(redirectrql)s" /> <table class="listing"> <tr class="header"> <th align="left"><input type="checkbox" onclick="setCheckboxesState('eid', this.checked)" value="" title="toggle check boxes" /></th>%(attrheaders)s </tr>%(lines)s </table> <table width="100%%"> <tr> <td align="left"> <input class="validateButton" type="submit" value="%(okvalue)s" title="%(oktitle)s" /> <input class="validateButton" type="reset" name="__action_cancel" value="%(cancelvalue)s" title="%(canceltitle)s" /> </td> </tr> </table> </fieldset> </form>'''WIDGET_CELL=u'''\<td%(csscls)s>%(error)s <div>%(widget)s</div></td>'''defcall(self,**kwargs):"""a view to edit multiple entities of the same type the first column should be the eid """req=self.reqform=req.formreq.add_js('cubicweb.edition.js')req.add_css('cubicweb.form.css')_=req._sampleentity=self.complete_entity(0)attrheaders=[u'<th>%s</th>'%rdef[0].display_name(req,rdef[-1])forrdefinsampleentity.relations_by_category('primary','add')ifrdef[0].type!='eid']ctx={'action':self.build_url('edit'),'error':self.error_message(),'progress':_('validating...'),'url':html_escape(req.url()),'formid':self.id,'redirectvid':html_escape(form.get('__redirectvid','list')),'redirectrql':html_escape(form.get('__redirectrql',self.rset.printable_rql())),'attrheaders':u'\n'.join(attrheaders),'lines':u'\n'.join(self.edit_form(ent)forentinself.rset.entities()),'okvalue':_('button_ok').capitalize(),'oktitle':_('validate modifications on selected items').capitalize(),'cancelvalue':_('button_reset').capitalize(),'canceltitle':_('revert changes').capitalize(),}self.w(self.EDITION_BODY%ctx)defreset_url(self,entity=None):self.build_url('view',rql=self.rset.printable_rql())defedit_form(self,entity):html=[]w=html.appendentity.complete()eid=entity.eidvalues=self.req.data.get('formvalues',())qeid=eid_param('eid',eid)checked=qeidinvaluesw(u'<tr class="%s">'%(entity.row%2andu'even'oru'odd'))w(u'<td>%s<input type="hidden" name="__type:%s" value="%s" /></td>'%(checkbox('eid',eid,checked=checked),eid,entity.e_schema))# attribute relations (skip eid which is handled by the checkboxwdg=entity.get_widgetwdgfactories=[wdg(rschema,x)forrschema,_,xinentity.relations_by_category('primary','add')ifrschema.type!='eid']# XXX both (add, delete)seid=html_escape(dumps(eid))forwobjinwdgfactories:ifisinstance(wobj,ComboBoxWidget):wobj.attrs['onchange']="setCheckboxesState2('eid', %s, 'checked')"%seidelifisinstance(wobj,InputWidget):wobj.attrs['onkeypress']="setCheckboxesState2('eid', %s, 'checked')"%seiderror=wobj.render_error(entity)iferror:csscls=u' class="error"'else:csscls=u''w(self.WIDGET_CELL%{'csscls':csscls,'error':error,'widget':wobj.edit_render(entity)})w(u'</tr>')return'\n'.join(html)classUnrelatedDivs(EntityView):id='unrelateddivs'__selectors__=(req_form_params_selector,)form_params=('relation',)@propertydeflimit(self):ifself.req.form.get('__force_display'):returnNonereturnself.req.property_value('navigation.related-limit')+1defcell_call(self,row,col):entity=self.entity(row,col)relname,target=self.req.form.get('relation').rsplit('_',1)rschema=self.schema.rschema(relname)hidden='hidden'inself.req.formis_cell='is_cell'inself.req.formself.w(self.build_unrelated_select_div(entity,rschema,target,is_cell=is_cell,hidden=hidden))defbuild_unrelated_select_div(self,entity,rschema,target,is_cell=False,hidden=True):options=[]divid='div%s_%s_%s'%(rschema.type,target,entity.eid)selectid='select%s_%s_%s'%(rschema.type,target,entity.eid)ifrschema.symetricortarget=='subject':targettypes=rschema.objects(entity.e_schema)etypes='/'.join(sorted(etype.display_name(self.req)foretypeintargettypes))else:targettypes=rschema.subjects(entity.e_schema)etypes='/'.join(sorted(etype.display_name(self.req)foretypeintargettypes))etypes=cut(etypes,self.req.property_value('navigation.short-line-size'))options.append('<option>%s%s</option>'%(self.req._('select a'),etypes))options+=self._get_select_options(entity,rschema,target)options+=self._get_search_options(entity,rschema,target,targettypes)if'Basket'inself.schema:# XXXoptions+=self._get_basket_options(entity,rschema,target,targettypes)relname,target=self.req.form.get('relation').rsplit('_',1)returnu"""\<div class="%s" id="%s"> <select id="%s" onchange="javascript: addPendingInsert(this.options[this.selectedIndex], %s, %s, '%s');">%s </select></div>"""%(hiddenand'hidden'or'',divid,selectid,html_escape(dumps(entity.eid)),is_celland'true'or'null',relname,'\n'.join(options))def_get_select_options(self,entity,rschema,target):"""add options to search among all entities of each possible type"""options=[]eid=entity.eidpending_inserts=self.req.get_pending_inserts(eid)rtype=rschema.typeforeview,reidinentity.vocabulary(rschema,target,self.limit):ifreidisNone:options.append('<option class="separator">-- %s --</option>'%html_escape(eview))else:optionid=relation_id(eid,rtype,target,reid)ifoptionidnotinpending_inserts:# prefix option's id with letters to make valid XHTML wiseoptions.append('<option id="id%s" value="%s">%s</option>'%(optionid,reid,html_escape(eview)))returnoptionsdef_get_search_options(self,entity,rschema,target,targettypes):"""add options to search among all entities of each possible type"""options=[]_=self.req._foreschemaintargettypes:mode='%s:%s:%s:%s'%(target,entity.eid,rschema.type,eschema)url=self.build_url(entity.rest_path(),vid='search-associate',__mode=mode)options.append((eschema.display_name(self.req),'<option value="%s">%s%s</option>'%(html_escape(url),_('Search for'),eschema.display_name(self.req))))return[oforl,oinsorted(options)]def_get_basket_options(self,entity,rschema,target,targettypes):options=[]rtype=rschema.type_=self.req._forbasketeid,basketnameinself._get_basket_links(self.req.user.eid,target,targettypes):optionid=relation_id(entity.eid,rtype,target,basketeid)options.append('<option id="%s" value="%s">%s%s</option>'%(optionid,basketeid,_('link to each item in'),html_escape(basketname)))returnoptionsdef_get_basket_links(self,ueid,target,targettypes):targettypes=set(targettypes)forbasketeid,basketname,elementsinself._get_basket_info(ueid):baskettypes=elements.column_types(0)# if every elements in the basket can be attached to the# edited entityifbaskettypes&targettypes:yieldbasketeid,basketnamedef_get_basket_info(self,ueid):basketref=[]basketrql='Any B,N WHERE B is Basket, B owned_by U, U eid %(x)s, B name N'basketresultset=self.req.execute(basketrql,{'x':ueid},'x')forresultinbasketresultset:basketitemsrql='Any X WHERE X in_basket B, B eid %(x)s'rset=self.req.execute(basketitemsrql,{'x':result[0]},'x')basketref.append((result[0],result[1],rset))returnbasketrefclassComboboxView(EntityView):"""the view used in combobox (unrelated entities) THIS IS A TEXT VIEW. DO NOT HTML_ESCAPE """id='combobox'accepts=('Any',)title=Nonedefcell_call(self,row,col):"""the combo-box view for an entity: same as text out of context view by default """self.wview('textoutofcontext',self.rset,row=row,col=col)