Greenstone tutorial exercise

Back to wiki
Back to index
Prerequisite: Designing a new interface: Part 2
Sample files: interfaces.zip
Devised for Greenstone version: 3.06
Modified for Greenstone version: 3.09

Designing a new interface: Part 3

In this tutorial, we will change the layout for all of the pages in our library at once by adding and modifying the main.xsl file for our interface.

Examining main.xsl

  1. From the sample_files → interfaces → aybara folder, copy main.xsl into Greenstone3 → web → interfaces → perrin → transform → layouts. Refresh your web browser -- you should now only see the new content we added to home.xsl.

    Open main.xsl in a text editor. It contains two templates: mainTemplate and gs_footer. The mainTemplate looks like this:

    <xsl:template name="mainTemplate">
    <html>
    <head>
    <!-- ***** in header.xsl ***** -->
    <xsl:call-template name="create-html-header"/>
    </head>
    <body>
    <xsl:apply-templates select="/page"/>
    <xsl:call-template name="gs_footer"/>
    </body>
    </html>
    </xsl:template>

    Every page in our library uses this template. As you can see, it contains the basic structure of our HTML page. Inside the HTML header, the create-html-header template is called (<xsl:call-template name="create-html-header"/>). This is the template in header.xsl we edited in the previous tutorial. Inside the body of the HTML, we use two templates: /page and gs_footer. (The /page template is "applied" instead of called. The reason for this is complex and not relevant to this tutorial.)

    The /page template is where the content of each individual page is. For the home page, it is the content we just added to home.xsl. Since the perrin interface does not have XSL files for any of the other page types, the content of all of the other pages depends on the default interface: on the collection's about page, it's the collection description; on search pages, it is the query boxes and search results; on document pages it is the document content, etc.

    The gs_footer template is where we will put our page's footer.

  1. Now we will add the HTML that all of the pages of our collection will share. (You might want to take another look at index-GS3.html to remind yourself what the layout will look like.)

    Let's start by adding a footer to our library. In the gs_footer template, immediately after the line <!-- Put footer in here. --> insert the following:

    <!-- ******DO NOT REMOVE THE LINK TO OS TEMPLATES: Template is free to use/modify, but this link MUST remain on all pages. Do not remove Copyright information. Replace "Your Webpage Here" (and it's link) with your own information.-->
    <div class="wrapper col8">
    <div id="copyright">
    <p class="fl_left">Copyright © 2013 - All Rights Reserved - <a href="#">Your Webpage Here</a></p>
    <p class="fl_right">Template by <a href="http://www.os-templates.com/" title="Free Website Templates">OS Templates</a></p>
    <br class="clear" />
    </div>
    </div>

  1. Save main.xsl and refresh the web browser. The footer will now appear at the bottom of every page. You can click into a collection to see that it is also on these pages. (If your collection does not have a description, the footer will be the only thing that appears on the collection's about page.)

  1. You'll notice that the image slider and the highlighted items are right up against the edge of the page. In main.xsl, replace <xsl:apply-templates select="/page"/> with:

    <div class="wrapper">
    <div class="container" id="gs_content">
    <xsl:apply-templates select="/page"/>
    <br class="clear" />
    </div>
    </div>

    Important note: in any interface's main.xsl file, the attribute id="gs_content"  must be set on the <div> element containing the <xsl:apply-templates select="/page"/> element. Much of the proper functioning of Greenstone3 is dependent on this. So be aware of this if you modify or create your own main.xsl for a custom Greenstone 3 interface.

  1. Save main.xsl and refresh the web browser. The slider and highlighted items should now line up with the collection descriptions below them. There is also a grey line above the footer.

Adding a navigation bar

  1. Next, we're going to add the navigation bar and quick search box. Since the templates for this are pretty complex, we're going to start by adding only plain HTML to see the basic structure. Insert the following immediately after the <body> tag:

    <div class="wrapper col2">
    <div id="topbar">
    <div id="topnav">
    <ul>
    <li class="active"><a href="index.html">Home</a></li>
    <li><a href="style-demo.html">Browse</a>
    <ul>
    <li><a href="#">Link 1</a></li>
    <li><a href="#">Link 2</a></li>
    <li><a href="#">Link 3</a></li>
    </ul>
    </li>
    <li><a href="#">Search</a>
    <ul>
    <li><a href="#">Link 1</a></li>
    <li><a href="#">Link 2</a></li>
    <li><a href="#">Link 3</a></li>
    </ul>
    </li>
    </ul>
    </div>
    <!--**********************************************************************-->
    <div id="search">
    <form action="#" method="post">
    <fieldset>
    <legend>Site Search</legend>
    <input type="text" value="Search Our Website…" onfocus="this.value=(this.value=='Search Our Website…')? '' : this.value ;" />
    <input type="submit" name="go" id="go" value="Search" />
    </fieldset>
    </form>
    </div>
    <br class="clear" />
    <div id="advanced"><a href="#">advanced search</a></div>
    <!--**********************************************************************-->
    </div>
    </div>

  1. Save main.xsl and refresh the web browser. The navigation bar should now appear at the top of the screen.

    As you can see in the code above, the navigation links are simply HTML list items, and the dropdown menus are simply lists inside of lists. This is a very common way to make navigation bars.

  1. However, this navigation bar is static. It will be exactly the same for every page in the library. Instead, and we want it to change depending on which page we are on and which collection we are in. To do this, we will be using a couple of pretty complicated looking templates. Just remember, the end result will be HTML structured in the same way as the lists above. First, call the navBar template by replacing this:

    <li class="active"><a href="index.html">Home</a></li>
    <li><a href="style-demo.html">Browse</a>
    <ul>
    <li><a href="#">Link 1</a></li>
    <li><a href="#">Link 2</a></li>
    <li><a href="#">Link 3</a></li>
    </ul>
    </li>
    <li><a href="#">Search</a>
    <ul>
    <li><a href="#">Link 1</a></li>
    <li><a href="#">Link 2</a></li>
    <li><a href="#">Link 3</a></li>
    </ul>
    </li>

    with this:

    <xsl:call-template name="navBar"/>

    Then add the following templates before the final </xsl:stylesheet>:

    <xsl:template name="navBar">
    <xsl:choose>
    <xsl:when test="page/pageResponse/collection">
    <xsl:variable name="count" select="count(/page/pageResponse/collection/serviceList/service[@name='ClassifierBrowse']/classifierList/classifier)"/>
    <xsl:variable name="currentPage" select="page/pageRequest/@fullURL"/>

    <li><a href="{$library_name}">Home</a></li>
    <li>
    <xsl:if test="page/pageRequest/@subaction='about'"><xsl:attribute name="class">active</xsl:attribute></xsl:if>
    <a href="{$library_name}/collection/{$collNameChecked}/page/about">About</a>
    </li>

    <xsl:choose>
    <xsl:when test="$count > 3">
    <li><a href="{$currentPage}">Browse</a>
    <ul>
    <xsl:call-template name="Browsing"/>
    </ul>
    </li>
    </xsl:when>
    <xsl:otherwise>
    <xsl:call-template name="Browsing"/>
    </xsl:otherwise>
    </xsl:choose>

    <xsl:if test="/page/pageResponse/collection/serviceList/service/@type='query'">
    <li><a href="{$currentPage}">Search</a>
    <ul>
    <xsl:for-each select="/page/pageResponse/collection/serviceList/service[@type='query']">
    <xsl:variable name="search" select="@name"/>
    <xsl:variable name="search_name" select="displayItem[@name='name']"/>
    <li><a href="{$library_name}/collection/{$collNameChecked}/search/{$search}"><xsl:value-of select="$search_name"/></a></li>
    </xsl:for-each>
    </ul>
    </li>
    </xsl:if>

    </xsl:when>
    <xsl:otherwise> </xsl:otherwise>
    </xsl:choose>
    </xsl:template>

    <xsl:template name="Browsing">
    <xsl:for-each select="/page/pageResponse/collection/serviceList/service[@name='ClassifierBrowse']/classifierList/classifier">
    <li>
    <xsl:choose>
    <!-- If this tab is selected then colour it differently -->
    <xsl:when test="util:contains(/page/pageRequest/paramList/param[@name = 'cl' and /page/pageRequest/@action = 'b']/@value, @name)">
    <xsl:attribute name='class'>active</xsl:attribute>
    </xsl:when>
    <xsl:otherwise> </xsl:otherwise>
    </xsl:choose>

    <a>
    <!-- Add a title element to the <a> tag if a description exists for this classifier -->
    <xsl:if test="displayItem[@name='description']">
    <xsl:attribute name='title'><xsl:value-of select="displayItem[@name='description']"/></xsl:attribute>
    </xsl:if>

    <!-- Add the href element to the <a> tag -->
    <xsl:choose>
    <xsl:when test="@name">
    <xsl:attribute name="href"><xsl:value-of select="$library_name"/>/collection/<xsl:value-of select="/page/pageResponse/collection[@name=$collNameChecked]/@name"/>/browse/<xsl:value-of select="@name"/></xsl:attribute>
    </xsl:when>
    <xsl:otherwise>
    <xsl:attribute name="href"><xsl:value-of select="$library_name"/>/collection/<xsl:value-of select="/page/pageResponse/collection[@name=$collNameChecked]/@name"/>/browse/1</xsl:attribute>
    </xsl:otherwise>
    </xsl:choose>

    <!-- Add the actual text of the <a> tag -->
    <xsl:value-of select="displayItem[@name='name']"/>
    </a>
    </li>
    </xsl:for-each>
    </xsl:template>

    A basic explanation of these templates follows (don't worry if you don't understand it). You can skip ahead to Step 9 if you'd like.

    Let's take a look at the navBar template and try to understand what it's doing. First, it's checking to see if the page is a collection page (<xsl:when test="page/pageResponse/collection">). If so, it will create a link back to the home page (<li><a href="{$library_name}">Home</a></li>) and to the current collection's about page.

    Then, if there are 3 or fewer browsing classifiers, they will each become a link on the navigation bar. If there are more than 3 browsing classifiers, then they will be made into a dropdown menu under the heading "Browse". (If there are too many links in the navigation bar, they will start to overlap.) Finally, if there are any search types, they will be in a dropdown menu under the heading "Search". In addition, it adds the attribute "class=active" to the link for the current page so that it will be colored differently.

  1. Save main.xsl and refresh the browser. The home page should have no links in the navigation bar at all. Enter a few different collections and see what the navigation bar looks like in each of those.

Adding functionality to the quick search box

  1. Currently, the quick search box doesn't actually work. To make the quick search work, replace this:

    <div id="search">
    <form action="#" method="post">
    <fieldset>
    <legend>Site Search</legend>
    <input type="text" value="Search Our Website…" onfocus="this.value=(this.value=='Search Our Website…')? '' : this.value ;" />
    <input type="submit" name="go" id="go" value="Search" />
    </fieldset>
    </form>
    </div>
    <br class="clear" />
    <div id="advanced"><a href="#">advanced search</a></div>

    With this:

    <xsl:choose>
    <xsl:when test="page/pageRequest/@subaction='home'">
    <xsl:call-template name="crossCollSearch"/>
    </xsl:when>
    <xsl:when test="page/pageRequest/paramList/param/@name='c' and /page/pageResponse/collection[@name=$collNameChecked]/serviceList/service[@name='TextQuery']">
    <xsl:call-template name="CollectionSearch"/>
    </xsl:when>
    <xsl:otherwise/>
    </xsl:choose>

    This says when you are on the home page, use the crossCollSearch template; when you are on a collection page (and that collection has plain, i.e. "TextQuery", search enabled), use the CollectionSearch template; otherwise, don't put anything at all.

  1. Now add in the crossCollSearch and CollectionSearch templates before the final </xsl:stylesheet>:

    <xsl:template name="crossCollSearch">
    <div id="search">
    <xsl:for-each select="/page/pageResponse/serviceList/service[@name='TextQuery']">
    <form name="QuickSearch" method="get" action="{$library_name}">
    <input type="hidden" name="a" value="q"/>
    <input type="hidden" name="rt" value="rd"/>
    <input type="hidden" name="s" value="{@name}"/>
    <input type="hidden" name="s1.collection" value="all"/>
    <input type="text" name="s1.query" id="search-text" value="Search all collections…" onfocus="this.value=(this.value=='Search all collections…')? '' : this.value ;" />
    <input type="submit" name="go" id="go" value="Search" />
    </form>
    </xsl:for-each>
    </div>
    <br class="clear" />
    </xsl:template>

    <xsl:template name="CollectionSearch">
    <div id="search">
    <xsl:variable name="subaction" select="/page/pageRequest/@subaction"/>
    <form action="{$library_name}/collection/{$collNameChecked}/search/TextQuery">
    <!-- This parameter says that we have come from the quick search area -->
    <input type="hidden" name="qs" value="1"/>
    <input type="hidden" name="rt" value="rd"/>
    <input type="hidden" name="s1.level">
    <xsl:attribute name="value">
    <xsl:choose>
    <xsl:when test="/page/pageRequest/paramList/param[@name = 's1.level']">
    <xsl:value-of select="/page/pageRequest/paramList/param[@name = 's1.level']/@value"/>
    </xsl:when>
    <xsl:otherwise>
    <xsl:value-of select="/page/pageResponse/collection/serviceList/service[@name='TextQuery']/paramList/param[@name = 'level']/@default"/>
    </xsl:otherwise>
    </xsl:choose>
    </xsl:attribute>
    </input>
    <xsl:choose>
    <xsl:when test="/page/pageResponse/service[@name = 'TextQuery']/paramList/param[@name = 'startPage']">
    <input type="hidden" name="s1.startPage" value="1"/>
    </xsl:when>
    <xsl:otherwise>
    <input type="hidden" name="startPage" value="1"/>
    </xsl:otherwise>
    </xsl:choose>
    <xsl:if test="not(/page/pageRequest/paramList/param[@name = 's1.hitsPerPage'])">
    <input type="hidden" name="s1.hitsPerPage" value="20"/>
    </xsl:if>
    <xsl:if test="not(/page/pageRequest/paramList/param[@name = 's1.maxDocs'])">
    <input type="hidden" name="s1.maxDocs" value="100"/>
    </xsl:if>
    <!-- The query text box -->
    <span class="querybox">
    <xsl:variable name="qs">
    <xsl:apply-templates select="/page/pageResponse/collection[@name=$collNameChecked]/serviceList/service[@name='TextQuery']/paramList/param[@name='query']" mode="calculate-default"/>
    </xsl:variable>
    <nobr>
    <xsl:apply-templates select="/page/pageResponse/collection[@name=$collNameChecked]/serviceList/service[@name='TextQuery']/paramList/param[@name='query']">
    <xsl:with-param name="default" select="java:org.greenstone.gsdl3.util.XSLTUtil.tidyWhitespace($qs, /page/@lang)"/>
    </xsl:apply-templates>
    </nobr>
    </span>
    <!-- The submit button (for TextQuery) -->
    <xsl:if test="/page/pageResponse/collection[@name=$collNameChecked]/serviceList/service[@name='TextQuery']">
    <input type="submit" name="go" id="go" value="Search" > </input>
    <br/>
    </xsl:if>
    </form>
    </div>
    <br class="clear" />
    <div id="advanced"><a href="{$library_name}/collection/{$collNameChecked}/search/TextQuery">advanced search</a></div>
    </xsl:template>

  1. Save main.xsl and refresh the web browser. Try searching for a word on the home page (like "economy"), and then enter a collection or two and try searching from there, as well.

Adding the library name and login links

  1. Next, we'll add a header to our page that displays the library's name (with a link to the home page), and, if we're inside a collection, displays the collection's name (with a link to the collection's about page). After the <body> tag in mainTemplate, add the following:

    <div class="wrapper">
    <div id="header">
    <div class="fl_left">
    <h1><a href="{$library_name}"><xsl:call-template name="siteName"/></a></h1>
    <p>&#160;
    <xsl:if test="page/pageResponse/collection">
    <a href="{$library_name}/collection/{$collNameChecked}/page/about">
    <xsl:value-of select="page/pageResponse/collection/displayItemList/displayItem[@name='name']"/>
    </a>
    </xsl:if>
    </p>
    </div>
    <br class="clear"/></div>
    </div>

    Save main.xsl and refresh the web browser to see the library name display at the top of the page. Enter a collection to see the collection name displayed.

  1. Finally, we will add the top bar which includes the links for logging in, the help page, and the preferences page. In the mainTemplate, replace the <body> tag with:

    <body id="top">
    <div class="wrapper col0">
    <div id="topline">
    <ul>
    <xsl:call-template name="loginLinks"/>
    <li><a href="{$library_name}/collection/{$collNameChecked}/page/pref">Preferences</a></li>
    <li><a href="{$library_name}/collection/{$collNameChecked}/page/help">Help</a></li>
    </ul>
    <br class="clear" />
    </div>
    </div>

    and add the following template before the final </xsl:stylesheet>:

    <xsl:template name="loginLinks">
    <xsl:variable name="username" select="/page/pageRequest/userInformation/@username"/>
    <xsl:variable name="groups" select="/page/pageRequest/userInformation/@groups"/>

    <xsl:choose>
    <xsl:when test="$username">
    <xsl:if test="contains($groups,'admin')">
    <li class="login"><a href="{$library_name}/admin/AddUser">Add user</a></li>
    <li class="login"><a href="{$library_name}/admin/ListUsers">Administration</a></li>
    </xsl:if>
    <li class="login"><a href="{$library_name}/admin/AccountSettings?s1.username={$username}">Logged in as: <xsl:value-of select="$username"/></a></li>
    <li class="login"><a href="{$library_name}?logout=">Logout</a></li>
    </xsl:when>
    <xsl:otherwise>
    <li class="login">
    <a href="{$library_name}?a=p&amp;sa=login&amp;redirectURL={$library_name}%3Fa=p%26sa=home">Login
    <xsl:attribute name="title">
    <xsl:value-of select="util:getInterfaceText($interface_name, /page/@lang, 'login_tip')"/>
    </xsl:attribute>
    </a>
    </li>
    </xsl:otherwise>
    </xsl:choose>
    </xsl:template>

  1. Save and close main.xsl and refresh the web browser to see the top bar. Click the Login button and log in as admin (if you hadn't already changed the password, use the default password of "admin") to see all of the links appear along the top, including links to the Add User page, to the Administration page, and to Logout.

Interface language files

  1. In the web browser, click the Help button. For simplicity's sake, we have not built this interface to be language independent. All of the text is simply written in English. The content of the help page, however, which is still being "borrowed" from the default interface, is built to be language independent.

    However, the help.xsl file in default is looking for a file called interface_<interface-name>2.properties. So, when the default interface is being used, it will look for the file interface_default2.properties, which does exist. But since our library is using the perrin interface, it is looking for the file interface_perrin2.properties, which does not exist. There are a few different solutions to this. The quickest solution at the moment is to create an interface_perrin2.properties file so that the English values will display.

  1. In the Greenstone3 → web → WEB-INF → classes folder, create a copy of the interface_default2.properties file (select the file and press Ctrl+C then Ctrl+V). This will create a file called interface_default2 - Copy.properties. Rename this file interface_perrin2.properties.

    When adding or editing properties files, you must restart the server for changes to take effect. In the Greenstone Server window, click the Restart Library button. When the server restarts, navigate to http://localhost:8383/greenstone3/golden/collection//page/help to see the English help text properly displayed.


Copyright © 2005-2019 by the New Zealand Digital Library Project at the University of Waikato, New Zealand
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled “GNU Free Documentation License.”