Posts Tagged ‘Pages’

It’s so annoying. I had a for look in a javascript and it was blocking on the “<” sign of my for loop, causing an error. Here’s the fix:

<xsl:template match="/">
 <script type="text/javascript" language="javascript"><xsl:text disable-output-escaping="yes"><![CDATA[
   for (var i=0; i < someArray.length; i++){
      ...
   }
]]></xsl:text>
 </script>
Advertisements

When you have an external in a SitePublisher component and the Datum parameter has a replicatable attribute set to yes, the parameter that gets passed to the external is the ReplicantSourceID made of comma separated list of the values of the replicants.

<External>
 <Parameters>
 <Datum Type="String" ID="dcrDir" Name="dcrDir" Replicatable="true" ReplicantSourceID="dcrDir">/templatedata/acme/article/data/</Datum>
 <Datum Type="String" ID="dcrDir-0" Name="dcrDir" Replicatable="true" ReplicantSourceID="dcrDir">/templatedata/acme/author/data/</Datum>
 </Parameters>
 <Object Scope="local">com.acme.SomeClass</Object>
 <Method>someMethod</Method>
</External>

Only thing, there’s a space before the comma and [] around the lot, so make sure to trim() the string after the split and remove the []!

String[] dcrDirs = context.getParameterString("dcrDir").split(",");
for (String dcrDir : dcrDirs) {
  String fullDcrDir = fileDal.getRoot() + dcrDir.trim();
  fullDcrDir = fullDcrDir.replace("[","");
  fullDcrDir = fullDcrDir.replace("]","");
  ...
}

A workflow pre-processor is a Java class that gets invoked before the workflow start event is fired.

You can have it invoked before the workflow instantiation form is displayed or when it gets submitted but before the workflow starts.

This is normally quite useful to do some cleanup operation or to attach files to the workflow definition before it concretely gets instantiated.

In workflow modeler, select the workflow properties and scroll rigth down on the properties pane. The last two propewrties are Commands’s preProcessor and postProcessor properties. PreProcessor is for classes to be invoked before the instantiation form gets displayed, postProcessor for when the form is first submitted but before the workflow starts.

Now that we know a little bit more about those processors, let’s create a class that will attach some files (the index.page) to our workflow model before it gets an instantiation form, so we’ll use a pre processor command. Our class will need to implement InProcessJavaCommand to quality as a pre processor.

package com.acme.workflow;

import com.interwoven.modeler.workflow.WFMWorkflow;
import com.interwoven.modeler.workflow.commands.InProcessJavaCommand;
import java.util.Map;

public class AcmePreProcessor implements InProcessJavaCommand {

 public WFMWorkflow execute(WFMWorkflow workflow, Map<String, String> params) {
     return(workflow);
 }

}

This is a starting point. The workflow is the workflow model of our future Teamsite job (remember, it’s not been instantiated yet) and the params contains your configurable variables as a Map of key/value pairs. It is a normal map, so you can create or modify configurable variables before instantiation using get/put methods. Pretty handy.

At the end, we return the workflow as we have modified it. Also note the class names. These are not CSSDK classes, they’re Workflow Modeler classes.

Below is the final class doing the work of finding .page files and whenever we find one, attach the index.page of the same folder to the workflow. This is handy when publishing .pages in static delivery environment when they need refreshing. Note that in preProcessors, the paths to files are starting from the content store (/default/main/…).

package com.acme.workflow;

import com.interwoven.modeler.workflow.WFMFile;
import com.interwoven.modeler.workflow.WFMWorkflow;
import com.interwoven.modeler.workflow.commands.InProcessJavaCommand;
import com.interwoven.modeler.workflow.exceptions.WFMException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AcmePreProcessor implements InProcessJavaCommand {

public WFMWorkflow execute(WFMWorkflow workflow, Map<String, String> params) {

        Pattern pagePattern = Pattern.compile("(.*/sites/.*/).*\\.page$");

        String[] dependentFiles = params.get("preProcessorDependentFiles").split(",");

        for (String dependentFile : dependentFiles) {
            WFMFile[] attachedFiles = workflow.getFiles();
            for (WFMFile wFMFile : attachedFiles) {
                System.out.println("preprocessor: " + wFMFile.getFilePath());
                Matcher pageMatcher = pagePattern.matcher(wFMFile.getFilePath()); // we check this is a SitePublisher page by its name
                if (pageMatcher.find()) {
                    String pageFolderPath = pageMatcher.group(1); // we get the first back reference of the regular expression
                    String pagePathToAttach = pageFolderPath + dependentFile.trim();
                    WFMFile pageFileToAttach = new WFMFile(pagePathToAttach, "page attached by the preProcessor");

                    // check that this file is not already in the array of attached files
                    boolean pageAlreadyAttached = false; // assume that it's not until proven otherwise
                    WFMFile[] uptodateAttachedFiles = workflow.getFiles();
                    for (int i = 0; i < uptodateAttachedFiles.length; i++) {
                        if (uptodateAttachedFiles[i].getFilePath().equals(pageFileToAttach.getFilePath())) {
                            pageAlreadyAttached = true;
                        }
                    }
                    // also check that the file actually does exist
                    File pageFile = new File(pagePathToAttach);

                    // if it's not already attached, attach it
                    if (!pageAlreadyAttached && pageFile.exists()) {
                        try {
                            System.out.println("attadching file " + pagePathToAttach);
                            workflow.addFile(pageFileToAttach);
                        } catch (WFMException ex) {
                            Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "unable to attach the workflow file", ex);
                        }
                    } else {
                        System.out.println("The file is alrady attached to the workflow");
                    }
                } else {
                    System.out.println("The file " + wFMFile.getFilePath() + " is not a page");
                }
            }
        }
        return (workflow);
    }
}

Compile the class in the customer toolkit on the Teamsite server. After the server is back up, we can now tell the workflow to use our class.

The parameter preProcessorDependentFiles is a comma separated list of dependent files to attach whenever a page is found. In the workflow modeler, modify the preProcessor property to be “inprocess:com.acme.workflow.AcmePreProcessor:preProcessorDependentFiles=index.page” and publish the workflow model. Instantiate the workflow by selecting a SitePublisher page and the index.page should automagically be attached to the workflow with the comment “page attached by the preProcessor”.

Pattern pagePattern = Pattern.compile(“(.*/sites/(.*/)(.*)\\.page$”);

WFMFile[] attachedFiles = workflow.getFiles();
for (WFMFile wFMFile : attachedFiles) {
System.out.println(“portal news submit preprocessor: ” + wFMFile.getFilePath());
Matcher pageMatcher = pagePattern.matcher(wFMFile.getFilePath()); // we check this is a SitePublisher page by its name
if (pageMatcher.find()){
String pageFolderPath=pageMatcher.group(1); // we get the first back reference of the regular expression
String pageName=pageMatcher.group(2); // we get the second back reference of the regular expression
String pagePathToAttach=pageFolderPath + “index.page”;
WFMFile pageFileToAttach=new WFMFile(pagePathToAttach, “page attached by the preProcessor”);

// check that this file is not already in the array of attached files
boolean pageAlreadyAttached=false; // assume that it’s not until proven otherwise
for (int i=0; i<attachedFiles.length; i++){
if (attachedFiles[i].equals(pageFileToAttach)){
pageAlreadyAttached=true;
}
}
// if it’s not already attached, attach it
if (!pageAlreadyAttached){
try {
workflow.addFile(pageFileToAttach);
} catch (WFMException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, “unable to attach the workflow file”, ex);
}
}
} else {
System.out.println(“The file ” + wFMFile.getFilePath() + ” is not a page”);
}
}

return(workflow);
}

}

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.

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>