Commit b60b2c5d authored by Evan Simpson's avatar Evan Simpson

Initial SiteAccess checkin

parent 2b117f88
"""AccessRule module
Provide a simple method to set up Access Rules
"""
from Globals import HTMLFile, MessageDialog
from ZPublisher.BeforeTraverse import \
registerBeforeTraverse, unregisterBeforeTraverse, queryBeforeTraverse, \
NameCaller
import os
SUPPRESS_ACCESSRULE = os.environ.has_key('SUPPRESS_ACCESSRULE')
class AccessRule(NameCaller):
meta_type = 'Set Access Rule'
def __call__(self, container, request):
if SUPPRESS_ACCESSRULE: return
if '_SUPPRESS_ACCESSRULE' in _swallow(request):
request.setVirtualRoot(request.steps)
return
NameCaller.__call__(self, container, request)
def _swallow(request):
path = request['TraversalRequestNameStack']
steps = request.steps
i = len(steps)
while i > 0 and steps[i - 1][:1] == '_':
i = i - 1
while path and path[-1][:1] == '_':
steps.append(path.pop())
return steps[i:]
def manage_addAccessRule(self, method_id=None, REQUEST=None, **ignored):
"""Point a __before_traverse__ entry at the specified method"""
# We want the original object, not stuff in between, and no acquisition
self = self.this()
self = getattr(self, 'aq_base', self)
priority = (1, 'AccessRule')
if method_id is None or (REQUEST and REQUEST.form.has_key('none')):
rules = unregisterBeforeTraverse(self, 'AccessRule')
if rules:
try: del getattr(self, rules[0].name).icon
except: pass
if REQUEST:
return MessageDialog(title='No Access Rule',
message='This object now has no Access Rule',
action='%s/manage_main' % REQUEST['URL1'])
elif method_id and hasattr(self, method_id):
rules = unregisterBeforeTraverse(self, 'AccessRule')
if rules:
try: del getattr(self, rules[0].name).icon
except: pass
hook = AccessRule(method_id)
registerBeforeTraverse(self, hook, 'AccessRule', 1)
try:
getattr(self, method_id).icon = 'misc_/SiteAccess/AccessRule.gif'
except: pass
if REQUEST:
return MessageDialog(title='Access Rule Set',
message='"%s" is now the Access Rule for this object'
% method_id,
action='%s/manage_main' % REQUEST['URL1'])
else:
if REQUEST:
return MessageDialog(title='Invalid Method Id',
message='"%s" is not the Id of a method of this object' % method_id,
action='%s/manage_main' % REQUEST['URL1'])
def getAccessRule(self, REQUEST=None):
"Return the name of the current AccessRule, if any"
self = self.this()
rules = queryBeforeTraverse(self, 'AccessRule')
if rules:
return rules[0][1].name
return ''
constructors = (
('manage_addAccessRuleForm', HTMLFile('www/AccessRuleAdd', globals())),
('manage_addAccessRule', manage_addAccessRule),
('manage_getAccessRule', getAccessRule),
)
2000-10-03 Evan Simpson <evan@digicool.com>
* Final release
* Added VirtualHostMonster, to make common virtual hosting
cases easy.
2000-07-20 Evan Simpson <evan@digicool.com>
* Beta 3
* Fixed bug with SiteRoot deletion
* Added ability to turn off SiteRoots and AccessRules on a case-by-case
basis, by adding _SUPPRESS_SITEROOT or _SUPPRESS_ACCESSRULE to a
URL just after the container name.
* Added crude beginnings of Help
2000-06-19 Evan Simpson <evan@digicool.com>
* Beta 2
* Removed a chunk of code left from 1.0 which messed up SiteRoots
2000-06-09 Evan Simpson <evan@digicool.com>
* Version 2.0.0
* Changed to use the new virtual hosting support in Zope 2.2
* INCOMPATIBLE with Zope versions prior to 2.2b1
* The tarball no longer includes 'lib/python/Products' in file
paths. It must be unpacked in the Products directory, for
better compatibility with INSTANCE_HOME installations.
2000-03-21 Evan Simpson <evan@4-am.com>
* Version 1.0.1
* Fix for FTP incompatibility
* Match changes in Zope 2.1.5/6
2000-01-18 Evan Simpson <evan@4-am.com>
* Version 1.0.0
* Decided that it's been long enough to call it stable
* Eliminated stale _v_absolute_url attributes (thanks
to Wade Simmons)
* Killed peculiar and obscure interaction with ZCatalog
1999-12-15 Evan Simpson <evan@4-am.com>
* Version 0.2.0
* Got absolute_url to do the right thing under both Zope 2.0 and 2.1.
Note that this will change 2.0's behavior to match 2.1
1999-11-19 Evan Simpson <evan@4-am.com>
* Added COPYRIGHT.txt, making Wide Open Source licence (BSD-style)
explicit. (Mike Goldman provided the text, I provided the silly name).
1999-11-18 Evan Simpson <evan@4-am.com>
* Version 0.1.4
* BASE tags generated for default pages should play nicely with
setURL(base=...)
* Setting the base but not the path works now.
* Added ./doc/ directory with copies of documentation from Zope.org
* SiteRoot* variables set in the environment are now seen.
1999-10-29 Evan Simpson <evan@4-am.com>
* Version 0.1.3
* Using DTML Docs/Methods as Access Rules should work better,
and allow normal acquisition. You won't have permissions for
anything unless you give the rule a Proxy Role, though.
* __no_before_traverse__ at the start of a URL path now persists
in generated URLs, making debugging easier.
1999-10-27 Evan Simpson <evan@4-am.com>
* Version 0.1.2
* Fixed absolute_url() of objects acquired from above the point
at which setURL was called (usually by a SiteRoot). (thanks again
to Bruce Perens and technocrat.net)
* Added Base and Path properties to SiteRoot, with Path defaulting
to '/', the most commonly used value. If these are blank, it
will search for SiteRootBASE and SiteRootPATH.
* REQUEST.setURL now accepts either a string or sequence of
strings for its path argument.
1999-10-24 Evan Simpson <evan@4-am.com>
* Version 0.1.1
* Made Access Rules work on unadorned root path (thanks to
Bruce Perens)
* Reorganized REQUEST.traverse to better handle changes
to the path within the loop.
* Experimental change: Overrides icon of methods designated
as Access Rules to provide visual feedback. This is a
fragile hack, but relatively harmless if it fails. (suggested
by Evan Gibson)
* Fixed Acquisition of SiteRoot* values (thanks to Oleg Broytmann)
* Rationalized file permissions (thanks to Joshua Brauer)
1999-10-19 Evan Simpson <evan@4-am.com>
* Version 0.1.0
* First (apparently) working version
* Implemented REQUEST.setURL(base, path) and made SiteRoot use it.
* Put a link to existing AccessRule on "Add AccessRule" page.
This software is released under the following Wide-Open Source licence:
Copyright (c) 1999 Evan Simpson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the Author nor the names of other contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
import Zope
from ZPublisher.BeforeTraverse import NameCaller, rewriteBeforeTraverse
from Products.SiteAccess.AccessRule import AccessRule
def updata(self):
"""Convert SiteAccess objects from 1.x to 2.x"""
_cvt_btr(self.REQUEST['PARENTS'][-1])
from Globals import MessageDialog
return MessageDialog(title='Update Complete', message='Update Complete!',
action='./manage_main')
def _cvt_btr(app):
stack = [app]
while stack:
o = stack.pop()
ov = getattr(o, 'objectValues', None)
if ov is not None:
stack.extend(list(ov()))
btr = getattr(o, '__before_traverse__', None)
if btr and type(btr) == type({}):
touched = 0
for k, v in btr.items():
if type(v) is type(''):
touched = 1
if k[1] == 'AccessRule':
btr[k] = AccessRule(v)
else:
btr[k] = NameCaller(v)
if touched:
rewriteBeforeTraverse(o, btr)
if __name__ == '__main__':
print "Converting SiteAccess objects from 1.x to 2.x ..."
app = Zope.app()
_cvt_btr(app)
get_transaction().commit()
print "Done."
SiteAccess
The SiteAccess product provides support for pluggable pre-
traversal methods, and supplies objects and methods which
can be used to rewrite access URLs at the start of resolution
and as folders are traversed.
The main intent is to provide for multi-site hosting in Zope.
More information is available at http://zope.org/Members/4am/SiteAccess
"""SiteRoot module
Defines the Traverser base class and SiteRoot class
"""
from Globals import HTMLFile, MessageDialog, Persistent
from OFS.SimpleItem import Item
from Acquisition import Implicit, ImplicitAcquisitionWrapper
from ExtensionClass import Base
from string import split, strip
from ZPublisher import BeforeTraverse
import os
from AccessRule import _swallow
SUPPRESS_SITEROOT = os.environ.has_key('SUPPRESS_SITEROOT')
class Traverser(Persistent, Item):
"""Class for overriding container's __before_traverse__
Containers are expected to have at most one instance of any particular
subclass, with Id equal to the meta_type of the subclass."""
meta_type='Traverser'
priority = 100
__ac_permissions__=()
def addToContainer(self, container):
container._setObject(self.id, self)
self.manage_afterAdd(self, container)
def manage_addToContainer(self, container, nextURL=''):
if nextURL:
if hasattr(getattr(container, 'aq_base', container), self.id):
return MessageDialog(title='Item Exists',
message='This object already contains a %s' % self.meta_type,
action=nextURL)
self.addToContainer(container)
if nextURL:
return MessageDialog(title='Item Added',
message='This object now has a %s' % self.meta_type,
action=nextURL)
def manage_beforeDelete(self, item, container):
if item is self:
BeforeTraverse.unregisterBeforeTraverse(container, self.meta_type)
def manage_afterAdd(self, item, container):
if item is self:
id = self.id
if callable(id): id = id()
# We want the original object, not stuff in between
container = container.this()
hook = BeforeTraverse.NameCaller(id)
BeforeTraverse.registerBeforeTraverse(container, hook,
self.meta_type,
self.priority)
def _setId(self, id):
if id != self.id:
raise Globals.MessageDialog(
title='Invalid Id',
message='Cannot change the id of a %s' % self.meta_type,
action ='./manage_main',)
class SiteRoot(Traverser, Implicit):
"""SiteAccess.SiteRoot object
A SiteRoot causes traversal of its container to replace the part
of the Request path traversed so far with the request's SiteRootURL."""
id = meta_type = 'SiteRoot'
title = ''
priority = 50
manage_options=({'label':'Edit', 'action':'manage_main', 'help': ('SiteAccess', 'SiteRoot_Edit.stx')},)
manage = manage_main = HTMLFile('www/SiteRootEdit', globals())
def __init__(self, title, base, path):
'''Title'''
self.title = strip(title)
self.base = base = strip(base)
self.path = path = strip(path)
if base: self.SiteRootBASE = base
else:
try: del self.SiteRootBASE
except: pass
if path: self.SiteRootPATH = path
else:
try: del self.SiteRootPATH
except: pass
def manage_edit(self, title, base, path, REQUEST=None):
'''Set the title, base, and path'''
self.__init__(title, base, path)
if REQUEST:
return MessageDialog(title='SiteRoot changed.',
message='The title is now "%s"<br>'
'The base is now "%s"<br>'
'The path is now "%s"<br>' % (title, base, path),
action='%s/manage_main' % REQUEST['URL1'])
def __call__(self, client, request, response=None):
'''Traversing'''
if SUPPRESS_SITEROOT: return
if '_SUPPRESS_SITEROOT' in _swallow(request):
request.setVirtualRoot(request.steps)
return
srd = [None, None]
for i in (0, 1):
srp = ('SiteRootBASE', 'SiteRootPATH')[i]
try:
srd[i] = getattr(self, srp)
except AttributeError:
srd[i] = request.get(srp, None)
if srd[i] is None:
srd[i] = request.environ.get(srp, None)
if srd[0] is not None:
request['SERVER_URL'] = srd[0]
request._resetURLS()
if srd[1] is not None:
request.setVirtualRoot(srd[1])
def get_size(self):
'''Make FTP happy'''
return 0
def manage_addSiteRoot(self, title='', base='', path='', REQUEST=None,
**ignored):
""" """
sr=SiteRoot(title, base, path)
if REQUEST:
return sr.manage_addToContainer(self.this(),
'%s/manage_main' % REQUEST['URL1'])
else:
sr.manage_addToContainer(self.this())
constructors = (
('manage_addSiteRootForm', HTMLFile('www/SiteRootAdd', globals())),
('manage_addSiteRoot', manage_addSiteRoot),
)
"""VirtualHostMonster module
Defines the VirtualHostMonster class
"""
from Globals import HTMLFile, MessageDialog, Persistent
from OFS.SimpleItem import Item
from Acquisition import Implicit, ImplicitAcquisitionWrapper
from ExtensionClass import Base
from string import split, strip
from ZPublisher import BeforeTraverse
import os
class VirtualHostMonster(Persistent, Item, Implicit):
"""Provide a simple drop-in solution for virtual hosting.
"""
meta_type='Virtual Host Monster'
priority = 25
title = ''
__ac_permissions__=(('View', ('manage_main',)),)
manage_options=({'label':'View', 'action':'manage_main'},)
manage_main = HTMLFile('www/VirtualHostMonster', globals())
def addToContainer(self, container):
container._setObject(self.id, self)
self.manage_afterAdd(self, container)
def manage_addToContainer(self, container, nextURL=''):
self.addToContainer(container)
if nextURL:
return MessageDialog(title='Item Added',
message='This object now has a %s' % self.meta_type,
action=nextURL)
def manage_beforeDelete(self, item, container):
if item is self:
BeforeTraverse.unregisterBeforeTraverse(container, self.meta_type)
def manage_afterAdd(self, item, container):
if item is self:
id = self.id
if callable(id): id = id()
# We want the original object, not stuff in between
container = container.this()
hook = BeforeTraverse.NameCaller(id)
BeforeTraverse.registerBeforeTraverse(container, hook,
self.meta_type,
self.priority)
def _setId(self, id):
id = str(id)
if id != self.id:
BeforeTraverse.unregisterBeforeTraverse(container,
self.meta_type)
hook = BeforeTraverse.NameCaller(id)
BeforeTraverse.registerBeforeTraverse(container, hook,
self.meta_type,
self.priority)
def __call__(self, client, request, response=None):
'''Traversing at home'''
stack = request['TraversalRequestNameStack']
if stack and stack[-1] == 'VirtualHostBase':
stack.pop()
protocol = stack.pop()
host = stack.pop()
if ':' in host:
host, port = split(host, ':')
request.setServerURL(protocol, host, port)
else:
request.setServerURL(protocol, host)
#request.setVirtualRoot([])
for ii in range(len(stack)):
if stack[ii] == 'VirtualHostRoot':
stack[ii] = self.id
break
def __bobo_traverse__(self, request, name):
'''Traversing away'''
if name in ('manage_main', 'manage_workspace'):
return self.manage_main
request.setVirtualRoot([])
stack = request['TraversalRequestNameStack']
stack.append(name)
parents = request.PARENTS
parents.pop() # I don't belong there
return parents.pop() # He'll get put back on
def manage_addVirtualHostMonster(self, id, REQUEST=None, **ignored):
""" """
vhm = VirtualHostMonster()
vhm.id = str(id)
if REQUEST:
return vhm.manage_addToContainer(self.this(),
'%s/manage_main' % REQUEST['URL1'])
else:
vhm.addToContainer(self.this())
constructors = (
('manage_addVirtualHostMonsterForm', HTMLFile('www/VirtualHostMonsterAdd', globals())),
('manage_addVirtualHostMonster', manage_addVirtualHostMonster),
)
__doc__="""SiteAccess product"""
import SiteRoot, AccessRule, VirtualHostMonster
def initialize(context):
context.registerClass(instance_class=SiteRoot.SiteRoot,
constructors=SiteRoot.constructors, legacy=SiteRoot.constructors,
icon='www/SiteRoot.gif')
context.registerClass(instance_class=AccessRule.AccessRule,
permission='Set Access Rule', constructors=AccessRule.constructors,
icon='www/AccessRule.gif')
context.registerClass(instance_class=VirtualHostMonster.VirtualHostMonster,
permission='Create Virtual Host Monster',
constructors=VirtualHostMonster.constructors,
icon='www/VirtualHostMonster.gif')
context.registerHelp()
<dtml-comment>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD> <TITLE>All about SiteAccess</TITLE> </HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#000066" VLINK="#606060" TOPMARGIN="0" LEFTMARGIN="0" MARGINWIDTH="0" MARGINHEIGHT="0">
</dtml-comment>
<dtml-var doc_header>
<h4>Basics</h4>
<p>
SiteAccess started as a Product to enable Zope Virtual Hosting, then became more
generalized. Virtual Hosting is the ability to publish sub-objects of a Zope hierarchy
as though they were the root of their own site. For example, it allows
'http:/www.silly-walks.org/current' to publish the Zope object 'silly-walks/current'
instead of just 'current'.
</p>
<h4>Usage</h4>
See:<ul>
<li><a href="http://www.zope.org/Members/4am/SiteAccess2/vhosting">Virtual Site Hosting</a></li>
<li><a href="http://www.zope.org/Members/4am/SiteAccess2/otheruse">Other Uses of Access Rules</a></li>
</ul>
<h4>What does SiteAccess do?</h4>
<p>
SiteAccess provides the ability to force ZPublisher to call objects
of your choice as it enters any folder. With this capability, you can designate a method
in the Zope root to examine the request parameters and alter or replace the URL.
</p>
<p>If an Access Rule is broken, and is preventing normal access, it can be disabled by
restarting Zope with environment variable SUPPRESS_ACCESSRULE set.</p>
<dtml-var doc_footer>
<dtml-comment>
</BODY></HTML>
</dtml-comment>
<dtml-comment>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD> <TITLE>Installing SiteAccess</TITLE> </HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#000066" VLINK="#606060" TOPMARGIN="0" LEFTMARGIN="0" MARGINWIDTH="0" MARGINHEIGHT="0">
</dtml-comment>
<dtml-var doc_header>
<h2>Installing SiteAccess</h2>
<ul>
<li>Read <a href="info">about it</a></li>
<li>Download <a href="/Members/4am/SiteAccess2/SiteAccess-2.0.0-nonbin.tgz">the tarball</a> and unpack it in your Products dir.</li>
<li>Restart Zope.</li>
</ul>
<p>Please let <a href="mailto:evan@4-am.com">me</a> know what you think.</p>
<dtml-var doc_footer>
<dtml-comment>
</BODY></HTML>
</dtml-comment>
<dtml-comment>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD> <TITLE>Other Uses of Access Rules</TITLE> </HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#000066" VLINK="#606060" TOPMARGIN="0" LEFTMARGIN="0" MARGINWIDTH="0" MARGINHEIGHT="0">
</dtml-comment>
<dtml-var doc_header>
<h4>Embedded Session Values</h4>
Sometimes it would be nice to be able to embed a variable/value in the middle of a URL,
rather than having to tack a query string onto the end. It can be essential, such as
when you want parameterized pages to be spiderable (spiders don't like query strings).
<br><br>
One way to accomplish this is with an Access Rule. For example, suppose we created a
Zope folder called 'Session', containing the following DTML Method Access Rule:
<pre>
&lt;dtml-let stack=&quot;REQUEST['TraversalRequestNameStack']&quot;&gt;
Don't intercept management requests
&lt;dtml-unless &quot;stack[0][:6]=='manage'&quot;&gt;
Is the next path segment a positive integer?
&lt;dtml-if &quot;_.int(stack[-1])&gt;0&quot;&gt;
Save it and remove it from the path
&lt;dtml-call &quot;REQUEST.set('SessionID', stack.pop())&quot;&gt;
Add it back into the logical path
&lt;dtml-call &quot;REQUEST.setVirtualRoot(REQUEST.steps+[SessionID])&quot;&gt;
&lt;dtml-else&gt;
&lt;dtml-raise type=&quot;Invalid&quot;&gt;Invalid Session ID!&lt;/dtml-raise&gt;
&lt;/dtml-if&gt;
&lt;/dtml-unless&gt;
&lt;/dtml-let&gt;
</pre>
Then the request URI 'http://www.mysite.com/foo/Session/84076/step3' will publish the
Zope object at '/foo/Session/step3', with variable 'SessionID' set to '84076'. Thanks
to acquisition, 'step3' doesn't need to be in 'Session'. 'Session' may be empty
except for the Access Rule, or it may contain session-management objects.
<br><br>
When writing this kind of Access Rule, it is useful to remember the following:
<ul>
<li>REQUEST['TraversalRequestNameStack'] is the stack of Ids yet to be traversed.</li>
<li>REQUEST.steps is the list of Ids already traversed.</li>
<li>You can manipulate the path stack with append, insert, pop, and similar list operations.</li>
<li>You should not manipulate 'steps', instead using REQUEST.setVirtualRoot('path') to alter the
apparent traversal history and URL generation.</li>
</ul>
<dtml-var doc_footer>
<dtml-comment>
</BODY></HTML>
</dtml-comment>
<dtml-comment>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD> <TITLE>Upgrading from SiteAccess 1</TITLE> </HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#000066" VLINK="#606060" TOPMARGIN="0" LEFTMARGIN="0" MARGINWIDTH="0" MARGINHEIGHT="0">
</dtml-comment>
<dtml-var doc_header>
<h3>Must Upgrade</h3>
<p>If your site has SiteAccess 1 objects in it, you must upgrade them. Until
you do, they will be inert</p>
<h4>From the command line</h4>
<p>Copy 'updata.py' from the 'Extensions' directory of the SiteAccess Product
to the 'lib/python' directory. Run it there, then delete it.</p>
<h4>From an External Method</h4>
<p>In the Zope root folder, add an External Method with method "updata" and
python file "SiteAccess.updata". Click on the "Try It" tab, then delete it.</p>
<dtml-var doc_footer>
<dtml-comment>
</BODY></HTML>
</dtml-comment>
<dtml-comment>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML>
<HEAD> <TITLE>Virtual Site Hosting</TITLE> </HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#000066" VLINK="#606060" TOPMARGIN="0" LEFTMARGIN="0" MARGINWIDTH="0" MARGINHEIGHT="0">
</dtml-comment>
<dtml-var doc_header>
<h4>Getting Started</h4>
Before you can set up virtual hosting in Zope, you need to answer two questions:
<ul>
<li>What is the URI of each site's logical root?</li>
<li>What is the corresponding path to the physical root?</li>
</ul>
Suppose, for example, that you want to use Zope to host the domain www.hotsite.com,
and you want 'http://www.hotsite.com' to publish the Zope object '/hotsite/index_html'.
Then 'http://www.hotsite.com' is the URI of your logical root, and '/hotsite' is the
path to your physical root.
<h4>Example 1: One Site</h4>
Put a SiteRoot in '/hotsite', your site's physical root, and accept the
default Path. Create a DTML Method in the Zope root folder containing
<pre>
Is the first-level domain 'hotsite.com'? Ignore sub-domains and port number.
&lt;dtml-if &quot;_.string.split(_.string.split(HTTP_HOST, ':')[0], '.')[-2:]==['hotsite', 'com']&quot;&gt;
Add physical root:
&lt;dtml-call &quot;REQUEST['TraversalRequestNameStack'].append('hotsite')&quot;&gt;
&lt;/dtml-if &gt;
</pre>
Use "Set Access Rule" with the DTML Method's Id. Want to understand this? Read on.
<h4>Getting Physical</h4>
The first half of virtual hosting is rewriting incoming URIs into physical
paths. Many people run ZServer behind Apache, or another HTTP server with
rewriting capabilities, or a proxy. In these cases, you can tell the
front-end HTTP server to rewrite 'http://www.hotsite.com/(.*)' to
'/blah/cgi/Zope.cgi/hotsite/$1', for example.
<br><br>
This works perfectly well, but if your clients are connecting directly to
ZServer, or if you would like to keep all of the virtual hosting logic
in Zope, you will need to do your rewriting in an Access Rule.
<br><br>
An Access Rule is just a regular method (DTML Method or Document, External
Method, Python Method, etc.) on which you have used SiteAccess'
"Set Access Rule" method. In this case, the method lives in the root, so it
will examine every incoming request and decide how to deal with it.
<br><br>
The example DTML Method above is the simplest kind of rewrite rule, forcing
all requests to traverse the 'hotsite' object before any others in the URI.
<h4>Getting Logical</h4>
The second, and more difficult, half of virtual hosting is getting your Zope
objects to generate correct logical URIs for links and images. For example,
if you are rewriting hotsite as described above, then a standard DTML snippet
such as
<pre>
&lt;a href=&quot;&lt;dtml-var URL&gt;/hottopics&quot;&gt;
</pre>
in object '/hotsite/forum' will generate
<pre>
&lt;a href=&quot;http://www.hotsite.com/hotsite/forum/hottopics&quot;&gt;
rather than
&lt;a href=&quot;http://www.hotsite.com/forum/hottopics&quot;&gt;
</pre>
To prevent this, all of the URLn and BASEn request variables and the
absolute_url() method need to be told to strip off '/hotsite'. That's what
SiteRoot objects do.
<br><br>
A SiteRoot object should be placed in the physical root folder ('/hotsite', in
this case) and told the logical URL at which to base all requests passing
through this folder. You tell it by setting its Path property, which in this
case should have the value '/'.
<br><br>
For flexibility's sake, Path can also be set
as a property 'SiteRootPATH' of the '/hotsite' folder or of the root folder,
or it can be set in the rewriting Access Rule with a call to
"REQUEST.set('SiteRootPATH', '/')", or it can be passed in from the
mediating web server as an environment variable. You can also provide
a Base ('SiteRootBASE') value, which will then replace the host:port/script
portion of URIs.
<h4>Example 2: Multiple Sites</h4>
Suppose we are hosting 'hotsite.net', 'fooflowers.com', and 'openmouths.org'
from '/hotsite', '/foof', and '/openm' respectively. We are distinguishing
requests via HTTP_HOST, and we don't care what subdomain or port was specified.
<br><br>
Put a SiteRoot in each of '/hotsite', '/foof', and '/openm'.
In each one, <b>erase</b> the default Path and leave Base blank.
Make a DTML Method in the root folder containing
<pre>
Extract the part of HTTP_HOST we care about, and declare our rewrite dictionary.
&lt;dtml-let hostname=&quot;_.string.join(_.string.split(_.string.split(HTTP_HOST, ':')[0], '.')[-2:], '.')&quot;
sitemap=&quot;{'hotsite.net': 'hotsite',
'fooflowers.com': 'foof',
'openmouths.org': 'openm'}&quot;&gt;
Do we have a match?
&lt;dtml-if expr=&quot;sitemap.has_key(hostname)&quot;&gt;
Set the logical root: &lt;dtml-call &quot;REQUEST.set('SiteRootPATH', '/')&quot;&gt;
Add physical root: &lt;dtml-call &quot;REQUEST['TraversalRequestNameStack'].append(sitemap[hostname])&quot;&gt;
&lt;/dtml-if&gt;
&lt;/dtml-let&gt;
</pre>
Use "Set Access Rule" with the DTML Method's Id. An almost identical method
can be used to distinguish sites by SERVER_ADDRESS and SERVER_PORT instead
of HTTP_HOST. In that case, though, you would probably add a line to set the
appropriate SiteRootBASE.
<br><br>
If you wanted all of these virtual hosts' root folders to live in the folder
'vhosts', you could add the line:
<pre>
Add vhost root: &lt;dtml-call &quot;REQUEST['TraversalRequestNameStack'].append('vhosts')&quot;&gt;
</pre>
<b>after</b> the 'Add physical root' line. If you wanted to add multiple path
elements for each site, you could use path.extend instead of path.append and
map 'fooflowers.org', for example, to ['foof', 'f', 'comsites']. This would
place the root of fooflowers in folder '/comsites/f/foof/'.
<h4>Minor Notes</h4>
<ul>
<li>The return value of an Access Rule is ignored and discarded. This allows
embedded string comments such as in the examples above, and the use of
&lt;dtml-return &quot; 'ignored' &quot;&gt; to exit the Rule.
It also means that if you want to redirect within an Access Rule, you must use
&lt;dtml-raise type=&quot;Redirect&quot;&gt; instead of
&quot;RESPONSE.redirect()&quot;</li>
<li>A SiteRoot object is essentially an Access Rule which calls<br>
REQUEST.setServerURL(SiteRootBASE) and REQUEST.setVirtualRoot(SiteRootPATH).
<li>You might want to exempt management access from being affected by the
virtual hosting. One way to do this is to have a 'magic folder' start all
management interactions. I use 'Z', and wrap the rest of the Access Rule
code in something like:
<pre>
Is there a path, and does it start with 'Z'?
&lt;dtml-let stack=&quot;REQUEST['TraversalRequestNameStack']&quot;&gt;
&lt;dtml-if &quot;stack and stack[-1]=='Z'&quot;&gt;
Get rid of 'Z': &lt;dtml-call &quot;stack.pop()&quot;&gt;
Put it back logically: &lt;dtml-call &quot;REQUEST.setVirtualRoot('Z')&quot;&gt;
&lt;dtml-else&gt;
...
&lt;/dtml-if&gt;
&lt;/dtml-let&gt;
</pre>
</li>
<li>If a SiteRooted folder is <strong>ever</strong> accessed through URLs with
a base or path that does not get rewritten to match the Base and Path of the SiteRoot,
you should make the SiteRoot's Base and Path blank and dynamically create SiteRootPATH/SiteRootBASE
variables. For example, if you made a 'Zope' global-access prefix as described
above, then the 'else' part should contain something like
&lt;dtml-call &quot;REQUEST.set('SiteRootPATH', '/')&quot;&gt;.
</li>
</ul>
<dtml-var doc_footer>
<dtml-comment>
</BODY></HTML>
</dtml-comment>
SiteRoot - Edit: Edit SiteRoot parameters
Description
This view displays the SiteRoot parameters and allows you to
change them.
Information
'Title' -- The optional title.
'Base' -- Replacement for base of URLs. If you do not want to replace this
portion of URLs, leave it blank.
'Path' -- Replacement for the URL path to the SiteRoot's container. If you
do not want to replace this portion of URLs, leave it blank.
Controls
'Change' -- Changes the parameters.
<html><head><title>Set the Access Rule</title>
</head>
<body bgcolor="#FFFFFF" link="#000099" vlink="#555555">
<h2>Set the Access Rule</h2>
<p>In the form below <em>Method Id</em> is the Id of a method in the Zope
root which will be called at the start of each request. It can implement
rewrite rules, preload request variables, etc.
</p>
<dtml-if "manage_getAccessRule(this())">
<p>The current Access Rule is
<a href="<dtml-var "'%s/%s/manage_workspace' % (this().absolute_url(), manage_getAccessRule(this()))">">
"<dtml-var "manage_getAccessRule(this())">"</a>.</p>
<dtml-else>
<p>No Access Rule is currently set.</p>
</dtml-if>
<form action="manage_addAccessRule" method="post">
<table>
<tr> <th align='LEFT'><em>Method Id<em></th>
<td align='LEFT'><input name="method_id" size="80">
</td></tr>
<tr><td colspan=2>
<input type="SUBMIT" name="submit" value=" Set Rule ">
<input type="SUBMIT" name="none" value=" No Access Rule ">
</td></tr>
</table>
</form>
</body></html>
<HTML><HEAD><TITLE>Add a SiteRoot</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<H2>Add a SiteRoot</H2>
<p>This will change URLs generated by all objects within the same container
as the SiteRoot. If a Base is specified (or a SiteRootBASE value can be
found) then it will replace the host:port/script portion of generated URLs.
If a Path is specified (or a SiteRootPATH value can be found) then it will
replace the remainder of each URL.</p>
<p>Values affected include DTML variables starting with URL or BASE, and
the absolute_url() methods of objects.</p>
<p>If Base is not set, the SiteRoot will first attempt to acquire SiteRootBASE
and then search the REQUEST for it. The same holds for Path and SiteRootPATH.</p>
<p>Base (if specified) should <strong>always</strong> start with "http://"</p>
<form action="manage_addSiteRoot" method="post">
<table>
<tr> <th align='LEFT'>Id</th>
<td align='LEFT'>SiteRoot</td></tr>
<tr> <th align='LEFT'><em>Title<em></th>
<td align='LEFT'><input name="title" size="40"></td></tr>
<tr> <th align='LEFT'><em>Base<em></th>
<td align='LEFT'><input name="base" size="40"></td></tr>
<tr> <th align='LEFT'><em>Path<em></th>
<td align='LEFT'><input name="path" size="40" value="/"></td></tr>
<tr><td colspan=2>
<input type="SUBMIT" name="submit" value=" Add ">
</td></tr>
</table>
</form>
</body></html>
<html>
<head>
<title>Edit <dtml-var title_or_id></title>
</head>
<body bgcolor="#ffffff" link="#000099" vlink="#555555" alink="#77003b">
<dtml-var manage_tabs>
<h2>Edit <dtml-var id></h2>
<p>This will change URLs generated by all objects within the same container
as the SiteRoot. If a Base is specified (or a SiteRootBASE value can be
found) then it will replace the host:port/script portion of generated URLs.
If a Path is specified (or a SiteRootPATH value can be found) then it will
replace the remainder of each URL.</p>
<p>Values affected include DTML variables starting with URL or BASE, and
the absolute_url() methods of objects.</p>
<p>If Base is not set, the SiteRoot will first attempt to acquire SiteRootBASE
and then search the REQUEST for it. The same holds for Path and SiteRootPATH.</p>
<p>Base (if specified) should <strong>always</strong> start with "http://"</p>
<form action="manage_edit" method="POST">
<table cellspacing="2">
<tr>
<th align="left" valign="top"><em>Title</em></th>
<td align="left" valign="top">
<input type="text" name="title" size="40" value="&dtml-title;">
</td>
</tr>
<tr>
<th align="left" valign="top"><em>Base</em></th>
<td align="left" valign="top">
<input type="text" name="base" size="40" value="<dtml-if base>&dtml-base;</dtml-if>">
</td>
</tr>
<tr>
<th align="left" valign="top"><em>Path</em></th>
<td align="left" valign="top">
<input type="text" name="path" size="40" value="<dtml-if path>&dtml-path;</dtml-if>">
</td>
</tr>
<tr><td></td>
<td align="left" valign="top">
<input type="submit" name="submit" value=" Change ">
</td>
</tr>
</table>
</form>
</body>
</html>
<html>
<head>
<title><dtml-var title_or_id></title>
</head>
<body bgcolor="#ffffff" link="#000099" vlink="#555555" alink="#77003b">
<dtml-var manage_tabs>
<p>This Virtual Host Monster changes the URLs generated inside of the
Folder in which it is placed, using information passed to it in special URL
path elements. Its purpose is to support virtual hosting, by
allowing you to centrally control the protocol, host, and path of the
URLs that Zope generates.</p>
<p>&dtml-id; doesn't do anything unless you insert one or both of the
following special path elements into a URL:
<i>VirtualHostBase</i> sets the protocol and host, while
<i>VirtualHostRoot</i> sets the path root. You would insert
these path elements using an Apache RewriteRule or ProxyPass
directive, or with a Zope Access Rule.</p>
<p>If the URL path of a request begins with
&quot;/VirtualHostBase/http/www.foo.com&quot;, for instance, then
URLs generated by Zope will start with <b>http://www.foo.com</b>.
</p><p>
If the URL contains <i>VirtualHostRoot</i>, then
all path elements up to that point are removed from generated URLs.
For instance, a request with path &quot;/a/b/c/VirtualHostRoot/d&quot;
will generate a URL with path <b>/d</b>.
</p>
<p>
Combining both allows you to cause a subfolder to act as the root of a
site. For example, to publish Folder &quot;/foo&quot; as
<b>http://www.foo.com/</b>, put a Virtual Host Monster in the root
folder and rewrite requests for that URL to
<b>/VirtualHostBase/http/www.foo.com/foo/VirtualHostRoot/</b>
</p>
<p>Values affected include DTML variables starting with URL or BASE
(such as URL1, BASE2, URLPATH0), and
the absolute_url() methods of objects.</p>
</body>
</html>
<HTML><HEAD><TITLE>Add a Virtual Host Monster</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<H2>Add a Virtual Host Monster</H2>
<form action="manage_addVirtualHostMonster" method="post">
<b>Id:</b> <input name="id" size="40">
<input type="SUBMIT" name="submit" value=" Add ">
</form>
<p>
<i>This is only useful if you are using some rewriting tool
(Apache or an Access Rule, for example) to insert these special
elements into your URL.</i><br><br>
A Virtual Host Monster changes the URLs generated by all objects
within the same Folder, using information passed to it in special URL
path elements.<br><br>
To set the protocol ('http', 'https') and host ('www.foo.com') portion
of generated URLs, insert &quot;VirtualHostBase&quot;, the protocol,
and the host into the path.<br><br>
Insert &quot;VirtualHostRoot&quot; directly after the name of the
Folder that is supposed to be the root of the virtual host.<br><br>
For example, to publish Folder &quot;/foo&quot; as <b>http://www.foo.com/</b>,
put a Virtual Host Monster in the root folder and rewrite requests for that
URL to <b>/VirtualHostBase/http/www.foo.com/foo/VirtualHostRoot/</b>
</p>
<p>Values affected include DTML variables starting with URL or BASE, and
the absolute_url() methods of objects.</p>
</body></html>
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