Dynamic Share Configuration Through Extension Modules

May 1st, 2012 by ddraper

Whilst preparing for a Tech Talk Live presentation on customizing Alfresco Share I realised that there was another extensibility capability that I’d not actually blogged about. This was a feature that had been requested internally and will be available in Alfresco Enterprise 4.0.2 and is in the latest Community source code on SVN.

Introduction

If you look in the “webapps/share/WEB-INF/classes/alfresco” folder of your web server hosting Alfresco then you’ll notice a number of files ending “-config.xml” (i.e. “share-config.xml”). These files contain configuration that is loaded into a Spring bean when Alfresco starts and is accessed by Share code to dictate many different aspects of its behaviour. This is purely static configuration in and changes to the files will only be applied with a server restart.

However, it is now possible to dynamically control the configuration through the use of extension modules. A module configuration file now supports the element which allows you to change the configuration without restarting the server by deploying and un-deploying modules or to use evaluations to determine when specific configuration is applied.

Example

Let’s say that you want to use the Flash uploader for some sites and the new HTML5 uploader for others. The Flash uploader is normally only used when Flash is enabled in the “share-document-library-config.xml” file.

However if we create a module with the following configuration:

<module>
  <id>Site Conditional Flash Upload</id>
  <description>Applies config based on site id</description>
  <evaluator type="site.module.evaluator">
    <params>
      <sites>noflash</sites>
      <sitePresets>.*</sitePresets>
    </params>
  </evaluator>
  <configurations>
    <config evaluator="string-compare" condition="DocumentLibrary" replace="true">
      <file-upload>
        <adobe-flash-enabled>false</adobe-flash-enabled>
        <in-memory-limit>262144000</in-memory-limit>
      </file-upload>
    </config>
  </configurations>
</module>

We are able to disable the Flash uploader for the site called “noflash”. The evaluator used is available “out-of-the-box” and for further information on evaluators read this post. The element contains a copy of the configuration taken from the “share-document-library-config.xml” file but changes the default <adobe-flash-enabled> value from “true” to “false”.

The configuration is only applied when the module is deployed and the user is uploading to the Document Library in the “noflash” site (see the screenshots below).

Important Notes

It is important to note that the same rules of statically extending configuration apply here, i.e. that it is not an augment capability. Note that I have had to include the  <in-memory-limit> element even though it has not changed, because otherwise it will be lost. If the “replace” attribute were removed from the <config> element then the result would be that two <adobe-flash-enabled> elements will be included in the configuration and it would almost be guaranteed that only the original statically defined element would be used.

CSS Data Image Support In Spring Surf

March 7th, 2012 by ddraper

Please note: The following features are at the time of writing only available in the Alfresco Community source code and have not as yet been included in any release.

Introduction

In my last blog post I described the new DependencyHandler service that has recently been added to Spring Surf.  Whilst its primary purpose was for appending checksums to JavaScript and CSS dependency requests to ensure that browsers did not use stale cached data, it does have another interesting capability.

Enhancing the performance of web applications has become increasingly important as they have become increasingly complex.  Ultimately most of these performance enhancements boil down to either reducing the amount of data required to render a page or reducing the number of HTTP requests required to download that data. Minifying text based resources and the use of GZIP on the server are obvious examples of reducing bandwidth and whilst minifying images is not an option (beyond regular compression) you can reduce the number of HTTP requests required by using CSS sprites. This is all well and good if you’re starting a new web application from scratch and can make sure you adhere to all the best practices from the start,  it’s not always so easy to retroactively apply these practices to a mature project. Share makes good use of minification but there is scope to improve the number of HTTP requests made for resources.

One of the big advantages of Share is how easily it can be customized to satisfy requirements but the disadvantage of this is that we need to do our best to support all the various customizations that exist out in the wild. So whilst we could spend a long time converting all of our individual images into one giant CSS sprite there’s a very good chance we could break a lot of other developers customizations.

Another option that is available is to actually embed the data for each image into the CSS files that reference them. Whilst not wanting to get involved in the various discussions about which approach is better, based on our requirements it is clearly the better choice for us.

Configuration and Example

Surf can now automatically produce CSS data images by simply adding the following line to its configuration file (for Share this is located at this can be found in the “webapps/share/WEB-INF/surf.xml”)

<web-framework>
   …
   <generate-css-data-images>true</generate-css-data-images>
   …
</web-framework>

Providing that checksum dependencies are also enabled then all images reference by CSS files will be embedded as data within those CSS files. This offers the additional benefit on top of the reduced number of HTTP requests in that when an image changes, the CSS checksum will change so it will not be possible for the browser to use stale cached images. The DependencyHandler service defers some of this work to a dedicated “CssDataImage” service which can be overridden in the Spring application context if required. All images are cached for the life-cycle of the server so that performance is not impeded when requesting the same image multiple times.

The following screenshot shows the Chrome Web Developer Tools displaying some images from a page in Share with CSS data images enabled:

Limitations

The main limitation as far as Share is concerned is that we haven’t used CSS styles throughout the application. There are still some uses of the <img> HTML element which won’t be affected. However, at the end of the day there will be less HTTP requests than before so hopefully performance should be improved as a result. The only other limitation is that CSS data images are not supported by Internet Explorer versions 6 and 7 (but then if you’re still using either of those then this will be the least of your performance concerns!)  so Surf will check the user-agent data though and not perform the conversion for those browsers.

More Reading

More information on CSS Data Images can be found here:

Checksum Dependencies In Surf

March 6th, 2012 by ddraper

Please note: The following features are at the time of writing only available in the Alfresco Community source code and have not as yet been included in any release.

Introduction

One of the problems that has affected upgrades of Alfresco in the past is that the end-users’ browsers may end up using cached copies of JavaScript and  CSS files that have been updated during the upgrade.  Spring Surf now has introduced a new service called the “DependencyHandler” which has been created to solve this specific problem. It does this by appending a unique checksum to the end of each requested JavaScript and CSS dependency where the checksum is generated from the contents of the file and so will change whenever the file content is changed. The checksum associated with the file is cached for the lifecycle of the web server meaning that it does not need to be generated for each request and Surf performance has actually been enhanced by this mechanism because Surf also caches the location from which the dependency is retrieved (Surf can retrieve dependencies from a number of different locations, e.g. JAR files, class path, file system, remote location, etc.).

Configuration and Usage

By default the capability is disabled in Surf but will be enabled in future releases of Alfresco Share. To enable is you simply need to set the following line within the Surf configuration (this can be found in the “webapps/share/WEB-INF/surf.xml”):

<web-framework>
   …
   <use-checksum-dependencies>true</use-checksum-dependencies>
   …
</web-framework>

In order to make use of the dependency handler you will need to use the <@script>, <@link> and <@checksumResource> FreeMarker directives in your TemplateInstance and WebScript files, e.g.

<@script src="${url.context}/res/yui/yahoo/yahoo.js"></@script>
<@link rel="stylesheet" type="text/css" href="${url.context}/res/css/base.css" />
<@checksumResource src="${url.context}/res/css/ipad.css"/>

(Those examples are taken from the resources.get.html.ftl file where the ${url.context} is a FreeMarker variable set to the application context, e.g. “share”)

  • <@script> generates JavaScript script import declarations.
  • <@link> rolls up multiple CSS requests into a style declaration using a separate “@import” statement for each usage of <@link>.
  • <@checksumResource> generates just the URL (i.e. without being specific to CSS or JavaScript and can therefore be used with images or even WebScript requests. One additional feature of the <@checksumResource> directive is that you can specify the attribute “parameter” which makes the checksum appear as the value of a request parameter of the supplied name (rather than as part of the file name itself).

Debug and Production Suffices

The DependencyHandler is capable of dealing with production (minified) and debug versions of files and actually handles these better than before. The Spring application context configuration for the bean allows you to specify the different file suffices that can be used for both production and debug versions and the DependencyHandler will work through the options until it finds a file. This means that Surf will always be able to fall back to the debug version of the code if a minified version does not exist. By default the debug suffices are:

  • “” (i.e. no suffix)
  • “_src”
  • “-debug”

And the production suffices are:

  • “-min”
  • “-minified”
  • “” (i.e. no suffix)

You can change these suffices by overriding the definition for the “dependency.handler” bean in the Spring application context in case you want to add, remove or re-order the default entries.

Example

The following screenshot shows the Chrome Developer Tools window displaying the JavaScript resources loaded for a page in Alfresco Share – note that each request is appended with a checksum value (apart from “tiny_mce.js” which handles its dynamic dependencies in its own way).

Current Limitations

The current limitation of this solution is that it only works with static requests from the page and not dynamic requests made from a script – although many JavaScript libraries provide their own solution to this problem (e.g. TinyMCE) however it is something to be aware of.

Share Configuration Extensibility

March 5th, 2012 by ddraper

Please Note: This post refers to recent updates to the Alfresco Community source – at the time of writing these features are NOT available in any current Alfresco release.

We’ve added a new capability to the Spring Surf extensibility that allows modules to make dynamic changes to the configuration service. Alfresco Share uses the Surf configuration service extensively to control its behaviour (check out all the files ending with the suffix “-config.xml” in the “share\WEB-INF\classes\alfresco” directory) and up until now it’s only been possible to make changes to this configuration between server restarts.

To make a module provide additional (or replace existing) configuration you simply need to add the configuration within a element in the module configuration (apologies for the overuse of the word “configuration” there!)

Here is an example module that replaces the Document Library configuration for Flash enablement for any site with the name “noflash” (the section in green is the important part – I’m using the “out-of-the-box” site evaluator to only apply the module to sites with the name “noflash” that were created using any preset).

<extension>
   <modules>
      <module>
         <id>Site_Conditional_Flash</id>
         <description>Applies config based on site id</description>
         <evaluator type="site.module.evaluator">
            <params>
               <sites>noflash</sites>
               <sitePresets>.*</sitePresets>
            </params>
         </evaluator>
         <configurations>
            <config evaluator="string-compare" condition="DocumentLibrary" replace="true">
               <file-upload>
                  <adobe-flash-enabled>false</adobe-flash-enabled>
                  <in-memory-limit>262144000</in-memory-limit>
               </file-upload>
            </config>
         </configurations>
      </module>
   </modules>
</extension>

The original configuration can be found in the file “share\WEB-INF\classes\alfresco\share-documentlibrary-config.xml”. The screenshots show the resulting upload dialog when accessed from the Document Library of two different sites, one called “flash” and the other “noflash” (after the module has been applied obviously – see earlier posts for more information on this)

Screenshot of Share showing the Flash uploader

Screenshot of Share showing the HTML5 uploader

It’s important to note that when replacing configuration that you will need to preserve any existing configuration from the original that you don’t want to lose. For example – I’ve kept the “in-memory-limit” setting because it would otherwise have been lost as part of the replace.

Equally if you don’t specify the “replace” attribute as true then you will only ADD additional configuration and it’s possible that your configuration change won’t be observed because it won’t be the first element found – i.e. if the “replace” attribute was not set in my configuration the end result would be:

<adobe-flash-enabled>true</adobe-flash-enabled>
<in-memory-limit>262144000</in-memory-limit>
<adobe-flash-enabled>false</adobe-flash-enabled>
<in-memory-limit>262144000</in-memory-limit>

…and when the Document Library code accessed the configuration it would detect Flash as enabled as the first “adobe-flash-enabled” element would be returned. If you’re familiar with overriding configuration in Alfresco through static files on the “web-extension” path then this shouldn’t be a surprise to you as the behaviour is identical – however, I thought it would be worth mentioning in case it takes people by surprise.

Extensibilty Updates Roundup

January 4th, 2012 by ddraper

There have been a few recent updates to the Spring Surf extensibility capabilities that I’ve been blogging about recently which I thought I should capture in a quick roundup post. These features are currently available in the Alfresco Community 4.0c release.

Improved i18n localization handling

In the original implementation of the i18n extension handling there was a limitation in that it was necessary to explicitly provide extensions for the locale that was ultimately resolved. This meant that there was no graceful degradation of the specificity of locale (e.g. from “en_GB” to “en” to the default properties file).  This has now been updated so that all matching locale files will be merged from least to most specific so that extensions can provide a combination of i18n properties files which will all be merged into the WebScript properties file with the most specific overrides “winning”. This update should make it simpler to provide i18n overrides – especially where different region locales might be used to access the same application (e.g. “en_US” and “en_GB”).

Module Auto-Deploy Changes

Originally there were only two ways for modules to be deployed – they would either ALL be deployed automatically or would each need to be manually deployed.  It is now possible for a module to request to be automatically deployed if allowed which broadens the choices to the following:

  1. Automatically deploy all modules
  2. Only automatically deploy any modules that request it
  3. All modules must be manually deployed.

This new option was introduced to support the forthcoming Records Management module and the Cloud offering. By default Alfresco remains configured in manual mode but the Records Management module will be able to override the default configuration to switch to “enable-auto-deploy” mode when it is AMPed in.

To configure Spring Surf applications to support this new option you should use the following configuration:

<module-deployment>
   <mode>manual</mode>
   <enable-auto-deploy-modules>true</enable-auto-deploy-modules>
</module-deployment>

…and to make a module request to be automatically deployed, add the following as a child element of <module>

<auto-deploy>true</auto-deploy>

Additional Dependencies

One feature that was already available in the earlier 4.0 Community releases (but not blogged about) was the ability for modules to request for additional JavaScript and CSS dependency resources to be imported.  This works in the same way as i18n, JavaScript controller and FreeMarker template customizations in that you need to specify a target package and if a WebScript declared at that package is executed on a request then those additional dependencies will be added as imports to the <head> element of the page.

A module that adds additional dependencies might look like this:

<module>
   <id>Add dependencies</id>
   <customizations>
      <customization>
         <targetPackageRoot>org.acme</targetPackageRoot>
         <dependencies>
            <css>/res/demo/dependencies/styles.css</css>
            <js>/res/demo/dependencies/script.js</js>
         </dependencies>
      </customization>
   </customizations>
</module>

An additional dependency can only be added once even if multiple modules are evaluated successfully that request them.

Updated Module Deployment Error Handling

There have been some updates to the module deployment code to provide better handling of errors. One of the main problems that can occur when deploying modules is that different authentication mechanisms are used for WebScript and remote client calls. This means you might be authenticated to access a WebScript (i.e. the module deployment script) but not to actually persist changes to the remote client.  The end result would be that modules might be deployed in application memory but would not survive a server restart. There are now clearer error messages to indicate that this is happening and could occur for other reasons (for example the Alfresco repository might be down).  You might also notices that on server start-up some errors are shown in the log indicating that module settings could not be saved – this isn’t actually a problem (previously persisted changes won’t be effected), but is something that we’re hoping to resolve. In the meantime it should at least be clearer when using the deployment WebScript when problems have occurred.

Advanced Share Customization (part 2)

November 3rd, 2011 by ddraper

Introduction

In the first part of this tutorial I demonstrated how you could add a new page to Alfresco Share and have it protected by the standard authentication mechanism. I also showed how you can make that new page the landing page for your Alfresco Share by setting it as the root page in the Spring Surf configuration. In the second part of this tutorial I’ll show how you can override the default Alfresco Share login page and in next blog I’ll show how to customize and re-use existing pages and access core services.

Step 3: Override the default login page

Spring Surf applications can define a login page by configuring the “login” page-type mapping to reference a specific Page object. In Alfresco Share this definition can be found in the “surf.xml” configuration file which sets the “login” page-type to map to the “slingshot-login” page.

To override this definition you should create a (or add to your existing) “share-config-custom.xml” file and place it in “alfresco/web-extension” folder (this location can quite happily exist within the JAR file that we started creating in the first part of this tutorial). Add the following content to this file:

<alfresco-config>
   <config evaluator="string-compare" condition="WebFramework">
      <web-framework>
         <defaults>
            <page-type>
               <id>login</id>
               <page-instance-id>blog-login</page-instance-id>
            </page-type>
         </defaults>
      </web-framework>
   </config>
</alfresco-config>

We now need to create the Page that we have referenced, create a file called: “blog-login.xml” and place it in the “alfresco/site-data/pages” path of your JAR. It should contain the following:

<page>
   <template-instance>blog-login</template-instance>
   <authentication>none</authentication>
</page>

Notice that the Page definition sets and <authentication> value of “none” – this is important to ensure that Surf doesn’t attempt to redirect to the login page when the user isn’t authenticated causing an infinite loop!

Next create a file called “blog-login.xml” and place it in the “alfresco/site-data/template-instances” path of your JAR. It should contain the following:

<template-instance>
   <template-type>blog/demo/blog-login</template-type>
</template-instance>

This file creates the mapping between the Template-Instance object and the FreeMarker template that will actually render your new login page. Create this file “blog-login.ftl” and place it in the “alfresco/templates/blog/demo” path of your JAR. This file should contain the following code to render a login page (NOTE: this is a completely stripped down login page – I’m not trying to win any awards for style, accessibility or i18n here… I’m just demonstrating the bare minimum required!!):

<html>
   <head>
      <title>Blog Application Login</title>
   </head>
   <body>
      <form id="loginform" accept-charset="UTF-8" method="post" action="${url.context}/page/dologin">
         Username: <input type="text" id="username" name="username"/><br>
         Password: <input type="password" id="password" name="password"/><br>
         <input type="submit" id="btn-login" />
      </form>
   </body>
</html>

Build your JAR, re-deploy it to “<tomcat-home>/webapps/share/WEB-INF/lib” and restart Tomcat (assuming you’re using Tomcat – adjust as necessary for your web server). Now when you attempt to view “http://localhost:8080/share” (adjust port as necessary) you will be prompted with your new login page and following successful authentication will be taken to your new landing page.

Spring Surf Presets

November 1st, 2011 by ddraper

Introduction

I started writing this post back in November last year but never got around to finishing it. Since some questions came up about presets at DevCon in San Diego last week I thought it would make sense to revisit and finally post it. It really just provides some general information on how to configure an alternative Presets Manager for Alfresco Share and provides an example of how to switch from using the repository to the local file system for preset generated Spring Surf objects. In the future I’ll try and post some more information specifically relating to customizing the Alfresco Share default presets, but hopefully this will be useful in the meantime!

Background

When any user logs into Share they are presented with their own unique dashboard that is a Spring Surf Page object in its own right. This page does not exist until the user logs in for the first time at which point it generated and persisted in the repository for re-use. This generation is achieved through the use of “presets”.

Presets are essentially parameterised abstract Spring Surf artefact definitions (Pages, Template-Instances, Components, etc) defined in XML files that can a token map can be applied to generate concrete org.springframework.extensions.surf.ModelObject instances. They are managed and can be used through the org.springframework.extensions.surf.PresetsManager that is registered in the “spring-surf-presets-context.xml” Spring configuration file as the “webframework.presets.manager” bean. This configuration file is used to define what type of files contain Preset configuration and where to look for them.

By default Presets can be defined in “presets.xml” files located in either “classes/alfresco/site-data/presets” or “classes/alfresco/web-extension/site-data/presets” (the use of “alfresco” is a remnant of the morphing of Alfresco Surf to Spring Surf) but can be customized by overriding the Spring bean configuration (or by defining a new PresetsManager bean) in consuming applications.

If you do amend the search paths you must keep them somewhere under the “classes” directory as they are located using a ClassPathStore (alternatively you can configure an alternative store and search path in the application context to use to locate the files). A correctly authored preset file contains a single “presets” element containing zero or more “preset” elements where each preset element can contain definitions for zero or more “pages”, “template-instances” and “components”. A basic skeleton structure could look something like this:

<?xml version='1.0' encoding='UTF-8'?>
<presets>
   <preset id="MyPreset">
      <components>
         <component id="${componentId}">
            ...
         </component>
      </components>
      <template-instances>
         <template-instance id="${templateId}">
            ...
         </template-instance>
      </template-instances>
      <pages>
         <page id="${pageId}">
            ...
         </page>
      </pages>
   </preset>
</presets>

The Share application contains a single Presets configuration file (WEB-INF/classes/alfresco/site-data/presets/presets.xml) that define a variety of different presets (including the default user dashboard configuration).

Instantiating Presets

You simply need to obtain your PresetsManager bean and call its “constructPresets” method to make use of the configured Presets. This method requires the ID of the preset to use and a java.util.Map of key/value pair tokens to apply to the parameters. The method will search through the list of configured Preset XML files looking for a <preset> element with a matching “id” attribute. The token map is applied to a matching definition to substitute the configurable parameters with actual values. Each defined ModelObject is then instantiated and persisted using the ModelObjectService bean. The resulting Pages, Template-Instances and Components are then available for use as though they were statically configured Spring Surf artefacts.

An alternative method for constructing presets is to use the custom Spring Surf JSP tags “constructPreset” and “presetToken”. These can be used as follows:

<%@ taglib prefix="surf" uri="http://www.springframework.org/tags/surf" %>
<surf:constructPreset preset="myPreset">
   <surf:presetToken key="pageId" value="MyPage"/>
   <surf:presetToken key="templateId" value="MyTemplate"/>
   <surf:presetToken key="componentId" value="MyComponent"/>
</surf:constructPreset>

A preset is only used to provide an initial definition for objects that you wish to create multiple instances of. There is little point in having a preset if you only wish to use it once (you may as well just create a normal configuration file) and the parameterisation only allows simple String substitution rather than any kind of complex logical processing (that you could achieve with FreeMarker or JSP processing of model properties).

Since the purpose is to create multiple instances of the same combinations of ModelObject it is vitally important that page and template instance “id” attributes are parameterised and that any scoped component definitions have their “source-id” attribute configured to match the page or template instance id. For example, if you define a template and a page then their IDs should be parameterised and the pages “template-type” attribute should used the same parameter as the template instances  “template-type” attribute.

Alternative Presets Manager

If you wish to use presets as part of a Share customization or are writing a Spring Surf application that has access to an Alfresco installation then you can make use of the default application context and your constructed preset ModelObjects will be stored in the repository store. However, if you are using Spring Surf for an application independently of Alfresco you will need to override the default application context.

The following configuration should work as it is currently used in the Spring Surf FVT application (the purpose of each bean definition is explained in-line):

The PresetManager requires a ModelObjectService to create and save ModelObjects. The default PresetManager uses a MultiObjectService that does NOT include access to the local file system. A ModelObjectService requires a PersisterService which requires a Persister which requires a Store. It is the Store that controls where the preset constructed ModelObjects will ultimately be persisted and to write to the local file system we need to use the LocalFileSystemStore.

lt;bean id="presets.model.object.service">
   <property name="objectPersistenceService" ref="presets.object.persistence.service"/>
</bean>

<bean id="presets.object.persistence.service" >
   <property name="persisterService" ref="presets.persister.service"/>
</bean>

<bean id="presets.persister.service" >
   <property name="persister" ref="presets.persister"/>
   <property name="webFrameworkConfig" ref="webframework.config.element"/>
</bean>

<bean id="presets.persister" parent="webframework.sitedata.persister.abstract">
   <property name="store" ref="presets.store"/>
   <property name="pathPrefix"><value>/</value></property>
</bean>

<bean id="presets.store" >
   <property name="root"><value>./WEB-INF</value></property>
   <property name="path"><value>/presetConstructs</value></property>
</bean>

We need to override the default PresetsManager (or we could define a new bean) and ensure that it references our ModelObjectService.

<bean id="webframework.presets.manager" >
   <property name="modelObjectService" ref="presets.model.object.service"/>
   <property name="searchPath" ref="webframework.presets.searchpath"/>
   <property name="files">
      <list>
         <value>presets.xml</value>
      </list>
   </property>
</bean>

Finally we need to override the default Autowire service to add make our new persisters available (otherwise it won’t be able to use any of the ModelObjects created from the PresetsManager).

<bean id="webframework.service.autowire" parent="webframework.service.autowire.abstract">
   <property>
      <list>
         <ref bean="webframework.sitedata.persister.classpath"/>
         <ref bean="presets.persister"/>
      </list>
   </property>
</bean>

The end result of this configuration should be that when an object is instantiated from a preset it is stored on the local file system rather than in the Alfresco repository.

Advanced Share Customization (part 1)

November 1st, 2011 by ddraper

Introduction

At DevCon in San Diego last week I got asked a number of times how possible it would be to either radically customize Share or add entirely new pages to it. I got the opportunity to demonstrate how easy this was and realised that it would probably be a good basis for a blog. In this first post I’m going to demonstrate how to add a new page to Alfresco Share (and have it protected by the standard authentication mechanism) and make it the application landing page. In the next post I’ll show how to replace the default login form and provide a link to a stripped down document library page.

Step 1: Add a new page

Adding a new page into Alfresco Share is almost trivially easy and can be done in a minimum of three files:

  • A Page definition file
  • A Template-Instance definition file
  • A FreeMarker template file

Stripping these files to the bare minimum content these should look as follows:

Page definition (“home-page.xml”)
This file defines the page to name (which is the file name) and a mapping to a Template-Instance that contains the content. It also defines the level of authentication required to view the page.

<page>
   <template-instance>home-page</template-instance>
   <authentication>user</authentication>
</page>

Template-Instance definition (“home-page.xml”)
This creates a mapping to the actual FreeMarker template that contains the content for the page.

<template-instance>
   <template-type>blog/demo/home-page</template-type>
</template-instance>

FreeMarker template file (“home-page.ftl”)
This contains the actual page content. The example shown here is trivial but this could also contain regions for binding Spring Surf components if required.

<html>
   <head>
      <title>Blog Application</title>
   </head>
   <body>
      Welcome To Extreme Share Customization!
   </body>
</html>

The files should be build into a JAR file in the following structure:

  • /alfresco/site-data/pages/home-page.xml
  • /alfresco/site-data/template-instances/home-page.xml
  • /alfresco/templates/blog/demo/home-page.ftl

Assuming that you are running on Tomcat, copy the JAR file into either “webapps/share/WEB-INF/lib” or “<tomcat-home>/shared/lib“. If you’re not running on Tomcat then you’ll need to adjust the location to suit your specific web server. You’ll need to ensure that your Tomcat server is correctly configured if you wish to use “<tomcat-home>/shared/lib” and you should be aware that the page you have defined will be available to all Spring Surf based applications running on that web server!!

Restart your web server and then open the following location in your browser to “http://localhost:8080/share/page/home-page” (adjust the port as necessary for your application configuration!). You will be prompted with the standard Alfresco Share login screen – this is because the page has specified and authentication level of “user“. If you want only your administrator to access the page then you could set this as “admin” and if you want the page to be public you could set it to “none“.

The key thing to note here is that although your page effectively belongs to the Alfresco Share application your FreeMarker template can contain any HTML/JavaScript/CSS that you wish – you are not tied into using YUI2 as is used in the implementation of Alfresco Share – should you wish to write a page a page in JQuery or Dojo or pure HTML  (or anything else) then you are quite at liberty to do so.

Also, as well as being able to re-use the standard Alfresco Share authentication mechanism you are also able to easily access all the WebScripts available on both the Web and Repository tiers. This means you can easily build your own UI around existing Alfresco functionality.

When bringing in resources (such as images, JavaScript and CSS files) it’s important to remember that you should place them under the META-INF folder in your JAR and use the “/res” prefix on the requests to access them (e.g. request the file “META-INF/blog/demo/example.css” using the URL “/share/res/blog/demo/example.css“.

Step 2: Make the new page the landing page

PLEASE NOTE: Whilst writing this section I discovered a couple of bugs in the Spring Surf code which have been fixed. In order to make sure this example works you will either need to build the latest version of Alfresco or wait for version 4.0.c

Spring Surf supports the notion of a default page which is defined in the site configuration (that’s site as in “Web Site”, rather than the Alfresco Share concept of site!). By default the default page is the “site-index.jsp” which works out and redirects to the authenticated users dashboard page. A the site default page will be rendered when a request is mapped to the Spring MVC RequestDispatcher (which by default is at “/page” and “/p“) but no page is included in the request (e.g. “/share/page“). This is also configured as the “welcome-file” in the Share “web.xml” file.

In order to change the landing page for the application you can simply override the default site configuration for Alfresco Share. The site configuration used is defined in “surf.xml” and is set to “slingshot.site.configuration” by default.

To customize Alfresco Share to use our new landing page you simply need to create a file called “slingshot.site.configuration.xml” containing the following:

<configuration>
   <source-id>site</source-id>
   <properties>
      <root-page>home-page</root-page>
   </properties>
</configuration>

Build the file into your JAR at the following location:

  • /alfresco/web-extension/site-data/configurations

Note that the file needs to under the “web-extension” path in order that it gets resolved before the Alfresco Share default!

After deploying your JAR file and restarting the web server you should find that when you point your browser at “http://localhost:8080/share” you will be taken to your new page by default (after you login of course!)

Auto-Readme Extension Example

September 2nd, 2011 by ddraper

Introduction

In my earlier blogs describing the new extensibility features my samples have not been particularly practical. Earlier this week I was asked how to extend the Document Library for a site to automatically display the content of any “readme” files in the current folder. This blog will take you through how I implemented it. This post is not intended to explain the extensibility features but simply to show how they can be applied to achieve a more practical result. These features are currently available in the latest Alfresco Community source and will be in Alfresco Enterprise 4.0.

Tutorial

The extension that we’re going to create will automatically display the contents of the first file called “readme.txt” found in the currently selected folder in a site Document Library.

The JavaScript widgets that back the Document Library are probably the most complex in the in Alfresco Share client source code. Making anything but the simplest of changes to it is always going to require a reasonable understanding of how it works.  This means that you’re need a good understanding of JavaScript and a reasonable about of YUI knowledge (or at least have a browser handy with access to the YUI documentation).

The standard Document Library (that you’ll find on a regular site) is made up of the following Components: filter, tree, tags, toolbar and document list. If you want to change the behaviour of these widgets then you’ll to take the following approach:

  1. Create a new JavaScript object that extends the JavaScript widget and store it in a .js file.
  2. Override the WebScript that backs the Component and ensure that the *.head.ftl loads your library and that the .html.ftl instantiates your widget.

This approach is not ideal because it requires that you override (rather than extend) a WebScript – however, we are looking at solutions to make this easier. Also, it’s important to note that there are other options for specifically extending the Document Library that haven’t been blogged about….. yet!

Fortunately this task is possible with the extensibility functions at our disposal because it only requires that we insert something between the toolbar and document list widgets. Using SurfBug we can identify that the Document List is rendered by the Component “template.documentlist.documentlibrary” and it’s Sub-Component “default” (which was generated from legacy configuration).

We can insert some new content by defining a new Sub-Component in an extension module. Our Sub-Component needs to be backed by a Web Script which will render some new HTML to display the contents of a “readme.txt” file if one exists. If you’ve used the Document Library you will have noticed that changing the path does not reload the entire page and inspection of the code reveals that AJAX requests to refresh the Document List are made on receipt of a “filterChanged” event. This means that we will need to create our own JavaScript widget to listen for those events and respond accordingly.

The full list of files we’ll need are as follows:

  • A configuration file defining a module that adds a new Sub-Component (“doclib-extension.xml“)
  • A descriptor file for a new WebScript that will display “readme.txt” content (“auto-readme.get.desc.xml“)
  • A new JavaScript file defining our widget that will listen for the “filterChanged” event (“auto-readme.js“)
  • A new CSS file with the styling for our Sub-Component (“auto-readme.css“)
  • A  WebScript .head.ftl file that will ensure that our JavaScript and CSS imports gets added to the HTML <head> element (“auto-readme.get.head.ftl“)
  • A WebScript .html.ftl file that will define the HTML for displaying the “readme.txt” content and instantiate our JavaScript widget (“auto-readme.get.html.ftl“)

The files will need to go in the following locations in a JAR file:

  • /alfresco/site-data/extensions/doclib-extension.xml
  • /webscripts/auto-readme.get.desc.xml
  • /webscripts/auto-readme.get.head.ftl
  • /webscripts/auto-readme.get.html.ftl
  • /META-INF/doclib/extensions/auto-readme.css
  • /META-INF/doclib/extensions/auto-readme.js

(Note: the JavaScript and CSS files can live anywhere under the META-INF… any resources that are not picked up by the Spring Surf configuration or WebScript classloader code need to be placed in the META-INF path in order to be loadable).

Since a lot of code is required, instead of copying and pasting it into the blog body I’ve attached the files
here in a ZIP.  The files should all be sufficiently commented to explain what they’re doing.

Once you’ve created your JAR, copied it to “webapps\share\WEB-INF\lib“, restarted Alfresco Share and deployed the module you should be able to see results like those shown below:

Screenshot Showing Readme Contents

Screenshot Of Path With No Readme File

SurfBug

August 31st, 2011 by ddraper

Introduction

In my previous blogs I’ve been introducing some of the new extensibility features that are currently available in the current Alfresco Community source and that will be available in Alfresco Enterprise 4.0. In many of these blogs I’ve demonstrated the use of a new Spring Surf debug tool called “Surf Bug” without providing much additional information about it. In this blog I’m going to describe Surf Bug in greater detail.

Background

When I first started working on the Alfresco Share user interface the most immediate challenge was identifying the various components that comprise each page. It’s was something of an art to find the source file in which the actual HTML on any given page is defined – and that was after you understood how a Spring Surf page is built!

This posed a major challenge for anyone extending a page in Alfresco Share as the first task was always identifying the  files that actually needed extending. In order to assist with this problem we have introduced “SurfBug” which (as its name suggests) took its inspiration from the FireFox add-on “FireBug” and when enabled is designed to highlight Components or Sub-Components that comprise the current page and provide information on the files and properties that define them.

SurfBug is a part of Spring Surf rather than Alfresco Share and as such is actually available to any Spring Surf application. It is enabled by the “surfBugStatus” WebScript (i.e. accessed through “<application context>/service/surfBugStatus“).

Enabling SurfBug and refreshing a page will overlay red boxes on the screen indicating the location of the Components or Sub-Components on the page (the information shown is based upon the Surf application configuration – if the Component interfaces is being fulfilled by the “org.springframework.extension.surf.type.AdvancedComponentImpl” class (which is the default) then Sub-Component information will be shown. When you mouse click on a box, a pop-up will be displayed that hopefully provides all the information about the Sub-Component that you could possibly need.

Things To Note

SurfBug is not guaranteed to show every Sub-Component on the page if the DOM elements for that page have been manipulated in certain ways (e.g. you won’t see highlights for the Sub-Components that make the pop-up panels for site creation, file upload, etc) and since the highlights are absolutely positioned on the page (to avoid affecting the DOM structure) they are not guaranteed to be in pixel perfect position – the rough position of a highlight and the information contained in its pop-up should hopefully be sufficient though!

You should also not attempt to drive an applications user interface with SurfBug enabled. The most effective way we’ve found of using it is to navigate to the page you’re interested in, toggle SurfBug on from another tab in your browser and then refresh the application page. If you need to move onto another page – simply toggle SurfBug back off, reload the page, navigate and switch back on.

One other important point is that Surf Bug gets enabled for the entire application – NOT just for the user that enables it. It’s intended to be used in development, not production. It’s “admin” protected so regular users won’t be able to switch it on, but if it gets enabled then every user will see the highlights until it’s switched off again.

Information Provided

The following table provides a breakdown of the information that SurfBug provides:

Page ID: The ID of the Page being displayed
Template ID: The ID of the Template being displayed
Template Type: Typically this is the path of the FreeMarker template used to render the Spring Surf Template referenced by the Page.
Component Details
ID: The ID of the Component that the Sub-Component belongs to.
Definition Location: The runtime path of the file containing the configuration for the Component
GUID: Generated unique id of the component
Region-id: The id of the region the template into which the component has been bound
Source-id: The id of the object at which the component is defined (this will typically be a Page id, a Template id or will be “global”)
Scope: The scope at which the Component has been defined (this will typically be “global”, “page” or “template”).
Custom Properties: Any custom properties that have been configured for the component. These are not used by Spring Surf to perform any rendered but may be used by the Component itself if it is parameterized in any way (this may be the case for Components backed by JSPs, WebScripts or FreeMarker).
Sub-Component Details
ID: The id of the Sub-Component – this is always prefixed by the parent Component id and a “#” indicates the start of Sub-Components identification
Contributing Paths: The runtime paths of all the files that have provided input into this Sub-Component (a Sub-Components property, index and evaluation configuration can all be updated by zero or more extension modules). If no extensions have been applied then this will only contain a single path.
Index: The specifically set index of the Sub-Component within the Component. This is the final index after all extensions have been applied. If nothing is shown it means that the default is being used.
Processor: The processor that has been used to render the Sub-Component. If this Sub-Component has been generated from legacy configuration then this could be either WebScript, WebTemplate or JSP (or some custom processor) – but AdvancedComponents only currently support WebScript processors and if the Sub-Component is not legacy generated then this will be blank.
Evaluated URI: The URI used to render the Sub-Component. This is the URI that is generated as a result of processing all Evaluations across all extensions – so is not necessarily the value configured in the source configuration file.
Evaluated By: This is the id of the first successful Evaluation and therefore the one that returned the “Evaluated URI” field. If this is blank it means that no Evaluations were performed on the Sub-Component.
WebScript Location: If the Sub-Component was rendered by a WebScript then this will show the runtime path of the WebScript descriptor file. The other WebScript files (template, controller, etc) will be co-located.
WebScript Details: This provides a link to the WebScript information which will be opened in a new tab/window.
Evaluated Properties: The properties for the Sub-Component as returned by a successful Evaluation. Properties can be overridden by Evaluations to change how a Sub-Component is rendered.
Extensibility Directives: A list of the extensibility directives that have been applied to the Sub-Component.

Alfresco Home | Legal | Privacy | Accessibility | Site Map | RSS  RSS

© 2012 Alfresco Software, Inc. All Rights Reserved.