Sample Greenstone3 Format Statements

This page provides examples of format statements for miscellaneous things. See also

Document Display

Linking to Abstract and Commentaries

<gsf:template name="topLevelSectionContent">
  <div style="text-align:right;">
    <gsf:if-metadata-exists>
      <gsf:metadata name="abstract"/>
      <gsf:if><gsf:link type="document" OIDmetadata="abstract" titlekey="abstract.linktitle"><gsf:icon file="iabstr.gif" select="collection"/><gsf:collectionText name="abstract"/></gsf:link></gsf:if>
      </gsf:if-metadata-exists>
      <gsf:if-metadata-exists>
        <gsf:metadata name="commentary"/>
        <gsf:if><gsf:link type="document" OIDmetadata="commentary" titlekey="commentary.linktitle"><gsf:icon file="icomm.gif" select="collection"/><gsf:collectionText name="commentary"/></gsf:link></gsf:if>
      </gsf:if-metadata-exists></div>
  </gsf:template>

Image Display Example

<gsf:option name="TOC" value="true"/>

<gsf:template name="documentHeading"/>

<gsf:template name="documentContent">
  <gsf:switch>
  <gsf:metadata name="FileFormat"/>
	<gsf:when test="equals" test-value="jpeg">
		<xsl:call-template name="imageDisplay"/>  
	</gsf:when>
	<gsf:otherwise>
		<h1><gsf:metadata name="Source"/></h1>
		<xsl:call-template name="wrappedSectionText"/>
	</gsf:otherwise>
  </gsf:switch>
</gsf:template>

<xsl:template name="additionalHeaderContent">
  <xsl:variable name="httpCollection">
    <xsl:value-of select="/page/pageResponse/collection/metadataList/metadata[@name='httpPath']"/>
  </xsl:variable>
  
  <link href="{$httpCollection}/style/imageDisplay.css" rel="stylesheet" type="text/css"/>
</xsl:template>

<xsl:template name="imageDisplay">
    <div class="doc-image">
      <gsf:link type="source">
        <gsf:metadata name="screenicon"/>
      </gsf:link>
      <div class="doc-quote">
        <p>
          <i>
            <gsf:metadata name="dc.Description"/>
          </i>
        </p>
      </div>
    </div>
    <div class="doc-info">
      <h1>
        <gsf:metadata name="dc.Title"/>
      </h1>
      <ul>
        <li>
          <b>Author:</b>
          <gsf:metadata name="dc.Creator"/>
        </li>
        <li>
          <b>Subjects:</b>
          <gsf:metadata name="dc.Subject"/>
        </li>
        <li><b>Image size:</b><gsf:metadata name="ImageHeight"/>px x <gsf:metadata name="ImageWidth"/>px</li>
      </ul>
    </div>
    <br class="clear"/>
</xsl:template>

This code provides a nice way to display images with their metadata on the document display page. Provides an example of gsf:switch and gsf:when statements, as well as linking to a CSS file.

This format statement will display any document with a FileFormat (ex.FileFormat) of "jpeg" as shown in the image. All other documents are displayed with the dc.Title as a heading and the document text.

If you have images of many different formats (png, jpg, jpeg, gif), you could replace the lines:

<gsf:metadata name="FileFormat"/>
<gsf:when test="exists" test-value="jpeg">

With the following:

<gsf:metadata name="Image"/>
<gsf:when test="exists">

For the image display to appear as shown in the image, you must include the following CSS in a file called imageDisplay.css in your collection's style folder:

div.separating-line {
    border-bottom: 1px solid #d8d8d8;
    padding: 5px;
    clear: both;
}

div.doc-image {
    float: left;
    margin-left: 10px;
    padding-right: 10px; 
}

div.doc-image img {
        border: 1px solid #5d5f60;
        padding: 5px;

}

div.doc-quote p {
width: 500px;
}

div.doc-info h4 {
    margin-top: 15px;
    margin-bottom: 5px;
}

div.doc-info ul {
    list-style-type: none;
    color: #5d5f60;
    margin-top: 0px;
    
}

div.doc-info ul li {
line-height: 20px;

}

How to link metadata values to a search for that value. For example, say a document has several authors, stored as dc.Creator metadata. You can display a list of the authors, each one linked to a search of the dc.Creator index for that author.

Collection setup:

  • dc.Creator metadata will need to be assigned to the documents
  • You need to add a search index on dc.Creator metadata
  • Build the collection

You will need to find out the shortname for the dc.Creator index. You can look in web/sites/localsite/collect/<your-coll-name>/index/buildConfig.xml. Look for the section like:

<indexList>
  <index name="text" shortname="TX" />
  <index name="dc.Title,ex.dc.Title,Title" shortname="TI" />
  <index name="dc.Creator" shortname="CR" />
</indexList>

You can see from this example that the dc.Creator index got the shortname "CR". You will need this shortname to construct the search URL.

Next you need to modify your format statement to add in the search links. If you want these links to appear on the document page, select the "display" format item. (If you wanted them in search results, or on classifier pages, select the "search/browse/CLx" format items)

The default XML for the display format item contains several commented out templates. Say you have simple documents (ie ones with no internal structure eg images), you can add the links to the "documentHeading" or "documentContent" templates. If you have structured documents with sections, then perhaps add it to the start of the "topLevelSectionContent" template.

The XML for the links looks something like the following:

Search for Authors: <gsf:foreach-metadata name='dc.Creator'> 
   <a><xsl:attribute name="href"><xsl:value-of select="$_httpquery_"/>/TextQuery?qs=1&amp;rt=rd&
   amp;s1.level=Doc&amp;startPage=1&amp;s1.query=<gsf:meta-value/>&amp;s1.index=CR</xsl:attribute>
   <gsf:meta-value/></a><xsl:text>, </xsl:text>
</gsf:foreach-metadata>

<gsf:foreach-metadata> iterates over the list of values for the specified metadata, and <gsf:meta-value> outputs the current value each time.

Use double quotes around the query value if you want exact matching, i.e.

s1.query="<gsf:meta-value/>"

The parameters are:

  • qs=1 - this search is a quick search. Can omit if you want to be taken to the TextQuery search page (equivalent to clicking the TextQuery link under the quick search form)
  • rt=rd - this determines, for a query, if you are displaying the search form, and performing the search or not. If you have qs=1, then you really only need rt=r here. But without qs=1, having the r will perform the search, and having the d will display the search form too.
  • startPage=1 - start at the first page of search results
  • s1.level=Doc - s1 params go to the query service itself. level is Doc or Sec.
  • s1.query - the query term, in this case the metadata value
  • s1.index=CR - the index to search in, in this case the CR dc.Creator index.

Using javascript to change the display

As an example of using javascript, we will see how to output the document filesize in a human readable form. For example, instead of displaying "834716 bytes", it will display "815 KB".

A basic javascript method to do this is the following:

function humanReadableFileSize(bytes)
{
    var filesize = bytes + " bytes";
    if (bytes > 1048576) {
	filesize = Math.round(bytes / 1048576.0) + " MB";
    }
    else if (bytes > 1024) {
	filesize = Math.round(bytes/1024.0)+ " KB";
    }
    return filesize;
}

So, we have a javascript function, but how do we get it into the page? And how do we call it?

Adding javascript into a page

To add the javascript into an XSL file, you encase it in script tags. And because XSL will escape double quotes by default, you need to put it inside <xsl:text disable-output-escaping="yes"> tags.

<script type="text/javascript">
  <xsl:text disable-output-escaping="yes">
    function humanReadableFileSize(bytes)
    {
      var filesize = bytes + " bytes";
      if (bytes > 1048576) {
        filesize = Math.round(bytes / 1048576.0) + " MB";
      }
      else if (bytes > 1024) {
	filesize = Math.round(bytes/1024.0)+ " KB";
      }
      return filesize;
    }
  </xsl:text>
</script>

There are multiple places you can use to add some javascript into your library.

  • All pages, all collections

web/interfaces/default/transform/layouts/header.xsl contains global templates used in the headers of all pages in the library. The create-html-header template sets up the header for each page. If you add your script element into here, it will appear in all pages in your library.

  • Only classifier pages, all collections

The create-html-header template calls an additional template additionalHeaderContent, which can be used to customise which pages get the extra javascript. For example, if we only want this function to be available for classifier pages, we can edit web/interfaces/default/transform/pages/classifier.xsl. Copy the <xsl:template name="additionalHeaderContent"> element from the header.xsl file into the classifier.xsl file. Add the new script tag into it.

  • All pages, single collection

The additionalHeaderContent template calls further specifric templates, including additionalHeaderContent-collection. This template can be added into the collection's collectionConfig.xml file to modify the header just for that collection. If you want the javascript to be available for all pages in this collection, add the template to the global format statement. In GLI, select global. In the file, it is the top level <format> element.

<xsl:template name="additionalHeaderContent-collection">
  <script type="text/javascript">
    <xsl:text disable-output-escaping="yes">
    function humanReadableFileSize(bytes)
    {
      .. function content here...
    }
    </xsl:text>
  </script>
</xsl:template>
  • single page, single collection

Say you just want to use this new javascript on the classifier pages, then add the template into the main classifier format element. Or add it into the format element of a specific classifier and it will only be available for that single classifier.

Using the javascript in the format statement

Now that we have the function included in the page, we can use it to modify our filesize display.

Lets modify a classifier format statement to use this function. The following is a simple format statement that displays an icon linking to the document, the title, and the filesize.

<gsf:template match="documentNode">
  <td valign="top">
    <gsf:link type="document">
      <gsf:icon type="document"/>
    </gsf:link>
  </td>
  <td valign="top">
    <gsf:metadata name="dc.Title"/>
  </td>
  <td>
    <gsf:metadata name="FileSize"/>
  </td>
</gsf:template>

We will modify the third <td> element to display a human readable form.

During development, we can use the simple document.write() method to output the result:

<td>
<script type="text/javascript">document.write(humanReadableFileSize(<gsf:metadata name="FileSize"/>));
</script>
</td>

This lets us check that our function is correct and we are getting the right metadata. However, using "document.write" is not considered good practice. And if you are adding this in a classifier which has bookshelves, then it ends up replacing the entire page with the filesize!

Instead, we will need to add to the html of the td element. This means we need to give the td element an id so we can find it again.

If the <td> we are adding the filesize to contains only the filesize, then we can use the element.innerHTML() method to set the content.

<td>
  <xsl:attribute name="id"><gsf:OID/>-fs</xsl:attribute>
  <script type="text/javascript"> 
    document.getElementById(<gsf:html>"</gsf:html><gsf:OID/>-fs<gsf:html>"</gsf:html>).innerHTML= humanReadableFileSize(<gsf:metadata name="FileSize"/>);
  </script>
</td>

However, if this <td> element already has content, then we need to append the filesize, using element.appendChild() and document.createTextNode(). For example:

<td>
  <xsl:attribute name="id"><gsf:OID/>-fs</xsl:attribute>
  ...other content here...
  <script type="text/javascript"> 
    document.getElementById(<gsf:html>"</gsf:html><gsf:OID/>-fs<gsf:html>"</gsf:html>).appendChild(document.createTextNode(humanReadableFileSize(<gsf:metadata name="FileSize"/>)));
  </script>
</td>

Element ids need to be unique, so in this case we will use the document id (<gsf:OID/>) with -fs just in case the OID has been used as an id elsewhere in the page. Because we are not assigning a simple string to the id attribute, we need to use the <xsl:attribute> element. We cannot write <td id="<gsf:OID/>">.

To set an element's text, we use document.getElementById("id").innerHTML = "xxx". document.getElementById finds the specified element, and element.innerHTML = "xxx" sets its text.

We set the text to be the result of calling our function on the filesize metadata.

To add to an element's content, we find the node using document.getElementById("id"), we create a text node using document.createTextNode("xxx") and append it to the element using element.appendChild(node).

One further tricky part. We want to write document.getElementById("<gsf:OID/>-fs"). However, the XSL transform process will escape the double quotes with &quot; and then the javascript will be invalid. To get around this, we use <xsl:text disable-output-escaping="yes">"</xsl:text>.

i.e. document.getElementById(<xsl:text disable-output-escaping="yes">"</xsl:text><gsf:OID/>-fs<xsl:text disable-output-escaping="yes">"</xsl:text>)

Greenstone provides a shorthand: <gsf:html>"</gsf:html>, which gets resolved to <xsl:text> with the disable-output-escaping attribute set.