"""abstract form classes for CubicWeb web client:organization: Logilab:copyright: 2001-2009 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"fromlogilab.common.decoratorsimporticlassmethodfromcubicweb.appobjectimportAppObjectfromcubicweb.viewimportNOINDEX,NOFOLLOWfromcubicwebimporttagsfromcubicweb.webimportstdmsgs,httpcache,formfieldsclassFormViewMixIn(object):"""abstract form view mix-in"""category='form'http_cache_manager=httpcache.NoHTTPCacheManageradd_to_breadcrumbs=Falsedefhtml_headers(self):"""return a list of html headers (eg something to be inserted between <head> and </head> of the returned page by default forms are neither indexed nor followed """return[NOINDEX,NOFOLLOW]deflinkable(self):"""override since forms are usually linked by an action, so we don't want them to be listed by appli.possible_views """returnFalse###############################################################################classmetafieldsform(type):"""metaclass for FieldsForm to retrieve fields defined as class attributes and put them into a single ordered list: '_fields_'. """def__new__(mcs,name,bases,classdict):allfields=[]forbaseinbases:ifhasattr(base,'_fields_'):allfields+=base._fields_clsfields=(itemforiteminclassdict.items()ifisinstance(item[1],formfields.Field))forfieldname,fieldinsorted(clsfields,key=lambdax:x[1].creation_rank):ifnotfield.name:field.set_name(fieldname)allfields.append(field)classdict['_fields_']=allfieldsreturnsuper(metafieldsform,mcs).__new__(mcs,name,bases,classdict)classFieldNotFound(Exception):"""raised by field_by_name when a field with the given name has not been found """classForm(AppObject):__metaclass__=metafieldsform__registry__='forms'parent_form=Noneforce_session_key=Nonedef__init__(self,req,rset,**kwargs):super(Form,self).__init__(req,rset=rset,**kwargs)self.restore_previous_post(self.session_key())@propertydefroot_form(self):"""return the root form"""ifself.parent_formisNone:returnselfreturnself.parent_form.root_form@propertydefform_previous_values(self):ifself.parent_formisNone:returnself._form_previous_valuesreturnself.parent_form.form_previous_values@propertydefform_valerror(self):ifself.parent_formisNone:returnself._form_valerrorreturnself.parent_form.form_valerror@iclassmethoddef_fieldsattr(cls_or_self):ifisinstance(cls_or_self,type):fields=cls_or_self._fields_else:fields=cls_or_self.fieldsreturnfields@iclassmethoddeffield_by_name(cls_or_self,name,role='subject'):"""return field with the given name and role. Raise FieldNotFound if the field can't be found. """forfieldincls_or_self._fieldsattr():iffield.name==nameandfield.role==role:returnfieldraiseFieldNotFound(name)@iclassmethoddeffields_by_name(cls_or_self,name,role='subject'):"""return a list of fields with the given name and role"""return[fieldforfieldincls_or_self._fieldsattr()iffield.name==nameandfield.role==role]@iclassmethoddefremove_field(cls_or_self,field):"""remove a field from form class or instance"""cls_or_self._fieldsattr().remove(field)@iclassmethoddefappend_field(cls_or_self,field):"""append a field to form class or instance"""cls_or_self._fieldsattr().append(field)@iclassmethoddefinsert_field_before(cls_or_self,new_field,name,role='subject'):field=cls_or_self.field_by_name(name,role)fields=cls_or_self._fieldsattr()fields.insert(fields.index(field),new_field)@iclassmethoddefinsert_field_after(cls_or_self,new_field,name,role='subject'):field=cls_or_self.field_by_name(name,role)fields=cls_or_self._fieldsattr()fields.insert(fields.index(field)+1,new_field)defsession_key(self):"""return the key that may be used to store / retreive data about a previous post which failed because of a validation error """ifself.force_session_keyisNone:return'%s#%s'%(self._cw.url(),self.domid)returnself.force_session_keydefrestore_previous_post(self,sessionkey):# get validation session data which may have been previously set.# deleting validation errors here breaks form reloading (errors are# no more available), they have to be deleted by application's publish# method on successful commitforminfo=self._cw.get_session_data(sessionkey,pop=True)ifforminfo:# XXX remove _cw.data assigment once cw.web.widget is killedself._cw.data['formvalues']=self._form_previous_values=forminfo['values']self._cw.data['formerrors']=self._form_valerror=forminfo['errors']self._cw.data['displayederrors']=self.form_displayed_errors=set()# if some validation error occured on entity creation, we have to# get the original variable name from its attributed eidforeid=self.form_valerror.entityforvar,eidinforminfo['eidmap'].items():ifforeid==eid:self.form_valerror.eid=varbreakelse:self.form_valerror.eid=foreidelse:self._form_previous_values={}self._form_valerror=None