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$)
Let's start with some basic setup; the traversable adapter is needed for
rendering the page templates.
>>> from zope import component
>>> 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()
>>> component.provideUtility(manager)
>>> from zope.traversing.adapters import DefaultTraversable
>>> component.provideAdapter(DefaultTraversable, (Interface,))
The layouts themselves are also specified as utilities.
>>> #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
=============
>>> 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 zope.publisher.browser import TestRequest
>>> page = Page(None, TestRequest())
>>> page = Page(Document(), TestRequest())
>>> page()
u'<!DOCTYPE ...>...<html ...>...</html>...'

View file

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

View file

@ -9,8 +9,8 @@
</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<tal:css repeat="macro view/resources/css">
<metal:css use-macro="macro" />
<tal:css repeat="view view/layouts/css">
<metal:css use-macro="view/renderer" />
</tal:css>
<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">
<!--<style type="text/css" media="all"
tal:attributes="media macro/media"
tal:content="string:@import url(${resourceBase}${macro/resourceName});">
tal:attributes="media view/media"
tal:content="string:@import url(${view/page/resourceBase}${view/context/resourceName});">
@import url(some.css);
</style>-->
</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.app.pagetemplate import ViewPageTemplateFile
from cybertools.composer.layout.base import Layout, LayoutInstance
from cybertools.composer.layout.interfaces import ILayoutManager
from cybertools.composer.layout.base import Layout
from cybertools.composer.layout.interfaces import ILayoutManager, ILayoutInstance
class BaseView(object):
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.request = request
if name is not None:
self.name = name
def update(self):
return True
for k, v in kw.items():
setattr(self, k, v)
def __call__(self):
return self.template(self)
def update(self):
return True
class Page(BaseView):
macroName = 'page'
@Lazy
def rootView(self):
return self
def __call__(self):
layout = Layout('page')
layout.renderer = ViewPageTemplateFile('main.pt').macros['page']
instance = LayoutInstance(self.context)
layout = Layout('page', 'page')
layout.renderer = ViewPageTemplateFile('main.pt').macros[self.macroName]
instance = ILayoutInstance(self.context)
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]
instance.view = 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):
@ -83,9 +101,9 @@ class LayoutView(BaseView):
def resources(self):
return ViewResources(self)
def getLayoutsFor(self, key, **kw):
def getLayoutsFor(self, key):
manager = component.getUtility(ILayoutManager)
return manager.getLayouts('.'.join((self.name, key)), **kw)
return manager.getLayouts('.'.join((self.name, key)), self.context)
# subview providers
@ -99,10 +117,12 @@ class ViewLayouts(object):
view = self.view
subviews = []
for layout in view.getLayoutsFor(key):
instance = LayoutInstance(view.client)
instance = ILayoutInstance(view.client)
instance.template = layout
instance.view = view
subviews.append(LayoutView(instance, view.request, name=key))
v = LayoutView(instance, view.request, name=key,
parent=view, page=view.page)
instance.view = v
subviews.append(v)
return subviews

View file

@ -37,17 +37,24 @@ class ILayoutManager(Interface):
""" A utility that manages layouts and regions.
"""
def register(layout, regionName):
""" Register the layout given for the region specified.
def getLayouts(regionName, instance):
""" 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):
""" Represents an ordered sequence of layout elements.
"""
name = schema.ASCIILine(
title=_(u'Layout name'),
title=_(u'Layout Name'),
description=_(u'The internal name of the layout.'),
required=True,)
title = schema.TextLine(
@ -63,13 +70,14 @@ class ILayout(ITemplate):
description=_(u'The name of a layout category this layout '
u'belongs to.'),
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.')
def registerFor(regionName):
""" Register the layout for the region specified.
"""
class ILayoutComponent(IComponent):
""" May be used for data entry or display.
@ -77,11 +85,11 @@ class ILayoutComponent(IComponent):
name = schema.ASCIILine(
title=_(u'Component name'),
description=_(u'The internal name of the component'),
description=_(u'The internal name of the component.'),
required=True,)
title = schema.TextLine(
title=_(u'Title'),
description=_(u'The title or label of the component'),
description=_(u'The title or label of the component.'),
required=True,)
description = schema.Text(
title=_(u'Description'),
@ -100,16 +108,25 @@ class ILayoutInstance(IInstance):
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):
""" 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(
title=_(u'Allowed layout categories'),
description=_(u'A collection of names of layout categories '
u'to which layouts may belong that may be placed '
u'in this region'),
u'in this region.'),
value_type=schema.ASCIILine(),
required=False,)