Share Custom Actions in a JAR

Update, 4th May 2011: The Backup action is now part of Share Extras and documentation and downloads are now available on the Custom Backup Action page.
Since the ability to package Share extensions was added in the HEAD codeline some weeks ago, both Kev and myself have demonstrated how you can package up dashlets in a JAR file for deployment into Share.
The example we both used was my Site Tags dashlet that we showed at the Alfresco Meetups last year, but at the Madrid meetup we also showed an example of how Share’s Document Library can be extended with a custom ‘backup’ action.
Custom backup action
Document Library actions have no web-tier webscripts, but hook into the client-side actions framework using a bit of custom JavaScript. Each action has a small 16×16 icon and a bit of CSS code to apply this image, one or more text labels (which should of course be externalised for i18n goodness) and a bit of config to hook them into the app. Lastly, since most actions by their nature do something, it’s likely that they will make a RESTful call back to the repository to perform their work, which may require a custom webscript there.
That’s quite a few files, but fortunately we can use the JAR extension mechanism to package everything up nicely.
Just like the Site Tags dashlet, I set this up in Eclipse using a standard project layout and my share extensions build script (with a couple of minor changes) to build the JAR file.
To make it easy to copy this structure, I’ve uploaded a ZIP of the project directory, containing the following files

  • build.xml – the extensions build script
  • config/alfresco/messages/slingshot-custom-backup-action.properties – contains the strings used for the action label and confirmation/failure messages
  • config/alfresco/templates/webscripts – contains the repository-tier webscript used to create a back-up copy of the file
  • config/org/springframework/extensions/surf/slingshot-custom-backup-action-context.xml – Spring config used to initialise the i18n messages
  • config/spring-surf-config-custom.xml – not used at present, but could define additional Surf endpoints for calling third-party RESTful services
  • web – contains all client-side resources used by the action

It should be relatively easy to copy this structure to define your own custom action, following the custom action wiki document to understand what each file does. This will help you to build other actions that call back to the repository via a web script, but actions aren’t limited to calling Alfresco services only. For some examples, take a look at my own list of Share action ideas.
Once you have your structure you can build the JAR file using Ant, e.g.

ant -Djar.name=share-backup-action.jar

The JAR file can then be dropped into Tomcat’s shared/lib directory, and all that remains is to configure the document actions web scripts to pull in the action definition. This is the slightly fiddly bit.
Firstly, copy the web script configuration file WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/documentlibrary/documentlist.get.config.xml from the Share webapp into the directory alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary in Tomcat’s shared/classes to override it. You should see a section <actionSet id="document"> which defines all the actions shown for a normal document in the document list view.
To add the backup action to this list, add the following line just before the </actionset> element for that block.

<action type="action-link" id="onActionBackup" permission="" label="actions.document.backup" />

If you also want the action to show up in the document details view, you need to copy the file WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/document-details/document-actions.get.config.xml into alfresco/web-extension/site-webscripts/org/alfresco/components/document-details in shared/classes in the same way.
Lastly, we need to ensure that the client-side JS and CSS assets get pulled into the UI as unfortunately the config files do not allow us to specify these dependencies.
To do this, we must override the file WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/documentlibrary/actions-common.get.head.ftl. Again, copy this into the corresponding directory in shared/classes/alfresco/web-extension and add the following lines at the bottom of the file.

<@link rel="stylesheet" type="text/css" href="${page.url.context}/res/components/documentlibrary/backup-action.css" />
<@script type="text/javascript" src="${page.url.context}/res/components/documentlibrary/backup-action.js"></@script>

That’s it. Now you can restart Tomcat and you should see the ‘Backup’ action – complete with UI labels and icon – in the Document Library.
Downloads

Adding Custom Aspect Support in Alfresco Share

Since Alfresco 3.2 introduced the ability to configure the metadata forms used in the Document Library, there have been several good articles published on how to add support for custom document types.
One of the first questions people often ask when they see Share is how they can easily extend the metadata fields that are stored against a document. Whilst this can be done using custom document types, aspects often provide a more agile solution.
So this article should explain how Share can be easily extended to support custom aspects using good practice techniques, specifically

  • Ensuring all extended configuration is placed outside of the share webapp, so protecting it from upgrades and redeployments, and
  • Using i18n labels for all text strings that appear in the UI, thus allowing translation of the labels.

The example provides a number of files, all of which should be placed below the tomcat/shared/classes directory of your Alfresco installation. If you are not using the Alfresco-bundled version of Tomcat then you may need to create this directory yourself and configure Tomcat’s shared classloader to use it.
First you will need to configure the repository with your custom model definition. In my case I am using a simple knowledge base model that defines a single aspect kb:referencable. The aspect adds a new text property that allows a unique KB reference number to be added to documents.
First, the Spring configuration defined in alfresco/extension/kb-model-context.xml

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
    <!-- Registration of new models -->
    <bean id="extension.kb.dictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap">
        <property name="models">
            <list>
                <value>alfresco/extension/kb-model.xml</value>
            </list>
        </property>
    </bean>
     <bean id="extension.kb.resourceBundle" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
       <property name="resourceBundles">
          <list>
             <value>alfresco.messages.knowledgebase</value>
          </list>
       </property>
    </bean>
</beans>

Then, define the model itself in alfresco/extension/kb-model.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- Definition of Knowledge Base Model -->
<model name="kb:knowledgebase" xmlns="http://www.alfresco.org/model/dictionary/1.0">
   <!-- Optional meta-data about the model -->
   <description>Knowledge Base Model</description>
   <author>Will Abson</author>
   <version>1.0</version>
   <!-- Imports are required to allow references to definitions in other models -->
   <imports>
      <!-- Import Alfresco Dictionary Definitions -->
      <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
      <!-- Import Alfresco Content Domain Model Definitions -->
      <import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
   </imports>
   <!-- Introduction of new namespaces defined by this model -->
   <namespaces>
      <namespace uri="http://www.alfresco.com/model/knowledgebase/1.0" prefix="kb"/>
   </namespaces>
    <aspects>
      <!-- Definition of new Content Aspect: Knowledge Base Document -->
      <aspect name="kb:referencable">
         <title>Knowledge Base Referencable</title>
         <properties>
            <property name="kb:documentRef">
               <type>d:text</type>
            </property>
         </properties>
      </aspect>
   </aspects>
</model>

The last file in our model definition adds some i18n labels for the aspect and property names. Add the following content to the file alfresco/messages/knowledgebase.properties.

# Custom knowledge base messages
kb_knowledgebase.property.kb_documentRef.title=KB Reference
kb_knowledgebase.aspect.kb_referencable.title=Knowledge Base Referencable
aspect.kb_referencable=Knowledge Base Referencable

With the model added the repository should start up without errors and will know about the new aspect. In order to use it, we need to configure Share to show this aspect in the Manage Aspects dialogue and to display the KB Reference field in forms, when a node has the aspect applied.
The file alfresco/web-extension/share-config-custom.xml can be used to do both these things.

<alfresco-config>
   <!-- Document Library config section -->
   <config evaluator="string-compare" condition="DocumentLibrary">
      <!--
         Used by the "Manage Aspects" action
         For custom aspects, remember to also add the relevant i18n string(s)
            cm_myaspect=My Aspect
      -->
      <aspects>
         <!-- Aspects that a user can see -->
         <visible>
            <aspect name="cm:generalclassifiable" />
            <aspect name="cm:complianceable" />
            <aspect name="cm:dublincore" />
            <aspect name="cm:effectivity" />
            <aspect name="cm:summarizable" />
            <aspect name="cm:versionable" />
            <aspect name="cm:templatable" />
            <aspect name="cm:emailed" />
            <aspect name="emailserver:aliasable" />
            <aspect name="cm:taggable" />
            <aspect name="app:inlineeditable" />
            <aspect name="kb:referencable" />
         </visible>
         <!-- Aspects that a user can add. Same as "visible" if left empty -->
         <addable>
         </addable>
         <!-- Aspects that a user can remove. Same as "visible" if left empty -->
         <removeable>
         </removeable>
      </aspects>
   </config>
   <!-- cm:content type (existing nodes) -->
   <config  evaluator="node-type" condition="cm:content">
      <forms>
         <!-- Default form configuration used on the document details and edit metadata pages -->
         <form>
            <field-visibility>
               <show id="kb:documentRef" />
            </field-visibility>
         </form>
         <!-- Document Library pop-up Edit Metadata form -->
         <form id="doclib-simple-metadata">
            <field-visibility>
               <show id="kb:documentRef" />
            </field-visibility>
            <edit-form template="../documentlibrary/forms/doclib-simple-metadata.ftl" />
         </form>
         <!-- Document Library Inline Edit form -->
         <form id="doclib-inline-edit">
            <field-visibility>
               <show id="kb:documentRef" />
            </field-visibility>
         </form>
      </forms>
   </config>
</alfresco-config>

This configuration will add the KB reference field at the bottom of the main Edit Metadata form, the pop-up edit form used in the document list view and lastly the in-line edit form used for HTML, text and XML content (introduced in Alfresco 3.3).
Note: More advanced control is possible over the placement of the field within the form, but this requires copying over the full form definitions for the cm:content type from the file alfresco/web-framework-config-commons.xml (or alfresco/share-form-config.xml in 3.3 onwards) inside the Share webapp and adding the attribute replace="true" on the <config> element.
Now that you’ve configured Share, you must restart Tomcat so that the changes are picked up. The application should start up and you should be able to add the aspect to some content and see the document reference field appear in forms.
The last thing to do is to add an i18n label for the Knowledge Base aspect in the Manage Aspects dialogue. To do this we need to define a small bit of Spring configuration in the file alfresco/web-extension/custom-slingshot-application-context.xml, which will wire the knowledgebase.properties file we created earlier into Share.

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
   <!-- Add Knowledge Base messages -->
   <bean id="webscripts.kb.resources" class="org.springframework.extensions.surf.util.ResourceBundleBootstrapComponent">
      <property name="resourceBundles">
         <list>
            <value>alfresco.messages.knowledgebase</value>
         </list>
      </property>
   </bean>
</beans>

In versions prior to Alfresco 3.3 (when some changes were made to the Share resource bundle classes) the following configuration must be used instead (note the different class name)

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
   <!-- Add Knowledge Base messages -->
   <bean id="webscripts.kb.resources" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
      <property name="resourceBundles">
         <list>
            <value>alfresco.messages.knowledgebase</value>
         </list>
      </property>
   </bean>
</beans>

This configuration tells Share to look in the file knowledgebase.properties for aspect labels, in addition to the core message bundles.
With that file added you should be able to restart Tomcat again and see the correct label in the Manage Aspects dialogue. You’ve now fully-customised Alfresco Share to support additional custom aspects.
Update: Thanks to Brian Ochs, who pointed out that the additional message aspect.kb_referencable is also required in knowledgebase.properties.
Update: The configuration files in this tutorial can now be downloaded in ZIP format. To use them directly extract the archive into tomcat/shared/classes and restart the server. Please do not use these files, which are now outdated.

Share Extensions Build Script

Update, 4th May 2011: The latest version of the build script and sample Eclipse project is now hosted on Share Extras. For more information please see the Sample Project page.
With Kev’s recent SpringSurf changes in the 3.3 code line, Share extensions are now much easier to deploy as shared libraries. However, it’s still up to individual developers to set up their own project structure and to package this up as a JAR.
So, to build the latest Site Tags dashlet using this method I put together an Ant build script, which hopefully will be useful to others.
As well as building a JAR it also supports the unofficial ZIP structure that we’d used previously to package up sample dashlets, in addition to the AMP format that we use for the more complex DoD extensions for Share.
The build script assumes a standard Alfresco project layout as follows

 /config - all web-tier configuration files, with a top-level 'alfresco' package
 /web - all static resources, e.g. CSS and JS files

You can use the build script with your existing projects if they fit this structure, or you can use my own zipped-up site tags dashlet project as a template for your own. This should import into a fresh Eclipse project but you could extract it elsewhere.
Once your project is set up and you’re ready to package up your extension, you can run the following command to build the JAR.

 ant -Djar.name=my-sample-project.jar package-jar

To package up a ZIP or AMP, substitute ‘jar’ in the parameters to ‘zip’ or ‘acp’. Easy, eh?

What are we educating for?

The other night I went to my first real unashamedly political event, a session organised by the “progressive conservative” bright blue (so new they’re yet to appear on Google) at the British Library, discussing the role of education in 21st century Britain.

The event deliberately went back to first principles on education, focussing on why we bother to send people to school/college/university in the first place, then going on to look at how we need to change our current approach. Hence the title, above.

The speakers really made the event for me, especially as Toby Young’s presence on Newsnight last week had motivated me to put down my own thoughts on the matter.

It must be said that Anthony Seddon generally out-shone Young in terms of stage presence, but the former came across much more so as someone who really wants to make a difference, rather than one who simply offers a critique of current policy.

The two were united in their criticism however, with Young pointing to various studies which have shown how measures such as social mobility (which, if nothing else, education should surely aim to improve?) have painted a worsening picture over the previous 50 years, and Seddon lambasting targets and exams in encouraging a sort of herd-like behaviour, where all effort is focussed on the short-term goal of achieving the best score, rather than in maximising purer academic performance.

As the arguments were developed further, the point was made that the education system focuses too heavily on teaching children to recall facts, rather than to develop the sort of critical thinking and logical reasoning required in today’s fast-moving world. PSE in particular was noted as form of indoctrination, where pupils are taught item-by-item what is right and wrong, rather than being given the chance to decide this for themselves.

There were disagreements between audience members and the panel and at times between the two speakers themselves over how best to formulate an overall educational policy and how to measure it’s outcomes, but there was almost universal agreement that the current system which has served us so well for the previous century is now looking increasingly out-dated, and that despite the personal interest of two PMs and many more high-profile education ministers, the massive investment made over the last few years have not delivered the improvements hoped-for.

The argument was therefore presented that another approach is needed. Seddon was particularly critical of policy for deliberately excluding parents from the education process, arguing that schools and parents must be in line with each other and that a failure to bring the two sides together causes alienation reduces the sense of belonging. Young’s school, if successful, could change this.

As Seddon surmised at the end, we are all common stakeholders in the process of education. We have all been educated, and many people have or may in the future have children who will go through the same process. Current circumstances provide a once-in-a-generation chance to get things right this time, and so now is the time to act.

http://www.brightblueonline.com/

Can Sweden teach us a lesson?

Interesting report on last night’s Newsnight over Tory “dreams” to bring a Swedish-style schools system to the UK, with independent groups receiving funding to operate within the state sector to increase competition and choice.

Radio 4 covered the same topic recently, interviewing actor Toby Young about why he wants to set up a new school here in Ealing. According to Young,

“Our parent group, which is about 250-strong, would apply to be the main sponsor of a new academy… Ours would be the first parent-sponsored academy.”

Doing a bit more digging, it seems Young kicked things off last year with an opinion piece in the Observer. The Ealing Gazette picked this up shortly afterwards and put a local twist on it and more recently, the Guardian and Newsnight re-opened the debate with their own further coverage on the subject.

Labour have taken us so far down the academies route, and (perhaps surprisingly) the juggernaut shows no sign of stopping.

But this is a scheme dreamt up in the heady days of the boom years. Underlying the academies scheme is a belief that the state (in combination with suitably wealthy donors) can play a central role in tearing down the old and replacing it with shiny new facilities, with scant regard for what is there already. Academies have done for the education system what the 1960s did for urban planning, to the extent that bodies like English Heritage now feel compelled to issue warnings.

In Education as in IT, the days of the all-enabling state are well-and-truly over. People have lost patience and the system has run out of money.

As Young’s example shows there are a huge number of people on the ground who think they can do it better and are motivated to do so. These are parents, teachers and others in the community who want to take back some of the control that’s been taken away from them over the last 60 years, first by central government and the LEAs and more recently by the academies. As Antony Seldon says in the Radio 4 piece:

“We’ve had a pretty state-run system for the last 100 years where schools have been run from the centre… and parents have been marched off to go to this school and it’s been pretty ordinary… We need to abandon the factory schools that served us so well in the 20th century and move towards a much more individualised system…”

The Newsnight report shows that the Swedish example is not perfect. Standards must be enforced (this being an ideal  role for the state) and non-profit status should be required for any organisation wanting to set up a new school, but bearing these in mind we can surely do things better by opening the process up further and allowing “individuals and organisations to flourish”.

A Limited Revolution

I’ve spent the last couple of days catching up on recent news, including some of the iPad stuff from the other week.

A few blog posts have pointed out that the people talking about it are probably not the target market for the device, and in any case they’re likely to already own a half-decent laptop/desktop and probably an iPhone too.

But the separation is not just about geeks versus non-geeks, it’s about creators and consumers.

Consumers in the classic sense will love the iPad. It’s designed for browsing the web, reading e-books, listening to music and watching movies. Though iTunes, Apple already makes plenty of dollars from these people and will likely be even more successful with a larger device. A bigger screen means more pixels, and HD means more money.

But for those of us who like to think of ourselves as creators the iPad, with it’s locked-down OS and lack of third party app store support, is not such a good thing. Nor is it helpful if we want to provide the next generation with the tools they need to think creatively and independently, and to encourage them to hack at and mash-up software, services and content.

The iPad may well transform home computing, but that will be as far as the revolution goes. We need more open platforms that we can build on if we’re going to carry on innovating, not another device to lock us further into Apple’s walled garden, and their vision of how computing should be.

Opportunistic Alfresco

Great post from Jono at Ubuntu on making the Ubuntu platform better suited to opportunistic development, basically helping people who want to ‘scratch an itch’ but don’t have the time or commitment to write an application in the traditional sense.
This is the first time I’ve come across a term for this sort of activity, but it describes very well what Apple have done so well with their app store, and what many of us hope Google may be about to do better.
The faster development cycle afforded with open source means that such an environment is much better suited in theory to rapid application development than proprietary platforms, but it does require the necessary infrastructure to be in place.
I’ve been having thoughts recently on how we could better support this type of development within Alfresco. Like the iPhone and like Ubuntu, Alfresco is much more than just a product, it’s a solid platform for others to develop on.
In our case the building blocks are the web scripts and Surf frameworks that we recently fed back up into Spring, and the delivery mechanism could easily be provided by the Share UI.
Share already allows developers to define self-contained bits of functionality as dashlets, and also provides mechanisms to manage the configuration and display of these dashlets within the application. That’s 90% of our intrastructure already there.
The missing 10% is a mechanism by which developers can publish these extensions to make them available for others, basically a distibution channel, or if you must, an app store.
This is why I’ve started spec’ing out what I’m tentatively calling Share Components, to provide a directory of Share extensions and an easy mechanism for users to query such directories (note, plural) and install components from them onto their own Alfresco systems.
I’m making it very clear that this is a proposed feature for Share but nonetheless it’s one that I firmly believe is critical to ensuring that others – including our partners and others in the wider community – are able to join up their opportunistic dots using the Alfresco platfom.