[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