Share Document Previews in Alfresco 4

One of the improvements introduced in Share’s Document Library as part of Alfresco 4 was the refactoring of the web-preview component, which is responsible for rendering the in-line document views in the Document Details page.
These preview capabilities are one of the core strengths of Share, and now these changes allow developers and administrators to tweak the capability as they need.

Flash Document Previewer

Web preview component in Alfresco 4 for a PowerPoint Presentation

The Basics

Let’s start with a quick overview.
The web-preview component is similar to most other Share components. The page component itself is rendered by a web script, which is responsible for loading some basic information about the document being requested, and outputting this into the markup that then forms part of the overall page.
The output does not render HTML directly, but rather instantiates a client-side component Alfresco.WebPreview, passing over the document metadata as an object literal.
Previously, that was it. The logic to set up the Flash previewer was contained within Alfresco.WebPreview, and that was what you got for all documents and images. Anything else that couldn’t be transformed by the repository to a SWF file would not be previewed.

What’s Changed?

Alfresco 4 changes that. Now, the rendering of content items is separated from Alfresco.WebPreview and instead performs the work via a series of plug-ins. The component still takes care of loading the document metadata, setting up the basic screen layout and working out which plug-ins can be used to render the content on-screen. This has several benefits –

  • System administrators can decide which plug-ins should be used under which circumstances, via configuration.
  • Plug-ins are chained together in the configuration. If one fails, then the other available ones are tried in turn, until one succeeds.
  • The implementation of the plug-ins by developers does not need to touch any core files. They are tied into the component via configuration alone.

What’s more, Share comes with a number of different plug-ins already present. It’s likely that you will still see the Flash document previewer for most of the content you look at, but you should notice that by default images are now rendered as real images within the page, and will be automatically resized if they are too big. That’s because images are now handled by a different plug-in, but you can still use the Flash previewer if you reconfigure the component.

Image Viewer

Web preview component in Alfresco 4 for a JPEG image


Other previewers are provided for displaying audio and video content using Strobe Media Player or FlashFox, plus direct playback of supported formats using the HTML5 <video> and <audio> tags. Take a look in the directory components/preview within the Share webapp and you will see them there. It’s worth looking at the source code if you’re thinking of implementing your own custom previewers, as many of them are surprisingly simple.

Configuring Plug-ins

The configuration that dictates which plug-ins are used under what circumstances is contained in the web script configuration file web-preview.get.config.xml. You can find this in the Share webapp in the directory WEB-INF/classes/alfresco/site-webscripts/org/alfresco/components/preview and view it there, but if you’re planning on making any changes you should first copy it into tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/preview (you may need to create the directories below web-extension).
You should see that the config is expressed as a series of conditions, each of which contains a list of plug-ins to attempt to use. For each condition that matches, each of the plug-ins within it is then added to the list to try to use.
Here’s an example for MPEG-4 video files

<condition mimeType="video/mp4" thumbnail="imgpreview">
    <plugin poster="imgpreview" posterFileSuffix=".png">StrobeMediaPlayback</plugin>
    <plugin poster="imgpreview" posterFileSuffix=".png">FlashFox</plugin>
    <plugin poster="imgpreview" posterFileSuffix=".png">Video</plugin>
 </condition>

The <condition> element’s attributes form sub-conditions, which are AND-ed together to give the overall result. In this case, the condition will match when the content being viewed has the MIME type video/mp4 and the thumbnail with the name imgpreview can be generated (note, it may not yet have been generated) via the thumbnail service. Other conditions will only test one of these sub-conditions, but here we test both.
Each of the <plugin> elements then provides the name of the plug-in in the element body and any configuration attributes required as element attributes. In this case, each of the plug-ins is being configured to use the imgpreview thumbnail as the static ‘poster’ image to display in the player before the play button is pressed, and to assume this image is in PNG format. Note, these attributes will be different for each plug-in since they are mapped to the options object on the plug-in implementation.
As an administrator, within this file you can add, remove and re-order plug-ins or conditions as you need, change the conditions or change the configuration that is passed to the plug-ins at instantiation time.

Implementing Your Own

It’s easy to create your own plug-ins to render content in interesting and different ways. A plug-in is implemented as a single JavaScript class, which must meet the following simple criteria

  • The name of  its constructor must be the same as the value used in the <plugin> configuration element to tie it into the component, and must be defined as a child property of Alfresco.WebPreview.prototype.Plugin. By convention CamelCase is used for the name, e.g. WebPreviewer, FlashFox, Image.
  • The object prototype must define an object literal named options and two functions report() and display(). See the in-line JSDoc of the out-of-the-box plug-ins for full details of the method signatures and return types required.

You will also need to ensure that the client-side file where you define the plug-in object is included in the <head> section of the Document Details page, you can do this as an administrator by overriding the web-preview.get.head.ftl template, or (better) as a developer by extending the .head.ftl template using a customization module applied to the web-preview.get web script.

New Viewers on Share Extras

Keep an eye out for the upcoming release of the (to-be-renamed) Media Previews add-on in Share Extras. Peter has been instrumental in helping to build up some new plug-ins for viewing different types of content that we hope can be used to remove the Flash dependency that still remains in Share for viewing document-based content. They should also provide some further examples of how you can define your own, custom, plug-ins.

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 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

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

From Web Scripts to Portlets with Alfresco 3.4

Since Alfresco 3.2r the ProxyPortlet support in Alfresco Share has allowed developers to easily embed specific bits of Share functionality into a portal such as Liferay, using only a small amount of XML configuration to wire in existing web scripts.
This support has been substantially improved in version 3.4 of Alfresco, in order to allow the entire Share Document Library page to be embedded within a portal. Unfortunately the changes mean that the steps in Luis’s original tutorial no longer work in the latest version.
As one of the features we demonstrated today at our Madrid event was the Doclib portlet, I managed to get five minutes to get the original web script-backed method working too.
Since the CMIS repository browsing web scripts used in Luis’s example are no longer shipped with Share, I used my own Hello World dashlet from share-extras as a starting point instead. The web script is basic, but demonstrates displaying a simple greeting to the user including their user name.
Hello World Portlet
The following steps should work using a recent version of Alfresco 3.4 and Liferay 5.2.3 running as the portal, provided that the two components are first set up as per the Installing and Configuring instructions for the Doclib Portlet.
Once you’ve set everything up, the first thing to do is to add the web script files to the instance of Share that you have already deployed to Liferay. Since you should already have created some directories in Liferay’s tomcat-x.x.x/shared/classes directory to define your share-config-custom.xml, the easiest thing is to create a new directory named site-webscripts within the existing tomcat-x.x.x/shared/classes/alfresco/web-extension and place the following files in it.
org/alfresco/test/hello-world.get.desc.xml

<webscript>
   <shortname>Hello World</shortname>
   <description>Displays Hello World text to the user</description>
   <authentication>none</authentication>
   <url>/test/hello-world</url>
</webscript>

org/alfresco/test/hello-world.get.html.ftl

<html>
   <head>
      <title>${msg("header")}</title>
   </head>
   <body>
      ${msg("label.hello", user.id)}
   </body>
</html>

org/alfresco/test/hello-world.get.properties

header=Hello World!
label.hello=Hello {0}

With those files added, you’ve successfully defined the web script that we’ll wire into Share in the next section.
Now although the web script itself will be automatically picked up by Share at load-time, some additional config is also needed in web.xml for the ProxyPortlet to work in version 3.4.
The following lines, which define a custom servlet and servlet mapping which will be invoked by the ProxyPortlet, should be placed in the web.xml file belonging to the Share instance which has been deployed in Liferay. You should find the path to this will be something like <LIFERAY_HOME>/tomcat-6.0.18/webapps/share/WEB-INF web.xml (if you have not already started Liferay you will need to do so to force it to deploy share.war and create this structure).

   <servlet>
      <servlet-name>HelloWorld</servlet-name>
      <servlet-class>com.liferay.portal.kernel.servlet.PortletServlet</servlet-class>
      <init-param>
         <param-name>portlet-class</param-name>
         <param-value>org.alfresco.web.portlet.ProxyPortlet</param-value>
      </init-param>
      <load-on-startup>0</load-on-startup>
   </servlet>
   <servlet-mapping>
      <servlet-name>HelloWorld</servlet-name>
      <url-pattern>/HelloWorld/*</url-pattern>
   </servlet-mapping>

You can place these two definitions anywhere within the top-level <web-app> element, but for consistency I always try to add them next to the existing <servlet> and <servlet-mapping> definitions.
Now you’ve done all you need to do to configure the scripts, we can move onto configuring the matching portlet definition which will be picked up by Liferay.
In the same WEB-INF directory where you modified web.xml you should find a file named portlet.xml, to which we add our new definition.

   <portlet>
      <description>Hello World</description>
      <portlet-name>HelloWorld</portlet-name>
      <portlet-class>org.alfresco.web.portlet.ProxyPortlet</portlet-class>
      <init-param>
         <name>viewScriptUrl</name>
         <value>/page/test/hello-world</value>
      </init-param>
      <supports>
         <mime-type>text/html</mime-type>
         <portlet-mode>VIEW</portlet-mode>
      </supports>
      <portlet-info>
         <title>Hello World</title>
         <short-title>Hello World</short-title>
      </portlet-info>
      <security-role-ref>
         <role-name>administrator</role-name>
      </security-role-ref>
      <security-role-ref>
         <role-name>guest</role-name>
      </security-role-ref>
      <security-role-ref>
         <role-name>power-user</role-name>
      </security-role-ref>
      <security-role-ref>
         <role-name>user</role-name>
      </security-role-ref>
   </portlet>

Add this right after the existing <portlet> definitions (which if you look at further it should be obvious define the three Doclib portlets) and save your changes.
Getting these details right is crucial if you’re deploying your own web scripts, so a couple of notes on this are probably useful.

  1. The contents of the <portlet-name> element must match the name of the servlet mapping you have defined in web.xml
  2. The viewScriptUrl parameter must match the URL of your web script, with a prefix of /page added to the beginning (note that in 3.2r the web app context path was also required in the URL, but this now causes an error if supplied)

Lastly you should add the portlet to the two Liferay-specific configuration files in WEB-INF to ensure that authentication is handled correctly and also that the portal appears in the correct category in Liferay’s Applications menu.
In liferay-portlet.xml add the following definition after the existing <portlet> elements

   <portlet>
      <portlet-name>HelloWorld</portlet-name>
      <user-principal-strategy>screenName</user-principal-strategy>
   </portlet>

In liferay-display.xml, add the following within the existing <category> element – it should be obvious that this is adding your portlet to the ‘Alfresco’ category.

<portlet id="HelloWorld"></portlet>

You’ll need to restart Liferay to get it to pick up the new portlet, and for Share in turn to load the additional web script. Once it’s finished loading you should be able to follow the configuration steps in the Doclib Portlet guide to walk you through adding it to a page.
The example is basic, but shows how you can add a web script as a portlet, with a small amount of personalisation based on the identify of the user.
It’s possible to add more complex web scripts, for example to load data from the Alfresco repository or other back-end data sources, but as Luis points out you should be careful how you render any hyperlinks within your scripts, to ensure that they are portal-safe.

To make sure that your URLs are correctly generated, please use the “scripturl()” function in your Freemarker templates to wrap them:
<a href=”${scripturl(url.serviceContext + “/sample/cmis/repo”, false)}”>CMIS Repository</a>

You can download the web script files used in this example in JAR format, which you can extract using any unzip program into the directories specified above (or even easier – simply drop the JAR file itself into Liferay’s tomcat-x.x.x/shared/lib folder).

Share Import-Export 1.1 released

The v1.1.1 release of Share Import-Export has been up for a few days now, but I wanted to summarise some of the changes in the new version.
The most significant addition is support for importing and exporting security groups in JSON format, via the new import-groups.py and export-groups.py scripts.
As well as this the import-users.py script has been made slightly more flexible, with the addition of a --users argument to allow you to import just a few users from a larger set. Since the sample data contains a large number of users that are used across all the different sample sites, you can now import just the users you need for a particular site.
As well as these functional improvements I’ve started cleaning up the code internally, an area I intend to focus a little more on over the next few weeks. For now I’ve just cleaned up the docstrings within each script, and updating the --help flags to re-use the usage information in there.
Last but not least, I should thank Dick from Formktek for reporting an issue with the user export script, which was causing some exported profile images to become corrupted when saved.
Beyond a few more tidy-ups the code is almost where I want it to be within the current constraints of the repository. But there have been a couple of ideas suggested for future uses of the scripts, so if there’s a particular purpose you think the scripts could have or you just want to share your experiences, please leave a comment below.

Refreshing Site Tags in Share

Sometimes the site tag data used in Share’s document library can get out of sync with the tags on the content itself, especially in older versions of the 3.x product.
I came across this today on the Partner Sales Enablement site that we’ve just launched fully today on partners.alfresco.com, but fortunately the tag scope data is easy to rebuild.
To do this, you need to create a small JavaScript file that will execute the ‘refresh-tagscope’ action against a space.

var refresh = actions.create("refresh-tagscope");
refresh.execute(space);

Once you’ve created this file (I called mine refresh-tagscope.js) in Data Dictionary > Scripts, use Alfresco Explorer to navigate into the documentLibrary space within the site you want to refresh, and click View Details from the More Actions menu.
You can then use the Run Action dialogue to execute the JavaScript file you’ve just created. You might also want to do the same for the site space itself, which also collects tag scope data.
Once that’s done you should be able to navigate back into the site document library in Share and you should find that the tag data has been fully refreshed!

Site Blog Dashlet for Alfresco Share

Updated December 2010: Site Blog Dashlet is now hosted on Share Extras.
This extension to Alfresco Share provides a custom Site Dashlet, which displays the most recent ten posts from the site blog component.

Site Blog Dashlet

Site Blog Dashlet


To install the dashlet download the ZIP file package the latest blog-dashlet.jar file from the Share Extras project and extract the contents into the tomcat directory of your Alfresco installation drop it into tomcat/shared/lib or WEB-INF/lib inside the Share webapp. The package will install the dashlet web script and a single CSS file.
Also pictured in the screenshot is the Site Tags Dashlet, which displays a tag cloud visualisation for all the tags within a site.

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