Example #1
0
class WeakCaching(BaseCaching):
    """Weak caching operation. A subclass of the generic BaseCaching
    operation to help make the UI approachable by mortals
    """

    # Type metadata
    classProvides(ICachingOperationType)

    title = _(u"Weak caching")
    description = _(
        u"Cache in browser but expire immediately and enable 304 "
        u"responses on subsequent requests. 304's require configuration "
        u"of the 'Last-modified' and/or 'ETags' settings. If Last-Modified "
        u"header is insufficient to ensure freshness, turn on ETag "
        u"checking by listing each ETag components that should be used to "
        u"to construct the ETag header. "
        u"To also cache public responses in Zope memory, set 'RAM cache' to True. "
    )
    prefix = 'plone.app.caching.weakCaching'
    sort = 3

    # Configurable options
    options = ('etags', 'lastModified', 'ramCache', 'vary', 'anonOnly')

    # Default option values
    maxage = 0
    smaxage = etags = vary = None
    lastModified = ramCache = anonOnly = False
Example #2
0
class StrongCaching(BaseCaching):
    """Strong caching operation. A subclass of the generic BaseCaching
    operation to help make the UI approachable by mortals
    """

    # Type metadata
    classProvides(ICachingOperationType)

    title = _(u"Strong caching")
    description = _(
        u"Cache in browser and proxy (default: 24 hrs). "
        u"Caution: Only use for stable resources "
        u"that never change without changing their URL, or resources "
        u"for which temporary staleness is not critical.")
    prefix = 'plone.app.caching.strongCaching'
    sort = 1

    # Configurable options
    options = ('maxage', 'smaxage', 'etags', 'lastModified', 'ramCache',
               'vary', 'anonOnly')

    # Default option values
    maxage = 86400
    smaxage = etags = vary = None
    lastModified = ramCache = anonOnly = False
Example #3
0
class ModerateCaching(BaseCaching):
    """Moderate caching operation. A subclass of the generic BaseCaching
    operation to help make the UI approachable by mortals
    """

    # Type metadata
    classProvides(ICachingOperationType)

    title = _(u"Moderate caching")
    description = _(
        u"Cache in browser but expire immediately (same as 'weak caching'), "
        u"and cache in proxy (default: 24 hrs). "
        u"Use a purgable caching reverse proxy for best results. "
        u"Caution: If proxy cannot be purged, or cannot be configured "
        u"to remove the 's-maxage' token from the response, then stale "
        u"responses might be seen until the cached entry expires. ")
    prefix = 'plone.app.caching.moderateCaching'
    sort = 2

    # Configurable options
    options = ('smaxage', 'etags', 'lastModified', 'ramCache', 'vary',
               'anonOnly')

    # Default option values
    maxage = 0
    smaxage = 86400
    etags = vary = None
    lastModified = ramCache = anonOnly = False
Example #4
0
    def processImport(self):
        profile = self.request.form.get('profile', None)
        snapshot = self.request.form.get('snapshot', True)

        if not profile:
            self.errors['profile'] = _(u'You must select a profile to import.')

        if self.errors:
            IStatusMessage(self.request).addStatusMessage(
                _(u'There were errors.'), 'error')
            return

        portal_setup = getToolByName(self.context, 'portal_setup')

        # Create a snapshot
        if snapshot:
            snapshotId = 'plone.app.caching.beforeimport.{0}'.format(
                datetime.datetime.now().isoformat().replace(':', '.'))
            portal_setup.createSnapshot(snapshotId)

        # Import the new profile
        portal_setup.runAllImportStepsFromProfile(
            'profile-{0}'.format(profile)
        )

        IStatusMessage(self.request).addStatusMessage(
            _(u'Import complete.'), 'info')
Example #5
0
class NoCaching(object):
    """A caching operation that tries to keep the response
    out of all caches.
    """
    implements(ICachingOperation)
    adapts(Interface, IHTTPRequest)

    # Type metadata
    classProvides(ICachingOperationType)

    title = _(u"No caching")
    description = _(u"Use this operation to keep the response "
                    u"out of all caches.")
    prefix = 'plone.app.caching.noCaching'
    sort = 4
    options = ()

    def __init__(self, published, request):
        self.published = published
        self.request = request

    def interceptResponse(self, rulename, response):
        return None

    def modifyResponse(self, rulename, response):
        doNotCache(self.published, self.request, response)
Example #6
0
    def processImport(self):
        profile = self.request.form.get('profile', None)
        snapshot = self.request.form.get('snapshot', True)

        if not profile:
            self.errors['profile'] = _(u"You must select a profile to import.")

        if self.errors:
            IStatusMessage(self.request).addStatusMessage(
                _(u"There were errors."), "error")
            return

        portal_setup = getToolByName(self.context, 'portal_setup')

        # Create a snapshot
        if snapshot:
            snapshotId = "plone.app.caching.beforeimport.%s" % (
                datetime.datetime.now().isoformat().replace(':', '.'))
            portal_setup.createSnapshot(snapshotId)

        # Import the new profile
        portal_setup.runAllImportStepsFromProfile("profile-%s" % profile)

        IStatusMessage(self.request).addStatusMessage(_(u"Import complete."),
                                                      "info")
Example #7
0
 def title(self):
     if self.rulesetName:
         return _(u'Edit ${operation} options for Ruleset: ${ruleset}',
                  mapping={'operation': self.operation.title,
                           'ruleset': self.ruleset.title})
     else:
         return _(u'Edit ${operation} options',
                  mapping={'operation': self.operation.title})
Example #8
0
 def title(self):
     if self.rulesetName:
         return _(u'Edit ${operation} options for Ruleset: ${ruleset}',
                  mapping={
                      'operation': self.operation.title,
                      'ruleset': self.ruleset.title
                  })
     else:
         return _(u'Edit ${operation} options',
                  mapping={'operation': self.operation.title})
Example #9
0
    def processPurge(self):

        if self.ramCache is None:
            IStatusMessage(self.request).addStatusMessage(_(u"RAM cache not installed."), "error")

        if self.errors:
            IStatusMessage(self.request).addStatusMessage(_(u"There were errors."), "error")
            return

        self.ramCache.invalidateAll()
        IStatusMessage(self.request).addStatusMessage(_(u"Cache purged."), "info")
Example #10
0
    def processPurge(self):

        if self.ramCache is None:
            IStatusMessage(self.request).addStatusMessage(_(u"RAM cache not installed."), "error")

        if self.errors:
            IStatusMessage(self.request).addStatusMessage(_(u"There were errors."), "error")
            return

        self.ramCache.invalidateAll()
        IStatusMessage(self.request).addStatusMessage(_(u"Cache purged."), "info")
Example #11
0
    def processPurge(self):

        if self.ramCache is None:
            IStatusMessage(self.request).addStatusMessage(
                _(u'RAM cache not installed.'), 'error')

        if self.errors:
            IStatusMessage(self.request).addStatusMessage(
                _(u'There were errors.'), 'error')
            return

        self.ramCache.invalidateAll()
        IStatusMessage(self.request).addStatusMessage(
            _(u'Cache purged.'), 'info')
Example #12
0
    def processPurge(self):

        if self.ramCache is None:
            IStatusMessage(self.request).addStatusMessage(
                _(u'RAM cache not installed.'), 'error')

        if self.errors:
            IStatusMessage(self.request).addStatusMessage(
                _(u'There were errors.'), 'error')
            return

        self.ramCache.invalidateAll()
        IStatusMessage(self.request).addStatusMessage(_(u'Cache purged.'),
                                                      'info')
Example #13
0
 def cancel(self, action):
     IStatusMessage(self.request).addStatusMessage(_(u"Edit cancelled."),
                                                   type="info")
     self.request.response.redirect(
         "%s/@@caching-controlpanel#detailed-settings" %
         self.context.absolute_url())
     return ''
Example #14
0
 def cancel(self, action):
     IStatusMessage(self.request).addStatusMessage(_(u'Edit cancelled.'),
                                                   type='info')
     self.request.response.redirect(
         '{0}/@@caching-controlpanel#detailed-settings'.format(
             self.context.absolute_url(), ), )
     return ''
Example #15
0
 def save(self, action):
     data, errors = self.extractData()
     if errors:
         self.status = self.formErrorsMessage
         return
     self.applyChanges(data)
     IStatusMessage(self.request).addStatusMessage(_(u"Changes saved."), "info")
     self.request.response.redirect("%s/@@caching-controlpanel#detailed-settings" % self.context.absolute_url())
Example #16
0
 def cancel(self, action):
     IStatusMessage(self.request).addStatusMessage(
         _(u'Edit cancelled.'), type='info')
     self.request.response.redirect(
         '{0}/@@caching-controlpanel#detailed-settings'.format(
             self.context.absolute_url(),
         ),
     )
     return ''
Example #17
0
 def clear(self, action):
     for key in self.getContent().keys():
         assert key.startswith("%s.%s." % (self.operation.prefix, self.rulesetName,))
         
         if key in self.registry.records:
             del self.registry.records[key]
     
     IStatusMessage(self.request).addStatusMessage(_(u"Ruleset-specific settings removed."), type="info")
     self.request.response.redirect("%s/@@caching-controlpanel#detailed-settings" % self.context.absolute_url())
     return ''
Example #18
0
 def save(self, action):
     data, errors = self.extractData()
     if errors:
         self.status = self.formErrorsMessage
         return
     self.applyChanges(data)
     IStatusMessage(self.request).addStatusMessage(_(u"Changes saved."),
                                                   "info")
     self.request.response.redirect(
         "%s/@@caching-controlpanel#detailed-settings" %
         self.context.absolute_url())
Example #19
0
 def save(self, action):
     data, errors = self.extractData()
     if errors:
         self.status = self.formErrorsMessage
         return
     self.applyChanges(data)
     IStatusMessage(self.request).addStatusMessage(_(u'Changes saved.'),
                                                   'info')
     self.request.response.redirect(
         '{0}/@@caching-controlpanel#detailed-settings'.format(
             self.context.absolute_url(), ), )
     return ''
Example #20
0
class NoCaching(object):
    """A caching operation that tries to keep the response
    out of all caches.
    """

    title = _(u'No caching')
    description = _(u'Use this operation to keep the response '
                    u'out of all caches.')
    prefix = 'plone.app.caching.noCaching'
    sort = 4
    options = ()

    def __init__(self, published, request):
        self.published = published
        self.request = request

    def interceptResponse(self, rulename, response):
        return None

    def modifyResponse(self, rulename, response):
        doNotCache(self.published, self.request, response)
Example #21
0
 def save(self, action):
     data, errors = self.extractData()
     if errors:
         self.status = self.formErrorsMessage
         return
     self.applyChanges(data)
     IStatusMessage(self.request).addStatusMessage(
         _(u'Changes saved.'), 'info')
     self.request.response.redirect(
         '{0}/@@caching-controlpanel#detailed-settings'.format(
             self.context.absolute_url(),
         ),
     )
     return ''
Example #22
0
class WeakCaching(BaseCaching):
    """Weak caching operation. A subclass of the generic BaseCaching
    operation to help make the UI approachable by mortals
    """

    title = _(u'Weak caching')
    description = _(
        u'Cache in browser but expire immediately and enable 304 '
        u'responses on subsequent requests. 304\'s require configuration '
        u'of the \'Last-modified\' and/or \'ETags\' settings. If '
        u'Last-Modified  header is insufficient to ensure freshness, turn on '
        u'ETag checking by listing each ETag components that should be used '
        u'to construct the ETag header. To also cache public responses in '
        u'Zope memory, set \'RAM cache\' to True.')
    prefix = 'plone.app.caching.weakCaching'
    sort = 3

    # Configurable options
    options = ('etags', 'lastModified', 'ramCache', 'vary', 'anonOnly')

    # Default option values
    maxage = 0
    smaxage = etags = vary = None
    lastModified = ramCache = anonOnly = False
Example #23
0
    def clear(self, action):
        for key in self.getContent().keys():
            assert key.startswith("%s.%s." % (
                self.operation.prefix,
                self.rulesetName,
            ))

            if key in self.registry.records:
                del self.registry.records[key]

        IStatusMessage(self.request).addStatusMessage(
            _(u"Ruleset-specific settings removed."), type="info")
        self.request.response.redirect(
            "%s/@@caching-controlpanel#detailed-settings" %
            self.context.absolute_url())
        return ''
Example #24
0
    def clear(self, action):
        for key in self.getContent().keys():
            key_suffix = '{0}.{1}.'.format(
                self.operation.prefix,
                self.rulesetName,
            )
            assert key.startswith(key_suffix)

            if key in self.registry.records:
                del self.registry.records[key]

        IStatusMessage(self.request).addStatusMessage(
            _(u'Ruleset-specific settings removed.'), type='info')
        self.request.response.redirect(
            '{0}/@@caching-controlpanel#detailed-settings'.format(
                self.context.absolute_url(), ), )
        return ''
Example #25
0
    def clear(self, action):
        for key in self.getContent().keys():
            key_suffix = '{0}.{1}.'.format(
                self.operation.prefix,
                self.rulesetName,
            )
            assert key.startswith(key_suffix)

            if key in self.registry.records:
                del self.registry.records[key]

        IStatusMessage(self.request).addStatusMessage(
            _(u'Ruleset-specific settings removed.'), type='info')
        self.request.response.redirect(
            '{0}/@@caching-controlpanel#detailed-settings'.format(
                self.context.absolute_url(),
            ),
        )
        return ''
Example #26
0
    def processSave(self):

        form = self.request.form

        # Form data
        enabled            = form.get('enabled', False)
        enableCompression  = form.get('enableCompression', False)
        contentTypesMap    = form.get('contenttypes', {})
        templatesMap       = form.get('templates', {})
        operations         = form.get('operations', {})

        purgingEnabled     = form.get('purgingEnabled', False)
        cachingProxies     = tuple(form.get('cachingProxies', ()))
        purgedContentTypes = tuple(form.get('purgedContentTypes', ()))
        virtualHosting     = form.get('virtualHosting', False)
        domains            = tuple(form.get('domains', ()))

        ramCacheMaxEntries      = form.get('ramCacheMaxEntries', None)
        ramCacheMaxAge          = form.get('ramCacheMaxAge', None)
        ramCacheCleanupInterval = form.get('ramCacheCleanupInterval', None)

        # Settings

        operationMapping          = {}
        contentTypeRulesetMapping = {}
        templateRulesetMapping    = {}

        # Process mappings and validate

        for ruleset, operation in operations.items():

            if not ruleset or not operation:
                continue

            if isinstance(ruleset, unicode): # should be ASCII
                ruleset = ruleset.encode('utf-8')

            if isinstance(operation, unicode): # should be ASCII
                operation = operation.encode('utf-8')

            ruleset = ruleset.replace('-', '.')
            operationMapping[ruleset] = operation

        for ruleset, contentTypes in contentTypesMap.items():

            if not ruleset:
                continue

            if isinstance(ruleset, unicode): # should be ASCII
                ruleset = ruleset.encode('utf-8')

            ruleset = ruleset.replace('-', '.')

            for contentType in contentTypes:

                if not contentType:
                    continue

                if isinstance(contentType, unicode): # should be ASCII
                    contentType = contentType.encode('utf-8')

                if contentType in contentTypeRulesetMapping:
                    self.errors.setdefault('contenttypes', {})[ruleset] = \
                        _(u"Content type ${contentType} is already mapped to the rule ${ruleset}.",
                            mapping={'contentType': self.contentTypesLookup.get(contentType, {}).get('title', contentType),
                                     'ruleset': contentTypeRulesetMapping[contentType]})
                else:
                    contentTypeRulesetMapping[contentType] = ruleset

        for ruleset, templates in templatesMap.items():

            if not ruleset:
                continue

            if isinstance(ruleset, unicode): # should be ASCII
                ruleset = ruleset.encode('utf-8')

            ruleset = ruleset.replace('-', '.')

            for template in templates:

                template = template.strip()

                if not template:
                    continue

                if isinstance(template, unicode): # should be ASCII
                    template = template.encode('utf-8')

                if template in templateRulesetMapping:
                    self.errors.setdefault('templates', {})[ruleset] = \
                        _(u"Template ${template} is already mapped to the rule ${ruleset}.",
                            mapping={'template': template,
                                      'ruleset': templateRulesetMapping[template]})
                else:
                    templateRulesetMapping[template] = ruleset

        # Validate purging settings

        for cachingProxy in cachingProxies:
            if not _isuri(cachingProxy):
                self.errors['cachingProxies'] = _(u"Invalid URL: ${url}", mapping={'url': cachingProxy})

        for domain in domains:
            if not _isuri(domain):
                self.errors['domain'] = _(u"Invalid URL: ${url}", mapping={'url': domain})

        # RAM cache settings

        try:
            ramCacheMaxEntries = int(ramCacheMaxEntries)
        except (ValueError, TypeError,):
            self.errors['ramCacheMaxEntries'] = _(u"An integer is required.")
        else:
            if ramCacheMaxEntries < 0:
                self.errors['ramCacheMaxEntries'] = _(u"A positive number is required.")

        try:
            ramCacheMaxAge = int(ramCacheMaxAge)
        except (ValueError, TypeError,):
            self.errors['ramCacheMaxAge'] = _(u"An integer is required.")
        else:
            if ramCacheMaxAge < 0:
                self.errors['ramCacheMaxAge'] = _(u"A positive number is required.")

        try:
            ramCacheCleanupInterval = int(ramCacheCleanupInterval)
        except (ValueError, TypeError,):
            self.errors['ramCacheCleanupInterval'] = _(u"An integer is required.")
        else:
            if ramCacheMaxAge < 0:
                self.errors['ramCacheCleanupInterval'] = _(u"A positive number is required.")

        # Check for errors
        if self.errors:
            IStatusMessage(self.request).addStatusMessage(_(u"There were errors."), "error")
            return

        # Save settings
        self.settings.enabled = enabled
        self.settings.operationMapping = operationMapping

        self.ploneSettings.enableCompression = enableCompression
        self.ploneSettings.templateRulesetMapping = templateRulesetMapping
        self.ploneSettings.contentTypeRulesetMapping = contentTypeRulesetMapping
        self.ploneSettings.purgedContentTypes = purgedContentTypes

        self.purgingSettings.enabled = purgingEnabled
        self.purgingSettings.cachingProxies = cachingProxies
        self.purgingSettings.virtualHosting = virtualHosting
        self.purgingSettings.domains = domains

        self.ramCache.update(ramCacheMaxEntries, ramCacheMaxAge, ramCacheCleanupInterval)

        IStatusMessage(self.request).addStatusMessage(_(u"Changes saved."), "info")
Example #27
0
    def processSave(self):

        form = self.request.form

        # Form data
        enabled = form.get('enabled', False)
        contentTypesMap = form.get('contenttypes', {})
        templatesMap = form.get('templates', {})
        operations = form.get('operations', {})

        purgingEnabled = form.get('purgingEnabled', False)
        cachingProxies = tuple(form.get('cachingProxies', ()))
        purgedContentTypes = tuple(form.get('purgedContentTypes', ()))
        virtualHosting = form.get('virtualHosting', False)
        domains = tuple(form.get('domains', ()))

        ramCacheMaxEntries = form.get('ramCacheMaxEntries', None)
        ramCacheMaxAge = form.get('ramCacheMaxAge', None)
        ramCacheCleanupInterval = form.get('ramCacheCleanupInterval', None)

        # Settings

        operationMapping = {}
        contentTypeRulesetMapping = {}
        templateRulesetMapping = {}

        # Process mappings and validate

        for ruleset, operation in operations.items():

            if not ruleset or not operation:
                continue

            if isinstance(ruleset, unicode):  # should be ASCII
                ruleset = ruleset.encode('utf-8')

            if isinstance(operation, unicode):  # should be ASCII
                operation = operation.encode('utf-8')

            ruleset = ruleset.replace('-', '.')
            operationMapping[ruleset] = operation

        for ruleset, contentTypes in contentTypesMap.items():

            if not ruleset:
                continue

            if isinstance(ruleset, unicode):  # should be ASCII
                ruleset = ruleset.encode('utf-8')

            ruleset = ruleset.replace('-', '.')

            for contentType in contentTypes:

                if not contentType:
                    continue

                if isinstance(contentType, unicode):  # should be ASCII
                    contentType = contentType.encode('utf-8')

                if contentType in contentTypeRulesetMapping:
                    self.errors.setdefault('contenttypes', {})[ruleset] = \
                        _(u"Content type ${contentType} is already mapped to the rule ${ruleset}.",
                            mapping={
                                'contentType': self.contentTypesLookup.get(contentType, {}).get('title', contentType),  # noqa
                                'ruleset': contentTypeRulesetMapping[contentType]})
                else:
                    contentTypeRulesetMapping[contentType] = ruleset

        for ruleset, templates in templatesMap.items():

            if not ruleset:
                continue

            if isinstance(ruleset, unicode):  # should be ASCII
                ruleset = ruleset.encode('utf-8')

            ruleset = ruleset.replace('-', '.')

            for template in templates:

                template = template.strip()

                if not template:
                    continue

                if isinstance(template, unicode):  # should be ASCII
                    template = template.encode('utf-8')

                if template in templateRulesetMapping:
                    self.errors.setdefault('templates', {})[ruleset] = \
                        _(u"Template ${template} is already mapped to the rule ${ruleset}.",
                            mapping={
                                'template': template,
                                'ruleset': templateRulesetMapping[template]})
                else:
                    templateRulesetMapping[template] = ruleset

        # Validate purging settings

        for cachingProxy in cachingProxies:
            if not _isuri(cachingProxy):
                self.errors['cachingProxies'] = _(
                    u"Invalid URL: ${url}", mapping={'url':
                                                     cachingProxy})  # noqa

        for domain in domains:
            if not _isuri(domain):
                self.errors['domain'] = _(u"Invalid URL: ${url}",
                                          mapping={'url': domain})

        # RAM cache settings

        try:
            ramCacheMaxEntries = int(ramCacheMaxEntries)
        except (
                ValueError,
                TypeError,
        ):
            self.errors['ramCacheMaxEntries'] = _(u"An integer is required.")
        else:
            if ramCacheMaxEntries < 0:
                self.errors['ramCacheMaxEntries'] = _(
                    u"A positive number is required.")

        try:
            ramCacheMaxAge = int(ramCacheMaxAge)
        except (
                ValueError,
                TypeError,
        ):
            self.errors['ramCacheMaxAge'] = _(u"An integer is required.")
        else:
            if ramCacheMaxAge < 0:
                self.errors['ramCacheMaxAge'] = _(
                    u"A positive number is required.")

        try:
            ramCacheCleanupInterval = int(ramCacheCleanupInterval)
        except (
                ValueError,
                TypeError,
        ):
            self.errors['ramCacheCleanupInterval'] = _(
                u"An integer is required.")
        else:
            if ramCacheMaxAge < 0:
                self.errors['ramCacheCleanupInterval'] = _(
                    u"A positive number is required.")

        # Check for errors
        if self.errors:
            IStatusMessage(self.request).addStatusMessage(
                _(u"There were errors."), "error")
            return

        # Save settings
        self.settings.enabled = enabled
        self.settings.operationMapping = operationMapping

        self.ploneSettings.templateRulesetMapping = templateRulesetMapping
        self.ploneSettings.contentTypeRulesetMapping = contentTypeRulesetMapping
        self.ploneSettings.purgedContentTypes = purgedContentTypes

        self.purgingSettings.enabled = purgingEnabled
        self.purgingSettings.cachingProxies = cachingProxies
        self.purgingSettings.virtualHosting = virtualHosting
        self.purgingSettings.domains = domains

        self.ramCache.update(ramCacheMaxEntries, ramCacheMaxAge,
                             ramCacheCleanupInterval)

        IStatusMessage(self.request).addStatusMessage(_(u"Changes saved."),
                                                      "info")
Example #28
0
    def processSave(self):

        form = self.request.form

        # Form data
        enabled = form.get('enabled', False)
        contentTypesMap = form.get('contenttypes', {})
        templatesMap = form.get('templates', {})
        operations = form.get('operations', {})

        purgingEnabled = form.get('purgingEnabled', False)
        cachingProxies = tuple(form.get('cachingProxies', ()))
        purgedContentTypes = tuple(form.get('purgedContentTypes', ()))
        virtualHosting = form.get('virtualHosting', False)
        domains = tuple(form.get('domains', ()))

        ramCacheMaxEntries = form.get('ramCacheMaxEntries', None)
        ramCacheMaxAge = form.get('ramCacheMaxAge', None)
        ramCacheCleanupInterval = form.get('ramCacheCleanupInterval', None)

        # Settings

        operationMapping = {}
        contentTypeRulesetMapping = {}
        templateRulesetMapping = {}

        # Process mappings and validate

        for ruleset, operation in operations.items():
            if not ruleset or not operation:
                continue

            ruleset = ruleset.replace('-', '.')
            operationMapping[ruleset] = operation

        for ruleset, contentTypes in contentTypesMap.items():
            if not ruleset:
                continue

            ruleset = ruleset.replace('-', '.')
            for contentType in contentTypes:
                if not contentType:
                    continue

                if contentType in contentTypeRulesetMapping:
                    self.errors.setdefault(
                        'contenttypes', {},
                    )[ruleset] = _(
                        u'Content type ${contentType} is already mapped to '
                        u'the rule ${ruleset}.',
                        mapping={
                            'contentType': self.contentTypesLookup.get(
                                contentType, {},
                            ).get(
                                'title',
                                contentType,
                            ),
                            'ruleset': contentTypeRulesetMapping[contentType],
                        },
                    )
                else:
                    contentTypeRulesetMapping[contentType] = ruleset

        for ruleset, templates in templatesMap.items():
            if not ruleset:
                continue

            ruleset = ruleset.replace('-', '.')
            for template in templates:
                template = template.strip()
                if not template:
                    continue

                if template in templateRulesetMapping:
                    self.errors.setdefault(
                        'templates', {},
                    )[ruleset] = _(
                        u'Template ${template} is already mapped to the rule '
                        u'${ruleset}.',
                        mapping={
                            'template': template,
                            'ruleset': templateRulesetMapping[template],
                        },
                    )
                else:
                    templateRulesetMapping[template] = ruleset

        # Validate purging settings
        for cachingProxy in cachingProxies:
            if not _isuri(cachingProxy):
                self.errors['cachingProxies'] = _(u'Invalid URL: ${url}', mapping={'url': cachingProxy})  # noqa

        for domain in domains:
            if not _isuri(domain):
                self.errors['domain'] = _(
                    u'Invalid URL: ${url}',
                    mapping={'url': domain},
                )

        # RAM cache settings
        try:
            ramCacheMaxEntries = int(ramCacheMaxEntries)
        except (ValueError, TypeError,):
            self.errors['ramCacheMaxEntries'] = _(u'An integer is required.')
        else:
            if ramCacheMaxEntries < 0:
                self.errors['ramCacheMaxEntries'] = _(
                    u'A positive number is required.',
                )
        try:
            ramCacheMaxAge = int(ramCacheMaxAge)
        except (ValueError, TypeError,):
            self.errors['ramCacheMaxAge'] = _(u'An integer is required.')
        else:
            if ramCacheMaxAge < 0:
                self.errors['ramCacheMaxAge'] = _(
                    u'A positive number is required.',
                )

        try:
            ramCacheCleanupInterval = int(ramCacheCleanupInterval)
        except (ValueError, TypeError,):
            self.errors['ramCacheCleanupInterval'] = _(
                u'An integer is required.',
            )
        else:
            if ramCacheMaxAge < 0:
                self.errors['ramCacheCleanupInterval'] = _(
                    u'A positive number is required.',
                )

        # Check for errors
        if self.errors:
            IStatusMessage(self.request).addStatusMessage(
                _(u'There were errors.'), 'error')
            return

        # Save settings
        self.settings.enabled = enabled
        self.settings.operationMapping = operationMapping

        self.ploneSettings.templateRulesetMapping = templateRulesetMapping
        self.ploneSettings.contentTypeRulesetMapping = contentTypeRulesetMapping  # noqa
        self.ploneSettings.purgedContentTypes = purgedContentTypes

        self.purgingSettings.enabled = purgingEnabled
        self.purgingSettings.cachingProxies = cachingProxies
        self.purgingSettings.virtualHosting = virtualHosting
        self.purgingSettings.domains = domains

        self.ramCache.update(
            ramCacheMaxEntries,
            ramCacheMaxAge,
            ramCacheCleanupInterval,
        )

        if not enabled and purgingEnabled:
            IStatusMessage(self.request).addStatusMessage(
                _(u'Purging is still enabled while caching is disabled!'),
                'warning',
            )

        IStatusMessage(self.request).addStatusMessage(
            _(u'Changes saved.'),
            'info',
        )
Example #29
0
    def processPurge(self):
        urls = self.request.form.get('urls', [])
        sync = self.request.form.get('synchronous', True)

        if not urls:
            self.errors['urls'] = _(u"No URLs or paths entered.")

        if self.errors:
            IStatusMessage(self.request).addStatusMessage(
                _(u"There were errors."), "error")
            return

        purger = getUtility(IPurger)

        serverURL = self.request['SERVER_URL']

        def purge(url):
            if sync:
                status, xcache, xerror = purger.purgeSync(url)

                log = url
                if xcache:
                    log += " (X-Cache header: " + xcache + ")"
                if xerror:
                    log += " -- " + xerror
                self.purgeLog.append(log)
            else:
                purger.purgeAsync(url)
                self.purgeLog.append(url)

        portal_url = getToolByName(self.context, 'portal_url')
        portal = portal_url.getPortalObject()
        portalPath = portal.getPhysicalPath()

        proxies = self.purgingSettings.cachingProxies

        for inputURL in urls:
            if not inputURL.startswith(serverURL):  # not in the site
                if '://' in inputURL:  # Full URL?
                    purge(inputURL)
                else:  # Path?
                    for newURL in getURLsToPurge(inputURL, proxies):
                        purge(newURL)
                continue

            physicalPath = relativePath = None
            try:
                physicalPath = self.request.physicalPathFromURL(inputURL)
            except ValueError:
                purge(inputURL)
                continue

            if not physicalPath:
                purge(inputURL)
                continue

            relativePath = physicalPath[len(portalPath):]
            if not relativePath:
                purge(inputURL)
                continue

            obj = portal.unrestrictedTraverse(relativePath, None)
            if obj is None:
                purge(inputURL)
                continue

            for path in getPathsToPurge(obj, self.request):
                for newURL in getURLsToPurge(path, proxies):
                    purge(newURL)
Example #30
0
    def processPurge(self):

        urls = self.request.form.get('urls', [])
        sync = self.request.form.get('synchronous', True)

        if not urls:
            self.errors['urls'] = _(u"No URLs or paths entered.")

        if self.errors:
            IStatusMessage(self.request).addStatusMessage(_(u"There were errors."), "error")
            return

        purger = getUtility(IPurger)

        serverURL = self.request['SERVER_URL']

        def purge(url):
            if sync:
                status, xcache, xerror = purger.purgeSync(url)

                log = url
                if xcache:
                    log += " (X-Cache header: " + xcache + ")"
                if xerror:
                    log += " -- " + xerror
                self.purgeLog.append(log)
            else:
                purger.purgeAsync(url)
                self.purgeLog.append(url)

        portal_url = getToolByName(self.context, 'portal_url')
        portal = portal_url.getPortalObject()
        portalPath = '/'.join(portal.getPhysicalPath())

        proxies = self.purgingSettings.cachingProxies

        for inputURL in urls:
            if not inputURL.startswith(serverURL): # not in the site
                if '://' in inputURL: # Full URL?
                    purge(inputURL)
                else:                 # Path?
                    for newURL in getURLsToPurge(inputURL, proxies):
                        purge(newURL)
                continue

            physicalPath = relativePath = None
            try:
                physicalPath = self.request.physicalPathFromURL(inputURL)
            except ValueError:
                purge(inputURL)
                continue

            if not physicalPath:
                purge(inputURL)
                continue

            relativePath = physicalPath[len(portalPath):]
            if not relativePath:
                purge(inputURL)
                continue

            obj = portal.unrestrictedTraverse(relativePath, None)
            if obj is None:
                purge(inputURL)
                continue

            for path in getPathsToPurge(obj, self.request):
                for newURL in getURLsToPurge(path, proxies):
                    purge(newURL)
Example #31
0
    def processSave(self):

        form = self.request.form

        # Form data
        enabled = form.get('enabled', False)
        contentTypesMap = form.get('contenttypes', {})
        templatesMap = form.get('templates', {})
        operations = form.get('operations', {})

        purgingEnabled = form.get('purgingEnabled', False)
        cachingProxies = tuple(form.get('cachingProxies', ()))
        purgedContentTypes = tuple(form.get('purgedContentTypes', ()))
        virtualHosting = form.get('virtualHosting', False)
        domains = tuple(form.get('domains', ()))

        ramCacheMaxEntries = form.get('ramCacheMaxEntries', None)
        ramCacheMaxAge = form.get('ramCacheMaxAge', None)
        ramCacheCleanupInterval = form.get('ramCacheCleanupInterval', None)

        # Settings

        operationMapping = {}
        contentTypeRulesetMapping = {}
        templateRulesetMapping = {}

        # Process mappings and validate

        for ruleset, operation in operations.items():
            if not ruleset or not operation:
                continue

            ruleset = ruleset.replace('-', '.')
            operationMapping[ruleset] = operation

        for ruleset, contentTypes in contentTypesMap.items():
            if not ruleset:
                continue

            ruleset = ruleset.replace('-', '.')
            for contentType in contentTypes:
                if not contentType:
                    continue

                if contentType in contentTypeRulesetMapping:
                    self.errors.setdefault(
                        'contenttypes',
                        {},
                    )[ruleset] = _(
                        u'Content type ${contentType} is already mapped to '
                        u'the rule ${ruleset}.',
                        mapping={
                            'contentType':
                            self.contentTypesLookup.get(
                                contentType,
                                {},
                            ).get(
                                'title',
                                contentType,
                            ),
                            'ruleset':
                            contentTypeRulesetMapping[contentType],
                        },
                    )
                else:
                    contentTypeRulesetMapping[contentType] = ruleset

        for ruleset, templates in templatesMap.items():
            if not ruleset:
                continue

            ruleset = ruleset.replace('-', '.')
            for template in templates:
                template = template.strip()
                if not template:
                    continue

                if template in templateRulesetMapping:
                    self.errors.setdefault(
                        'templates',
                        {},
                    )[ruleset] = _(
                        u'Template ${template} is already mapped to the rule '
                        u'${ruleset}.',
                        mapping={
                            'template': template,
                            'ruleset': templateRulesetMapping[template],
                        },
                    )
                else:
                    templateRulesetMapping[template] = ruleset

        # Validate purging settings
        for cachingProxy in cachingProxies:
            if not _isuri(cachingProxy):
                self.errors['cachingProxies'] = _(
                    u'Invalid URL: ${url}', mapping={'url':
                                                     cachingProxy})  # noqa

        for domain in domains:
            if not _isuri(domain):
                self.errors['domain'] = _(
                    u'Invalid URL: ${url}',
                    mapping={'url': domain},
                )

        # RAM cache settings
        try:
            ramCacheMaxEntries = int(ramCacheMaxEntries)
        except (
                ValueError,
                TypeError,
        ):
            self.errors['ramCacheMaxEntries'] = _(u'An integer is required.')
        else:
            if ramCacheMaxEntries < 0:
                self.errors['ramCacheMaxEntries'] = _(
                    u'A positive number is required.', )
        try:
            ramCacheMaxAge = int(ramCacheMaxAge)
        except (
                ValueError,
                TypeError,
        ):
            self.errors['ramCacheMaxAge'] = _(u'An integer is required.')
        else:
            if ramCacheMaxAge < 0:
                self.errors['ramCacheMaxAge'] = _(
                    u'A positive number is required.', )

        try:
            ramCacheCleanupInterval = int(ramCacheCleanupInterval)
        except (
                ValueError,
                TypeError,
        ):
            self.errors['ramCacheCleanupInterval'] = _(
                u'An integer is required.', )
        else:
            if ramCacheMaxAge < 0:
                self.errors['ramCacheCleanupInterval'] = _(
                    u'A positive number is required.', )

        # Check for errors
        if self.errors:
            IStatusMessage(self.request).addStatusMessage(
                _(u'There were errors.'), 'error')
            return

        # Save settings
        self.settings.enabled = enabled
        self.settings.operationMapping = operationMapping

        self.ploneSettings.templateRulesetMapping = templateRulesetMapping
        self.ploneSettings.contentTypeRulesetMapping = contentTypeRulesetMapping  # noqa
        self.ploneSettings.purgedContentTypes = purgedContentTypes

        self.purgingSettings.enabled = purgingEnabled
        self.purgingSettings.cachingProxies = cachingProxies
        self.purgingSettings.virtualHosting = virtualHosting
        self.purgingSettings.domains = domains

        self.ramCache.update(
            ramCacheMaxEntries,
            ramCacheMaxAge,
            ramCacheCleanupInterval,
        )

        if not enabled and purgingEnabled:
            IStatusMessage(self.request).addStatusMessage(
                _(u'Purging is still enabled while caching is disabled!'),
                'warning',
            )

        IStatusMessage(self.request).addStatusMessage(
            _(u'Changes saved.'),
            'info',
        )
Example #32
0
class EditForm(form.Form):
    """General edit form for operations.

    This is not registered as a view directly. Instead, we parameterise it
    manually and return it from the ``publishTraverse()`` method in
    ``controlpanel.py``

    This form can be used in two slightly different ways: to edit "global"
    settings for an operation, or to edit "ruleset-specific" overrides. The
    latter mode is invoked when ``rulesetName`` and ``ruleset`` are set.

    The form fields are built from the records in registry corresponding to
    the operation's ``options`` list, taking the ``prefix`` into account.
    See ``plone.caching`` for a detailed explanation of how the naming scheme
    works.

    If a global record cannot be found, the option is ignored, i.e. no field
    is rendered for it.

    If we are editing ruleset-specific options and a particular ruleset-
    specific option does not exist, we take the global option field as a
    basis, and create a new record on the fly in ``applyChanges()``.

    The only other complication comes from the fact that we need to clone
    the persistent fields for two purposes:

    * Every record's field has the same name -- "value". We need to give it
      a different name in the form, so we clone the field and set a new name.
    * When we create a new ruleset-specific record, we also need a clone of
      the field.

    The ``cloneField()`` method takes care of this for us.

    Once the fields have been set up, the form operations on a dictionary
    context (as returned by ``getContent()``), where the keys are the record
    names.
    """

    template = ViewPageTemplateFile('edit.pt')

    # Keep the ZPublisher happy - would normally be done by the ZCML
    # registration
    __name__ = 'cache-operation-edit'

    def __init__(
        self,
        context,
        request,
        operationName,
        operation,
        rulesetName=None,
        ruleset=None,
    ):
        self.context = context
        self.request = request
        self.operationName = operationName
        self.operation = operation
        self.rulesetName = rulesetName
        self.ruleset = ruleset

    def update(self):

        self.registry = getUtility(IRegistry)

        # If we were using plone.z3cform, this would be done with
        # z2.switch_on()
        if not IFormLayer.providedBy(self.request):
            alsoProvides(self.request, IFormLayer)

        self.request.set('disable_border', True)

        # Create fields for records we actually have. Where applicable, fall
        # back on the  global record if a ruleset-specific record does not
        # yet exist - it will be created later in applyChanges()

        fields = []
        prefix = self.operation.prefix

        for option in self.operation.options:
            newField = None
            fieldName = '{0}.{1}'.format(prefix, option)

            if self.rulesetName:
                rulesetFieldName = '{0}.{1}.{2}'.format(
                    prefix,
                    self.rulesetName,
                    option,
                )

                if rulesetFieldName in self.registry.records:
                    newField = self.cloneField(
                        self.registry.records[rulesetFieldName].field)
                    newField.__name__ = rulesetFieldName
                elif fieldName in self.registry.records:
                    newField = self.cloneField(
                        self.registry.records[fieldName].field)
                    newField.__name__ = rulesetFieldName

            else:
                if fieldName in self.registry.records:
                    newField = self.cloneField(
                        self.registry.records[fieldName].field)
                    newField.__name__ = fieldName

            if newField is not None:
                fields.append(newField)

        self.fields = field.Fields(*fields)

        # Set up widgets and actions as normal

        super(EditForm, self).update()

        # Plonify the buttons

        self.actions['save'].addClass('context')
        self.actions['cancel'].addClass('standalone')
        self.actions['clear'].addClass('destructive')

        # Hide 'clear' action if we're not editing a ruleset

        if not self.rulesetName:
            del self.actions['clear']

    # Context

    @memoize
    def getContent(self):
        """Operate on a dictionary context that contains the values for
        all options for which we actually have records.
        """
        context = {}

        prefix = self.operation.prefix
        options = self.operation.options

        for option in options:
            recordName = '{0}.{1}'.format(
                prefix,
                option,
            )

            # If a ruleset-specific record does not exist, we can fall back on
            # a global record, since the per-ruleset records will be created
            # as necessary in applyChanges()

            if self.rulesetName:
                rulesetRecordName = '{0}.{1}.{2}'.format(
                    prefix,
                    self.rulesetName,
                    option,
                )

                if rulesetRecordName in self.registry.records:
                    context[rulesetRecordName] = self.registry[
                        rulesetRecordName]
                elif recordName in self.registry.records:
                    context[rulesetRecordName] = self.registry[recordName]

            else:
                if recordName in self.registry.records:
                    context[recordName] = self.registry[recordName]

        return context

    def applyChanges(self, data):
        """Save changes in the given data dictionary to the registry.
        """

        for key, value in data.items():

            # Lazily create per-ruleset records if necessary
            if key not in self.registry.records:

                # This should only ever happen if we have a not-yet-creted
                # ruleset-specific record
                assert self.rulesetName in key

                # Strip the ruleset name out, leaving the original key - this
                # must exist, otherwise getContent() would not have put it in
                # the data dictionary
                globalKey = self.operation.prefix + \
                    key[len(self.operation.prefix) +
                        len(self.rulesetName) + 1:]
                assert globalKey in self.registry.records

                # Create a new record with a FieldRef
                field = self.registry.records[globalKey].field
                self.registry.records[key] = Record(FieldRef(globalKey, field),
                                                    value)

            else:
                self.registry[key] = value

    def cloneField(self, field):

        # XXX: The field may sometimes not have data for reasons known only
        # to Jim.
        try:
            field._p_activate()
        except AttributeError:
            pass

        clone = field.__class__.__new__(field.__class__)
        clone.__dict__.update(field.__dict__)

        for name, attr in field.__dict__.items():
            if IField.providedBy(attr):
                clone.__dict__[name] = self.cloneField(attr)

        return clone

    # Display

    @property
    def title(self):
        if self.rulesetName:
            return _(u'Edit ${operation} options for Ruleset: ${ruleset}',
                     mapping={
                         'operation': self.operation.title,
                         'ruleset': self.ruleset.title
                     })
        else:
            return _(u'Edit ${operation} options',
                     mapping={'operation': self.operation.title})

    @property
    def description(self):
        return self.operation.description

    # Buttons/actions

    @button.buttonAndHandler(_(u'Save'), name='save')
    def save(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return
        self.applyChanges(data)
        IStatusMessage(self.request).addStatusMessage(_(u'Changes saved.'),
                                                      'info')
        self.request.response.redirect(
            '{0}/@@caching-controlpanel#detailed-settings'.format(
                self.context.absolute_url(), ), )
        return ''

    @button.buttonAndHandler(_(u'Cancel'), name='cancel')
    def cancel(self, action):
        IStatusMessage(self.request).addStatusMessage(_(u'Edit cancelled.'),
                                                      type='info')
        self.request.response.redirect(
            '{0}/@@caching-controlpanel#detailed-settings'.format(
                self.context.absolute_url(), ), )
        return ''

    @button.buttonAndHandler(_(u'Delete settings (use defaults)'),
                             name='clear')
    def clear(self, action):
        for key in self.getContent().keys():
            key_suffix = '{0}.{1}.'.format(
                self.operation.prefix,
                self.rulesetName,
            )
            assert key.startswith(key_suffix)

            if key in self.registry.records:
                del self.registry.records[key]

        IStatusMessage(self.request).addStatusMessage(
            _(u'Ruleset-specific settings removed.'), type='info')
        self.request.response.redirect(
            '{0}/@@caching-controlpanel#detailed-settings'.format(
                self.context.absolute_url(), ), )
        return ''
Example #33
0
class BaseCaching(object):
    """A generic caching operation class that can do pretty much all the usual
    caching operations based on options settings. For UI simplicity, it might
    be easier to subclass this in your custom operations to set a few default
    operations.

    Generic options (Default value for each is None):

    ``maxage`` is the maximum age of the cached item, in seconds..

    ``smaxage`` is the maximum age of the cached item in proxies, in seconds.

    ``etags'' is a list of etag components to use when constructing an etag.

    ``lastModified`` is a boolean indicating whether to set a Last-Modified header
    and turn on 304 responses.

    ``ramCache`` is a boolean indicating whether to turn on RAM caching for this
    item. Etags are only required if the URL is not specific enough to ensure
    uniqueness.

    ``vary`` is a string to add as a Vary header value in the response.
    """
    implements(ICachingOperation)
    adapts(Interface, IHTTPRequest)

    # Type metadata
    classProvides(ICachingOperationType)

    title = _(u"Generic caching")
    description = _(
        u"Through this operation, all standard caching functions "
        u"can be performed via various combinations of the optional "
        u"parameter settings. For most cases, it's probably easier "
        u"to use one of the other simpler operations (Strong caching, "
        u"Moderate caching, Weak caching, or No caching).")
    prefix = 'plone.app.caching.baseCaching'
    options = ('maxage', 'smaxage', 'etags', 'lastModified', 'ramCache',
               'vary', 'anonOnly')

    # Default option values
    maxage = smaxage = etags = vary = None
    lastModified = ramCache = anonOnly = False

    def __init__(self, published, request):
        self.published = published
        self.request = request

    def interceptResponse(self, rulename, response, class_=None):
        options = lookupOptions(class_ or self.__class__, rulename)

        etags = options.get('etags') or self.etags
        anonOnly = options.get('anonOnly', self.anonOnly)
        ramCache = options.get('ramCache', self.ramCache)
        lastModified = options.get('lastModified', self.lastModified)

        # Add the ``anonymousOrRandom`` etag if we are anonymous only
        if anonOnly:
            if etags is None:
                etags = ['anonymousOrRandom']
            elif 'anonymousOrRandom' not in etags:
                etags = tuple(etags) + ('anonymousOrRandom', )

        etag = getETagAnnotation(self.published, self.request, keys=etags)
        lastModified = getLastModifiedAnnotation(self.published,
                                                 self.request,
                                                 lastModified=lastModified)

        # Check for cache stop request variables
        if cacheStop(self.request, rulename):
            return None

        # Check if this should be a 304 response
        if not isModified(self.request, etag=etag, lastModified=lastModified):
            return notModified(self.published,
                               self.request,
                               response,
                               etag=etag,
                               lastModified=lastModified)

        # Check if this is in the ram cache
        if ramCache:
            context = getContext(self.published)
            portal_state = getMultiAdapter((context, self.request),
                                           name=u'plone_portal_state')

            if portal_state.anonymous():
                cached = fetchFromRAMCache(self.request,
                                           etag=etag,
                                           lastModified=lastModified)
                if cached is not None:
                    return cachedResponse(self.published, self.request,
                                          response, *cached)

        return None

    def modifyResponse(self, rulename, response, class_=None):
        options = lookupOptions(class_ or self.__class__, rulename)

        maxage = options.get('maxage', self.maxage)
        smaxage = options.get('smaxage', self.smaxage)
        etags = options.get('etags') or self.etags

        anonOnly = options.get('anonOnly', self.anonOnly)
        ramCache = options.get('ramCache', self.ramCache)
        vary = options.get('vary', self.vary)

        # Add the ``anonymousOrRandom`` etag if we are anonymous only
        if anonOnly:
            if etags is None:
                etags = ['anonymousOrRandom']
            elif 'anonymousOrRandom' not in etags:
                etags = tuple(etags) + ('anonymousOrRandom', )

        etag = getETagAnnotation(self.published, self.request, etags)
        lastModified = getLastModifiedAnnotation(self.published, self.request,
                                                 options['lastModified'])

        # Check for cache stop request variables
        if cacheStop(self.request, rulename):
            # only stop with etags if configured
            if etags:
                etag = "%s%d" % (time.time(), random.randint(0, 1000))
                return setCacheHeaders(self.published,
                                       self.request,
                                       response,
                                       etag=etag)
            # XXX: should there be an else here? Last modified works without extra headers.
            #      Are there other config options?

        # Do the maxage/smaxage settings allow for proxy caching?
        proxyCache = smaxage or (maxage and smaxage is None)

        # Check if the content can be cached in shared caches
        public = True
        if ramCache or proxyCache:
            if etags is not None:
                if 'userid' in etags or 'anonymousOrRandom' in etags or 'roles' in etags:
                    context = getContext(self.published)
                    portal_state = getMultiAdapter((context, self.request),
                                                   name=u'plone_portal_state')
                    public = portal_state.anonymous()
            public = public and visibleToRole(self.published, role='Anonymous')

        if proxyCache and not public:
            # This is private so keep it out of both shared and browser caches
            maxage = smaxage = 0

        setCacheHeaders(self.published,
                        self.request,
                        response,
                        maxage=maxage,
                        smaxage=smaxage,
                        etag=etag,
                        lastModified=lastModified,
                        vary=vary)

        if ramCache and public:
            cacheInRAM(self.published,
                       self.request,
                       response,
                       etag=etag,
                       lastModified=lastModified)
Example #34
0
    def processPurge(self):
        urls = self.request.form.get('urls', [])
        sync = self.request.form.get('synchronous', True)

        if not urls:
            self.errors['urls'] = _(u'No URLs or paths entered.')

        if self.errors:
            IStatusMessage(self.request).addStatusMessage(
                _(u'There were errors.'), 'error')
            return

        if six.PY3:
            urls = [
                x.decode('utf8') if isinstance(x, six.binary_type) else x
                for x in urls
            ]

        purger = getUtility(IPurger)
        serverURL = self.request['SERVER_URL']

        def purge(url):
            if sync:
                status, xcache, xerror = purger.purgeSync(url)

                log = url
                if xcache:
                    log += ' (X-Cache header: ' + xcache + ')'
                if xerror:
                    log += ' -- ' + xerror
                if not str(status).startswith('2'):
                    log += ' -- WARNING status ' + str(status)
                self.purgeLog.append(log)
            else:
                purger.purgeAsync(url)
                self.purgeLog.append(url)

        portal_url = getToolByName(self.context, 'portal_url')
        portal = portal_url.getPortalObject()
        portalPath = portal.getPhysicalPath()

        proxies = self.purgingSettings.cachingProxies

        for inputURL in urls:
            if not inputURL.startswith(serverURL):  # not in the site
                if '://' in inputURL:  # Full URL?
                    purge(inputURL)
                else:  # Path?
                    for newURL in getURLsToPurge(inputURL, proxies):
                        purge(newURL)
                continue

            physicalPath = relativePath = None
            try:
                physicalPath = self.request.physicalPathFromURL(inputURL)
            except ValueError:
                purge(inputURL)
                continue

            if not physicalPath:
                purge(inputURL)
                continue

            relativePath = physicalPath[len(portalPath):]
            if not relativePath:
                purge(inputURL)
                continue

            obj = portal.unrestrictedTraverse(relativePath, None)
            if obj is None:
                purge(inputURL)
                continue

            for path in getPathsToPurge(obj, self.request):
                for newURL in getURLsToPurge(path, proxies):
                    purge(newURL)
Example #35
0
 def cancel(self, action):
     IStatusMessage(self.request).addStatusMessage(_(u"Edit cancelled."), type="info")
     self.request.response.redirect("%s/@@caching-controlpanel#detailed-settings" % self.context.absolute_url())
     return ''