vendor/CMF/1.6.3/CMFCore

view Skinnable.py @ 2:4c712d7bd1d7

Added tag 1.6.3 for changeset 1babb9d61518
author Georges Racinet on purity.racinet.fr <georges@racinet.fr>
date Fri, 09 Sep 2011 12:44:00 +0200
parents
children
line source
1 ##############################################################################
2 #
3 # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
4 #
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE.
11 #
12 ##############################################################################
13 """ Base class for object managers which can be "skinned".
15 Skinnable object managers inherit attributes from a skin specified in
16 the browser request. Skins are stored in a fixed-name subobject.
18 $Id$
19 """
21 import logging
22 from thread import get_ident
23 from AccessControl import ClassSecurityInfo
24 from Acquisition import aq_base
25 from Acquisition import ImplicitAcquisitionWrapper
26 from Globals import InitializeClass
27 from OFS.ObjectManager import ObjectManager
28 from ZODB.POSException import ConflictError
31 logger = logging.getLogger('CMFCore.Skinnable')
34 # superGetAttr is assigned to whatever ObjectManager.__getattr__
35 # used to do.
36 try:
37 superGetAttr = ObjectManager.__getattr__
38 except AttributeError:
39 try:
40 superGetAttr = ObjectManager.inheritedAttribute('__getattr__')
41 except AttributeError:
42 superGetAttr = None
44 _marker = [] # Create a new marker object.
47 SKINDATA = {} # mapping thread-id -> (skinobj, skinname, ignore, resolve)
49 class SkinDataCleanup:
50 """Cleanup at the end of the request."""
51 def __init__(self, tid):
52 self.tid = tid
53 def __del__(self):
54 tid = self.tid
55 if SKINDATA.has_key(tid):
56 del SKINDATA[tid]
59 class SkinnableObjectManager(ObjectManager):
61 security = ClassSecurityInfo()
63 security.declarePrivate('getSkinsFolderName')
64 def getSkinsFolderName(self):
65 # Not implemented.
66 return None
68 def __getattr__(self, name):
69 '''
70 Looks for the name in an object with wrappers that only reach
71 up to the root skins folder.
73 This should be fast, flexible, and predictable.
74 '''
75 if not name.startswith('_') and not name.startswith('aq_'):
76 sd = SKINDATA.get(get_ident())
77 if sd is not None:
78 ob, skinname, ignore, resolve = sd
79 if not ignore.has_key(name):
80 if resolve.has_key(name):
81 return resolve[name]
82 subob = getattr(ob, name, _marker)
83 if subob is not _marker:
84 # Return it in context of self, forgetting
85 # its location and acting as if it were located
86 # in self.
87 retval = aq_base(subob)
88 resolve[name] = retval
89 return retval
90 else:
91 ignore[name] = 1
92 if superGetAttr is None:
93 raise AttributeError, name
94 return superGetAttr(self, name)
96 security.declarePrivate('getSkin')
97 def getSkin(self, name=None):
98 """Returns the requested skin.
99 """
100 skinob = None
101 sfn = self.getSkinsFolderName()
103 if sfn is not None:
104 sf = getattr(self, sfn, None)
105 if sf is not None:
106 if name is not None:
107 skinob = sf.getSkinByName(name)
108 if skinob is None:
109 skinob = sf.getSkinByName(sf.getDefaultSkin())
110 if skinob is None:
111 skinob = sf.getSkinByPath('')
112 return skinob
114 security.declarePublic('getSkinNameFromRequest')
115 def getSkinNameFromRequest(self, REQUEST=None):
116 '''Returns the skin name from the Request.'''
117 sfn = self.getSkinsFolderName()
118 if sfn is not None:
119 sf = getattr(self, sfn, None)
120 if sf is not None:
121 return REQUEST.get(sf.getRequestVarname(), None)
123 security.declarePublic('changeSkin')
124 def changeSkin(self, skinname):
125 '''Change the current skin.
127 Can be called manually, allowing the user to change
128 skins in the middle of a request.
129 '''
130 skinobj = self.getSkin(skinname)
131 if skinobj is not None:
132 tid = get_ident()
133 SKINDATA[tid] = (skinobj, skinname, {}, {})
134 REQUEST = getattr(self, 'REQUEST', None)
135 if REQUEST is not None:
136 REQUEST._hold(SkinDataCleanup(tid))
138 security.declarePublic('getCurrentSkinName')
139 def getCurrentSkinName(self):
140 '''Return the current skin name.
141 '''
142 sd = SKINDATA.get(get_ident())
143 if sd is not None:
144 ob, skinname, ignore, resolve = sd
145 if skinname is not None:
146 return skinname
147 # nothing here, so assume the default skin
148 sfn = self.getSkinsFolderName()
149 if sfn is not None:
150 sf = getattr(self, sfn, None)
151 if sf is not None:
152 return sf.getDefaultSkin()
153 # and if that fails...
154 return None
156 security.declarePublic('clearCurrentSkin')
157 def clearCurrentSkin(self):
158 """Clear the current skin."""
159 tid = get_ident()
160 if SKINDATA.has_key(tid):
161 del SKINDATA[tid]
163 security.declarePublic('setupCurrentSkin')
164 def setupCurrentSkin(self, REQUEST=None):
165 '''
166 Sets up skindata so that __getattr__ can find it.
168 Can NOT be called manually to change skins in the middle of a
169 request! Use changeSkin for that.
170 '''
171 if REQUEST is None:
172 REQUEST = getattr(self, 'REQUEST', None)
173 if REQUEST is None:
174 # self is not fully wrapped at the moment. Don't
175 # change anything.
176 return
177 if SKINDATA.has_key(get_ident()):
178 # Already set up for this request.
179 return
180 skinname = self.getSkinNameFromRequest(REQUEST)
181 self.changeSkin(skinname)
183 def __of__(self, parent):
184 '''
185 Sneakily sets up the portal skin then returns the wrapper
186 that Acquisition.Implicit.__of__() would return.
187 '''
188 w_self = ImplicitAcquisitionWrapper(self, parent)
189 try:
190 w_self.setupCurrentSkin()
191 except ConflictError:
192 raise
193 except:
194 # This shouldn't happen, even if the requested skin
195 # does not exist.
196 logger.exception("Unable to setupCurrentSkin()")
197 return w_self
199 def _checkId(self, id, allow_dup=0):
200 '''
201 Override of ObjectManager._checkId().
203 Allows the user to create objects with IDs that match the ID of
204 a skin object.
205 '''
206 superCheckId = SkinnableObjectManager.inheritedAttribute('_checkId')
207 if not allow_dup:
208 # Temporarily disable skindata.
209 # Note that this depends heavily on Zope's current thread
210 # behavior.
211 tid = get_ident()
212 sd = SKINDATA.get(tid)
213 if sd is not None:
214 del SKINDATA[tid]
215 try:
216 base = getattr(self, 'aq_base', self)
217 if not hasattr(base, id):
218 # Cause _checkId to not check for duplication.
219 return superCheckId(self, id, allow_dup=1)
220 finally:
221 if sd is not None:
222 SKINDATA[tid] = sd
223 return superCheckId(self, id, allow_dup)
225 InitializeClass(SkinnableObjectManager)