Commit 7abb893a authored by Hanno Schlichting's avatar Hanno Schlichting

Removed `AccessRule` and `SiteRoot` from `Products.SiteAccess`.

parent cff4baf4
......@@ -41,6 +41,8 @@ Features Added
Restructuring
+++++++++++++
- Removed `AccessRule` and `SiteRoot` from `Products.SiteAccess`.
- Removed `Products.ZReST` and the `reStructuredText` wrapper, you can use
`docutils` directly to gain `reST` support.
......
......@@ -14,8 +14,6 @@
<five:deprecatedManageAddDelete
class="Products.SiteAccess.VirtualHostMonster.VirtualHostMonster"/>
<five:deprecatedManageAddDelete
class="Products.SiteAccess.SiteRoot.Traverser"/>
<five:deprecatedManageAddDelete
class="Products.SiteErrorLog.SiteErrorLog.SiteErrorLog"/>
......
......@@ -23,7 +23,7 @@ from zope.traversing.interfaces import BeforeTraverseEvent
import ExtensionClass
from Acquisition import aq_base, aq_inner, aq_parent
from Products.SiteAccess.AccessRule import AccessRule
from ZPublisher.BeforeTraverse import NameCaller
from ZPublisher.BeforeTraverse import registerBeforeTraverse
from ZPublisher.BeforeTraverse import unregisterBeforeTraverse
......@@ -64,7 +64,7 @@ def enableSite(obj, iface=ISite):
obj = aq_base(obj)
if not IPossibleSite.providedBy(obj):
raise TypeError, 'Must provide IPossibleSite'
hook = AccessRule(HOOK_NAME)
hook = NameCaller(HOOK_NAME)
registerBeforeTraverse(obj, hook, HOOK_NAME, 1)
if not hasattr(obj, HOOK_NAME):
......
"""AccessRule module
Provide a simple method to set up Access Rules
"""
from cgi import escape
import os
from App.Dialogs import MessageDialog
from App.special_dtml import DTMLFile
from ZPublisher.BeforeTraverse import NameCaller
from ZPublisher.BeforeTraverse import queryBeforeTraverse
from ZPublisher.BeforeTraverse import registerBeforeTraverse
from ZPublisher.BeforeTraverse import unregisterBeforeTraverse
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
NameCaller.__call__(self, container, request)
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)
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 = 'accessrule.gif'
except:
pass
if REQUEST:
return MessageDialog(title='Access Rule Set',
message='"%s" is now the Access Rule for this object'
% escape(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'
% escape(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:
try:
return rules[0][1].name
except:
return 'Invalid BeforeTraverse data: ' + `rules`
return ''
constructors = (
('manage_addAccessRuleForm', DTMLFile('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 Zope Foundation and Contributors
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.
def updata(self):
"""Convert SiteAccess objects from 1.x to 2.x"""
_cvt_btr(self.REQUEST['PARENTS'][-1])
from App.Dialogs import MessageDialog
return MessageDialog(title='Update Complete', message='Update Complete!',
action='./manage_main')
def _cvt_btr(app):
from ZPublisher.BeforeTraverse import NameCaller
from ZPublisher.BeforeTraverse import rewriteBeforeTraverse
from Products.SiteAccess.AccessRule import AccessRule
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__':
import Zope2
import transaction
print "Converting SiteAccess objects from 1.x to 2.x ..."
app = Zope2.app()
_cvt_btr(app)
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 cgi import escape
import os
from Acquisition import Implicit
from App.Dialogs import MessageDialog
from App.special_dtml import DTMLFile
from OFS.SimpleItem import Item
from Persistence import Persistent
from ZPublisher.BeforeTraverse import NameCaller
from ZPublisher.BeforeTraverse import registerBeforeTraverse
from ZPublisher.BeforeTraverse import unregisterBeforeTraverse
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' % escape(self.meta_type),
action=nextURL)
def manage_beforeDelete(self, item, container):
if item is self:
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 = NameCaller(id)
registerBeforeTraverse(container, hook,
self.meta_type,
self.priority)
def _setId(self, id):
if id != self.id:
raise ValueError('Cannot change the id of a %s'
% escape(self.meta_type))
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 = DTMLFile('www/SiteRootEdit', globals())
manage_main._setName('manage_main')
def __init__(self, title, base, path):
self.title = title.strip()
self.base = base = base.strip()
self.path = path = path.strip()
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='SiteRoot changed.',
action='%s/manage_main' % REQUEST['URL1'])
def __call__(self, client, request, response=None):
""" Traversing.
"""
rq = request
if SUPPRESS_SITEROOT:
return
base = (self.base or
rq.get('SiteRootBASE') or
rq.environ.get('SiteRootBASE'))
path = (self.path or
rq.get('SiteRootPATH') or
rq.environ.get('SiteRootPATH'))
if base is not None:
rq['ACTUAL_URL'] = rq['ACTUAL_URL'].replace(rq['SERVER_URL'], base)
rq['SERVER_URL'] = base
rq._resetURLS()
if path is not None:
old = rq['URL']
rq.setVirtualRoot(path)
rq['ACTUAL_URL'] = rq['ACTUAL_URL'].replace(old, rq['URL'])
def get_size(self):
""" Make FTP happy
"""
return 0
def manage_addSiteRoot(self, title='', base='', path='', REQUEST=None,
**ignored):
""" Add a SiteRoot to a container.
"""
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', DTMLFile('www/SiteRootAdd', globals())),
('manage_addSiteRoot', manage_addSiteRoot),
)
"""SiteAccess product
"""
def initialize(context):
import SiteRoot
import AccessRule
import VirtualHostMonster
context.registerClass(instance_class=SiteRoot.SiteRoot,
permission='Add Site Roots',
constructors=SiteRoot.constructors, legacy=SiteRoot.constructors)
context.registerClass(instance_class=AccessRule.AccessRule,
permission='Manage Access Rules', constructors=AccessRule.constructors)
def initialize(context):
import VirtualHostMonster
context.registerClass(instance_class=VirtualHostMonster.VirtualHostMonster,
permission='Add Virtual Host Monsters',
......
<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;&amp;dtml-URL;/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>
# SiteAccess test package
#
\ No newline at end of file
import unittest
class AccessRuleTests(unittest.TestCase):
_old_SAR = None
def setUp(self):
from Testing.ZopeTestCase import ZopeLite
ZopeLite.startup()
def tearDown(self):
if self._old_SAR is not None:
self._set_SUPPRESS_ACCESSRULE(self._old_SAR)
def _set_SUPPRESS_ACCESSRULE(self, value):
from Products.SiteAccess import AccessRule as AR
(self._old_SAR,
AR.SUPPRESS_ACCESSRULE) = (AR.SUPPRESS_ACCESSRULE, value)
def _getTargetClass(self):
from Products.SiteAccess.AccessRule import AccessRule
return AccessRule
def _makeOne(self, method_id='testing'):
return self._getTargetClass()(method_id)
def test___call___w_SUPPRESS_ACCESSRULE_set(self):
self._set_SUPPRESS_ACCESSRULE(1)
_called = []
def _func(*args):
_called.append(args)
rule = self._makeOne()
request = DummyRequest(TraversalRequestNameStack=[])
container = DummyContainer(testing=_func)
rule(container, request)
self.assertFalse(_called)
def test___call___w_SUPPRESS_ACCESSRULE_in_URL(self):
# This behavior changed in landing lp:142878.
_called = []
def _func(*args):
_called.append(args)
rule = self._makeOne()
request = DummyRequest(TraversalRequestNameStack=
['_SUPPRESS_ACCESSRULE'])
request.steps = []
container = DummyContainer(testing=_func)
rule(container, request)
self.assertTrue(_called)
self.assertEqual(request._virtual_root, None)
def test___call___wo_SUPPRESS_ACCESSRULE(self):
_called = []
def _func(*args):
_called.append(args)
rule = self._makeOne()
request = DummyRequest(TraversalRequestNameStack=[])
request.steps = []
container = DummyContainer(testing=_func)
rule(container, request)
self.assertTrue(_called)
self.assertEqual(request._virtual_root, None)
class Test_manage_addAccessRule(unittest.TestCase):
def _callFUT(self, container, method_id, REQUEST):
from Products.SiteAccess.AccessRule import manage_addAccessRule
return manage_addAccessRule(container, method_id, REQUEST)
def test_no_method_id_no_existing_rules_no_request(self):
container = DummyContainer()
result = self._callFUT(container, None, None)
self.assertTrue(result is None)
self.assertFalse(container.__dict__)
def test_no_method_id_no_existing_rules_w_request(self):
container = DummyContainer()
result = self._callFUT(container, None, {'URL1': 'http://example.com/'})
self.assertTrue(isinstance(result, str))
self.assertTrue('<TITLE>No Access Rule</TITLE>' in result)
self.assertFalse(container.__dict__)
def test_no_method_id_w_existing_rules_no_request(self):
from ZPublisher.BeforeTraverse import registerBeforeTraverse
container = DummyContainer()
old_rule = container.old_rule = DummyObject(name='old_rule',
icon='rule_icon.jpg')
registerBeforeTraverse(container, old_rule, 'AccessRule')
result = self._callFUT(container, None, None)
self.assertTrue(result is None)
self.assertFalse(container.__before_traverse__)
self.assertFalse('icon' in old_rule.__dict__)
def test_w_method_id_w_existing_rules_w_request_none(self):
from ZPublisher.BeforeTraverse import registerBeforeTraverse
container = DummyContainer()
old_rule = container.old_rule = DummyObject(name='old_rule',
icon='rule_icon.jpg')
registerBeforeTraverse(container, old_rule, 'AccessRule')
request = DummyRequest(URL1 = 'http://example.com/')
request.form = {'none': '1'}
result = self._callFUT(container, None, request)
self.assertTrue(isinstance(result, str))
self.assertTrue('<TITLE>No Access Rule</TITLE>' in result)
self.assertFalse(container.__before_traverse__)
self.assertFalse('icon' in old_rule.__dict__)
def test_w_invalid_method_id_w_existing_rules_no_request(self):
from ZPublisher.BeforeTraverse import registerBeforeTraverse
container = DummyContainer()
old_rule = container.old_rule = DummyObject(name='old_rule',
icon='rule_icon.jpg')
registerBeforeTraverse(container, old_rule, 'AccessRule')
result = self._callFUT(container, 'nonesuch', None)
self.assertTrue(result is None)
self.assertTrue((99, 'AccessRule') in container.__before_traverse__)
rule = container.__before_traverse__[(99, 'AccessRule')]
self.assertEqual(rule.name, 'old_rule')
self.assertEqual(old_rule.icon, 'rule_icon.jpg')
def test_w_invalid_method_id_w_existing_rules_w_request(self):
from ZPublisher.BeforeTraverse import registerBeforeTraverse
container = DummyContainer()
old_rule = container.old_rule = DummyObject(name='old_rule',
icon='rule_icon.jpg')
registerBeforeTraverse(container, old_rule, 'AccessRule')
request = DummyRequest(URL1 = 'http://example.com/')
request.form = {}
result = self._callFUT(container, 'nonesuch', request)
self.assertTrue(isinstance(result, str))
self.assertTrue('<TITLE>Invalid Method Id</TITLE>' in result)
self.assertTrue((99, 'AccessRule') in container.__before_traverse__)
rule = container.__before_traverse__[(99, 'AccessRule')]
self.assertEqual(rule.name, 'old_rule')
self.assertEqual(old_rule.icon, 'rule_icon.jpg')
def test_w_valid_method_id_w_existing_rules_no_request(self):
from ZPublisher.BeforeTraverse import registerBeforeTraverse
container = DummyContainer()
old_rule = container.old_rule = DummyObject(name='old_rule',
icon='rule_icon.jpg')
new_rule = container.new_rule = DummyObject(name='new_rule')
registerBeforeTraverse(container, old_rule, 'AccessRule')
result = self._callFUT(container, 'new_rule', None)
self.assertTrue(result is None)
self.assertFalse((99, 'AccessRule') in container.__before_traverse__)
self.assertTrue((1, 'AccessRule') in container.__before_traverse__)
rule = container.__before_traverse__[(1, 'AccessRule')]
self.assertEqual(rule.name, 'new_rule')
self.assertFalse('icon' in old_rule.__dict__)
self.assertEqual(new_rule.icon, 'accessrule.gif')
def test_w_valid_method_id_w_existing_rules_w_request(self):
from ZPublisher.BeforeTraverse import registerBeforeTraverse
container = DummyContainer()
old_rule = container.old_rule = DummyObject(name='old_rule',
icon='rule_icon.jpg')
new_rule = container.new_rule = DummyObject(name='new_rule')
registerBeforeTraverse(container, old_rule, 'AccessRule')
request = DummyRequest(URL1 = 'http://example.com/')
request.form = {}
result = self._callFUT(container, 'new_rule', request)
self.assertTrue(isinstance(result, str))
self.assertTrue('<TITLE>Access Rule Set</TITLE>' in result)
self.assertFalse((99, 'AccessRule') in container.__before_traverse__)
self.assertTrue((1, 'AccessRule') in container.__before_traverse__)
rule = container.__before_traverse__[(1, 'AccessRule')]
self.assertEqual(rule.name, 'new_rule')
self.assertFalse('icon' in old_rule.__dict__)
self.assertEqual(new_rule.icon, 'accessrule.gif')
class Test_getAccessRule(unittest.TestCase):
def _callFUT(self, container, REQUEST=None):
from Products.SiteAccess.AccessRule import getAccessRule
return getAccessRule(container, REQUEST)
def test_no_rules(self):
container = DummyContainer()
self.assertEqual(self._callFUT(container), '')
def test_w_rule_invalid(self):
from ZPublisher.BeforeTraverse import registerBeforeTraverse
container = DummyContainer()
registerBeforeTraverse(container, DummyObject(), 'AccessRule')
self.assertTrue(self._callFUT(container).startswith(
'Invalid BeforeTraverse data: '))
def test_w_rule_valid(self):
from ZPublisher.BeforeTraverse import registerBeforeTraverse
container = DummyContainer()
registerBeforeTraverse(container, DummyObject(name='foo'), 'AccessRule')
self.assertEqual(self._callFUT(container), 'foo')
class DummyRequest(dict):
_virtual_root = None
def setVirtualRoot(self, root):
self._virtual_root = root
class DummyObject(object):
def __init__(self, **kw):
self.__dict__.update(kw)
class DummyContainer(object):
def __init__(self, **kw):
self.__dict__.update(kw)
def this(self):
return self
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(AccessRuleTests),
unittest.makeSuite(Test_manage_addAccessRule),
unittest.makeSuite(Test_getAccessRule),
))
This diff is collapsed.
<dtml-var manage_page_header>
<dtml-let form_title="'Set Access Rule'">
<dtml-var manage_form_title>
</dtml-let>
<p class="form-help">
<strong>
WARNING: Access Rules are powerful, and can temporarily disable
Zope access! Don't use them unless you have read all about them and
know how to recover from mistakes!
</strong>
</p>
<p class="form-help">
In the form below <em>rule id</em> is the id of an object in this
Zope Folder which will be called whenever the Folder is published.
It can implement rewrite rules, preload request variables, etc.
</p>
<dtml-if manage_getAccessRule>
<p class="form-text">
The current Access Rule is
<a href="&dtml-absolute_url;/&dtml-manage_getAccessRule;/manage_workspace">
"&dtml-manage_getAccessRule;"</a>.
</p>
<dtml-else>
<p class="form-text">
No Access Rule is currently set.
</p>
</dtml-if>
<form action="manage_addAccessRule" method="post">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Rule Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="method_id" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="SUBMIT" name="submit" value=" Set Rule ">
<input class="form-element" type="SUBMIT" name="none" value=" No Access Rule ">
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-let form_title="'Add SiteRoot'"><dtml-var manage_form_title></dtml-let>
<p class="form-help">
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 class="form-help">
Values affected include DTML variables starting with URL or BASE, and
the absolute_url() methods of objects.
</p>
<p class="form-help">
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 class="form-help">
Base (if specified) should <strong>always</strong> start with
&quot;http://&quot;
</p>
<form action="manage_addSiteRoot" method="post">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">SiteRoot</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Base
</div>
</td>
<td align="left" valign="top">
<input type="text" name="base" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Path
</div>
</td>
<td align="left" valign="top">
<input type="text" name="path" size="40" value="/" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value=" Add " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<p class="form-help">
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 class="form-help">
Values affected include DTML variables starting with URL or BASE, and
the absolute_url() methods of objects.
</p>
<p class="form-help">
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 class="form-help">
Base (if specified) should <strong>always</strong> start with "http://"
</p>
<form action="manage_edit" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40"
value="<dtml-if title>&dtml-title;</dtml-if>" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Base
</div>
</td>
<td align="left" valign="top">
<input type="text" name="base" size="40"
value="<dtml-if base>&dtml-base;</dtml-if>" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Path
</div>
</td>
<td align="left" valign="top">
<input type="text" name="path" size="40"
value="<dtml-if path>&dtml-path;</dtml-if>" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Save Changes" />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
......@@ -53,14 +53,6 @@ def session_timeout_minutes(value):
value not in (None, default) and _setenv('ZSESSION_TIMEOUT_MINS', value)
return value
def suppress_all_access_rules(value):
value and _setenv('SUPPRESS_ACCESSRULE', value)
return value
def suppress_all_site_roots(value):
value and _setenv('SUPPRESS_SITEROOT', value)
return value
def large_file_threshold(value):
import ZServer
ZServer.LARGE_FILE_THRESHOLD = value
......
......@@ -688,27 +688,6 @@
<metadefault>20</metadefault>
</key>
<key name="suppress-all-access-rules" datatype="boolean"
default="off" handler="suppress_all_access_rules">
<description>
If this directive is set to on, no access rules in your Zope site
will be executed. This is useful if you "lock yourself out" of a
particular part of your site by setting an improper access rule.
</description>
<metadefault>off</metadefault>
</key>
<key name="suppress-all-site-roots" datatype="boolean"
default="off" handler="suppress_all_site_roots">
<description>
If this directive is set to on, no site roots in your Zope site will
be effective. This is useful if you "lock yourself out" of a
particular part of your site by setting an improper site root.
</description>
<metadefault>off</metadefault>
</key>
<section type="eventlog" name="*" attribute="eventlog">
<description>
Describes what level of log output is desired and where it
......
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