Archive for June, 2009

The last stage of a content standard submit workflow will most likely show you a “add to favourites” pages that, depending on your use, you may find completely redundant.

If you want to remove it from the workflow, then you’ll have to look at the wizard that takes you through it.

When you edit a SitePublisher page, you in effect load Edit.do, a struts action that lets you edit your page (if you want more information on the Edit.do action, you can find it here but it’s a bit of a long read). When you do this via ccstd, the next button will kick off a wizard associated with the next url, namely /iw-cc/command/iw.ccstd.submit with lots of query string parameters at the end.

Given this command, we apply some magic to find out the class behind the command on its other parameters, namely the wizard id if it has any. We find the command iw.ccstd.submit is defined in /httpd/webapps/content_center/WEB-INF/conf/ccstd/commands.xml.

    <command id="iw.ccstd.submit">
        <!-- Operation on CSDir and files. -->
        <operation id="Submit"/>
        <impl/>
        <command-param id="wizard_id" value="iw.submit"/>
    </command>

So now we know that the wizard defined as the “iw.submit” wizard is the one we’re after. Now we need to look at the innards of that wizard and tell it what we think of its favorites page…

The Wizard

As explained in this post, you can find the wizard quite easily using an easy technique. In our case, the wizard we’re after is in httpd/webapps/content_center/WEB-INF/conf/ccstd/wizard_submit.xml.

The first step in the wizard is the confirm step. It checks everything is ok and show you the next actions drop down list. We can get those from the “next”  elements (new_job, new_job_attach, transition_task, later). It transitions to the next step which is the tagging one (in my case tag_new_job, the metadata step).

After tagging has taken place, the submit command occurs (in the new_job step). This, in effect, transfers the control to the workflow that can be triggered by the submit event (defined in available templates and available_models).

To achieve this, it does a submit command that says “at the end of the workflow, run the iw.ccstd.wft_helper command”, which is the one doing the feedback. Unfortunately, it’s the same code that instantiates our workflow model so changing the success URL to something else would not work.

    <command id="iw.ccstd.wft_helper">
        <forward url="/ccstd/workflow/cgi_or_feedback.jsp"/>
    </command>

After the workflow has transitioned and completed, the cgi_or_feedback.jsp forwards to the iw.ccstd_workflow_feedback command, which does display the iw-cc/workflow/feedback.jsp. What we can do is to overwrite the foward of the iw.ccstd_workflow_feedback command so that it closes the window, which is what we wanted all along. Note that we could (and probably should) also create a custom jsp that would look at the done_page and redirect us there if there is one, but this demonstration is now done.

    <command id="iw.ccstd.workflow_feedback">
      <impl/>
      <!--<forward url="/ccstd/workflow/workflow_feedback.jsp"/>-->
      <forward url="/teamsite/common/close_window.jsp" />
    </command>

In order to find the wizard behind a command, get the command id (e.g. iw.ccstd.submit), and find the details of that command, as described in finding the class behind a command. Then, declare a wizard variable with the id of the wizard you found in the file.

wizard=”iw.submit”

And run the following code on it:

for file in `find /apps/interwoven/teamsite/httpd/webapps/content_center/WEB-INF/conf -name "*wizard*" 2>/dev/null`
 do
  echo ${file}
  cat ${file} | grep ${wizard}
 done
In our example, the wizard is witout a doubt in the file httpd/webapps/content_center/WEB-INF/conf/ccstd/wizard_submit.xml

Generally speaking, when users enter data into a page, they think the page and the content are synonymous. This is why the components on the page have datum elements in them to allow the user to do just that. The page also contains elements of the presentation through for example the positioning of the components. This does go against the MVC principles of separating data from presentation.

Teamsite does offer its authors the chance to remediate all that through the use of DCRs, or Data Content Records. By having a datum of type DCR, this enables to point the page to the data, thus separating the concerns. Your content can be re-used as many times as you want (for example to generate a PDF document or import it into a 3rd party application) without changing your site no more.

Whenever you see people enter the data directly into the page, fight it. it pays off in the long run – always.

This article aims to explain how the inContext edit works for a SitePublisher page. I initially dug in as I wanted to understand how it worked in order to try to resize some elements. Still, this explains how some of the work in it is done.

When you edit a component in context, you end up with a URL like

http://teamsite.acme.com/iw-cc/Site/Page/Edit.do
?method=pageEdit
&vpath=//localhost/default/main/intranet/WORKAREA/Technology/sites/webdesign/index.page
&siteVpath=//localhost/default/main/intranet/WORKAREA/Technology/sites/webdesign
&isInContextEdit=true
&dialogMode=false
&full_redirect=false
&done_page=/command/iw.ui%3fvpath%3d//localhost/default/main/intranet/WORKAREA/Technology/sites/webdesign/
all requests for java web applications are routed though web.xml which defines the servlets that handle the requests based on patterns.
Edit.do is handled by the action servlet (as per httpd/webapps/content_center/WEB-INF/web.xml).
    <servlet-mapping>
 <servlet-name>action</servlet-name>
 <url-pattern>*.do</url-pattern>
    </servlet-mapping>

The servlet itself is defined with the following:
    <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
        <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts/struts-config.xml</param-value>
        </init-param>
        <init-param>
            <param-name>debug</param-name>
            <param-value>2</param-value>
        </init-param>
        <init-param>
            <param-name>detail</param-name>
            <param-value>2</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>

We can see that the struts servlet has a configuration file /WEB-INF/struts/struts-config.xml.
Struts defines a series of actions and routes the requests to the actions.

When we’re doing edit on a page, the path of the request is /iw-cc/Site/Page/Edit.do?method=pageEdit&vpath=…
so based on that knowledge, the action for this request is this one:

    <action path=”/Site/Page/Edit” parameter=”method” type=”com.interwoven.livesite.web.site.action.PageEditAction” name=”pageEditForm” scope=”request”>
      <forward name=”dispatchPageEdit” path=”/Site/Page/Edit.do?method=pageEdit” redirect=”true”/>
      <forward name=”openPageEdit” path=”/livesite/Site/Page/Edit/OpenPageEdit.jsp” redirect=”false”/>
      <forward name=”sessionConflict” path=”/livesite/Site/Page/Edit/SessionConflict.jsp” redirect=”false”/>
      <forward name=”sessionDirty” path=”/livesite/Site/Page/Edit/SessionDirty.jsp” redirect=”false”/>
      <forward name=”pageEdit” path=”/livesite/Site/Page/Edit/Edit.jsp” redirect=”false”/>
      <forward name=”hidden” path=”/livesite/Site/Page/Edit/EditHidden.jsp” redirect=”false”/>
      <forward name=”submitWorkflow” path=”/livesite/pageEdit-start-workflow.jsp” redirect=”false”/>
      <forward name=”noSite” path=”/livesite/Site/Page/Edit/NoSite.jsp” redirect=”false”/>
      <forward name=”addComponentToPage” path=”/livesite/Site/Page/Edit/AddComponentToPage.jsp” redirect=”false”/>
      <forward name=”paletteFreestyleLayout” path=”/livesite/Site/Page/Edit/Palette.jsp” redirect=”false”/>
      <forward name=”managementConsoleFreestyleLayout” path=”/livesite/Site/Page/Edit/ManagementConsole.jsp” redirect=”false”/>
      <forward name=”paletteFixedLayout” path=”/livesite/Site/Page/Edit/FixedLayoutPalette.jsp” redirect=”false”/>
      <forward name=”managementConsoleFixedLayout” path=”/livesite/Site/Page/Edit/FixedLayoutManagementConsole.jsp” redirect=”false”/>
    </action>

The action associates the method parameter of our request URL with the name and forwards the request to a jsp, in the case of pageEdit, that would be /livesite/Site/Page/Edit/Edit.jsp.
This jsp resides inside the web application so the path on the server, relative to the teamsite installation, translates to httpd/webapps/content_center/livesite/Site/Page/Edit/Edit.jsp.

Edit.jsp includes a few utilities javascript files, but also the two main ones for this activity, namely pageEdit.jsp and inContextEdit.jsp, both of them in livesite/javascript.
The load continues with the output of a hidden form (usefull later) and the dialog divs and then, includes the InContextEditor.jsp in the /livesite/includes/Page/Edit/ directory.

the InContextEditor.jsp outputs two divs, ModalInContextEdit and ContainerInContextEdit,the latter contains an iframe that points to /livesite/blank.html.
As its name suggests, blank.html is an empty file. This means that the content of the frame is empty at first and is populated dynamically a bit later, so we should look to javascript calls.

as an aside, InContextEditor.jsp includes resize.js, drag.js and snapgrid.js, commonly shared between most sitepublisher functions. In addition, the mysteriously named /livesite/includes/3p/ext/include-script.jsp is included.
This script includes another two scripts, /livesite/includes/3p/ext/js/ext-base.js and ext-all.js, which seem to be some library to manipulate the divs, positions and such-like.

Once the page is loaded (Edit.jsp), the HtmlUtil.addEventHandler is called to add an onload event to call the startPageEdit function, in pageEdit.jsp (it’s a javascript).
startPageEdit() sets the edit mode to inContextEdit if need be and calls reloadChildWindows(), which reloads the console to the right and the palette window to the left.
The reload mechanism is achieved by submitting the hidden form (discussed earlier) to the iframe, achieving a reload with all the required content.
The first parameter of the submit is the method parameter (the same method parameter as before for Edit.do, which defines what actually ahppens), the second is the name of the iframe it gets loaded in.
In our case, this means the console and the palette.

It is the setEditmode function which is of interest. It calls the toggleEditMode function which in turn calls toggleInContextEditMode();
It is this function that we believe sets the scene for in context editing and sizing the in context edit frames.

in inContextEdit.jsp, the function pollenableInContextEdit is called. It prevents people from moving and resizing things around and then enables the incontext edit in the function enableInContextEdit.

Somwhere along the line (I must admit I can’t find the call for now and stopped looking once I found it), the function initInContextEditElements must be called to set the behaviour of the in-context-editable sections, and set the onclick event which will populate the iframe with the DCR element we clicked on and show the div containing the iframe.
The onclick is a function named dispatchInContextEdit which invokes the InContext.do servlet (as before with the action servlet defined in web.xml) by submitting the form to it with the dispatchInContextEdit method and populates the InContextEditFrame.

Once received by the server, the request is routed to the action servlet which (from struts-config.xml) invokes /livesite/Site/Page/Edit/InContextEditDispatch.jsp.
This is where we will modify the frame properties.

InContextEditDispatch.jsp, IframeInContextEdit, iceIFrame()

first, the jsp loads some javascript libraries, including tiny_mce. None of them seem of particular interest at this point and seem to do run-of-the-mil interwoven stuff (if there is such a thing).
Then, the page defines its own set of javascript functions:
cancelInContextEdit,
reloadComponent,
dcrSaveDone,
applyChanges,
presubmitForm,
startInContextEdit

Then comes a HTML form pointing to /Site/Page/Edit/InContextUpdate, which makes us of the presubmit javascript function. The form is quite long and includes:
a few hidden input fields for method, vpath and such like
a few infoboxes to display success or warning messages,
a few hidden input fields to store some DCR properties
the dcrEditFrame, initially sized at 540 wide and 100%.
the Finish, Cancel buttons at the bottom.

The body of the document has an onLoad method executing startInContextEdit(). It executes initInContextEditor (within inContextEdit.jsp) and passes it the current frame (IframeInContextEdit).
This sizes the outter frame. After the sizing of the whole IframeInContextEdit, the dcrEditFrame’s src attribute is updated with the form that allows you to edit the content of the DCR.
This form is located at /command/iw.livesite.edit_dcr so we’ll need to resolve this address and see where it leads.

In order to do that, We’re back in web.xml and we find this url would be served by the commandServlet
    <servlet-mapping>
        <servlet-name>CommandServlet</servlet-name>
        <url-pattern>/command/*</url-pattern>
    </servlet-mapping>

The servlet itself resolves to this class:
    <servlet>
        <servlet-name>CommandServlet</servlet-name>
        <servlet-class>com.interwoven.ui.base.impl.command.CommandServlet</servlet-class>
    </servlet>

in /apps/interwoven/teamsite/httpd/webapps/content_center/WEB-INF/conf/livesite/commands.xml, we find the following entry:
  <command id=”iw.livesite.edit_dcr”>
    <operation id=”GenerateForm”/>
    <impl/>
    <command-param id=”wizard_id” value=”iw.livesite.edit_form”/>
  </command>

This tells us it is a wizard. We find wizard configuration in the WEB-INF/conf folders, so ours is in livesite. Looking at the file names, wizard_edit_dcr.xml is the right one with the wizard id matching.

<wizard id=”iw.livesite.edit_form”
        template=”/ccpro/formspub/wizard_base.jsp”
        start-step=”edit_dcr”>
 …
</wizard>

the first step points to a command (which really does nothing but is being checked to see if you can execute it). If the command has executed ok, it forwards to
/ccpro/formspub/wizard_datacapture.jsp, which is the datacapture form we’re after. This jsp includes a javascript /formspub/wizard/wizard_datacapture.js and executes the dc_wizard_initialize function wich also executes the dc_initForm() function.

All in a days work…