DevCon Berlin rocked, now to San Jose

So with last week’s DevCon event in Berlin over and the second event in San Jose about to kick off, I wanted to share a few of my highlights so far, in addition to what Jeff already posted.
Tor Brandenburg
Last week kicked off with a full-day hackathon building on top of Alfresco, facilitated by Nathan McMinn, which I would recommend to anyone who has worked with Alfresco before and is interested in expanding  their horizons. This was about people coming together to work collectively on building something in a day, and the format worked fantastically well. I believe Nathan will be posting soon on what was produced in the session.
As always John and John kicked off the main sessions with the high-level view of Alfresco, seamlessly mixing open source content management with tales from the wider IT industry and a satirical take on current global politics. If you’re interested in any of those things, it’s a must see.
I didn’t get to see as many technical sessions as I have done in previous years, but the quality of those I did see really stood out. In particular:

  • The public API live coding (part 1, part 2) session by Peter, Gethin and Steve was a great opportunity to get to grips with Alfresco Cloud’s new public API, and like the hackathon it felt great to have some working code at the end, much aided by the sample project provided on the USB sticks.
  • Expanding on the idea of developing your own projects, Gab Columbro’s talk on the long-awaited and brand-new Maven SDK was a highlight as much for the content as for Gab’s style, although you’ll want to do some background reading on Maven and how it differs from more traditional build tools before you use it for real.
  • On the second day I attended Dave and Erik’s Share Customizations Live session where they demonstrated, through the use of site presets and extensibility modules, some concrete examples which showed them taking a vanilla Share and hacking away until it was barely recognisable. What was interesting was how the mechanisms for doing this have improved over the last year, and the point that often it is just as useful to remove non-required OOTB functions as it is to add new ones, which the framework makes easy.
  • Jared’s talk on our own Integrations team and what we’ve been up to over the last year (or six months in my case), and Nathan’s overview of PDF Toolkit and the latest new features, based on the work he’s done recently building on Jared’s earlier work. If you’re interested in integrating Share with external systems and working with PDF content you’ll find both sessions really useful.

Overall what stood out this year for me was the diversity of sessions available, not only in terms of topics but in their formats as well. There were less of the traditional-style PowerPoint-only type of sessions and more richer interactive sessions.
I experimented with a local Git repo in my own Developing Great Dashlets talk, switching between eight different branches of my example project to show progressively adding more features to an at-first-basic dashlet. It was great to see others experimenting in even more ways and adding more diversity to the two days.
If you’re attending the event in San Jose you’re I’ll be presenting the same dashlets “Zero to Hero” talk on Day 1 and Day 2, plus a topic on pimping the Document Details screen with your own document viewers and a lightning talk on the fantastic Leaflet. Do join if you can!

CMIS support in LibreOffice Experimental Features

Since I upgraded the LibreOffice installation on my Macbook to version 3.6, I’ve been playing around with the CMIS integration built into this version. This is currently an experimental LibreOffice feature, which means you should not use this against a production server.
The integration allows LibreOffice to connect to a remote Alfresco server in order to open and save documents. This is a feature Microsoft Office has had for a long time, although limited to SharePoint-compatible servers (including Alfresco, via our SharePoint protocol support!). Now you can do the same thing with LibreOffice, via the magic of CMIS.
The first thing to do is to enable LibreOffice’s experimental features within the Preferences dialogue.

After you’ve confirmed that change, you can open a file stored in Alfresco using the standard Open menu. When the file browser loads, you should notice a small button next to the location drop-down menu labelled with an ellipsis (…).

Clicking that button allows you to add a server. LibreOffice supports a range of server types, including the CMIS AtomPub service provided by Alfresco.
First you must enter a name for the server – I entered ‘Alfresco’ – and select the server type. If you do not see CMIS (Atom Binding) in the list then check to ensure that you have LibreOffice 3.6+ and that you have enabled Experimental Features.

The binding URL for Alfresco 4.2 will be something like http://localhost:8080/alfresco/cmisatom if you are running a local instance. I found that if I used the legacy CMIS URL below /alfresco/service (or the short form /alfresco/s) then document saves did not work, but the newer URL worked great.
Next is the slightly tricky part, entering the CMIS repository ID. Your Alfresco instance will only expose one repository, but other CMIS providers provide others, and so you need to enter this.
Hitting the repository binding URL directly will give you the ID, but unfortunately the non-standard document MIME type returned means it does not open directly in a web browser. You could save the response to disk and then use a text editor to view its contents, but I found it easier to use curl at the command line, together with less.

curl -u admin:admin http://localhost:8080/alfresco/cmisatom | less

In the response, do a search for the string repositoryId and within that element you should see a GUID value which you can copy and paste into the dialogue in LibreOffice.
The last item you need to enter is the username that will be used to access Alfresco (you will enter the password later).

You should now have the full set of information needed to add the server. Click OK, and it should be added to the side panel on the left of the Open dialogue.
You can navigate through the folders below the repository’s Company Home space, and open any compatible document. I used the Word documents located under Sites > swsdp > documentLibrary > Meeting Notes, which opened perfectly.

You can make any modifications, hit Save and the content will be automatically saved back into Alfresco.

I also tried creating a new document from the New menu, and was able to save the document back to a folder Alfresco via the Save As dialogue. I found I needed to set the correct MIME type for new documents back in Alfresco Share to get previews to work, but this could easily be done using a rule.
All in all not bad for an experimental feature, and it will be interesting to see if this makes it into LibreOffice 3.7 for real.

Creating self-signed SSL certificates for Solr

Since the default SSL certificates that ship with Alfresco’s Solr integration expired the other week I’ve been having to fix up my local development installs that I’d previously configured with Solr.
The instructions on the wiki are pretty comprehensive, and together with the text file provided in the Solr integration package provide all the info you need to understand the process of creating your own keys and certificates for local testing.
After I stepped through the complete procedure documented there, with some slight modifications to avoid the interactive prompting, I found I had a set of commands which I could use in order to repeatedly re-generate a set of keys inside each installation directory on my system.
The first section in the following listing could be placed inside your .bash_profile file, and this is recommended to ensure that the password values you choose to use are not captured in your command history or in your terminal. The commands after that should be used repeatedly against each Alfresco installation you wish to generate keys and certificates for.

# The subject name of the key used to sign the certificates
REPO_SUBJECT_NAME="/C=GB/ST=UK/L=Maidenhead/O=Alfresco Software Ltd./OU=Unknown/CN=Alfresco Repository"
# The repository server certificate subject name, as specified in tomcat/conf/tomcat-users.xml with roles="repository"
REPO_CERT_DNAME="CN=Alfresco Repository, OU=Unknown, O=Alfresco Software Ltd., L=Maidenhead, ST=UK, C=GB"
# The SOLR client certificate subject name, as specified in tomcat/conf/tomcat-users.xml with roles="repoclient"
SOLR_CLIENT_CERT_DNAME="CN=Alfresco Repository Client, OU=Unknown, O=Alfresco Software Ltd., L=Maidenhead, ST=UK, C=GB"
# The number of days before the certificate expires
CERTIFICATE_VALIDITY=36525
# Keystore password
KEYSTORE_PASSWORD=custompassword
BROWSER_KEYSTORE_PASSWORD=alfresco
openssl genrsa -des3 -passout pass:$KEYSTORE_PASSWORD -out ca.key 1024
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -passin pass:$KEYSTORE_PASSWORD -subj "$REPO_SUBJECT_NAME" -passout pass:$KEYSTORE_PASSWORD
# Generate Alfresco Repository SSL keystores
keytool -genkey -alias 'ssl.repo' -keyalg RSA -keystore ssl.keystore -storetype JCEKS -dname "$REPO_CERT_DNAME" -storepass "$KEYSTORE_PASSWORD" -keypass "$KEYSTORE_PASSWORD"
keytool -keystore ssl.keystore -alias 'ssl.repo' -certreq -file repo.csr -storetype JCEKS -storepass "$KEYSTORE_PASSWORD"
openssl x509 -CA ca.crt -CAkey ca.key -CAcreateserial -req -in repo.csr -out repo.crt -days "$CERTIFICATE_VALIDITY" -passin pass:$KEYSTORE_PASSWORD
keytool -import -alias 'alfresco.ca' -file ca.crt -keystore ssl.keystore -storetype JCEKS -storepass $KEYSTORE_PASSWORD -noprompt
keytool -import -alias 'ssl.repo' -file repo.crt -keystore ssl.keystore -storetype JCEKS -storepass $KEYSTORE_PASSWORD -noprompt
keytool -importkeystore -srckeystore ssl.keystore -srcstorepass $KEYSTORE_PASSWORD -srcstoretype JCEKS -srcalias 'ssl.repo' -srckeypass $KEYSTORE_PASSWORD -destkeystore browser.p12 -deststoretype pkcs12 -deststorepass $BROWSER_KEYSTORE_PASSWORD -destalias repo -destkeypass $BROWSER_KEYSTORE_PASSWORD
keytool -import -alias alfresco.ca -file ca.crt -keystore ssl.truststore -storetype JCEKS -storepass $KEYSTORE_PASSWORD -noprompt
# Generate Alfresco Solr SSL keystores
keytool -genkey -alias 'ssl.repo.client' -keyalg RSA -keystore ssl.repo.client.keystore -storetype JCEKS -storepass $KEYSTORE_PASSWORD -keypass "$KEYSTORE_PASSWORD" -dname "$SOLR_CLIENT_CERT_DNAME"
keytool -keystore ssl.repo.client.keystore -alias 'ssl.repo.client' -certreq -file ssl.repo.client.csr -storetype JCEKS -storepass $KEYSTORE_PASSWORD
openssl x509 -CA ca.crt -CAkey ca.key -CAcreateserial -req -in ssl.repo.client.csr -out ssl.repo.client.crt -days "$CERTIFICATE_VALIDITY" -passin pass:$KEYSTORE_PASSWORD
keytool -import -alias 'alfresco.ca' -file ca.crt -keystore ssl.repo.client.keystore -storetype JCEKS -storepass $KEYSTORE_PASSWORD -noprompt
keytool -import -alias 'ssl.repo.client' -file ssl.repo.client.crt -keystore ssl.repo.client.keystore -storetype JCEKS -storepass $KEYSTORE_PASSWORD -noprompt
keytool -import -alias 'alfresco.ca' -file ca.crt -keystore ssl.repo.client.truststore -storetype JCEKS -storepass $KEYSTORE_PASSWORD -noprompt
# Copy files
cp ssl.keystore ssl.truststore browser.p12 data/keystore/
cp ssl.repo.client.keystore ssl.repo.client.truststore solr/workspace-SpacesStore/conf
cp ssl.repo.client.keystore ssl.repo.client.truststore solr/archive-SpacesStore/conf
# Remove temporary files
rm repo.csr ca.key ca.crt repo.crt ssl.keystore browser.p12 ssl.truststore ssl.repo.client.csr ssl.repo.client.crt ssl.repo.client.keystore ssl.repo.client.truststore

Note that this will not update the Alfresco and Solr configuration with the password you choose for the keystores – you must do this separately in your alfresco-global.properties and in the properties file associated with each Solr core, as detailed in the Solr installation guide.
Lastly, this procedure should not be used in any public-facing or production instances, it is intended to be used in development environments only.

Media Previews is dead, long live Media Viewers

A while ago I wrote about the newly-extensible Document Preview component in Alfresco Share and how you can use it to customize the out-of-the-box viewers and add your own custom implementations.
I mentioned at the end of that post that there were some new examples of custom viewers coming in Share Extras and I’m excited that we’re now ready to release them on the site.
Peter Löfgren had the great idea of using the pdf.js project to perform direct rendering of document content in the web browser using HTML5. I’d already started work on some simpler examples of custom viewers, and on refining the Flash audio/video players that Share Extras previously provided for Alfresco 3.3/3.4.
The result is what we’re calling the Media Viewers add-on. This bundles up a total of six viewer implementations designed to show different ways of implementing custom document views, both with and without Flash.

  • PdfJs displays documents, presentations and any other file capable of being transformed to PDF in-line in the web browser using the excellent pdf.js viewer, which uses the power of HTML5 to remove Share’s Flash dependency for document viewing.
    The viewer supports a number of features not directly supported by the Flash document previewer, such as a sidebar with thumbnail, outline and search views, bookmarking of individual pages of a document, and will remember the page number and zoom level of previous documents that you have viewed.
    PdfJs Viewer
  • FLVPlayer and MP3Player display compatible audio and video files respectively, within the web-browser using the open source FLV Player and MP3 Player media players by neolao. Based on the content’s MIME type, the updated component automatically chooses the appropriate previewer to use.
    While similar to the Flash players provided by Share out-of-the-box, these implementations allow advanced customization of the player via configuration and if FFmpeg is installed, will fire up a transformation to allow viewing of non-H264/FLV video and non-MP3 audio. The user is informed when conversion is in progress and the screen automatically updates when the content can be viewed.
  • Embed uses an in-line iFrame to embed the content itself directly inside the web page. It is suitable for use with content types that can be viewed directly within the web browser such as plain text and PDF, with the Chrome or Acrobat plugins installed. Again, this can be used to avoid the use of the Flash previewer for some clients.
  • Prettify allows formatted code, mark-up and other supported text formats to be displayed in directly in the document and uses the google-code-prettify project to provide an in-line browser-based view with syntax highlighting.
    Prettify Viewer
  • WebODF is an EXPERIMENTAL viewer which uses the AGPL-licensed WebODF project to display ODF content directly in the web browser.
    WebODF cannot be distributed with the add-in itself, so in order to use it you must also download the latest JAR file from the supporting share-webodf project and install it in the same way as the main media-viewers JAR file.

What’s most exciting is that we’re building on top of some great projects such as google-code-prettify, WebODF and pdf.js, that are evolving at a fast pace and changing the way that documents are viewed in a browser environment.
There is plenty more information including download links on the main Media Viewers project page on Share Extras. Please try it out, let us know what you think and help us to improve this collection.

Twitter Dashlets 2.5 Released

If you’ve tried Share Extras‘s Twitter add-ons recently then you might know that the Twitter Dashlets add-on for Alfresco 4 bundles up two similarly-named add-ons that I released last year.
Using the dashlets that the add-on provides, you can follow a particular user or Twitter list, or follow the results of a Twitter search, direct from your Share Dashboard. Great for monitoring the results of a social media campaign related to a particular site, or just topics that are important to you.
But the dashlet that I’m now using the most is the Twitter Timeline dashlet that allows you to easily connect to Twitter’s authenticated resources via OAuth, which I showed at last year’s Alfresco DevCon.
Release 2.5 tidies up a number of bugs but more importantly now provides a consistent experience across all three dashlets, in a couple of areas.
You can view a short video of the updated dashlets or submit feedback on the Share Extras site.
Authenticated access
The Reply, Retweet and Favorite actions were always present on the Twitter Timeline dashlet but now also appear on the other dashlets too, if you have previously connected your Share user account to a Twitter profile.
Twitter dashlet actions
This has the added benefit that you will be able to view the protected tweets of any users you follow in the Twitter Feed dashlet, since requests will always be authenticated whenever you are connected.
Notifications
All three dashlets will periodically poll for new Tweets, and the notifications have been made more compact and added into the dashlet title bar
Twitter Timeline notifications
To load the new tweets, just click the green notification icon.
More options for Search results
The Twitter Search dashlet now allows you to choose between recent tweets, ‘top’ tweets as rated by Twitter, or a mix of the two.

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.

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.