File: browserdefault.py

package info (click to toggle)
zope-cmfdynamicviewfti 2.1-1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 172 kB
  • ctags: 136
  • sloc: python: 719; xml: 57; makefile: 31; sh: 2
file content (250 lines) | stat: -rw-r--r-- 9,739 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
##############################################################################
#
# CMFDynamicViewFTI
# Copyright (c) 2005 Plone Foundation. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
# Authors:  Martin Aspeli
#           Christian Heimes
#
##############################################################################
"""Mixin class for selectable views

This module contains a mixin-class to support selecting default layout
templates and/or default pages (in the style of default_page/index_html).
The implementation extends TemplateMixin from Archetypes, and implements
the ISelectableBrowserDefault interface from CMFPlone.
"""
__author__  = 'Martin Aspeli, Christian Heimes, Sasha Vincic'
__docformat__ = 'plaintext'


from zope.interface import implements
import zope.component
from ExtensionClass import Base
from AccessControl import ClassSecurityInfo
from Globals import InitializeClass
from Acquisition import aq_parent
from Acquisition import aq_base
from Acquisition import aq_inner
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.permissions import View

from Products.CMFDynamicViewFTI.permissions import ModifyViewTemplate
from Products.CMFDynamicViewFTI.fti import DynamicViewTypeInformation

from Products.CMFDynamicViewFTI.interface import ISelectableBrowserDefault
from Products.CMFDynamicViewFTI.interfaces import ISelectableBrowserDefault as ZopeTwoISelectableBrowserDefault

_marker = object()
fti_meta_type = DynamicViewTypeInformation.meta_type

class BrowserDefaultMixin(Base):
    """Mixin class for content types using the dynamic view FTI

    Allow the user to select a layout template (in the same way as
    TemplateMixin in Archetypes does), and/or to set a contained
    object's id as a default_page (acting in the same way as index_html)

    Note: folderish content types should overwrite HEAD like ATContentTypes
    """
    implements(ISelectableBrowserDefault)
    __implements__ = (ZopeTwoISelectableBrowserDefault, )

    _at_fti_meta_type = fti_meta_type
    aliases = {
        '(Default)'  : '(dynamic view)',
        'view'       : '(selected layout)',
        'index.html' : '(dynamic view)',
        'edit'       : 'base_edit',
        'properties' : 'base_metadata',
        'sharing'    : 'folder_localrole_form',
        'gethtml'    : '',
        'mkdir'      : '',
        }

    default_view = "base_view"
    suppl_views = ()

    security = ClassSecurityInfo()

    security.declareProtected(View, 'defaultView')
    def defaultView(self, request=None):
        """
        Get the actual view to use. If a default page is set, its id will
        be returned. Else, the current layout's page template id is returned.
        """
        fti = self.getTypeInfo()
        if fti is None:
            return self.default_view
        else:
            return fti.defaultView(self)

    security.declareProtected(View, '__call__')
    def __call__(self):
        """
        Resolve and return the selected view template applied to the object.
        This should not consider the default page.
        """
        template = self.unrestrictedTraverse(self.getLayout())
        context = aq_inner(self)
        template = template.__of__(context)
        return template(context, context.REQUEST)

    security.declareProtected(View, 'getDefaultPage')
    def getDefaultPage(self):
        """Return the id of the default page, or None if none is set.

        The default page must be contained within this (folderish) item.
        """
        fti = self.getTypeInfo()
        if fti is None:
            return None
        else:
            plone_utils = getToolByName(self, 'plone_utils', None)
            if plone_utils is not None:
                return plone_utils.getDefaultPage(self)
            else:
                return fti.getDefaultPage(self, check_exists=True)

    security.declareProtected(View, 'getLayout')
    def getLayout(self, **kw):
        """Get the selected view method.

        Note that a selected default page will override the view method.
        """
        fti = self.getTypeInfo()
        if fti is None:
            return None
        else:
            return fti.getViewMethod(self)

    security.declarePublic('canSetDefaultPage')
    def canSetDefaultPage(self):
        """Check if the user has permission to select a default page on this
        (folderish) item, and the item is folderish.
        """
        if not self.isPrincipiaFolderish:
            return False
        mtool = getToolByName(self, 'portal_membership')
        member = mtool.getAuthenticatedMember()
        return member.has_permission(ModifyViewTemplate, self)

    security.declareProtected(ModifyViewTemplate, 'setDefaultPage')
    def setDefaultPage(self, objectId):
        """Set the default page to display in this (folderish) object.

        The objectId must be a value found in self.objectIds() (i.e. a contained
        object). This object will be displayed as the default_page/index_html object
        of this (folderish) object. This will override the current layout
        template returned by getLayout(). Pass None for objectId to turn off
        the default page and return to using the selected layout template.
        """
        new_page = old_page = None
        if objectId is not None:
            new_page = getattr(self, objectId, None)
        if self.hasProperty('default_page'):
            pages = self.getProperty('default_page','')
            if isinstance(pages, (list, tuple)):
                for page in pages:
                    old_page = getattr(self, page, None)
                    if page is not None: break
            elif isinstance(pages, str):
                old_page = getattr(self, pages, None)

            if objectId is None:
                self.manage_delProperties(['default_page'])
            else:
                self.manage_changeProperties(default_page = objectId)
        else:
            if objectId is not None:
                self.manage_addProperty('default_page', objectId, 'string')
        if new_page != old_page:
            if new_page is not None:
                new_page.reindexObject(['is_default_page'])
            if old_page is not None:
                old_page.reindexObject(['is_default_page'])

    security.declareProtected(ModifyViewTemplate, 'setLayout')
    def setLayout(self, layout):
        """Set the layout as the current view.

        'layout' should be one of the list returned by getAvailableLayouts(), but it
        is not enforced. If a default page has been set with setDefaultPage(), it is
        turned off by calling setDefaultPage(None).
        """
        if not (layout and isinstance(layout, basestring)):
            raise ValueError, ("layout must be a non empty string, got %s(%s)" %
                               (layout, type(layout)))

        defaultPage = self.getDefaultPage()
        if defaultPage is None and layout == self.getLayout():
            return

        if self.hasProperty('layout'):
            self.manage_changeProperties(layout = layout)
        else:
            if getattr(aq_base(self), 'layout', _marker) is not _marker:
                # Archetypes remains? clean up
                old = self.layout
                if old and not isinstance(old, basestring):
                    raise RuntimeError, ("layout attribute exists on %s and is"
                                         "no string: %s" % (self, type(old)))
                delattr(self, 'layout')

            self.manage_addProperty('layout', layout, 'string')

        self.setDefaultPage(None)

    security.declareProtected(View, 'getDefaultLayout')
    def getDefaultLayout(self):
        """Get the default layout method.
        """
        fti = self.getTypeInfo()
        if fti is None:
            return "base_view" # XXX
        else:
            return fti.getDefaultViewMethod(self)

    security.declarePublic('canSetLayout')
    def canSetLayout(self):
        """Check if the current authenticated user is permitted to select a layout.
        """
        mtool = getToolByName(self, 'portal_membership')
        member = mtool.getAuthenticatedMember()
        return member.has_permission(ModifyViewTemplate, self)

    security.declareProtected(View, 'getAvailableLayouts')
    def getAvailableLayouts(self):
        """Get the layouts registered for this object from its FTI.
        """
        fti = self.getTypeInfo()
        if fti is None:
            return ()
        result = []
        method_ids = fti.getAvailableViewMethods(self)
        for mid in method_ids:
            view = zope.component.queryMultiAdapter((self, self.REQUEST),
                                                    zope.interface.Interface,
                                                    name=mid)

            if view is not None:
                result.append((mid, mid))
            else:
                method = getattr(self, mid, None)
                if method is not None:
                    # a method might be a template, script or method
                    try:
                        title = method.aq_inner.aq_explicit.title_or_id()
                    except AttributeError:
                        title = mid
                    result.append((mid, title))
        return result

InitializeClass(BrowserDefaultMixin)