work in progress: layout managememnt

git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@2891 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2008-09-19 09:43:30 +00:00
parent 7793462e9a
commit 97cea0f183
10 changed files with 161 additions and 86 deletions

View file

@ -4,40 +4,52 @@ Layout Management
($Id$) ($Id$)
Let's start with some basic setup; the traversable adapter is needed for
rendering the page templates.
>>> from zope import component >>> from zope import component
>>> from zope.interface import Interface >>> from zope.interface import Interface
>>> from zope.traversing.adapters import DefaultTraversable
>>> component.provideAdapter(DefaultTraversable, (Interface,))
For testing we define a simple content class.
>>> class Document(object):
... text = ''
The layout management is controlled by a global utility, the layout
manager.
>>> from cybertools.composer.layout.base import LayoutManager, LayoutInstance
>>> from cybertools.composer.layout.interfaces import ILayout
>>> from cybertools.composer.layout.base import LayoutManager
>>> manager = LayoutManager() >>> manager = LayoutManager()
>>> component.provideUtility(manager) >>> component.provideUtility(manager)
>>> from zope.traversing.adapters import DefaultTraversable The layouts themselves are also specified as utilities.
>>> component.provideAdapter(DefaultTraversable, (Interface,))
>>> #from cybertools.composer.layout.browser.liquid.default import css
>>> #component.provideUtility(css, ILayout, name='css')
>>> from cybertools.composer.layout.browser.liquid.default import body
>>> component.provideUtility(body, ILayout, name='body.liquid')
>>> from cybertools.composer.layout.browser.default import footer
>>> component.provideUtility(footer, ILayout, name='footer.default')
In addition we have to provide at least one layout instance adapter that
connects a layout with the client object.
>>> component.provideAdapter(LayoutInstance, (object,))
Browser Views Browser Views
============= =============
>>> from zope.app.pagetemplate import ViewPageTemplateFile
>>> standardRenderers = ViewPageTemplateFile('browser/standard.pt').macros
>>> from cybertools.composer.layout.base import Layout
>>> from cybertools.composer.layout.interfaces import ILayout
>>> #css = Layout('page.css', renderer=standardRenderers['css'])
>>> # css = ResourceCollection('css', resourceRenderers['css'])
>>> #component.provideUtility(css, ILayout, name='css')
>>> from cybertools.composer.layout.browser.liquid.default import BodyLayout
>>> bodyLayout = BodyLayout()
>>> component.provideUtility(bodyLayout, ILayout, name='body.liquid')
>>> footerLayout = Layout('body.footer', renderer=standardRenderers['footer'])
>>> component.provideUtility(footerLayout, ILayout, name='footer.default')
>>> from cybertools.composer.layout.browser.view import Page >>> from cybertools.composer.layout.browser.view import Page
>>> from zope.publisher.browser import TestRequest >>> from zope.publisher.browser import TestRequest
>>> page = Page(None, TestRequest())
>>> page = Page(Document(), TestRequest())
>>> page() >>> page()
u'<!DOCTYPE ...>...<html ...>...</html>...' u'<!DOCTYPE ...>...<html ...>...</html>...'

View file

@ -47,37 +47,35 @@ class LayoutManager(object):
region.layouts.append(layout) region.layouts.append(layout)
return result return result
def getLayouts(self, key, **kw): def getLayouts(self, key, instance):
region = self.regions.get(key) region = self.regions.get(key)
if region is None: if region is None:
return [] return []
# TODO: filter region.layouts result = []
return region.layouts for layout in region.layouts:
if self.check(layout, instance):
result.append(layout)
return result
def register(self, layout, regionName): def check(self, layout, instance):
region = self.regions.setdefault(regionName, Region(regionName)) if instance is None or instance.checkLayout(layout):
region.layouts.append(layout) return True
class Layout(Template): class Layout(Template):
implements(ILayout) implements(ILayout)
name = '' title = description = u''
category = 'default'
renderer = None renderer = None
regionName = None
skin = 'default'
def __init__(self, regionName, **kw): def __init__(self, name, regionName, **kw):
self.name = name
self.regionName = regionName self.regionName = regionName
for k, v in kw.items(): for k, v in kw.items():
setattr(self, k, v) setattr(self, k, v)
def registerFor(self, regionName):
manager = component.getUtility(ILayoutManager)
manager.register(self, regionName)
self.regionName = regionName
class LayoutInstance(object): class LayoutInstance(object):
@ -91,3 +89,7 @@ class LayoutInstance(object):
@property @property
def renderer(self): def renderer(self):
return self.template.renderer return self.template.renderer
def checkLayout(self, layout):
return True

View file

@ -0,0 +1,3 @@
<div metal:define-macro="footer">
Some footer text.
</div>

View file

@ -27,7 +27,8 @@ from zope.app.pagetemplate import ViewPageTemplateFile
from cybertools.composer.layout.base import Layout from cybertools.composer.layout.base import Layout
standardRenderers = ViewPageTemplateFile('standard.pt').macros defaultRenderers = ViewPageTemplateFile('default.pt').macros
footer = Layout('body.footer', renderer=standardRenderers['footer']) footer = Layout('footer.default', 'body.footer',
renderer=defaultRenderers['footer'])

View file

@ -30,17 +30,8 @@ from zope.interface import implements
from cybertools.composer.layout.base import Layout from cybertools.composer.layout.base import Layout
template = ViewPageTemplateFile('default.pt') defaultRenderers = ViewPageTemplateFile('default.pt').macros
body = Layout('body.liquid', 'page.body',
class BodyLayout(Layout): renderer=defaultRenderers['body'])
regionName = 'page.body'
def __init__(self):
pass
@Lazy
def renderer(self):
return template.macros['body']

View file

@ -9,8 +9,8 @@
</title> </title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<tal:css repeat="macro view/resources/css"> <tal:css repeat="view view/layouts/css">
<metal:css use-macro="macro" /> <metal:css use-macro="view/renderer" />
</tal:css> </tal:css>
<base href="." tal:attributes="href request/URL"> <base href="." tal:attributes="href request/URL">

View file

@ -1,12 +1,7 @@
<div metal:define-macro="footer">
Some footer text.
</div>
<metal:css define-macro="css"> <metal:css define-macro="css">
<!--<style type="text/css" media="all" <!--<style type="text/css" media="all"
tal:attributes="media macro/media" tal:attributes="media view/media"
tal:content="string:@import url(${resourceBase}${macro/resourceName});"> tal:content="string:@import url(${view/page/resourceBase}${view/context/resourceName});">
@import url(some.css); @import url(some.css);
</style>--> </style>-->
</metal:css> </metal:css>

View file

@ -0,0 +1,34 @@
#
# Copyright (c) 2008 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
#
"""
Default layouts.
$Id$
"""
from zope.app.pagetemplate import ViewPageTemplateFile
from cybertools.composer.layout.base import Layout
standardRenderers = ViewPageTemplateFile('standard.pt').macros
footer = Layout('footer.default', 'body.footer',
renderer=standardRenderers['footer'])

View file

@ -27,38 +27,56 @@ from zope.interface import Interface, implements
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from zope.app.pagetemplate import ViewPageTemplateFile from zope.app.pagetemplate import ViewPageTemplateFile
from cybertools.composer.layout.base import Layout, LayoutInstance from cybertools.composer.layout.base import Layout
from cybertools.composer.layout.interfaces import ILayoutManager from cybertools.composer.layout.interfaces import ILayoutManager, ILayoutInstance
class BaseView(object): class BaseView(object):
template = ViewPageTemplateFile('base.pt') template = ViewPageTemplateFile('base.pt')
def __init__(self, context, request, name=None): page = None
parent = None
skin = None
def __init__(self, context, request, **kw):
self.context = self.__parent__ = context self.context = self.__parent__ = context
self.request = request self.request = request
if name is not None: for k, v in kw.items():
self.name = name setattr(self, k, v)
def update(self):
return True
def __call__(self): def __call__(self):
return self.template(self) return self.template(self)
def update(self):
return True
class Page(BaseView): class Page(BaseView):
macroName = 'page'
@Lazy
def rootView(self):
return self
def __call__(self): def __call__(self):
layout = Layout('page') layout = Layout('page', 'page')
layout.renderer = ViewPageTemplateFile('main.pt').macros['page'] layout.renderer = ViewPageTemplateFile('main.pt').macros[self.macroName]
instance = LayoutInstance(self.context) instance = ILayoutInstance(self.context)
instance.template = layout instance.template = layout
view = LayoutView(instance, self.request, name='page') view = LayoutView(instance, self.request, name='page',
parent=self, page=self)
view.body = view.layouts['body'][0] view.body = view.layouts['body'][0]
instance.view = view
return view.template(view) return view.template(view)
@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 LayoutView(BaseView): class LayoutView(BaseView):
@ -83,9 +101,9 @@ class LayoutView(BaseView):
def resources(self): def resources(self):
return ViewResources(self) return ViewResources(self)
def getLayoutsFor(self, key, **kw): def getLayoutsFor(self, key):
manager = component.getUtility(ILayoutManager) manager = component.getUtility(ILayoutManager)
return manager.getLayouts('.'.join((self.name, key)), **kw) return manager.getLayouts('.'.join((self.name, key)), self.context)
# subview providers # subview providers
@ -99,10 +117,12 @@ class ViewLayouts(object):
view = self.view view = self.view
subviews = [] subviews = []
for layout in view.getLayoutsFor(key): for layout in view.getLayoutsFor(key):
instance = LayoutInstance(view.client) instance = ILayoutInstance(view.client)
instance.template = layout instance.template = layout
instance.view = view v = LayoutView(instance, view.request, name=key,
subviews.append(LayoutView(instance, view.request, name=key)) parent=view, page=view.page)
instance.view = v
subviews.append(v)
return subviews return subviews

View file

@ -37,17 +37,24 @@ class ILayoutManager(Interface):
""" A utility that manages layouts and regions. """ A utility that manages layouts and regions.
""" """
def register(layout, regionName): def getLayouts(regionName, instance):
""" Register the layout given for the region specified. """ Return a sequence of layouts for the region given that are
valid sub-layouts for the layout instance given.
""" """
def check(layout, instance):
""" Return True if the layout given is a valid sub-layout
for the instance given.
"""
class ILayout(ITemplate): class ILayout(ITemplate):
""" Represents an ordered sequence of layout elements. """ Represents an ordered sequence of layout elements.
""" """
name = schema.ASCIILine( name = schema.ASCIILine(
title=_(u'Layout name'), title=_(u'Layout Name'),
description=_(u'The internal name of the layout.'), description=_(u'The internal name of the layout.'),
required=True,) required=True,)
title = schema.TextLine( title = schema.TextLine(
@ -63,13 +70,14 @@ class ILayout(ITemplate):
description=_(u'The name of a layout category this layout ' description=_(u'The name of a layout category this layout '
u'belongs to.'), u'belongs to.'),
required=False,) required=False,)
regionName = schema.ASCIILine(
title=_(u'Region Name'),
description=_(u'A dotted name that specifies the region '
u'this layout should be used for.'),
required=True,)
renderer = Attribute(u'An object responsible for rendering the layout.') renderer = Attribute(u'An object responsible for rendering the layout.')
def registerFor(regionName):
""" Register the layout for the region specified.
"""
class ILayoutComponent(IComponent): class ILayoutComponent(IComponent):
""" May be used for data entry or display. """ May be used for data entry or display.
@ -77,11 +85,11 @@ class ILayoutComponent(IComponent):
name = schema.ASCIILine( name = schema.ASCIILine(
title=_(u'Component name'), title=_(u'Component name'),
description=_(u'The internal name of the component'), description=_(u'The internal name of the component.'),
required=True,) required=True,)
title = schema.TextLine( title = schema.TextLine(
title=_(u'Title'), title=_(u'Title'),
description=_(u'The title or label of the component'), description=_(u'The title or label of the component.'),
required=True,) required=True,)
description = schema.Text( description = schema.Text(
title=_(u'Description'), title=_(u'Description'),
@ -100,16 +108,25 @@ class ILayoutInstance(IInstance):
renderer = Attribute(u'An object responsible for rendering the layout.') renderer = Attribute(u'An object responsible for rendering the layout.')
def checkLayout(layout):
""" Return True if the layout given is a valid sub-layout
for this instance.
"""
class IRegion(Interface): class IRegion(Interface):
""" A part of a layout "canvas" that may be filled with layout objects. """ A part of a layout "canvas" that may be filled with layout objects.
""" """
name = schema.ASCIILine(
title=_(u'Region name'),
description=_(u'The internal name of the region.'),
required=True,)
allowedLayoutCategories = schema.List( allowedLayoutCategories = schema.List(
title=_(u'Allowed layout categories'), title=_(u'Allowed layout categories'),
description=_(u'A collection of names of layout categories ' description=_(u'A collection of names of layout categories '
u'to which layouts may belong that may be placed ' u'to which layouts may belong that may be placed '
u'in this region'), u'in this region.'),
value_type=schema.ASCIILine(), value_type=schema.ASCIILine(),
required=False,) required=False,)