12.4. Varying a Shared Layout

Once you have a shared-layout Page subclass, such as SockPuppetPage above, you will likely have single pages that vary the boilerplate content slightly. For example, you might have a single page which uses the same header, footer, and sidebar as all other pages on your site, but which adds an extra boilerplate component above the header, such as a banner ad.

You can accomplish this with further sub-classing. You can define a subclass of SockPuppetPage, BannerAdSockPuppetPage, which has all of the same pre-filled components but adds a second header, a BannerAd component:

package com.arsdigita.bebop.demo;

import com.arsdigita.bebop.*;
import com.arsdigita.xml.*;
import com.arsdigita.dispatcher.RequestContext;
import com.arsdigita.dispatcher.DispatcherHelper;
// fictitious banner ad management system
import com.arsdigita.personalization.BannerAdManager;

public class BannerAdSockPuppetPage extends SockPuppetPage {
    
    private Component m_banner_ad;
    
    public BannerAdSockPuppetPage() {
        this("");
    }

    public BannerAdSockPuppetPage(String s) { 
        // calling super() adds default header, footer, and sidebar
        super("SockPuppet.com: " + s);
        m_banner_ad = new BannerAd();
    }

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

        // generate XML for header, footer, sidebar, and main panel
        super.addContents(layout, ps); 
        
        // just add the new banner ad here
        Element bannerAd = new Element("socksite:bannerAd", 
           SOCKSITE_XML_NS);
        layout.addContent(bannerAd);
        m_banner_ad.generateXML(ps, bannerAd);
    }

    /**
     * Header component.  Demonstrates dynamic content.
     */
    private class BannerAd extends SimpleComponent { 
        public void generateXML(PageState ps, Element parent) { 
            Element elt = new Element("socksite:bannerAd", 
                SOCKSITE_XML_NS);
            elt.setText(BannerAdManager.getInstance().getBanner());
            parent.addContent(elt);
        }
    }
}

You must also amend the XSLT stylesheet:

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

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

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

Note that the above stylesheet will work whether or not the input DOM actually contains a banner ad; the <xsl:apply-templates select="socksite:bannerAd"/> code will simply be ignored if no such element exists in the DOM.