Customizing Alfresco Share FreeMarker Templates

cancel
Showing results for 
Search instead for 
Did you mean: 

Customizing Alfresco Share FreeMarker Templates

Intermediate II
0 14 4,228

Introduction

In the previous two blog posts I've introduced extension module customizations for i18n properties files and JavaScript controllers. In this post I'm going to describe how Spring Surf has been updated to allow FreeMarker templates to be customized through the use of new extensibility directives. These features are currently available in the latest Alfresco Community source and will be available in Alfresco Enterprise 4.0.

Background

Customizing the i18n properties file was relatively trivial to implement because ultimately it becomes a map and is easy to update. Giving additional JavaScript controllers the opportunity to update the template model was also reasonably straightforward. However.... providing the opportunity for extension files to update a FreeMarker template was a much more significant challenge. Essentially the objective was to provide a way of dynamically editing HTML through configuration.

The approach we have taken to solve this problem may not solve all your customization problems immediately, but what we have implemented should pave the way for future extensibility enhancements in later releases. We've also provided an extensibility platform to provide your own solutions to the problem.

Essentially we have changed Spring Surf so that instead of writing templates directly to the output stream we write to an in-memory model and then allow extensions to manipulate that model before it gets flushed to the output stream. The mechanism for updating the model is through the use of new and updated FreeMarker template directives.

Wherever an extensibility directive is used in a bass template it can be manipulated by a corresponding extension file. There are a initial set of actions:

  • remove (completely remove the directive contents from the model)
  • replace (replace the directive contents in the model with something new)
  • before (place a new directive immediately before the target directive contents)
  • after (place new directive contents immediate after the target directive contents)
  • In the current code we provide 2 directives that support extensibility: <@region> and <@markup>.

The <@region> directive was used extensively in previous versions of Alfresco Share to define the Regions into which Component are bound - we have simply updated its implementation to work with the extensibility model. The <@markup> directive model is entirely new and is simply used to demarcate sections of HTML in a template.

In the first 2 blog posts I described how you can add and remove content from Alfresco Share by modifying the Components (and their Sub-Components). This approach relies on their being a Component available that will be bound to a Region in the template.

Using this alternative mechanism makes it possible to add new regions and also completely remove regions to prevent Components from being bound. This is significantly different because it is a much more volatile approach but could be useful in certain circumstances. If you want to remove some content and prevent another module from either restoring or adding to it, you can remove it entirely from the model so that it cannot be changed.

Tutorial

In an earlier blog I demonstrated how to add a new content to an Alfresco Share page by providing an additional Sub-Component as an extension. This could have also been done using a <@region> directive extension.

Enable Surf Bug and click on the Title bar.  Note the 'Template Type' property which in this case is 'org/alfresco/dashboard'. This means that the file we need to extend is 'dashboard.ftl' in the 'org.alfresco' package.

Screen shot showing Surf Bug data for Title on User Dashboard

Update the 'blog-demo.xml' file to add the following module definition:

<module>
    <id>Blog Module (Add Region)</id>
    <customizations>
        <customization>
            <targetPackageRoot>org.alfresco</targetPackageRoot>
            <sourcePackageRoot>blog.demo.customization</sourcePackageRoot>
        </customization>
    </customizations>
</module>

Create a file called 'dashboard.ftl' and place it in the package 'alfresco.templates.blog.demo.customization'. The file should contain the following:

<@region id='additional-content' target='title' action='before' scope='global' />

Note that the package we've added the file to is prefixed by 'alfresco.templates'. This is the source package at which Spring Surf Class Loader starts looking for template files. It is vitally important to include this prefix to your package or your extension won't be found.

We need to create a new Component to bind to our new Region. We're going to be lazy and use the legacy configuration style (although we could use the new style, this way is shorter and suitable for our purposes).

Create a file called 'global.additional-content.xml' and place it in the 'alfresco.site-data.components' package. The file should contain the following:

<component>
    <region-id>additional-content</region-id>
    <source-id>global</source-id>
    <scope>global</scope>
    <uri>/blog/demo/new-content</uri>
</component>

Note that we're re-using the same Web Script created in the first extensibility blog. If you haven't completed the tutorial in that blog then the Component won't find the Web Script specified by the <uri> element.

Re-build and re-deploy the JAR and restart the Alfresco Share web server and you should see the additional content.

Screen shot showing customized user dashboard

If you want to test out the other customization operations then you can update the 'dashboard.ftl' as follows:

To place the new content AFTER the title bar:

<@region id='additional-content' target='title' action='after' scope='global' />

To REPLACE the content of the title bar with the new content:

<@region id='additional-content' target='title' action='replace' scope='global' />

To REMOVE the title bar region completely:

<@region id='additional-content' target='title' action='remove' />

<@markup> Directives

At the time of writing there are currently no uses of the <@markup> directive anywhere in Alfresco Share. It's hoped that the Alfresco Community will make suggestions/contributions as to where they would best be added. They can wrap any section of FreeMarker source in either a WebScript or TemplateInstance to identify sections of the template that can be customized. If you have any suggestions then please post to the Community forums.

14 Comments
Active Member
How do you re-use an existing alfresco component on another page? I'm trying to add web-preview to the edit-metadata page as a customization.
The edit-metadata.ftl picks up my override, where I have


The output is only .
This of course means that the component is not properly declared. I've tried to do this in multiple ways, none has worked.
My extension config

  
     
         Edit Metadata with Preview
         Adding document preview to edit metadata page
        
           
               org.alfresco
               se.loftux
           
        
        
           
               metadata-web-preview
               edit-metadata
               template
              
                 
                     /components/preview/web-preview
                    
                        {nodeRef}
                    
                 
              
           
        
     
  
Active Member
The xml in my previous comment didn't get posted... Hope the question makes sense anyway. I have both a customizations/customization followed by a components/component section were the component is defined with region-id/metadata-web-preview
Intermediate II
The 'web-preview' Component will always be bound to the Document-Details page because that is where it is defined - that is an unfortunately limitation of the original Spring Surf implementation that we can't do anything about at this stage. However, you should be able to re-use the WebScript that renders the 'web-preview' content in a custom page.

To do this you will need to either append or insert a new  into the edit-metadata page (by targeting an existing region and using the 'before' or 'after' action) and create a new Component definition to bind to that region. Or alternatively, you can define a new Sub-Component of an existing Component on the edit-metadata page.

In order to re-use the WebScript you'll need to ensure that you're setting a  property in your new Component definition.

That all said, it looks like you might be trying to do that already (difficult to tell because as you said your XML hasn't been posted correctly)... it might be better to move this question to the Surf Development forum http://forums.alfresco.com/en/viewforum.php?f=50 where we will be able to post XML and it might be easier to resolve the issue?
Active Member
Intermediate II
I've posted an answer to the question on that thread - hopefully it solves the problem.
Active Member
Hi David,

there appears to be an error in your global.additional-content.xml file, it should look something like this:


    additional-content
    user/admin/dashboard
    global
    /blog/demo/new-content


best regards
Intermediate II
@banana No, because when defining a new component (to add as a Sub-Component this way) it MUST be global.
Active Member
Hello Dave,

Could you please point me to the right direction? I'm trying to figure out how to customize web script template (*.html.ftl). Thanks to your previous posts it is clear how to extend web script i18n properties and js controller. This post shows how to customize @regions in freemarker templates. Is there any option to customize, say, dashlet freemarker template? I see now the only option - override web script completely placing my version into web-extension/site-webscripts but I do not like this idea.

Best regards,
Alex
Intermediate II
Hi Alex,

Customizing the WebScript templates can be done via  directives in exactly the same way as the  directives in TemplateInstance FreeMarker templates - the catch is that we currently have not added very many  directives into the Share source code yet.

The reason for this is that we want to move the Share WebScript implementations to a new common 'boiler-plate' style where all the dependencies are handled in the *.head.ftl file, the client-side widget instantiation is handled from the JS controller and the all of the code in the *.html.ftl file is grouped into a common sensible arrangement. These changes have started to be made to the latest Community source already.

We have used  directives in some WebScripts - but not in very many. At the moment your only option for customizing the WebScript templates is to use the 'web-extension' approach or to wrap the sections you want to control in your own  tags (this would be an easier approach for dealing with future updates).

Regards,
Dave
Active Member
Thanks Dave. Got it. And special thanks for sharing the roadmap. I am really waiting for this - 'client-side widget instantiation is handled from the JS controller' - to happen. It would be great! :-)
Active Member
Where's Hello World! defined? Could you post the complete packaged code?
Intermediate II
@Rich... in a previous blog, see the section:  'Note that we’re re-using the same Web Script created in the first extensibility blog. If you haven’t completed the tutorial in that blog then the Component won’t find the Web Script specified by the  element.'
Active Member
Balls - this doesn't work in 4.2e does it?
Intermediate II
@Rich... well the concept still works, but the specific example doesn't because we've changed the header since the blog was written.