Skip to content

Commit

Permalink
svn merge -r 121965:121982 svn+ssh://svn.zope.org/repos/main/grokcore…
Browse files Browse the repository at this point in the history
….view/branches/jw-contentproviders .
  • Loading branch information
janwijbrand committed Jun 28, 2011
2 parents 30930d9 + c5e23a0 commit 82cd06a
Show file tree
Hide file tree
Showing 29 changed files with 542 additions and 20 deletions.
3 changes: 1 addition & 2 deletions CHANGES.txt
Expand Up @@ -4,8 +4,7 @@ Changes
2.6 (unreleased)
----------------

- Nothing changed yet.

- Add the ContentProvider component.

2.5 (2011-04-04)
----------------
Expand Down
23 changes: 13 additions & 10 deletions setup.py
Expand Up @@ -11,12 +11,13 @@ def read(*rnames):
)

install_requires = [
'setuptools',
'grokcore.component >= 2.1',
'grokcore.security >= 1.5',
'martian >= 0.13',
'setuptools',
'zope.browserresource >= 3.9.0',
'zope.component',
'zope.contentprovider',
'zope.interface',
'zope.pagetemplate',
'zope.ptresource >= 3.9.0',
Expand All @@ -26,18 +27,18 @@ def read(*rnames):
]

tests_require = [
'zope.app.wsgi',
'zope.container',
'zope.securitypolicy',
'zope.site',
'zope.testing',
'zope.login',
'zope.configuration',
'zope.app.appsetup',
'zope.app.publication',
'zope.app.wsgi',
'zope.browserpage',
'zope.configuration',
'zope.container',
'zope.login',
'zope.password',
'zope.principalregistry',
'zope.securitypolicy',
'zope.site',
'zope.testing',
]

publication_require = [
Expand Down Expand Up @@ -68,6 +69,8 @@ def read(*rnames):
zip_safe=False,
install_requires=install_requires,
tests_require=tests_require,
extras_require={'test': tests_require,
'security_publication': publication_require},
extras_require={
'test': tests_require,
'security_publication': publication_require
},
)
4 changes: 3 additions & 1 deletion src/grokcore/view/__init__.py
Expand Up @@ -19,7 +19,9 @@
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.publisher.interfaces.browser import IDefaultBrowserLayer

from grokcore.view.components import View, ViewSupport
from grokcore.view.components import ViewSupport
from grokcore.view.components import View
from grokcore.view.components import ContentProvider
from grokcore.view.components import PageTemplate, PageTemplateFile
from grokcore.view.interfaces import IGrokSecurityView
from grokcore.view.components import DirectoryResource
Expand Down
47 changes: 47 additions & 0 deletions src/grokcore/view/components.py
Expand Up @@ -22,6 +22,7 @@
from zope import interface
from zope.browserresource import directory
from zope.browserresource.interfaces import IResourceFactoryFactory
from zope.contentprovider.provider import ContentProviderBase
from zope.pagetemplate import pagetemplate, pagetemplatefile
from zope.pagetemplate.engine import TrustedAppPT
from zope.ptresource.ptresource import PageTemplateResourceFactory
Expand Down Expand Up @@ -229,6 +230,52 @@ def _annotateGrokInfo(self, name, location):
def _initFactory(self, factory):
pass

class ContentProvider(ContentProviderBase):
interface.implements(interfaces.IContentProvider)

template = None

def __init__(self, context, request, view):
super(ContentProvider, self).__init__(context, request, view)
self.context = context
self.request = request
self.view = view
self.__name__ = self.__view_name__
self.static = component.queryAdapter(
self.request,
interface.Interface,
name=self.module_info.package_dotted_name,
)

def default_namespace(self):
namespace = {}
namespace['context'] = self.context
namespace['provider'] = self
namespace['request'] = self.request
namespace['static'] = self.static
namespace['view'] = self.view
return namespace

def namespace(self):
return {}

def _render_template(self):
return self.template.render(self)

def render(self, **kwargs):
"""A content provider can either be rendered by an associated
template, or it can implement this method to render itself from
Python. This is useful if the view's output isn't XML/HTML but
something computed in Python (plain text, PDF, etc.)
render() can take arbitrary keyword parameters which will be
filled in from the request (in that case they *must* be
present in the request).
"""
return self._render_template()

render.base_method = True


class GrokTemplate(BaseTemplate):
"""A slightly more advanced page template
Expand Down
1 change: 1 addition & 0 deletions src/grokcore/view/configure.zcml
Expand Up @@ -2,6 +2,7 @@
xmlns="http://namespaces.zope.org/zope"
xmlns:grok="http://namespaces.zope.org/grok">

<include package="zope.contentprovider" />
<include package="zope.browserresource" />

<!-- ZPT support -->
Expand Down
8 changes: 8 additions & 0 deletions src/grokcore/view/ftesting.zcml
Expand Up @@ -15,6 +15,7 @@
<include package="grokcore.view" file="meta.zcml" />

<include package="zope.security" />
<include package="zope.securitypolicy" />
<include package="zope.principalregistry" />
<include package="zope.login" />
<include package="zope.password" />
Expand All @@ -23,10 +24,17 @@
<include package="zope.publisher" />
<include package="zope.traversing" />
<include package="zope.traversing.browser" />
<include package="zope.annotation" />
<include package="zope.app.appsetup" />
<include package="zope.app.publication" />

<include package="grokcore.view" />

<!-- We define our test permission here (don't have grok.Role) -->
<permission id="bone.gold" title="Gold Bone" />
<role id="grok.BoneOwner" title="Bone Owner" />
<grant role="grok.BoneOwner" permission="bone.gold" />

<grok:grok package="grokcore.view.ftests" />

<securityPolicy
Expand Down
1 change: 1 addition & 0 deletions src/grokcore/view/ftests/contentprovider/__init__.py
@@ -0,0 +1 @@
#package
64 changes: 64 additions & 0 deletions src/grokcore/view/ftests/contentprovider/context.py
@@ -0,0 +1,64 @@
"""
Content providers auto-associate with the context object that may be in a
module.
Set up the model object to view::
>>> root = getRootFolder()
>>> root['cave'] = cave = Cave()
We also set up another model that the content provider should not be auto-
associated with::
>>> root['club'] = club = Club()
Let's get a content rpvoider associated with ``cave``::
>>> from zope.publisher.browser import TestRequest
>>> request = TestRequest()
>>> from zope import component
>>> view = component.getMultiAdapter((cave, request), name='index')
>>> from zope.contentprovider.interfaces import IContentProvider
>>> mgr = component.getMultiAdapter((cave, request, view), IContentProvider,
... 'manage.cavemen')
We cannot get this content provider for ``club``, as there is none associated
with that as a context for the given name::
>>> component.queryMultiAdapter((club, request, view), IContentProvider,
... 'manage.caveman') is None
True
Ther is for one with a different name, using an explicit grok.context directive
however:
>>> mgr = component.getMultiAdapter((club, request, view), IContentProvider,
... 'manage.clubmen')
"""

import zope.interface
import grokcore.view as grok
from context_fixture import Club

class CavemenContentProvider(grok.ContentProvider):
grok.name('manage.cavemen')

def render(self):
pass

class Cave(grok.Context):
pass

class Index(grok.View):
grok.context(zope.interface.Interface)

def render(self):
return u"Hi"

class ClubContentProvider(grok.ContentProvider):
grok.name('manage.clubmen')
grok.context(Club)

def render(self):
pass
10 changes: 10 additions & 0 deletions src/grokcore/view/ftests/contentprovider/context_fixture.py
@@ -0,0 +1,10 @@
"""
This file is used by viewlet_context. It defines a model that the viewlets
and viewlet manager should *not* be associating with.
"""

import grokcore.view as grok

class Club(grok.Context):
pass

49 changes: 49 additions & 0 deletions src/grokcore/view/ftests/contentprovider/interface.py
@@ -0,0 +1,49 @@
"""
Verify that associating a content provider with an view interface instead of
with a view class works as expected.
Set up the model object to view::
>>> root = getRootFolder()
>>> root['cave'] = Cave()
Viewing the cave object should result in the content provider being displayed,
as it is associated with the view's interface::
>>> from zope.app.wsgi.testlayer import Browser
>>> browser = Browser()
>>> browser.handleErrors = False
>>> browser.open("http://localhost/cave")
>>> print browser.contents
Hi
>>> browser.open("http://localhost/cave/@@secondindex")
>>> print browser.contents
Hi
"""

from zope.interface import Interface
import grokcore.view as grok

class Cave(grok.Context):
pass

class ICavemenView(Interface):
pass

class Index(grok.View):
grok.implements(ICavemenView)

class CavemenContentProvider(grok.ContentProvider):
grok.name('manage.cavemen')
grok.view(ICavemenView)

def render(self):
return u'Hi'

class SecondIndex(grok.View):
grok.implements(ICavemenView)

def render(self):
return u'Hi'
@@ -0,0 +1 @@
<tal:block content="provider:manage.cavemen" />
51 changes: 51 additions & 0 deletions src/grokcore/view/ftests/contentprovider/layer.py
@@ -0,0 +1,51 @@
"""
Content providers can be discriminated based on layer too::
>>> root = getRootFolder()
>>> root['wilma'] = CaveWoman()
Traverse to the view on the model object. We get the content provider
registered for the default layer::
>>> from zope.app.wsgi.testlayer import Browser
>>> browser = Browser()
>>> browser.handleErrors = False
>>> browser.open("http://localhost/wilma/@@caveview")
>>> print browser.contents
Soup pot
Traverse to the view on the model object. We get the content provider
registered for the "boneskin" layer::
>>> browser.open("http://localhost/++skin++boneskin/wilma/@@caveview")
>>> print browser.contents
Layered pot
"""

from zope.interface import Interface
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
import grokcore.view as grok

class CaveWoman(grok.Context):
pass

class CaveView(grok.View):
grok.context(Interface)

class Pot(grok.ContentProvider):
grok.context(Interface)

def render(self):
return u"Soup pot"

class IBoneLayer(IDefaultBrowserLayer):
grok.skin('boneskin')

class LayeredPot(grok.ContentProvider):
grok.name('pot')
grok.context(Interface)
grok.layer(IBoneLayer)

def render(self):
return u"Layered pot"
@@ -0,0 +1 @@
<span tal:replace="provider:pot" />

0 comments on commit 82cd06a

Please sign in to comment.