[Checkins] SVN: grok/trunk/ * Implement .url() method on views.
Martijn Faassen
faassen at infrae.com
Tue Oct 31 09:07:20 EST 2006
Log message for revision 71005:
* Implement .url() method on views.
* Fix bug where IAbsoluteURL on grok Views wouldn't work due to lack
of __name__. Since we already have __name__ in use, override
IAbsoluteURL for grok Views so we use __view_name__. Make sure
such exist on views registered by grok.
* the work requires the introduction of a configure.zcml for grok;
please re-run your bin/buildout.
Changed:
U grok/trunk/buildout.cfg
U grok/trunk/src/grok/_grok.py
U grok/trunk/src/grok/components.py
A grok/trunk/src/grok/configure.zcml
U grok/trunk/src/grok/ftests/test_grok_functional.py
A grok/trunk/src/grok/ftests/url/
A grok/trunk/src/grok/ftests/url/__init__.py
A grok/trunk/src/grok/ftests/url/url.py
U grok/trunk/src/grok/interfaces.py
-=-
Modified: grok/trunk/buildout.cfg
===================================================================
--- grok/trunk/buildout.cfg 2006-10-31 14:04:36 UTC (rev 71004)
+++ grok/trunk/buildout.cfg 2006-10-31 14:07:19 UTC (rev 71005)
@@ -20,6 +20,7 @@
grokwiki
zcml = *
+ grok
grok-meta
grokwiki
Modified: grok/trunk/src/grok/_grok.py
===================================================================
--- grok/trunk/src/grok/_grok.py 2006-10-31 14:04:36 UTC (rev 71004)
+++ grok/trunk/src/grok/_grok.py 2006-10-31 14:07:19 UTC (rev 71005)
@@ -279,6 +279,8 @@
factory)
view_name = util.class_annotation(factory, 'grok.name', factory_name)
+ # __view_name__ is needed to support IAbsoluteURL on views
+ factory.__view_name__ = view_name
component.provideAdapter(factory,
adapts=(view_context, IDefaultBrowserLayer),
provides=interface.Interface,
@@ -305,6 +307,7 @@
templates.markAssociated(name)
+ TemplateView.__view_name__ = name
component.provideAdapter(TemplateView,
adapts=(context, IDefaultBrowserLayer),
provides=interface.Interface,
Modified: grok/trunk/src/grok/components.py
===================================================================
--- grok/trunk/src/grok/components.py 2006-10-31 14:04:36 UTC (rev 71004)
+++ grok/trunk/src/grok/components.py 2006-10-31 14:07:19 UTC (rev 71005)
@@ -16,6 +16,7 @@
import persistent
import types
+import urllib
from zope import component
from zope import interface
@@ -29,6 +30,9 @@
from zope.formlib import form
from zope.formlib.namedtemplate import INamedTemplate
from zope.schema.interfaces import IField
+from zope.traversing.browser.interfaces import IAbsoluteURL
+from zope.traversing.browser.absoluteurl import AbsoluteURL
+from zope.traversing.browser.absoluteurl import _safe as SAFE_URL_CHARACTERS
from zope.app.pagetemplate.engine import TrustedAppPT
from zope.app.publisher.browser import getDefaultViewName
@@ -38,7 +42,7 @@
from zope.app.container.btree import BTreeContainer
from zope.app.container.contained import Contained
-from grok import util, security
+from grok import util, security, interfaces
class Model(Contained, persistent.Persistent):
@@ -67,7 +71,8 @@
class View(BrowserPage):
-
+ interface.implements(interfaces.IGrokView)
+
def __init__(self, context, request):
# Jim would say: WAAAAAAAAAAAAH!
self.context = removeSecurityProxy(context)
@@ -94,10 +99,42 @@
# XXX give nice error message if template is None
return self.template.macros[key]
+ def url(self, obj=None, name=None):
+ # if the first argument is a string, that's the name. There should
+ # be no second argument
+ if isinstance(obj, basestring):
+ if name is not None:
+ raise TypeError(
+ 'url() takes either obj argument, obj, string arguments, '
+ 'or string argument')
+ name = obj
+ obj = None
+
+ if name is None and obj is None:
+ # create URL to view itself
+ obj = self
+ elif name is not None and obj is None:
+ # create URL to view on context
+ obj = self.context
+ url = component.getMultiAdapter((obj, self.request), IAbsoluteURL)()
+ if name is None:
+ # URL to obj itself
+ return url
+ # URL to view on obj
+ return url + '/' + urllib.quote(name.encode('utf-8'),
+ SAFE_URL_CHARACTERS)
+
def before(self):
pass
+class GrokViewAbsoluteURL(AbsoluteURL):
+ def _getContextName(self, context):
+ return getattr(context, '__view_name__', None)
+ # XXX breadcrumbs method on AbsoluteURL breaks as it does not use
+ # _getContextName to get to the name of the view. What does breadcrumbs do?
+
+
class XMLRPC(object):
pass
Added: grok/trunk/src/grok/configure.zcml
===================================================================
--- grok/trunk/src/grok/configure.zcml 2006-10-31 14:04:36 UTC (rev 71004)
+++ grok/trunk/src/grok/configure.zcml 2006-10-31 14:07:19 UTC (rev 71005)
@@ -0,0 +1,26 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope">
+
+ <!-- we register special IAbsoluteURL views on grok views so that
+ can have them inspect __view_name__ instead of __name__.
+ __name__ is already used as the class name, and overriding it
+ may make error messages more confusing. -->
+
+ <view
+ for=".interfaces.IGrokView"
+ name="absolute_url"
+ factory=".components.GrokViewAbsoluteURL"
+ type="zope.publisher.interfaces.http.IHTTPRequest"
+ permission="zope.Public"
+ allowed_interface="zope.traversing.browser.interfaces.IAbsoluteURL"
+ />
+
+ <view
+ for=".interfaces.IGrokView"
+ factory=".components.GrokViewAbsoluteURL"
+ type="zope.publisher.interfaces.http.IHTTPRequest"
+ permission="zope.Public"
+ provides="zope.traversing.browser.interfaces.IAbsoluteURL"
+ />
+
+</configure>
Modified: grok/trunk/src/grok/ftests/test_grok_functional.py
===================================================================
--- grok/trunk/src/grok/ftests/test_grok_functional.py 2006-10-31 14:04:36 UTC (rev 71004)
+++ grok/trunk/src/grok/ftests/test_grok_functional.py 2006-10-31 14:07:19 UTC (rev 71005)
@@ -56,7 +56,7 @@
def test_suite():
suite = unittest.TestSuite()
- for name in ['view', 'static', 'xmlrpc', 'traversal', 'form']:
+ for name in ['view', 'static', 'xmlrpc', 'traversal', 'form', 'url']:
suite.addTest(suiteFromPackage(name))
return suite
Added: grok/trunk/src/grok/ftests/url/__init__.py
===================================================================
--- grok/trunk/src/grok/ftests/url/__init__.py 2006-10-31 14:04:36 UTC (rev 71004)
+++ grok/trunk/src/grok/ftests/url/__init__.py 2006-10-31 14:07:19 UTC (rev 71005)
@@ -0,0 +1 @@
+#
Added: grok/trunk/src/grok/ftests/url/url.py
===================================================================
--- grok/trunk/src/grok/ftests/url/url.py 2006-10-31 14:04:36 UTC (rev 71004)
+++ grok/trunk/src/grok/ftests/url/url.py 2006-10-31 14:07:19 UTC (rev 71005)
@@ -0,0 +1,124 @@
+# -*- coding: UTF-8 -*-
+"""
+Views have a method that can be used to construct URLs:
+
+ >>> import grok
+ >>> grok.grok('grok.ftests.url.url')
+
+ >>> from grok.ftests.url.url import Herd, Mammoth
+ >>> herd = Herd()
+ >>> getRootFolder()['herd'] = herd
+ >>> manfred = Mammoth()
+ >>> herd['manfred'] = manfred
+
+The views in this test implement self.url():
+
+ >>> from zope.testbrowser.testing import Browser
+ >>> browser = Browser()
+ >>> browser.handleErrors = False
+ >>> browser.open("http://localhost/herd/manfred/index")
+ >>> print browser.contents
+ http://localhost/herd/manfred/index
+ >>> browser.open("http://localhost/herd/manfred/another")
+ >>> print browser.contents
+ http://localhost/herd/manfred/another
+ >>> browser.open("http://localhost/herd/manfred/yet_another")
+ >>> print browser.contents
+ http://localhost/herd/manfred/yet_another
+
+We get the views manually so we can do a greater variety of url() calls:
+
+ >>> from zope import component
+ >>> from zope.publisher.browser import TestRequest
+ >>> request = TestRequest()
+ >>> index_view = component.getMultiAdapter((manfred, request), name='index')
+ >>> index_view.url()
+ 'http://127.0.0.1/herd/manfred/index'
+ >>> another_view = component.getMultiAdapter((manfred, request),
+ ... name='another')
+ >>> another_view.url()
+ 'http://127.0.0.1/herd/manfred/another'
+ >>> yet_another_view = component.getMultiAdapter((manfred, request),
+ ... name='yet_another')
+ >>> yet_another_view.url()
+ 'http://127.0.0.1/herd/manfred/yet_another'
+
+Now let's get a URL for a specific object:
+
+ >>> index_view.url(manfred)
+ 'http://127.0.0.1/herd/manfred'
+
+This works with any other view too (as they share the same request):
+
+ >>> another_view.url(manfred)
+ 'http://127.0.0.1/herd/manfred'
+
+This shows that the default argument is the view itself:
+
+ >>> another_view.url(another_view)
+ 'http://127.0.0.1/herd/manfred/another'
+
+We can get the URL for any object in content-space:
+
+ >>> another_view.url(herd)
+ 'http://127.0.0.1/herd'
+
+We can also pass a name along with this, to generate a URL to a
+particular view on the object:
+
+ >>> another_view.url(herd, 'something')
+ 'http://127.0.0.1/herd/something'
+
+It works properly in the face of non-ascii characters in URLs:
+
+ >>> url = another_view.url(herd, unicode('árgh', 'UTF-8'))
+ >>> url
+ 'http://127.0.0.1/herd/%C3%A1rgh'
+ >>> import urllib
+ >>> expected = unicode('http://127.0.0.1/herd/árgh', 'UTF-8')
+ >>> urllib.unquote(url).decode('utf-8') == expected
+ True
+
+It's also possible to just pass in a name. In this case, a URL to that
+view on the context object will be constructed:
+
+ >>> another_view.url('yet_another_view')
+ 'http://127.0.0.1/herd/manfred/yet_another_view'
+
+Some combinations of arguments just don't make sense:
+
+ >>> another_view.url('foo', 'bar')
+ Traceback (most recent call last):
+ ...
+ TypeError: url() takes either obj argument, obj, string arguments, or
+ string argument
+ >>> another_view.url('foo', herd)
+ Traceback (most recent call last):
+ ...
+ TypeError: url() takes either obj argument, obj, string arguments, or
+ string argument
+ >>> another_view.url(herd, 'bar', 'baz')
+ Traceback (most recent call last):
+ ...
+ TypeError: url() takes at most 3 arguments (4 given)
+
+"""
+import grok
+
+class Herd(grok.Container, grok.Model):
+ pass
+
+class Mammoth(grok.Model):
+ pass
+
+grok.context(Mammoth)
+
+class Index(grok.View):
+ def render(self):
+ return self.url()
+
+class Another(grok.View):
+ def render(self):
+ return self.url()
+
+yet_another = grok.PageTemplate('<p tal:replace="view/url" />')
Modified: grok/trunk/src/grok/interfaces.py
===================================================================
--- grok/trunk/src/grok/interfaces.py 2006-10-31 14:04:36 UTC (rev 71004)
+++ grok/trunk/src/grok/interfaces.py 2006-10-31 14:07:19 UTC (rev 71005)
@@ -128,3 +128,7 @@
def schema_fields(class_):
"""Return a list of schema fields defined for a model or view."""
+
+class IGrokView(interface.Interface):
+ """Grok views all provide this interface.
+ """
More information about the Checkins
mailing list