[server] Port BFSS to py3k
The BFSS API changes in python 3:
* 'defaultdir' MUST be a unicode object
* 'fsencoding' MUST NOT be set
In python 2, fsencoding handles both the encoding of file paths on the
file system (utf-8 by default, but the system may actually be using
something else) and the encoding of file paths that will be stored in
the database.
So in python 3, we wipe the slate clean:
* rely on sys.getfilesystemencoding() to convert unicode objects to
bytes
* always encode paths to utf-8 for storage in the database
Caveat emptor / here be dragons:
* sys.getfilesystemencoding() depends on the current locale, which
therefore MUST be set properly
* when migrating an existing instance from py2 to py3, one MAY need
to reencode file paths stored in the database
# copyright 2003-2013 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 controller classe for CubicWeb web client"""__docformat__="restructuredtext en"fromsiximportPY2fromlogilab.mtconverterimportxml_escapefromlogilab.common.registryimportyesfromlogilab.common.deprecationimportdeprecatedfromcubicweb.appobjectimportAppObjectfromcubicweb.mailimportformat_mailfromcubicweb.webimportLOGGER,Redirect,RequestErrorNAVIGATION_PARAMETERS=(('vid','__redirectvid'),('rql','__redirectrql'),('__redirectpath','__redirectpath'),('__redirectparams','__redirectparams'),)NAV_FORM_PARAMETERS=tuple(fpforap,fpinNAVIGATION_PARAMETERS)defredirect_params(form):"""transform redirection parameters into navigation parameters """params={}# extract navigation parameters from redirection parametersfornavparam,redirectparaminNAVIGATION_PARAMETERS:ifnavparam==redirectparam:continueifredirectparaminform:params[navparam]=form[redirectparam]returnparamsdefappend_url_params(url,params):"""append raw parameters to the url. Given parameters, if any, are expected to be already url-quoted. """ifparams:ifnot'?'inurl:url+='?'else:url+='&'url+=paramsreturnurlclassController(AppObject):"""a controller is responsible to make necessary stuff to publish a request. There is usually at least one standard "view" controller and another linked by forms to edit objects ("edit"). """__registry__='controllers'__select__=yes()def__init__(self,*args,**kwargs):self.appli=kwargs.pop('appli',None)super(Controller,self).__init__(*args,**kwargs)# attributes use to control after edition redirectionself._after_deletion_path=Noneself._edited_entity=Nonedefpublish(self,rset=None):"""publish the current request, with an optional input rset"""raiseNotImplementedError# generic methods useful for concrete implementations ######################defprocess_rql(self):"""execute rql if specified"""req=self._cwrql=req.form.get('rql')ifrql:req.ensure_ro_rql(rql)ifPY2andnotisinstance(rql,unicode):rql=unicode(rql,req.encoding)pp=req.vreg['components'].select_or_none('magicsearch',req)ifppisnotNone:returnpp.process_query(rql)if'eid'inreq.formandnotisinstance(req.form['eid'],list):returnreq.eid_rset(req.form['eid'])returnNonedefnotify_edited(self,entity):"""called by edit_entity() to notify which entity is edited"""# NOTE: we can't use entity.rest_path() at this point because# rest_path() could rely on schema constraints (such as a required# relation) that might not be satisfied yet (in case of creations)ifnotself._edited_entity:self._edited_entity=entity@deprecated('[3.18] call view.set_http_cache_headers then ''.is_client_cache_valid() method and return instead')defvalidate_cache(self,view):view.set_http_cache_headers()self._cw.validate_cache()defsendmail(self,recipient,subject,body):senderemail=self._cw.user.cw_adapt_to('IEmailable').get_email()msg=format_mail({'email':senderemail,'name':self._cw.user.dc_title(),},[recipient],body,subject)ifnotself._cw.vreg.config.sendmails([(msg,[recipient])]):msg=self._cw._('could not connect to the SMTP server')url=self._cw.build_url(__message=msg)raiseRedirect(url)defreset(self):"""reset form parameters and redirect to a view determinated by given parameters """newparams={}# sets message if needed# XXX - don't call .message twice since it pops the idmsg=self._cw.messageifmsg:newparams['_cwmsgid']=self._cw.set_redirect_message(msg)if'__action_apply'inself._cw.form:self._return_to_edition_view(newparams)if'__action_cancel'inself._cw.form:self._return_to_lastpage(newparams)else:self._return_to_original_view(newparams)def_return_to_original_view(self,newparams):"""validate-button case"""# transforms __redirect[*] parameters into regular form parametersnewparams.update(redirect_params(self._cw.form))# find out if we have some explicit `rql` needsrql=newparams.pop('rql',None)# if rql is needed (explicit __redirectrql or multiple deletions for# instance), we have to use the old `view?rql=...` formifrql:path='view'newparams['rql']=rqlelif'__redirectpath'inself._cw.form:# if redirect path was explicitly specified in the form, use itpath=self._cw.form['__redirectpath']if(self._edited_entityandpath!=self._edited_entity.rest_path()and'_cwmsgid'innewparams):# are we here on creation or modification?ifany(eid==self._edited_entity.eidforeidinself._cw.data.get('eidmap',{}).values()):msg=self._cw._('click here to see created entity')else:msg=self._cw._('click here to see edited entity')msg=u'(<a href="%s">%s</a>)'%(xml_escape(self._edited_entity.absolute_url()),msg)self._cw.append_to_redirect_message(msg)elifself._after_deletion_path:# else it should have been set during form processingpath,params=self._after_deletion_pathparams=dict(params)# params given as tupleparams.update(newparams)newparams=paramselifself._edited_entity:# clear caches in case some attribute participating to the rest path# has been modifiedself._edited_entity.cw_clear_all_caches()path=self._edited_entity.rest_path()else:path='view'url=self._cw.build_url(path,**newparams)url=append_url_params(url,self._cw.form.get('__redirectparams'))raiseRedirect(url)def_return_to_edition_view(self,newparams):"""apply-button case"""form=self._cw.formifself._edited_entity:path=self._edited_entity.rest_path()newparams.pop('rql',None)# else, fallback on the old `view?rql=...` url formelif'rql'inself._cw.form:path='view'newparams['rql']=form['rql']else:self.warning('the edited data seems inconsistent')path='view'# pick up the correction edition viewifform.get('__form_id'):newparams['vid']=form['__form_id']# re-insert copy redirection parametersforredirectparaminNAV_FORM_PARAMETERS:ifredirectparaminform:newparams[redirectparam]=form[redirectparam]raiseRedirect(self._cw.build_url(path,**newparams))def_return_to_lastpage(self,newparams):"""cancel-button case: in this case we are always expecting to go back where we came from, and this is not easy. Currently we suppose that __redirectpath is specifying that place if found, else we look in the request breadcrumbs for the last visited page. """if'__redirectpath'inself._cw.form:# if redirect path was explicitly specified in the form, use itpath=self._cw.form['__redirectpath']url=self._cw.build_url(path)url=append_url_params(url,self._cw.form.get('__redirectparams'))else:url=self._cw.last_visited_page()# The newparams must update the params in all casesurl=self._cw.rebuild_url(url,**newparams)raiseRedirect(url)fromcubicwebimportset_log_methodsset_log_methods(Controller,LOGGER)