Back to index

plone3  3.1.7
browserdefault.py
Go to the documentation of this file.
00001 """Mixin class for selectable views
00002 
00003 This module contains a mixin-class to support selecting default layout
00004 templates and/or default pages (in the style of default_page/index_html).
00005 The implementation extends TemplateMixin from Archetypes, and implements
00006 the ISelectableBrowserDefault interface from CMFPlone.
00007 """
00008 
00009 from zope.interface import implements
00010 import zope.component
00011 
00012 from zope.app.publisher.interfaces.browser import IBrowserMenu
00013 
00014 from ExtensionClass import Base
00015 from AccessControl import ClassSecurityInfo
00016 from Globals import InitializeClass
00017 from Acquisition import aq_base
00018 from Acquisition import aq_inner
00019 from Products.CMFCore.utils import getToolByName
00020 from Products.CMFCore.permissions import View
00021 
00022 from Products.CMFDynamicViewFTI.permissions import ModifyViewTemplate
00023 from Products.CMFDynamicViewFTI.fti import DynamicViewTypeInformation
00024 
00025 from Products.CMFDynamicViewFTI.interface import ISelectableBrowserDefault
00026 from Products.CMFDynamicViewFTI.interfaces import ISelectableBrowserDefault as ZopeTwoISelectableBrowserDefault
00027 
00028 _marker = object()
00029 fti_meta_type = DynamicViewTypeInformation.meta_type
00030 
00031 class BrowserDefaultMixin(Base):
00032     """Mixin class for content types using the dynamic view FTI
00033 
00034     Allow the user to select a layout template (in the same way as
00035     TemplateMixin in Archetypes does), and/or to set a contained
00036     object's id as a default_page (acting in the same way as index_html)
00037 
00038     Note: folderish content types should overwrite HEAD like ATContentTypes
00039     """
00040     implements(ISelectableBrowserDefault)
00041     __implements__ = (ZopeTwoISelectableBrowserDefault, )
00042 
00043     _at_fti_meta_type = fti_meta_type
00044     aliases = {
00045         '(Default)'  : '(dynamic view)',
00046         'view'       : '(selected layout)',
00047         'edit'       : 'base_edit',
00048         'properties' : 'base_metadata',
00049         'sharing'    : 'folder_localrole_form',
00050         'gethtml'    : '',
00051         'mkdir'      : '',
00052         }
00053 
00054     default_view = "base_view"
00055     suppl_views = ()
00056 
00057     security = ClassSecurityInfo()
00058 
00059     security.declareProtected(View, 'defaultView')
00060     def defaultView(self, request=None):
00061         """
00062         Get the actual view to use. If a default page is set, its id will
00063         be returned. Else, the current layout's page template id is returned.
00064         """
00065         fti = self.getTypeInfo()
00066         if fti is None:
00067             return self.default_view
00068         else:
00069             return fti.defaultView(self)
00070 
00071     security.declareProtected(View, '__call__')
00072     def __call__(self):
00073         """
00074         Resolve and return the selected view template applied to the object.
00075         This should not consider the default page.
00076         """
00077         template = self.unrestrictedTraverse(self.getLayout())
00078         context = aq_inner(self)
00079         template = template.__of__(context)
00080         return template(context, context.REQUEST)
00081 
00082     security.declareProtected(View, 'getDefaultPage')
00083     def getDefaultPage(self):
00084         """Return the id of the default page, or None if none is set.
00085 
00086         The default page must be contained within this (folderish) item.
00087         """
00088         fti = self.getTypeInfo()
00089         if fti is None:
00090             return None
00091         else:
00092             plone_utils = getToolByName(self, 'plone_utils', None)
00093             if plone_utils is not None:
00094                 return plone_utils.getDefaultPage(self)
00095             else:
00096                 return fti.getDefaultPage(self, check_exists=True)
00097 
00098     security.declareProtected(View, 'getLayout')
00099     def getLayout(self, **kw):
00100         """Get the selected view method.
00101 
00102         Note that a selected default page will override the view method.
00103         """
00104         fti = self.getTypeInfo()
00105         if fti is None:
00106             return None
00107         else:
00108             return fti.getViewMethod(self)
00109 
00110     security.declarePublic('canSetDefaultPage')
00111     def canSetDefaultPage(self):
00112         """Check if the user has permission to select a default page on this
00113         (folderish) item, and the item is folderish.
00114         """
00115         if not self.isPrincipiaFolderish:
00116             return False
00117         mtool = getToolByName(self, 'portal_membership')
00118         member = mtool.getAuthenticatedMember()
00119         return member.has_permission(ModifyViewTemplate, self)
00120 
00121     security.declareProtected(ModifyViewTemplate, 'setDefaultPage')
00122     def setDefaultPage(self, objectId):
00123         """Set the default page to display in this (folderish) object.
00124 
00125         The objectId must be a value found in self.objectIds() (i.e. a contained
00126         object). This object will be displayed as the default_page/index_html object
00127         of this (folderish) object. This will override the current layout
00128         template returned by getLayout(). Pass None for objectId to turn off
00129         the default page and return to using the selected layout template.
00130         """
00131         new_page = old_page = None
00132         if objectId is not None:
00133             new_page = getattr(self, objectId, None)
00134         if self.hasProperty('default_page'):
00135             pages = self.getProperty('default_page','')
00136             if isinstance(pages, (list, tuple)):
00137                 for page in pages:
00138                     old_page = getattr(self, page, None)
00139                     if page is not None: break
00140             elif isinstance(pages, str):
00141                 old_page = getattr(self, pages, None)
00142 
00143             if objectId is None:
00144                 self.manage_delProperties(['default_page'])
00145             else:
00146                 self.manage_changeProperties(default_page = objectId)
00147         else:
00148             if objectId is not None:
00149                 self.manage_addProperty('default_page', objectId, 'string')
00150         if new_page != old_page:
00151             if new_page is not None:
00152                 new_page.reindexObject(['is_default_page'])
00153             if old_page is not None:
00154                 old_page.reindexObject(['is_default_page'])
00155 
00156     security.declareProtected(ModifyViewTemplate, 'setLayout')
00157     def setLayout(self, layout):
00158         """Set the layout as the current view.
00159 
00160         'layout' should be one of the list returned by getAvailableLayouts(), but it
00161         is not enforced. If a default page has been set with setDefaultPage(), it is
00162         turned off by calling setDefaultPage(None).
00163         """
00164         if not (layout and isinstance(layout, basestring)):
00165             raise ValueError, ("layout must be a non empty string, got %s(%s)" %
00166                                (layout, type(layout)))
00167 
00168         defaultPage = self.getDefaultPage()
00169         if defaultPage is None and layout == self.getLayout():
00170             return
00171 
00172         if self.hasProperty('layout'):
00173             self.manage_changeProperties(layout = layout)
00174         else:
00175             if getattr(aq_base(self), 'layout', _marker) is not _marker:
00176                 # Archetypes remains? clean up
00177                 old = self.layout
00178                 if old and not isinstance(old, basestring):
00179                     raise RuntimeError, ("layout attribute exists on %s and is"
00180                                          "no string: %s" % (self, type(old)))
00181                 delattr(self, 'layout')
00182 
00183             self.manage_addProperty('layout', layout, 'string')
00184 
00185         self.setDefaultPage(None)
00186 
00187     security.declareProtected(View, 'getDefaultLayout')
00188     def getDefaultLayout(self):
00189         """Get the default layout method.
00190         """
00191         fti = self.getTypeInfo()
00192         if fti is None:
00193             return "base_view" # XXX
00194         else:
00195             return fti.getDefaultViewMethod(self)
00196 
00197     security.declarePublic('canSetLayout')
00198     def canSetLayout(self):
00199         """Check if the current authenticated user is permitted to select a layout.
00200         """
00201         mtool = getToolByName(self, 'portal_membership')
00202         member = mtool.getAuthenticatedMember()
00203         return member.has_permission(ModifyViewTemplate, self)
00204 
00205     security.declareProtected(View, 'getAvailableLayouts')
00206     def getAvailableLayouts(self):
00207         """Get the layouts registered for this object from its FTI.
00208         """
00209         fti = self.getTypeInfo()
00210         if fti is None:
00211             return ()
00212         result = []
00213         method_ids = fti.getAvailableViewMethods(self)
00214         for mid in method_ids:
00215             view = zope.component.queryMultiAdapter((self, self.REQUEST),
00216                                                     zope.interface.Interface,
00217                                                     name=mid)
00218 
00219             if view is not None:
00220                 menu = zope.component.getUtility(
00221                     IBrowserMenu, 'plone_displayviews')
00222                 item = menu.getMenuItemByAction(self, self.REQUEST, mid)
00223                 title = item and item.title or mid
00224                 result.append((mid, title))
00225             else:
00226                 method = getattr(self, mid, None)
00227                 if method is not None:
00228                     # a method might be a template, script or method
00229                     try:
00230                         title = method.aq_inner.aq_explicit.title_or_id()
00231                     except AttributeError:
00232                         title = mid
00233                     result.append((mid, title))
00234         return result
00235 
00236 InitializeClass(BrowserDefaultMixin)