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">&lt;<a href="mailto:janwijbrand@gmail.com" target="_blank">janwijbrand@gmail.com</a>&gt;</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&#39;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>
 &quot;&quot;&quot;Grok utility functions.<br>
 &quot;&quot;&quot;<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 += &#39;/&#39; + urllib.quote(name.encode(&#39;utf-8&#39;),<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(&#39;/++skin++&#39;):<br>
+            # Find next / in the path.<br>
+            idx = path.find(&#39;/&#39;, 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 = &#39;/++skin++%s%s&#39; % (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>
   &gt;&gt;&gt; browser.contents<br>
   &#39;11&#39;<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>
+  &gt;&gt;&gt;<br>
browser.open(&#39;<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>&#39;)<br>
+  &gt;&gt;&gt; browser.contents<br>
+  &quot;I&#39;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>&quot;<br>
+<br>
+We get the views manually so we can do a greater variety of url() calls:<br>
+<br>
+  &gt;&gt;&gt; from zope.publisher.browser import applySkin<br>
+  &gt;&gt;&gt; request = TestRequest()<br>
+  &gt;&gt;&gt; applySkin(request, URLTestingSkin)<br>
+  &gt;&gt;&gt; # Shifting names normally happens during URL traversal.<br>
+  &gt;&gt;&gt; request._traversed_names = [&#39;++skin++urltesting&#39;]<br>
+  &gt;&gt;&gt; request.shiftNameToApplication()<br>
+  &gt;&gt;&gt; index_view = component.getMultiAdapter((manfred, request),<br>
name=&#39;test&#39;)<br>
+  &gt;&gt;&gt; index_view.url()<br>
+  &#39;<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>&#39;<br>
+<br>
+Explicitely remove the skin part:<br>
+<br>
+  &gt;&gt;&gt; index_view.url(skin=None)<br>
+  &#39;<a href="http://127.0.0.1/herd/manfred/test" target="_blank">http://127.0.0.1/herd/manfred/test</a>&#39;<br>
+<br>
+Use another skin:<br>
+<br>
+  &gt;&gt;&gt; index_view.url(skin=AnotherURLTestingSkin)<br>
+  &#39;<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>&#39;<br>
+<br>
+Use something that is not a skin will fail:<br>
+<br>
+  &gt;&gt;&gt; index_view.url(skin=&#39;foobar&#39;)<br>
+  Traceback (most recent call last):<br>
+  ...<br>
+  AttributeError: &#39;str&#39; object has no attribute &#39;queryTaggedValue&#39;<br>
+<br>
 &quot;&quot;&quot;<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(&#39;&lt;p tal:replace=&quot;view/url&quot; /&gt;&#39;)<br>
+<br>
+class URLTestingSkin(grok.IBrowserRequest):<br>
+    grok.skin(&#39;urltesting&#39;)<br>
+<br>
+class AnotherURLTestingSkin(grok.IBrowserRequest):<br>
+    grok.skin(&#39;anotherurltesting&#39;)<br>
+<br>
+class URLTestingViewOnASkin(grok.View):<br>
+    grok.layer(URLTestingSkin)<br>
+    <a href="http://grok.name" target="_blank">grok.name</a>(&#39;test&#39;)<br>
+<br>
+    def render(self):<br>
+        return u&quot;I&#39;m on a url testing skin: %s&quot; % 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>
         &quot;&quot;&quot;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&#39;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>