delete-trailing-whitespaces, add page_size argument to .paginate
"""The automatic entity form.:organization: Logilab:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr"""__docformat__="restructuredtext en"fromlogilab.common.decoratorsimporticlassmethodfromcubicwebimporttyped_eidfromcubicweb.webimportstdmsgs,uicfgfromcubicweb.web.formimportFieldNotFound,EntityFieldsFormfromcubicweb.web.formfieldsimportguess_fieldfromcubicweb.web.formwidgetsimportButton,SubmitButtonfromcubicweb.web.views.editformsimporttoggleable_relation_link,relation_id_=unicodeclassAutomaticEntityForm(EntityFieldsForm):"""base automatic form to edit any entity. Designed to be fully generated from schema but highly configurable through: * rtags (rcategories, rfields, rwidgets, inlined, rpermissions) * various standard form parameters You can also easily customise it by adding/removing fields in AutomaticEntityForm instances. """id='edition'cwtarget='eformframe'cssclass='entityForm'copy_nav_params=Trueform_buttons=[SubmitButton(stdmsgs.BUTTON_OK),Button(stdmsgs.BUTTON_APPLY,cwaction='apply'),Button(stdmsgs.BUTTON_CANCEL,cwaction='cancel')]attrcategories=('primary','secondary')# class attributes below are actually stored in the uicfg module since we# don't want them to be reloadedrcategories=uicfg.rcategoriesrfields=uicfg.rfieldsrwidgets=uicfg.rwidgetsrinlined=uicfg.rinlinedrpermissions_overrides=uicfg.rpermissions_overrides@classmethoddefvreg_initialization_completed(cls):"""set default category tags for relations where it's not yet defined in the category relation tags """foreschemaincls.schema.entities():forrschema,tschemas,roleineschema.relation_definitions(True):fortschemaintschemas:ifrole=='subject':X,Y=eschema,tschemacard=rschema.rproperty(X,Y,'cardinality')[0]composed=rschema.rproperty(X,Y,'composite')=='object'else:X,Y=tschema,eschemacard=rschema.rproperty(X,Y,'cardinality')[1]composed=rschema.rproperty(X,Y,'composite')=='subject'ifnotcls.rcategories.get(rschema,role,X,Y):ifeschema.is_metadata(rschema):category='generated'elifcardin'1+':ifnotrschema.is_final()andcomposed:category='generated'else:category='primary'elifrschema.is_final():category='secondary'else:category='generic'cls.rcategories.tag_relation(category,(X,rschema,Y),role)@classmethoddeferelations_by_category(cls,entity,categories=None,permission=None,rtags=None):"""return a list of (relation schema, target schemas, role) matching categories and permission """ifcategoriesisnotNone:ifnotisinstance(categories,(list,tuple,set,frozenset)):categories=(categories,)ifnotisinstance(categories,(set,frozenset)):categories=frozenset(categories)eschema=entity.e_schemaifrtagsisNone:rtags=cls.rcategoriespermsoverrides=cls.rpermissions_overridesifentity.has_eid():eid=entity.eidelse:eid=Noneforrschema,targetschemas,roleineschema.relation_definitions(True):# check category first, potentially lower cost than checking# permission which may imply rql queriesifcategoriesisnotNone:targetschemas=[tschemafortschemaintargetschemasifrtags.etype_get(eschema,rschema,role,tschema)incategories]ifnottargetschemas:continueifpermissionisnotNone:# tag allowing to hijack the permission machinery when# permission is not verifiable until the entity is actually# created...ifeidisNoneand'%s_on_new'%permissioninpermsoverrides.etype_get(eschema,rschema,role):yield(rschema,targetschemas,role)continueifrschema.is_final():ifnotrschema.has_perm(entity.req,permission,eid):continueelifrole=='subject':ifnot((eidisNoneandrschema.has_local_role(permission))orrschema.has_perm(entity.req,permission,fromeid=eid)):continue# on relation with cardinality 1 or ?, we need delete perm as well# if the relation is already setif(permission=='add'andrschema.cardinality(eschema,targetschemas[0],role)in'1?'andeidandentity.related(rschema.type,role)andnotrschema.has_perm(entity.req,'delete',fromeid=eid,toeid=entity.related(rschema.type,role)[0][0])):continueelifrole=='object':ifnot((eidisNoneandrschema.has_local_role(permission))orrschema.has_perm(entity.req,permission,toeid=eid)):continue# on relation with cardinality 1 or ?, we need delete perm as well# if the relation is already setif(permission=='add'andrschema.cardinality(targetschemas[0],eschema,role)in'1?'andeidandentity.related(rschema.type,role)andnotrschema.has_perm(entity.req,'delete',toeid=eid,fromeid=entity.related(rschema.type,role)[0][0])):continueyield(rschema,targetschemas,role)@classmethoddefesrelations_by_category(cls,entity,categories=None,permission=None):"""filter out result of relations_by_category(categories, permission) by removing final relations return a sorted list of (relation's label, relation'schema, role) """result=[]forrschema,ttypes,roleincls.erelations_by_category(entity,categories,permission):ifrschema.is_final():continueresult.append((rschema.display_name(entity.req,role),rschema,role))returnsorted(result)@iclassmethoddeffield_by_name(cls_or_self,name,role='subject',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)exceptFieldNotFound:# XXX should raise more explicit exceptionifeschemaisNoneornotnameincls_or_self.schema:raiserschema=cls_or_self.schema.rschema(name)fieldcls=cls_or_self.rfields.etype_get(eschema,rschema,role)iffieldcls:returnfieldcls(name=name,role=role,eidparam=True)widget=cls_or_self.rwidgets.etype_get(eschema,rschema,role)ifwidget:field=guess_field(eschema,rschema,role,eidparam=True,widget=widget)else:field=guess_field(eschema,rschema,role,eidparam=True)iffieldisNone:raisereturnfielddef__init__(self,*args,**kwargs):super(AutomaticEntityForm,self).__init__(*args,**kwargs)entity=self.edited_entityifentity.has_eid():entity.complete()forrschema,roleinself.editable_attributes():try:self.field_by_name(rschema.type,role)continue# explicitly specifiedexceptFieldNotFound:# has to be guessedtry:field=self.field_by_name(rschema.type,role,eschema=entity.e_schema)self.fields.append(field)exceptFieldNotFound:# meta attribute such as <attr>_formatcontinueself.maxrelitems=self.req.property_value('navigation.related-limit')self.force_display=bool(self.req.form.get('__force_display'))@propertydefrelated_limit(self):ifself.force_display:returnNonereturnself.maxrelitems+1defrelations_by_category(self,categories=None,permission=None):"""return a list of (relation schema, target schemas, role) matching given category(ies) and permission """returnself.erelations_by_category(self.edited_entity,categories,permission)definlined_relations(self):"""return a list of (relation schema, target schemas, role) matching given category(ies) and permission """# we'll need an initialized varmaker if there are some inlined relationself.initialize_varmaker()returnself.erelations_by_category(self.edited_entity,True,'add',self.rinlined)defsrelations_by_category(self,categories=None,permission=None):"""filter out result of relations_by_category(categories, permission) by removing final relations return a sorted list of (relation's label, relation'schema, role) """returnself.esrelations_by_category(self.edited_entity,categories,permission)defaction(self):"""return the form's action attribute. Default to validateform if not explicitly overriden. """try:returnself._actionexceptAttributeError:returnself.build_url('validateform')defset_action(self,value):"""override default action"""self._action=valueaction=property(action,set_action)defeditable_attributes(self):"""return a list of (relation schema, role) to edit for the entity"""return[(rschema,x)forrschema,_,xinself.relations_by_category(self.attrcategories,'add')ifrschema!='eid']defrelations_table(self):"""yiels 3-tuples (rtype, target, 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=self.edited_entitypending_deletes=self.req.get_pending_deletes(entity.eid)forlabel,rschema,roleinself.srelations_by_category('generic','add'):relatedrset=entity.related(rschema,role,limit=self.related_limit)ifrschema.has_perm(self.req,'delete'):toggleable_rel_link_func=toggleable_relation_linkelse:toggleable_rel_link_func=lambdax,y,z:u''related=[]forrowinxrange(relatedrset.rowcount):nodeid=relation_id(entity.eid,rschema,role,relatedrset[row][0])ifnodeidinpending_deletes:status=u'pendingDelete'label='+'else:status=u''label='x'dellink=toggleable_rel_link_func(entity.eid,nodeid,label)eview=self.view('oneline',relatedrset,row=row)related.append((nodeid,dellink,status,eview))yield(rschema,role,related)defrestore_pending_inserts(self,cell=False):"""used to restore edition page as it was before clicking on 'search for <some entity type>' """eid=self.edited_entity.eidcell=celland"div_insert_"or"tr"pending_inserts=set(self.req.get_pending_inserts(eid))forpendingidinpending_inserts:eidfrom,rtype,eidto=pendingid.split(':')iftyped_eid(eidfrom)==eid:# subjectlabel=display_name(self.req,rtype,'subject')reid=eidtoelse:label=display_name(self.req,rtype,'object')reid=eidfromjscall="javascript: cancelPendingInsert('%s', '%s', null, %s);" \%(pendingid,cell,eid)rset=self.req.eid_rset(reid)eview=self.view('text',rset,row=0)# XXX find a clean way to handle basketsifrset.description[0][0]=='Basket':eview='%s (%s)'%(eview,display_name(self.req,'Basket'))yieldrtype,pendingid,jscall,label,reid,eview# should_* method extracted to allow overridingdefshould_inline_relation_form(self,rschema,targettype,role):"""return true if the given relation with entity has role and a targettype target should be inlined """returnself.rinlined.etype_get(self.edited_entity.id,rschema,role,targettype)defshould_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+'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'+*'defetype_relation_field(etype,rtype,role='subject'):eschema=AutomaticEntityForm.schema.eschema(etype)returnAutomaticEntityForm.field_by_name(rtype,role,eschema)