From 6cb31e51c2802b5b856054c525257dc781e3334b Mon Sep 17 00:00:00 2001 From: Bernd Dorn Date: Mon, 2 Oct 2006 19:48:34 +0000 Subject: [PATCH] implemented unconsumed_url, which returns the absolute_url but with the stack info --- src/z3c/traverser/stackinfo/README.txt | 27 +++++++++- src/z3c/traverser/stackinfo/configure.zcml | 8 +++ src/z3c/traverser/stackinfo/consumer.py | 8 ++- src/z3c/traverser/stackinfo/tests.py | 6 ++- src/z3c/traverser/stackinfo/traversing.py | 60 ++++++++++++++++++++-- 5 files changed, 101 insertions(+), 8 deletions(-) diff --git a/src/z3c/traverser/stackinfo/README.txt b/src/z3c/traverser/stackinfo/README.txt index 015df57..4c3dd2a 100644 --- a/src/z3c/traverser/stackinfo/README.txt +++ b/src/z3c/traverser/stackinfo/README.txt @@ -140,7 +140,7 @@ BeforeTraverseEvents. >>> stack = [u'index.html', u'v2', u'k2', u'kv', u'v1', u'k1', u'kv'] >>> request.setTraversalStack(stack) >>> traversing.applyStackConsumers(content, request) - >>> request.annotations[traversing.ANNOTATION_KEY] + >>> request.annotations[traversing.CONSUMERS_ANNOTATION_KEY] [, ] @@ -154,7 +154,7 @@ to ITraversalStackInfo. >>> len(ti) 2 - + The adapter always returs an empty TraversalStackInfoObject if there is no traversalstack information. @@ -162,3 +162,26 @@ is no traversalstack information. >>> ti = interfaces.ITraversalStackInfo(request) >>> len(ti) 0 + +URL Handling +============ + +Let us try these things with a real url, in our test the root is the site. + + >>> from zope.traversing.browser.absoluteurl import absoluteURL + >>> absoluteURL(root, request) + 'http://127.0.0.1' + +There is an unconsumedURL function which returns the url of an object +with the traversal information, which is normally omitted. + + >>> request = TestRequest() + >>> root['content'] = content + >>> absoluteURL(root['content'], request) + 'http://127.0.0.1/content' + >>> stack = [u'index.html', u'v2 space', u'k2', u'kv', u'v1', u'k1', u'kv'] + >>> request.setTraversalStack(stack) + >>> traversing.applyStackConsumers(root['content'], request) + >>> traversing.unconsumedURL(root['content'], request) + 'http://127.0.0.1/content/kv/k1/v1/kv/k2/v2%20space' + diff --git a/src/z3c/traverser/stackinfo/configure.zcml b/src/z3c/traverser/stackinfo/configure.zcml index 3ded922..6167dfc 100644 --- a/src/z3c/traverser/stackinfo/configure.zcml +++ b/src/z3c/traverser/stackinfo/configure.zcml @@ -7,5 +7,13 @@ + + \ No newline at end of file diff --git a/src/z3c/traverser/stackinfo/consumer.py b/src/z3c/traverser/stackinfo/consumer.py index 0933d56..a60e75f 100644 --- a/src/z3c/traverser/stackinfo/consumer.py +++ b/src/z3c/traverser/stackinfo/consumer.py @@ -6,7 +6,7 @@ @component.adapter(IBrowserRequest) @interface.implementer(interfaces.ITraversalStackInfo) def requestTraversalStackInfo(request): - cons = request.annotations.get(traversing.ANNOTATION_KEY, []) + cons = request.annotations.get(traversing.CONSUMERS_ANNOTATION_KEY, []) return TraversalStackInfo(cons) class TraversalStackInfo(tuple): @@ -25,9 +25,13 @@ def __init__(self, context, request): def consume(self): stack = self.request.getTraversalStack() self.__name__ = stack.pop() + consumed = [self.__name__] for name in self.arguments: - setattr(self, name, stack.pop()) + v = stack.pop() + consumed.append(v) + setattr(self, name, v) self.request.setTraversalStack(stack) + return consumed def __repr__(self): return '<%s named %r>' % (self.__class__.__name__, diff --git a/src/z3c/traverser/stackinfo/tests.py b/src/z3c/traverser/stackinfo/tests.py index 3666a92..435ace3 100644 --- a/src/z3c/traverser/stackinfo/tests.py +++ b/src/z3c/traverser/stackinfo/tests.py @@ -2,9 +2,12 @@ import unittest from zope.testing.doctestunit import DocFileSuite, DocFileSuite from zope.app.testing import setup +import zope.traversing.testing def setUp(test): - setup.placefulSetUp() + root = setup.placefulSetUp(True) + zope.traversing.testing.setUp() + test.globs['root'] = root def tearDown(test): setup.placefulTearDown() @@ -14,6 +17,7 @@ def test_suite(): return unittest.TestSuite( ( DocFileSuite('README.txt', + setUp=setUp, tearDown=tearDown, optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS, ), )) diff --git a/src/z3c/traverser/stackinfo/traversing.py b/src/z3c/traverser/stackinfo/traversing.py index 02dc884..61e10fb 100644 --- a/src/z3c/traverser/stackinfo/traversing.py +++ b/src/z3c/traverser/stackinfo/traversing.py @@ -1,8 +1,12 @@ from zope import component import interfaces from zope.publisher.interfaces import NotFound +from zope.traversing.browser.absoluteurl import absoluteURL +import urllib +from zope.publisher.browser import BrowserView -ANNOTATION_KEY='z3c.traverser.consumers' +CONSUMERS_ANNOTATION_KEY='z3c.traverser.consumers' +CONSUMED_ANNOTATION_KEY='z3c.traverser.consumed' def getStackConsumers(context, request): """consumes the stack""" @@ -25,5 +29,55 @@ def getStackConsumers(context, request): break def applyStackConsumers(context, request): - cons = [cons for name, cons in getStackConsumers(context, request)] - request.annotations[ANNOTATION_KEY] = cons + if not request.annotations.has_key(CONSUMED_ANNOTATION_KEY): + request.annotations[CONSUMED_ANNOTATION_KEY] = [] + orgStack = request.getTraversalStack() + cons = [cons for name, cons in getStackConsumers( + context, request)] + newStack = request.getTraversalStack() + if newStack != orgStack: + consumed = request.annotations[CONSUMED_ANNOTATION_KEY] + items = orgStack[len(newStack):] + items.reverse() + consumed.append((context, items)) + request.annotations[CONSUMERS_ANNOTATION_KEY] = cons + +def _encode(v, _safe='@+'): + return urllib.quote(v.encode('utf-8'), _safe) + +def unconsumedURL(context, request): + + consumed = list(request.annotations.get(CONSUMED_ANNOTATION_KEY)) + if not consumed: + return absoluteURL(context, request) + from zope.traversing import api + + from zope.traversing.interfaces import IContainmentRoot + name = api.getName(context) + items = name and [name] or [] + for obj, names in consumed: + if obj == context: + items.extend(names) + break + if IContainmentRoot.providedBy(context): + base = absoluteURL(context, request) + else: + base = unconsumedURL(api.getParent(context), request) + items = map(_encode, items) + if not base.endswith('/'): + base += '/' + return base + '/'.join(items) + +class UnconsumedURL(BrowserView): + # XXX test this + def __unicode__(self): + return urllib.unquote(self.__str__()).decode('utf-8') + + def __str__(self): + return unconsumedURL(self.context, self.request) + + __call__ = __str__ + + + +