12.3. Site-Wide Master Pages

Usually the pages within the scope of a single application share a common layout. For example, all of the pages may have the same four-panel layout: the same header, footer, and sidebar content, while the main content of the page varies. Another way of looking at this is that the main content of each page is enclosed in another master page.

There are two aspects to making shared-layout pages like this:

  1. A declaration of page components in Java. With Bebop, the top-level Page object must contain the components that generate the dynamic content displayed in each of the header, footer, sidebar, and main panels as DOM fragments.

  2. An XSLT stylesheet to render the page properly. The XSLT stylesheet is responsible for selecting the content for each of the panels on the page, and placing them appropriately in the output, with the right look-and-feel (color scheme, dimensions, etc.) Note that this means whatever DOM is generated by the page components above must allow for the XSLT stylesheet to distinguish between the content that belongs in the header, footer, etc.

The preferred way to implement shared-layout pages like this in Bebop is to subclass Page. Users of this class add components to the page as usual, but the page's constructor and DOM-generation methods are overloaded to pre-fill the page with the appropriate boilerplate components.

The following example is a Page that always contains a header, footer, and sidebar:

package com.arsdigita.bebop.demo;

import com.arsdigita.bebop.*;
import com.arsdigita.xml.*;
import com.arsdigita.dispatcher.RequestContext;
import com.arsdigita.dispatcher.DispatcherHelper;

/**
 * This is a common page for a fictitious site, SockPuppet.com.
 * It includes a common header, a footer, and a main "content"
 * area.  We override the .generateXML method to pre-fill the page
 * with the boilerplate content.
 */
public class SockPuppetPage extends Page {
    
    private Component m_top;
    private Component m_bottom;
    private Component m_side;
    
    public SockPuppetPage() {
        this("");
    }

    public SockPuppetPage(String s) { 
        super("SockPuppet.com: " + s);
        m_top = new SiteHeader();
        m_bottom = new SiteFooter();
        m_side = new SiteSide();
    }

    public void generateXML(PageState ps, Document doc) { 
        Element page = generateXML(doc);
        Element layout = new Element("socksite:layout", SOCKSITE_XML_NS);
        page.addContent(layout);

        addContents(layout, ps);
    }

    protected void addContents(Element layout, PageState ps) {
        Element topPanel = 
           new Element("socksite:top", SOCKSITE_XML_NS);
        layout.addContent(topPanel);
        m_top.generateXML(ps, topPanel);

        Element sidePanel = 
           new Element("socksite:side", SOCKSITE_XML_NS);
        layout.addContent(sidePanel);
        m_side.generateXML(ps, sidePanel);

        Element bottomPanel = 
           new Element("socksite:bottom", SOCKSITE_XML_NS);
        layout.addContent(bottomPanel);
        m_bottom.generateXML(ps, bottomPanel);

        Element mainPanel = 
           new Element("socksite:main", SOCKSITE_XML_NS);
        layout.addContent(mainPanel);
        m_panel.generateXML(ps, mainPanel);
    }

    /**
     * Header component.  Demonstrates dynamic content.
     */
    private class SiteHeader extends Label { 
        public SiteHeader() { 
            super(new PrintListener() {
                public void prepare(PrintEvent pevt) { 
                    Label target = (Label)pevt.getTarget();
                    PageState ps = pevt.getPageState();
                    RequestContext rc = 
                      DispatcherHelper.getRequestContext
                        (ps.getRequest());
                    target.setLabel("SockPuppet.com: 
                      dynamic page header."
                       + "  You requested: " + rc.getOriginalURL());
                }
            }
        }
    }
    
    /**
     * Footer component.  All static.
     */
    private class SiteFooter extends Label { 
        public SiteFooter() { 
            super("SockPuppet.com: static footer.");
        }
    }

    /**
     * Sidebar component.  All static.
     */
    private class SiteSide extends Label { 
        public SiteSide() { 
            super("SockPuppet.com: static sidebar.");
        }
    }
}

You would use this Page like any other, for example, Page p = new SockPuppetPage(); p.add(...); .... However, when you call buildDocument, the generated DOM will include all of the content from the header, footer, etc.:

<bebop:page> 

... some page boilerplate ...

<socksite:top>
  <bebop:label>
    SockPuppet.com: dynamic page header.  You requested: ...
  </bebop:label>
</socksite:top>

<socksite:side>
  <bebop:label>
    SockPuppet.com: static side panel
  </bebop:label>
</socksite:side>

<socksite:bottom>
  <bebop:label>
    SockPuppet.com: static footer
  </bebop:label>
</socksite:bottom>

<socksite:main>
  ... main contents here ...
</socksite:main>

</bebop:page>

NoteNote
 

The order of the components in the DOM may have nothing at all to do with the ordering of the output.

The page components just ensure that the right content is generated with the right logical structure. It is up to the XSLT stylesheet to ensure that a socksite:top element really appears at the top of the page.

You would need an XSLT template rule such as the following to convert the above DOM into meaningful XHTML output:

<!-- match the layout element and put the 
header, footer, etc., in their appropriate places. -->

<xsl:template match="socksite:layout" 
               xmlns:socksite="http://www.sockpuppet.com/xmlns">

 <xsl:apply-templates select="socksite:top"/>

 <table>
   <tr>
     <td width="25%"><xsl:apply-templates 
        select="socksite:side"/></td>
     <td><xsl:apply-templates 
        select="socksite:main"/></td>
   </tr>
 </table>

 <xsl:apply-templates select="bebop:bottom"/>

</xsl:template>

This template must be associated with the appropriate application (for example, the main SockSite application) or site node.