diff --git a/composer/layout/README.txt b/composer/layout/README.txt index 9ce03e2..9d0ae06 100644 --- a/composer/layout/README.txt +++ b/composer/layout/README.txt @@ -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'.........' diff --git a/composer/layout/base.py b/composer/layout/base.py index ae65121..9431ef0 100644 --- a/composer/layout/base.py +++ b/composer/layout/base.py @@ -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 + diff --git a/composer/layout/browser/default.pt b/composer/layout/browser/default.pt new file mode 100644 index 0000000..5497492 --- /dev/null +++ b/composer/layout/browser/default.pt @@ -0,0 +1,3 @@ +
+ Some footer text. +
diff --git a/composer/layout/browser/default.py b/composer/layout/browser/default.py index 1949e26..974a664 100644 --- a/composer/layout/browser/default.py +++ b/composer/layout/browser/default.py @@ -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']) diff --git a/composer/layout/browser/liquid/default.py b/composer/layout/browser/liquid/default.py index cbc0b49..598caa4 100644 --- a/composer/layout/browser/liquid/default.py +++ b/composer/layout/browser/liquid/default.py @@ -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']) diff --git a/composer/layout/browser/main.pt b/composer/layout/browser/main.pt index 7159ef9..86154cf 100644 --- a/composer/layout/browser/main.pt +++ b/composer/layout/browser/main.pt @@ -9,8 +9,8 @@ - - + + diff --git a/composer/layout/browser/standard.pt b/composer/layout/browser/standard.pt index fae3f7e..10dedf3 100644 --- a/composer/layout/browser/standard.pt +++ b/composer/layout/browser/standard.pt @@ -1,12 +1,7 @@ -
- Some footer text. -
- - diff --git a/composer/layout/browser/standard.py b/composer/layout/browser/standard.py new file mode 100644 index 0000000..cc44315 --- /dev/null +++ b/composer/layout/browser/standard.py @@ -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']) diff --git a/composer/layout/browser/view.py b/composer/layout/browser/view.py index a58b43a..2433684 100644 --- a/composer/layout/browser/view.py +++ b/composer/layout/browser/view.py @@ -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 diff --git a/composer/layout/interfaces.py b/composer/layout/interfaces.py index e4775a6..d3ae2b5 100644 --- a/composer/layout/interfaces.py +++ b/composer/layout/interfaces.py @@ -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,)