The template can be used to set a rule for every element in the source tree. Another element can be used within the template to repeat a rule. This element is <xsl:for-each> and is never an empty element. The only attribute for this element is select, which has the value of an XPath expression. The <xsl:sort> element can be used with <xsl:for-each> to sort the source elements before returning to the results tree. Literal text, other elements, and rules can be used between the start and end elements of this <xsl:for-each> element just as for the templates. The following template will return all the field names from the FMPXMLRESULT grammar:
<xsl:template match="fm:FMPXMLRESULT/fm:METADATA"> <xsl:for-each select="fm:FIELD"> FieldName: <xsl:value-of select="@NAME" /><br /> </xsl:for-each> </xsl:template>
FileMaker Pro can presort before exporting or web publishing the XML results, but sometimes that is not sufficient for the resulting output. Two XSL elements, <xsl:apply-templates> and <xsl:for-each>, can use a child element, <xsl:sort>, to presort the selected elements. Otherwise, no sorting is done to the document and all rules are applied to the elements as they occur in the document.
Multiple <xsl:sort> elements can be used, just as multiple fields in FileMaker Pro can be used in a sort. This element has several attributes to specify the type of sort. The first attribute, select, can use child elements of the current node or even attributes as the key for sorting. The lang attribute is used to specify the language used for the sort. The data-type attribute has a value of "text" or "number" for the sort. The default sort order is "ascending" by uppercase, followed by lowercase. You can change these attributes or explicitly declare them. For example, order="ascending" or "descending" or case-order="upper- first" or "lower-first".
<xsl:sort select="fm:lastName" order="descending" data-type="text" /> <xsl:sort select="fm:firstName" />
Several elements are used to copy the source elements, the value of particular elements, literal text, or the value of comments. These elements are the most used to display the text content of the XML document. By default, the string value of any element (even those with children elements) is the concatenation of all the string values for all children elements. To be specific on what is returned to the result tree, including the value of attributes or XPath expressions, use these elements.
Element and Attribute Values—The <xsl:value-of> element is used to return the string value of an element and its descendants, if any. Any XPath expression may be used as the value for the single attribute select including any attribute name in any element. The other attribute, disable-output-escaping, has the value of "yes" or "no" and is used to return raw data or encode entities. The <xsl:value-of> element is always empty and has no child elements. If a node with child elements is used for the value of the select attribute, the result string will be a concatenation of the string values of the element and all its descendants.
<!-- show the value "5" --> <xsl:value-of select="2+3" /> <!-- show the contents of the 3rd row, 2nd column, DATA element --> <xsl:value-of select="fm:ROW/fm:COL/:fm:DATA" /> <!-- show the value of the NAME attribute for the Database --> <xsl:value-of select="/fm:DATABASE[@NAME]" />
Text—Sometimes you need to include white space characters and do not want them ignored by the processors. You can also use the element <xsl:text> to display any literal values that may be parsed into entities, such as ">" or "<." New lines (carriage return and/or linefeeds) can be added between the start and end elements. See the following examples. This element is never empty and has one attribute. Use the attribute disable-output-escaping with the value of "yes" to prevent the literal contents from being parsed. This element can contain any parsed character data.
<!-- add three spaces here --> <xsl:text> </xsl:text> <!-- here's a new line: --> <xsl:text> </xsl:text> <!-- this will produce the correct characters "<?" --> <xsl:text disable-output-escaping="yes"><?</xsl:text>
Copy and Copy Of—You can use the source XML elements in the result XML. The element <xsl:copy> will copy the current element and its text value to the result document. The attributes and children elements may not be copied. This is called a shallow copy. The <xsl:copy> element may be empty or have other template elements. The attribute use-attribute-sets allows you to apply a particular set of attributes to the copy.
<xsl:for-each select="fm:ROW"> <xsl:copy /> </xsl:for-each>
If you want to use a deeper copy, the empty element <xsl:copy-of> can be used. The copy-of element is similar to the value-of element, but the result is not converted to a string. The required attribute select has an XPath expression for the value. The example below will place the element <DATA> and its text value into the result XML document.
<xsl:copy-of select="fm:DATA" />
Comments—You can add comments to the result XML with the use of the <xsl:comment> element. Whatever you place between the start and end tags will be in the resulting comment. The comment is inserted between "<!–" and "–>" when it is placed in the result document.
<xsl:comment> This text will be my comment. </xsl:comment> <!--This text will be my comment.-->
Literal Text and Elements—You can add literal text to the result document by simply adding it to the template. Other elements, such as the HTML tags, can also be used in the template. The text and elements will be placed in the result XML. The example below inserts the literal text and a small HTML table into the result for each row/record in the found set.
<xsl:template match="fm:ROW"> Hey! I found a Record. Let's create a table:<br /> <table> <tr> <td>Table!</td> </tr> </table> </xsl:template>
This is an easy way to start building a template for repeating elements. First, display some text for the first occurrence of the expression to let you know you have made the correct match. Then change the literal to something dynamic, based upon a value of the match. Finally, remove the predicate "" so that each repeat will be displayed:
<table> <xsl:template match="fm:ROW"> <tr> <td>Hey! I found a Record. Let's create another row, if any:</td> </tr> </xsl:template> </table>
FileMaker Pro has several script steps and functions to test the value of something (field or literal) and then proceed when the correct (or true) condition is met. The FileMaker Pro logical functions are If(test, trueResult, falseResult), Case(test1, result1, test2, result2, …, Default- Result), Choose(NumericValueTest, result0, result1, … resultN), IsEmpty(test), and IsValid(test). XSL has just two conditional elements, and the XPath expressions can be used to test for an empty value.
If—The element <xsl:if> has one required attribute, test. The template rules and literal text or elements between the start and end tags are used in the result only if the test is true. The test can be any XPath expression. This element can test for an empty value by using the text() function of an element. The test must resolve to a Boolean true. There is no "else" or "elseif" tests or "false" result to the test. The next element, <xsl:choose>, can be used for more than one test. You can nest a choose inside an if should you need to test the existence before proceeding, as seen in Listing 7.6. The example below tests for a value in a field.
<xsl:if test="fm:DATA/text()"> Ah ha! This has the data: <xsl:value-of select="." /> </xsl:if>
Choose—The <xsl:choose> element is similar to the FileMaker Pro Case() function. This element is never empty but must have at least one child element, <xsl:when>. The "when" element is like the <xsl:if> element because it has the test attribute and only a Boolean "true" will process the template between the start and end tags. You may have multiple <xsl:when> tests in the <xsl:choose> element. You may nest other conditional statements inside the <xsl:when> element.
Just like FileMaker Pro's Case() function, the <xsl:choose> element has an optional child element, <xsl:otherwise>. There is no attribute for the <xsl:otherwise> element, but it is never empty if it is used in the <xsl:choose> conditional tests. The example below uses the <xsl:if> and <xsl:choose> elements:
Listing 7.6: Conditional XSL
<!-- is there an attribute "Color"? --> <xsl:if test="@Color"> <!-- if there is then output the HEX value --> <xsl:choose> <xsl:when test="@Color = red">#FF0000</xsl:when> <xsl:when test="@Color = blue">#0000FF</xsl:when> <xsl:when test="@Color = green">#00FF00</xsl:when> <xsl:otherwise>#000000</xsl:otherwise> </xsl:choose> </xsl:if>
You can add elements by simply including them in the template for the result document. Most HTML elements are added this way. You can use the <xsl:copy> and <xsl:copy-of> elements to make replicas of elements from the source to the result. You can also use the XSL element <xsl:element> to create new elements in the result document.
This tag is most often used to transform an attribute into an element. For example, the FMPDSORESULT grammar and the FMPXMLRESULT grammar have the repeating element ROW for each record in the found set. Within the ROW element are the two attributes modid and recordid. Should you need to make these attributes into an element you could use the example below. The element <xsl:element> has one required attribute, name, and two optional attributes, namespace and use-attribute-sets.
<!-- This tag in the XML source: --> <xsl:element name="ModID"> <xsl:value-of select="fm:ROW/@MODID" /> </xsl:element> <!-- becomes in the XML result: --> <ModID>2</ModID>
Attributes may be added to created elements or copied elements by using the use-attribute-sets attribute with <xsl:element> and <xsl:copy>. Attributes may also be explicitly added to elements, such as HTML elements in the template source. XSL has an element for adding attributes called <xsl:attribute>. This element has one required attribute, name, which is the name of the attribute. The attribute namespace is optional for the <xsl:attribute> element. The <xsl:attribute> element may be used to transform an element in the source to an attribute in the result document.
Multiple <xsl:attribute> elements may be added to a single element. You can use attribute-sets or list all of the attributes for a single element. The created attributes will be applied to the nearest element in the source to become an element with those attributes in the result. The value of the attribute is the XPath expression or literal text between the start and end tags. Two examples are shown below. Listing 7.7 creates a hyperlink with one attribute for the href and the text for the link between the start and end tags. Listing 7.8 creates an image element, <img />, with three attributes. Even though the element is empty, the attributes are added to it.
Listing 7.7: Creating a hyperlink with a field value
<a> <xsl:attribute name="href"> <xsl:value-of select="fm:link" /> </xsl:attribute> <xsl:attribute name="target"> <xsl:text>_top</xsl:text> </xsl:attribute> <xsl:value-of select="fm:text" /> </a> <!-- becomes in the result: --> <a href="mylink.html" target="_top">My Text<a>
Listing 7.8: Displaying an image with path name field
<img /> <xsl:attribute name="src"> <xsl:value-of select="fm:PictNamePath" /> </xsl:attribute> <xsl:attribute name="width"> <xsl:value-of select="fm:PictWidth" /> </xsl:attribute> <xsl:attribute name="height"> <xsl:value-of select="fm:PictHeight" /> </xsl:attribute> <!-- becomes in the result: --> <img src="/books/3/422/1/html/2/images/MyPict.gif" width="100" height="75" />
The <xsl:attribute> element may also be used to display images in container fields in FileMaker Pro. The image may be in the database or a reference to the image. The FMPXMLRESULT grammar and the FMPDSORESULT grammar both will return the image as a linked source. The -img parameter on the element value tells Web Companion to make the connection to the database, grab the image in the field on a given record, and return it as a JPEG.
<!-- FMPDSORESULT for container field "Pict" --> <Pict>FMPro?db=Products.fp5&RecID=16&Pict=&-img<Pict> <!-- use this in a stylesheet: --> <xsl:element name="img" /> <xsl:attribute name="src"> <xsl:value-of select="fm:Pict" /> </xsl:attribute>
Using <xsl:element> and <xsl:attribute> allows you the freedom to transform elements and attributes into attributes and elements. Also, because the XSL elements are not nested, the XSL processors can set multiple attributes to a single element.