[Grok-dev] a "skin" keyword argument to view.url() method
Jan-Wijbrand Kolman
janwijbrand at gmail.com
Tue Nov 20 10:33:18 UTC 2012
Hi,
In the application we develop we regularly need to compute URLs for a
specific skin. We have some helper functions for that, but I guess
having this functionality as part of the ``view.url()`` method could be
generally useful.
Below you will find a diff between the current grokcore.view trunk and
the jw-urls-with-skin branch I created to implement this feature.
Please tell me if you think this feature is indeed generally useful and
whether you'd agree with my implementation. Thx!
regards, jw
Index: src/grokcore/view/util.py
===================================================================
--- src/grokcore/view/util.py (.../trunk) (revision 128365)
+++ src/grokcore/view/util.py (.../branches/jw-urls-with-skin) (revision
128365)
@@ -14,18 +14,37 @@
"""Grok utility functions.
"""
import urllib
+import urlparse
from grokcore.security.util import check_permission
from zope.component import getMultiAdapter
from zope.security.checker import NamesChecker, defineChecker
from zope.traversing.browser.absoluteurl import _safe as
SAFE_URL_CHARACTERS
from zope.traversing.browser.interfaces import IAbsoluteURL
+import directive
-def url(request, obj, name=None, data=None):
+ASIS = object()
+
+def url(request, obj, name=None, skin=ASIS, data=None):
url = getMultiAdapter((obj, request), IAbsoluteURL)()
if name is not None:
url += '/' + urllib.quote(name.encode('utf-8'),
SAFE_URL_CHARACTERS)
+ if skin is not ASIS:
+ # Remove whatever ``++skin++[name]`` is active.
+ parts = list(urlparse.urlparse(url))
+ path = parts[2]
+ if path.startswith('/++skin++'):
+ # Find next / in the path.
+ idx = path.find('/', 1)
+ path = path[idx:]
+ if skin is not None:
+ # If a skin is set, add ``++skin++`` as the leading path
segment.
+ name = directive.skin.bind().get(skin)
+ path = '/++skin++%s%s' % (name, path)
+ parts[2] = path
+ url = urlparse.urlunparse(parts)
+
if not data:
return url
Index: src/grokcore/view/ftests/url/url.py
===================================================================
--- src/grokcore/view/ftests/url/url.py (.../trunk) (revision 128365)
+++ src/grokcore/view/ftests/url/url.py
(.../branches/jw-urls-with-skin) (revision 128365)
@@ -171,6 +171,45 @@
>>> browser.contents
'11'
+It is possible to compute URLs for specific skin names.
+
+First show how a view registered for a view, will by default compute URLs
+for that skin:
+
+ >>>
browser.open('http://127.0.0.1/++skin++urltesting/herd/manfred/@@test')
+ >>> browser.contents
+ "I'm on a url testing skin:
+ http://127.0.0.1/++skin++urltesting/herd/manfred/test"
+
+We get the views manually so we can do a greater variety of url() calls:
+
+ >>> from zope.publisher.browser import applySkin
+ >>> request = TestRequest()
+ >>> applySkin(request, URLTestingSkin)
+ >>> # Shifting names normally happens during URL traversal.
+ >>> request._traversed_names = ['++skin++urltesting']
+ >>> request.shiftNameToApplication()
+ >>> index_view = component.getMultiAdapter((manfred, request),
name='test')
+ >>> index_view.url()
+ 'http://127.0.0.1/++skin++urltesting/herd/manfred/test'
+
+Explicitely remove the skin part:
+
+ >>> index_view.url(skin=None)
+ 'http://127.0.0.1/herd/manfred/test'
+
+Use another skin:
+
+ >>> index_view.url(skin=AnotherURLTestingSkin)
+ 'http://127.0.0.1/++skin++anotherurltesting/herd/manfred/test'
+
+Use something that is not a skin will fail:
+
+ >>> index_view.url(skin='foobar')
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'str' object has no attribute 'queryTaggedValue'
+
"""
import grokcore.view as grok
from zope.container.contained import Contained
@@ -199,3 +238,16 @@
return unicode(self.age * 2)
yetanother = grok.PageTemplate('<p tal:replace="view/url" />')
+
+class URLTestingSkin(grok.IBrowserRequest):
+ grok.skin('urltesting')
+
+class AnotherURLTestingSkin(grok.IBrowserRequest):
+ grok.skin('anotherurltesting')
+
+class URLTestingViewOnASkin(grok.View):
+ grok.layer(URLTestingSkin)
+ grok.name('test')
+
+ def render(self):
+ return u"I'm on a url testing skin: %s" % self.url()
Index: src/grokcore/view/components.py
===================================================================
--- src/grokcore/view/components.py (.../trunk) (revision 128365)
+++ src/grokcore/view/components.py (.../branches/jw-urls-with-skin)
(revision 128365)
@@ -78,7 +78,7 @@
return self.request.response.redirect(
url, status=status, trusted=trusted)
- def url(self, obj=None, name=None, data=None):
+ def url(self, obj=None, name=None, skin=util.ASIS, data=None):
"""Return string for the URL based on the obj and name.
If no arguments given, construct URL to view itself.
@@ -91,6 +91,16 @@
If both object and name arguments are supplied, construct URL
to `obj/name`.
+ Optionally pass a `skin` keyword argument. This should be a
+ skin component and the skin's name is taken from this
+ component. The effect of this argument is a leading
+ ``++skin++[skinname]/`` segment in the path-part of the URL.
+ When the argument is not passed, whatever skin is currently set
+ on the request will be effective in the URL.
+
+ When passing ``None`` whatever skin is currently effective will
+ be removed from the URLs.
+
Optionally pass a `data` keyword argument which gets added to
the URL as a CGI query string.
@@ -110,7 +120,7 @@
# create URL to view on context
obj = self.context
- return util.url(self.request, obj, name, data)
+ return util.url(self.request, obj, name, skin, data)
More information about the Grok-dev
mailing list