source: products/vendor/Products.CacheSetup/current/Products/CacheSetup/content/content_cache_rule.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: 9.8 KB
Line 
1"""
2CacheSetup
3~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5$Id: $
6"""
7
8__authors__ = 'Geoff Davis <geoff@geoffdavis.net>'
9__docformat__ = 'restructuredtext'
10
11from AccessControl import ClassSecurityInfo
12
13from Products.Archetypes.interfaces import IBaseObject
14from Products.Archetypes.atapi import BaseContent
15from Products.Archetypes.atapi import DisplayList
16from Products.Archetypes.atapi import listTypes
17from Products.Archetypes.atapi import registerType
18from Products.Archetypes.atapi import Schema
19
20from Products.Archetypes.atapi import BooleanField
21from Products.Archetypes.atapi import LinesField
22from Products.Archetypes.atapi import StringField
23from Products.Archetypes.atapi import TextField
24
25from Products.Archetypes.atapi import BooleanWidget
26from Products.Archetypes.atapi import LinesWidget
27from Products.Archetypes.atapi import MultiSelectionWidget
28from Products.Archetypes.atapi import StringWidget
29from Products.Archetypes.atapi import TextAreaWidget
30
31from Products.CMFCore import permissions
32from Products.CMFCore import Expression
33from Products.CMFCore.utils import getToolByName
34
35from Products.CacheSetup.config import PROJECT_NAME
36from Products.CacheSetup.utils import base_hasattr
37import base_cache_rule as BaseCacheRule
38
39schema = BaseContent.schema.copy() + Schema((
40
41    TextField(
42        'description',
43        required=0,
44        allowable_content_types = ('text/plain',),
45        default='A cache rule for CMF content types',
46        write_permission = permissions.ManagePortal,
47        widget=TextAreaWidget(
48            label='Description',
49            cols=60,
50            rows=5,
51            description='Basic documentation for this cache rule')),
52
53    LinesField(
54        'contentTypes',
55        required=1,
56        default=(),
57        multiValued = 1,
58        vocabulary='getContentTypesVocabulary',
59        write_permission = permissions.ManagePortal,
60        widget=MultiSelectionWidget(
61            label='Content Types',
62            size=10,
63            description='Please indicate the content types to which this rule applies')),
64
65    BooleanField(
66        'defaultView',
67        default=1,
68        write_permission = permissions.ManagePortal,
69        widget=BooleanWidget(
70            label='Default View',
71            description='Should this rule apply to the default view of this content object? '
72                        'The "default view" is the template that is shown when the user '
73                        'navigates to a content object without appending a template '
74                        'or view id to the end of the URL.')),
75
76    LinesField(
77        'templates',
78        write_permission = permissions.ManagePortal,
79        widget=LinesWidget(
80            label='Templates',
81            description='IDs for additional templates to which this rule should apply, one '
82                        'per line. If the template is a Zope 3-style view, enter its name '
83                        'without the @@ prefix. For example, to cache the @@types-controlpanel '
84                        'view (probably not a good idea), enter "types-controlpanel"')),
85
86    )) + BaseCacheRule.header_set_schema + BaseCacheRule.etag_schema + Schema((
87
88    StringField(
89        'purgeExpression',
90        required=0,
91        write_permission = permissions.ManagePortal,
92        widget=StringWidget(
93            label='Purge Expression',
94            size=80,
95            description='A TALES expression that generates a list of additional URLs to purge '
96                        '(URLs should be relative to the portal root) when an object is reindexed. '
97                        ' Available values: request, object.')),
98    ))
99
100schema['id'].widget.ignore_visible_ids=True                       
101schema['id'].widget.description="Should not contain spaces, underscores or mixed case. An 'X-Caching-Rule-Id' header with this id will be added."
102
103class ContentCacheRule(BaseCacheRule.BaseCacheRule):
104    """
105    """
106    security = ClassSecurityInfo()
107    archetype_name = 'Content Cache Rule'
108    portal_type = meta_type = 'ContentCacheRule'
109    __implements__ = BaseCacheRule.BaseCacheRule.__implements__
110    schema = schema
111    _at_rename_after_creation = True
112
113    actions = (
114        {'action':      'string:$object_url',
115         'category':    'object',
116         'id':          'view',
117         'name':        'Cache Setup',
118         'permissions': (permissions.ManagePortal,),
119         'visible':     False},
120    )
121
122    aliases = {
123        '(Default)':    'cache_policy_item_config',
124        'view' :        'cache_policy_item_config',
125        'edit' :        'cache_policy_item_config'
126    }
127
128    def getPurgeExpression(self):
129        expression = self.getField('purgeExpression').get(self)
130        if expression:
131            return expression.text
132       
133    def setPurgeExpression(self, expression):
134        return self.getField('purgeExpression').set(self, Expression.Expression(expression))
135
136    def validate_purgeExpression(self, expression):
137        return self._validate_expression(expression)
138
139    def getPurgeExpressionValue(self, expr_context):
140        expression = self.getField('purgeExpression').get(self)
141        if expression:
142            return expression(expr_context)
143
144    security.declarePublic('getHeaderSet')
145    def getHeaderSet(self, request, object, view, member):
146        # see if this rule applies
147        if not base_hasattr(object, 'portal_type'):
148            return
149        if object.portal_type not in self.getContentTypes():
150            return None
151        if view not in self.getTemplates() and \
152           not (self.getDefaultView() and view == self.getObjectDefaultView(object)):
153            return None
154
155        header_set = self._getHeaderSet(request, object, view, member)
156
157        # associate template with PageCacheManager
158        if header_set and header_set.getPageCache():
159            self._associateTemplate(object, view)
160           
161        return header_set
162
163
164    def _getViewsUrlsForObject(self, object):
165        """return a list of relative URLs to the possible views for an object"""
166        suffixes = []
167        if self.getDefaultView():
168            suffixes.extend(['', '/', '/view', '/'+self.getObjectDefaultView(object)])
169        templates = self.getTemplates()
170        if templates:
171            for t in templates:
172                if t:
173                    if t.startswith('/'):
174                        suffixes.append(t)
175                    else:
176                        suffixes.append('/'+t)
177
178        return suffixes
179
180
181    def _getObjectFieldUrls(self, object):
182        """return a list of relative URLs to fields for an object"""
183        if not IBaseObject.providedBy(object):
184            return []
185
186        schema = object.Schema()
187
188        def InterestingField(field):
189            # Argh. Evil Z2 interfaces alert.
190            return field.getType() in ["Products.Archetypes.Field.ImageField",
191                                       "Products.Archetypes.Field.FieldField"]
192
193        urls=[]
194        for field in schema.filterFields(InterestingField):
195            baseurl="/"+field.getName()
196            urls.append(baseurl)
197
198            if field.getType()=="Products.Archetypes.Field.ImageField":
199                for size in field.getAvailableSizes(object).keys():
200                    urls.append("%s_%s" % (baseurl, size))
201
202        return urls
203
204
205
206    security.declarePublic('getRelativeUrlsToPurge')
207    def getRelativeUrlsToPurge(self, object, urls):
208        if object.portal_type == "Discussion Item":
209            object=self.plone_utils.getDiscussionThread(object)[0]
210
211        # Abort if this is not a known content
212        if object.portal_type not in self.getContentTypes():
213            return
214
215        suffixes=self._getViewsUrlsForObject(object)
216        suffixes.extend(self._getObjectFieldUrls(object))
217
218        if suffixes:
219            url_tool = getToolByName(self, 'portal_url')
220            obj_url = url_tool.getRelativeUrl(object)
221            urls.union_update([obj_url + s for s in suffixes])
222            portal = url_tool.getPortalObject()
223            if object.aq_base is not portal:
224                parent = object.getParentNode()
225                parent_default_view = self.getObjectDefaultView(parent)
226                if object.getId() == parent_default_view:
227                    parent_url = url_tool.getRelativeUrl(parent)
228                    urls.union_update([parent_url + s for s in ('','/','/view', '/'+parent_default_view)])
229
230
231        purge_expression = self.getPurgeExpression()
232        if purge_expression:
233            expr_context = self._getExpressionContext(self.REQUEST, object, None, None)
234            urls.union_update(self.getPurgeExpressionValue(expr_context))
235
236    def getContentTypesVocabulary(self):
237        tt = getToolByName(self, 'portal_types')
238        types_list = []
239
240        from Products.Archetypes.public import listTypes
241        cachefu_types = [t['portal_type'] for t in listTypes(PROJECT_NAME)]
242
243        atct_criteria = []
244        try:
245            from Products.ATContentTypes.config import PROJECTNAME as ATCTNAME
246            from Products.ATContentTypes.interfaces import IATTopicCriterion
247            for t in listTypes(ATCTNAME):
248                if IATTopicCriterion.isImplementedByInstancesOf(t['klass']):
249                    atct_criteria.append(t['portal_type'])
250        except:
251            pass
252
253        for t in tt.listTypeInfo():
254            # filter out a few types
255            id = t.getId()
256            if id == 'TempFolder':
257                continue
258            if id in cachefu_types:
259                continue
260            if id in atct_criteria:
261                continue
262           
263            title = t.getProperty('title')
264            if not title:
265                title = id
266            else:
267                if title != id:
268                    title = '%s (%s)' % (title, id)
269            types_list.append((id, title))
270        types_list.sort(lambda x, y: cmp(x[1], y[1]))
271        return DisplayList(tuple(types_list))
272
273registerType(ContentCacheRule, PROJECT_NAME)
274
275__all__ = (
276    'ContentCacheRule',
277)
Note: See TracBrowser for help on using the repository browser.