Back to index

plone3  3.1.7
SkinsTool.py
Go to the documentation of this file.
00001 ##############################################################################
00002 #
00003 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
00004 #
00005 # This software is subject to the provisions of the Zope Public License,
00006 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
00007 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
00008 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00009 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
00010 # FOR A PARTICULAR PURPOSE.
00011 #
00012 ##############################################################################
00013 """Portal skins tool.
00014 
00015 $Id: SkinsTool.py 77113 2007-06-26 20:36:26Z yuppie $
00016 """
00017 
00018 from AccessControl import ClassSecurityInfo
00019 from Acquisition import aq_base
00020 from DateTime import DateTime
00021 from Globals import DTMLFile
00022 from Globals import InitializeClass
00023 from Globals import PersistentMapping
00024 from OFS.DTMLMethod import DTMLMethod
00025 from OFS.Folder import Folder
00026 from OFS.Image import Image
00027 from OFS.ObjectManager import REPLACEABLE
00028 from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
00029 from Products.PythonScripts.PythonScript import PythonScript
00030 from zope.interface import implements
00031 
00032 from ActionProviderBase import ActionProviderBase
00033 from DirectoryView import base_ignore
00034 from DirectoryView import ignore
00035 from DirectoryView import ignore_re
00036 from interfaces import ISkinsTool
00037 from interfaces.portal_skins import portal_skins as z2ISkinsTool
00038 from permissions import AccessContentsInformation
00039 from permissions import ManagePortal
00040 from permissions import View
00041 from SkinsContainer import SkinsContainer
00042 from utils import _dtmldir
00043 from utils import getToolByName
00044 from utils import UniqueObject
00045 
00046 def modifiedOptions():
00047     # Remove the existing "Properties" option and add our own.
00048     rval = []
00049     pos = -1
00050     for o in Folder.manage_options:
00051         label = o.get('label', None)
00052         if label != 'Properties':
00053             rval.append(o)
00054     rval[1:1] = [{'label':'Properties',
00055                   'action':'manage_propertiesForm'}]
00056     return tuple(rval)
00057 
00058 
00059 class SkinsTool(UniqueObject, SkinsContainer, Folder, ActionProviderBase):
00060 
00061     """ This tool is used to supply skins to a portal.
00062     """
00063 
00064     implements(ISkinsTool)
00065     __implements__ = (z2ISkinsTool, SkinsContainer.__implements__,
00066                       ActionProviderBase.__implements__)
00067 
00068     id = 'portal_skins'
00069     meta_type = 'CMF Skins Tool'
00070     allow_any = 0
00071     cookie_persistence = 0
00072     default_skin = ''
00073     request_varname = 'portal_skin'
00074     selections = None
00075 
00076     security = ClassSecurityInfo()
00077 
00078     manage_options = ( modifiedOptions() +
00079                       ({ 'label' : 'Overview', 'action' : 'manage_overview' }
00080                      ,
00081                      ) + ActionProviderBase.manage_options
00082                      )
00083 
00084     def __init__(self):
00085         self.selections = PersistentMapping()
00086 
00087     def _getSelections(self):
00088         sels = self.selections
00089         if sels is None:
00090             # Backward compatibility.
00091             self.selections = sels = PersistentMapping()
00092         return sels
00093 
00094     #
00095     #   ZMI methods
00096     #
00097     security.declareProtected(ManagePortal, 'manage_overview')
00098     manage_overview = DTMLFile( 'explainSkinsTool', _dtmldir )
00099 
00100     security.declareProtected(ManagePortal, 'manage_propertiesForm')
00101     manage_propertiesForm = DTMLFile('dtml/skinProps', globals())
00102 
00103     # the following two methods override those in FindSupport, to
00104     # support marking of objects used in specific skins
00105     security.declareProtected(ManagePortal, 'manage_findResult')
00106     manage_findResult = DTMLFile('findResult', _dtmldir,
00107                                  management_view='Find')
00108 
00109     security.declareProtected(ManagePortal, 'manage_findForm')
00110     manage_findForm = DTMLFile('findForm', _dtmldir,
00111                                management_view='Find')
00112 
00113 
00114 
00115     security.declareProtected(ManagePortal, 'manage_skinLayers')
00116     def manage_skinLayers(self, chosen=(), add_skin=0, del_skin=0,
00117                           skinname='', skinpath='', REQUEST=None):
00118         """ Change the skinLayers.
00119         """
00120         sels = self._getSelections()
00121         if del_skin:
00122             for name in chosen:
00123                 del sels[name]
00124 
00125         if REQUEST is not None:
00126             for key in sels.keys():
00127                 fname = 'skinpath_%s' % key
00128                 val = REQUEST[fname]
00129 
00130                 # if val is a list from the new lines field
00131                 # then munge it back into a comma delimited list
00132                 # for hysterical reasons
00133                 if isinstance(val, list):
00134                     val = ','.join([layer.strip() for layer in val])
00135 
00136                 if sels[key] != val:
00137                     self.testSkinPath(val)
00138                     sels[key] = val
00139 
00140         if add_skin:
00141             skinpath = ','.join([layer.strip() for layer in skinpath])
00142             self.testSkinPath(skinpath)
00143             sels[str(skinname)] = skinpath
00144 
00145         if REQUEST is not None:
00146             return self.manage_propertiesForm(
00147                 self, REQUEST, management_view='Properties', manage_tabs_message='Skins changed.')
00148 
00149 
00150     security.declareProtected(ManagePortal, 'isFirstInSkin')
00151     def isFirstInSkin(self, template_path, skin=None):
00152         """
00153         Is the specified template the one that would get returned from the current
00154         skin?
00155         """
00156         if skin is None or skin == 'None':
00157             skin = self.getDefaultSkin()
00158         template = self.restrictedTraverse(template_path)
00159         name = template.getId()
00160         skin_path = self.getSkinPath(skin)
00161         if not skin_path:
00162             return 0
00163         parts = list(skin_path.split(","))
00164         found = ""
00165         for part in parts:
00166             part = part.strip()
00167             if part[0] == "_":
00168                 continue
00169             partob = getattr(self, part, None)
00170             if partob:
00171                 skin_template = getattr(partob.aq_base, name, None)
00172                 if skin_template:
00173                     found = skin_template
00174                     break
00175         if found == template:
00176             return 1
00177         else:
00178             return 0
00179 
00180     security.declareProtected(ManagePortal, 'manage_properties')
00181     def manage_properties(self, default_skin='', request_varname='',
00182                           allow_any=0, chosen=(), add_skin=0,
00183                           del_skin=0, skinname='', skinpath='',
00184                           cookie_persistence=0, REQUEST=None):
00185         """ Changes portal_skin properties. """
00186         self.default_skin = str(default_skin)
00187         self.request_varname = str(request_varname)
00188         self.allow_any = allow_any and 1 or 0
00189         self.cookie_persistence = cookie_persistence and 1 or 0
00190         if REQUEST is not None:
00191             return self.manage_propertiesForm(
00192                 self, REQUEST, management_view='Properties', manage_tabs_message='Properties changed.')
00193 
00194     security.declarePrivate('PUT_factory')
00195 
00196     def PUT_factory( self, name, typ, body ):
00197         """
00198             Dispatcher for PUT requests to non-existent IDs.  Returns
00199             an object of the appropriate type (or None, if we don't
00200             know what to do).
00201         """
00202         major, minor = typ.split('/', 1)
00203 
00204         if major == 'image':
00205             return Image( id=name
00206                         , title=''
00207                         , file=''
00208                         , content_type=typ
00209                         )
00210 
00211         if major == 'text':
00212 
00213             if minor == 'x-python':
00214                 return PythonScript( id=name )
00215 
00216             if minor in ('html', 'xml'):
00217                 return ZopePageTemplate( name )
00218 
00219             return DTMLMethod( __name__=name )
00220 
00221         return None
00222 
00223     # Make the PUT_factory replaceable
00224     PUT_factory__replaceable__ = REPLACEABLE
00225 
00226 
00227     security.declarePrivate('testSkinPath')
00228     def testSkinPath(self, p):
00229         """ Calls SkinsContainer.getSkinByPath().
00230         """
00231         self.getSkinByPath(p, raise_exc=1)
00232 
00233     #
00234     #   'SkinsContainer' interface methods
00235     #
00236     security.declareProtected(AccessContentsInformation, 'getSkinPath')
00237     def getSkinPath(self, name):
00238         """ Convert a skin name to a skin path.
00239         """
00240         sels = self._getSelections()
00241         p = sels.get(name, None)
00242         if p is None:
00243             if self.allow_any:
00244                 return name
00245         return p  # Can be None
00246 
00247     security.declareProtected(AccessContentsInformation, 'getDefaultSkin')
00248     def getDefaultSkin(self):
00249         """ Get the default skin name.
00250         """
00251         return self.default_skin
00252 
00253     security.declareProtected(AccessContentsInformation, 'getRequestVarname')
00254     def getRequestVarname(self):
00255         """ Get the variable name to look for in the REQUEST.
00256         """
00257         return self.request_varname
00258 
00259     #
00260     #   UI methods
00261     #
00262     security.declareProtected(AccessContentsInformation, 'getAllowAny')
00263     def getAllowAny(self):
00264         '''
00265         Used by the management UI.  Returns a flag indicating whether
00266         users are allowed to use arbitrary skin paths.
00267         '''
00268         return self.allow_any
00269 
00270     security.declareProtected(AccessContentsInformation, 'getCookiePersistence')
00271     def getCookiePersistence(self):
00272         '''
00273         Used by the management UI.  Returns a flag indicating whether
00274         the skins cookie is persistent or not.
00275         '''
00276         return self.cookie_persistence
00277 
00278     security.declareProtected(AccessContentsInformation, 'getSkinPaths')
00279     def getSkinPaths(self):
00280         '''
00281         Used by the management UI.  Returns the list of skin name to
00282         skin path mappings as a sorted list of tuples.
00283         '''
00284         sels = self._getSelections()
00285         rval = []
00286         for key, value in sels.items():
00287             rval.append((key, value))
00288         rval.sort()
00289         return rval
00290 
00291     #
00292     #   'portal_skins' interface methods
00293     #
00294     security.declarePublic('getSkinSelections')
00295     def getSkinSelections(self):
00296         """ Get the sorted list of available skin names.
00297         """
00298         sels = self._getSelections()
00299         rval = list(sels.keys())
00300         rval.sort()
00301         return rval
00302 
00303     security.declareProtected(View, 'updateSkinCookie')
00304     def updateSkinCookie(self):
00305         """ If needed, updates the skin cookie based on the member preference.
00306         """
00307         # XXX: this method violates the rules for tools/utilities:
00308         # it depends on a non-utility tool and uses self.REQUEST
00309         mtool = getToolByName(self, 'portal_membership')
00310         utool = getToolByName(self, 'portal_url')
00311         member = mtool.getAuthenticatedMember()
00312         if hasattr(aq_base(member), 'getProperty'):
00313             mskin = member.getProperty('portal_skin', None)
00314             if mskin:
00315                 req = self.REQUEST
00316                 cookie = req.cookies.get(self.request_varname, None)
00317                 if cookie != mskin:
00318                     resp = req.RESPONSE
00319                     portal_path = req['BASEPATH1'] + '/' + utool(1)
00320 
00321                     if not self.cookie_persistence:
00322                         # *Don't* make the cookie persistent!
00323                         resp.setCookie(self.request_varname, mskin,
00324                                        path=portal_path)
00325                     else:
00326                         expires = ( DateTime( 'GMT' ) + 365 ).rfc822()
00327                         resp.setCookie( self.request_varname
00328                                       , mskin
00329                                       , path=portal_path
00330                                       , expires=expires
00331                                       )
00332                     # Ensure updateSkinCookie() doesn't try again
00333                     # within this request.
00334                     req.cookies[self.request_varname] = mskin
00335                     req[self.request_varname] = mskin
00336                     return 1
00337         return 0
00338 
00339     security.declareProtected(View, 'clearSkinCookie')
00340     def clearSkinCookie(self):
00341         """ Expire the skin cookie.
00342         """
00343         # XXX: this method violates the rules for tools/utilities:
00344         # it depends on a non-utility tool and uses self.REQUEST
00345         req = self.REQUEST
00346         resp = req.RESPONSE
00347         utool = getToolByName(self, 'portal_url')
00348         portal_path = req['BASEPATH1'] + '/' + utool(1)
00349         resp.expireCookie(self.request_varname, path=portal_path)
00350 
00351     security.declareProtected(ManagePortal, 'addSkinSelection')
00352     def addSkinSelection(self, skinname, skinpath, test=0, make_default=0):
00353         '''
00354         Adds a skin selection.
00355         '''
00356         sels = self._getSelections()
00357         skinpath = str(skinpath)
00358 
00359         # Basic precaution to make sure the stuff we want to ignore in
00360         # DirectoryViews gets prevented from ending up in a skin path
00361         path_elems = [x.strip() for x in skinpath.split(',')]
00362         ignored = base_ignore + ignore
00363 
00364         for elem in path_elems[:]:
00365             if elem in ignored or ignore_re.match(elem):
00366                 path_elems.remove(elem)
00367 
00368         skinpath = ','.join(path_elems)
00369 
00370         if test:
00371             self.testSkinPath(skinpath)
00372         sels[str(skinname)] = skinpath
00373         if make_default:
00374             self.default_skin = skinname
00375 
00376 InitializeClass(SkinsTool)