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