Chat Window XSL Styles Howto
This tutorial is only applicable for Kopete versions 0.11 and earlier
Goals:
The goal of this HOWTO is to help you in creating your own Kopete Chatwindow style, or to modify an existing style to suite your needs. It is aimed toward people who know HTML and CSS, but nothing about XSLT. People who already know XSLT will probably do better to just download the Kopete Message XSD Schema and get cracking.
Prerequisites:
Knowlege of HTML and CSS is essential. Knowledge of XSLT is highly reccomended.
As we go through this HOWTO, we will be constructing a very simple example chat style. If you wish, you can download this example to follow along.
The Main Message Element:
The main message element is normally the root of the message XML document. That is, the message is the document. Under normal circumstances, Kopete will processes and transform each message individually. The exception is if you enable the TransformAllMessages flag. More details on this flag will be provided later.
First, let's take a look at the XML structure of a message.
<xsd:element name="message">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="from" type="metaContact" minOccurs="0" maxOccurs="1"/>
<xsd:element name="to" type="metaContact" minOccurs="0" maxOccurs="1"/>
<xsd:element name="body" type="messageBody" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
<!-- The time only. eg 12:00 pm -->
<xsd:attribute name="time" type="xsd:string" use="required"/>
<!-- Full timestamp. eg Tue Feb 8 19:04:49 AST 2005 -->
<xsd:attribute name="timestamp" type="xsd:string" use="required"/>
<!-- Formatted timestamp. eg 12:00:57 pm -->
<xsd:attribute name="formattedTimestamp" type="xsd:string" use="required"/>
<!-- Message subject. Used by Jabber Email. -->
<xsd:attribute name="subject" type="xsd:string" use="required"/>
<!-- Message direction (Inbound, Outbound, Internal).
This is deprecated. Use @type and @route -->
<xsd:attribute name="direction" type="direction" use="required"/>
<!-- Message route (Inbound, Outbound, Internal).-->
<xsd:attribute name="route" type="route" use="required"/>
<!-- Message route (Normal, Action).-->
<xsd:attribute name="type" type="type" use="required"/>
<!-- Message importance.-->
<xsd:attribute name="importance" type="importance" use="required"/>
<!-- This is the main contact Id - the other person in the
converation besides you. If it is a group chat, it is the first
person who was being spoken to, or the group chat name. -->
<xsd:attribute name="mainContactId" type="xsd:string" use="optional"/>
</xsd:complexType>
</xsd:element>
As you can see, a message has many attributes available for the style to utilize. They key thing to remember when designing your stylesheet is that (normally) each message is processed as an individual document. So you don't usually need to use any loops or other fancy constructs in your stylesheet. What you will mainly be concerned about, is selecting attributes out of the document to print on the screen, and possibly performing tests on some of them to see how you should display the message.
XSLT and XPath:
In XSLT, you retrive a document parameter via a language called XPath. XPath is a book on it's own, so we won't be going into all the fancy things you can do with selectors here. We will only be using the basic tree-oriented XPath syntax. What this means, is that you select the nodes relative to their position in the document tree you are currently at, with each level being delimited by a slash. For example, 'from/contact' is the XPath location of the 'from' contact node.
To get some data from the document to print to the screen, you use a tag called 'xsl:value-of', with a 'select' parameter that specifies the XPath expression needed to reach the node. That is, if I want to select the "from" node of the message, I do so like this:
<xsl:value-of select="from/contact"/>
Now, the above line is not very useful on its own, because the "from" and "to" elements have their own sub-structures. Let's have a look at them:
<!-- The contact element -->
<xsd:complexType name="metaContact">
<xsd:sequence>
<xsd:element name="contact">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="contactDisplayName" type="displayName" minOccurs="1" maxOccurs="1"/>
<xsd:element name="metaContactDisplayName" type="displayName" minOccurs="1" maxOccurs="1"/>
</xsd:sequence>
<!-- The contact's id -->
<xsd:attribute name="contactId" type="xsd:string" use="required"/>
<!-- The contact's custom color -->
<xsd:attribute name="color" type="xsd:string" use="required"/>
<!-- The contact's photo. This file name only remains valid
while the message is in transit -->
<xsd:attribute name="userPhoto" type="xsd:string" use="optional"/>
<!-- The contact's protocol icon -->
<xsd:attribute name="protocolIcon" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
Getting Attributes from the Message:
Now, say I want to print out a span, with the foreground color being the "from" contact's color, and the contents being his contact display name. These items are XML attributes. To access an attribute, you provide the path to it's node, and them "@attributeName". So, if I wanted to get the 'color' attribute of a contact, I would do so like this:
<xsl:value-of select="from/contact/@color"/>
Adding Attributes to HTML Tags:
Another thing you have to do, is add the attribute into the style value of the span tag. To do this, you use the 'xsl:attribute' tag. This tag means that I want to add an attribute to whatever node it is under. For example, say I wanted to add the string 'color:blue' as a 'style' attribute of a span. I would do so like this:
<span>
<xsl:attribute name="style">
color:blue
</xsl:attribute>
</span>
Now, of course, you would never do this, since you could just type the style out directly into the tag! Where this capability becomes important, is when you want to set the value of an HTML attribute to something you selected from the XML document.
As an example, let's bring it all together and print out the span I described back in the last section:
<span>
<xsl:attribute name="style">
color:<xsl:value-of select="from/contact/@color"/>
</xsl:attribute>
Message from: <xsl:value-of select="from/contact/contactDisplayName/@text"/>
</span>
Getting text from the Message:
Ok, so now you know how to print attributes. "What about the actual message?" you say. The message is a chunk of text inside a node, and is therefore very easy to print out. You just print out the value of the node itself. Example:
<span> <xsl:value-of disable-output-escaping="yes" select="body"/> </span>
Simple, isn't it? The 'disable-output-escaping' attribute is to tell libxml not to escape any entities in the text. This is important if you expect that the data you are selecting may contain ampersands (&) or other special entities.
Testing on Values:
Ok, now for something a bit more advanced. Normally, you will not want every message in the chat to look exactly the same. You will want Inbound messages different from Outbound, etc. To do this, you will need to use tests, namely, 'xsl:if' and 'xsl:choose'.
The 'xsl:if' test is different from most programming languages in that there is no 'else' option. If you want to do something on one condition, and another thing if anything else, you basically need to use 'xsl:choose' along with 'xsl:when', which is more like a 'switch' statement along with 'case' statements in other languages.
The easiest way to show how to use 'xsl:if' and 'xsl:choose' is to give an example:
<div>
<xsl:attribute name="style">
<xsl:if test="@type='action'">
font-weight:bold;
</xsl:if>
<xsl:choose>
<xsl:when test="@route='inbound'">
background-color:red;
</xsl:when>
<xsl:when test="@route='outbound'">
background-color:blue;
</xsl:when>
<xsl:otherwise>
background-color:black;
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</div>
What this is going to do, is test if the message is an action. If it is, the text in it will be bolded. Then, it checks to see what the route of the message is. If it is incoming, it will make the background red. If it is outgoing, it will make the background blue. Anything else, and it will make it black.
Header and Footer:
The last thing you need to worry about are the headers and footers required on the stylesheet. I am not going to go into what these things mean and how you could tweak them - if you want, you can read up on it at your leisure.
<?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="message">
<!-- Your stuff goes here -->
</xsl:template>
</xsl:stylesheet>
Putting it All Together:
So, let's put everything we have done into a final stylesheet:
<?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="message">
<div>
<xsl:attribute name="style">
<xsl:if test="@type='action'">
font-weight:bold;
</xsl:if>
<xsl:choose>
<xsl:when test="@route='inbound'">
background-color:red;
</xsl:when>
<xsl:when test="@route='outbound'">
background-color:blue;
</xsl:when>
<xsl:otherwise>
background-color:black;
</xsl:otherwise>
</xsl:choose>
color:white;
</xsl:attribute>
<span>
<xsl:attribute name="style">
color:<xsl:value-of select="from/contact/@color"/>
</xsl:attribute>
Message from: <xsl:value-of select="from/contact/contactDisplayName/@text"/>
</span>
 
<span>
<xsl:value-of disable-output-escaping="yes" select="body"/>
</span>
</div>
</xsl:template>
</xsl:stylesheet>
All we have done here is combine the previous examples - and now we have an actual, working Kopete style! Granted, it is pretty ugly :) You may notice the ' ' in there and wonder what that is all about. Well, basically, ' ' is a non-breaking space in XML. doesn't work here guys.
There are many other pieces of data in the message that we did not cover, such as all the formatting information, the message directionality, etc. You can discover these properties either by reviewing the message XSD document (linked to above), or by looking to existing styles for examples.
kopete:i18n:
Kopete XSL styles have a special tag called 'kopete:i18n'. Basically, anything wrapped in this tag is parsed out for the KDE translators. Any localised strings in an XSL style that is to be included in CVS, *must* be wrapped in 'kopete:i18n' tags.
[ Edit ]
Kopete Instant Messenger