wiki:Tutorial/A Family Tree

A Family Tree

If you want to create a family homepage with corinis, you might as well include a family tree. We use the profiler and some sql to create the tree and some javascript to display the fields (so we can have a nice layout).

The first thing to be done is to create a profile we can use for the tree. Basically every user is a member of the family thus having a father, a mother, a partner and a gender (this is the information we need to successfully create the tree). All other information is optional (like include an email, maiden name and so on). I use quite a lot of java in this to simplify the xml tree (provides speed when parsing the tree to create the webpage and while building the xml-tree).

There is only one big problem with different branches. Not from the xml layout, but from the html layout. It is hard to almost impossible to create a layout that is able to support different family branches correctly, so we will have different branches.

The jsp

<%@include file="includes/header.ijsp"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>Family Tree</title> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="includes/style.css">
<script>
var x, y;
var lastlay;

function showLayer(id)
{
	var lay = document.getElementById(id);
	if (lastlay == lay)
		return;
	if (lastlay != null)
		hideLayer();
	lay.style.display = "block";
	lay.style.left = x + "px";
	lay.style.top = y + "px";
	lastlay = lay;
}

function hideLayer ()
{
	lastlay.style.display = "none";
	lastlay.style.top=0;
}

function followMouse (e)
{
	if (window.event)
	{
		x = window.event.clientX + document.body.scrollLeft;
		y = window.event.clientY + document.body.scrollTop;
		 
	}
	else
	{
		x = e.pageX;
		y = e.pageY;
	}
}

function init()
{
document.onmousemove = followMouse;
if (!window.event) document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE);
}
</script>
</head>

<body onload="init()">
<%
String startparent = request.getParameter("tree");
if (startparent == null)
	startparent = "-100";



// Build the Family Tree
Profiler prof = new Profiler (core);
Connection conn = null;
ResultSet results;
Statement stmt = null;
Element root = core.getDoc().createElement("TREE");
debug = "";

try
{
	// Step 1: get the first ones (both parents -1)
	conn = core.getConfigurator().getConnPool().getConnection();

	stmt = conn.createStatement();
	results = stmt.executeQuery("select distinct cp1.localuserid id from corinisprofiler as cp1, corinisprofiler as cp2 where (cp1.profilerfield_name='mutter' and cp1.data="+startparent+") and (cp2.profilerfield_name='vater' and cp2.data="+startparent+") and cp1.localuserid=cp2.localuserid");
	while (results.next ())
	{
		// add the top nodes in the tree 
		addPartner(createMember(results.getLong("id"), prof, core.getDoc()), root, core.getDoc());
	}
	results.close();
	stmt.close();
	
	// Step 2: lets do the *real* work
	buildTree (root, conn, core.getDoc(), prof, core);

}
catch (Exception e)
{
	out.println(e.getMessage());
}
%>
<div align="left">
<%
// each branch starts with partners with mother&father having id -100/-200/-300/... in their profile
String[] parents = {"Family Branch 1", "Family Branch 2", "Family Branch 3"};
%>
-
<%
for (int i=1; i <= parents.length; i++)
{
	if (String.valueOf(i * -100).equals(startparent))
	{
%><b><%=parents[i-1]%></b> - <%
	}
	else
	{
%>
	<a href="?tree=<%=i*-100%>"><u><%=parents[i-1]%> Baum</u></a> -
<%
	}
}
%>
<br/>
<%=core.parseDomHtml(root, "famtree.xsl")%>
</div>
</body></html>

<%!
String debug;
public void buildTree(Element parent, Connection conn, Document doc, Profiler prof, Core core) throws Exception
{
	for (Element child = (Element)parent.getFirstChild(); child != null; child = (Element)child.getNextSibling())
	{
		String mom = null;
		String dad = null;
		if (child.getNodeName().equals("PARTNER"))
		{
			// ok now find one of the parents
			mom = child.getAttribute("partnerw");
			dad = child.getAttribute("partnerm");
			if (mom == null || mom.length()==0) mom = "-1";
			if (dad == null || dad.length() == 0) dad = "-1";
			
			// select the kids out of the db
			Statement stmt = conn.createStatement();
			ResultSet results = stmt.executeQuery("select distinct cp1.localuserid id from corinisprofiler as cp1, corinisprofiler as cp2 where (cp1.profilerfield_name='mutter' and cp1.data='"+mom+"') and (cp2.profilerfield_name='vater' and cp2.data='"+dad+"') and cp1.localuserid=cp2.localuserid");
			
			while (results.next ())
			{
				// add the top nodes in the tree
				addPartner(createMember(results.getLong("id"), prof, core.getDoc()), child, core.getDoc());
				
				// find the partner
				Statement stmt2 = conn.createStatement();
				ResultSet results2 = stmt2.executeQuery("select distinct cp1.localuserid id from corinisprofiler as cp1 where (cp1.profilerfield_name='partner' and cp1.data='"+results.getLong("id")+"')");
				if (results2.next())
					addPartner(createMember(results2.getLong("id"), prof, core.getDoc()), child, core.getDoc());

				results2.close();
				stmt2.close();
			}
			
			results.close();
			stmt.close();                                         
		}
		buildTree(child, conn, doc, prof, core);
	}
}

public void addPartner(Element member, Element parent, Document doc)
{
	// got the partner id:
	String partnerId = member.getAttribute("partner");
	String sex = member.getAttribute("geschlecht");
	String sexP = sex.equals("m")?"w":"m";
	Element partnerNode = null;
	for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling())
	{
		if (child.getNodeName().equals("PARTNER"))
		{
			if (((Element)child).getAttribute("partner" + sexP).equals(partnerId))
			{
				partnerNode = (Element)child;
				break;
			}
		}
	}
	if (partnerNode == null)
	{
		partnerNode = doc.createElement("PARTNER");
		parent.appendChild(partnerNode);
	}
	partnerNode.setAttribute("partner"+sex, member.getAttribute("id"));
	partnerNode.appendChild(member);
}


public Element createMember (long id, Profiler prof, Document doc)
{
	prof.pview.id = id;
	Element e = prof.getEntry();
	Element root = doc.createElement("MEMBER");
	root.setAttribute ("id", String.valueOf(id));
	// we should set the partner, sex and parent ids here... otherwise the other code may become quite messy
	for (Node child = e.getFirstChild(); child != null; child = child.getNextSibling())
	{
		if (child.getNodeName().equals("ENTRY"))
		{
			root.setAttribute(DomUtil.getResultString(child, "NAME"), DomUtil.getResultString(child, "VALUE"));
		}
	}
	// add the email address
	root.setAttribute("EMail", prof.getCore().getUserEmail(id));
	return root;
}

%>

the xsl

Make sure you edit the filter in the information according to your profiler names:

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
        <table width="100%">
                <tr>
                        <xsl:apply-templates select="/TREE/PARTNER"/>
                </tr>
        </table>
  </xsl:template>
  
  <xsl:template match="PARTNER">
        <td align="center" valign="top">
                <span >
                        <xsl:attribute name="onmouseover">showLayer('<xsl:value-of select="MEMBER[position()=1]/@id"/>')</xsl:attribute>
                        <xsl:value-of select="MEMBER[position()=1]/@name"/>
                        <xsl:if test="not(MEMBER[position()=1]/@mname='')">
                                (<i><xsl:value-of select="MEMBER[position()=1]/@mname"/></i>)
                        </xsl:if>
                        <div style="display:none;position:absolute;top:0px;left:0px;overflow:visible;width:200px;height:100px;">
                        <xsl:attribute name="id"><xsl:value-of select="MEMBER[position()=1]/@id"/></xsl:attribute>
                        
                        <table style="background-color:#ccc;border: 1px solid #444;">
                        <tr><td>Born:</td><td><xsl:value-of select="MEMBER[position()=1]/@gebTag"/>.<xsl:value-of select="MEMBER[position()=1]/@gebMonat"/>&#160;<xsl:value-of select="MEMBER[position()=1]/@gebJahr"/> in <xsl:value-of select="MEMBER[position()=1]/@gort"/></td></tr>
                        <xsl:apply-templates select="MEMBER[position()=1]"/>
                        </table>
                        </div>
                </span>
                                                                
                <xsl:if test="count(MEMBER) = 2">&#160;<img src="images/rings.gif"/>
                        <xsl:text>&#160;</xsl:text>
                        <span>
                        <xsl:attribute name="onmouseover">showLayer('<xsl:value-of select="MEMBER[position()=2]/@id"/>')</xsl:attribute>

                        <xsl:value-of select="MEMBER[position()=2]/@vorname"/>
                        <xsl:if test="not(MEMBER[position()=2]/@mname='')">
                        (<i><xsl:value-of select="MEMBER[position()=2]/@mname"/></i>)
                        </xsl:if>
                        <div style="display:none;position:absolute;top:0px;left:0px;overflow:visible;width:200px;height:100px;">
                        <xsl:attribute name="id"><xsl:value-of select="MEMBER[position()=2]/@id"/></xsl:attribute>

                        <table style="background-color:#ccc;border: 1px solid #444;">
                        <tr><td>Geboren:</td><td><xsl:value-of select="MEMBER[position()=2]/@gebTag"/>.<xsl:value-of select="MEMBER[position()=2]/@gebMonat"/>&#160;<xsl:value-of select="MEMBER[position()=2]/@gebJahr"/> in <xsl:value-of select="MEMBER[position()=2]/@gort"/></td></tr>
                         <xsl:apply-templates select="MEMBER[position()=2]"/>
                        </table>
                        </div>

                        </span>
                </xsl:if>
                <br/>
                <xsl:value-of select="MEMBER/@nachname"/><br/>
                <table width="100%" style="border-top:1px solid">
                <tr>
                        <xsl:apply-templates select="PARTNER"/>
                </tr>
                </table>
        </td>
  </xsl:template>

  <xsl:template match="MEMBER">
        <xsl:for-each select="@*">
                <xsl:if test="(string-length(.) &gt; 1) and not (name()='gort') and not (starts-with(name(), 'geb')) and not (name()='vorname') and not (name()='mname') and not (name()='mutter') and not (name()='vater') and not(name()='nachname') and not(name()='partner') and not (name()='id')">
                <tr>
                        <td><xsl:value-of select="name()"/>:</td>
                        <td><xsl:value-of select="."/></td>
                </tr>
                </xsl:if>
        </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>