"""HTTP cache managers: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"fromdatetimeimportdatetime# time delta usable to convert localized time to GMT timeGMTOFFSET=-(datetime.now()-datetime.utcnow())classNoHTTPCacheManager(object):"""default cache manager: set no-cache cache control policy"""def__init__(self,view):self.view=viewself.req=view.reqself.rset=view.rsetdefset_headers(self):self.req.set_header('Cache-control','no-cache')classMaxAgeHTTPCacheManager(NoHTTPCacheManager):"""max-age cache manager: set max-age cache control policy, with max-age specified with the `cache_max_age` attribute of the view """defset_headers(self):self.req.set_header('Cache-control','max-age=%s'%self.view.cache_max_age)classEtagHTTPCacheManager(NoHTTPCacheManager):"""etag based cache manager for startup views * etag is generated using the view name and the user's groups * set policy to 'must-revalidate' and expires to the current time to force revalidation on each request """# GMT time requireddate_format="%a, %d %b %Y %H:%M:%S GMT"defetag(self):returnself.view.id+'/'+','.join(sorted(self.req.user.groups))defmax_age(self):# 0 to actually force revalidationreturn0deflast_modified(self):returnself.view.last_modified()defset_headers(self):req=self.reqtry:req.set_header('Etag','"%s"'%self.etag())exceptNoEtag:self.req.set_header('Cache-control','no-cache')returnreq.set_header('Cache-control','must-revalidate;max-age=%s'%self.max_age())mdate=self.last_modified()req.set_header('Last-modified',mdate.strftime(self.date_format))classEntityHTTPCacheManager(EtagHTTPCacheManager):"""etag based cache manager for view displaying a single entity * etag is generated using entity's eid, the view name and the user's groups * get last modified time from the entity definition (this may not be the entity's modification time since a view may include some related entities with a modification time to consider) using the `last_modified` method """defetag(self):ifself.rsetisNoneorlen(self.rset)==0:# entity startup view for instancereturnsuper(EntityHTTPCacheManager,self).etag()iflen(self.rset)>1:raiseNoEtag()etag=super(EntityHTTPCacheManager,self).etag()eid=self.rset[0][0]ifself.req.user.owns(eid):etag+=',owners'returnstr(eid)+'/'+etagclassNoEtag(Exception):"""an etag can't be generated"""__all__=('GMTOFFSET','NoHTTPCacheManager','MaxAgeHTTPCacheManager','EtagHTTPCacheManager','EntityHTTPCacheManager')# monkey patching, so view doesn't depends on this module and we have all# http cache related logic herefromcubicwebimportviewasviewmoddefset_http_cache_headers(self):self.http_cache_manager(self).set_headers()viewmod.View.set_http_cache_headers=set_http_cache_headersdeflast_modified(self):"""return the date/time where this view should be considered as modified. Take care of possible related objects modifications. /!\ must return GMT time /!\ """# XXX check view module's file modification time in dev mod ?ctime=datetime.utcnow()ifself.cache_max_age:mtime=self.req.header_if_modified_since()ifmtime:tdelta=(ctime-mtime)iftdelta.days*24*60*60+tdelta.seconds>self.cache_max_age:mtime=ctimeelse:mtime=ctimeelse:mtime=ctime# mtime = ctime will force page rerenderingreturnmtimeviewmod.View.last_modified=last_modified# configure default cachingviewmod.View.http_cache_manager=NoHTTPCacheManager# max-age=0 to actually force revalidation when neededviewmod.View.cache_max_age=0viewmod.EntityView.http_cache_manager=EntityHTTPCacheManagerviewmod.StartupView.http_cache_manager=MaxAgeHTTPCacheManagerviewmod.StartupView.cache_max_age=60*60*2# stay in http cache for 2 hours by default