Archive for July, 2009

If you want to have a section of a page, for example the footer, created in teamsite but transformed as a server side include (following our example, the file to include would be “footer.shtml” on the presentation tier, the method for it is surprisingly easy.

First, create a footer component and add some HTML in it through the appearance xsl, say

<xsl:template match="/">
<div>footer goes here</div>
</xsl:template>

Then, add a boolean datum (preferably on the appearance xml box), to indicate if you want to  perform an include or display the content.

<Datum ID="include" Type="Boolean" Name="Include">true</Datum>

Add a choose statement to look at the value of the datum to perform the include. We also only want to perform the include on the final resulting page, not when we’re previewing. When we preview, we want to see the content normally.

<xsl:template match="/">
<xsl:choose>
            <xsl:when test="(/Properties/@Admin = 'true' or /Properties/Datum[@ID='include'] = 'false')">
                  <div>footer goes here</div>
            </xsl:when>
            <xsl:otherwise>
                  <div>include should go here</div>
            </xsl:otherwise>

</xsl:template>

Save the component and submit it. Next, we’ll create two pages in our site. One called footer.page which we will use to generate footer.shtml, and sample.page wich we use to represent one of your pages that will have an include in it.

On footer.page, add the footer component and make sure its include setting on the appearance tab is set to no. Save and generate the page footer.shtml. You can look at the source (actions > view source). It should have the text “footer goes here”. The only thing is, it needs to have a different layout that does not include all the HTML stuff we don’t want, like another header and stuff like that, so you need to create a new layout that does that. You can find some instructions on it on this article I wrote to modify some meta headers (but the method is the same – I’ll write an article on creating layouts on its own soon).

On sample.page, add the footer component and make sure its include setting on the appearance tab is set to yes.  Save and generate sample.shtml. you can look at the source (actions > view source). It should have the text “include should go here”.

Our pages are nearly ready, the logic is working but we don’t actually have an include directive yet.

Edit the footer component and replace in the appearance xsl box the div for the include until you have this xsl:

<xsl:template match="/">
<xsl:choose>
            <xsl:when test="(/Properties/@Admin = 'true' or /Properties/Datum[@ID='include'] = 'false')">
                  <div>footer goes here</div>
            </xsl:when>
            <xsl:otherwise>
                  <xsl:variable name="include_directive">/footer.shtml</xsl:variable>
                  <xsl:text disable-output-escaping="yes"><![CDATA[<!--#include virtual="]]></xsl:text>
                  <xsl:value-of select="$include_directive" />
                  <xsl:text disable-output-escaping="yes"><![CDATA[" -->]]></xsl:text>
            </xsl:otherwise>
</xsl:choose>

</xsl:template>

Save and submit the component, get the latest component for the footer.page and sample.page and regerate footer.page and sample.page again. This time, sample.shtml should have an include directive that actually does something. (you may have to change the page to match your own path to footer.shtml).

Note: you may have to generate the files as html and rename them (manually or through a workflow) or configure sitepublisher to generate files as .shtml.

Advertisements

There are times you want ot figure out if you are generating the page, editing it or previewing it. The difference for example might be useful to replace some output with a server side include directive or outputing the in-context-edit span only when we are in in-context-edit mode.

There are properties that we can look at within our components to identify our context. When you’re editing the component, preview it in order to show the resulting XML document that appearance XSL will process. Towards the top, /Properties/@Admin is set to true. When that is the case, we’re inside the teamsite/SitePublisher environment, e.g. we’re either previewing or editing the page  or the component. When it’s not set, we’re generating the page.

<xsl:choose>
<xsl:when test="not(/Properties/@Admin)">
   ...   not previewing ...
</xsl:when>
<xsl:when test="/Properties/@Admin = 'true'">
   ...   editing or previewing ...
</xsl:when>
</xsl:choose>

For a page in in-context edit with the spans and a generated page without them, use the following:

<xsl:choose>
    <xsl:when test="not(/Properties/@Admin)">
        <xsl:value-of select="article/title" />
    </xsl:when>
    <xsl:when test="/Properties/@Admin = 'true'">
        <span in-context-edit="{/Properties/Data/Datum[@ID='article']/@ID}" dcr-item-paths="/article/title"><xsl:value-of select="article/title" /></span>
    </xsl:when>
</xsl:choose>


/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.acme;

import com.interwoven.cssdk.access.CSAuthenticationException;
import com.interwoven.cssdk.access.CSAuthorizationException;
import com.interwoven.cssdk.access.CSExpiredSessionException;
import com.interwoven.cssdk.common.CSClient;
import com.interwoven.cssdk.common.CSException;
import com.interwoven.cssdk.common.CSRemoteException;
import com.interwoven.cssdk.factory.CSFactory;
import com.interwoven.cssdk.factory.CSLocalFactory;
import com.interwoven.cssdk.filesys.CSExtendedAttribute;
import com.interwoven.cssdk.filesys.CSSimpleFile;
import com.interwoven.cssdk.filesys.CSVPath;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
*
* @author laurent
*
*/
public class MediaSurfaceArticleDownloader {

private DocumentBuilder builder;
private XPathExpression xPathExpressionDate;
private XPathExpression xPathExpressionTitle;
private SimpleDateFormat dateFormat = new SimpleDateFormat(“dd MMMM yyyy”);
private SimpleDateFormat reverseDateFormat = new SimpleDateFormat(“yyyyMMdd”);
private Transformer transformer;
private CSClient client;
private CSExtendedAttribute[] extendedAttributes = new CSExtendedAttribute[1];

public MediaSurfaceArticleDownloader() {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
this.builder = factory.newDocumentBuilder();
System.out.print(“Preparing XPath pattern…”);
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xPath = xPathfactory.newXPath();
this.xPathExpressionDate = xPath.compile(“/article/date”);
this.xPathExpressionTitle = xPath.compile(“/article/title”);
System.out.println(“Done”);
TransformerFactory tFactory = TransformerFactory.newInstance();
this.transformer = tFactory.newTransformer();
System.out.print(“Connecting to localhost…”);
Properties localProperties = new Properties();
localProperties.setProperty(“cssdk.cfg.path”, “/apps/interwoven/teamsite/cssdk/cssdk.cfg”);
CSFactory csFactory = CSLocalFactory.getFactory(localProperties);
this.client = csFactory.getClientForCurrentUser(new Locale(“en”, “uk”), “MediaSurfaceArticleDownloader”, “localhost”);
System.out.println(“Done”);
System.out.print(“Preparing extended attributes…”);
extendedAttributes[0] = new CSExtendedAttribute(“TeamSite/Templating/DCR/Type”, “intranet/article”);
System.out.println(“Done”);
} catch (CSAuthenticationException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (CSRemoteException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (CSException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (ParserConfigurationException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (TransformerConfigurationException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (XPathExpressionException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
}
}

private void download(URL targetURL) {
Document document;
String targetDirectoryName = “/default/main/intranet/WORKAREA/Compliance/templatedata/intranet/article/data/compliance/news”;
byte[] fileBytes = new byte[255];
try {

// read the targetURL and create an XML file with it
InputStream inputStream = targetURL.openStream();
String outputFileName = targetDirectoryName + “/” + targetURL.getFile().replaceAll(“/compliance/whats-new/(.*)\\?.*$”, “$1”);
System.out.println(“output file name: ” + outputFileName);
OutputStream outputStream = new FileOutputStream(outputFileName);
int bytesRead;
while ((bytesRead=inputStream.read(fileBytes))!= -1) {
outputStream.write(fileBytes, 0, bytesRead);
}

// set the metadata on the file
CSSimpleFile inputFile = (CSSimpleFile) client.getFile(new CSVPath(outputFileName));
inputFile.setExtendedAttributes(extendedAttributes);

// rename the file with the article date and the title
// we need to re-open the stream that the document builder has closed for us.
InputSource inputSource = new InputSource(new FileInputStream(inputFile.getVPath().getPathNoServer().toString()));
String dateAsString = xPathExpressionDate.evaluate(inputSource);
String reverseDateString;
Date newsDate = new Date();
try {
newsDate = dateFormat.parse(dateAsString);
} catch (Exception e) {
newsDate = new Date();
}
try {
reverseDateString = reverseDateFormat.format(newsDate);
} catch (Exception e) {
reverseDateString = “19700101”; /* use the epoch */
}

// we need to re-open the stream that the date XPath evaluation has closed for us.
inputSource = new InputSource(new FileInputStream(inputFile.getVPath().getPathNoServer().toString()));
String title = xPathExpressionTitle.evaluate(inputSource);
String titleNoSpace = title.replaceAll(“\\s“, “-“);

// come up with the file name that will contain this DCR
outputFileName = reverseDateString + “-” + titleNoSpace;
outputFileName = outputFileName.replaceAll(“(<B>|</B>|<I>|</I>)”,””);
outputFileName = outputFileName.replaceAll(“[‘\\\\/:*?\”<>|,&;’`]”,””);
System.out.println(“renaming file to ” + outputFileName);
inputFile.rename(outputFileName, true);

} catch (CSAuthorizationException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (CSExpiredSessionException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (CSRemoteException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (CSException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (XPathExpressionException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
//        } catch (TransformerConfigurationException ex) {
//            Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
//        } catch (TransformerException ex) {
//            Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
//        } catch (SAXException ex) {
//            Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
}

}

public void seek(boolean download) {
InputStream inputStream = null;
try {

//URL articlesURL = new URL(“file:///home/piquetl/WIM-1734/mediasurface.source.articles.xml“);
//URL articlesURL = new URL(“http://webcontent/whatsnew/compliance/?view=toTeamSite“);
URL articlesURL = new URL(“http://webcontent/compliance/whats-new/?view=toTeamSite“);
inputStream = articlesURL.openStream();
InputSource inputSource = new InputSource(inputStream);
Document document = builder.parse(inputSource);
NodeList articleNodes = document.getElementsByTagName(“article”);

for (int i = 0; i < articleNodes.getLength(); i++) {
URL articleURL = new URL(articleNodes.item(i).getFirstChild().getNodeValue());
//System.err.println(articleURL.getPath());

if (!articleURL.getFile().contains(“.pdf”) &&
!articleURL.getFile().contains(“.doc”) &&
!articleURL.getFile().contains(“.xls”) &&
!articleURL.getFile().contains(“.ppt”)) {
System.out.println(articleNodes.item(i).getFirstChild().getNodeValue());
if (download) {
download(articleURL);
}
}
}

} catch (SAXException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (MalformedURLException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
} finally {
try {
inputStream.close();
} catch (IOException ex) {
Logger.getLogger(MediaSurfaceArticleDownloader.class.getName()).log(Level.SEVERE, null, ex);
}
}

}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
MediaSurfaceArticleDownloader urlDownloader = new MediaSurfaceArticleDownloader();
urlDownloader.seek(true); // true for download, false otherwise
}
}

This code recursively traverses the sites directory of the current workarea and returns each  folder.

This is usefull when creating new pages automatically through workflows.

package com.acme.intranet;

import com.interwoven.cssdk.common.CSClient;
import com.interwoven.cssdk.common.CSException;
import com.interwoven.cssdk.filesys.CSAreaRelativePath;
import com.interwoven.cssdk.filesys.CSDir;
import com.interwoven.cssdk.filesys.CSVPath;
import com.interwoven.cssdk.filesys.CSWorkarea;
import com.interwoven.datasource.SortedValuesMapDataSource;
import com.interwoven.datasource.core.DataSourceContext;
import com.interwoven.livesite.common.cssdk.datasource.AbstractDataSource;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SiteFoldersDataSource extends AbstractDataSource implements SortedValuesMapDataSource {

    private Map<String, String> dataSourceValues = new HashMap<String, String>();
    private File startingDirectory = null;

    public final void traverse( final File f ) throws IOException {
  if (f.isDirectory()) {
   onDirectory(f);
   final File[] childs = f.listFiles();
   for( File child : childs ) {
    traverse(child);
   }
   return;
  }
  onFile(f);
 }

    /* method executed whenever a directory is encountered. */
    public void onDirectory(final File directory) {
        String directoryRelativePath = new CSVPath(directory.getAbsolutePath()).getAreaRelativePath().toString();
        String directoryPath=directory.getAbsolutePath();
        if (!directory.equals(startingDirectory)) {
            // omit the sites directory
            dataSourceValues.put(directoryRelativePath, directoryRelativePath);
        }
    }

    /* method executed whenever a file is encountered. You should override this method */
    public void onFile( final File f ) {
 }

    public Map<String, String> execute(DataSourceContext dsContext) {
        CSClient client = getClient(dsContext);
        try {
            CSWorkarea workarea = client.getWorkarea(new CSVPath(dsContext.getServerContext()).getArea(), true);
            String sitesDirectoryRelativePath="sites";
            CSDir sitesCSDirectory = (CSDir) workarea.getFile(new CSAreaRelativePath(sitesDirectoryRelativePath));
            startingDirectory = new File(sitesCSDirectory.getVPath().getPathNoServer().toString());

            try {
                traverse(startingDirectory);
            } catch (IOException ex) {
                Logger.getLogger(SiteFoldersDataSource.class.getName()).log(Level.SEVERE, null, ex);
            }
            return(dataSourceValues);
        } catch (CSException ex) {
            Logger.getLogger(SiteFoldersDataSource.class.getName()).log(Level.SEVERE, null, ex);
        }
        return(dataSourceValues);
    }
}

Whenever I need to do development work with teamsite, I use the following tools:

NetBeans IDE

Image representing NetBeans as depicted in Cru...

Image via CrunchBase

This editor lets me do Java, Perl, XSL, XML, HTML, Javascript, CSS and text

editing in general.

JD Gui

This is a java de-compiler, for looking under the hood of java applications

Putty + Putty Connection Manager

This allows me to do open secure terminals to the Teamsite servers

WinSCP

This allows me to do secure copying of files from the teamsite servers to my local development machine.

Internet Explorer Developer toolbar / Firebug

This is to quickly look at sections of pages and look at the DOM quickly. It also helps with CSS.

Interwoven Workflow Modeller

This is to develop new workflow models.

The Regex Coach

I use this to ensure my regular expressions are always accurate. It’s a brilliant tool.