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:
helmutm 2006-03-26 16:43:43 +00:00
parent a551d4548a
commit 10c6434de9
10 changed files with 225 additions and 43 deletions

View file

@ -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
View 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>

View file

@ -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"
/>

View file

@ -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

View file

@ -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__':

View file

@ -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')

View file

@ -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>

View 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')

View file

@ -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> &middot;
<b><a href="http://www.zope.org/DevHome/Wikis/DevSite/Projects/ComponentArchitecture/FrontPage">Zope 3</a></b>
</metal:footer>
</metal:footer><br />&nbsp;
</div>
</body>

View file

@ -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__':