Improved Geo Capabilities in Share Extras

Prompted by Jeff’s post the other day on developing custom actions for Share, I’ve spent a small amount of time this week revisiting the custom Document Library actions hosted on Share Extras, and making a few 4.0-compatibility updates.
There are now two add-ons within the project that provide custom doclib actions for 4.0

  • The Execute Script action provides a small JavaScript action with a dialogue for selecting a JavaScript file to run against a document or folder. Although duplicated by the new onActionFormDialog-powered document-execute-script action (configured out-of-the-box but not included in the default action groups) in 4.0, it provides a useful example of a simple client-side custom action.
  • The Document Geographic Details add-on provides a Geotag action for adding or modifying geo data on repository items, plus two other actions which complement the out-of-the-box View in Google Maps action by adding support for OpenStreetMap, as well as other mapping providers via the Geohack service used by Wikipedia articles.

Since the second add-on provides a few more varied examples of custom actions (which may not be obvious from the project name!) I thought it was worth stepping through each of these in turn.

Geotag Action

This JavaScript action was present in the original 3.x version, and allows geographic information – basically latitude and longitude values – to be added to items, or modified on items which already have it. If your camera doesn’t have a GPS receiver to capture this information, or if you need to make minor corrections to it, then this might be useful.
To make the action compatible with 4.0 there weren’t too many changes needed. Actions are now declared via Bubbling rather than as additional prototype methods of Alfresco.doclib.Actions, so a small refactoring of the code is necessary, but should not affect the main logic of your JavaScript action.
I’ll try to post an update in the near future on converting your existing actions to 4.0.
Geotag action
The action works by calling a separate web script – included within the add-on – to display a modal dialogue with a Google Map view. You can use the cursor to position the marker indicating the item’s location as you need, then the position of the marker is saved back to the repository when the OK button is clicked.

View in OpenStreetMap Action

One of the new features in Alfresco 4.0 is an additional page that allows you to view the geographic location of an item using an embedded Google Map. But sometimes Google Maps just doesn’t have the detail that other mapping providers have – particularly for rural areas – and OpenStreetMap is a great example of a collaboratively-built map available on open terms.
The OpenStreetMap view looks similar to the Google Maps view. In fact, although it declares a new site page, it borrows everything but the map view itself from existing Share page components on the Google Maps page.
View in OpenStreetMap page
The map view is provided by a single additional web script, which delegates the work of setting up the map to a client-side component, as per the regular Share pattern.
When I first added the map view last year, I implemented the view using the popular OpenLayers viewer, but I had problems getting a marker and an associated pop-up that looked as good as Google’s. So I switched the other day to the upstart Leaflet,  which provides a much easier-to-implement and cleaner-looking alternative.
From their site,

Leaflet is a modern, lightweight BSD-licensed JavaScript library for making tile-based interactive maps for both desktop and mobile web browsers, developed by CloudMade to form the core of its next generation JavaScript API.
It is built from the ground up to work efficiently and smoothly on both platforms, utilizing cutting-edge technologies included in HTML5. Its top priorities are usability, performance, small size, A-grade browser support, flexibility and easy to use API. The OOP-based code of the library is designed to be modular, extensible and very easy to understand. Find out more on the features page.

This is the first time I’ve tried Leaflet, but it works well here. I’d be interested in any feedback on this, as I may consider replacing the Google Maps view currently provided in the Document Geographic Details page component with it in the future.
There’s no doubt a few enhancements possible here, such as displaying the location of nearby repository items on the map view in addition to the currently-selected item. Again I’d be interested in any feedback.
Back to the action definition. Now since the action itself just needs to link to the new page, it can be easily handled by the pagelink action type, much easier than implementing a custom client-side action handler. An action evaluator ensures that the action is only shown for geotagged items, and is borrowed from the configuration of the View in Google Maps action.
Lastly, if you look closely at the screenshot you’ll see that as well as displaying the map itself, it also indicates the location of the marker, e.g.

Showing item in London Borough of Richmond upon Thames, Greater London, London, England, United Kingdom

This is looked up via a separate AJAX call to OpenStreetMap’s Nominatim reverse geocoding service, which takes a latitude and longitude, and returns some text indicating the name of the area that it resides in. Nominatim is accessed via a custom Surf endpoint, declared in the add-on’s configuration.

View Location on Geohack Action

The Geohack service allows a referring web site to specify a latitude and longitude as URL parameters and then returns links to that location on a wide range of mapping providers. If you want to view a location on other mapping services besides Google Maps and OpenStreetMap then you can use this action to do that.
Like the Geotag action this is implemented as a client-side JavaScript action, but since it only needs to open a new window with the Geohack service there is no additional dialogue or page definition needed.
With 4.0, it is possible to implement such actions much more easily using the link action type, which takes a target URL in its configuration, and which can include parameters that are substituted in at runtime, based on the node’s properties. But implementing it in a client-side handler as I’ve done here can give you more control.

Developing your own actions

Hopefully the three examples I’ve walked through will provide examples of what is possible to achieve in Alfresco using custom actions. You can find the code linked to from the Document Geographic Details add-on page, and further docs in Client-side template and action extensions on docs.alfresco.com and in Mike Hatfield’s DevCon 2012 presentation.

New Community 4.0.c Amazon EC2 AMI

Since Alfresco Community 4.0.c was quietly slipped out over the Christmas break, I’d been meaning to get round to creating an AMI with the new release. After I was asked by a colleague who was trying to do something similar with our upcoming Enterprise version, I managed to grab a short amount of time to revisit the image creation process that I wrote about back in November.
First off, I decided to reduce some of the complexity by breaking with the past and going for an EBS boot image only, rather than the S3-backed instance-store types that I previously put together. As well as being simpler to create, EBS boot images offer several advantages, the chief one being that by virtue of their EBS-backed storage, they can persist their state when turned off.
From my initial testing, the EBS boot images seem to perform well too, so unless I get any reports of significant problems I’ll be using the same method to create all future AMIs as well.

Running the images

The new 4.0.c image is listed on the Alfresco EC2 Images page. Launching it is easy using the AWS web console.

  1. Click the Launch in AWS Management Console icon next to the version you want to run
  2. Log in using the e-mail and password you registered with Amazon, if you are not already logged in
  3. Step through the wizard, making sure that you select Small in the instance size
  4. When you get to the security part, make sure you select a security profile that has inbound SSH (port 22) and HTTP (port 80) enabled, plus any other interfaces you want to allow into Alfresco

After the wizard has completed you can monitor the start-up of your instance in the Instances area of the EC2 web console. To connect to Alfresco Share on your instance, simply paste the public DNS name of the instance into your browser address bar. It may take up to five minutes for the repository to run through the bootstrap process, after which you will be redirected to the normal Share login page.

Creating your own images

There’s nothing to stop you creating your own Alfresco AMIs, using the same procedure that I’ve used here. As I mentioned, when creating EBS boot instances the procedure is much simpler, and there is no need to copy private key files across the network onto the host.
There are a few steps to carry out, but I’ve tried to make these as straightforward as possible

1. Start up your base image

Start by running up a pre-configured Ubuntu or other Linux AMI – see Canonical’s list for the latest official versions. You should pick the right one for your geography and size requirements, but I use the most recent 32-bit EBS boot AMI from the West Europe region.
To start up the image, just click the relevant link in the list, or use the Launch wizard in the EC2 Management Console to pick an alternative Linux-based AMI. The scripts have been tested with Ubuntu 11.10 and 12.04 32-bit instances, so I’d recommend either of those where possible. 64-bit AMIs do not seem to run well on small instances, so if you use one of those then ensure you pick a larger instance size.

2. Log in via SSH

I use PuTTY to connect to the newly-created instance. You’ll need to have created a keypair for your AWS account and have imported it into PuTTY. You can then connect via SSH as the relevant user account (the Ubuntu images use the user ‘ubuntu’) and the correct key file.
There is general information on connecting to Linux instances in the EC2 help and Eric Hammond has a page that explains some more about securely connecting to the official Ubuntu images, which should be accurate for all the AMIs listed on his site.

3. Download and extract the Ubuntu Quickstart scripts

These are now available as a ZIP download from the alfresco-ubuntu-qs project on Google Code. Find the latest version in the Downloads section and right click to copy the download link to your clipboard.
Back in your SSH client you can then download the files using the following commands (modify as needed for the latest version of the scripts), or you can skip the first line by downloading the file from the project site and SCP’ing it up to your Amazon instance.

curl http://alfresco-ubuntu-qs.googlecode.com/files/alfresco-ubuntu-qs-0.9.8.tar.gz -o alfresco-ubuntu-qs-0.9.8.tar.gz
tar xzf alfresco-ubuntu-qs-0.9.8.tar.gz

4. Install Alfresco
Now we can install the Alfresco files using the install.sh script supplied in the Ubuntu Quickstart package.

cd alfresco-ubuntu-qs
sudo ./install.sh --alf-version 4.2.b --jdk-version openjdk-7 --no-install-dod

Note that the --no-install-dod option to install.sh is only needed for version 4.0, for which the DoD5015.2 add-on module is currently unavailable.
For Alfresco Community 4.2, only JDK7 is required, which you must enable using --jdk-version openjdk-7.
You can follow the progress of the script in your SSH client. When you are prompted for a MySQL password, you must enter ‘alfresco’ – unless you have changed the value of $MYSQL_USER in install.sh to something else.
When the script has finished it will tell you that installation is complete and that Alfresco can be started. DO NOT START ALFRESCO UP AT THIS TIME – unless you want a pre-installed repository to be part of your AMI.
5. Create your AMI
One of the benefits of EBS boot instances is that you can create images with a single command using the AWS API tools, or directly from the EC2 web console.
To create your AMI from the web console, right-click the running instance in the Instances section, select Create Image from the menu, enter an appropriate file path in the ‘name’ field, and confirm.
The image creation process should take no more than a few minutes – far quicker than the instance-store method! Once it is done, you should be able to see your image in the AMIs section of the web console. You can test it by starting up an instance of it as per the steps in Running the images, above.

6. Cleaning up

Last but not least, make sure that the instance that you used to create your image is terminated, so it will not keep on consuming billable resources! You should do the same for any additional instances that you’ve fired up to test your image.

Alfresco 4.0 in Amazon EC2

Update January ’12: These instructions are now deprecated. A simpler procedure, allowing easy creation of EBS-boot images is now documented as a follow-up post.
I’ve just added a new AMI for Alfresco 4 onto my Alfresco EC2 Images list. Running these files is now even easier, based on the method used by Eric Hammond’s alestic.com, with a link next to each image that allows you to click directly through to the AWS Management Console. If you have an AWS account, you’re now just a few clicks away from launching your own cloud-based instance of Alfresco 4.0.
Of course, the usual disclaimers apply here. These are not official images in any way, and should not be used for production purposes. But if you want to try out Alfresco 4 without the hassle of managing your own install, hopefully it will be useful.
It’s also worth pointing out that the scripts I use to create these are public, hosted on the alfresco-ubuntu-qs project on Google Code.
You should be able to create your own Alfresco 4 AMIs by following these simple steps

  1. Start by running up a preconfigured Ubuntu or other Linux AMI – I use Eric Hammond’s list for the latest versions. Pick the right one for your geography and size requirements, I use the most recent 32-bit instance-store AMI from the W Europe region
  2. While the instance is starting up, download the latest Quickstart scripts from Google Code
  3. Once the machine is started, check you can connect to it via SSH, using the keypair you specified when starting the image and the username ‘ubuntu’
  4. Create a new directory named ‘ec2’ in your home directory on the running instance
  5. Use SCP or rsync to copy the quickstart scripts bundle, plus your AWS certificate and private key files (cert-blah.cert and cert-blah.pk) from your local machines. Place the script bundle in /home/ubuntu and the certificate and key files in the new /home/ubuntu/ec2 directory
  6. Back in your SSH session on the instance, extract the contents of the quickstart bundle and change into the new alfresco-ubuntu-qs directory
  7. Use the install.sh script to install Alfresco and its dependencies on the instance by typing sudo ./install.sh. For 4.0.a and 4.0.b, which do not support the DOD Records Management module, you will need to add the --no-install-dod option to the command.
  8. The script will run through and you will be prompted for a MySQL password. You must enter ‘alfresco’ unless you have changed the value of $MYSQL_USER in the script to something else.
  9. The script will indicate that it has finished installing Alfresco. Do not start Tomcat, since this will bootstrap the repository data, which you do not want to do before bundling.
  10. Change back into your home directory
  11. Create the AMI files using the sc2-bundle-vol command
    sudo ec2-bundle-vol -d /mnt -p alfresco-community-mysql-4.0.a-i386 -u 111111111111 -k ec2/pk-*.pem -c ec2/cert-*.pem -e /home/ubuntu/ec2,/home/ubuntu/.ssh,/home/ubuntu/.cache,/home/ubuntu/.sudo_as_admin_successful,/home/ubuntu/.byobu,/home/ubuntu/alfresco-ubuntu-qs,/home/ubuntu/alfresco-ubuntu-qs-*.tar -s 4096
    You must set your numerical AWS account ID using the -u flag. Also you should review the list of excluded files to ensure that you are not bundling any files that you do not want to.
  12. Once the bundling process has finished, upload it to your S3 bucket using the ec2-upload-bundle command
    ec2-upload-bundle -b my-s3-bucket -m /mnt/alfresco-community-mysql-4.0.a-i386.manifest.xml -a aid -s secret --location EU
    You must specify your S3 bucket name using the -b option, and ensure that you set your AWS access key and AWS secret key using the -a and -s options
  13. Once the upload has completed, log into your AWS EC2 web console, navigate to the AMIs section and click the Register New AMI button to register your new image. Enter the path of the uploaded manifest file within the bundle you just uploaded, this will be something like ‘my-s3-bucket/alfresco-community-mysql-4.0.a-i386.manifest.xml’
  14. Now your AMI is registered you can see if it works by creating a new instance of it. If it does, then you can safely shut down the originial Ubuntu instance as you will no longer need this.
  15. If you want others to be able to run your image then you will need to add the necessary permissions for this, using the web console.

New 4.0 features for Share Import-Export

If you’ve not come across the project before, Share Import-Export provides a set of Python scripts to export sites and their supporting data into a standardised structure based on ACP and JSON, and can also then import these definitions into another Alfresco system. As well as the scripts, a set of sample sites from the Alfresco Cloud Trial are provided to help you get started.
With version 1.3 of Share Import-Export fresh out of the door, I wanted to post a quick update on the changes in this version, which are designed to provide even more options for those early adopters of Alfresco 4.0. So you can read on for more details, or download the new version straight away.
Tag support
The first major addition is the new support for importing and exporting the tags associated with site content. Share sites just look more complete with tags populated, so this has been on the list for a little while.
Unfortunately the ACP format used doesn’t currently allow tag definitions to be embedded in exports of site content, but tag information can be easily pulled out in JSON format and this can be easily persisted to an additional file alongside the ACP.
You should see this if you run export-site.py with the --export-tags option, and I’ve also added tag data for the sample sites in the package, so you should see the definitions in the data folder too.
When importing sites with import-site.py, again the tag definitions aren’t yet included by default, but you can include these using the the --import-tags option.
The idea is that this will help when demonstrating Alfresco Share in 4.0, but it’s not limited to that version and works well with 3.4 in my own tests. So please do try this out and post your feedback.
Bootstrap your sites
The second improvement is specific to 4.0, but allows sites that you’ve exported using the scripts to be packaged up as bootstrappable components – complete with Spring configuration – inside a single JAR file, which can be automatically imported when Alfresco is started up.
The format used is the same as the Web Site Design Project site, first introduced in Alfresco Team, and also included in Alfresco 4.0, which provides the default sample content. So you can install additional users and sites alongside the out-of-the-box site, or completely replace it. (Look for the bean with id patch.siteLoadPatch.swsdp in the patch-services-context.xml file in WEB-INF/classes/alfresco/patch inside the Alfresco webapp, which you can comment out to disable the default site)

Bootstrap your own site definitions alongside the default sample site


Of course you can also continue to import sites manually using the import-site.py script, but if you’re distributing Alfresco instances to others (say a demo package used across your sales team) then the bootstrap packages can be useful to automatically import the sites when Alfresco is started up.
To package up a site in this format, you must previously have exported the site from Alfresco into the normal local structure using export-site.py. You can then point the new script create-bootstrap-package.py to this local definition and tell it the name of the JAR file to produce, containing the bootstrap components, for example

python create-bootstrap-package.py data/sites/branding.json sample-branding-site.jar --users-file=data/cloud-users.json

You should find that this builds a JAR file named sample-branding-site.py in the current directory, using the contents of the Company Rebranding site from the bundled cloud trial sites. If you have problems, or you want to know what other options, type  create-bootstrap-package.py --help for more information.
Other improvements
Lots of bug fixes and small improvements have gone into this release, based on testing across various versions of Alfresco from 3.2 to 4.0.
Download packages
If you check out the downloads page you’ll see that there’s now a choice of packages there. The full 54MB package, with scripts and full Green Energy sample sites and users, is still recommended for most users and is linked to from the home page. However, you can also grab a version with just the scripts plus user data (8MB), or with just the scripts themselves and no sample data (61kB). If you don’t need the full sample data, you might find one of these smaller packages useful.
Download Share Import-Export from Google Code

Custom Admin Console Components in Share

Having released v0.2 of the Node Browser Component for Share this week and having put a bit of work into refactoring the underlying code, it seemed like a good time to write a quick guide to creating Administration Console Components for Share.
Like dashlets, Admin Console components are easily defined using a single web script, along with some supporting resources, and are therefore are automatically picked up by the framework once added to Share, with no additional configuration needed.
Alfresco’s Engineering team have added a number of new console components to the upcoming Swift release, and in addition to the Node Browser Share Extras provides two other additional components that you can add to your existing 3.3 or 3.4 installations.

  • Complenting the Node Browser, Florian Maul’s JavaScript Console allows custom JavaScript code to be executed against the repository to display information or make changes. Loading and saving of scripts to and from the Data Dictionary is supported, as well as syntax highlighting and custom space contexts.
  • Secondly the Create Bulk Users component allows administrators to create multiple users in the repository, based on CSV/TSV or JSON data, and is capable of auto-generating usernames and passwords, sending email notifications and logging of all user created.


Each of the three add-on pages has information on checking out a copy of the source code, which you can use as a basis for creating your own components. You should also grab a copy of the Sample Project bundle, which can be used as a template for your projects.
But before you dive into the code, it’s useful to have an understanding of the different parts that make up a component. So let’s step through each of them in turn, using the Create Bulk Users component as an example.
1. Web-tier web script
Technically, this is the only required part of the component. The component is bound into the application via this web script, which must be declared as a member of the admin-console family within its .desc.xml file. It should also define a single, unique URL, have a unique path and provide a HTML template to render the component’s UI.
The following XML is from the descriptor file (create-users.get.desc.xml) for the Create Bulk Users component, which should illustrate this.

<webscript>
   <shortname>Admin Console Create Users</shortname>
   <description>Administration Console - Create Users Console</description>
   <url>/extras/components/console/create-users</url>
   <family>admin-console</family>
</webscript>

Although it’s obviously possible to provide a JavaScript controller for the web script, it’s normally not necessary if you’re using a client-side module to encapsulate your business logic, as we’ll explore in the next section.
A HTML template (create-users.get.html.ftl in this example) is responsible for laying out out the basic skeleton mark-up of the component, including instantiation of any client-side modules being used.
Although technically optional you will normally also need a corresponding .head.ftl template (create-users.get.head.ftl in this example) to include your own basic, custom CSS file as well as any client-side JavaScript files the component requires.
You can see these templates and all other files that make up this web script in the Create Bulk Users source code under config/alfresco/site-webscripts.
2. Client-side component
It’s recommended that you use a client-side component for all but the most basic console components, since it will allow you to handle interactions with the user using standard script-based controls and widgets, and in a manner consistent with other admin console components.
Like dashlets that utilise client-side code, your client-side component will normally be defined in a dedicated .js file, which should be part of the static files (in addition to your CSS) supplied with your component.
The component should be defined using the standard Share pattern

  1. Components are JavaScript classes, defined within a specific namespace, which can be instantiated in page components when needed
  2. Components declare a constructor, then extend a base class using YAHOO.extend() and add their own class methods
  3. Component definitions are contained within a function closure

But whereas dashlet classes would typically extend Alfresco.component.Base, admin console components extend Alfresco.ConsoleTool, which adds some additional fields and methods to support multiple panels and the YUI History Manager.
The component constructor is also slightly more complex than for a dashlet component, since to support the panel approach it is necessary to define within the constructor at least one inner panel class, plus an instantiation of each class.
Panels provide different views of a console component, and whereas simple components such as the Create Bulk Users Console have only one panel, it allows more complex components such as the Node Browser to define more than one (in this case one for the search panel and another for the node view panel).
Like the main component, the pattern used to define the panel class is the constuctor + YUI.extend() method, with panels extending the base class Alfresco.ConsolePanelHandler.
Although this might seem complex, once understood, it provides a consistent pattern which can be applied again and again to define as many admin console components as you need.
So wrapping all of this up, the general pattern for admin console client-side components is something like the following, as illustrated for the Create Bulk Users component.

// Ensure Extras namespace exists
if (typeof Extras == "undefined" || !Extras)
{
   var Extras = {};
}
(function() // Function closure
{
   ...
   Extras.ConsoleCreateUsers = function(htmlId) // Component constructor
   {
      ...
      FormPanelHandler = function FormPanelHandler_constructor() // Form panel constructor
      {
         FormPanelHandler.superclass.constructor.call(this, "form");
      };
      YAHOO.extend(FormPanelHandler, Alfresco.ConsolePanelHandler, // Extend Alfresco.ConsolePanelHandler
      {
         onLoad: function onLoad() // Fired on initial panel load
         {
            ...
         }
         ...
      });
      new FormPanelHandler(); // Instantiate panel instance
      return this;
   };
   ...
   YAHOO.extend(Extras.ConsoleCreateUsers, Alfresco.ConsoleTool,
   {
      options: // Console component configurable options
      {
         ...
      },
      onReady: function ConsoleCreateUsers_onReady() // Fired when component ready
      {
         ...
      },
      ...
   });
})(); // End function closure

Much of the code is stripped out here to convey the overall structure. In reality you would have other statements mixed in with the functions above, and all public classes, constructors and methods should be documented with appropriate JSDoc.
To explorer this some more, it’s recommended that you take a look at the client-side JavaScript files inside the JavaScript Console and Create Bulk User Console add-ons. You will find these files in the checked-out source code under the directory source/web.
3. Web-tier Spring configuration and message bundle
Most of the strings that appear in the admin console component UI should be externalised in your web script .properties file, and will be accessible from the Freemarker template of the web script via the root-scoped msg() function.
However, the name of your component also appears in the component list on the left hand side of the Admin Console, and since the page component responsible for rendering this does not have access the messages defined in your web script, it is necessary to provide a globally-scoped message bundle to provide some text for a label and a description of our component.
You can define your message bundle in any valid .properties file, but for the sake of convention I normally give mine a unique name and place them in the package alfresco/messages in my configuration. This is the file used for the Create Bulk Users component, which you can find in the source code under config/alfresco/messages/create-bulk-users.properties.

tool.create-users.label=Create Bulk Users
tool.create-users.description=Create repository users from CSV or JSON data

The message names should comply with this standard structure, with the part between the periods (.) being the same as the name of the console component’s web script, minus the method part. Since the name of the web script was create-users.get, the message names required are tool.create-users.label are and tool.create-users.description.
Lastly, a small piece of Spring configuration is needed to bind the message bundle into the application. This file must have a unique name with the suffix -context.xml, and be placed within the package org/springframework/extensions/surf. See the file config/org/springframework/extensions/create-bulk-users-context.xml in the Create Bulk Users source code for an example.
4. Repository components
Lastly, it’s likely that your admin console component will require some custom repository logic to perform your administration tasks. Normally this would take the form of one or more repository-tier web scripts, but other components such as a custom data model could also be required.
Fortunately the source directory structure used by the Sample Project in Share Extras (and by the three admin console add-ons) allows these to be placed in the same project, and you will find the repository web script create-users.post used by the Create Bulk Users component in the source code under config/alfresco/templates/webscripts.
Summary
You should now have an idea of the basic parts involved in an admin console components such as the three examples provided by Share Extras, and how they interact with each other.
You can study these examples to help you gain further understanding of how each of the four parts are implemented in practice, by checking out the source code for them. The JavaScript Console and Create Bulk Users examples are the simplest, so start there and look at the Node Browser if you need a multi-panel example.
The Sample Project provides a good skeleton project for you to define your own add-ons. Although the example code provided in it implements a web script, you can easily delete these and add your own files to define an admin console component, based on one of the three examples.
As well as giving you a basic directory structure, the project also gives you an Ant build script that you can use to build, test and distribute your add-on.
If you have any feedback, please leave a comment below.

Custom Web Previews in Share

Note: This post discusses how document previews may be customized in Alfresco Share 3.x. In version 4.0 onwards, an improved method is available, based on a plugin framework. See my presentation Customizing Web Previews at Alfresco’s DevCon 2012 event for more details.
I was asked by a partner today how one would go about enhancing the document details page in Share to better support previewing of specific file types.
Share’s built-in Flash previewer provides great browser-based previews across a whole range of different document types, from PowerPoint files to PDFs. But often you may only be dealing with a couple of different content types, and you may need a wider set of features in your viewer, specific to those content types.
Or, you may want to add support for an additional content type, not supported by the built-in web previewer.
Either way, it is not difficult to add support for other types of previewers. These could be based on Flash widgets, similar to the web previewer, or other technologies such as Java applets or ActiveX controls. Increasingly the HTML5 support in modern browsers, coupled with modern processors, can perform a lot of heavy lifting that previously required a plug-in.
A good example is provided by Share Extras, in the form of the Media Previews add-on. This uses a third-party Flash component to preview video and audio content in the browser, wrapped by a client-side JS module, but the basic approach is the same regardless of the technology you’re choosing. Basically, if you can embed a previewer or player on a website, you can integrate it into Share.

The first step is to read the documentation for the previewer you’re using. Work out which files are required to make it work and how they are referenced in the HTML mark-up on the page.
The files may consist of static resources such as Flash files, JavaScript scripts, CSS files and even compiled code packages (for Java/ActiveX). Sometimes they may be hosted on third-party servers, but normally you will need to bundle them yourself.
Assuming the latter case, you should add them into a new directory inside the Share webapp, and ideally in a collection of sub-directories within that. For example, the Media Previews files are placed in the directory /extras/components/preview, mirroring the structure used by Share’s web preview, but in a separate subdirectory reserved for Share Extras add-ons.
The next step is to modify the web script that hooks in the web previewer. You can find the files for this web-preview.get web script in the following directory in the Share webapp
WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/preview
Take a look in web-preview.get.html.ftl. You should see that it inserts the standard web previewer using a JavaScript module, instantiated inside a <script> element.
Now look inside the customised web-preview.get.html.ftl in the Media Previews source code. You will see that it uses a Freemarker <#if> directive to choose between the standard previewer and the custom ones, based on the content item’s MIME type.
Although the two custom previewers used here also use client-side code to instantiate themselves on the page, you could equally just use an <object> tag to reference the Flash .swf files directly. The benefit of wrapping the previewer with JavaScript code is that it allows you to apply more complex logic, such as detecting if the user has the correct version of Flash installed, or hooking the previewer into page events to update the preview when a new version of the content is uploaded.
Once you’re happy with the mark-up you want to add into the Freemarker file to insert your custom previewer, you can insert this into the file. But rather than overwriting the original, you should use Spring Surf’s override mechanism to separate your custom code from the original version.
To override the HTML template, take a copy of the web-preview.get.html.ftl and place it into the following directory, after first creating any missing directories.
<TOMCAT>/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/preview
When you reference your static files that you added into the webapp earlier, you should use URLs such as ${url.context}/mydir/path/to/asset.swf, or in 3.4 ${url.context}/res/mydir/path/to/asset.swf. The url.context variable will automatically resolve to Share’s webapp context path, i.e. share by default, and mydir/path/to should be the directory structure you created in the webapp.
To reference the item’s content by URL, you can use an expression such as ${url.context}/proxy/alfresco/api/node/${node.nodeRef?replace("://", "/")}/content in your Freemarker markup. To reference the content of a named thumbnail, add /thumbnails/imgpreview to the end of the URL, where imgpreview is the name of the thumbnail.
If your previewer does use some JavaScript code or custom CSS styles from additional files which you’ve bundled, you will need to reference these files in web-preview.get.html.ftl. See the customised web-preview.get.html.ftl in Share Extras for an example, and note the override mechanism is exactly the same as for the .html.ftl template. You are recommended to use the <@script> and <@style> macros provided by component.head.inc, but note that with <@script> you will need to bundle a compressed version of your JavaScript file(s) with the -min.js suffix.
When you’ve finished making changes in your override files you can put these into effect by refreshing Share’s list of web scripts or by restarting your Tomcat server.
Once you’re happy with your customisation, you’ll want to create a dedicated project structure to manage your source files. So Share Extras also provide a sample project, complete with directory structure and an Ant build script that can be used to package up your add-on in JAR format.

Enhancing your Repository Web Scripts

I reorganised the front page of Share Extras the other week to organise the growing list of add-ons by type, which should hopefully make it easier to find specific extensions.
Today I added a new type on the front page, Pages, in order to categorise the latest add on – Content Expiration.

The new add-on is interesting for a couple of reasons. Firstly it contains a number of different types of extensions, which (like all the other add-ons on the site) are all bundled in a single JAR file.

  • A custom model that defines a new Validity aspect
  • A repository web script to return a list of all ‘expired’ content items
  • A custom dashlet to allow a user to quickly see his or her expired items
  • A full page definition to display a more detailed report for a user (or for all users)

It’s also interesting because the add-on started out as a single basic web script, which together with the custom model, we put together for the first version of the Alfresco Partner Site. If you take a look at the repository web script still has a HTML template attached to it, which provided the original user interface.
The basic steps we went through to adapt it for the Share UI could equally be applied to any basic repository web script. I wanted to share our approach as not only can it be used to bring your legacy web scripts up-to-date, but it’s also a perfect way of prototyping your extensions. First start off with a basic repository script, then build up a richer web-tier layer on top.

  1. Define your basic repository script, by defining a JavaScript controller and a HTML template to present the data
  2. Once you’re happy with your data, add a JSON template to the script to provide a machine-readable representation
  3. Now define a basic Share dashlet, pulling in data from the repository script as JSON
  4. If you’re tight on space in the dashlet, you can always link to the HTML version of the repository web script for the full report
  5. Finally add a full-page version of your dashlet as a custom user or site page, and provide a link to this from the dashlet

I’ll follow up with a more detailed walk-through of this process, but in the meantime I’m interested to know if you have used a similar methodology in your own projects.
You can download version 0.1 of the Content Expiration add-on from the Downloads page.

New Contributors to Share Extras

I was really pleased by the feedback I got when I first talked about Share Extras at Alfresco’s Sales Kick-off in Orlando earlier this year, when I showed a few of the initial add-ons.
Since then, the list has grown significantly, but I was especially pleased when Peter joined the project and contributed his Google Site News Dashlet and CKEditor Form Control add-ons for Share. For me it marked the point where the project became more than the list of ideas that had been in my head.
Now as of last week two contributors have become four, with the addition of two great add-ons from Florian Maul and Romain Guinot. I’m really excited about these both.
Florian’s JavaScript Console add-on is an essential tool for developers building extensions for Alfresco, and complements my port of Dave Caruana’s original Node Browser. Like the Execute Script action, it also provides a useful tool for administrators who want to run one-off scripts against the Alfresco repository.
JavaScript Console Tool
Romain’s Sample Audit Dashlet shows how different types of Audit information can be displayed in Share. This is a great addition since it shows how auditing can be used outside of the DOD Records Management module, to track specific events within the repository. Even custom audit applications are supported.

Not only do these new add-ons take the total number to almost 25, they also extend the number of languages supported in Share Extras, by providing examples in French and German, in addition to the English and Swedish examples added by myself and Peter.
Lastly, I’ve made a few significant improvements to the Site Poll Dashlet last week, which I couldn’t have done without the help of several others. So thanks to Toni de la Fuente for helping me to debug and fix a couple of previously unreproducible (at least for me) issues with the repository web scripts, to Jeff Potts for his suggestion to update the site activity feed when someone votes, and to Ed Mannion for reporting an obvious issue with using Date.now() on non-Mozilla browsers.
If you want to help contribute to Share Extras, you can do so in several ways. Of course new add-ons are always welcome but ideas, bug reports/fixes and translations are equally valid ways of helping out. More information is available on the Contribution Process page.

Refreshing Web Scripts from Ant

I’ve always been a big fan of using Ant to automate some of the more mundane tasks such as building packages, when developing Share add-ons like the 20 now available on Share Extras, and the Sample Dashlet project now hosted on there now provides a template for others to use in their own projects.
That project’s build script provides a target to hot-copy files into a running Tomcat instance for local testing, but whenever I’ve updated web script components (either on the repo side or in Share) I’ve had to remember to hit the Refresh Web Scripts button on the scripts index page. This calls the Web Scripts Maintenance page, which is actually a web script itself.
Since I was doing a small bit of work this week to provide a couple of new custom document actions, I figured it was worth looking again for a more automated solution to this.
Ant comes complete with it’s own Get Task which can make outgoing HTTP calls, but as the name implies it’s limited to GETs only and not the POSTs required to hit the Web Scripts Maintenance page. So I started looking for alternatives.
Ant’s Sandbox does provide a HTTP module which can apparently make other requests, but the lack of any updates since 2007 did not bode well, and the lack of any documentation was too much of a barrier anyway. Lastly I revisited the Ant-Contrib tasks which I’d experimented with a while back, but again there were no recent updates and the library still did not support the authentication necessary to call the Maintenance web script.
Finally via a thread on Stack Overflow, I came across this project by Alex Sherwin, which is actively being maintained and supports POST requests with authentication and request parameters. Exactly what I needed.
So I added the following definitions to my build script, which seem to work well on 3.4.
<!-- Tomcat properties to reload web scripts or the manager webapp -->
<property name="tomcat.url" value="http://localhost:8080" />
<property name="tomcat.repo.url" value="${tomcat.url}" />
<property name="tomcat.share.url" value="${tomcat.url}" />
<!-- Tomcat properties to reload web scripts -->
<property name="webapp.alfresco.path" value="/alfresco" />
<property name="webapp.share.path" value="/share" />
<property name="post.verbose" value="false" />
<property name="repo.admin.username" value="admin" />
<property name="repo.admin.password" value="admin" />
<property name="repo.scripts.index" value="${tomcat.repo.url}${webapp.alfresco.path}/service/index" />
<property name="share.scripts.index" value="${tomcat.share.url}${webapp.share.path}/page/index" />

<path id="ml-ant-http.classpath">
    <fileset dir="lib">
        <include name="ml-ant-http-1.1.1.jar" />
    </fileset>
</path>

<taskdef name="http" classname="org.missinglink.ant.task.http.HttpClientTask">
    <classpath>
        <path refid="ml-ant-http.classpath" />
    </classpath>
</taskdef>

<!--
Web script reloading from Ant. These tasks use the HTTP task from
http://code.google.com/p/missing-link/.
-->
<target name="reload-webscripts-repo" depends="" description="Reload repository webscripts">
    <http url="${repo.scripts.index}"
        method="POST"
        printrequest="false"
        printrequestheaders="false"
        printresponse="false"
        printresponseheaders="false"
        expected="200"
        failonunexpected="true">
        <credentials username="${repo.admin.username}" password="${repo.admin.password}" />
        <query>
            <parameter name="reset" value="on" />
        </query>
    </http>
</target>

<target name="reload-webscripts-share" depends="" description="Reload Share webscripts">
    <http url="${share.scripts.index}"
        method="POST"
        printrequest="false"
        printrequestheaders="false"
        printresponse="false"
        printresponseheaders="false"
        expected="200"
        failonunexpected="true">
        <credentials username="${repo.admin.username}" password="${repo.admin.password}" />
        <query>
            <parameter name="reset" value="on" />
        </query>
    </http>
</target>

I’ve tried to structure the properties to cater for most peoples’ development settings, for example if you have separate Tomcats running the repo and Share on different ports, then you can specify different values for -Dtomcat.repo.url and -Dtomcat.share.url when you call the script. You should see that the username and password used to authenticate to the web script can be easily overridden if you have changed the default values.
The scripts haven’t yet been tested against v3.3, but I suspect that the share.scripts.index property will need overriding in this version in order to use the ‘service’ servlet name instead of ‘page’ used in 3.4, e.g. -Dshare.scripts.index=http://localhost:8080/share/service/index .
There’s just a couple of negatives. I couldn’t figure out how to make the http task a little less verbose with it’s output, but I’m sure that will be addressed in time. Also it would be good if the Maintenance web script came with a plain text output template rather than just HTML, as although it would be useful to output the result from the script (which shows the number of web scripts currently/previously loaded, plus any problems), the HTML markup isn’t really readable enough to echo to the terminal.
Since the changes seem pretty stable, I’ve added these to the Sample Project in Subversion. You can try it out yourself by grabbing a copy of the build.xml file as well as the JAR file ml-ant-http-1.1.1.jar from the lib directory. I’ll put a new release of the project ZIP file out in the coming days.
Lastly, here’s a screenshot of the Execute Script action that I was able to test using the new build script.
Execute Script action

New Additions to Share Extras

In the run-up to Alfresco’s Kick Off event in Orlando at the end of March, I spent a bit of time adding some new items into Share Extras and tweaking some of the existing add-ons. Now with the presentation done and the slides up on SlideShare, I thought it would be useful to post a summary of some of the new additions, which bring the total number of add-ons to 19.
Site Geotagged Content Dashlet
The Site Geotagged Content dashlet embeds a map display in the dashboard, with geotagged content marked on the map. Currently Google Maps is used to render the map, based on the method used by the map dashlet distributed with the Calais Integration forge project.

When I originally blogged about the 0.1 version of the dashlet, the map zoom was fixed in the code and the centre was auto-calculated as the mean of all the content item coordinates. Version 0.2 changed this, defaulting to a zoom level of 1 (the whole world) and allowing the user to change the zoom and centre using the map controls, but ensuring that the settings are saved in your user preferences.
So now you can change the view as needed, navigate to another page and then come back to the same map view on your dashboard. The bounds of the map are also now sent with the query to get the list of geotagged items, so even on sites with large numbers of geotagged items only items which will be visible on the map are returned.
Train Times Dashlet
The new Train Times dashlet displays train status information from the National Rail Enquires web site. It’s designed to provide another example of a dashlet loading third-party data (similar to the existing BBC Weather dashlet), but also to demonstrate the use of a few of the more advanced YUI widgets such as DataTables and AutoComplete controls.

Data is loaded from the National Rail Enquiries web site as JSON, but the dashlet uses a second web-tier web script to act as a proxy, loading the data and performing some minor reformatting to better work with the YUI DataSource used by the client-side code.
As well as departure times the web service also provides details of any service disruptions reported at the selected station(s), which are displayed at the top of the dashlet.
Flickr Slideshow Dashlets
The Flickr Slideshow dashlets will display a slideshow display of photostreams sourced from Flickr’s public API. The dashlets are able to display public photos from three types of feed.

  • User photos
  • User contacts photos
  • User favourite photos


The dashlets show how rich effects such as those provided by YUI’s Animation module can be used to display images on the dashboard.
iCalendar Feed Dashlet
The new iCalendar Feed dashlet complements the RSS dashlet provided with Share, displaying upcoming events from any public iCalendar feed. The dashlet has been well-tested with the Alfresco Events feed, but it should also work with other public feeds such as those exposed by Google Calendar.

Wiki Rich Content Customisations
Hopefully a slight change from all these custom dashlets! The Wiki Rich Content add-on provides a number of customisations to the Alfresco Share wiki component to improve it’s visual appearance, and add support for dynamic tables of contents and syntax-highlighted code using Google Code Prettify.

Node Browser Administration Console Component
This is the first example on Share Extras of an additional Admin Console component. The Node Browser component defines a Share administration console component to navigate and view information on nodes stored in the repository stores, similar to its namesake in Alfresco Explorer.

Document Geographic Details Component
Like the Site Geotagged Content dashlet, the Document Geographic Details component displays a map view, this one being designed to integrate into the Document Details page in Share’s Document Library to show the location of that item on the map, when Latitude and Longitude values are available.

Define Your Own Projects

All of the add-ons I’ve mentioned in this post, along with a range of others, are available from the Share Extras project site. The aim of the project is to demonstrate how different types of Share extensions can be developed using scripting and templating, following best practices.
All of the dashlets and other add-ons can be downloaded from the site in JAR format and easily installed into an existing Share installation, so please grab a copy of any that you find interesting and let us know how you get on. For any problems that you find, please use the Issues section on the site.
But really the ultimate aim of the project is to inspire other contributors to come up with their own ideas for dashlets. To make this as easy as possible I’ve created a Sample Dashlet project, which bundles a few files from a basic Hello World dashlet in a basic structure that you can re-purpose to define your own dashlets. Kudos to Erik for the idea.
For now I’ll be focusing myself as I get time on tidying up a few known issues with the existing 19 add-ons. But if you have a great idea for a dashlet or other add-on, please file an issue, use the sample project to get going with the implementation yourself, and consider contributing it to the project if you think others could benefit from it.

Erik Winlöf