source: products/vendor/Products.CacheSetup/current/Products/CacheSetup/content/cache_tool.py @ 3296

Last change on this file since 3296 was 3296, checked in by fenix, 13 years ago

Load Products.CacheSetup?-1.2.1 into vendor/Products.CacheSetup?/current.

  • Property svn:eol-style set to native
File size: 29.3 KB
Line 
1"""
2CacheSetup
3~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5$Id: $
6"""
7
8__authors__ = 'Geoff Davis <geoff@geoffdavis.net>'
9__docformat__ = 'restructuredtext'
10
11import sets
12import urlparse
13
14import zope.component
15from zope.interface import implements
16
17from Acquisition import aq_get
18from Acquisition import aq_inner
19from AccessControl import ClassSecurityInfo
20from AccessControl.PermissionRole import rolesForPermissionOn
21from BTrees import Length
22from DateTime import DateTime
23from ZODB.POSException import ConflictError
24from Products.Transience import Transience
25
26from Products.CMFCore.utils import UniqueObject
27from Products.CMFCore.utils import getToolByName
28from Products.CMFCore import permissions
29from Products.CMFCore.utils import _checkPermission
30
31from Products.Archetypes.atapi import Schema
32from Products.Archetypes.atapi import BaseSchema
33from Products.Archetypes.atapi import DisplayList
34from Products.Archetypes.atapi import OrderedBaseFolder
35from Products.Archetypes.atapi import registerType
36
37from Products.Archetypes.atapi import BooleanField
38from Products.Archetypes.atapi import StringField
39from Products.Archetypes.atapi import LinesField
40
41from Products.Archetypes.atapi import BooleanWidget
42from Products.Archetypes.atapi import SelectionWidget
43from Products.Archetypes.atapi import LinesWidget
44from Products.Archetypes.atapi import StringWidget
45
46from Products.Archetypes.debug import log_exc
47from Products.CMFPlone import PloneMessageFactory as _
48
49from Products.statusmessages.interfaces import IStatusMessage
50
51from Products.CacheSetup.interfaces import ICacheTool
52from Products.CacheSetup.interfaces import IPurgeUrls
53from Products.CacheSetup.enabler import enableCacheFu
54from Products.CacheSetup import config
55from Products.CacheSetup import __version__
56from nocatalog import NoCatalog
57
58schema = BaseSchema.copy() + Schema((
59
60    BooleanField(
61        'enabled',
62        default=False,
63        write_permission = permissions.ManagePortal,
64        widget=BooleanWidget(
65            label='Enable CacheFu',
66            description='Uncheck to turn off CacheFu\'s caching behavior.  Note: Disabling CacheFu '
67                        'does not purge proxy or browser caches so stale content may still continue '
68                        'to be served out of those caches.')),
69
70    StringField(
71        'activePolicyId',
72        default=config.DEFAULT_POLICY_ID,
73        vocabulary='getActivePolicyVocabulary',
74        write_permission = permissions.ManagePortal,
75        widget=SelectionWidget(
76            label='Active Cache Policy',
77            description='Please indicate which cache policy to use.',
78            condition='python:len(object.getActivePolicyVocabulary()) > 1')),
79
80    # XXX: The cacheConfig field is no longer used, and kept here hidden only so that we can migrate away from it
81    StringField( 
82        'cacheConfig',
83        widget=SelectionWidget(
84            label='Cache Configuration',
85            visible={'edit': 'invisible', 'view': 'invisible'})), 
86
87    StringField(
88        'proxyPurgeConfig',
89        required=1,
90        default='no-purge',
91        write_permission = permissions.ManagePortal,
92        vocabulary=DisplayList((
93            ('no-purge','No Purge (zope-only, or zope-behind-apache)'),
94            ('no-rewrite','Simple Purge (squid/varnish in front)'),
95            ('vhm-rewrite','Purge with VHM URLs (squid/varnish behind apache, VHM virtual hosting)'),
96            ('custom-rewrite','Purge with custom URLs (squid/varnish behind apache, custom virtual hosting)'))),
97        widget=SelectionWidget(
98            label='Proxy Cache Purge Configuration',
99            description='If you are using a caching proxy such as Squid or Varnish in front '
100                        'of Zope, CacheFu needs to be able to tell this proxy to purge its '
101                        'cache of certain pages. If Apache is in front of Squid/Varnish, then '
102                        'this depends on Apache\'s "virtual hosting" configuration. The most common '
103                        'Apache configuration generates VirtualHostMonster-style URLs with '
104                        'RewriteRules/ProxyPass. If you have a legacy CacheFu 1.0 Squid-Apache '
105                        'install or other custom Apache configuration, you may want to choose '
106                        'the "custom URLs" option and customize the rewritePurgeUrls.py script.')),
107
108    LinesField(
109        'domains',
110        edit_accessor='getDomains',
111        write_permission = permissions.ManagePortal,
112        widget=LinesWidget(
113            label='Site Domains',
114            description='Enter a list of domains for your site.  This is not needed if you chose "No Purge" '
115                        'under the Proxy Cache Purge Configuration option above. '
116                        'If your site handles both http://www.mysite.com:80 and http://mysite.com:80, '
117                        'be sure to include both. Also include https versions of your domains if you use them. '
118                        'Be sure to include a port for each site.')),
119
120    LinesField(
121        'squidURLs',
122        edit_accessor='getSquidURLs',
123        write_permission = permissions.ManagePortal,
124        widget=LinesWidget(
125            label='Proxy Cache Domains',
126            description='Enter a list of domains for any purgeable proxy caches. This is not needed if '
127                        'you chose "No Purge" or "Simple Purge" under "Proxy Cache Purge Configuration" '
128                        'above. For example, if you are using Squid with Apache in front, there will '
129                        'commonly be a single squid instance at http://127.0.0.1:3128')),
130
131    StringField(
132        'gzip',
133        default='accept-encoding',
134        write_permission = permissions.ManagePortal,
135        vocabulary=DisplayList((
136            ('never','Never'),
137            ('always','Always'),
138            ('accept-encoding','Use Accept-Encoding header'),
139            ('accept-encoding+user-agent','Use Accept-Encoding and User-Agent headers'))),
140        widget=SelectionWidget(
141            label='Compression',
142            description='Should Zope compress pages before serving them, and if so, what criteria '
143                        'should be used to determine whether pages should be gzipped? The most common '
144                        'settings are "Never" (no compression) or "Use Accept-Encoding header" '
145                        '(only compress content if the browser explicitly declared support for compression).')),
146
147    StringField(
148        'varyHeader',
149        default='Accept-Encoding',
150        write_permission = permissions.ManagePortal,
151        widget=StringWidget(
152            label='Vary Header',
153            size=60,
154            description='Value for the Vary header.  If you are using gzipping, you may need to include '
155                        '"Accept-Encoding" and possibly "User-Agent". If you are running a multi-lingual site, '
156                        'you may also need "Accept-Language". Values should be separated by commas. '
157                        '(Upon submit, this value will be cleaned up and checked for any obvious omissions)')),
158    ))
159
160schema['title'].default = config.TOOL_TITLE
161schema['title'].widget.visible = {'edit': 'invisible', 'view': 'invisible'}
162schema['id'].widget.visible = {'edit': 'invisible', 'view': 'invisible'}
163
164class CacheTool(NoCatalog, OrderedBaseFolder, UniqueObject):
165    """
166    """
167
168    implements(ICacheTool)
169    archetype_name = 'Cache Configuration Tool'
170    portal_type = meta_type = 'CacheTool'
171    plone_tool = 1
172    security = ClassSecurityInfo()
173    schema = schema
174    content_icon = 'cachesetup_tool_icon.gif'
175    global_allow = 0
176    allowed_content_types=('CachePolicy',)
177    _catalog_count = None
178    _permission_count = None
179    _change_date = None
180
181    actions = (
182        {'action':      'string:$object_url',
183         'category':    'object',
184         'id':          'view',
185         'name':        'Cache Setup',
186         'permissions': (permissions.ManagePortal,),
187         'visible':     False
188        },
189    )
190
191    aliases = {
192        '(Default)':    'cache_tool_config',
193        'view' :        'cache_tool_config',
194        'edit' :        'base_edit'
195    }
196
197    def __init__(self, oid, **kwargs):
198        # keep track of the installed version
199        OrderedBaseFolder.__init__(self, oid, **kwargs)
200        self.installedversion = __version__
201
202    def updateInstalledVersion(self):
203        # call this method after a reinstall
204        # to update installedversion tracking
205        self.installedversion = __version__
206
207    def initializeArchetype(self, **kwargs):
208        # get values from other places before archetypes initializes them
209        squid_urls = self._getSquidUrls()
210        OrderedBaseFolder.initializeArchetype(self, **kwargs)
211        # don't stomp on squid urls
212        self._setSquidUrls(squid_urls)
213
214    def getActivePolicyVocabulary(self, display=None):
215        active = self.getActivePolicyId()
216        vocabulary = [(p.getId(), p.Title()) for p in self.objectValues() if p.portal_type=="CachePolicy"]
217        if display:
218            vocabulary = [('(active policy)', '(active policy)')] + vocabulary
219        return DisplayList(tuple(vocabulary))
220
221    # if active is gone, find another
222    def getActivePolicyId(self):
223        policy_id = self.getField('activePolicyId').get(self)
224        if getattr(self, str(policy_id), None) is None:
225            rules = getattr(self, config.RULES_ID, None)
226            if rules is not None:
227                # we haven't migrated yet
228                return ''
229            else:
230                # policy deleted so try the first one
231                policies = self.objectValues()
232                if len(policies):
233                    policy_id = policies[0].getId()
234                else:
235                    # no policies so add a blank set
236                    self.addPolicyFolder(config.DEFAULT_POLICY_ID, 'Default Cache Policy')
237                    policy_id = config.DEFAULT_POLICY_ID
238            self.setActivePolicyId(policy_id)
239        return policy_id
240
241    # use browser cookies to store current display policy id
242    def setDisplayPolicy(self, id=None, camefrom=None, redirect=True):
243        request = self.REQUEST
244        response = request.response
245        cookie_name = 'cachetool_policy'
246        id = str(id)
247        display_policy = getattr(self, id, None)
248        if getattr(self, id, None) is None:
249            # clear the cookie
250            response.setCookie(cookie_name, '', path='/')
251            id = None
252        else:
253            # expire cookie after 6 hours
254            expires = (DateTime() + .25).toZone('GMT').rfc822()
255            response.setCookie(cookie_name, id, path='/', expires=expires)
256        if redirect:
257            if camefrom is None:
258                response.redirect('%s/cache_policy_config' % self.absolute_url())
259            else:
260                policy = self.getPolicy(id)
261                response.redirect('%s/%s' % (policy.absolute_url(), camefrom))
262        return ''
263
264    def getEnabled(self):
265        """Let's disable CacheFu if the file system version doesn't
266        match the installed version to make sure we don't kill the site
267        if the schemas have changed.
268        """
269        if getattr(self, 'installedversion', None) != __version__ :
270            return False
271        return self.getField('enabled').get(self)
272
273    def setEnabled(self, value):
274        # convert to boolean for backwards compat with Plone 2.5
275        if not value or value == '0' or value == 'False':
276            value = False
277        else:
278            value = True
279        # if version mismatch, send status message
280        if value and getattr(self, 'installedversion', None) != __version__ :
281            IStatusMessage(self.REQUEST).addStatusMessage(
282                    _(u"Refusing to enable CacheSetup until installed version matches "
283                      u"filesystem version. Update/reinstall CacheSetup to fix."),
284                    type="error")
285        # change field only if different
286        elif value != self.getField('enabled').get(self):
287            self.getField('enabled').set(self, value)
288            enableCacheFu(self, value)
289
290    def getPolicy(self, policy_id=None):
291        if policy_id is None:
292            policy_id = self.getActivePolicyId()
293        policy = getattr(self, policy_id, None)
294        if policy is None:
295            rules = getattr(self, config.RULES_ID, None)
296            if rules is not None:
297                # we haven't migrated yet
298                policy = self
299            else:
300                # policy deleted so try the first one
301                policies = self.objectValues()
302                if len(policies):
303                    policy = policies[0]
304                else:
305                    # no policies so add a blank set
306                    self.addPolicyFolder(config.DEFAULT_POLICY_ID, 'Default Cache Policy')
307                    policy = getattr(self, config.DEFAULT_POLICY_ID)
308        return policy
309
310    def getDisplayPolicy(self):
311        request = self.REQUEST
312        response = request.response
313        cookie_name = 'cachetool_policy'
314        id = request.cookies.get(cookie_name, '')
315        display_policy = getattr(self, id, None)
316        if display_policy is None:
317            if id != '': response.setCookie(cookie_name, '', path='/')
318            display_policy = self.getPolicy()
319        return display_policy
320
321    def getRules(self, policy_id=None):
322        policy = self.getPolicy(policy_id)
323        rules = getattr(policy, config.RULES_ID, None)
324        if rules is None:
325            policy_id = policy.getId()
326            self.addRulesFolder(policy_id)
327            rules = self.getRules(policy_id)
328        return rules
329
330    def getHeaderSets(self, policy_id=None):
331        policy = self.getPolicy(policy_id)
332        header_sets = getattr(policy, config.HEADERSETS_ID, None)
333        if header_sets is None:
334            policy_id = policy.getId()
335            self.addHeaderSetsFolder(policy_id)
336            header_sets = self.getHeaderSets(policy_id)
337        return header_sets
338
339    def getHeaderSetById(self, id):
340        return getattr(self.getHeaderSets(), id)
341
342    def addPolicyFolder(self, policy_id, policy_title, empty=None):
343        policy = getattr(self, policy_id, None)
344        if policy is not None:
345            self.manage_delObjects(policy_id)
346        self.invokeFactory(id=policy_id, type_name='CachePolicy')
347        policy = getattr(self, policy_id)
348        policy.unmarkCreationFlag()
349        policy.setTitle(policy_title)
350        policy.reindexObject()
351        if empty is not None:
352            policy_id = policy.getId()
353            self.addRulesFolder(policy_id)
354            self.addHeaderSetsFolder(policy_id)
355
356    def addRulesFolder(self, policy_id):
357        policy = getattr(self, policy_id)
358        policy.allowed_content_types = ('RuleFolder',)
359        policy.invokeFactory(id=config.RULES_ID, type_name='RuleFolder')
360        policy.allowed_content_types = ()
361        rules = self.getRules(policy.getId())
362        rules.unmarkCreationFlag()
363        rules.setTitle('Rules')
364        rules.reindexObject()
365
366    def addHeaderSetsFolder(self, policy_id):
367        policy = getattr(self, policy_id)
368        policy.allowed_content_types = ('HeaderSetFolder',)
369        policy.invokeFactory(id=config.HEADERSETS_ID, type_name='HeaderSetFolder')
370        policy.allowed_content_types = ()
371        header_sets = self.getHeaderSets(policy.getId())
372        header_sets.unmarkCreationFlag()
373        header_sets.setTitle('Headers')
374        header_sets.reindexObject()
375
376    # ##### Counters for use in ETag/cache key building #####
377
378    def updateChangeDate(self):
379        # change_date is a DateTime that we will update
380        # every time we increment a counter
381        if self._change_date is None:
382            self._change_date = Transience.Increaser(DateTime())
383        self._change_date.set(DateTime())
384
385    def getChangeDate(self):
386        return self._change_date()
387
388    def incrementCatalogCount(self):
389        # catalog_count is a minimal counter object that we will increment
390        # every time an object is indexed/reindexed/unindexed -- we will
391        # then use this for cache invalidation
392        if self._catalog_count is None:
393            self._catalog_count = Length.Length()
394        self._catalog_count.change(1)
395        self.updateChangeDate()
396
397    def getCatalogCount(self):
398        return self._catalog_count()
399
400    def incrementPermissionCount(self):
401        # permission_count is a minimal counter object that we will increment every
402        # time the relationship between roles and permissions changes.  We will use
403        # this value for cache invalidation
404        if self._permission_count is None:
405            self._permission_count = Length.Length()
406        self._permission_count.change(1)
407        self.updateChangeDate()
408
409    def getPermissionCount(self):
410        if self._permission_count is None:
411            self._permission_count = Length.Length()
412        return self._permission_count()
413
414    # ##### Accessors, mutators, and helper methods used in configuration ######
415
416    def _getCompleteUrl(self, url):
417        if url.find('//') == -1:
418            url = 'http://' + url
419        p = urlparse.urlparse(url)
420        protocol = p[0]
421        if not protocol:
422            protocol = 'http'
423        host = p[1]
424        split_host = host.split(':')
425        if len(split_host) == 1:
426            if protocol == 'https':
427                port = '443'
428            else:
429                port = '80'
430            host = split_host[0] + ':' + port
431        return urlparse.urlunparse((protocol, host, '','','',''))
432
433    def _getSquidUrls(self):
434        # get a list of urls from squid
435        squid_tool = getToolByName(self, 'portal_squid')
436        return tuple([url for url in squid_tool.getSquidURLs().split('\n') if url])
437       
438    def _setSquidUrls(self, list_of_urls):
439        # pass a \n-joined list of urls to squid tool
440        squid_tool = getToolByName(self, 'portal_squid')
441        squid_tool.manage_setSquidSettings('\n'.join(list_of_urls))
442
443    def hasPurgeableProxy(self):
444        # return self.getCacheConfig() in ('squid', 'squid_behind_apache')
445        return self.getProxyPurgeConfig() != 'no-purge'
446
447    def getDomains(self):
448        if self.getProxyPurgeConfig() in ('vhm-rewrite','custom-rewrite'):
449            return self.getField('domains').get(self)
450        else:
451            return self._getSquidUrls()
452
453    security.declareProtected(permissions.ManagePortal, 'setDomains')
454    def setDomains(self, value):
455        if value is None:
456            value = ''
457        if type(value) == type(''):
458            value = value.replace('\r','\n')
459            value = value.split('\n')
460        value = [v.strip() for v in value if v]
461        domains = []
462        for v in value:
463            domains.append(self._getCompleteUrl(v))
464        if self.getProxyPurgeConfig() in ('vhm-rewrite','custom-rewrite'):
465            self.getField('domains').set(self, domains)
466        else:
467            self._setSquidUrls(domains)
468
469    security.declareProtected(permissions.View, 'getSquidURLs')
470    def getSquidURLs(self):
471        if self.getProxyPurgeConfig() in ('vhm-rewrite','custom-rewrite'):
472            return self._getSquidUrls()
473        else:
474            return ''
475
476    security.declareProtected(permissions.ManagePortal, 'setSquidURLs')
477    def setSquidURLs(self, value):
478        if self.getProxyPurgeConfig() in ('vhm-rewrite','custom-rewrite'):
479            if value is None:
480                value = ''
481            if type(value) == type(''):
482                value = value.replace('\r','\n')
483                value = value.split('\n')
484            self._setSquidUrls([self._getCompleteUrl(v) for v in value if v])
485
486    security.declareProtected(permissions.ManagePortal, 'setVaryHeader')
487    def setVaryHeader(self, value):
488        # Correct for missing headers
489        values = [v.strip() for v in value.split(',')]
490        request = aq_get(self, 'REQUEST', None)
491        if request is not None:
492            gzip = request.get('gzip',None)
493        else:
494            gzip = self.getGzip()
495        if gzip in ('accept-encoding', 'accept-encoding+user-agent'):
496            if not 'Accept-Encoding' in values:
497                values.append('Accept-Encoding')
498            if gzip == 'accept-encoding+user-agent':
499                if not 'User-Agent' in values:
500                    values.append('User-Agent')
501        value = ', '.join(values)
502        self.getField('varyHeader').set(self, value)
503
504    def post_validate(self, REQUEST, errors):
505        proxy_purge_config = REQUEST.get('proxyPurgeConfig',None)
506        squid_urls = REQUEST.get('squidURLs',None)
507        if proxy_purge_config in ('vhm-rewrite','custom-rewrite'):
508            if not squid_urls:
509                errors['squidURLs'] = 'Please enter the URLs for your proxy caches.  We need this to generate the URLs for PURGE requests.'
510        else:
511            if squid_urls:
512                errors['squidURLs'] = 'Set this field only if rewriting proxy cache PURGE requests'
513
514        if proxy_purge_config in ('no-rewrite','vhm-rewrite','custom-rewrite'):
515            if not REQUEST.get('domains', None):
516                errors['domains'] = 'Please enter the domains that you will be caching. We need this to generate proper PURGE requests.'
517
518        # Not needed anymore since setVaryHeader fixes itself if necessary
519        #gzip = REQUEST.get('gzip',None)
520        #vary_header = REQUEST.get('varyHeader','')
521        #values = [v.strip() for v in vary_header.split(',')]
522        #if gzip in ('accept-encoding', 'accept-encoding+user-agent'):
523        #    if not 'Accept-Encoding' in values:
524        #        errors['varyHeader'] = 'When Compression is set to "%s", you need "Accept-Encoding" in the Vary header' % gzip
525        #    if gzip == 'accept-encoding+user-agent':
526        #        if not 'User-Agent' in values:
527        #            errors['varyHeader'] = 'When Compression is set to %s, you need "User-Agent" in the Vary header' % gzip
528
529    security.declareProtected(permissions.ManagePortal, 'manage_purgePageCache')
530    def manage_purgePageCache(self, REQUEST=None):
531        """Purge the page cache manager"""
532        msg = 'portal_status_message=Page+cache+not+purged:+CacheFu+disabled'
533        if self.getEnabled():
534            pc = getToolByName(self, config.PAGE_CACHE_MANAGER_ID)
535            pc.manage_purge()
536            msg = 'portal_status_message=Page+cache+purged'
537        if REQUEST is not None:
538            url = REQUEST.get('HTTP_REFERER', self.absolute_url()+'/edit')
539            if url.find('?') != -1:
540                url += '&' + msg
541            else:
542                url += '?' + msg
543            return REQUEST.RESPONSE.redirect(url)
544
545    # ##### Helper methods used for building ETags and for header setting ######
546
547    def canAnonymousView(self, object):
548        """Returns True if anonymous users can view an object"""
549        if 'Anonymous' in rolesForPermissionOn('View', object):
550            return True
551        # XXX i am not sure it is possible to assign local roles to the anonymous user
552        # XXX if it is, there may need to be some local role tomfoolery here
553        # XXX something like the following
554        # roles_with_view = {}
555        # for r in rolesForPermissionOn('View', obj):
556        #    roles_with_view[r] = 1
557        # try:
558        #    all_local_roles = portal.acl_users._getAllLocalRoles(obj)
559        # except AttributeError:
560        #    all_local_roles = _mergedLocalRoles(obj)
561        # if 'Anonymous user' in all_local_roles:
562        #    for r in all_local_roles['Anonymous user']:
563        #       if r in roles_with_view:
564        #          return True
565        return False
566
567    security.declarePublic('isGzippable')
568    def isGzippable(self, css=0, js=0, REQUEST=None):
569        """Indicate whether gzipping is allowed for the current request.  Returns
570           a tuple.  The first argument indicates whether gzipping should be enabled,
571           the second indicates whether gzipping should be forced, and the third
572           whether the browser will accept gzipped content."""
573        # force: force http compression even if the browser doesn't send an accept
574        # debug: return compression state (0: no, 1: yes, 2: force)
575        # css: set this to 1 inside a css file (for later use)
576        # js: set this to 1 inside a js file (for later use)
577
578        if REQUEST is None:
579            REQUEST = self.REQUEST
580        use_gzip = self.getGzip()
581        if not self.getEnabled():
582            use_gzip = 'never'
583
584        force = 0
585        if use_gzip == 'never':
586            enable_compression = 0
587        elif use_gzip == 'always':
588            enable_compression = 1
589            force = 1
590        elif use_gzip == 'accept-encoding':
591            # compress everything except css and js
592            enable_compression = 1
593        elif use_gzip == 'accept-encoding+user-agent':
594            # gzip compatibility info courtesy of
595            # http://httpd.apache.org/docs/2.2/mod/mod_deflate.html
596            user_agent = REQUEST.get('HTTP_USER_AGENT', '')
597            if user_agent.startswith('Mozilla/4'):
598                # Netscape 4.x can't handle gzipped css and js
599                enable_compression = (css==0 and js==0)
600            # Netscape 4.0.6-4.0.8 has some gzip-related bugs
601            if user_agent[len('Mozilla/4.')] in ('6','7','8'):
602                enable_compression = 0
603            # Some versions of MSIE pretend to be Netscape 4.x but are OK with gzipping
604            if user_agent.find('MSIE'):
605                enable_compression = 1
606
607        return (enable_compression, force, REQUEST.get('HTTP_ACCEPT_ENCODING', '').find('gzip') != -1)
608
609    # ##### Main methods ######
610
611    security.declarePublic('getRuleAndHeaderSet')
612    def getRuleAndHeaderSet(self, request, object, view, member):
613        """Get the caching rule that applies here and the header set specified by the rule"""
614        if not self.getEnabled():
615            return (None, None)
616        rules = self.getRules().objectValues()
617        for rule in rules:
618            try:
619                header_set = rule.getHeaderSet(request, object, view, member)
620                if header_set is not None:
621                    return (rule, header_set)
622            except ConflictError:
623                raise
624            except:
625                log_exc()
626        return (None, None)
627
628    security.declarePublic('getUrlsToPurge')
629    def getUrlsToPurge(self, object):
630        """Get a list of URLs to be purged when the given object is added / modified / deleted"""
631
632        # if nothing to purge or cachefu disabled, return an empty list
633        if not self.hasPurgeableProxy() or not self.getEnabled():
634            return []
635       
636        relative_urls = sets.Set()
637        rules = self.getRules().objectValues()
638        for rule in rules:
639            try:
640                rule.getRelativeUrlsToPurge(object, relative_urls)
641            except ConflictError:
642                raise
643            except:
644                log_exc()
645
646        for adapter in zope.component.subscribers([aq_inner(object)], IPurgeUrls):
647            relative_urls.union_update(adapter.getRelativeUrls())
648
649        relative_urls = list(relative_urls)
650
651        absolute_urls=[]
652        for adapter in zope.component.subscribers([aq_inner(object)], IPurgeUrls):
653            absolute_urls.extend(adapter.getAbsoluteUrls(relative_urls))
654
655        if relative_urls:
656            proxy_purge_config = self.getProxyPurgeConfig()
657            if proxy_purge_config == 'vhm-rewrite':
658                # unless defined otherwise, we assume urls passed to the cache proxy
659                # are of the standard form expected by the VirtualHostMonster:
660                # [squid_url]/VirtualHostBase/[protocol]/[host]:[port]/[path to portal root]/VirtualHostRoot/[path]
661                url_tool = getToolByName(self, 'portal_url')
662                portal_path = '/'.join(url_tool.getPortalObject().getPhysicalPath())
663                domains = self.getDomains()
664                prefixes = []
665                for d in domains:
666                    p = urlparse.urlparse(d)
667                    protocol = p[0]
668                    host = p[1]
669                    split_host = host.split(':')
670                    host = split_host[0]
671                    port = split_host[1]
672                    prefixes.append('VirtualHostBase/%s/%s:%s%s/VirtualHostRoot/' \
673                                     % (protocol, host, port, portal_path))
674                relative_urls = [prefix+url for prefix in prefixes for url in relative_urls]
675            elif proxy_purge_config == 'custom-rewrite':
676                domains = [urlparse.urlparse(d) for d in self.getDomains()]
677                relative_urls = self.rewritePurgeUrls(relative_urls, domains)
678
679
680        return relative_urls + absolute_urls
681
682    # A few helper methods
683   
684    def getMember(self):
685        """Utility method for getting a member for use in expression contexts.  Returns
686           the Member object for the currently authenticated member or None if the
687           user is not authenticated."""
688        pm = getToolByName(self, 'portal_membership', None)
689        # stick to the CachingPolicyManager expression convention
690        if not pm or pm.isAnonymousUser():
691            return None 
692        else:
693            return pm.getAuthenticatedMember()
694       
695    # a few methods for generating non-hideous default ids
696    security.declareProtected(permissions.ManagePortal, 'generateUniqueId')
697    def generateUniqueId(self, type_name):
698        context = self.REQUEST.PARENTS[0]
699        question_ids = context.objectIds()
700        n = len(question_ids)+1
701        while str(n) in question_ids:
702            n = n + 1
703        return str(n)
704
705    def _isIDAutoGenerated(self, id):
706        try:
707            int(id)
708            return True
709        except:
710            return False
711       
712registerType(CacheTool, config.PROJECT_NAME)
Note: See TracBrowser for help on using the repository browser.