View controller now basically functional, with macro registries for CSS and JS resources
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@1146 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
a551d4548a
commit
10c6434de9
10 changed files with 225 additions and 43 deletions
|
@ -6,3 +6,88 @@ We first set up a test and working environment:
|
|||
>>> from zope.app import zapi
|
||||
>>> from zope.app.testing import ztapi
|
||||
|
||||
The View Controller
|
||||
-------------------
|
||||
|
||||
There is a special view class that does not directly adapt to a real context
|
||||
(i.e. typically a content) object but to a view instead. Thus it can provide
|
||||
additional functionality e.g. for templates without the view being aware
|
||||
of it.
|
||||
|
||||
This view controller (or controller view) is typically provided by the
|
||||
Controller class. Let's use the Controller sub-class from the Liquid skin
|
||||
because this already provides some predefined stuff:
|
||||
|
||||
>>> from cybertools.browser.liquid.controller import Controller
|
||||
|
||||
Before creating a controller we have to set up a context object and
|
||||
a view:
|
||||
|
||||
>>> class SomeObject(object): pass
|
||||
>>> obj = SomeObject()
|
||||
>>> class View(object):
|
||||
... def __init__(self, context, request):
|
||||
... self.context = context
|
||||
... self.request = request
|
||||
>>> from zope.publisher.browser import TestRequest
|
||||
>>> request = TestRequest()
|
||||
>>> view = View(obj, request)
|
||||
|
||||
>>> controller = Controller(view, request)
|
||||
>>> controller.view is view
|
||||
True
|
||||
>>> controller.context is obj
|
||||
True
|
||||
>>> controller.request is request
|
||||
True
|
||||
|
||||
The controller registers itself with the view:
|
||||
|
||||
>>> view.controller is controller
|
||||
True
|
||||
|
||||
The resourceBase attribute gives a base URL to which one can simply append
|
||||
the name of a resource.
|
||||
|
||||
>>> controller.resourceBase
|
||||
'http://127.0.0.1/@@/'
|
||||
|
||||
If necessary, a ++skin++xxx path element is provided
|
||||
with the resourceBase to care for proper skin setting. This will work only
|
||||
(and is only necessary) when the skin is set programmatically
|
||||
|
||||
>>> class DummySkin(object): pass
|
||||
>>> skin = DummySkin; skin.__name__ = 'dummy'
|
||||
|
||||
Note that we make heavy use of Lazy attributes, so we have to get a new
|
||||
controller object to get an updated setting:
|
||||
|
||||
>>> controller = Controller(view, request)
|
||||
>>> controller.skin = skin
|
||||
>>> controller.resourceBase
|
||||
'http://127.0.0.1/++skin++dummy/@@/'
|
||||
|
||||
The controller may be used as a provider for content elements using
|
||||
ZPT macros:
|
||||
|
||||
>>> cssMacros = controller.macros['css']
|
||||
>>> len(cssMacros)
|
||||
4
|
||||
>>> m1 = cssMacros[0]
|
||||
>>> print m1.name, m1.media, m1.resourceName
|
||||
css all zope3_tablelayout.css
|
||||
|
||||
Calling a macro provided by Controller.macros[] returns the real ZPT macro:
|
||||
|
||||
>>> m1()
|
||||
[...base_macros.pt...css...]
|
||||
|
||||
The pre-set collection of macros for a certain slot may be extended:
|
||||
|
||||
>>> controller.macros.register('css', resourceName='node.css', media='all')
|
||||
>>> len(controller.macros['css'])
|
||||
5
|
||||
>>> m5 = cssMacros[4]
|
||||
>>> print m5.name, m5.media, m5.resourceName
|
||||
css all node.css
|
||||
|
||||
|
|
15
browser/base_macros.pt
Normal file
15
browser/base_macros.pt
Normal file
|
@ -0,0 +1,15 @@
|
|||
<metal:css define-macro="css">
|
||||
<style type="text/css" media="all"
|
||||
tal:attributes="media macro/media"
|
||||
tal:content="string:@import url(${resourceBase}${macro/resourceName});">
|
||||
@import url(some.css);
|
||||
</style>
|
||||
</metal:css>
|
||||
|
||||
|
||||
<metal:js define-macro="js">
|
||||
<script type="text/javascript" src="node.js"
|
||||
tal:attributes="src string:${resourceBase}${macro/resourceName}">
|
||||
</script>
|
||||
</metal:js>
|
||||
|
|
@ -6,7 +6,8 @@
|
|||
i18n_domain="zope"
|
||||
>
|
||||
|
||||
<page name="controller" for="*"
|
||||
<page name="controller"
|
||||
for="zope.app.publisher.interfaces.browser.IBrowserView"
|
||||
class="cybertools.browser.controller.Controller"
|
||||
permission="zope.Public"
|
||||
/>
|
||||
|
|
|
@ -23,24 +23,63 @@ $Id$
|
|||
"""
|
||||
|
||||
from zope.app import zapi
|
||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
|
||||
|
||||
class Controller(object):
|
||||
|
||||
def __init__(self, context, request):
|
||||
self.view = context # the controller is adapted to a view
|
||||
self.context = context.context
|
||||
self.request = request
|
||||
self.skin = None # may be overwritten by the view
|
||||
context.controller = self # notify the view
|
||||
|
||||
@Lazy
|
||||
def macros(self):
|
||||
return Macros(self)
|
||||
|
||||
@Lazy
|
||||
def resourceBase(self):
|
||||
skinSetter = self.skin and ('/++skin++' + self.skin.__name__) or ''
|
||||
# TODO: put '/@@' etc after path to site instead of directly after URL0
|
||||
return self.request.URL[0] + skinSetter + '/@@/'
|
||||
|
||||
class Macros(object):
|
||||
|
||||
class Macros(dict):
|
||||
|
||||
# TODO: move to namedTemplate
|
||||
standardTemplate = ViewPageTemplateFile('base_macros.pt')
|
||||
|
||||
def __init__(self, controller):
|
||||
self.controller = controller
|
||||
self.context = controller.context
|
||||
self.request = controller.request
|
||||
|
||||
def register(self, slot, template=None, name=None, position=None, **kw):
|
||||
if template is None:
|
||||
template = self.standardTemplate
|
||||
if name is None:
|
||||
name = slot
|
||||
macro = Macro(template, name, **kw)
|
||||
if slot not in self:
|
||||
self[slot] = []
|
||||
if position is None:
|
||||
self[slot].append(macro)
|
||||
else:
|
||||
self[slot].insert(position, macro)
|
||||
|
||||
class Macro(object):
|
||||
|
||||
def __init__(self, template, name, **kw):
|
||||
self.template = template
|
||||
self.name = name
|
||||
for k in kw:
|
||||
setattr(self, k, kw[k])
|
||||
|
||||
@Lazy
|
||||
def css(self):
|
||||
return 'Here comes the CSS stuff...'
|
||||
|
||||
def macro(self):
|
||||
return self.template.macros[self.name]
|
||||
|
||||
def __call__(self):
|
||||
return self.macro
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from zope.app.testing.functional import FunctionalDocFileSuite
|
|||
|
||||
def test_suite():
|
||||
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
||||
browser = FunctionalDocFileSuite('liquid.txt', optionflags=flags)
|
||||
browser = FunctionalDocFileSuite('liquid/README.txt', optionflags=flags)
|
||||
return unittest.TestSuite((browser,))
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -11,7 +11,7 @@ We first set up a test and working environment:
|
|||
>>> browser = Browser()
|
||||
>>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
|
||||
|
||||
We can now open a page using the CyberView skin:
|
||||
We can now open a page using the Liquid skin:
|
||||
|
||||
>>> browser.addHeader('Accept-Language', 'en-US')
|
||||
>>> browser.open('http://localhost/++skin++Liquid')
|
|
@ -22,13 +22,15 @@
|
|||
permission="zope.View"
|
||||
layer="liquid" />
|
||||
|
||||
<resource name="base.css" file="base.css"
|
||||
layer="liquid" />
|
||||
<page name="controller"
|
||||
for="zope.app.publisher.interfaces.browser.IBrowserView"
|
||||
class="cybertools.browser.liquid.controller.Controller"
|
||||
permission="zope.Public"
|
||||
layer="liquid"
|
||||
/>
|
||||
|
||||
<resource name="print.css" file="print.css"
|
||||
layer="liquid" />
|
||||
|
||||
<resource name="custom.css" file="custom.css"
|
||||
layer="liquid" />
|
||||
<resource name="base.css" file="base.css" layer="liquid" />
|
||||
<resource name="print.css" file="print.css" layer="liquid" />
|
||||
<resource name="custom.css" file="custom.css" layer="liquid" />
|
||||
|
||||
</configure>
|
||||
|
|
45
browser/liquid/controller.py
Normal file
45
browser/liquid/controller.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
#
|
||||
# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
"""
|
||||
View controller for the Liquid skin.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from cybertools.browser.controller import Controller as BaseController
|
||||
|
||||
|
||||
class Controller(BaseController):
|
||||
|
||||
def __init__(self, context, request):
|
||||
super(Controller, self).__init__(context, request)
|
||||
self.setupCss()
|
||||
self.setupJs()
|
||||
|
||||
def setupCss(self):
|
||||
macros = self.macros
|
||||
params = [('zope3_tablelayout.css', 'all'),
|
||||
('base.css', 'screen'),
|
||||
('custom.css', 'all'), ('print.css', 'print')]
|
||||
for param in params:
|
||||
macros.register('css', resourceName=param[0], media=param[1])
|
||||
|
||||
def setupJs(self):
|
||||
#self.macros['js'] = []
|
||||
self.macros.register('js', resourceName='zope3.js')
|
|
@ -1,46 +1,39 @@
|
|||
<metal:block define-macro="page"><metal:block define-slot="doctype"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"></metal:block>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
|
||||
i18n:domain="zope"
|
||||
tal:define="resourceBase view/resourceBase | string:${request/URL/0}/@@/">
|
||||
tal:define="controller nocall:view/@@controller;
|
||||
resourceBase controller/resourceBase">
|
||||
|
||||
<metal:block metal:define-slot="settings" />
|
||||
|
||||
<head metal:define-macro="head">
|
||||
<title metal:define-slot="title"
|
||||
tal:content="options/title|view/title|context/title|default">
|
||||
Powered by Zope 3
|
||||
</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<style type="text/css" media="all"
|
||||
tal:content="string:@import url(${resourceBase}zope3_tablelayout.css);">
|
||||
@import url(zope3_tablelayout.css);
|
||||
</style>
|
||||
|
||||
<tal:css repeat="macro controller/macros/css">
|
||||
<metal:css use-macro="macro" />
|
||||
</tal:css>
|
||||
<metal:block define-slot="ecmascript_slot" />
|
||||
|
||||
<metal:block metal:define-slot="headers">
|
||||
<script type="text/javascript" src="zope3.js"
|
||||
tal:attributes="src string:${resourceBase}zope3.js" >
|
||||
</script>
|
||||
<tal:js repeat="macro controller/macros/js">
|
||||
<metal:css use-macro="macro" />
|
||||
</tal:js>
|
||||
<span metal:use-macro="context/@@standard_macros/navigation_tree_js" />
|
||||
</metal:block>
|
||||
<metal:block define-slot="style_slot" />
|
||||
<style type="text/css" media="screen"
|
||||
tal:content="string:@import url(${resourceBase}base.css);">
|
||||
@import url(base.css);
|
||||
</style>
|
||||
<style type="text/css" media="all"
|
||||
tal:content="string:@import url(${resourceBase}custom.css);">
|
||||
@import url(custom.css);
|
||||
</style>
|
||||
<style type="text/css" media="print"
|
||||
tal:content="string:@import url(${resourceBase}print.css);">
|
||||
@import url(print.css);
|
||||
</style>
|
||||
<metal:block define-slot="ecmascript_slot" />
|
||||
|
||||
<link rel="icon" type="image/png"
|
||||
tal:attributes="href string:${resourceBase}favicon.png" />
|
||||
</head>
|
||||
|
||||
<body tal:attributes="onload body_onload | nothing">
|
||||
|
||||
<div id="header">
|
||||
<div class="top">
|
||||
<div id="global">
|
||||
<div class="top" metal:define-slot="top">
|
||||
<a href="#" name="top" metal:define-slot="logo"
|
||||
tal:attributes="href string:${request/URL/1}"><img
|
||||
src="logo.gif" border="0"
|
||||
|
@ -48,6 +41,7 @@
|
|||
tal:attributes="src string:${resourceBase}logo.gif" /></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="menu">
|
||||
<metal:menu define-slot="navigators">
|
||||
<span metal:use-macro="context/@@standard_macros/navigation_tree_box" />
|
||||
|
@ -73,7 +67,7 @@
|
|||
</tal:block>
|
||||
</div>
|
||||
|
||||
<!--<tal:test content="views/controller/macros/css" />-->
|
||||
<!--<h2 tal:content="controller/macros/css" />-->
|
||||
<metal:content define-slot="body">Here comes the body</metal:content>
|
||||
|
||||
</div>
|
||||
|
@ -86,7 +80,7 @@
|
|||
<metal:footer define-slot="footer">
|
||||
Powered by <b><a href="http://www.python.org">Python</a></b> ·
|
||||
<b><a href="http://www.zope.org/DevHome/Wikis/DevSite/Projects/ComponentArchitecture/FrontPage">Zope 3</a></b>
|
||||
</metal:footer>
|
||||
</metal:footer><br />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# $Id$
|
||||
|
||||
import unittest
|
||||
import unittest, doctest
|
||||
from zope.testing.doctestunit import DocFileSuite
|
||||
from zope.app.testing import ztapi
|
||||
from zope.interface.verify import verifyClass
|
||||
|
@ -8,8 +8,9 @@ from zope.app import zapi
|
|||
|
||||
|
||||
def test_suite():
|
||||
flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
|
||||
return unittest.TestSuite((
|
||||
DocFileSuite('README.txt'),
|
||||
DocFileSuite('README.txt', optionflags=flags),
|
||||
))
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Add table
Reference in a new issue