This article is a step-by-step guide to manually building a simple standalone web site with the SURF framework. This will familiarise you with some of the major components and building blocks used by SURF and provide a good foundation for understanding how Web Studio works under the covers.
As described by the Alfresco Wiki – Alfresco Surf is a lightweight, scriptable web framework built on top of Alfresco’s Web Script and Templating runtime. It is packaged as a single WAR file meaning it can exist as a standalone application. One of the nice things about the SURF framework is that you do not HAVE to use Alfresco, or any other data source to construct a web application, so for the purposes of this example, we will simply construct a basic standalone website without using Alfresco. We will take a static HTML web site template, componentise it and demonstrate loading different areas of the page using very basic Web Scripts.
09 Feb 2009 – Updated to use SURF starter WAR instead of alfwf.
Overview
SURF utilises the MVC pattern using various components and objects. XML files are effectively bound together to allow a page to be loaded and rendered. These include things like Pages, Templates, Components and Themes.
The array of Objects and configurations available to SURF is extensive and powerful and can be somewhat overwhelming for the purposes of just “getting started”. For this reason, I have chosen not to include the majority of these features and functions, however more advanced usage will be discussed in future articles. I would reccomend reading the Alfresco Wiki SURF Overview, as this explains the context of SURF in detail – http://wiki.alfresco.com/wiki/Surf_Platform. The SURF Platform Developers Guide also discusses in detail all of the available objects.
Take a look at the following diagram that represent a simplified path to how a page is loaded:
In simple terms, the page is specified and requested by the URL. The page definition then looks up the Template Instance, which then calls the actual template. This could be for example a Freemarker template file. The template file is broken down into regions which can each call page components.
Build the SURF war file
The SURF starter WAR file can be built from source using:
ant incremental-surf-starter
This creates the starter WAR file which includes example components and configurations. Alternatively you can download the WAR file here. Please be aware this was built on 09 Feb and may have changed since then, so building from source is reccomended.
When the WAR file is deployed, you should be able to access the splash page on http://localhost:8080/surf/page providing that you are using your local machine (localhost) and you have not renamed the webapp folder i.e. surf.
Cache
The SURF framework includes a cache facility and at the time of writing this was enabled by default. To save application server restarts it is a good idea to disable it during the development process.
- Edit the /WEB-INF/classes/alfresco/web-framework-application-context.xml file.
- Search for the word “updateDelay” and amend the value to “0″. There are two occurances and properties to change.
<property name="updateDelay"><value>0</value></property>
- Restart your application server for the change to take effect.
Site Configuration
- Create a new site configuration file within /WEB-INF/classes/alfresco/site-data/configurations named default.site.configuration.xml.
- Add the following code to configure the default page to be “index”:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<title>Sample Site Configuration</title>
<description>Sample Site Configuration</description>
<source-id>site</source-id>
<properties>
<root-page>index</root-page>
</properties>
</configuration>
Creating The Home Page
- Create a new page file within the pages dir – /WEB-INF/classes/alfresco/site-data/pages named index.xml. This will be the website home page.
/WEB-INF/classes/alfresco/site-data/pages/index.xml
<?xml version='1.0' encoding='UTF-8'?>
<page>
<title>Home</title>
<description>Site Home page</description>
<template-instance>index</template-instance>
<authentication>none</authentication>
</page>
Template-Instance
The template-instance references the appropriate renderer, and uses freemarker by default.
- Create a new template-instance within /WEB-INF/classes/alfresco/site-data/template-instances
- Call it index.xml. This must match the name used within the <template-instance> parameters within the page example above.
/WEB-INF/classes/alfresco/site-data/template-instances/index.xml
<?xml version='1.0' encoding='UTF-8'?>
<template-instance>
<template-type>index</template-type>
</template-instance>
In this example /WEB-INF/classes/alfresco/site-data/template-instances/index.xml references index.ftl within /WEB-INF/classes/alfresco/templates.
Templates
Next, we will create the corresponding template as referenced above by the in /WEB-INF/classes/alfresco/templates named “index.ftl”. This is the main page structure of our site. As mentioned previously, for the purposes of this article, we will construct a new site using an HTML site template. Choose an example HTML template for your site. There are plenty available on the web e.g. http://www.solucija.com/free-templates. Select a basic layout so that the site construction is simple.
- Open the main page of your new downloaded HTML template (typically index.html) and copy the source code of the index.html into your newly created index.ftl.
- Save the index.ftl template.
Now you should be able to test the new page by hitting your app server using the URL http://localhost:8080/surf/page?p=index. You should see your web template (probably with missing style and image data).
Now we will add in any missing page furniture files e.g. images, css, JavaScript etc so that the page will render correctly.
Copy the CSS file from the downloaded HTML template into /surf/css.
- Repeat the file copy for any other page furniture that may be part of your web template e.g. images and JavaScript files. For example, images should be copied to /surf/images and JavaScript to /surf/js etc.
- Edit your main freemarker template (/WEB-INF/classes/alfresco/templates/index.ftl) to reference these CSS/image/JS files. You can also use the Freemarker reference to URL context – ${url.context} to navigate to the correct directory, for example:
<link rel="stylesheet" type="text/css" href="${url.context}/css/style.css" media="screen" />
- Do the same for any other CSS, JS and images which are referenced within your HTML template.
- Refresh your browser and make sure that all of the referenced files are present. Remember you may have to alter the css file itself if it is referencing image files etc. You should then have a working home page.
Cache Refresh
When working with SURF editing files regularly, it may be required that the cache is cleared before your pages will render correctly. This can be done browsing to the cache invalidate page as follows:
http://localhost:8080/surf/control/cache/invalidate
Componentise The Page
A component is an instance of a component type that has been “bound” into a region or a slot. Basically the page is made up of components which can have different scopes. These can be either global, template or page depending on where you would like to use each component.
- Global – Components to be used on ALL pages e.g. a logo
- Template – components to be used on SOME pages i.e. > 1 but < all
- Page – component to be used on a single page
Global Components
Start by creating a global component such as the page header. These are sections of the page that will not change from one page to the next. On my example HTML template, the logo and general header section will not change between pages (with the exception of the top navigation which i will componentise later), so I will start with that.
- Create a new file in /WEB-INF/classes/alfresco/site-data/components named global.header.xml.
Naming Components
Naming SURF components is very important as it binds the component to a specific region of the page (i.e. where the component will be displayed), and defines the scope. The name is defined as follows scope.regionId.sourceId.xml so in the above example (global.header.xml) the component is of global scope, it is bound to a page region using header. Note there is no third parameter of the name (the sourceId binding to a specific page) in this example as this is a global scope component and therefore is used on all pages.
To provide a little further information on the Source ID, as noted above the value depends on the scope of the binding. So for page it’s the pageId, template it’s the templateId and as noted above global it’s always “global” so we ignore it and don’t make it part of the binding. The value is optional in the XML definitions for pages/templates/components because it can always be derived from the file name of the item – because the ID is bound in the filename.
Go ahead and create the global.header.xml as listed below:
/WEB-INF/classes/alfresco/site-data/components/global.header.xml
<?xml version='1.0' encoding='UTF-8'?>
<component>
<scope>global</scope>
<region-id>header</region-id>
<source-id>global</source-id>
<url>/header</url>
</component>
Web Scripts
One of the great things about SURF is the ability to run web scripts without requiring a connection to Alfresco. Introducing/detailing web scripts is out of scope for the purposes of this article however web scripts are discussed in detail on the Alfresco Wiki. I would recommend a basic understanding of Web Scripts before continuing.
For the purposes of this article, we will be using very simple Web Scripts to return our HTML components i.e. the html components that will make up our page. We actually have a couple of options here as to how the Web Scripts will work. For example, we could componentise the HTML into different physical files stored in a specific directory, and then use a web script to actually include the required HTML fragment file. Alternatively, we can simply store the HTML fragment within the Web Script Freemarker template file, so that this is the content that is rendered. Either option is fine, but I will use the latter for absolute simplicity! So for each Web Script, we will have two files – the XML descriptor file and the FTL renderer file.
You may have noticed above within the global.header.xml component definition that there is a parameter named <url>. This specifies the web script url to request. With this example, the web script to be executed is being called by /header. Lets setup the /header web script and the related Freemarker file to return the header HTML.
Web scripts within SURF are typically stored within /WEB-INF/classes/alfresco/site-webscripts.
- Create a web script descriptor file named header.get.desc.xml within /WEB-INF/classes/alfresco/site-webscripts.
- Add the following to the file:
/WEB-INF/classes/alfresco/site-webscripts/header.get.desc.xml
<webscript>
<shortname>Header</shortname>
<description>Loads global Header</description>
<url>/header</url>
</webscript>
- Save and close this file and create the accompanying Freemarker template within the same directory. Name it header.get.html.ftl.
Next we will chop out the HTML from our index.ftl that represents the region we are componentising i.e. the header HTML. Using my example, this is the header HTML that makes up outlined area in the image above.
- Edit the /WEB-INF/classes/alfresco/templates/index.ftl file and locate the HTML specific to your chosen global region e.g. header.
- Cut this code so that it is saved to the clipboard and then paste it into the newly created WEB-INF/classes/alfresco/site-webscripts/header.get.html.ftl file. My HTML looks like the following, obviously this will be different depending upon what HTML template you downloaded:
/WEB-INF/classes/alfresco/site-webscripts/header.get.html.ftl
<div id="top">
<p><a href="#">Home</a><a href="#">About</a><a href="#">Archive</a><a href="#">Sitemap</a></p>
<form id="search_engine" method="post" action="." accept-charset="UTF-8">
<p><input class="searchfield" name="search_query" type="text" id="keywords" value="Search Keywords" onfocus="document.forms['search_engine'].keywords.value='';" onblur="if (document.forms['search_engine'].keywords.value == '') document.forms['search_engine'].keywords.value='Search Keywords';" />
<input class="searchbutton" name="submit" type="submit" value="Search" /></p>
</form>
</div>
<div id="logo">
<img src="images/Alfresco-logo.jpg" width="225" height="65" alt="Alfresco Logo" />
</div>
<ul id="menu">
<li><a class="current" href="#">Home</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">Products</a></li>
<li><a href="#">Clients</a></li>
<li><a href="#">Services</a></li>
<li><a href="#">About Us</a></li>
<li><a href="#">Contact Us</a></li>
</ul>
Now we just need to reference the new component from the main index.ftl template – /WEB-INF/classes/alfresco/templates/index.ftl using the “region” tag. This is effectively a pointer to the component.
- Edit the /WEB-INF/classes/alfresco/templates/index.ftl once again and locate the section of the page where the header HTML was previously removed.
- Insert the region tag pointing to the header component as follows:
<@region id="header" scope="global" />
- Save and close the file.
Once you have created and saved the two web script files and referenced the component from the template, we can refresh the web script environment to register the new script and test it. To refresh the web script environment access http://localhost:8080/surf/service/. You will see a button at the bottom of the page named “Refresh Web Scripts”. Select this and the new script will be registered. You can then test the Web Script by accessing it via the URL as listed within the descriptor file i.e. http://localhost:8080/surf/service/header. You should see the page header HTML:
Now when you hit the index home page once again, you should see your full page with the header global component being included – http://localhost:8080/surf/page?p=index
Page Scope Components
Once the header component is in place and running fine, we can move on to create some more components, this time using the page scope i.e. components that will change with each page. In my example, im going to build 2 for the home page named main and sidebar. I have highlighted where these components will be used within the index page:
The process is the same as we have just completed i.e.:
- Create the two component files within /WEB-INF/classes/alfresco/site-data/components
- Name them accordingly using scope.regionId.sourceId.xml
- Create two new web scripts to return the required HTML for each area of the page
- Chop the HTML out of the index.ftl page and paste into each of the corresponding web script FTL files
- Edit the index.ftl and put in the placeholders to reference the components
- Register the new web scripts
- clear the SURF cache
I will detail another example (as listed above), this time for the main content part of my page (the left hand body text).
- Create a new component within /WEB-INF/classes/alfresco/site-data/components named page.main.index.xml.
- Configure the component for page scope, mapping to a region of choice e.g. “main” and mapping the source id to the index page. Finally map the url to the web script with a suitable URL e.g. /index/main
/WEB-INF/classes/alfresco/site-data/components/page.main.index.xml
<?xml version='1.0' encoding='UTF-8'?>
<component>
<scope>page</scope>
<region-id>main</region-id>
<source-id>index</source-id>
<url>/index/main</url>
</component>
- Create the new web script within /WEB-INF/classes/alfresco/site-webscripts. First the descriptor file which I will call index.main.get.desc.xml. Make sure the url mapping matches that set in the component file above i.e. /index/main.
/WEB-INF/classes/alfresco/site-webscripts/index.main.get.desc.xml
<webscript>
<shortname>index</shortname>
<description>Returns the main page content for the index page.</description>
<url>/index/main</url>
</webscript>
- Next the Freemarker part of the web script. I will name it index.main.get.html.ftl. Chop out the HTML for the appropriate region from your index.ftl file and paste it into this file. Save again to /WEB-INF/classes/alfresco/site-webscripts.
- Edit the /WEB-INF/classes/alfresco/templates/index.ftl file again and put in the region placeholder where the component will be bound to. The id must match that set within the component definition.
<@region id="main" scope="page" />
- Register the new web script by hitting http://localhost:8080/surf/service/ and select “Refresh Web Scripts”
- Clear the SURF cache by visiting http://localhost:8080/surf/control/cache/invalidate
- Refresh your home page to see the component included – http://localhost:8080/surf/page?p=index
Summary
Once the above is completed you will have successfully setup a SURF page, created several components and bound them to the page in the appropriate areas. You will also have created some basic web scripts to return the HTML components. Go ahead and create any other required page components for your home page, and remember to use the appropriate scope e.g. global for a footer etc.
Its now probably a good time re visit the Wiki documentation on SURF as this will give you a good understanding of what other features and capabilities are available. What we have completed here has barely scratched the surface, however should be useful for moving forward. Next I will look at adding new pages and navigation.
Download
The finished web application is available for download here – SURF Part 1 – Getting Started.
See also – SURF Part 2 – Pages and Navigation and SURF Part 3 – Alfresco WCM Content






{ 3 trackbacks }
{ 7 comments }
Thanks for this easy to follow guide!
two corrections though:
1. when creating the header.get.desc.xml rather than duplicating the header component code, instead it should be:
Header Webscript
serves the header content
/header
2. when snipping the header component from the index.ftl it’s not enough to just cut it out and put in the webscript, you also have to include a special freemarker command to include the region. So where you’ve cut the header from the index.ftl you should now include:
Cheers,
Carsten.
argh, xml tags have been stripped from previous post!
Well spotted Carsten! I have amended the article accordingly for both points.
Regards, Ben.
This is a very useful piece of tutorial; it is also very clear.
Just about a detail (if you want perfection…) there are some links pointing to an /alfwf app, such as:
_ Link text is – http://localhost:8080/surf/page?p=index
_ While the same link points to – http://localhost:8080/alfwf/page?p=index
Thanks again for this!
Hi Paolo,
Good spot and thanks for letting me know. I updated the article to use the new SURF WAR but missed those! All fixed.
Regards, Ben.
Hi…Thanks for sharing such nice tutorial.
I am using community edition 3.3g.. It uses spring surf. Do you have any tutorial regarding spring surf similar to this, so that I can convert a normal website which has only html pages to spring surf site, so that I can include webscripts in it.
Regards,
qwr
Here you had mentioned to start from spring war. Can you kindly mention how to start for spring surf?
Is there any such war for spring surf or any starter to build from spring surf source…
Comments on this entry are closed.