Server Side Framework (Surf) - Alfresco Content Services - 23.4 - 23.4 - Ready - Alfresco - external

Alfresco Content Services

Platform
Alfresco
Product
Alfresco Content Services
Release
23.4
License

The layout of a Share page is defined with the Surf development framework, which is a server side framework (For more information on Surf deep dive, see Surf Framework). This means that the involved files are processed on the server side (compared to Browser processing of JavaScript files). Surf is based on the Model View Controller (MVC) pattern where the controller(s) is mostly implemented in server side JavaScript (The Rhino JavaScript engine is included on the server side). The template is written in FreeMarker, and the model is a hash map that is set up in the controller(s) and available in the template.

Each page template defines one or more regions for things like header, footer, body, navigation, see the following picture:

Examples of page templates with header, footer and, body elements

To be able to reuse regions we can scope them to page, template, or global usage:

Examples of page templates components layouts in Global scope, Template Scopre, and Page Scope

Each region is implemented as a reusable component. A component implementation is done with a Surf web script, which is the same thing as the REST-based request and response model, the predominant Web Service design model. The component web scripts will typically return HTML fragments that make up different parts of the page:

Example of the way the component web script returns HTML fragments that make up different parts of the page

With all these different objects we might expect there to be some form of model that makes up the whole Surf UI development framework. It looks like this:

The Surf UI development framework with Page, Template instance, template and three different regions, each with a component and web script

The model is referred to as the siteData and has more stuff than just pages and templates (for more information, see Surf Framework). You will however mostly be dealing with component, page, and template-instance files, which are simple XML files:

/WEB-INF/classes/alfresco
  /site-data
    /chrome
    /components
      ...
      global.header.xml
    /component-types
    /configurations
    /content-associations
    /extensions
    /page-associations
    /pages
      ...
      documentlibrary.xml
      ...
      search.xml
      ...
      task-details.xml
      ...
    /page-types
    /template-instances
        1-column.xml
        2-columns.xml
        3-columns.xml
        ...
        content-viewer.xml
        ...
        search.xml
        ...
    /template-types
    /themes

The Site Data model defines the page in XML, like in the following example for Search (alfresco/tomcat/webapps/share/WEB-INF/classes/alfresco/site-data/pages/search.xml):

<?xml version='1.0' encoding='UTF-8'?>
<page>
   <title>Search</title>
   <title-id>page.search.title</title-id>
   <description>Search view</description>
   <description-id>page.search.description</description-id>
   <template-instance>search</template-instance>
   <authentication>user</authentication>
   <components>

      <!-- Title -->
      <component>
         <region-id>title</region-id>
         <url>/components/title/search-title</url>
      </component>

      <!-- Search -->
      <component>
         <region-id>search</region-id>
         <url>/components/search/search</url>
      </component>

   </components>
</page>

Here we can see that some components have been defined inline in the search page definition, instead of in the /components directory as separate files. The name of the page definition file is implicitly setting the page id to search. A corresponding template instance file is expected to be present in the template-instances directory. In our case it will be a file called search.xml:

<?xml version='1.0' encoding='UTF-8'?>
<template-instance>
   <template-type>org/alfresco/search</template-type>
</template-instance>

It will have a link to the physical template that contains the layout of the page. The template files are located under a different directory called /templates, which is on the same level as the site-data directory:

/WEB-INF/classes/alfresco
  /site-data
  /templates
    /org
      /alfresco
        1-column.ftl
        2-columns.ftl
        3-columns.ftl
        ...
        content-viewer.ftl
        ...
        search.ftl
        ...

The search.ftl template file looks like this with the regions etc:

<#include "include/alfresco-template.ftl" />
<@templateHeader />

<@templateBody>
   <@markup id="alf-hd">
   <div id="alf-hd">
      <@region scope="global" id="share-header" chromeless="true"/>
   </div>
   </@>
   <@markup id="bd">
   <div id="bd">
      <div class="yui-t1">
         <div id="yui-main">
            <@region id="search" scope="page" />
         </div>
      </div>
   </div>
   </@>
</@>

<@templateFooter>
   <@markup id="alf-ft">
   <div id="alf-ft">
      <@region id="footer" scope="global" />
   </div>
   </@>
</@>

The search page reuses the global header and footer components and then defines a page specific region called search. The web script to call for the search component is already defined in the page definition XML above (that is, /components/search/search). The controller file for the search Web Script looks like this (alfresco/tomcat/webapps/share/WEB-INF/classes/alfresco/site-webscripts/components/search/search.get.js):

/**
 * Search component GET method
 */

function main()
{
   // fetch the request params required by the search component template
   var siteId = (page.url.templateArgs["site"] != null) ? page.url.templateArgs["site"] : "";
   var siteTitle = null;
   if (siteId.length != 0)
   {
      // Call the repository for the site profile
      var json = remote.call("/api/sites/" + siteId);
...

This is server side JavaScript code that sets up a model with data for the template. The template looks like this (alfresco/tomcat/webapps/share/WEB-INF/classes/alfresco/site-webscripts/components/search/search.get.html.ftl):

<@markup id="css" >
   <#-- CSS Dependencies -->
   <@link href="${url.context}/res/components/search/search.css" group="search"/>
</@>

<@markup id="js">
   <#-- JavaScript Dependencies -->
   <@script src="${url.context}/res/components/search/search-lib.js" group="search"/>
   <@script src="${url.context}/res/components/search/search.js" group="search"/>
</@>

<@markup id="widgets">
   <@createWidgets group="search"/>
</@>

<@markup id="html">
   <@uniqueIdDiv>
      <#assign el=args.htmlid>
      <#assign searchconfig=config.scoped['Search']['search']>
      <div id="${el}-body" class="search">
         <#assign context=searchconfig.getChildValue('repository-search')!"context">
         <#if searchQuery?length == 0 && context != "always">
         <div class="search-sites">
...

The template is where we will find references to client side code/resources. The css and js sections above points to the client side CSS and JS that should be part of the <head> section in the web page, and downloaded and executed by the browser to create the user interface. So as we are starting to talk about the client side, let’s dig into it a bit more in the next section.