[server/session] Implement anonymous_session
Now we have a simple rule to compute if a session is anonymous we can implement
the property for server session too. Having it on server side session will helps
the rework of the API to access repository. The new schema drop the concept of
DBAPISession and use server side session for the same purpose.
Related to #2953943
Related to #2503918
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of CubicWeb.## CubicWeb is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## CubicWeb is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with CubicWeb. If not, see <http://www.gnu.org/licenses/>."""abstract form classes for CubicWeb web client"""__docformat__="restructuredtext en"fromwarningsimportwarnfromlogilab.common.decoratorsimporticlassmethodfromlogilab.common.deprecationimportdeprecatedfromcubicweb.appobjectimportAppObjectfromcubicweb.viewimportNOINDEX,NOFOLLOWfromcubicweb.webimporthttpcache,formfields,controller,formwidgetsasfwdgsclassFormViewMixIn(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=Nonedomid='form'copy_nav_params=Falsecontrol_fields=set(('__form_id','__errorurl','__domid','__redirectpath','_cwmsgid','__message',))def__init__(self,req,rset=None,row=None,col=None,submitmsg=None,mainform=True,**kwargs):# process kwargs first so we can properly pass them to Form and match# order expectation (ie cw_extra_kwargs populated almost first)hiddens,extrakw=self._process_kwargs(kwargs)# now call ancestor initsuper(Form,self).__init__(req,rset=rset,row=row,col=col,**extrakw)# then continue with further specific initializationself.fields=list(self.__class__._fields_)forkey,valinhiddens:self.add_hidden(key,val)ifmainform:formid=kwargs.pop('formvid',self.__regid__)self.add_hidden(u'__form_id',formid)self._posting=self._cw.form.get('__form_id')==formidifmainform:self.add_hidden(u'__errorurl',self.session_key())self.add_hidden(u'__domid',self.domid)self.restore_previous_post(self.session_key())# XXX why do we need two different variables (mainform and copy_nav_params ?)ifself.copy_nav_params:forparamincontroller.NAV_FORM_PARAMETERS:ifnotparaminkwargs:value=req.form.get(param)ifvalue:self.add_hidden(param,value)ifsubmitmsgisnotNone:self.set_message(submitmsg)def_process_kwargs(self,kwargs):hiddens=[]extrakw={}# search for navigation parameters and customization of existing# attributes; remaining stuff goes in extrakwargsforkey,valinkwargs.iteritems():ifkeyincontroller.NAV_FORM_PARAMETERS:hiddens.append((key,val))elifkey=='redirect_path':hiddens.append((u'__redirectpath',val))elifhasattr(self.__class__,key)andnotkey[0]=='_':setattr(self,key,val)else:extrakw[key]=valreturnhiddens,extrakwdefset_message(self,submitmsg):"""sets a submitmsg if exists, using _cwmsgid mechanism """cwmsgid=self._cw.set_redirect_message(submitmsg)self.add_hidden(u'_cwmsgid',cwmsgid)@propertydefroot_form(self):"""return the root form"""ifself.parent_formisNone:returnselfreturnself.parent_form.root_form@propertydefform_valerror(self):"""the validation error exception if any"""ifself.parent_formisNone:# unset if restore_previous_post has not be calledreturngetattr(self,'_form_valerror',None)returnself.parent_form.form_valerror@propertydefform_previous_values(self):"""previously posted values (on validation error)"""ifself.parent_formisNone:# unset if restore_previous_post has not be calledreturngetattr(self,'_form_previous_values',{})returnself.parent_form.form_previous_values@propertydefposting(self):"""return True if the form is being posted, False if it is being generated. """# XXX check behaviour on regeneration after errorifself.parent_formisNone:returnself._postingreturnself.parent_form.posting@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=None):"""Return field with the given name and role. Raise :exc:`FieldNotFound` if the field can't be found. """forfieldincls_or_self._fieldsattr():iffield.name==nameandfield.role==role:returnfieldraiseFieldNotFound(name,role)@iclassmethoddeffields_by_name(cls_or_self,name,role=None):"""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 the given field."""cls_or_self._fieldsattr().remove(field)@iclassmethoddefappend_field(cls_or_self,field):"""Append the given field."""cls_or_self._fieldsattr().append(field)@iclassmethoddefinsert_field_before(cls_or_self,field,name,role=None):"""Insert the given field before the field of given name and role."""bfield=cls_or_self.field_by_name(name,role)fields=cls_or_self._fieldsattr()fields.insert(fields.index(bfield),field)@iclassmethoddefinsert_field_after(cls_or_self,field,name,role=None):"""Insert the given field after the field of given name and role."""afield=cls_or_self.field_by_name(name,role)fields=cls_or_self._fieldsattr()fields.insert(fields.index(afield)+1,field)@iclassmethoddefadd_hidden(cls_or_self,name,value=None,**kwargs):"""Append an hidden field to the form. `name`, `value` and extra keyword arguments will be given to the field constructor. The inserted field is returned. """kwargs.setdefault('ignore_req_params',True)kwargs.setdefault('widget',fwdgs.HiddenInput)field=formfields.StringField(name=name,value=value,**kwargs)if'id'inkwargs:# by default, hidden input don't set id attribute. If one is# explicitly specified, ensure it will be setfield.widget.setdomid=Truecls_or_self.append_field(field)returnfielddefsession_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.session.data.pop(sessionkey,None)ifforminfo:self._form_previous_values=forminfo['values']self._form_valerror=forminfo['error']# if some validation error occurred 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=Nonedeffield_error(self,field):"""return field's error if specified in current validation exception"""ifself.form_valerror:iffield.eidparamandself.edited_entity.eid!=self.form_valerror.eid:returnNonetry:returnself.form_valerror.errors.pop(field.role_name())exceptKeyError:iffield.roleandfield.nameinself.form_valerror:warn('%s: errors key of attribute/relation should be suffixed by "-<role>"'%self.form_valerror.__class__,DeprecationWarning)returnself.form_valerror.errors.pop(field.name)returnNonedefremaining_errors(self):returnsorted(self.form_valerror.errors.items())