We often have the following problem :

  1. a CSS or a Javascript file is changed in a package
  2. we make a release (or not) and put it to production
  3. some visitors still gets old CSS and Javascript versions (except if by chance we remembered to reinstall package / re-save portal_css and portal_javascripts configs) and need to flush their browser cache

As we do not have great memory and as our customer’s Helpdesk was tired of doing remote cache flush, we wanted to do something (automatic) about it.
Plone adds cache keys as suffixes for CSS and JS files, as for example base-cachekey6247.css.
This cache key doesn’t change, even after an instance restart. This is why browsers can keep the “old” versions cached.

To avoid that (and without touching to Apache or whatever), we wrote a little script that exectutes at the end of the instance startup process.It forces the “cook” of resources which generates a new cache key so that browser cannot cache CSS and JS anymore !

This script can of course be improved, but it fits our needs.

[Please note that this code is written for Plone 3.]
First, we subscribe to IDatabaseOpenedWithRootEvent (too bad we couldn’t use IProcessStarting because we have no context to get Plone sites) :


Then, our handler gets the work done :

# -*- coding: utf-8 -*-                                                    

from Products.CMFCore.utils import getToolByName                           
from zope.app.appsetup.bootstrap import getInformationFromEvent            
import transaction                                                         

from our.package import logger                                       

def clear_cache(event):                                                    
    Force cache key renewal for CSS and JS on all Plone sites at startup.  
    This avoids any caching of changed CSS or JS.          
    db, connection, root, root_folder = getInformationFromEvent(event)     
    app = root_folder                                                      
    for obj_tuple in app.items():                                           
        (obj_name, obj) = obj_tuple                                            
        if obj.get('portal_type') == 'Plone Site':                         
            p_css = getToolByName(obj, 'portal_css')                        
            p_js = getToolByName(obj, 'portal_javascripts')                 
            logger.info('Cache key changed for CSS/JS on Plone %s' % obj_name)

And voilĂ , no more browser cache flush hassle for our customer and their Helpdesk :-)

If you have any thoughts on this, or think that this should be packaged in a collective egg (useful for you ?), feel free to comment.