Commit a76325fd authored by Evan Simpson's avatar Evan Simpson

Collector #809: Added and documented Traversable.py methods absolute_url_path...

Collector #809: Added and documented Traversable.py methods absolute_url_path and virtual_url_path, and reverted earlier change to absolute_url behaviour.
parent 27188f79
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
############################################################################## ##############################################################################
__doc__='''Application support __doc__='''Application support
$Id: Application.py,v 1.196 2003/11/28 16:45:25 jim Exp $''' $Id: Application.py,v 1.197 2003/12/10 17:52:46 evan Exp $'''
__version__='$Revision: 1.196 $'[11:-2] __version__='$Revision: 1.197 $'[11:-2]
import Globals,Folder,os,sys,App.Product, App.ProductRegistry, misc_ import Globals,Folder,os,sys,App.Product, App.ProductRegistry, misc_
import time, traceback, os, Products import time, traceback, os, Products
...@@ -137,21 +137,24 @@ class Application(Globals.ApplicationDefaultPermissions, ...@@ -137,21 +137,24 @@ class Application(Globals.ApplicationDefaultPermissions,
test_url=ZopeAttributionButton test_url=ZopeAttributionButton
def absolute_url(self, relative=0): def absolute_url(self, relative=0):
'''Return a canonical URL for this object based on its '''The absolute URL of the root object is BASE1 or "/".'''
physical containment path, possibly modified by virtual hosting. if relative: return ''
If the optional 'relative' argument is true, only return the
path portion of the URL.'''
try: try:
# We need a REQUEST that uses physicalPathToURL to create # Take advantage of computed URL cache
# BASE1 and BASEPATH1, so probe for it. return self.REQUEST['BASE1']
req = self.REQUEST except (AttributeError, KeyError):
req.physicalPathToURL return '/'
except AttributeError:
return '' def absolute_url_path(self):
# Take advantage of computed URL cache '''The absolute URL path of the root object is BASEPATH1 or "/".'''
if relative: try:
return req['BASEPATH1'][1:] return self.REQUEST['BASEPATH1']
return req['BASE1'] except (AttributeError, KeyError):
return '/'
def virtual_url_path(self):
'''The virtual URL path of the root object is empty.'''
return ''
def getPhysicalPath(self): def getPhysicalPath(self):
'''Returns a path that can be used to access this object again '''Returns a path that can be used to access this object again
......
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
############################################################################## ##############################################################################
'''This module implements a mix-in for traversable objects. '''This module implements a mix-in for traversable objects.
$Id: Traversable.py,v 1.22 2003/11/28 16:45:44 jim Exp $''' $Id: Traversable.py,v 1.23 2003/12/10 17:52:46 evan Exp $'''
__version__='$Revision: 1.22 $'[11:-2] __version__='$Revision: 1.23 $'[11:-2]
from Acquisition import Acquired, aq_inner, aq_parent, aq_base from Acquisition import Acquired, aq_inner, aq_parent, aq_base
...@@ -31,20 +31,63 @@ class Traversable: ...@@ -31,20 +31,63 @@ class Traversable:
absolute_url__roles__=None # Public absolute_url__roles__=None # Public
def absolute_url(self, relative=0): def absolute_url(self, relative=0):
'''Return a canonical URL for this object based on its """
physical containment path, possibly modified by virtual hosting. Return the absolute URL of the object.
If the optional 'relative' argument is true, only return the
path portion of the URL.''' This a canonical URL based on the object's physical
containment path. It is affected by the virtual host
configuration, if any, and can be used by external
agents, such as a browser, to address the object.
If the relative argument is provided, with a true value, then
the value of virtual_url_path() is returned.
Some Products incorrectly use '/'+absolute_url(1) as an
absolute-path reference. This breaks in certain virtual
hosting situations, and should be changed to use
absolute_url_path() instead.
"""
if relative:
return self.virtual_url_path()
spp = self.getPhysicalPath() spp = self.getPhysicalPath()
try: try:
toUrl = self.REQUEST.physicalPathToURL toUrl = self.REQUEST.physicalPathToURL
except AttributeError: except AttributeError:
return '/'.join(map(quote, spp[1:])) return path2url(spp[1:])
if relative:
# Remove leading slash for backward compatibility sake.
return toUrl(spp, relative)[1:]
return toUrl(spp) return toUrl(spp)
absolute_url_path__roles__=None # Public
def absolute_url_path(self):
"""
Return the path portion of the absolute URL of the object.
This includes the leading slash, and can be used as an
'absolute-path reference' as defined in RFC 2396.
"""
spp = self.getPhysicalPath()
try:
toUrl = self.REQUEST.physicalPathToURL
except AttributeError:
return path2url(spp) or '/'
return toUrl(spp, relative=1) or '/'
virtual_url_path__roles__=None # Public
def virtual_url_path(self):
"""
Return a URL for the object, relative to the site root.
If a virtual host is configured, the URL is a path relative to
the virtual host's root object. Otherwise, it is the physical
path. In either case, the URL does not begin with a slash.
"""
spp = self.getPhysicalPath()
try:
toVirt = self.REQUEST.physicalPathToVirtualPath
except AttributeError:
return path2url(spp[1:])
return path2url(toVirt(spp))
getPhysicalRoot__roles__=() # Private getPhysicalRoot__roles__=() # Private
getPhysicalRoot=Acquired getPhysicalRoot=Acquired
...@@ -159,4 +202,7 @@ class Traversable: ...@@ -159,4 +202,7 @@ class Traversable:
restrictedTraverse__roles__=None # Public restrictedTraverse__roles__=None # Public
def restrictedTraverse(self, path, default=_marker): def restrictedTraverse(self, path, default=_marker):
def path2url(path):
return '/'.join(map(quote, path))
return self.unrestrictedTraverse(path, default, restricted=1) return self.unrestrictedTraverse(path, default, restricted=1)
...@@ -94,12 +94,43 @@ class ObjectManagerItem: ...@@ -94,12 +94,43 @@ class ObjectManagerItem:
def absolute_url(relative=None): def absolute_url(relative=None):
""" """
Return the absolute url to the object. Return the absolute URL of the object.
If the relative argument is provided with a true value, then This a canonical URL based on the object's physical
the URL returned is relative to the site object. Note, if containment path. It is affected by the virtual host
virtual hosts are being used, then the path returned is a configuration, if any, and can be used by external
logical, rather than a physical path. agents, such as a browser, to address the object.
If the relative argument is provided, with a true value, then
the value of virtual_url_path() is returned.
Some Products incorrectly use '/'+absolute_url(1) as an
absolute-path reference. This breaks in certain virtual
hosting situations, and should be changed to use
absolute_url_path() instead.
Permission -- Always available
"""
def absolute_url_path():
"""
Return the path portion of the absolute URL of the object.
This includes the leading slash, and can be used as an
'absolute-path reference' as defined in RFC 2396.
Permission -- Always available
"""
def virtual_url_path():
"""
Return a URL for the object, relative to the site root.
If a virtual host is configured, the URL is a path relative to
the virtual host's root object. Otherwise, it is the physical
path. In either case, the URL does not begin with a slash.
Permission -- Always available Permission -- Always available
""" """
......
"""Virtual Host Monster regression tests.
These tests mainly verify that OFS.Traversable.absolute_url()
works correctly in a VHM environment.
Also see http://zope.org/Collectors/Zope/809
Note: Tests require Zope >= 2.7
$Id: testVirtualHostMonster.py,v 1.2 2003/12/10 17:52:47 evan Exp $
"""
from Testing.makerequest import makerequest
import Zope
Zope.startup()
import unittest
class VHMRegressions(unittest.TestCase):
def setUp(self):
get_transaction().begin()
self.app = makerequest(Zope.app())
try:
self.app.manage_addProduct['SiteAccess'].manage_addVirtualHostMonster('VHM')
self.app.manage_addFolder('folder')
self.app.folder.manage_addDTMLMethod('doc', '')
self.app.REQUEST.set('PARENTS', [self.app])
self.traverse = self.app.REQUEST.traverse
except:
self.tearDown()
def tearDown(self):
get_transaction().abort()
self.app._p_jar.close()
def testAbsoluteUrl(self):
m = self.app.folder.doc.absolute_url
self.assertEqual(m(), 'http://foo/folder/doc')
def testAbsoluteUrlPath(self):
m = self.app.folder.doc.absolute_url_path
self.assertEqual(m(), '/folder/doc')
def testVirtualUrlPath(self):
m = self.app.folder.doc.absolute_url
self.assertEqual(m(relative=1), 'folder/doc')
m = self.app.folder.doc.virtual_url_path
self.assertEqual(m(), 'folder/doc')
def testPhysicalPath(self):
m = self.app.folder.doc.getPhysicalPath
self.assertEqual(m(), ('', 'folder', 'doc'))
def gen_cases():
for vbase, ubase in (
('', 'http://foo'),
('/VirtualHostBase/http/example.com:80', 'http://example.com'),
):
yield vbase, '', '', 'folder/doc', ubase
for vr, _vh, p in (
('folder', '', 'doc'),
('folder', 'foo', 'doc'),
('', 'foo', 'folder/doc'),
):
vparts = [vbase, vr, 'VirtualHostRoot']
if not vr:
del vparts[1]
if _vh:
vparts.append('_vh_' + _vh)
yield '/'.join(vparts), vr, _vh, p, ubase
for i, (vaddr, vr, _vh, p, ubase) in enumerate(gen_cases()):
def test(self):
ob = self.traverse('%s/%s/' % (vaddr, p))
vhp = '/' + '/'.join([x for x in _vh, p if x])
self.assertEqual(ob.absolute_url(), ubase + vhp)
self.assertEqual(ob.absolute_url_path(), vhp)
self.assertEqual(ob.absolute_url(relative=1), p)
self.assertEqual(ob.virtual_url_path(), p)
self.assertEqual(ob.getPhysicalPath(), ('', 'folder', 'doc'))
app = ob.aq_parent.aq_parent
self.assertEqual(app.absolute_url(), ubase + '/' + _vh)
self.assertEqual(app.absolute_url_path(), '/' + _vh)
self.assertEqual(app.absolute_url(relative=1), '')
self.assertEqual(app.virtual_url_path(), '')
setattr(VHMRegressions, 'testTraverse%s' % i, test)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(VHMRegressions))
return suite
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
############################################################################## ##############################################################################
__version__='$Revision: 1.93 $'[11:-2] __version__='$Revision: 1.94 $'[11:-2]
import re, sys, os, urllib, time, random, cgi, codecs import re, sys, os, urllib, time, random, cgi, codecs
from types import StringType, UnicodeType from types import StringType, UnicodeType
...@@ -1149,7 +1149,7 @@ class HTTPRequest(BaseRequest): ...@@ -1149,7 +1149,7 @@ class HTTPRequest(BaseRequest):
v.insert(0, '') v.insert(0, '')
else: else:
v.insert(0, other['SERVER_URL']) v.insert(0, other['SERVER_URL'])
URL = '/'.join(v) URL = '/'.join(v) or '/'
if other.has_key('PUBLISHED'): if other.has_key('PUBLISHED'):
# Don't cache URLs until publishing traversal is done. # Don't cache URLs until publishing traversal is done.
other[key] = URL other[key] = URL
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment