I'm new to XSLT. I know I need to use xsl:for-each-group, but I can't figure out anything other than a basic list. Would some sort of recursion work better? Any XSLT 1.0 or 2.0 solution would be fine.
Below is the example XML. Note the most important attribute for organizing data into a tree structure is #taxonomy. Other attributes #taxonomyName and #level are provided as optional helper attributes.
<?xml version="1.0" encoding="utf-8"?>
<documents>
<document level="0" title="Root document test" taxonomy="" taxonomyName="" />
<document level="1" title="Level one document test" taxonomy="\CategoryI" taxonomyName="CategoryI" />
<document level="1" title="Level one document test #2" taxonomy="\CategoryII" taxonomyName="CategoryII" />
<document level="2" title="Level two document test" taxonomy="\CategoryII\SubcategoryA" taxonomyName="SubcategoryA" />
<document level="2" title="Level two document test #2" taxonomy="\CategoryII\SubcategoryA" taxonomyName="SubcategoryA" />
<document level="3" title="Level three document test" taxonomy="\CategoryII\SubcategoryA\Microcategory1" taxonomyName="Microcategory1" />
<document level="2" title="Level two, no level one test" taxonomy="\CategoryIII\SubcategoryZ" taxonomyName="SubcategoryZ" />
</documents>
Here's the expected output. (Please note that indenting is not necessary in the output. I've done it here for readability.)
<ul>
<li>Root document test</li>
<li>CategoryI
<ul>
<li>Level one document test</li>
</ul>
</li>
<li>CategoryII
<ul>
<li>Level one document test #2</li>
<li>SubcategoryA
<ul>
<li>Level two document test</li>
<li>Level two document test #2</li>
<li>Microcategory1
<ul>
<li>Level three document test</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>CategoryIII
<ul>
<li>SubcategoryZ
<ul>
<li>Level two, no subcategory test</li>
</ul>
</li>
</ul>
</li>
</ul>
Here's the best I can do.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:key name="contacts-by-taxonomy" match="document" use="#taxonomy" />
<xsl:template match="documents">
<ul>
<xsl:for-each-group select="document" group-by="#taxonomy">
<xsl:sort select="#taxonomy" />
<li>
<h3><xsl:value-of select="current-grouping-key()"/></h3>
<ul>
<xsl:for-each select="current-group()">
<li><xsl:value-of select="#title"/></li>
</xsl:for-each>
</ul>
</li>
</xsl:for-each-group>
</ul>
</xsl:template>
</xsl:stylesheet>
I'll keep chugging away at it, but would be eternally grateful if someone could throw me a life jacket. Thanks!
OK, here's my solution at last. :-) Basically it recurses through the tree, and at each level, it does a for-each-group group-by="the next level of #taxonomy".
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" indent="yes" />
<xsl:template match="documents">
<ul>
<xsl:call-template name="tree-depth-n">
<xsl:with-param name="population" select="document"/>
<xsl:with-param name="depth" select="0"/>
<xsl:with-param name="taxonomy-so-far" select="''"/>
</xsl:call-template>
</ul>
</xsl:template>
<!-- This template is called with a population that are all descendants
of the same ancestors up to level n. -->
<xsl:template name="tree-depth-n">
<xsl:param name="depth" required="yes"/>
<xsl:param name="population" required="yes"/>
<xsl:param name="taxonomy-so-far" required="yes"/>
<!-- output a <li> for each document that is a leaf at this level,
and a <li> for each sub-taxon of this level. -->
<xsl:for-each-group select="$population"
group-by="string(tokenize(#taxonomy, '\\')[$depth + 2])">
<xsl:sort select="#taxonomy" />
<xsl:choose>
<!-- process documents at this level. -->
<xsl:when test="current-grouping-key() = ''">
<xsl:for-each select="current-group()">
<li><xsl:value-of select="#title"/></li>
</xsl:for-each>
</xsl:when>
<!-- process subcategories -->
<xsl:otherwise>
<li>
<h3><xsl:value-of select="current-grouping-key()"/></h3>
<ul>
<!-- recurse -->
<xsl:call-template name="tree-depth-n">
<xsl:with-param name="population" select="current-group()"/>
<xsl:with-param name="depth" select="$depth + 1"/>
<xsl:with-param name="taxonomy-so-far"
select="concat($taxonomy-so-far, '\\', current-grouping-key())"/>
</xsl:call-template>
</ul>
</li>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
With the given input, the output is:
<ul>
<li>Root document test</li>
<li>
<h3>CategoryI</h3>
<ul>
<li>Level one document test</li>
</ul>
</li>
<li>
<h3>CategoryII</h3>
<ul>
<li>Level one document test #2</li>
<li>
<h3>SubcategoryA</h3>
<ul>
<li>Level two document test</li>
<li>Level two document test #2</li>
<li>
<h3>Microcategory1</h3>
<ul>
<li>Level three document test</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>
<h3>CategoryIII</h3>
<ul>
<li>
<h3>SubcategoryZ</h3>
<ul>
<li>Level two, no level one test</li>
</ul>
</li>
</ul>
</li>
</ul>
Which I believe is what you wanted. (I put <h3>s in there as you did in your XSL attempt, for the category names and not for the document titles.)
Related
I'm trying to create list elements in the below structure using xsl:apply-templates. Is it possible to achieve the below output without using xsl:for-each?
i am able to acheive thee below structure with xsl:for-each but would like to know if it is possible with xsl:apply-templates.
Below is my XML
<Properties>
<Root>
<group-container>
<group-title>
<title-name>Packs1</title-name>
<title-sub-links>
<subtitle-name>sub1</subtitle-name>
</title-sub-links>
<title-sub-links>
<subtitle-name>sub2</subtitle-name>
</title-sub-links>
</group-title>
<group-title>
<title-name>Packs2</title-name>
<title-sub-links>
<subtitle-name>abc</subtitle-name>
</title-sub-links>
<title-sub-links>
<subtitle-name>xyz</subtitle-name>
</title-sub-links>
</group-title>
</group-container>
<group-title>
<title-name>link title 1</title-name>
</group-title>
<group-title>
<title-name>link xyz</title-name>
</group-title>
</Root>
</Properties>
XSL
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<div class="col-9 tab">
<ul>
<xsl:apply-templates select = "/Properties/Root/group-container/group-title"/>
<xsl:apply-templates select = "/Properties/Root/group-container/group-title/title-sub-links"/>
</ul>
</div>
</xsl:template>
<xsl:template match = "group-title">
<li>
<xsl:value-of select="title-name"/>
</li>
</xsl:template>
<xsl:template match = "title-sub-links">
<li>
<xsl:value-of select="subtitle-name"/>
</li>
</xsl:template>
</xsl:stylesheet>
Output received
<div class="col-9 tab">
<ul>
<li>Packs1</li>
<li>Packs2</li>
<li>sub1</li>
<li>sub2</li>
<li>abc</li>
<li>xyz</li>
</ul>
</div>
Expected output
<div class="col-9 tab">
<ul>
<li>Packs1</li>
<li>sub1</li>
<li>sub2</li>
</ul>
<ul>
<li>Packs2</li>
<li>abc</li>
<li>xyz</li>
</ul>
</div>
I think (!) you want to do:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<div class="col-9 tab">
<xsl:apply-templates select = "/Properties/Root/group-container/group-title"/>
</div>
</xsl:template>
<xsl:template match = "group-title">
<ul>
<li>
<xsl:value-of select="title-name"/>
</li>
<xsl:apply-templates select = "title-sub-links"/>
</ul>
</xsl:template>
<xsl:template match = "title-sub-links">
<li>
<xsl:value-of select="subtitle-name"/>
</li>
</xsl:template>
</xsl:stylesheet>
Below I created a simplyfied XML example of what my xml looks like. I have an attribute which contains a number that I would like to watch. It's sort of a counter and during the transformation I would like to add something whenever the counter ++.
The problem is the number of levels in my xml file. Here I only made three but I actually have like 8 or maybe even more. I need to find a way to compare the current node against the previous one (or vice versa) but with the levels taken into account. So for instance in the example below the lvl2 node with the id of 4 needs to be compared with the lvl3 node with id 3 simply to find out if the id attribute has been raised.
xml:
<lvl1 id="1">
<lvl2 id="1">
<lvl3 id="1"></lvl3>
<lvl3 id ="2"></lvl3>
</lvl2>
<lvl2 id="2">
<lvl3 id="3"></lvl3>
</lvl2>
<lvl2 id="4"></lvl2>
</lvl1>
Since global counter variables are out of the question with xslt Im currently out of ideas and can't seem to find any here or anywhere else..
the output would be something like:
<ul>
<div>id 1</div>
<li>
<ul>
<li>
<ul>
<li></li>
<div>id 2</div>
<li></li>
</ul>
</li>
<li>
<ul>
<div>id 3</div>
<li></li>
</ul>
</li>
<div>id 4</div>
<li></li>
</ul>
</li>
here the stylesheet which transforms the xml into the html output but without the divs:
<?xml version="1.0" encoding="UTF-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<ul>
<xsl:value-of select="#id"/>
<xsl:apply-templates select="lvl1"/>
</ul>
</xsl:template>
<xsl:template match="lvl1">
<li class="{#id}">
lvl 1
<ul>
<xsl:apply-templates select="lvl2"/>
</ul>
</li>
</xsl:template>
<xsl:template match="lvl2">
<li class="{#id}">lvl 2
<ul>
<xsl:apply-templates select="lvl3"/>
</ul>
</li>
</xsl:template>
<xsl:template match="lvl3">
<li class="{#id}">lvl 3
</li>
</xsl:template>
If you apply this XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />
<xsl:template match='/'>
<ul>
<xsl:apply-templates select='*' />
</ul>
</xsl:template>
<xsl:template match="*">
<li>
<div>
<xsl:value-of select='#id' />
<xsl:if test='count(*)>0'>
<ul>
<xsl:apply-templates select='*' />
</ul>
</xsl:if>
</div>
</li>
</xsl:template>
</xsl:stylesheet>
you get something that may be what you ask:
<ul>
<li>
<div>1<ul>
<li>
<div>1<ul>
<li>
<div>1</div>
</li>
<li>
<div>2</div>
</li>
</ul>
</div>
</li>
<li>
<div>2<ul>
<li>
<div>3</div>
</li>
</ul>
</div>
</li>
<li>
<div>4</div>
</li>
</ul>
</div>
</li>
</ul>
The question is a bit more complex than it seems, as preceding:: does not always work here to find last #id before current element. You need also check #id on parent and consider root position as well. This code produces the desired result with the XML given in the question. See my comments inside.
<xsl:template match="/">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="*">
<!-- get preceding (or parent) id -->
<xsl:variable name="lastId">
<xsl:choose>
<!-- try to get id from first preceding element -->
<xsl:when test="preceding::*[1]/#id">
<xsl:value-of select="preceding::*[1]/#id"/>
</xsl:when>
<!-- there could still be ids in parent elements -->
<xsl:when test="../#id">
<xsl:value-of select="../#id"/>
</xsl:when>
<!-- or this is the root element -->
<xsl:otherwise>
<xsl:value-of select="0"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- now compare current and last id -->
<xsl:if test="#id > $lastId">
<div>
<xsl:text>id </xsl:text>
<xsl:value-of select="#id"/>
</div>
</xsl:if>
<!-- create list item -->
<li>
<!-- check for subelements -->
<xsl:if test="*">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:if>
</li>
</xsl:template>
Depending on your real scenario, you might want to limit match="*" to match="*[starts-with(local-name(),'lvl')]", as proposed in a comment by LarsH.
I have the following markup:
<ul id="slider">
<!-- slider item -->
<li>
...
</li>
<!-- end of slider item -->
</ul>
and I have defined the following itemStyle and GroupStyle xsl in header.xsl and itemStyle.xsl for displaying data from a SharePoint 2010 List:
<!-- in header.xsl -->
<xsl:template name="Slider" match="*[#GroupStyle='Slider']" mode="header">
<ul id="slider">
</ul>
</xsl:template>
<!-- in itemStyle.xsl -->
<xsl:template name="Slider" match="Row[#Style='Slider']" mode="itemstyle">
<xsl:variable name="SafeImageUrl">
<xsl:call-template name="OuterTemplate.GetSafeStaticUrl">
<xsl:with-param name="UrlColumnName" select="#Picture"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="Title">
<xsl:call-template name="OuterTemplate.GetTitle">
<xsl:with-param name="Title" select="#Title" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="Details">
<xsl:call-template name="OuterTemplate.GetTitle">
<xsl:with-param name="Title" select="#Details" />
</xsl:call-template>
</xsl:variable>
<li>
<img src="{$SafeImageUrl}" alt="{$Title}" />
<section class="media-description">
<h2 class="slider-headline"><xsl:value-of disable-output-escaping="yes" select="$Title" /></h2>
<p><xsl:value-of disable-output-escaping="yes" select="$Details" /></p>
</section>
</li>
</xsl:template>
but the thing is, when applying the previous two templates, the <ul id="slider"></ul> appears isolated from all <li> items as below:
<ul id="slider"></ul>
<!-- a bunch of tables and td here.. -->
<ul style="width: 100%;" class="dfwp-column dfwp-list">
<li class="dfwp-item"></li>
<li>
<img alt="Must-see US exhibitions" src="">
<section class="media-description"><h2 class="slider-headline">Must-see US exhibitions</h2>
<p>(Blank)</p>
</section>
</li>
...
</ul>
all I want is to have <ul id="slider>" element to wrap those li's directly,
so how can i do that ?
Thanks
What's your input XML?
You'd do something like this:
<xsl:template name="Slider" match="*[#GroupStyle='Slider']" mode="header"><!-- Sure you want to match *? -->
<ul id="slider">
<!-- Match the input XML path to your rows from the context of the matched element above -->
<xsl:apply-templates select="Row[#Style='Slider']" mode="itemstyle" />
</ul>
</xsl:template>
<xsl:template name="Slider" match="Row[#Style='Slider']" mode="itemstyle">
<li>..</li>
</xsl:template>
Can't figure out why you are using "mode"s either.
problem solved, thanks to #James Love,
and here are the steps:
make a copy of ContentQueryMain.xsl, as the following article says
after having a copy of ContentQueryMain.xsl, edit it and look for OutTemplate.Body
then you can place your wrapper in the following variables (but they have to be escaped)
<xsl:template name="OuterTemplate.Body">
<xsl:variable name="BeginColumn1" select="string('<ul id="slider" class="dfwp-column dfwp-list" style="width:')" />
<!-- ^------------------^ -->
<xsl:variable name="BeginColumn2" select="string('%" >')" />
<xsl:variable name="BeginColumn" select="concat($BeginColumn1, $cbq_columnwidth, $BeginColumn2)" />
<xsl:variable name="EndColumn" select="string('</ul>')" />
<!-- ^---------^ -->
stupid workaround, but its working :S
I am having an issue with the following, I need the ID to be of each promotiom:
Here is my XML:
<Promotions>
<Promotion>
<Category>Arts & Entertainment</Category>
<Client>Client 1</Client>
<ID>2</ID>
<Title>Get your Free 2</Title>
</Promotion>
<Promotion>
<Category>Client 1</Category>
<Client>Artsquest</Client>
<ID>4</ID>
<Title>Get your Free 4</Title>
</Promotion>
<Promotion>
<Category>Client 1</Category>
<Client>Artsquest</Client>
<ID>5</ID>
<Title>Get your Free 5</Title>
</Promotion>
<Promotion>
<Category>Community & Neighborhood</Category>
<Client>Client 2</Client>
<ID>1</ID>
<Title>Get your Free 1</Title>
</Promotion>
<Promotion>
<Category>Education</Category>
<Client>Client 3</Client>
<ID>3</ID>
<Title>Get Your Free 3</Title>
</Promotion>
<Promotion>
<Category>Home & Garden</Category>
<Client>Client 4</Client>
<ID>6</ID>
<Title>Get your Free 6</Title>
</Promotion>
</Promotions>
Here is my XSLT file:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:asp="remove">
<xsl:output method="html" indent="yes" />
<xsl:key name="categories" match="Category" use="." />
<xsl:key name="client" match="Client" use="." />
<xsl:key name="title" match="Title" use="." />
<xsl:template match="/">
<ul id="red" class="treeview-red">
<xsl:for-each select="/Promotions/Promotion/Category[
generate-id(.) = generate-id(key('categories', .)[1])
]">
<li>
<span>
<xsl:value-of select="."/>
</span>
<ul>
<xsl:call-template name="category-client">
<xsl:with-param name="category" select="."/>
</xsl:call-template>
</ul>
</li>
</xsl:for-each>
</ul>
</xsl:template>
<xsl:template name="category-client">
<xsl:param name="category" />
<xsl:for-each select="/Promotions/Promotion[Category=$category]/Client[
generate-id(.) = generate-id(key('client', .)[1])
]">
<li>
<span>
<xsl:value-of select="."/>
</span>
<ul>
<xsl:call-template name="category-client-title">
<xsl:with-param name="category" select="$category"/>
<xsl:with-param name="client" select="."/>
</xsl:call-template>
</ul>
</li>
</xsl:for-each>
</xsl:template>
<xsl:template name="category-client-title">
<xsl:param name="category" />
<xsl:param name="client" />
<xsl:for-each select="/Promotions/Promotion[Category=$category]/Title[
generate-id(.) = generate-id(key('title', .)[1])
]">
<li>
<span>
<asp:LinkButton ID ="{/Promotions/Promotion[Category=$category]/ID}" onclick="LinkClicked">
<xsl:value-of select="."/>
</asp:LinkButton>
</span>
</li>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
My issue is that the ID outputted if the client has multiple promotions in a category, I only get the first ID and it repeats for each, what is the best way to change this so that the ID matches the row the promotion title is coming from?
here is the output I am speaking of,I am so not grasping xsl...
<ul id="red" class="treeview-red" xmlns:asp="remove">
<li><span>Arts & Entertainment</span><ul>
<li><span>Client 1</span><ul>
<li><span><asp:LinkButton ID="2" onclick="LinkClicked">Get your Free 2</asp:LinkButton></span></li>
<li><span><asp:LinkButton ID="2" onclick="LinkClicked">Get your Free 4</asp:LinkButton></span></li>
<li><span><asp:LinkButton ID="2" onclick="LinkClicked">Get your Free 5</asp:LinkButton></span></li>
</ul>
</li>
</ul>
</li>
<li><span>Community & Neighborhood</span><ul>
<li><span>Client 2</span><ul>
<li><span><asp:LinkButton ID="1" onclick="LinkClicked">Get your Free 1</asp:LinkButton></span></li>
</ul>
</li>
</ul>
</li>
<li><span>Education</span><ul>
<li><span>Client 3</span><ul>
<li><span><asp:LinkButton ID="3" onclick="LinkClicked">Get Your Free 3</asp:LinkButton></span></li>
</ul>
</li>
</ul>
</li>
<li><span>Home & Garden</span><ul>
<li><span>Client 4</span><ul>
<li><span><asp:LinkButton ID="6" onclick="LinkClicked">Get your Free 6</asp:LinkButton></span></li>
</ul>
</li>
</ul>
</li>
</ul>
The correct XPath in this situation is:
<asp:LinkButton ID ="{../ID}" onclick="LinkClicked">
Since you are in <Title> context within the <xsl:for-each>, you must go up one level and fetch the <ID> from there. Your try
/Promotions/Promotion[Category=$category]/ID
fetches all <Promotion>s of a certain category and takes the first ID from the bunch, which is the same every time, naturally.
Just change
<asp:LinkButton ID ="{/Promotions/Promotion[Category=$category]/ID}" onclick="LinkClicked">
to
<asp:LinkButton ID ="{../ID}" onclick="LinkClicked">
Try something like position()=1 in your XSLT to select the first occurrence.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" />
<xsl:template match="/">
<html>
<body>
<span>
<div style="background-color:#000066;color:#EEEEEE;padding:7px">
<a name="top" style="padding-left:10px;font-size:28pt">Alerting Variables</a>
</div>
</span>
<div style="display:block;padding-left:50px;padding-bottom:10px" class="hbuttons">
LDMS Alerts
LDSM Alerts
Why?
Examples
Resources
</div>
<div style="clear: left;"></div>
<!-- This is the Table of Contents-->
<div style="padding:5px">
<div style="padding:5px;margin-top:10pt;margin-bottom:10pt;font-weight:bold;font-size:20px">Table of Contents -
<a style="position:absolute;margin-left:40px" href="PrintPages/PrintAll.html">
<img border="0" src="images/PrintButton.png" />
</a></div>
<div style="font-family:Arial;font-weight:bold;margin-left:30px;font-size:10pt">
<xsl:if test="contains(identifiers/sectionname/alert/#name, 'Agent Watcher')">
Agent Watcher
<a style="position:absolute;margin-left:40px" href="PrintPages/PrintAW.html">
<img border="0" src="images/PrintButton.png" />
</a>
</xsl:if>
<ol style="margin-top:5">
<xsl:for-each select="identifiers/sectionname/alert">
<xsl:if test="contains(#name, 'Agent Watcher')">
<li style="margin-left:10pt;font-size:8pt">
<a>
<xsl:attribute name="href">#
<xsl:value-of select="#name" /></xsl:attribute>
<xsl:value-of select="#name" />
</a>
</li>
</xsl:if>
</xsl:for-each>
</ol>
</div>
<div style="font-family:Arial;font-weight:bold;margin-left:30px;font-size:10pt">
Intel vPro
<a style="position:absolute;margin-left:40px" href="PrintPages/PrintvPro.html">
<img border="0" src="images/PrintButton.png" />
</a>
<ol style="margin-top:5">
<xsl:for-each select="identifiers/SectionName/alert">
<xsl:if test="contains(#name, 'Intel vPro')">
<li style="margin-left:10pt;font-size:8pt">
<a>
<xsl:attribute name="href">#
<xsl:value-of select="#name" /></xsl:attribute>
<xsl:value-of select="#name" />
</a>
</li>
</xsl:if>
</xsl:for-each>
</ol>
</div>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Above is my code sample.
The first xsl:if statement always fails and never shows the Agent Watcher text or print me button. Even if the section is filled out in the XML. If the section is there, the first xsl:if statement fails, but the second one, contained in the xsl:for-each shows the content. How do I get this to work.
I want to have it encompassing so that if the XML has content in the section it will put it up but if not it wont be empty content with a header or vice versa. Attaching sample XML to process.
<identifiers>
<sectionname>
<alert name="Agent Watcher Service Startup"></alert>
<alert name="Agent Watcher Service Not Started"></alert>
<alert name="Agent Watcher Service Uninstalled"></alert>
<alert name="Agent Watcher File Deleted"></alert>
</sectionname>
<sectionname>
<alert name="Intel vPro agentless discovery failure"></alert>
<alert name="Intel vPro System Defense Remediation Alert"></alert>
<alert name="Intel vPro Enhanced System Defense Remediation Alert"></alert>
<alert name="Intel vPro Enhanced System Defense Alert"></alert>
</sectionname>
</identifiers>
Blockquote
I have a few other suggestions but you need to post the entire (relevant) XSLT before I can go on. At least the enclosing template is necessary.
EDIT: Here is my proposal for your stylesheet:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tmp="http://tempuri.org"
exclude-result-prefixes="tmp"
>
<tmp:config>
<tmp:alert label="Agent Watcher" link="PrintPages/PrintAW.html" />
<tmp:alert label="Intel vPro" link="PrintPages/PrintvPro.html" />
</tmp:config>
<xsl:variable name="everyAlert" select="
/identifiers/sectionname/alert
" />
<xsl:template match="/">
<html>
<body>
<!-- 8< snip -->
<div style="...">
<div style="...">
<xsl:text>Table of Contents - </xsl:text>
<a style="..." href="PrintPages/PrintAll.html">
<img border="0" src="images/PrintButton.png" />
</a>
</div>
<xsl:for-each select="document('')/*/tmp:config/tmp:alert">
<xsl:call-template name="section" />
</xsl:for-each>
</div>
</body>
</html>
</xsl:template>
<xsl:template name="section">
<xsl:variable name="this" select="." />
<xsl:variable name="alerts" select="
$everyAlert[contains(#name, $this/#label)]
" />
<xsl:if test="$alerts">
<div style="...">
<a href="#{translate($this/#label, ' ', '_')}">
<xsl:value-of select="$this/#label" />
</a>
<a style="..." href="{$this/#link}">
<img border="0" src="images/PrintButton.png" />
</a>
<ol style="...">
<xsl:for-each select="$alerts">
<li style="...">
<xsl:value-of select="#name" />
</li>
</xsl:for-each>
</ol>
</div>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Key features:
efficient code reuse through a named template
printed sections are easily configurable
uses <xsl:text> elements to avoid unwanted whitespace in the output while retaining freedom to format the XSLT code properly
uses attribute literal notation (the curly braces {}) instead of verbose <xsl:attribute> elements
uses a temporary namespace to allow storing config data in the stylesheet itself
uses an <xsl:for-each> loop and the document() function to retrieve and work with that config data
the for-each makes use of the context to transport the current #label and #link so no <xsl:param> is necessary (the <xsl:template name="section"> runs in tmp:config/tmp:alert context, not in sectionname/alert context!)
uses a global variable ($everyAlert) to store all nodes for later use