Chris Elleman Technical Stuff

15 May/10 Off

Filtering transformed XML when displaying HTML

The problem: at the Media Company my team has a number of systems which developers/testers/etc require accounts on; when a new person starts we have to manually provision accounts on each system which is a bit of a pain, especially when you are working on lots of projects and can get distracted. I wanted to put together a webpage which automatically queried each system and then consolidated a view of users accounts on the various systems, so that almost in real-time you could see their accounts being provisioned.

The Systems: we are using the following systems, with the APIs used to query them in brackets:

  • Anthill Pro - automatated Build/Release tool (Oracle DB Query)
  • Atlassin Jira - Bug tracking tools (MySQL DB Query)
  • VersionOne - Sprint planning/tracking tool (Rest API)
  • SubVersion - Code respository (.htpasswd Query)
  • TestRun - test script tracking tool (MySQL DB Query)

How it was built: rather than have a dynamic webpage query each system in real-time, I thought it would be better to gather the information from each system and produce a master XML file which has the detail for all of the systems, this also means that any user load is absorted by the web-server. I choose to use a perl script to make various requests using the APIs of the different systems to produce the resultant XML, an example of which is below:

<?xml version="1.0" encoding="UTF-8"?>
<accounts>
 <user username="celleman">
 <AD_dn>CN=Elleman\, Christopher,OU=Users,OU=Technology,OU=Departments,DC=....</AD_dn>
 <AD_email>Christopher.Elleman@...</AD_email>
 <AD_name>Elleman, Christopher</AD_name>
 <AD_sAMAccountName>CElleman</AD_sAMAccountName>
 <AD_title>System Engineer</AD_title>
 <ahpro_role>System Admin</ahpro_role>
 <ahpro_user_id>412</ahpro_user_id>
 <email>christopher.elleman@...</email>
 <jira_account>celleman</jira_account>
 <jira_user_id>10010</jira_user_id>
 <svn_roles>solengg-admins build-admins</svn_roles>
 <testrun_admin>0</testrun_admin>
 <testrun_userid>1</testrun_userid>
 <username>celleman</username>
 <v1_member_id>0</v1_member_id>
 <v1_role>System Admin</v1_role>
 <v1_username>..\celleman</v1_username>
 </user>
 <summary>
  <elapsedSeconds>3</elapsedSeconds>
  <generated>Sat May 15 22:10:03 BST 2010</generated>
 </summary>
</accounts>

The Transformation: The XSL is shown below, this converts the XML into HTML:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />

<xsl:template match="/">
 <table>
 <tr bgcolor="#66CCFF">
  <th>Username</th>
  <th>Name</th>
  <th>Title</th>
  <th>Email</th>
  <th>Anthill Pro</th>
  <th width="50">Jira</th>
  <th width="50">SVN</th>
  <th>TestRun</th>
  <th>VersionOne</th>
 </tr>
 <xsl:for-each select="accounts/user">
 <tr>
  <td><xsl:value-of select="@username"/></td>
  <td><xsl:value-of select="AD_name"/></td>
  <td><xsl:value-of select="AD_title"/></td>
  <td><a href="mailto:{email}"><xsl:value-of select="email"/></a></td>
  <xsl:choose>
   <xsl:when test="ahpro_role">
    <td><xsl:value-of select="ahpro_role"/></td>
   </xsl:when>
   <xsl:otherwise>
    <td style="background-color: red"></td>
   </xsl:otherwise>
  </xsl:choose>
  <xsl:choose>
   <xsl:when test="jira_user_id">
    <td><xsl:value-of select="jira_user_id"/></td>
   </xsl:when>
   <xsl:otherwise>
    <td style="background-color: red"></td>
   </xsl:otherwise>
  </xsl:choose>
  <xsl:choose>
   <xsl:when test="svn_roles">
    <td></td>
   </xsl:when>
   <xsl:otherwise>
    <td style="background-color: red"></td>
   </xsl:otherwise>
  </xsl:choose>
  <xsl:choose>
   <xsl:when test="testrun_admin">
    <td></td>
   </xsl:when>
   <xsl:otherwise>
    <td style="background-color: red"></td>
   </xsl:otherwise>
  </xsl:choose>
  <xsl:choose>
   <xsl:when test="v1_role">
    <td><xsl:value-of select="v1_role"/></td>
   </xsl:when>
   <xsl:otherwise>
    <td style="background-color: red"></td>
   </xsl:otherwise>
  </xsl:choose>
 </tr>
 </xsl:for-each>
 </table>
 <p>Generated: <xsl:value-of select="accounts/summary/generated"/></p>
</xsl:template>
</xsl:stylesheet>

The HTML + Javascript: in order to combine the XML and XSL at runtime, and also import the CSS, the html including the inline JavaScript is below, the example URLs are:

http://accounts.html

and, with filtering:

http://accounts.html?username=celleman


<html>
<head>
<link rel="stylesheet" href="style.css" type="text/css"/>
<title>Sol Eng Tools Accounts</title>
<meta http-equiv="Refresh" content="1800">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="Author" content="Chris Elleman" />
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script>
google.load("jquery", "1.3.1");
function getURLParam( name )
{
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( window.location.href );
  if( results == null )
    return "";
  else
    return results[1];
}
function loadXMLDoc(fname)
{
  var xmlDoc;
  // code for IE
  if (window.ActiveXObject)
  {
    xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
  }
  // code for Mozilla, Firefox, Opera, etc.
  else if (document.implementation
  && document.implementation.createDocument)
  {
    xmlDoc=document.implementation.createDocument("","",null);
   }
  else
  {
    alert('Your browser cannot handle this script');
  }
xmlDoc.async=false;
xmlDoc.load(fname);
return(xmlDoc);
}
function displayResult()
{
xml=loadXMLDoc("accounts.xml");
xsl=loadXMLDoc("accounts.xsl");
// code for IE
if (window.ActiveXObject)
  {
    ex=xml.transformNode(xsl);
    document.getElementById("example").innerHTML=ex;
  }
  // code for Mozilla, Firefox, Opera, etc.
  else if (document.implementation
  && document.implementation.createDocument)
  {
    xsltProcessor=new XSLTProcessor();
    xsltProcessor.importStylesheet(xsl);
    resultDocument = xsltProcessor.transformToFragment(xml,document);
    document.getElementById("example").appendChild(resultDocument);
  }
  ShowSelectedUser();
}
function ShowSelectedUser() {
 var users = getURLParam("username");
 if(users.length < 1) {
        return;
 }
 // Hide all rows
 var debug=document.getElementById("debug");
 $(".accounts_main_table").find("tr").hide().eq(0).show();
 // Un-Hide rows in the username variable
 var usersArray = users.split(",");
 for ( var i=0; i < usersArray.length; ++i ){
        var currentUser = usersArray[i];
   debug.innerHTML += "user: "+currentUser+"<br/>\n";
        $(".accounts_main_table").find("tr").each(function(index){
   if($(this).find("td").eq(0).text()==currentUser){
    $(this).show();
   }
 });
 }
}
</script>
</head>
<body onLoad="displayResult()">
<div id="debug" style="display: none;">
<b>DEBUG:</b><br/>
</div>
<div id="example">
</div>
</body>
</html>

Ok, so the un-hide part of the javascript is not the most efficient, but hey, it works!