"""The automatic entity form.: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"_=unicodefromwarningsimportwarnfromsimplejsonimportdumpsfromlogilab.mtconverterimportxml_escapefromlogilab.common.decoratorsimporticlassmethod,cachedfromcubicwebimporttyped_eid,neg_role,uilibfromcubicweb.schemaimportdisplay_namefromcubicweb.viewimportEntityViewfromcubicweb.selectorsimport(match_kwargs,match_form_params,non_final_entity,specified_etype_implements)fromcubicweb.webimportstdmsgs,uicfg,eid_param, \formasf,formwidgetsasfw,formfieldsasfffromcubicweb.web.viewsimportforms_AFS=uicfg.autoform_section_AFFK=uicfg.autoform_field_kwargs# inlined form handling ########################################################classInlinedFormField(ff.Field):def__init__(self,view=None,**kwargs):kwargs.setdefault('label',None)super(InlinedFormField,self).__init__(name=view.rtype,role=view.role,eidparam=True,**kwargs)self.view=viewdefrender(self,form,renderer):"""render this field, which is part of form, using the given form renderer """view=self.viewi18nctx='inlined:%s.%s.%s'%(form.edited_entity.e_schema,view.rtype,view.role)returnu'<div class="inline-%s-%s-slot">%s</div>'%(view.rtype,view.role,view.render(i18nctx=i18nctx,row=view.cw_row,col=view.cw_col))defform_init(self,form):"""method called before by build_context to trigger potential field initialization requiring the form instance """ifself.view.form:self.view.form.build_context(form.formvalues)@propertydefneeds_multipart(self):ifself.view.form:# take a look at inlined forms to check (recursively) if they need# multipart handling.returnself.view.form.needs_multipartreturnFalsedefhas_been_modified(self,form):returnFalsedefprocess_posted(self,form):pass# handled by the subformclassInlineEntityEditionFormView(f.FormViewMixIn,EntityView):""" :attr peid: the parent entity's eid hosting the inline form :attr rtype: the relation bridging `etype` and `peid` :attr role: the role played by the `peid` in the relation :attr pform: the parent form where this inlined form is being displayed """__regid__='inline-edition'__select__=non_final_entity()&match_kwargs('peid','rtype')_select_attrs=('peid','rtype','role','pform','etype')removejs="removeInlinedEntity('%s', '%s', '%s')"def__init__(self,*args,**kwargs):forattrinself._select_attrs:# don't pop attributes from kwargs, so the end-up in# self.cw_extra_kwargs which is then passed to the edition form (see# the .form method)setattr(self,attr,kwargs.get(attr))super(InlineEntityEditionFormView,self).__init__(*args,**kwargs)def_entity(self):assertself.cw_rowisnotNone,selfreturnself.cw_rset.get_entity(self.cw_row,self.cw_col)@property@cacheddefform(self):entity=self._entity()form=self._cw.vreg['forms'].select('edition',self._cw,entity=entity,formtype='inlined',form_renderer_id='inline',copy_nav_params=False,mainform=False,parent_form=self.pform,**self.cw_extra_kwargs)ifself.pformisNone:form.restore_previous_post(form.session_key())#assert form.parent_formself.add_hiddens(form,entity)returnformdefcell_call(self,row,col,i18nctx,**kwargs):""" :param peid: the parent entity's eid hosting the inline form :param rtype: the relation bridging `etype` and `peid` :param role: the role played by the `peid` in the relation """entity=self._entity()divonclick="restoreInlinedEntity('%s', '%s', '%s')"%(self.peid,self.rtype,entity.eid)self.render_form(i18nctx,divonclick=divonclick,**kwargs)defrender_form(self,i18nctx,**kwargs):"""fetch and render the form"""entity=self._entity()divid='%s-%s-%s'%(self.peid,self.rtype,entity.eid)title=self.form_title(entity,i18nctx)removejs=self.removejsandself.removejs%(self.peid,self.rtype,entity.eid)countkey='%s_count'%self.rtypetry:self._cw.data[countkey]+=1exceptKeyError:self._cw.data[countkey]=1self.w(self.form.render(divid=divid,title=title,removejs=removejs,i18nctx=i18nctx,counter=self._cw.data[countkey],**kwargs))defform_title(self,entity,i18nctx):returnself._cw.pgettext(i18nctx,entity.__regid__)defadd_hiddens(self,form,entity):"""to ease overriding (see cubes.vcsfile.views.forms for instance)"""iid='rel-%s-%s-%s'%(self.peid,self.rtype,entity.eid)# * str(self.rtype) in case it's a schema object# * neged_role() since role is the for parent entity, we want the role# of the inlined entityform.add_hidden(name=str(self.rtype),value=self.peid,role=neg_role(self.role),eidparam=True,id=iid)defkeep_entity(self,form,entity):ifnotentity.has_eid():returnTrue# are we regenerating form because of a validation error ?ifform.form_previous_values:cdvalues=self._cw.list_form_param(eid_param(self.rtype,self.peid),form.form_previous_values)ifunicode(entity.eid)notincdvalues:returnFalsereturnTrueclassInlineEntityCreationFormView(InlineEntityEditionFormView):""" :attr etype: the entity type being created in the inline form """__regid__='inline-creation'__select__=(match_kwargs('peid','petype','rtype')&specified_etype_implements('Any'))_select_attrs=InlineEntityEditionFormView._select_attrs+('petype',)@propertydefremovejs(self):entity=self._entity()rdef=entity.e_schema.rdef(self.rtype,neg_role(self.role),self.petype)card=rdef.role_cardinality(self.role)# when one is adding an inline entity for a relation of a single card,# the 'add a new xxx' link disappears. If the user then cancel the addition,# we have to make this link appears back. This is done by giving add new link# id to removeInlineForm.ifcardnotin'?1':return"removeInlineForm('%%s', '%%s', '%s', '%%s')"%self.roledivid="addNew%s%s%s:%s"%(self.etype,self.rtype,self.role,self.peid)return"removeInlineForm('%%s', '%%s', '%s', '%%s', '%s')"%(self.role,divid)@cacheddef_entity(self):try:cls=self._cw.vreg['etypes'].etype_class(self.etype)except:self.w(self._cw._('no such entity type %s')%etype)returnentity=cls(self._cw)entity.eid=self._cw.varmaker.next()returnentitydefcall(self,i18nctx,**kwargs):self.render_form(i18nctx,**kwargs)classInlineAddNewLinkView(InlineEntityCreationFormView):""" :attr card: the cardinality of the relation according to role of `peid` """__regid__='inline-addnew-link'__select__=(match_kwargs('peid','petype','rtype')&specified_etype_implements('Any'))_select_attrs=InlineEntityCreationFormView._select_attrs+('card',)form=None# no actual form wrappeddefcall(self,i18nctx,**kwargs):self._cw.set_varmaker()divid="addNew%s%s%s:%s"%(self.etype,self.rtype,self.role,self.peid)self.w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'%divid)js="addInlineCreationForm('%s', '%s', '%s', '%s', '%s', '%s')"%(self.peid,self.petype,self.etype,self.rtype,self.role,i18nctx)ifself.pform.should_hide_add_new_relation_link(self.rtype,self.card):js="toggleVisibility('%s'); %s"%(divid,js)__=self._cw.pgettextself.w(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'%(self.rtype,self.peid,js,__(i18nctx,'add a %s'%self.etype)))self.w(u'</div>')# generic relations handling ##################################################defrelation_id(eid,rtype,role,reid):"""return an identifier for a relation between two entities"""ifrole=='subject':returnu'%s:%s:%s'%(eid,rtype,reid)returnu'%s:%s:%s'%(reid,rtype,eid)deftoggleable_relation_link(eid,nodeid,label='x'):"""return javascript snippet to delete/undelete a relation between two entities """js=u"javascript: togglePendingDelete('%s', %s);"%(nodeid,xml_escape(dumps(eid)))returnu'[<a class="handle" href="%s" id="handle%s">%s</a>]'%(js,nodeid,label)defget_pending_inserts(req,eid=None):"""shortcut to access req's pending_insert entry This is where are stored relations being added while editing an entity. This used to be stored in a temporary cookie. """pending=req.get_session_data('pending_insert')or()return['%s:%s:%s'%(subj,rel,obj)forsubj,rel,objinpendingifeidisNoneoreidin(subj,obj)]defget_pending_deletes(req,eid=None):"""shortcut to access req's pending_delete entry This is where are stored relations being removed while editing an entity. This used to be stored in a temporary cookie. """pending=req.get_session_data('pending_delete')or()return['%s:%s:%s'%(subj,rel,obj)forsubj,rel,objinpendingifeidisNoneoreidin(subj,obj)]defparse_relations_descr(rdescr):"""parse a string describing some relations, in the form subjeids:rtype:objeids where subjeids and objeids are eids separeted by a underscore return an iterator on (subject eid, relation type, object eid) found """forrstrinrdescr:subjs,rtype,objs=rstr.split(':')forsubjinsubjs.split('_'):forobjinobjs.split('_'):yieldtyped_eid(subj),rtype,typed_eid(obj)defdelete_relations(req,rdefs):"""delete relations from the repository"""# FIXME convert to using the syntax subject:relation:eidsexecute=req.executeforsubj,rtype,objinparse_relations_descr(rdefs):rql='DELETE X %s Y where X eid %%(x)s, Y eid %%(y)s'%rtypeexecute(rql,{'x':subj,'y':obj},('x','y'))req.set_message(req._('relations deleted'))definsert_relations(req,rdefs):"""insert relations into the repository"""execute=req.executeforsubj,rtype,objinparse_relations_descr(rdefs):rql='SET X %s Y where X eid %%(x)s, Y eid %%(y)s'%rtypeexecute(rql,{'x':subj,'y':obj},('x','y'))classGenericRelationsWidget(fw.FieldWidget):defrender(self,form,field,renderer):stream=[]w=stream.appendreq=form._cw_=req.___=_eid=form.edited_entity.eidw(u'<table id="relatedEntities">')forrschema,role,relatedinfield.relations_table(form):# already linked entitiesifrelated:w(u'<tr><th class="labelCol">%s</th>'%rschema.display_name(req,role))w(u'<td>')w(u'<ul>')forviewparamsinrelated:w(u'<li class="invisible">%s<div id="span%s" class="%s">%s</div></li>'%(viewparams[1],viewparams[0],viewparams[2],viewparams[3]))ifnotform.force_displayandform.maxrelitems<len(related):link=(u'<span class="invisible">''[<a href="javascript: window.location.href+=\'&__force_display=1\'">%s</a>]''</span>'%_('view all'))w(u'<li class="invisible">%s</li>'%link)w(u'</ul>')w(u'</td>')w(u'</tr>')pendings=list(field.restore_pending_inserts(form))ifnotpendings:w(u'<tr><th> </th><td> </td></tr>')else:forrowinpendings:# soon to be linked to entitiesw(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],xml_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'<select id="relationSelector_%s" tabindex="%s" ''onchange="javascript:showMatchingSelect(this.options[this.selectedIndex].value,%s);">'%(eid,req.next_tabindex(),xml_escape(dumps(eid))))w(u'<option value="">%s</option>'%_('select a relation'))fori18nrtype,rschema,roleinfield.relations:# more entities to link tow(u'<option value="%s_%s">%s</option>'%(rschema,role,i18nrtype))w(u'</select>')w(u'</th>')w(u'<td id="unrelatedDivs_%s"></td>'%eid)w(u'</tr>')w(u'</table>')return'\n'.join(stream)classGenericRelationsField(ff.Field):widget=GenericRelationsWidgetdef__init__(self,relations,name='_cw_generic_field',**kwargs):assertrelationskwargs['eidparam']=Truesuper(GenericRelationsField,self).__init__(name,**kwargs)self.relations=relationsdefprocess_posted(self,form):todelete=get_pending_deletes(form._cw)iftodelete:delete_relations(form._cw,todelete)toinsert=get_pending_inserts(form._cw)iftoinsert:insert_relations(form._cw,toinsert)return()defrelations_table(self,form):"""yiels 3-tuples (rtype, role, related_list) where <related_list> itself a list of : - node_id (will be the entity element's DOM id) - appropriate javascript's togglePendingDelete() function call - status 'pendingdelete' or '' - oneline view of related entity """entity=form.edited_entitypending_deletes=get_pending_deletes(form._cw,entity.eid)forlabel,rschema,roleinself.relations:related=[]ifentity.has_eid():rset=entity.related(rschema,role,limit=form.related_limit)ifrole=='subject':haspermkwargs={'fromeid':entity.eid}else:haspermkwargs={'toeid':entity.eid}ifrschema.has_perm(form._cw,'delete',**haspermkwargs):toggleable_rel_link_func=toggleable_relation_linkelse:toggleable_rel_link_func=lambdax,y,z:u''forrowinxrange(rset.rowcount):nodeid=relation_id(entity.eid,rschema,role,rset[row][0])ifnodeidinpending_deletes:status,label=u'pendingDelete','+'else:status,label=u'','x'dellink=toggleable_rel_link_func(entity.eid,nodeid,label)eview=form._cw.view('oneline',rset,row=row)related.append((nodeid,dellink,status,eview))yield(rschema,role,related)defrestore_pending_inserts(self,form):"""used to restore edition page as it was before clicking on 'search for <some entity type>' """entity=form.edited_entitypending_inserts=set(get_pending_inserts(form._cw,form.edited_entity.eid))forpendingidinpending_inserts:eidfrom,rtype,eidto=pendingid.split(':')iftyped_eid(eidfrom)==entity.eid:# subjectlabel=display_name(form._cw,rtype,'subject',entity.__regid__)reid=eidtoelse:label=display_name(form._cw,rtype,'object',entity.__regid__)reid=eidfromjscall="javascript: cancelPendingInsert('%s', 'tr', null, %s);" \%(pendingid,entity.eid)rset=form._cw.eid_rset(reid)eview=form._cw.view('text',rset,row=0)yieldrtype,pendingid,jscall,label,reid,eviewclassUnrelatedDivs(EntityView):__regid__='unrelateddivs'__select__=match_form_params('relation')defcell_call(self,row,col):entity=self.cw_rset.get_entity(row,col)relname,role=self._cw.form.get('relation').rsplit('_',1)rschema=self._cw.vreg.schema.rschema(relname)hidden='hidden'inself._cw.formis_cell='is_cell'inself._cw.formself.w(self.build_unrelated_select_div(entity,rschema,role,is_cell=is_cell,hidden=hidden))defbuild_unrelated_select_div(self,entity,rschema,role,is_cell=False,hidden=True):options=[]divid='div%s_%s_%s'%(rschema.type,role,entity.eid)selectid='select%s_%s_%s'%(rschema.type,role,entity.eid)ifrschema.symmetricorrole=='subject':targettypes=rschema.objects(entity.e_schema)etypes='/'.join(sorted(etype.display_name(self._cw)foretypeintargettypes))else:targettypes=rschema.subjects(entity.e_schema)etypes='/'.join(sorted(etype.display_name(self._cw)foretypeintargettypes))etypes=uilib.cut(etypes,self._cw.property_value('navigation.short-line-size'))options.append('<option>%s%s</option>'%(self._cw._('select a'),etypes))options+=self._get_select_options(entity,rschema,role)options+=self._get_search_options(entity,rschema,role,targettypes)relname,role=self._cw.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,xml_escape(dumps(entity.eid)),is_celland'true'or'null',relname,'\n'.join(options))def_get_select_options(self,entity,rschema,role):"""add options to search among all entities of each possible type"""options=[]pending_inserts=get_pending_inserts(self._cw,entity.eid)rtype=rschema.typeform=self._cw.vreg['forms'].select('edition',self._cw,entity=entity)field=form.field_by_name(rschema,role,entity.e_schema)limit=self._cw.property_value('navigation.combobox-limit')# NOTE: expect 'limit' arg on choices method of relation fieldforeview,reidinfield.vocabulary(form,limit=limit):ifreidisNone:ifeview:# skip blank valueoptions.append('<option class="separator">-- %s --</option>'%xml_escape(eview))elifreid!=ff.INTERNAL_FIELD_VALUE:optionid=relation_id(entity.eid,rtype,role,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,xml_escape(eview)))returnoptionsdef_get_search_options(self,entity,rschema,role,targettypes):"""add options to search among all entities of each possible type"""options=[]_=self._cw._foreschemaintargettypes:mode='%s:%s:%s:%s'%(role,entity.eid,rschema.type,eschema)url=self._cw.build_url(entity.rest_path(),vid='search-associate',__mode=mode)options.append((eschema.display_name(self._cw),'<option value="%s">%s%s</option>'%(xml_escape(url),_('Search for'),eschema.display_name(self._cw))))return[oforl,oinsorted(options)]# The automatic entity form ####################################################classAutomaticEntityForm(forms.EntityFieldsForm):"""base automatic form to edit any entity. Designed to be fully generated from schema but highly configurable through: * uicfg (autoform_* relation tags) * various standard form parameters * overriding You can also easily customise it by adding/removing fields in AutomaticEntityForm instances or by inheriting from it. """__regid__='edition'cwtarget='eformframe'cssclass='entityForm'copy_nav_params=Trueform_buttons=[fw.SubmitButton(),fw.Button(stdmsgs.BUTTON_APPLY,cwaction='apply'),fw.Button(stdmsgs.BUTTON_CANCEL,cwaction='cancel')]# for attributes selection when searching in uicfg.autoform_sectionformtype='main'# set this to a list of [(relation, role)] if you want to explictily tell# which relations should be editeddisplay_fields=None@iclassmethoddeffield_by_name(cls_or_self,name,role=None,eschema=None):"""return field with the given name and role. If field is not explicitly defined for the form but `eclass` is specified, guess_field will be called. """try:returnsuper(AutomaticEntityForm,cls_or_self).field_by_name(name,role,eschema)exceptf.FieldNotFound:ifname=='_cw_generic_field'andnotisinstance(cls_or_self,type):returncls_or_self._generic_relations_field()raise# base automatic entity form methods #######################################def__init__(self,*args,**kwargs):super(AutomaticEntityForm,self).__init__(*args,**kwargs)entity=self.edited_entityifentity.has_eid():entity.complete()forrtype,roleinself.editable_attributes():try:self.field_by_name(str(rtype),role)continue# explicitly specifiedexceptf.FieldNotFound:# has to be guessedtry:field=self.field_by_name(str(rtype),role,eschema=entity.e_schema)self.fields.append(field)exceptf.FieldNotFound:# meta attribute such as <attr>_formatcontinueifself.fieldsets_in_order:fsio=list(self.fieldsets_in_order)else:fsio=[None]self.fieldsets_in_order=fsio# add fields for relation whose target should have an inline formforformviewinself.inlined_form_views():field=self._inlined_form_view_field(formview)self.fields.append(field)ifnotfield.fieldsetinfsio:fsio.append(field.fieldset)ifself.formtype=='main':# add the generic relation field if necessaryifentity.has_eid()and(self.display_fieldsisNoneor'_cw_generic_field'inself.display_fields):try:field=self.field_by_name('_cw_generic_field')exceptf.FieldNotFound:# no editable relationpasselse:self.fields.append(field)ifnotfield.fieldsetinfsio:fsio.append(field.fieldset)self.maxrelitems=self._cw.property_value('navigation.related-limit')self.force_display=bool(self._cw.form.get('__force_display'))fnum=len(self.fields)self.fields.sort(key=lambdaf:f.orderisNoneandfnumorf.order)@propertydefrelated_limit(self):ifself.force_display:returnNonereturnself.maxrelitems+1defaction(self):"""return the form's action attribute. Default to validateform if not explicitly overriden. """try:returnself._actionexceptAttributeError:returnself._cw.build_url('validateform')defset_action(self,value):"""override default action"""self._action=valueaction=property(action,set_action)# autoform specific fields #################################################def_generic_relations_field(self):try:srels_by_cat=self.srelations_by_category('generic','add',strict=True)warn('[3.6] %s: srelations_by_category is deprecated, use uicfg or ''override editable_relations instead'%classid(form),DeprecationWarning)exceptAttributeError:srels_by_cat=self.editable_relations()ifnotsrels_by_cat:raisef.FieldNotFound('_cw_generic_field')fieldset=u'%s :'%self._cw.__('This %s'%self.edited_entity.e_schema)fieldset=fieldset.capitalize()returnGenericRelationsField(self.editable_relations(),fieldset=fieldset,label=None)def_inlined_form_view_field(self,view):# XXX allow more customizationkwargs=_AFFK.etype_get(self.edited_entity.e_schema,view.rtype,view.role,view.etype)ifkwargsisNone:kwargs={}returnInlinedFormField(view=view,**kwargs)# methods mapping edited entity relations to fields in the form ############def_relations_by_section(self,section,permission='add',strict=False):"""return a list of (relation schema, target schemas, role) matching given category(ies) and permission """return_AFS.relations_by_section(self.edited_entity,self.formtype,section,permission,strict)defeditable_attributes(self,strict=False):"""return a list of (relation schema, role) to edit for the entity"""ifself.display_fieldsisnotNone:returnself.display_fieldsifself.edited_entity.has_eid()andnotself.edited_entity.has_perm('update'):return[]# XXX we should simply put eid in the generated section, no?return[(rtype,role)forrtype,_,roleinself._relations_by_section('attributes','update',strict)]defeditable_relations(self):"""return a sorted list of (relation's label, relation'schema, role) for relations in the 'relations' section """result=[]forrschema,_,roleinself._relations_by_section('relations',strict=True):result.append((rschema.display_name(self.edited_entity._cw,role,self.edited_entity.__regid__),rschema,role))returnsorted(result)definlined_relations(self):"""return a list of (relation schema, target schemas, role) matching given category(ies) and permission """returnself._relations_by_section('inlined')# inlined forms control ####################################################definlined_form_views(self):"""compute and return list of inlined form views (hosting the inlined form object) """allformviews=[]entity=self.edited_entityforrschema,ttypes,roleinself.inlined_relations():# show inline forms only if there's one possible target type# for rschemaiflen(ttypes)!=1:self.warning('entity related by the %s relation should have ''inlined form but there is multiple target types, ''dunno what to do',rschema)continuettype=ttypes[0].typeifself.should_inline_relation_form(rschema,ttype,role):formviews=list(self.inline_edition_form_view(rschema,ttype,role))card=rschema.role_rdef(entity.e_schema,ttype,role).role_cardinality(role)# there is no related entity and we need at least one: we need to# display one explicit inline-creation viewifself.should_display_inline_creation_form(rschema,formviews,card):formviews+=self.inline_creation_form_view(rschema,ttype,role)# we can create more than one related entity, we thus display a link# to add new related entitiesifself.should_display_add_new_relation_link(rschema,formviews,card):addnewlink=self._cw.vreg['views'].select('inline-addnew-link',self._cw,etype=ttype,rtype=rschema,role=role,card=card,peid=self.edited_entity.eid,petype=self.edited_entity.e_schema,pform=self)formviews.append(addnewlink)allformviews+=formviewsreturnallformviewsdefshould_inline_relation_form(self,rschema,targettype,role):"""return true if the given relation with entity has role and a targettype target should be inlined At this point we now relation has inlined_attributes tag (eg is returned by `inlined_relations()`. Overrides this for more finer control. """returnTruedefshould_display_inline_creation_form(self,rschema,existant,card):"""return true if a creation form should be inlined by default true if there is no related entity and we need at least one """returnnotexistantandcardin'1+'orself._cw.form.has_key('force_%s_display'%rschema)defshould_display_add_new_relation_link(self,rschema,existant,card):"""return true if we should add a link to add a new creation form (through ajax call) by default true if there is no related entity or if the relation has multiple cardinality """returnnotexistantorcardin'+*'# XXX add target type permisssionsdefshould_hide_add_new_relation_link(self,rschema,card):"""return true if once an inlined creation form is added, the 'add new' link should be hidden by default true if the relation has single cardinality """returncardin'1?'definline_edition_form_view(self,rschema,ttype,role):"""yield inline form views for already related entities through the given relation """entity=self.edited_entityrelated=entity.has_eid()andentity.related(rschema,role)ifrelated:vvreg=self._cw.vreg['views']# display inline-edition view for all existing related entitiesfori,relentityinenumerate(related.entities()):ifrelentity.has_perm('update'):yieldvvreg.select('inline-edition',self._cw,rset=related,row=i,col=0,etype=ttype,rtype=rschema,role=role,peid=entity.eid,pform=self)definline_creation_form_view(self,rschema,ttype,role):"""yield inline form views to a newly related (hence created) entity through the given relation """yieldself._cw.vreg['views'].select('inline-creation',self._cw,etype=ttype,rtype=rschema,role=role,peid=self.edited_entity.eid,petype=self.edited_entity.e_schema,pform=self)## default form ui configuration ############################################### use primary and not generated for eid since it has to be an hidden_AFS.tag_attribute(('*','eid'),'main','attributes')_AFS.tag_attribute(('*','eid'),'muledit','attributes')_AFS.tag_attribute(('*','description'),'main','attributes')_AFS.tag_attribute(('*','creation_date'),'main','metadata')_AFS.tag_attribute(('*','modification_date'),'main','metadata')_AFS.tag_attribute(('*','cwuri'),'main','metadata')_AFS.tag_attribute(('*','has_text'),'main','hidden')_AFS.tag_subject_of(('*','in_state','*'),'main','hidden')_AFS.tag_subject_of(('*','owned_by','*'),'main','metadata')_AFS.tag_subject_of(('*','created_by','*'),'main','metadata')_AFS.tag_subject_of(('*','require_permission','*'),'main','hidden')_AFS.tag_subject_of(('*','by_transition','*'),'main','attributes')_AFS.tag_subject_of(('*','by_transition','*'),'muledit','attributes')_AFS.tag_object_of(('*','by_transition','*'),'main','hidden')_AFS.tag_object_of(('*','from_state','*'),'main','hidden')_AFS.tag_object_of(('*','to_state','*'),'main','hidden')_AFS.tag_subject_of(('*','wf_info_for','*'),'main','attributes')_AFS.tag_subject_of(('*','wf_info_for','*'),'muledit','attributes')_AFS.tag_object_of(('*','wf_info_for','*'),'main','hidden')_AFS.tag_subject_of(('CWPermission','require_group','*'),'main','attributes')_AFS.tag_subject_of(('CWPermission','require_group','*'),'muledit','attributes')_AFS.tag_attribute(('CWEType','final'),'main','hidden')_AFS.tag_attribute(('CWRType','final'),'main','hidden')_AFS.tag_attribute(('CWUser','firstname'),'main','attributes')_AFS.tag_attribute(('CWUser','surname'),'main','attributes')_AFS.tag_attribute(('CWUser','last_login_time'),'main','metadata')_AFS.tag_subject_of(('CWUser','in_group','*'),'main','attributes')_AFS.tag_subject_of(('CWUser','in_group','*'),'muledit','attributes')_AFS.tag_subject_of(('*','primary_email','*'),'main','relations')_AFS.tag_subject_of(('*','use_email','*'),'main','inlined')_AFS.tag_subject_of(('CWRelation','relation_type','*'),'main','inlined')_AFS.tag_subject_of(('CWRelation','from_entity','*'),'main','inlined')_AFS.tag_subject_of(('CWRelation','to_entity','*'),'main','inlined')_AFFK.tag_attribute(('RQLExpression','expression'),{'widget':fw.TextInput})_AFFK.tag_subject_of(('TrInfo','wf_info_for','*'),{'widget':fw.HiddenInput})defregistration_callback(vreg):globaletype_relation_fielddefetype_relation_field(etype,rtype,role='subject'):eschema=vreg.schema.eschema(etype)returnAutomaticEntityForm.field_by_name(rtype,role,eschema)vreg.register_all(globals().values(),__name__)