Thursday, January 8, 2009

Extending Share 3 - Adding new pages to share

In a previous blog article 'Extending Share 2 - Adding a New Content button to Document Library', we walked through the process of modifying Document Library to add a new toolbar component that included a button titled 'New Content'. In this article, We will walk through the process of creating a new page for Share that can be included for a site, adding to the existing pages including wiki, blog, discussion, document library, calendar etc. that can be selected from the site navigation bar. As an example, the new page we add will present a new view of document library content, leveraging some existing components form the Document Library page such as the tree navigation component.

This blog article will add these new pages into the share extension project structure discussed in the previous blog article 'Extending Share 1 - Creating a share extension project' that provides a clean way to isolate our changes from the existing share code. The new page will be able to be added to existing or new sites by using the 'Customize Site' feature of the site dashboard and selecting the new page.

Step 1 - Setting up the project

We will start with creating a project structure similar to that referenced in the previous blog article 'Extending Share 1 - Creating a share extension project' and the latest Alfresco SDK.

1. Create a new project (i.e. 'someco-share-extension') with the following source directories :

  • source/java - contains java code to be packaged in someco-share-ext.jar
  • config/alfresco/web-extension - contains 'web-framework-config-custom.xml'
  • config/alfresco/web-extension/site-data - contains custom model objects like pages, template-instances, and components
  • config/alfresco/web-extension/site-webscripts containing folder /com/someco to contain webscripts in a unique namespace
  • config/alfresco/templates - contains custom templates containing folder /com/someco to contain templates in unique namespace
  • source/web - contains web assets and delivered to the war, used for javascript and css as included by html templates
  • lib - contains required library elements (includes junit.jar for testing
  • test - contains java test code and configuration
I created each of these folders as a 'source directory' in Eclipse. Source directories in Eclipse automatically copy files to the classpath. This project depends on SDK Embedded to allow embedded testing (unit testing of custom data web scripts) (see the 'Unit Testing Web Scripts' blog article for using the SDK to run embedded unit tests of data web scripts.

2. Create the 'web-framework-config-custom.xml' file in the to contain a reference to our new page so that it can be included by customizing a site.

3. Copy the ant build.xml file from the source in the previous 'Extending Share' blog article into the root of the project. This build script has some handy tasks. Some of which were derived from the great works by Jeff Potts and his great new book Alfresco Developers Guide from Packt Publishing. Make sure to change the project name in the build.xml file as appropriate (i.e. 'someco-share-extension').

  • The 'deploy' tasks will call the 'package' task and copy the created jar to the APP_TOMCAT_HOME/WEB-INF/lib directory (should be configured to point to the expanded share war file directory in tomcat), and copy the contents of the 'source/web' and 'config/alfresco/*' folders to the APP_TOMCAT_HOME/WEB-INF/classes/alfresco directory.
  • The 'package' task will call the 'compile' taks and create a jar (someco-share-ext.jar) including our java code in the source/java folder, web scripts in the 'config/someco/site-webscripts' and templates in the 'config/someco/templates' folders.
Make sure APP_TOMCAT_HOME environment variable is configured to point to the directory where share war is deployed. In Eclipse, you can add this to the ant configuration (windows > preferences > ant > runtime, 'properties' tab as env.APP_TOMCAT_HOME).

4. Create (or copy) the build.properties file to the project root directory containing the following:
#directory names

dir.name.assemble=assemble
dir.name.bin=bin
dir.name.build=build
dir.name.classes=classes
dir.name.config=config
dir.name.source=source
dir.name.devenv=devenv
dir.name.dist=dist
dir.name.distro=distro
dir.name.docs=docs
dir.name.generated=generated
dir.name.java=java
dir.name.lib=lib
dir.name.test.results=
dir.name.test.results=test-results
dir.name.test.resources=test-resources
dir.name.web=web

file.name.jar=someco-share-ext.jar

dir.junit.lib=lib

Now we are ready to start creating our custom configuration. You can adjust these to make the ant build work with your own project structure.

Step 2 - Creating the page and template instance

To start out, we will create a new page called 'Content Grid' that is intended to display a list of content in a way similar to Document Library but as a grid showing meta-data and a content extract in columns. This extension follows the model object structure as outlined in the Alfresco Surf Platform - Developers Guide page, and analyzed in the 'Learning Surf 2 - Examining Slingshot configuration' blog article.

The custom model objects we create will be added to the 'config/alfresco/web-extension' source directory. The contents of this directory will be copied into the Share war directory using the ant deploy task in the Ant build file.

1. Create 'contentgrid.xml' file in the 'pages' folder of the 'source/alfresco/web-extension/site-data' source directory, with the following content:
<?xml version='1.0' encoding='UTF-8'?>
<page>
<title>Content Grid</title>
<description>Document library with Tree view</description>
<template-instance>contentgrid</template-instance>
<authentication>user</authentication>
</page>
This defines a page with the title 'Content Grid'. It refers to a template instance called 'contentgrid' defined next.

2. Create the 'contentgrid.xml' template instance in the 'template-instances' folder of the 'config/alfresco/web-extension/site-data' source folder with the following contents:
<?xml version='1.0' encoding='UTF-8'?>
<template-instance>
<template-type>org/alfresco/simple-contentgrid</template-type>
<properties>
<hasBreadcrumb>true</hasBreadcrumb>
<hasTreeview>true</hasTreeview>
<hasPackager>true</hasPackager>
</properties>
</template-instance>
This file tells Surf to use the template found from path org/alfresco/contentgrid path of the config/alfresco/templates folder.

Step 3 - Creating the template type

The template we will create first will be a simple test template with no functionality, to test our configuration and deployment. This template is a gutted out version of the documentlibrary.ftl found in the config/alfresco/templates source folder in the org/alfresco package. It includes a standard set of included global regions with a simple body announcing that the template has rendered.

1. Create 'simple-contentgrid.ftl' freemarker template in the org/alfresco path of the 'config/alfresco/templates' source folder with the following contents:
<#import "/org/alfresco/import/alfresco-template.ftl" as template />
<@template.header>
<link rel="stylesheet" type="text/css" href="${url.context}/templates/documentlibrary/documentlibrary.css" />
<script type="text/javascript">//<![CDATA[
(function()
{
// If no location.hash exists, convert a location.search to a location.hash and replace the page
var loc = window.location;
if (loc.hash === "" && loc.search !== "")
{
var url = loc.protocol + "//" + loc.host + loc.pathname + "#" + loc.search.substring(1);
window.location.replace(url);
}
})();
//]]></script>
<script type="text/javascript" src="${url.context}/templates/documentlibrary/documentlibrary.js"></script>
<script type="text/javascript" src="${url.context}/modules/documentlibrary/doclib-actions.js"></script>
</@>

<@template.body>
<div id="hd">
<@region id="header" scope="global" protected=true />
<@region id="title" scope="template" protected=true />
<@region id="navigation" scope="template" protected=true />
</div>
<div id="bd">
This is the Content Grid template
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
</@>

<@template.footer>
<div id="ft">
<@region id="footer" scope="global" protected=true />
</div>
</@>

2. Modify 'web-framework-config-custom.xml' file in the 'config/alfresco/web-extensions' folder to add a reference to the new page we just created to allow this page to be added when customizing the site. It should read:
<alfresco-config>


<config evaluator="string-compare" condition="SitePages" replace="true">
<pages>
<page id="calendar">calendar</page>
<page id="wiki-page">wiki-page?title=Main_Page</page>
<page id="documentlibrary">documentlibrary</page>
<page id="contentgrid">contentgrid</page>
<page id="discussions-topiclist">discussions-topiclist</page>
<page id="blog-postlist">blog-postlist</page>
</pages>
</config>

</alfresco-config>
When Share is started, the new page will not be configured to display with any sites. The new page can be added to existing sites by using the 'customize site' feature. If you wish this new page to be automatically added to new sites, add it to the 'presets.xml' file.

3. Run the ant deploy task to copy the configuration components to the deployed share war (APP_TOMCAT_HOME/webapps/share)

4. Start alfresco and share by starting tomcat

5. Login to share

6. Create the new site (or select it if already created)

7. Select the 'Customize Site' button and click 'Add Page' and select the 'Content Grid' page. The 'Content Grid' page should be added to the navigation bar.

8. Press this option to see the page we just created with the simple-contentgrid.ftl template. The message 'This is the Content Grid template' should be displayed.

Step 4 - Mapping the page components
Now we can advance to a more interesting page template. The previous simple-contentgrid.ftl only demonstrated that we could add a page and it would render, now we will create a page template that will be similar to the Document Library template, but display our custom grid view UI.
1. create contentgrid.ftl in the /org/alfresco directory of the /config/alfresco/templates source folder, with containing the following:
<#import "/org/alfresco/import/alfresco-template.ftl" as template />
<@template.header>
<link rel="stylesheet" type="text/css" href="${url.context}/templates/documentlibrary/documentlibrary.css" />
<script type="text/javascript">//<![CDATA[
(function()
{
// If no location.hash exists, convert a location.search to a location.hash and replace the page
var loc = window.location;
if (loc.hash === "" && loc.search !== "")
{
var url = loc.protocol + "//" + loc.host + loc.pathname + "#" + loc.search.substring(1);
window.location.replace(url);
}
})();
//]]></script>
<script type="text/javascript" src="${url.context}/templates/documentlibrary/documentlibrary.js"></script>
<script type="text/javascript" src="${url.context}/modules/documentlibrary/doclib-actions.js"></script>
</@>

<@template.body>
<div id="hd">
<@region id="header" scope="global" protected=true />
<@region id="title" scope="template" protected=true />
<@region id="navigation" scope="template" protected=true />
</div>
<div id="bd">
<div class="yui-t1" id="divDocLibraryWrapper">
<div id="yui-main">
<div class="yui-b" id="divDocLibraryDocs">
<@region id="toolbar" scope="template" protected=true />
<@region id="gridview" scope="template" protected=true />
</div>
</div>
<div class="yui-b" id="divDocLibraryFilters">
<@region id="filter" scope="template" protected=true />
<@region id="tree" scope="template" protected=true />
<@region id="tags" scope="template" protected=true />
</div>
</div>
</div>
<p>&nbsp;</p>
<p>&nbsp;</p>
</@>

<@template.footer>
<div id="ft">
<@region id="footer" scope="global" protected=true />
</div>
</@>


This template contains several regions, some global and some template specific. The template specific regions must have component configuraton xml files to indicate what ui webscript will be used to render in that region. The components follow a naming convention: ..