[CMF-checkins] SVN: CMF/branches/1.4/ Backported several
hand-picked fixes from CMF 1.5 branch, most of them
Sidnei da Silva
sidnei at enfoldsystems.com
Fri Sep 2 09:58:14 EDT 2005
Log message for revision 38244:
Backported several hand-picked fixes from CMF 1.5 branch, most of them
related to caching and the caching policy manager and a couple other
ones that I've considered critical by reading the log messages.
- DCWorkflow Guard check now uses utils._checkPermission instead of Zope's
checkPermission (allows using proxy roles)
- Added check for executable owner and proxy roles to _checkPermission
- Put the DiscussionItem in its container before indexing it.
- CMFCore.utils: The return value from _mergedLocalRoles allowed
direct manipulation of objects' local role settings since it
contained references to the actual values instead of copies.
(http://www.zope.org/Collectors/CMF/376)
- CMFDefault.Image and CMFDefault.File: When calling the constructor,
any intelligent content type detection would be destroyed by the
call to initialize Dublin Core values, which would overwrite the
content_type. It is now preserved, if possible.
(http://www.zope.org/Collectors/CMF/370)
- CMFCore.FSImage and FSFile: Unlike the current behavior of Zope
itself, FSImage and FSFile would set a content-length response
header for 304 (not modified) responses, which should not be
done according to RFC 2616. It won't do so anymore, but Zope
itself will still force a content-length header in
ZServer.HTTPResponse. This misbehavior has been filed as a Zope
issue (http://www.zope.org/Collectors/Zope/1866).
(http://www.zope.org/Collectors/CMF/372)
- CMFCore.PortalContent, CMFCore.FSSTXMethod, CMFTopic.Topic,
CMFDefault.SkinnedFolder: Cache headers from the Caching Policy
Manager never got set for DTML-based skins due to the way the
view template __call__ method was
invoked. (http://www.zope.org/Collectors/CMF/374)
- Make CookieCrumbler set a 'Cache-Control' header, default to
'private' when a autentication cookie is present.
- Be verbose about what caching policy manager set headers by
including a 'X-Cache-Headers-Set-By' header.
- Added 'Vary' setting to Caching Policy Manager.
- Provide a __delattr__ method to FSDirectoryView to avoid ZODB
bloat (http://www.zope.org/Collectors/CMF/316).
- Preserve cache manager associations when customizing
FSObject-based objects into the ZODB.
Changed:
U CMF/branches/1.4/CHANGES.txt
U CMF/branches/1.4/CMFCore/CachingPolicyManager.py
U CMF/branches/1.4/CMFCore/CookieCrumbler.py
U CMF/branches/1.4/CMFCore/DirectoryView.py
U CMF/branches/1.4/CMFCore/FSDTMLMethod.py
U CMF/branches/1.4/CMFCore/FSFile.py
U CMF/branches/1.4/CMFCore/FSImage.py
U CMF/branches/1.4/CMFCore/FSObject.py
U CMF/branches/1.4/CMFCore/FSSTXMethod.py
U CMF/branches/1.4/CMFCore/PortalContent.py
U CMF/branches/1.4/CMFCore/dtml/cachingPolicies.dtml
U CMF/branches/1.4/CMFCore/tests/base/dummy.py
A CMF/branches/1.4/CMFCore/tests/fake_skins/fake_skin/testDTML.dtml
A CMF/branches/1.4/CMFCore/tests/fake_skins/fake_skin/testDTML.dtml.metadata
U CMF/branches/1.4/CMFCore/tests/test_CachingPolicyManager.py
U CMF/branches/1.4/CMFCore/tests/test_DirectoryView.py
A CMF/branches/1.4/CMFCore/tests/test_FSDTMLMethod.py
U CMF/branches/1.4/CMFCore/tests/test_FSFile.py
U CMF/branches/1.4/CMFCore/tests/test_FSImage.py
U CMF/branches/1.4/CMFCore/tests/test_FSPageTemplate.py
A CMF/branches/1.4/CMFCore/tests/test_utils.py
U CMF/branches/1.4/CMFCore/utils.py
U CMF/branches/1.4/CMFDefault/DiscussionItem.py
U CMF/branches/1.4/CMFDefault/File.py
U CMF/branches/1.4/CMFDefault/Image.py
U CMF/branches/1.4/CMFDefault/SkinnedFolder.py
U CMF/branches/1.4/CMFDefault/tests/test_Image.py
U CMF/branches/1.4/CMFTopic/Topic.py
U CMF/branches/1.4/DCWorkflow/Guard.py
-=-
Modified: CMF/branches/1.4/CHANGES.txt
===================================================================
--- CMF/branches/1.4/CHANGES.txt 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CHANGES.txt 2005-09-02 13:58:14 UTC (rev 38244)
@@ -2,11 +2,58 @@
Bug Fixes
+ - DCWorkflow Guard check now uses utils._checkPermission instead of Zope's
+ checkPermission (allows using proxy roles)
+
+ - Added check for executable owner and proxy roles to _checkPermission
+
+ - Put the DiscussionItem in its container before indexing it.
+
+ - CMFCore.utils: The return value from _mergedLocalRoles allowed
+ direct manipulation of objects' local role settings since it
+ contained references to the actual values instead of copies.
+ (http://www.zope.org/Collectors/CMF/376)
+
+ - CMFDefault.Image and CMFDefault.File: When calling the constructor,
+ any intelligent content type detection would be destroyed by the
+ call to initialize Dublin Core values, which would overwrite the
+ content_type. It is now preserved, if possible.
+ (http://www.zope.org/Collectors/CMF/370)
+
+ - CMFCore.FSImage and FSFile: Unlike the current behavior of Zope
+ itself, FSImage and FSFile would set a content-length response
+ header for 304 (not modified) responses, which should not be
+ done according to RFC 2616. It won't do so anymore, but Zope
+ itself will still force a content-length header in
+ ZServer.HTTPResponse. This misbehavior has been filed as a Zope
+ issue (http://www.zope.org/Collectors/Zope/1866).
+ (http://www.zope.org/Collectors/CMF/372)
+
+ - CMFCore.PortalContent, CMFCore.FSSTXMethod, CMFTopic.Topic,
+ CMFDefault.SkinnedFolder: Cache headers from the Caching Policy
+ Manager never got set for DTML-based skins due to the way the
+ view template __call__ method was
+ invoked. (http://www.zope.org/Collectors/CMF/374)
+
+ - Make CookieCrumbler set a 'Cache-Control' header, default to
+ 'private' when a autentication cookie is present.
+
+ - Be verbose about what caching policy manager set headers by
+ including a 'X-Cache-Headers-Set-By' header.
+
+ - Added 'Vary' setting to Caching Policy Manager.
+
+ - Provide a __delattr__ method to FSDirectoryView to avoid ZODB
+ bloat (http://www.zope.org/Collectors/CMF/316).
+
+ - Preserve cache manager associations when customizing
+ FSObject-based objects into the ZODB.
+
- CMFCore.FSObject: Make FSObject-derivatives role managers, so that
role settings in '.metadata' files work. Thanks to Dieter Maurer for
the patch! (http://www.zope.org/Collectors/CMF/359)
- - DCWorkflow.Scripts: Usinng inherited manage_main requires wrapping
+ - DCWorkflow.Scripts: Usinng inherited manage_main requires wrapping
for Zope 2.8 compatibility.
- CMFDefault.SyndicationTool: Zope 2.8 raises AttributeError where
@@ -86,16 +133,16 @@
due to inappropriate borrowing of an unbound method from
ZopePageTemplate.
- - CMFDefault.DiscussionItem: Workflow did not get notified when a
+ - CMFDefault.DiscussionItem: Workflow did not get notified when a
DiscussionItem was added to content.
(http://zope.org/Collectors/CMF/280)
-
+
CMF 1.4.7 (2004/08/11)
New Features
- - Minor featurelet: The "Action Providers" ZMI tab on the portal_actions
- tool now links directly to the tools shown
+ - Minor featurelet: The "Action Providers" ZMI tab on the portal_actions
+ tool now links directly to the tools shown
(http://zope.org/Collectors/CMF/181)
Bug Fixes
@@ -123,7 +170,7 @@
- CMFCore.PortalFolder: Unlike other content, only Managers were able
to create PortalFolders using mkdir in FTP. Fixed by inserting
- missing security declaration for PortalFolder.manage_addFolder
+ missing security declaration for PortalFolder.manage_addFolder
(http://zope.org/Collectors/CMF/167)
- Default text format for NewsItems is now structured-text, just like
@@ -135,7 +182,7 @@
was faulty.
- CMFDefault.DublinCore: Use the portal_metadata tool's 'getPublisher'
- for the DublinCore 'Publisher' element (thanks to Eric Brown for the
+ for the DublinCore 'Publisher' element (thanks to Eric Brown for the
patch).
- CMFDefault.Document: Make Document render compliant XHTML when format
@@ -169,10 +216,10 @@
- CMFDefault.Document and CMFDefault.Link: PUT() caused improper
splitting of 'Contributors' metadata header.
(http://plone.org/collector/3217)
-
+
- CMFCore.utils: Introduced contributorsplitter() utility function.
- - CMFCore.PortalFolder: checkIdAvailable() failed to catch
+ - CMFCore.PortalFolder: checkIdAvailable() failed to catch
zExceptions.BadRequest.
CMF 1.4.5 (2004/07/08)
Modified: CMF/branches/1.4/CMFCore/CachingPolicyManager.py
===================================================================
--- CMF/branches/1.4/CMFCore/CachingPolicyManager.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/CachingPolicyManager.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -99,6 +99,15 @@
the "Cache-control" header will be set using 'max_age_secs',
if passed; it should be an integer value in seconds.
+ - The "Vary" HTTP response headers will be set if a value is
+ provided. The Vary header is described in RFC 2616. In essence,
+ it instructs caches that respect this header (such as Squid
+ after version 2.4) to distinguish between requests not just by
+ the request URL, but also by values found in the headers showing
+ in the Vary tag. "Vary: Cookie" would force Squid to also take
+ Cookie headers into account when deciding what cached object to
+ choose and serve in response to a request.
+
- Other tokens will be added to the "Cache-control" HTTP response
header as follows:
@@ -117,6 +126,7 @@
, no_cache=0
, no_store=0
, must_revalidate=0
+ , vary=''
):
if not predicate:
@@ -135,6 +145,7 @@
self._no_cache = int( no_cache )
self._no_store = int( no_store )
self._must_revalidate = int( must_revalidate )
+ self._vary = vary
def getPolicyId( self ):
"""
@@ -171,6 +182,11 @@
"""
return self._must_revalidate
+ def getVary( self ):
+ """
+ """
+ return getattr(self, '_vary', '')
+
def getHeaders( self, expr_context ):
"""
Does this request match our predicate? If so, return a
@@ -211,6 +227,9 @@
if control:
headers.append( ( 'Cache-control', ', '.join( control ) ) )
+ if self.getVary():
+ headers.append( ( 'Vary', self._vary ) )
+
return headers
@@ -266,6 +285,7 @@
, no_cache # boolean (def. 0)
, no_store # boolean (def. 0)
, must_revalidate # boolean (def. 0)
+ , vary
, REQUEST=None
):
"""
@@ -278,6 +298,7 @@
, no_cache
, no_store
, must_revalidate
+ , vary
)
if REQUEST is not None:
REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
@@ -295,6 +316,7 @@
, no_cache # boolean (def. 0)
, no_store # boolean (def. 0)
, must_revalidate # boolean (def. 0)
+ , vary
, REQUEST=None
):
"""
@@ -307,6 +329,7 @@
, no_cache
, no_store
, must_revalidate
+ , vary
)
if REQUEST is not None:
REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
@@ -375,6 +398,7 @@
, no_cache
, no_store
, must_revalidate
+ , vary
):
"""
Add a policy to our registry.
@@ -394,6 +418,7 @@
, no_cache
, no_store
, must_revalidate
+ , vary
)
idlist = list( self._policy_ids )
idlist.append( policy_id )
@@ -408,6 +433,7 @@
, no_cache
, no_store
, must_revalidate
+ , vary
):
"""
Update a policy in our registry.
@@ -422,6 +448,7 @@
, no_cache
, no_store
, must_revalidate
+ , vary
)
security.declarePrivate( '_reorderPolicy' )
Modified: CMF/branches/1.4/CMFCore/CookieCrumbler.py
===================================================================
--- CMF/branches/1.4/CMFCore/CookieCrumbler.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/CookieCrumbler.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -68,6 +68,8 @@
'label':'Auto-login page ID'},
{'id':'logout_page', 'type': 'string', 'mode':'w',
'label':'Logout page ID'},
+ {'id':'cache_header_value', 'type': 'string', 'mode':'w',
+ 'label':'Cache-Control header value'},
)
auth_cookie = '__ac'
@@ -76,6 +78,7 @@
persist_cookie = '__ac_persistent'
auto_login_page = 'login_form'
logout_page = 'logged_out'
+ cache_header_value = 'private'
security.declarePrivate('delRequestVar')
def delRequestVar(self, req, name):
@@ -167,6 +170,12 @@
resp.unauthorized = self.unauthorized
resp._unauthorized = self._unauthorized
if attempt != ATTEMPT_NONE:
+ if self.cache_header_value:
+ # we don't want caches to cache the resulting page
+ resp.setHeader('Cache-Control', self.cache_header_value)
+ # demystify this in the response.
+ resp.setHeader('X-Cache-Control-Hdr-Modified-By',
+ 'CookieCrumbler')
phys_path = self.getPhysicalPath()
if self.logout_page:
# Cookies are in use.
Modified: CMF/branches/1.4/CMFCore/DirectoryView.py
===================================================================
--- CMF/branches/1.4/CMFCore/DirectoryView.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/DirectoryView.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -415,6 +415,11 @@
d[name] = value
setattr(d['_real'], name, value)
+ def __delattr__(self, name):
+ d = self.__dict__
+ del d[name]
+ delattr(d['_real'], name)
+
security.declareProtected(ManagePortal, 'manage_propertiesForm')
manage_propertiesForm = DTMLFile( 'dirview_properties', _dtmldir )
Modified: CMF/branches/1.4/CMFCore/FSDTMLMethod.py
===================================================================
--- CMF/branches/1.4/CMFCore/FSDTMLMethod.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/FSDTMLMethod.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -23,7 +23,7 @@
from OFS.DTMLMethod import DTMLMethod, decapitate, guess_content_type
from AccessControl.Role import RoleManager
-from utils import _dtmldir
+from utils import _dtmldir, _setCacheHeaders
from CMFCorePermissions import View
from CMFCorePermissions import ViewManagementScreens
from CMFCorePermissions import FTPAccess
@@ -153,6 +153,9 @@
else:
c, e=guess_content_type(self.getId(), r)
RESPONSE.setHeader('Content-Type', c)
+ if RESPONSE is not None:
+ # caching policy manager hook
+ _setCacheHeaders(self, {})
result = decapitate(r, RESPONSE)
if not self._cache_namespace_keys:
self.ZCacheable_set(result)
Modified: CMF/branches/1.4/CMFCore/FSFile.py
===================================================================
--- CMF/branches/1.4/CMFCore/FSFile.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/FSFile.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -90,6 +90,9 @@
"""
self._updateFromFS()
data = self._readFile(0)
+ data_len = len(data)
+ last_mod = self._file_mod_time
+ status = 200
# HTTP If-Modified-Since header handling.
header=REQUEST.get_header('If-Modified-Since', None)
if header is not None:
@@ -100,23 +103,31 @@
# with common servers such as Apache (which can usually
# understand the screwy date string as a lucky side effect
# of the way they parse it).
- try: mod_since=long(DateTime(header).timeTime())
- except: mod_since=None
+ try:
+ mod_since=long(DateTime(header).timeTime())
+ except:
+ mod_since=None
+
if mod_since is not None:
- last_mod = self._file_mod_time
if last_mod > 0 and last_mod <= mod_since:
- # Set header values since apache caching will return
- # Content-Length of 0 in response if size is not set here
- RESPONSE.setHeader('Last-Modified', rfc1123_date(last_mod))
- RESPONSE.setHeader('Content-Type', self.content_type)
- RESPONSE.setHeader('Content-Length', self.get_size())
- RESPONSE.setStatus(304)
- return ''
+ status = 304
+ data = ''
- RESPONSE.setHeader('Last-Modified', rfc1123_date(self._file_mod_time))
+ #Last-Modified will get stomped on by a cache policy it there is
+ #one set....
+ RESPONSE.setStatus(status)
+ RESPONSE.setHeader('Last-Modified', rfc1123_date(last_mod))
RESPONSE.setHeader('Content-Type', self.content_type)
- RESPONSE.setHeader('Content-Length', len(data))
+ if status != 304:
+ # Avoid setting content-length for a 304. See RFC 2616.
+ # Zope might still, for better or for worse, set a
+ # content-length header with value "0".
+ RESPONSE.setHeader('Content-Length', data_len)
+
+ #There are 2 Cache Managers which can be in play....
+ #need to decide which to use to determine where the cache headers
+ #are decided on.
if self.ZCacheable_getManager() is not None:
self.ZCacheable_set(None)
else:
Modified: CMF/branches/1.4/CMFCore/FSImage.py
===================================================================
--- CMF/branches/1.4/CMFCore/FSImage.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/FSImage.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -122,8 +122,13 @@
RESPONSE.setStatus(status)
RESPONSE.setHeader('Last-Modified', rfc1123_date(last_mod))
RESPONSE.setHeader('Content-Type', self.content_type)
- RESPONSE.setHeader('Content-Length', data_len)
+ if status != 304:
+ # Avoid setting content-length for a 304. See RFC 2616.
+ # Zope might still, for better or for worse, set a
+ # content-length header with value "0".
+ RESPONSE.setHeader('Content-Length', data_len)
+
#There are 2 Cache Managers which can be in play....
#need to decide which to use to determine where the cache headers
#are decided on.
Modified: CMF/branches/1.4/CMFCore/FSObject.py
===================================================================
--- CMF/branches/1.4/CMFCore/FSObject.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/FSObject.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -78,7 +78,13 @@
"""
obj = self._createZODBClone()
-
+
+ # Preserve cache manager associations
+ cachemgr_id = self.ZCacheable_getManagerId()
+ if ( cachemgr_id and
+ getattr(obj, 'ZCacheable_setManagerId', None) is not None ):
+ obj.ZCacheable_setManagerId(cachemgr_id)
+
id = obj.getId()
fpath = tuple(split(folder_path, '/'))
portal_skins = getToolByName(self,'portal_skins')
Modified: CMF/branches/1.4/CMFCore/FSSTXMethod.py
===================================================================
--- CMF/branches/1.4/CMFCore/FSSTXMethod.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/FSSTXMethod.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -130,7 +130,7 @@
template = getattr( self, 'stxmethod_view', self._default_template )
if getattr( template, 'isDocTemp', 0 ):
- posargs = ( self, REQUEST )
+ posargs = ( self, REQUEST, RESPONSE )
else:
posargs = ()
Modified: CMF/branches/1.4/CMFCore/PortalContent.py
===================================================================
--- CMF/branches/1.4/CMFCore/PortalContent.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/PortalContent.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -111,7 +111,7 @@
'''
view = _getViewFor(self)
if getattr(aq_base(view), 'isDocTemp', 0):
- return view(self, self.REQUEST)
+ return view(self, self.REQUEST, self.REQUEST['RESPONSE'])
else:
return view()
Modified: CMF/branches/1.4/CMFCore/dtml/cachingPolicies.dtml
===================================================================
--- CMF/branches/1.4/CMFCore/dtml/cachingPolicies.dtml 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/dtml/cachingPolicies.dtml 2005-09-02 13:58:14 UTC (rev 38244)
@@ -82,6 +82,16 @@
</tr>
<tr valign="top">
+ <th align="right"> Vary </th>
+ <td colspan="3">
+ <input type="text"
+ name="vary"
+ value="&dtml-getVary;"
+ size="40">
+ </td>
+ </tr>
+
+ <tr valign="top">
<td><br /></td>
<td colspan="3">
<input type="submit" name="updatePolicy:method" value=" Change ">
@@ -89,6 +99,7 @@
<input type="submit" name="movePolicyUp:method" value=" Up ">
<input type="submit" name="movePolicyDown:method" value=" Down ">
</td>
+ </tr>
</table>
@@ -157,7 +168,7 @@
<input type="text" name="max_age_secs:int" value="0">
</td>
- <th align="right"> Most-revalidate? </th>
+ <th align="right"> Must-revalidate? </th>
<td>
<input type="checkbox" name="must_revalidate:int"
value="1">
@@ -165,6 +176,13 @@
</tr>
<tr valign="top">
+ <th align="right"> Vary </th>
+ <td colspan="3">
+ <input type="text" name="vary" size="40">
+ </td>
+ </tr>
+
+ <tr valign="top">
<td><br /></td>
<td>
<input type="submit" name="addPolicy:method" value=" Add ">
Modified: CMF/branches/1.4/CMFCore/tests/base/dummy.py
===================================================================
--- CMF/branches/1.4/CMFCore/tests/base/dummy.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/tests/base/dummy.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -216,7 +216,10 @@
def getRoles(self):
return ('Authenticated', 'Member')
+ def _check_context(self, object):
+ return 1
+
class DummyUserFolder(Implicit):
""" A dummy User Folder with 2 dummy Users.
"""
@@ -283,3 +286,14 @@
def notifyCreated(self, ob):
self.test_notified = ob
+
+class DummyCachingManager:
+
+ def getHTTPCachingHeaders( self, content, view_name, keywords, time=None ):
+ return (
+ ('foo', 'Foo'), ('bar', 'Bar'),
+ ('test_path', '/'.join(content.getPhysicalPath())),
+ )
+
+ def getPhysicalPath(self):
+ return ('baz',)
Added: CMF/branches/1.4/CMFCore/tests/fake_skins/fake_skin/testDTML.dtml
===================================================================
--- CMF/branches/1.4/CMFCore/tests/fake_skins/fake_skin/testDTML.dtml 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/tests/fake_skins/fake_skin/testDTML.dtml 2005-09-02 13:58:14 UTC (rev 38244)
@@ -0,0 +1 @@
+<dtml-var expr="REQUEST.get('SERVER_NAME')">
Property changes on: CMF/branches/1.4/CMFCore/tests/fake_skins/fake_skin/testDTML.dtml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: CMF/branches/1.4/CMFCore/tests/fake_skins/fake_skin/testDTML.dtml.metadata
===================================================================
--- CMF/branches/1.4/CMFCore/tests/fake_skins/fake_skin/testDTML.dtml.metadata 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/tests/fake_skins/fake_skin/testDTML.dtml.metadata 2005-09-02 13:58:14 UTC (rev 38244)
@@ -0,0 +1,2 @@
+[default]
+title=Zope Pope
Modified: CMF/branches/1.4/CMFCore/tests/test_CachingPolicyManager.py
===================================================================
--- CMF/branches/1.4/CMFCore/tests/test_CachingPolicyManager.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/tests/test_CachingPolicyManager.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -251,14 +251,14 @@
self.assertEqual( len( headers ), 0 )
self.assertRaises( KeyError, mgr._updatePolicy
- , 'xyzzy', None, None, None, None, None, None )
+ , 'xyzzy', None, None, None, None, None, None, '' )
self.assertRaises( KeyError, mgr._removePolicy, 'xyzzy' )
self.assertRaises( KeyError, mgr._reorderPolicy, 'xyzzy', -1 )
def test_addPolicy( self ):
mgr = self._makeOne()
- mgr._addPolicy( 'first', 'python:1', None, 0, 0, 0, 0 )
+ mgr._addPolicy( 'first', 'python:1', None, 0, 0, 0, 0, '' )
headers = mgr.getHTTPCachingHeaders( content=DummyContent(self._epoch)
, view_method='foo_view'
, keywords={}
@@ -283,7 +283,7 @@
for policy_id in policy_ids:
mgr._addPolicy( policy_id
, 'python:"%s" in keywords.keys()' % policy_id
- , None, 0, 0, 0, 0 )
+ , None, 0, 0, 0, 0, '' )
ids = tuple( map( lambda x: x[0], mgr.listPolicies() ) )
self.assertEqual( ids, policy_ids )
@@ -306,7 +306,7 @@
for policy_id, max_age_secs in policy_tuples:
mgr._addPolicy( policy_id
, 'python:"%s" in keywords.keys()' % policy_id
- , None, max_age_secs, 0, 0, 0 )
+ , None, max_age_secs, 0, 0, 0, '' )
return mgr
Modified: CMF/branches/1.4/CMFCore/tests/test_DirectoryView.py
===================================================================
--- CMF/branches/1.4/CMFCore/tests/test_DirectoryView.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/tests/test_DirectoryView.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -125,6 +125,31 @@
# Test that the .test1.py is ignored
assert('#test1' not in self.ob.fake_skin.objectIds())
+ def test_surrogate_writethrough(self):
+ # CMF Collector 316: It is possible to cause ZODB writes because
+ # setting attributes on the non-persistent surrogate writes them
+ # into the persistent DirectoryView as well. This is bad in situations
+ # where you only want to store markers and remove them before the
+ # transaction has ended - they never got removed because there was
+ # no equivalent __delattr__ on the surrogate that would clean up
+ # the persistent DirectoryView as well.
+ fs = self.ob.fake_skin
+ test_foo = 'My Foovalue'
+ fs.foo = test_foo
+
+ self.assertEqual(fs.foo, test_foo)
+ self.assertEqual(fs.__dict__['_real'].foo, test_foo)
+
+ del fs.foo
+
+ self.assertRaises(AttributeError, getattr, fs, 'foo')
+ self.assertRaises( AttributeError
+ , getattr
+ , fs.__dict__['_real']
+ , 'foo'
+ )
+
+
if DevelopmentMode:
class DebugModeTests( FSDVTest ):
Added: CMF/branches/1.4/CMFCore/tests/test_FSDTMLMethod.py
===================================================================
--- CMF/branches/1.4/CMFCore/tests/test_FSDTMLMethod.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/tests/test_FSDTMLMethod.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -0,0 +1,130 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors. 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.
+#
+##############################################################################
+""" Unit tests for FSDTMLMethod module.
+
+$Id$
+"""
+from unittest import TestSuite, makeSuite, main
+import Testing
+try:
+ import Zope2
+except ImportError: # BBB: for Zope 2.7
+ import Zope as Zope2
+Zope2.startup()
+
+from os.path import join as path_join
+
+from OFS.Folder import Folder
+from Products.PageTemplates.TALES import Undefined
+from Products.StandardCacheManagers import RAMCacheManager
+
+from Products.CMFCore.FSDTMLMethod import FSDTMLMethod
+from Products.CMFCore.FSMetadata import FSMetadata
+from Products.CMFCore.tests.base.dummy import DummyCachingManager
+from Products.CMFCore.tests.base.testcase import FSDVTest
+from Products.CMFCore.tests.base.testcase import RequestTest
+from Products.CMFCore.tests.base.testcase import SecurityTest
+
+
+class FSDTMLMaker(FSDVTest):
+
+ def _makeOne( self, id, filename ):
+ path = path_join(self.skin_path_name, filename)
+ metadata = FSMetadata(path)
+ metadata.read()
+ return FSDTMLMethod( id, path, properties=metadata.getProperties() )
+
+
+class FSDTMLMethodTests( RequestTest, FSDTMLMaker ):
+
+ def setUp(self):
+ FSDTMLMaker.setUp(self)
+ RequestTest.setUp(self)
+
+ def tearDown(self):
+ RequestTest.tearDown(self)
+ FSDTMLMaker.tearDown(self)
+
+ def test_Call( self ):
+ script = self._makeOne( 'testDTML', 'testDTML.dtml' )
+ script = script.__of__(self.root)
+ self.assertEqual(script(self.root, self.REQUEST), 'foo\n')
+
+ def test_caching( self ):
+ # Test HTTP caching headers.
+ self.root.caching_policy_manager = DummyCachingManager()
+ original_len = len( self.RESPONSE.headers )
+ script = self._makeOne('testDTML', 'testDTML.dtml')
+ script = script.__of__(self.root)
+ script(self.root, self.REQUEST, self.RESPONSE)
+ self.failUnless( len( self.RESPONSE.headers ) >= original_len + 2 )
+ self.failUnless( 'foo' in self.RESPONSE.headers.keys() )
+ self.failUnless( 'bar' in self.RESPONSE.headers.keys() )
+
+
+class FSDTMLMethodCustomizationTests( SecurityTest, FSDTMLMaker ):
+
+ def setUp( self ):
+ FSDTMLMaker.setUp(self)
+ SecurityTest.setUp( self )
+
+ self.root._setObject( 'portal_skins', Folder( 'portal_skins' ) )
+ self.skins = self.root.portal_skins
+
+ self.skins._setObject( 'custom', Folder( 'custom' ) )
+ self.custom = self.skins.custom
+
+ self.skins._setObject( 'fsdir', Folder( 'fsdir' ) )
+ self.fsdir = self.skins.fsdir
+
+ self.fsdir._setObject( 'testDTML'
+ , self._makeOne( 'testDTML', 'testDTML.dtml' ) )
+
+ self.fsDTML = self.fsdir.testDTML
+
+ def test_customize( self ):
+
+ self.fsDTML.manage_doCustomize( folder_path='custom' )
+
+ self.assertEqual( len( self.custom.objectIds() ), 1 )
+ self.failUnless( 'testDTML' in self.custom.objectIds() )
+
+ def test_customize_caching(self):
+ # Test to ensure that cache manager associations survive customizing
+ cache_id = 'gofast'
+ RAMCacheManager.manage_addRAMCacheManager( self.root
+ , cache_id
+ , REQUEST=None
+ )
+ self.fsDTML.ZCacheable_setManagerId(cache_id, REQUEST=None)
+
+ self.assertEqual(self.fsDTML.ZCacheable_getManagerId(), cache_id)
+
+ self.fsDTML.manage_doCustomize(folder_path='custom')
+ custom_pt = self.custom.testDTML
+
+ self.assertEqual(custom_pt.ZCacheable_getManagerId(), cache_id)
+
+ def tearDown(self):
+ SecurityTest.tearDown(self)
+ FSDTMLMaker.tearDown(self)
+
+
+def test_suite():
+ return TestSuite((
+ makeSuite(FSDTMLMethodTests),
+ makeSuite(FSDTMLMethodCustomizationTests),
+ ))
+
+if __name__ == '__main__':
+ main(defaultTest='test_suite')
Property changes on: CMF/branches/1.4/CMFCore/tests/test_FSDTMLMethod.py
___________________________________________________________________
Name: svn:keywords
+ Author Date Id Revision
Name: svn:eol-style
+ native
Modified: CMF/branches/1.4/CMFCore/tests/test_FSFile.py
===================================================================
--- CMF/branches/1.4/CMFCore/tests/test_FSFile.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/tests/test_FSFile.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -1,3 +1,19 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors. 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.
+#
+##############################################################################
+""" Unit tests for FSFile module.
+
+$Id$
+"""
import unittest
import Zope
import os.path
@@ -2,10 +18,4 @@
-class DummyCachingManager:
- def getHTTPCachingHeaders( self, content, view_name, keywords, time=None ):
- return (
- ('foo', 'Foo'), ('bar', 'Bar'),
- ('test_path', '/'.join(content.getPhysicalPath())),
- )
-
from Products.CMFCore.tests.base.testcase import RequestTest, FSDVTest
+from Products.CMFCore.tests.base.dummy import DummyCachingManager
@@ -99,6 +109,9 @@
data = file.index_html( self.REQUEST, self.RESPONSE )
self.assertEqual( data, '' )
+ # test that we don't supply a content-length
+ self.assertEqual( self.RESPONSE.getHeader('Content-Length'.lower()),
+ None )
self.assertEqual( self.RESPONSE.getStatus(), 304 )
Modified: CMF/branches/1.4/CMFCore/tests/test_FSImage.py
===================================================================
--- CMF/branches/1.4/CMFCore/tests/test_FSImage.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/tests/test_FSImage.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -1,3 +1,19 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors. 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.
+#
+##############################################################################
+""" Unit tests for FSImage module.
+
+$Id$
+"""
import unittest
import Zope
import os.path
@@ -2,10 +18,4 @@
-class DummyCachingManager:
- def getHTTPCachingHeaders( self, content, view_name, keywords, time=None ):
- return (
- ('foo', 'Foo'), ('bar', 'Bar'),
- ('test_path', '/'.join(content.getPhysicalPath())),
- )
-
from Products.CMFCore.tests.base.testcase import RequestTest, FSDVTest
+from Products.CMFCore.tests.base.dummy import DummyCachingManager
@@ -93,6 +103,9 @@
data = image.index_html( self.REQUEST, self.RESPONSE )
self.assertEqual( data, '' )
+ # test that we don't supply a content-length
+ self.assertEqual( self.RESPONSE.getHeader('Content-Length'.lower()),
+ None )
self.assertEqual( self.RESPONSE.getStatus(), 304 )
@@ -157,6 +170,35 @@
self.failUnless('bar' in headers.keys())
self.assertEqual(headers['test_path'], '/test_image')
+ def test_index_html_with_304_and_caching( self ):
+
+ # See collector #355
+ self.root.caching_policy_manager = DummyCachingManager()
+ original_len = len(self.RESPONSE.headers)
+ path, ref = self._extractFile()
+
+ import os
+ from webdav.common import rfc1123_date
+
+ mod_time = os.stat( path )[ 8 ]
+
+ image = self._makeOne( 'test_image', 'test_image.gif' )
+ image = image.__of__( self.root )
+
+ self.REQUEST.environ[ 'IF_MODIFIED_SINCE'
+ ] = '%s;' % rfc1123_date( mod_time+3600 )
+
+ data = image.index_html( self.REQUEST, self.RESPONSE )
+
+ self.assertEqual( data, '' )
+ self.assertEqual( self.RESPONSE.getStatus(), 304 )
+
+ headers = self.RESPONSE.headers
+ self.failUnless(len(headers) >= original_len + 3)
+ self.failUnless('foo' in headers.keys())
+ self.failUnless('bar' in headers.keys())
+ self.assertEqual(headers['test_path'], '/test_image')
+
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(FSImageTests),
Modified: CMF/branches/1.4/CMFCore/tests/test_FSPageTemplate.py
===================================================================
--- CMF/branches/1.4/CMFCore/tests/test_FSPageTemplate.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/tests/test_FSPageTemplate.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -4,11 +4,9 @@
from Products.CMFCore.tests.base.testcase import RequestTest
from Products.CMFCore.tests.base.testcase import SecurityTest
from Products.CMFCore.tests.base.testcase import FSDVTest
+from Products.StandardCacheManagers import RAMCacheManager
+from Products.CMFCore.tests.base.dummy import DummyCachingManager
-class DummyCachingManager:
- def getHTTPCachingHeaders( self, content, view_name, keywords, time=None ):
- return ( ( 'foo', 'Foo' ), ( 'bar', 'Bar' ) )
-
class FSPTMaker(FSDVTest):
def _makeOne( self, id, filename ):
@@ -110,6 +108,23 @@
self.assertEqual( len( self.custom.objectIds() ), 1 )
self.failUnless( 'testPT' in self.custom.objectIds() )
+ def test_customize_caching(self):
+ # Test to ensure that cache manager associations survive customizing
+ cache_id = 'gofast'
+ RAMCacheManager.manage_addRAMCacheManager( self.root
+ , cache_id
+ , REQUEST=None
+ )
+ self.fsPT.ZCacheable_setManagerId(cache_id, REQUEST=None)
+
+ self.assertEqual(self.fsPT.ZCacheable_getManagerId(), cache_id)
+
+ self.fsPT.manage_doCustomize(folder_path='custom')
+ custom_pt = self.custom.testPT
+
+ self.assertEqual(custom_pt.ZCacheable_getManagerId(), cache_id)
+
+
def test_dontExpandOnCreation( self ):
self.fsPT.manage_doCustomize( folder_path='custom' )
Added: CMF/branches/1.4/CMFCore/tests/test_utils.py
===================================================================
--- CMF/branches/1.4/CMFCore/tests/test_utils.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/tests/test_utils.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -0,0 +1,188 @@
+from unittest import TestSuite, makeSuite, main
+
+import Testing
+import Zope
+try:
+ Zope.startup()
+except AttributeError:
+ # for Zope versions before 2.6.1
+ pass
+
+from AccessControl import getSecurityManager
+from AccessControl.Owned import Owned
+from AccessControl.Permission import Permission
+
+from Products.CMFCore.tests.base.dummy import DummyObject
+from Products.CMFCore.tests.base.dummy import DummySite
+from Products.CMFCore.tests.base.dummy import DummyUserFolder
+from Products.CMFCore.tests.base.testcase import SecurityTest
+from Products.CMFCore.utils import _checkPermission
+
+
+class DummyObject(Owned, DummyObject):
+ pass
+
+
+class CoreUtilsTests(SecurityTest):
+
+ def setUp(self):
+ SecurityTest.setUp(self)
+ self.site = DummySite('site').__of__(self.root)
+ self.site._setObject( 'acl_users', DummyUserFolder() )
+ self.site._setObject('content_dummy', DummyObject(id='content_dummy'))
+ self.site._setObject('actions_dummy', DummyObject(id='actions_dummy'))
+
+ def test__checkPermission(self):
+ o = self.site.actions_dummy
+ Permission('View',(),o).setRoles(('Anonymous',))
+ Permission('WebDAV access',(),o).setRoles(('Authenticated',))
+ Permission('Manage users',(),o).setRoles(('Manager',))
+ eo = self.site.content_dummy
+ eo._owner = (['acl_users'], 'user_foo')
+ getSecurityManager().addContext(eo)
+ self.failUnless( _checkPermission('View', o) )
+ self.failIf( _checkPermission('WebDAV access', o) )
+ self.failIf( _checkPermission('Manage users', o) )
+
+ eo._proxy_roles = ('Authenticated',)
+ self.failIf( _checkPermission('View', o) )
+ self.failUnless( _checkPermission('WebDAV access', o) )
+ self.failIf( _checkPermission('Manage users', o) )
+
+
+def test_suite():
+ return TestSuite((
+ makeSuite(CoreUtilsTests),
+ ))
+
+if __name__ == '__main__':
+ main(defaultTest='test_suite')
+from unittest import TestSuite, makeSuite, main
+import Testing
+try:
+ import Zope2
+except ImportError: # BBB: for Zope 2.7
+ import Zope as Zope2
+Zope2.startup()
+
+from Products.CMFCore.tests.base.testcase import SecurityTest
+
+class CoreUtilsTests(SecurityTest):
+
+ def _makeSite(self):
+ from AccessControl.Owned import Owned
+ from Products.CMFCore.tests.base.dummy import DummySite
+ from Products.CMFCore.tests.base.dummy import DummyUserFolder
+ from Products.CMFCore.tests.base.dummy import DummyObject
+
+ class _DummyObject(Owned, DummyObject):
+ pass
+
+ site = DummySite('site').__of__(self.root)
+ site._setObject( 'acl_users', DummyUserFolder() )
+ site._setObject('content_dummy', _DummyObject(id='content_dummy'))
+ site._setObject('actions_dummy', _DummyObject(id='actions_dummy'))
+
+ return site
+
+ def test__checkPermission(self):
+ from AccessControl import getSecurityManager
+ from AccessControl.Permission import Permission
+ from Products.CMFCore.utils import _checkPermission
+
+ site = self._makeSite()
+ o = site.actions_dummy
+ Permission('View',(),o).setRoles(('Anonymous',))
+ Permission('WebDAV access',(),o).setRoles(('Authenticated',))
+ Permission('Manage users',(),o).setRoles(('Manager',))
+ eo = site.content_dummy
+ eo._owner = (['acl_users'], 'user_foo')
+ getSecurityManager().addContext(eo)
+ self.failUnless( _checkPermission('View', o) )
+ self.failIf( _checkPermission('WebDAV access', o) )
+ self.failIf( _checkPermission('Manage users', o) )
+
+ eo._proxy_roles = ('Authenticated',)
+ self.failIf( _checkPermission('View', o) )
+ self.failUnless( _checkPermission('WebDAV access', o) )
+ self.failIf( _checkPermission('Manage users', o) )
+
+ def test_normalize(self):
+ from Products.CMFCore.utils import normalize
+
+ self.assertEqual( normalize('foo/bar'), 'foo/bar' )
+ self.assertEqual( normalize('foo\\bar'), 'foo/bar' )
+
+ def test_keywordsplitter_empty(self):
+ from Products.CMFCore.utils import keywordsplitter
+
+ for x in [ '', ' ', ',', ',,', ';', ';;' ]:
+ self.assertEqual( keywordsplitter({'Keywords': x}),
+ [] )
+
+ def test_keywordsplitter_single(self):
+ from Products.CMFCore.utils import keywordsplitter
+
+ for x in [ 'foo', ' foo ', 'foo,', 'foo ,,', 'foo;', 'foo ;;' ]:
+ self.assertEqual( keywordsplitter({'Keywords': x}),
+ ['foo'] )
+
+ def test_keywordsplitter_multi(self):
+ from Products.CMFCore.utils import keywordsplitter
+
+ for x in [ 'foo, bar, baz'
+ , 'foo, bar , baz'
+ , 'foo, bar,, baz'
+ , 'foo; bar; baz'
+ ]:
+ self.assertEqual( keywordsplitter({'Keywords': x}),
+ ['foo', 'bar', 'baz'] )
+
+ def test_contributorsplitter_emtpy(self):
+ from Products.CMFCore.utils import contributorsplitter
+
+ for x in [ '', ' ', ';', ';;' ]:
+ self.assertEqual( contributorsplitter({'Contributors': x}),
+ [] )
+
+ def test_contributorsplitter_single(self):
+ from Products.CMFCore.utils import contributorsplitter
+
+ for x in [ 'foo', ' foo ', 'foo;', 'foo ;;' ]:
+ self.assertEqual( contributorsplitter({'Contributors': x}),
+ ['foo'] )
+
+ def test_contributorsplitter_multi(self):
+ from Products.CMFCore.utils import contributorsplitter
+
+ for x in [ 'foo; bar; baz'
+ , 'foo; bar ; baz'
+ , 'foo; bar;; baz'
+ ]:
+ self.assertEqual( contributorsplitter({'Contributors': x}),
+ ['foo', 'bar', 'baz'] )
+
+ def test_mergedLocalRolesManipulation(self):
+ # The _mergedLocalRoles function used to return references to
+ # actual local role settings and it was possible to manipulate them
+ # by changing the return value. http://www.zope.org/Collectors/CMF/376
+ from Products.CMFCore.tests.base.dummy import DummyContent
+ from Products.CMFCore.utils import _mergedLocalRoles
+ obj = DummyContent()
+ obj.manage_addLocalRoles('dummyuser1', ['Manager', 'Owner'])
+ self.assertEqual(len(obj.get_local_roles_for_userid('dummyuser1')), 2)
+
+ merged_roles = _mergedLocalRoles(obj)
+ merged_roles['dummyuser1'].append('FOO')
+
+ # The values on the object itself should still the the same
+ self.assertEqual(len(obj.get_local_roles_for_userid('dummyuser1')), 2)
+
+
+def test_suite():
+ return TestSuite((
+ makeSuite(CoreUtilsTests),
+ ))
+
+if __name__ == '__main__':
+ main(defaultTest='test_suite')
Property changes on: CMF/branches/1.4/CMFCore/tests/test_utils.py
___________________________________________________________________
Name: svn:keywords
+ Author Date Id Revision
Name: svn:eol-style
+ native
Modified: CMF/branches/1.4/CMFCore/utils.py
===================================================================
--- CMF/branches/1.4/CMFCore/utils.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFCore/utils.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -15,7 +15,8 @@
from os import path as os_path
import re
import operator
-from types import StringType
+from types import StringType, UnicodeType
+from copy import deepcopy
from Globals import package_home
from Globals import HTMLFile
@@ -25,7 +26,7 @@
from ExtensionClass import Base
from Acquisition import Implicit
-from Acquisition import aq_get, aq_inner, aq_parent
+from Acquisition import aq_base, aq_get, aq_inner, aq_parent
from AccessControl import ClassSecurityInfo
from AccessControl import ModuleSecurityInfo
@@ -114,12 +115,31 @@
security.declarePrivate('_checkPermission')
def _checkPermission(permission, obj, StringType = type('')):
roles = rolesForPermissionOn(permission, obj)
- if type(roles) is StringType:
+ if type(roles) in (StringType, UnicodeType):
roles=[roles]
- if _getAuthenticatedUser( obj ).allowed( obj, roles ):
- return 1
- return 0
+ context = getSecurityManager()._context
+ # check executable owner and proxy roles
+ # this code is ported from ZopeSecurityPolicy.validate
+ stack = context.stack
+ if stack:
+ eo = stack[-1]
+ owner = eo.getOwner()
+ if owner is not None:
+ if not owner.allowed(obj, roles):
+ return 0
+ proxy_roles = getattr(eo, '_proxy_roles', None)
+ if proxy_roles:
+ if obj is not aq_base(obj):
+ if not owner._check_context(obj):
+ return 0
+ for r in proxy_roles:
+ if r in roles:
+ return 1
+ return 0
+
+ return context.user.allowed(obj, roles)
+
security.declarePrivate('_verifyActionPermissions')
def _verifyActionPermissions(obj, action):
pp = action.getPermissions()
@@ -225,8 +245,9 @@
object=getattr(object, 'aq_inner', object)
continue
break
- return merged
+ return deepcopy(merged)
+
mergedLocalRoles = _mergedLocalRoles # XXX: Deprecated spelling
security.declarePrivate('_ac_inherited_permissions')
@@ -301,6 +322,10 @@
RESPONSE = REQUEST['RESPONSE']
for key, value in headers:
RESPONSE.setHeader(key, value)
+ if headers:
+ RESPONSE.setHeader('X-Cache-Headers-Set-By',
+ 'CachingPolicyManager: %s' %
+ '/'.join(manager.getPhysicalPath()))
class _ViewEmulator(Implicit):
"""Auxiliary class used to adapt FSFile and FSImage
Modified: CMF/branches/1.4/CMFDefault/DiscussionItem.py
===================================================================
--- CMF/branches/1.4/CMFDefault/DiscussionItem.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFDefault/DiscussionItem.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -288,7 +288,7 @@
# Discussable interface
#
security.declareProtected(ReplyToItem, 'createReply')
- def createReply( self, title, text, Creator=None ):
+ def createReply( self, title, text, Creator=None, text_format='structured-text' ):
"""
Create a reply in the proper place
"""
@@ -297,21 +297,21 @@
id = int(DateTime().timeTime())
while self._container.get( str(id), None ) is not None:
id = id + 1
+
id = str( id )
item = DiscussionItem( id, title=title, description=title )
- item._edit( text_format='structured-text', text=text )
+ self._container[id] = item
+ item = item.__of__(self)
+ item._edit( text_format=text_format, text=text )
if Creator:
item.creator = Creator
+ item.indexObject()
- item.__of__( self ).indexObject()
-
item.setReplyTo( self._getDiscussable() )
- item.__of__( self ).notifyWorkflowCreated()
+ item.notifyWorkflowCreated()
- self._container[ id ] = item
-
return id
security.declareProtected(ManagePortal, 'deleteReply')
Modified: CMF/branches/1.4/CMFDefault/File.py
===================================================================
--- CMF/branches/1.4/CMFDefault/File.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFDefault/File.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -147,12 +147,20 @@
, contributors=()
, effective_date=None
, expiration_date=None
- , format='text/html'
+ , format=None
, language='en-US'
, rights=''
):
OFS.Image.File.__init__( self, id, title, file
, content_type, precondition )
+
+ # If no file format has been passed in, rely on what OFS.Image.File
+ # detected. Unlike Images, which have code to try and pick the content
+ # type out of the binary data, File objects only provide the correct
+ # type if a "hint" in the form of a filename extension is given.
+ if format is None:
+ format = self.content_type
+
DefaultDublinCoreImpl.__init__( self, title, subject, description
, contributors, effective_date, expiration_date
, format, language, rights )
Modified: CMF/branches/1.4/CMFDefault/Image.py
===================================================================
--- CMF/branches/1.4/CMFDefault/Image.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFDefault/Image.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -139,12 +139,18 @@
, contributors=()
, effective_date=None
, expiration_date=None
- , format='image/png'
+ , format=None
, language='en-US'
, rights=''
):
OFS.Image.File.__init__( self, id, title, file
, content_type, precondition )
+
+ # If no file format has been passed in, rely on what OFS.Image.File
+ # detected.
+ if format is None:
+ format = self.content_type
+
DefaultDublinCoreImpl.__init__( self, title, subject, description
, contributors, effective_date, expiration_date
, format, language, rights )
Modified: CMF/branches/1.4/CMFDefault/SkinnedFolder.py
===================================================================
--- CMF/branches/1.4/CMFDefault/SkinnedFolder.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFDefault/SkinnedFolder.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -75,7 +75,7 @@
'''
view = _getViewFor(self)
if getattr(aq_base(view), 'isDocTemp', 0):
- return view(self, self.REQUEST)
+ return view(self, self.REQUEST, self.REQUEST['RESPONSE'])
else:
return view()
Modified: CMF/branches/1.4/CMFDefault/tests/test_Image.py
===================================================================
--- CMF/branches/1.4/CMFDefault/tests/test_Image.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFDefault/tests/test_Image.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -51,8 +51,28 @@
image.setFormat('image/gif')
self.assertEqual(image.Format(), 'image/gif')
self.assertEqual(image.content_type, 'image/gif')
-
+ def test_ImageContentTypeUponConstruction(self):
+ # Test the content type after calling the constructor with the
+ # file object being passed in (http://www.zope.org/Collectors/CMF/370)
+ testfile = open(TEST_JPG, 'rb')
+ image = Image('testimage', file=testfile)
+ testfile.close()
+ self.assertEqual(image.Format(), 'image/jpeg')
+ self.assertEqual(image.content_type, 'image/jpeg')
+
+ def test_FileContentTypeUponConstruction(self):
+ # Test the content type after calling the constructor with the
+ # file object being passed in (http://www.zope.org/Collectors/CMF/370)
+ testfile = open(TEST_JPG, 'rb')
+ # Notice the cheat? File objects lack the extra intelligence that
+ # picks content types from the actual file data, so it needs to be
+ # helped along with a file extension...
+ file = File('testfile.jpg', file=testfile)
+ testfile.close()
+ self.assertEqual(file.Format(), 'image/jpeg')
+ self.assertEqual(file.content_type, 'image/jpeg')
+
class TestImageCopyPaste(RequestTest):
# Tests related to http://www.zope.org/Collectors/CMF/176
Modified: CMF/branches/1.4/CMFTopic/Topic.py
===================================================================
--- CMF/branches/1.4/CMFTopic/Topic.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/CMFTopic/Topic.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -117,7 +117,7 @@
"""
view = _getViewFor( self )
if getattr( aq_base( view ), 'isDocTemp', 0 ):
- return view(self, self.REQUEST)
+ return view(self, self.REQUEST, self.REQUEST['RESPONSE'])
else:
return view()
Modified: CMF/branches/1.4/DCWorkflow/Guard.py
===================================================================
--- CMF/branches/1.4/DCWorkflow/Guard.py 2005-09-02 13:40:29 UTC (rev 38243)
+++ CMF/branches/1.4/DCWorkflow/Guard.py 2005-09-02 13:58:14 UTC (rev 38244)
@@ -24,6 +24,7 @@
from Acquisition import Explicit
from Products.CMFCore.CMFCorePermissions import ManagePortal
+from Products.CMFCore.utils import _checkPermission
from Expression import Expression, StateChangeInfo, createExprContext
from utils import _dtmldir
@@ -47,7 +48,7 @@
if pp:
found = 0
for p in pp:
- if sm.checkPermission(p, ob):
+ if _checkPermission(p, ob):
found = 1
break
if not found:
@@ -102,7 +103,7 @@
res.append('<br/>')
res.append('Requires expr:')
res.append('<code>' + escape(self.expr.text) + '</code>')
- return join(res, ' ')
+ return ' '.join(res)
def changeFromProperties(self, props):
'''
@@ -114,12 +115,12 @@
s = props.get('guard_permissions', None)
if s:
res = 1
- p = map(strip, split(s, ';'))
+ p = [ permission.strip() for permission in s.split(';') ]
self.permissions = tuple(p)
s = props.get('guard_roles', None)
if s:
res = 1
- r = map(strip, split(s, ';'))
+ p = [ role.strip() for role in s.split(';') ]
self.roles = tuple(r)
s = props.get('guard_expr', None)
if s:
@@ -131,13 +132,13 @@
def getPermissionsText(self):
if not self.permissions:
return ''
- return join(self.permissions, '; ')
+ return '; '.join(self.permissions)
security.declareProtected(ManagePortal, 'getRolesText')
def getRolesText(self):
if not self.roles:
return ''
- return join(self.roles, '; ')
+ return '; '.join(self.roles)
security.declareProtected(ManagePortal, 'getExprText')
def getExprText(self):
More information about the CMF-checkins
mailing list