Sunday, 15 April 2012

Adding Document Library Actions in Alfresco 4.0

This is an update of the “Adding Document Library Actions inAlfresco 3.4” article to work with version 4.0 development API changes and configuration changes.

In many Alfresco extension projects you want to customize the Document Library in Alfresco Share. And quite often this involves adding new actions that can be applied to the content in the library. Document Library actions does not use Web Scripts to implement their business logic, instead they hook into the client-side actions framework using custom JavaScript.
Each action has a small 16×16 icon, one or more text labels, and configuration to hook them into the application. Most actions by their nature do something and it’s likely that they will make a RESTful call back to the repository to perform their work, which may require a custom Web Script there.
For the purpose of demonstrating how to add document library actions we will add one action that can be used to send documents as attachments in an email. The send email action will be available in both the Document Library Browse view and in the Details view. We will implement two versions of this action, first one that does not take any input from the user and then another version of the action that displays a form to collect subject and body text for the email.
Document Library actions that should be visible in the document browse view are added via configuration in the share-config-custom.xml file in your Share JAR extension project.
The following picture shows what page and actions we are talking about:

Document Library actions that should be visible in the document details view are also added via the share-config-custom.xml file.
The following picture shows what actions we are talking about here:

The way custom Document Library actions are configured in version 4.0 has been greatly improved. No longer do you have to override files belonging to the out-of-the-box Web Scripts document-actions.get.* and documentlist.get.*. This makes it possible to have more than one extension project adding actions to the Document Library, which was previously a problem.
You can download the source code for this article from here.
For more information about the build project that I use and its directory structure see this blog entry.

Implementing a Send As Email action

The following sections go through how to implement the “Send As Email” document library action.
The steps are as follows:
1.      Configuring the Action
2.      Adding a custom Icon
3.      Adding an Evaluator
4.      Adding a Status Indicator
5.      Implementing the Client Side Java Script code
6.      Implementing the Web Script that sends the doc with an email
7.      Testing the Web Script
8.      Testing the Action

Configuring the Action

The first thing we are going to do is tell Alfresco about the new email action that we want to add to the document library. In Alfresco 4 this is done via the share-config-custom.xml configuration file. This file is available in the extension project directory trunk\_share\config\alfresco\web-extension.
Open up the share-config-custom.xml file and add the send email action as follows to the DocLibActions configuration section, this configuration section probably does not exist in your shar-config-custom.xml file so you would need to add it:
<config evaluator="string-compare" condition="DocLibActions">
      <action id="mycompany.doclib.action.sendAsEmail"
         <param name="function">onActionSendAsEmail</param>
         <param name="successMessage">message.sendAsEmail.success</param>
         <param name="failureMessage">message.sendAsEmail.failure</param>
         <evaluator negate="true">mycompany.evaluator.doclib.action.isEmailed

The different attributes and sub-elements for the action element have the following meaning:

The global identifier for this action. Is used when you refer to this action in other parts of the configuration.
Alfresco will look for an icon that starts with this name and
ends with “-16.png”. So it will look for email-16.png in our case. Alfresco expects the image file to be located in the /components/documentlibrary/actions directory.
If not set, the id is used.
Sets the type of action; this can be either javascript (as in our example) if you want the action to execute some Java Script code, link if you want to invoke some URL, or pagelink if you want to invoke a URL within the Share web application.
Points to a property name in a resource file where the value of this property will be displayed in the UI as the action’s label.
There can be zero or more parameters set for an action. In case of a javascript action they will be passed into the Java Script code, there is one special parameter with the name function that sets the Java Script function that should be called when the action is executed. In case of a link action the parameters would typically be used to specify href and target. In case of a pagelink action a page parameter is be used to specify a relative URL within the Share web application.
Spring Bean id for an evaluator that is called by the system to find out if the action should be visible or not in the UI. An evaluator extends the org.alfresco.web.evaluator.BaseEvaluator class. You can negate the result of calling the evaluator by setting the negate attribute to “true”.
The action is added to the sub-section <actions> but we also need to add configuration for it in another sub-section called <actionGroups>.  In this sub-section we configure in what document library views the action should be visible and where in the list of actions our action should be displayed (ordering).

The following table shows available actionGroups:

Action Group Id
Default usage
Documents on the browse page
Document on the document details page
Folders on the browse page
Folders on the folder details page
Links to documents on the browse page
Links to documents on the document details page
Links to folders on the browse page
Links to folder on the folder details page

We want the “send as email” action to be visible in the document-browse and document-details views, this is configured as follows:

      <actionGroup id="document-browse">
         <action index="400" id="mycompany.doclib.action.sendAsEmail" />
      <actionGroup id="document-details">
         <action index="400" id="mycompany.doclib.action.sendAsEmail" />

The index argument is specifying the order of this action in the list of actions. The higher the number the lower it will be displayed in the action list. By having a look in the share-documentlibrary-config.xml configuration file located in the alfresco\tomcat\webapps\share\WEB-INF\classes\alfresco directory of your Alfresco 4.0 installation, you can find out that the highest index for document-browse actions is 320 and for document-details actions 350. So if we set our index for the “send as email” action to 400 it should end up last in both of these action lists.

If you want more examples of how document library actions can be configured have a look in the share-documentlibrary-config.xml file and the DocLibActions section.

Add the action label and messages to the file located in the _share\config\alfresco\messages directory of the build project:

actions.mycompany.sendAsEmail=Send as Email
message.sendAsEmail.success='{0}' successfully sent in email to {1}.
message.sendAsEmail.failure=Couldn't send '{0}' in email to {1}.

We also add success or failure messages that will be displayed from the JavaScript code depending on if it was possible to send the email or not.

If we build the extension now, and deploy into Alfresco Share (i.e. by running the deploy-share-jar ant target in the build project), then we would see something like this in the Document Library:

As we can see, there is no email icon for the action and the log will also print out a message such as the following:

2012-04-08 10:25:49,037  WARN  [web.scripts.ActionEvaluatorHelper] [http-8080-17] Evaluator 'evaluator.doclib.action.isEmailed' not found.
We will fix the icon next and then the evaluator.

Adding a custom Icon

The icons for all the document library actions are stored in the tomcat\webapps\share\components\documentlibrary\actions directory in your Alfresco 4 installation. The system will try and load any custom document library action icons from this directory. Icons are loaded via the resource Servlet and action icons related to the document library are loaded with the http://localhost:8080/share/res/components/documentlibrary/actions/<icon>-16-png URL.  

Copy the eml.gif icon from the Alfresco Explorer web application tomcat\webapps\alfresco\images\filetypes directory to the extension trunk\_share\config\META-INF\components\documentlibrary\actions directory (note that you might need to create some of these directories) in the build project.

The icon needs to be in PNG format with the file name ending in -16.png so convert the image from GIF format to PNG format and delete the GIF file as follows:

C:\work\blogging\trunk4\_share\config\META-INF\components\documentlibrary\images>c:\Alfresco4\imagemagick\convert.exe eml.gif email-16.png
C:\work\blogging\trunk4\_share\config\META-INF\components\documentlibrary\images>del eml.gif

There is no other icon in the /components/documentlibrary/actions directory with the name email-16.png so we can use it. When you add new custom icons make sure you give them file names that do not clash with any names that are already taken by out of the box icons.

We now got a resource directory structure with the actions directory as follows:

If we build the extension now we should see something like this:

Adding an Evaluator

For demonstration purpose we will implement the action so it sets the cm:emailed aspect on the document after it has been sent in an email. This will then be checked by the evaluator, which will disable the action if the document has the cm:emailed aspect applied.
There are three parts to setting up an evaluator for a document library action:
1)      Configure it with the <evaluator> element in the action configuration in share-config-custom.xml (We have already done this)
2)      Create a Java class that extends the org.alfresco.web.evaluator.BaseEvaluator class
3)      Define a spring bean with an id matching the <evaluator> configuration element’s value and then set the class for the Spring bean to the one implemented in step 2
The Java class that we will use looks like this:
package com.mycompany.cms.documentlibrary.action.evaluator;

import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.web.evaluator.BaseEvaluator;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

public class CheckIfDocIsEmailedEvaluator extends BaseEvaluator {
    private static final String ASPECT_EMAILED = "cm:emailed";

    public boolean evaluate(JSONObject jsonObject) {
        try {

            JSONArray nodeAspects = getNodeAspects(jsonObject);
            if (nodeAspects == null) {
                return false;
            } else {
                if (nodeAspects.contains(ASPECT_EMAILED)) {
                    return true;
                } else {
                    return false;
        } catch (Exception err) {
            throw new AlfrescoRuntimeException("JSONException whilst running action evaluator: " + err.getMessage());

The evaluate method gets a JSON object passed in from which you can get all the information you need about the node that the action is being applied to. Here we use the getNodeAspects method to get all the aspects that have been applied to the node (for more methods look in the BaseEvaluator class). Then we just check if the cm:emailed aspect has been applied to the node.

Next thing we need to do is define a spring bean for this evaluator, this is done in the custom-slingshot-application-context.xml file (note. Alfresco Share was previously called Slingshot):

<bean id="mycompany.evaluator.doclib.action.isEmailed"          class="com.mycompany.cms.documentlibrary.action.evaluator.CheckIfDocIsEmailedEvaluator" />

To try this out just go into the document library and apply the cm:emailed aspect to a document via the UI action Manage Aspects in the document details page. Then make sure the “Send As Email” action is not visible for that document.

There are also a number of predefined evaluators (i.e. out of the box evaluators ready to use):

·        Has aspect
·        Is mimetype
·        Property not Null
·        Site preset
·        Site / No Site
·        Container Type
·        Node Type
·        Always false
·        Value-based
·        Metadata value
·        Is Browser (type)
·        Is Portlet mode

See the slingshot-documentlibrary-context.xml file located in the alfresco\tomcat\webapps\share\WEB-INF\classes\alfresco directory for more information about out-of-the-box evaluators.

Adding a Status Indicator

Sometimes you might want to know if a document has been emailed without going in and checking if the cm:emailed aspects has been applied. This can be achieved by adding a so called status indicator. An indicator is displayed in the Document Library browse view and leverages the work we have already done with the evaluator.
There are three parts to setting up an indicator for a document library action:

1)                Make sure you got an <evaluator> element in the action configuration in share-config-custom.xml (We have already done this) and that this evaluator has been implemented (We have already done this)
2)                Add an indicator configuration to the DocumentLibrary section config in share-config-custom.xml
3)                Add i18n label to property file
4)                Add an image to be used as indicator to the components/documentlibrary/indicators directory

The indicator configuration in share-custom-config.xml points to the evaluator previously implemented and looks like this in the DocumentLibrary section:

    <config evaluator="string-compare" condition="DocumentLibrary">

            <indicator id="mycompany.indicator.doclib.action.isEmailed"

The global identifier for this indicator.
The name of the icon to display as the status indicator. Alfresco expects the image file to be located in the /components/documentlibrary/indicators directory.
If not specified, “id” is used.
Note. In this case Alfresco does not assume *-16.png format but you have to specify the complete file name.
Is used to order the indicator in the UI if there are several indicators displayed for a document. Looking in the share-documentlibrary-config.xml (located in the share webapp folder structure) one can see that the largest index for out-of-the-box indicators are 60, so by using 100 the emailed indicator will be displayed last in the list, if there are more indicators to display.
Points to a property name in a resource file where the value of this property will be displayed in the UI as the indicators tooltip.
If not specified, “id” is used.
Spring Bean id for an evaluator that is called by the system to find out if the indicator should be visible or not in the UI.

Now update the file with the value for the label as follows:

actions.mycompany.sendAsEmail=Send as Email
message.sendAsEmail.success='{0}' successfully sent in email to {1}.
message.sendAsEmail.failure=Couldn't send '{0}' in email to {1}.
indicators.mycompany.emailed=This document has been emailed

As an indicator image we will use the same one as is used for the action. Copy the emlail-16.png icon from the trunk\_share\config\META-INF\components\documentlibrary\actions directory to the trunk\_share\config\META-INF\components\documentlibrary\indicators directory in the build project.

The indicator, including tooltip, will look like in the following screenshot when the cm:emailed aspect has been applied to a document:

Implementing the Client Side Java Script code

Next step is to include some JavaScript (client side JS) code that is called when the user clicks one the send email action, and in turn calls a Web Script that does the job of sending the email. Before doing that add a new file called email-actions.js to the extension project trunk\_share\config\META-INF\components\documentlibrary directory.

And make sure that the email-actions.js file will be loaded by the document library page by updating the DocLibCustom section in share-config-custom.xml as follows:

    <config evaluator="string-compare" condition="DocLibCustom">
             <js src="components/documentlibrary/email-actions.js" />
       <dependencies />

Note. This is a significant improvement from 3.4 where you had to override Alfresco’s documentlist.get.head.ftl Web Script file and the document-actions.get.head.ftl Web Script file. In 4.0 you can now have more than one Share JAR extension module adding document library actions that uses custom JavaScript and CSS without clashing.
The ant build script will also need to compress the JS file because the system will want to load a compressed email-actions–min.js file. The ant target to do this should look something like this:

<target name="js.minify">
        <apply executable="java" parallel="false">
            <fileset dir="." includes="**/email-actions.js"/>
            <arg line="-jar"/>
            <arg path="_share/lib/yuicompressor-2.4.8pre.jar"/>
            <arg line="-o"/>
            <mapper type="glob" from="*.js" to="*-min.js"/>

Note. This is not strictly needed as currently Alfresco doesn’t automatically add “-min” when not in debug mode for resources configured via the dependencies tag. However, in the future it might be necessary.
Now add the following Java Script code to the email-actions.js file, it will call a data Web Script that we will implement in a bit:

 (function() {"registerAction",
        actionName: "onActionSendAsEmail",
        fn: function mycompany_onActionSendEmail(file) {
                    message: this.msg("message.sendAsEmail.success", file.displayName, Alfresco.constants.USERNAME)
                    message: this.msg("message.sendAsEmail.failure", file.displayName, Alfresco.constants.USERNAME)
                    name: "mycompany/sendDocInEmail?nodeRef={nodeRef}&userName={userName}",
                    stem: Alfresco.constants.PROXY_URI,
                    method: Alfresco.util.Ajax.GET,
                        nodeRef: file.nodeRef,
                        userName: Alfresco.constants.USERNAME


The way you plug-in your JavaScript action handlers has also changed from 3.4 to 4.0, and to the better. You can now just call the"registerAction"...) method to tell the system about your new action JavaScript code and it will be plugged in after the out- of-the-box code to allow for customization and extensions (rather than as additional prototype method of Alfresco.doclib.Actions in 3.4).

In the mycompany_onActionSendEmail function we use the this.modules.actions.genericAction function to define a generic action for calling the custom Web Script that will send the email. The genericAction function is defined in the doclib-actions.js file. This function sets up a callback function based on the passed in parameters (i.e. success.message, failure.message,, etc). This callback function will then later be called when the send email action is clicked on and the Web Script call returns.

The complete URL for the Web Script will look something like this:


The call to the Alfresco Repository Web Script is proxied via Alfresco Share so authentication credentials etc are managed automatically for us (for more information about the /share/proxy/alfresco URL see this blog posting).

Implementing the Web Script that sends the doc with an email

This is the Web Script that we are calling from the YUI code in the email-actions.js file to send the document as an email attachment. Sending attachments with an email is not supported by the email action that you normally use in Java Script (i.e. var mail = actions.create("mail");). Instead we are going to use a Java-backed Web Script and the Java Mail library to send the email with the attachment.

The descriptor for this Web script should be put in the trunk\_alfresco\config\alfresco\extension\templates\webscripts\com\mycompany\sendemail directory.

Note. This Web Script will only require a descriptor and a controller implemented as a Java class. The Java class will extend the AbstractWebScript class, which does not support using also a JavaScript controller and a FreeMarker template as part of the Web Script. If you would like to use also for example a FreeMarker template together with the Java controller then the Java class should instead extend the DeclarativeWebScript class.
Let’s start with the descriptor file sendDocInEmail.get.desc.xml:

<shortname>Send Document in Email</shortname>
<description>Send document as an email attachment.
To test: curl -v -u admin:admin "http://localhost:8080/alfresco/service/mycompany/sendDocInEmail?nodeRef=workspace://SpacesStore/cbb63e68-9884-4d24-abb3-28aaf8677169&amp;userName=admin"
<format default="html"/>

There descriptor is pretty standard and says that the Web Script can be accessed via the /mycompany/sendDocInEmail URL and that it requires user authentication but no transaction (i.e. a read-only operation). Two URL parameters are required – the node reference for the document that should be sent as an attachment and the username for the Alfresco user that should receive the email.

Next up is the controller, this is a Java backed Web Script so the controller will take the form of a Java class that extends the AbstractWebScript class. The controller class is stored in the trunk/_alfresco/source/java/com/mycompany/cms/webscript directory in the build project.
The first part of the Web Script controller implementation looks like this:

public class SendDocInEmailWebScript extends AbstractWebScript {
    private static Log logger = LogFactory.getLog(SendDocInEmailWebScript.class);

    private ContentService m_contentService;
    private NodeService m_nodeService;
    private PersonService m_personService;

    public void setContentService(ContentService contentService) {
        m_contentService = contentService;

    public void setNodeService(NodeService nodeService) {
        m_nodeService = nodeService;

    public void setPersonService(PersonService personService) {
        m_personService = personService;

This defines the Web Script class as extending the out of the box abstract Web Script class and then we define the Alfresco services that we will be needing. The only method we need to implement is the execute method so we do that next, it will start by checking the URL parameters:

    public void execute(WebScriptRequest req, WebScriptResponse res)
            throws IOException {
        String userName = req.getParameter("userName");
        if (StringUtils.isBlank(userName)) {
            throw new WebScriptException("UserName parameter cannot be blank");

        String docNodeRefStr = req.getParameter("nodeRef");
        if (StringUtils.isBlank(docNodeRefStr)) {
            throw new WebScriptException("Document nodeRef parameter cannot be blank");

        // Get the user node reference and email address
        NodeRef userNodeRef = m_personService.getPerson(userName);
        Serializable emailAddress = m_nodeService.getProperty(userNodeRef, ContentModel.PROP_EMAIL);
        if (emailAddress == null) {
            throw new WebScriptException("User's email address is not specified");

        // Get document filename
        NodeRef docNodeRef = new NodeRef(docNodeRefStr);
        Serializable filename = m_nodeService.getProperty(docNodeRef, ContentModel.PROP_NAME);
        if (filename == null) {
            throw new WebScriptException("Document filename is null");
        String documentName = (String) filename;

If we don’t have the required parameters or we cannot get the person for passed in username then we throw a Web Script exception. The person is also required to have an email address specified.
Next up is the code that sends the email; we will use Java Mail for that:

        try {
            // Create mail session
            Properties props = new Properties();
            props.put("", "IXDV1210");    // localhost will not work
            Session session = Session.getDefaultInstance(props, null);

            // Define message
            Message message = new MimeMessage(session);
            String fromAddress = "";
            message.setFrom(new InternetAddress(fromAddress));
                    new InternetAddress((String) emailAddress));
            String subject = "Sending document: " + filename;

            // Create the message part with body text
            BodyPart messageBodyPart = new MimeBodyPart();
            messageBodyPart.setText("Sending a document as an attachment");
            Multipart multipart = new MimeMultipart();

            // Create the Attachment part

            // Get the document content bytes
            byte[] documentData = getDocumentContentBytes(docNodeRef, documentName);
            if (documentData == null) {
                throw new WebScriptException("Document content is null");

            // Attach document
            messageBodyPart = new MimeBodyPart();
            messageBodyPart.setDataHandler(new DataHandler(new ByteArrayDataSource(
                    documentData, new MimetypesFileTypeMap().getContentType(documentName))));

            // Put parts in message

            // Send the message

            // Set status on node as "sent via email"
            Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
            properties.put(ContentModel.PROP_ORIGINATOR, fromAddress);
            properties.put(ContentModel.PROP_ADDRESSEE, emailAddress);
            properties.put(ContentModel.PROP_SUBJECT, subject);
            properties.put(ContentModel.PROP_SENTDATE, new Date());
            m_nodeService.addAspect(docNodeRef, ContentModel.ASPECT_EMAILED, properties);
        } catch (MessagingException me) {
            throw new WebScriptException("Could not send email: " + me.getMessage());

        res.getWriter().append("Successfully sent email with document (" + filename + ") as attachment to " + emailAddress);

Here we create a Mime Message with two parts, one for the email body and one for the email attachment. Note that you need to change the value to your computer hostname (don’t use localhost as that will not work). The last thing we do after the email has been sent is to apply the cm:emailed aspect to the document Node Reference (this is new compared to the 3.4 article implementation). In the next section I will show you how to use an SMTP Server locally for testing.

We can also see that we use the custom getDocumentContentBytes method to get to the document content so we need to implement it as follows:

    private byte[] getDocumentContentBytes(NodeRef documentRef, String documentFilename) {
        // Get a content reader
        ContentReader contentReader = m_contentService.getReader(documentRef, ContentModel.PROP_CONTENT);
        if (contentReader == null) {
            logger.error("Could not send document with email, content reader was null [filename=" +
                    documentFilename + "][docNodeRef=" + documentRef + "]");

            return null;

        // Get the document content bytes
        InputStream is = contentReader.getContentInputStream();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] documentData = null;

        try {
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len = > 0) {
                bos.write(buf, 0, len);
            documentData = bos.toByteArray();
        } catch (IOException ioe) {
            logger.error("Could not send document with email, content could not be read: " +
                    ioe.getMessage() + " [filename=" + documentFilename + "][docNodeRef=" + documentRef + "]");
            return null;
        } finally {
            if (is != null) {
                try {
                } catch (Throwable e) {
                    logger.error("Could not close doc content input stream during attachment creation: " +
                            e.getMessage() + " [filename=" + documentFilename + "][docNodeRef=" + documentRef + "]");

        return documentData;

Here we use the Content Service to get to a reader for the content referred to by the cm:content property. We then get the input stream for the content and read it while updating a byte array output stream that we will return to the code sending the email. Note that we close the input stream so we do not get file descriptors eaten up.

Working with Java backed Web Scripts require a lot more coding as we can see but it gives us the freedom to use any Java library that we want.

The final step we need to do is define a Spring bean that declares this Java class as a Java backed Web Script. So in the services-context.xml Spring configuration file that is located in the trunk/_alfresco/config/alfresco/module/com_mycompany_module_cms/context directory we define the Spring bean as follows:

<bean id=""
          class="com.mycompany.cms.webscript.SendDocInEmailWebScript" parent="webscript">
        <property name="contentService">
            <ref bean="ContentService"/>
        <property name="nodeService">
            <ref bean="NodeService"/>
        <property name="personService">
            <ref bean="PersonService"/>

The bean id has the following naming convention needed for it to be recognized as Web Script:

webscript.<package>.<webscript name>.[get|post|put|delete]

This completes our Web Script and we can now deploy this new Web Script by running the deploy-alfresco-amp target. Make sure that Alfresco Tomcat is stopped (read more in the build project blog about this).

Testing the Web Script

To test that it works as expected we can use curl and an SMTP server emulator called Antix SMTP Server Emulator  - (there is also another SMTP emulator that could be used called Neptune but it is not as good as you cannot visually see the sent emails). Start up the SMTP emulator so you see a Window as follows:

In this case I have already sent one email so that’s why there is an entry in the list. Now upload a document and make a note of its node reference. Then send the email as follows (note. –v = verbose output):

X:\tools\curl7.21>curl -v -u admin:admin "http://localhost:8080/alfresco/service/mycompany/sendDocInEmail?nodeRef=workspace://SpacesStore/cbb63e68-9884-4d24-abb3-28aaf8677169&userName=admin"
* About to connect() to localhost port 8080 (#0)
*   Trying connected
* Connected to localhost ( port 8080 (#0)
* Server auth using Basic with user 'admin'
> GET /alfresco/service/mycompany/sendDocInEmail?nodeRef=workspace://SpacesStore/cbb63e68-9884-4d24-abb3-28aaf8677169&userName=admin HTTP/1.1
> Authorization: Basic YWRtaW46YWRtaW4=
> User-Agent: curl/7.21.1 (i386-pc-win32) libcurl/7.21.1 OpenSSL/0.9.8o zlib/1.2.5
> Host: localhost:8080
> Accept: */*
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Length: 157
< Date: Tue, 13 Dec 2011 15:29:04 GMT
Successfully sent email with document (Getting_Started_with_Alfresco_Records_Management_for Enterprise_Edition_3_2_r.pdf) as attachment to
* Connection #0 to host localhost left intact
* Closing connection #0

You should now see a second email in the SMTP emulator window. Clicking on the email should bring it up in for example Windows Live email client.

Testing the Action

The implementation is now complete and you should be able to go to the Document Library of an Alfresco Site and click the Send in Email action for a document. The email should then popup in the Antix SMTP Server log window.

The indicator should be visible as the cm:emailed aspect was set after the email was sent, the page would need to be refreshed for the indicator to be visible:

The document properties will now also contain email specific information that was set when the emailed aspect was applied:

What about simple link actions, how are they setup?

So far in this article we have used a Document Library action that was implemented by calling some client side JavaScript code (i.e. action type=”javascript”). It is also possible to implement an action as a simple link (i.e. action type=”link”) that will take you out of the Share user interface and into some other web application. A simple link is setup as follows:

    <action id="mycompany.doclib.action.goToGoogle"
        <param name="href"></param>
        <param name="target">_blank</param>

Here we are setting up a simple link that will take us out of Alfresco Share into a new Browser window displaying Google search. The action should only be visible for folders in browse view so the action groups’ configuration looks like this:

    <actionGroup id="folder-browse">
        <action index="400" id="mycompany.doclib.action.goToGoogle"/>

It will look as follows in Share:

What about Doc Lib Actions that should just invoke a Repository Action?

Now, let’s say that you have an Alfresco Repository action (examples of out-of-the-box repo actions are create-version, check-in, copy, move, mail, simple-workflow, transform etc) since before or you just implemented one now.

Can you somehow call it from a Document Library action without too much coding? Yes you can, there is an out-of-the-box javascript action implementation called onActionSimpleRepoAction that you can use for this. Let’s say you have a repo action called publishDocToIntranet then you can call it as follows:

    <action id=" mycompany.doclib.action.publishDocToIntranet"
        <param name="function">onActionSimpleRepoAction</param>
        <param name="action">publishDocToIntranet</param>
        <param name="successMessage">message.publish-doc.success</param>
        <param name="failureMessage">message.publish-doc.failure</param>

The important bits here is the function and action parameters that need to be set to onActionSimpleRepoAction and your custom repository action ID.

This action has not been implemented in my example project and is just an example of how you could call repository actions in an easy way.

What about Doc Lib Actions that need to collect data from the user?

The “Send as Email” action that we just implemented does not allow the user to specify the “to address”, “subject”, or “body” content of the email. This is all setup by the Web Script. What if you had a requirement to change this to be collected via the Share UI when the action is executed, how would you do that?

There is help available for this in the form of the out-of-the-box javascript action implementation called onActionFormDialog. This action implementation allows you to point to a form defined in share-config-custom.xml and a repository action that will receive the form data.

We could use this action implementation to define a new “Send As Email (From)” action as follows:

    <action id="mycompany.doclib.action.sendAsEmailWithForm"
        <param name="function">onActionFormDialog</param>
        <param name="itemKind">action</param>
        <param name="itemId">send-as-email</param>
        <param name="mode">create</param>
        <param name="destination">{node.nodeRef}</param>
        <param name="successMessage">message.send-as-email.success</param>
        <param name="failureMessage">message.send-as-email.failure</param>
        <evaluator negate="true">

For this form based action we re-use the icon, evaluator, and the success and failure messages that we created earlier. The rest of the parameters have the following meanings:

The "kind" of item the form is for. For example, node, task, type, action, mbean.
The identifier of the item the form is for, this will be different for each "kind" of item, for an action it would be the Spring bean ID for the repository action definition, for a node it would be a NodeRef etc.
The mode the form will be rendered in, valid values are view, edit and create, the default is edit.
Provides a destination for any new items created by the form, when present a hidden field is generated with a name of alf_destination.

Note. This parameter is necessary even of the action is not creating any new items/nodes.

If there are multiple forms for the item kind then you can also add an extra parameter called formId. It is the form configuration to lookup, refers to the id attribute of the form element. If omitted the default form i.e. the form element without an id attribute is used.

This action is configured to be displayed in the same views as the non-form “Send As Email” action as follows:

        <actionGroup id="document-browse">
            <action index="400" id="mycompany.doclib.action.sendAsEmail"/>
            <action index="401" id="mycompany.doclib.action.sendAsEmailWithForm"/>
        <actionGroup id="document-details">
            <action index="400" id="mycompany.doclib.action.sendAsEmail"/>
            <action index="401" id="mycompany.doclib.action.sendAsEmailWithForm"/>
        <actionGroup id="folder-browse">
            <action index="400" id="mycompany.doclib.action.goToGoogle"/>

The form used for the Send As Email (Form) action is defined in share-config-custom.xml and looks like this:

    <config evaluator="string-compare" condition="send-as-email">
                     <show id="to"/>
                     <show id="subject"/>
                     <show id="body_text"/>
                     <field id="to" label-id=""/>
                     <field id="subject" label-id="send-as-email.field.subject"/>
                     <field id="body_text" label-id="send-as-email.field.body_text">
template="/org/alfresco/components/form/controls/textarea.ftl" />

The important thing here is that the itemId, which points to a repository action Spring Bean ID send-as-email, needs to be specified as the condition attribute for the form configuration. The form field ids (i.e. to, subject, body_text) need to be used later in the repository action implementation.

The file is also updated with the new i18n labels:

actions.mycompany.sendAsEmail=Send as Email
actions.mycompany.sendAsEmailWithForm=Send as Email (Form)
actions.mycompany.goToGoogle=Google Search
message.sendAsEmail.success='{0}' successfully sent in email to {1}.
message.sendAsEmail.failure=Couldn't send '{0}' in email to {1}.
indicators.mycompany.emailed=This document has been emailed
send-as-email.field.body_text=Body Text

The repository action is defined like this in the service-context.xml file of the Alfresco WAR AMP extension project:

    <bean id="send-as-email" class="com.mycompany.cms.action.SendAsEmailActionExecuter" parent="action-executer">
        <property name="alfrescoRepoHelper">
            <ref bean="alfrescoRepoHelper"/>
        <property name="nodeService">
            <ref bean="NodeService"/>

The implementation of this repo action class is done in the same way as for the Web Script, it just picks up the parameters (i.e. to, subject, body_text) at the beginning and uses them when sending the email:

public class SendAsEmailActionExecuter extends ActionExecuterAbstractBase {
    private static Log logger = LogFactory.getLog(SendAsEmailActionExecuter.class);

    // Form parameters
    public static final String PARAM_EMAIL_TO_NAME = "to";
    public static final String PARAM_EMAIL_SUBJECT_NAME = "subject";
    public static final String PARAM_EMAIL_BODY_NAME = "body_text";

    private AlfrescoRepoHelper m_alfrescoRepoHelper;
    private NodeService m_nodeService;

    public void setAlfrescoRepoHelper(AlfrescoRepoHelper alfrescoRepoHelper) {
        m_alfrescoRepoHelper = alfrescoRepoHelper;

    public void setNodeService(NodeService nodeService) {
        m_nodeService = nodeService;

    protected void addParameterDefinitions(List<ParameterDefinition> paramList) {
        paramList.add(new ParameterDefinitionImpl(PARAM_EMAIL_TO_NAME,
                DataTypeDefinition.TEXT, true,
        paramList.add(new ParameterDefinitionImpl(PARAM_EMAIL_SUBJECT_NAME,
                DataTypeDefinition.TEXT, true,
        paramList.add(new ParameterDefinitionImpl(PARAM_EMAIL_BODY_NAME,
                DataTypeDefinition.TEXT, true,

    protected void executeImpl(Action action, NodeRef actionedUponNodeRef) {
        if (m_nodeService.exists(actionedUponNodeRef) == true) {
            // Get the email properties entered via Share Form
            String to = (String) action.getParameterValue(PARAM_EMAIL_TO_NAME);
            String subject = (String)
            String body = (String) action.getParameterValue(PARAM_EMAIL_BODY_NAME);

For the rest of the implementation download the source code.

When running this we will see the new form based action in the Share UI as follows:

And clicking on it brings up the form as follows:


  1. Thank you Martin, another excellent post!

  2. hi,

    nice one. where to download the amp and jar file?

  3. Hi,

    There is no AMP or JAR available, but you can download the build project from here: and then build it yourself.
    For more information about the build project have a look here:

  4. Hi,

    Could you plz to help me -- in which sdk package you take jar's?
    I downloaded latest sdk from and find BaseEvaluator as class from package -- org.alfresco.web.ui.common.component.evaluator.BaseEvaluator
    but it's contain only this method:
    public abstract class BaseEvaluator extends org.springframework.extensions.webscripts.ui.common.component.SelfRenderingComponent {
    public abstract boolean evaluate();

  5. Hi,

    I use org.alfresco.web.evaluator.BaseEvaluator in alfresco-share-4.0.0.jar


    1. Thanks you, that helped a lot :)

  6. Hi Martin,

    I'm trying to only invoke a repository action.

    I've already programmed the class that implements ActionExecuterAbstractBase.

    I followed the project structure designed for Alfresco AMP.

    I've successfully deployed it to alfresco.war

    Now the only part that I'm missing is how associate the buttons in alfresco share to my action..

    Can you help me?

    I've already added this to my share-config-custom.xml:

    I'm lost..


  7. Hi Nuno,

    The <action definition seems to be missing the icon="fileTransfer" attribute. Add this and an icon and see if it works better then.
    So in this example you need an image file with the name “fileTransfer-16.png”. Alfresco expects the image file to be located in the /components/documentlibrary/actions directory.


  8. Hi Martin,

    Excellent article. Its great you have covered all of the use cases for actions and also for 4.0. I looked into this before and the info was very thin on the ground so this really helps.


    Brian (seedim)

  9. Hi Martin,

    Thank you for your article about share actions.
    I was wondering in the part of "What about simple link actions..." would it be possible to take the current noderef and put in to an e-mail link i.e. Send Noderef this would make it possible to easily e-mail locations or files to colleagues to start getting used to alfresco and let them adapt to the new dms.

    And may I ask why using a form inside Alfresco for sending attachments wouldn't it be more handy if you use like Outlook where all your contact data and signatures are stored. (I'm not sure if this is possible)

    Kind regards,


    1. Hi Marcel,

      I have not tried this but it might work with:

      ?subject=Link to doc&body={downloadUrl}

      If you look into the Share out-of-the-box action configurations in share-documentlibrary-config.xml (located in webapps/share/WEB-INF/classes/alfresco) you will see lots of examles such as the following in link definitions:


      Regarding the second question: To demonstrate the form feature I just keep building on the Send Doc as Email action example and you would probably not use a form for this normally. Doing a form that picks up Outlook contact info would be more interesting...


    2. Hi Martin,

      Thank you for your reply.
      The first mailto url works fine now!

      Ok I understand. However I'm trying to seperate it from the actual forms in Alfresco and develop something which will trigger outlook and handover an attachment to the newly composed e-mail message.
      So the sending and handling will be done by Outlook instead of Alfresco.

      Kind regards,


  10. OK, this is fantastic and appears to be exactly what I need BUT.... unfortunately I am something of a noob when it comes to java and would like some guidance as to how to create the webscript. Do you gave an article on this, in particular?

    I am right across amending the xml files and have done this already to integrate the changes that you have suggested with others already implemented but the webscript stuff stumps me!

    1. Hi Paul, It is not strictly necessary to implement the Web Script logic in Java. This example requires the java.mail library so it is easier to use a Java controller for the Web Script than a JavaScript controller. It might be enough for you with JavaScript controllers for the Web Scripts you are working with.
      Jeff Potts has a couple of years old article that should still be useful to get up to speed on Web Scripts:

      If you still got problems then please post some more information about what your Web Script should be doing.


  11. Hello Martin,

    Really nice article, explaining the share actions a lot more in detail. I do have an additional question: what if you want to add a autocomplete function to a specific field. say for instance that for the subject a lookup is done whether there is a similar documenttitle in the alfresco repository?

    1. Hi, there is an autocomplete.ftl control that you can use for this.

  12. This comment has been removed by the author.

  13. Hi Martin, this is a great article. I followed your instruction but when I clicked the send-email with form, the form only have title bar with no form body where the fields should be located. The log said "a form could not be found for item : [action]send-as-email". Did I miss something ?
    Thank you.

    1. I'm facing with the same problem here.

    2. Hi,

      Make sure

      config evaluator="string-compare" condition="send-as-email"

      Is defined in share-config-custom.xml

    3. And make sure send-as-email is declared as action in your alfresco repository:

      bean id="send-as-email" class="com.mycompany.cms.action.SendAsEmailActionExecuter" parent="action-executer"

    4. Hi, I've been trying to do this in Alfresco Community 5.0.a, but I'm having the same error Fredy has: form could not be found. How can I solve it?

    5. I have the seme problem descibed above...
      my log say: ""a form could not be found for item : [action]send-as-email".
      Please is it possible to have a little step by step instruction for the install?
      I follow what is descibed on
      Please Help me

  14. Hi Martin,
    Fantastic tutorial, thanks for that!
    I'm having the same error than Fredy Wijaya, but also I'd like to ask if in email-actions.js, the way it is now... this shouldn't be the "Window" object, at least that is what I'm getting now so this.msg and this.modules.actions.genericAction doesn't exist in that context. Do you know how to solve the scoping issue?

    1. Hard to say without seeing the code for email-actions.js


  15. This comment has been removed by the author.

  16. In the case of an action showing a form to collect data from user.
    Is it possible to make some fiels optional?
    My action is working well, but the OK button stays disabled while the user doesn't fill in the field.

    I tried to avoid this using the configuration below

    Note the mandatory="false".

    But the field is still needed in order to allow the OK button click.

    Can you help me with this?

    Thanks in advance.

    1. Hi, the mandatory parameter has no effect if the servers sends information that the field is mandatory. Such as when defining a field as mandatory in a content model.

    2. Can you suggest me an workaround for this?

      Thank you.

  17. Hi Martin! Great article.

    I want to add an evaluator that depends on a node association. This is the case: If the node has a certain association, the action has to be shown as an option, otherwise this action has to be disable. How to do it extending from BaseEvaluator? Any clue or idea?

    Thanks in advance

    1. I think you can try something like this.

      Using the reference to jsonObject, get the nodeRef and then you can use it to get the node and look at association metadata

      String nodeRef;

      JSONObject node = (JSONObject) jsonObject.get("node");
      if (node != null) {
      nodeRef = (String) node.get("nodeRef");

      //using nodeRef, try to get info you need using alfresco api

  18. And, what about Doc Lib Actions that should just invoke a javascript with your custom action (not Java-backed Web Script)?

    Thanks in advance

    1. I would think you could do something like this:

      (function() {
      * Action that is calling some JavaScript code
      * @method onActionDoSomeJS
      * @param file the file that is in the context (Object Literal)
      actionName: "onActionDoSomeJS",
      fn: function mycompany_onActionDoSomeJS(file) {
      /** JavaScript code goes here */


  19. Hi Martin!, This is the most descriptive article I have read on document library actions.Thanks for the information. I have a requirement to enable "Download" action link only for the users who has "Download" permission.

    I tried the following , but it didn't work for me. I have added an existing action available in "share-documentlibrary-config.xml" . Following is the configuration I have used,


    1. Hi, I cannot see the code in your comment.

    2. Hi Martin , following is the configuration I have added to the "share-documentlibrary-config.xml" file

      <config evaluator="string-compare" condition="DocLibActions" >
      <action id="document-download" type="link" label="" replace="true">
      <param name="href">{downloadUrl}</param>
      <permission allow="true">Download</permission>


  20. Hi Martin

    First of all, thanks for this great article.

    I've managed to put your tutorial working, but I have a question.
    When, on the details page, we add the cm:emailed aspect, via the "Manage Aspects" action, the page is refreshed automatically, and the "Send as Email" action link disappears (as expected), but when we send an email, we have to manually refresh the page in order to make the link disappear.
    Is there a way to make the page refresh automatically after successfully invoking an action?


  21. And, what about Doc Lib Actions that should show info about the node in a form or a dialog? I mean, you have a button (for instance, "show metadata") and you want to show some metadata of the node in a dialog or a form...

    Thanks in advance again! Great blog

  22. Thank you for the great article!
    Viktor Chekh

  23. Martin would it be possible for you to show how to add the sendEmail function to the toolbar (Selected Items) to send multiple docs by mail (i.e. call the webscript multiple times) I am struggeling because it is not passing the nodeRef to the script when calling it.

    1. Hi,

      This blog might help you:

  24. Hello Martin,

    yes it helped me already to configure the toolbar with share-custom-config.xml but I am struggeling to execute it via this i never get the nodeRef passed for the call. Probably I am missing the point where to add a toolbar JS logic :S

  25. Thanks for the terrific, detailed article. I'm been using your stuff as a launching point for making my own modification. To that end, I'd like to ask a question - and point out a couple of places where the article could have a little more info.

    First, rather than email a document I'm trying to open it by way of another server. The problem I've run into is that email-actions.js appears to have no access to many of the Javascript root variable. In particular, "session" is not available. Its ticket property is necessary for the other system to retrieve the document from Alfresco. I'm currently getting 401s. My Alfresco extension embeds a URL for its document within a URL that drives the other system's RESTful interface.

    Is there another way to navigate from whatever objects -are- in scope for the Javascript to the session ticket I need?

    As for other things, it might be helpful to mention that won't do any good without custom-slingshot-application-context.xml. And at one point you name a customization file "share-custom-config.xml" rather than "share-config-custom.xml".

    Any assistance with the session ticket would be most welcome.


  26. how to fix - [web.scripts.ActionEvaluatorHelper] [http-8080-7] Evaluator 'mycompany.evaluator.doclib.action.isEmailed' not found.

    1. Post here the code you used to config your evaluator and where did you put the jar file which contains your evaluator class?

    2. This comment has been removed by the author.

    3. I used the code I downloaded from this page. But it doesn't create the evaluator class in the jar. The is not contained in the jar when I run the Ant build in eclipses. And there is a that points to class in custom-slingshot-application-context.xml.

    4. Hi,

      Everything you need should be part of the "Adding Document Library Actions in Share Sites - for" file.

    5. When testing the action it fails to send a mail, checking the alfrescotomcat-stdout.2013-05-09.log I found this warning 2013-05-09 10:44:36,898 WARN [web.scripts.ActionEvaluatorHelper] [http-8080-8] Evaluator 'mycompany.evaluator.doclib.action.isEmailed' not found. How to fix this. this

      is the link to a class but the class is not found in the amp that I applied.

    6. the bean that links to the class is there but the class named CheckIfDocIsEmailedEvaluator is not found in the amp file.

    7. Hi,

      The Java class for the evaluator will not be in the AMP file, it should be in the Share JAR extension file that is applied to the Share.war file.


    8. The problem that I was missing the compile-share in the build.xml

      Packaging extension JAR for AMP


  27. hi thanx for this artical but am unable to find 2nd step
    Add the action label and messages to the file located in the _share\config\alfresco\messages directory of the build project:
    There is not such file found Plz help me

    1. I think you just have to create this file, with name, and add those lines in it.

  28. Find your dream homes through the experts in Real Estate sector on : WWW. PropertyDealerOntips.COM 01165556555
    Finding experts and getting the best consultancy in Real Estate becomes so easy now, you say and we give….Just call on 01165556555 and get the details for FREE on your phone right away.

  29. Hello Martin
    I am configuring alfresco Email Form as per your guidance provided by you but i got the error like
    org.springframework.extensions.webscripts.WebScriptException - 09090001 Script url /api/action/send-as-email/formprocessor does not support the method GET


    If you can answer me what is the problem..Thanks..

  30. Hi,

    perfect tutorial. Thanks! :-)
    One additional question: How it is possible to open a new window on a document actions click where I'm able to create this link on repository side before?
    The result should be like explained in "What about simple link actions, how are they setup?". But until the user clicks in the link I'm not able to provide an URL. The URL has to be created by the given NodeRef via a web-script on the repository tier of Alfresco once the user clicked on the document action. So I have to deal with a result in the JS file that open the
    result with
    Is there an easy way to provide this? Even with less programming?

  31. Great article ! Please could you confirm me that it is applicable to Alfresco CE 4.2.e ?

    1. Hi, I use this example in an internal Ixxus bootcamp and have used it in 4.2.d and 4.2.0. So I am quite sure it will/should work in 4.2.e too. Let me know if that is not true and I will update the blog.

  32. I have picked up a lot of handy things out of this amazing blog.

    development Services

  33. Hi,
    I have looked at the following :
    and have managed to get an icom to appear in the selected items drop down in alfresco community 4.2.
    However I am still struggling to get my action to work for multiple objects. is there something I am missing here?

  34. hello, perfect tutorial, thank you ;)

    how to use SMTP configuration in instead of
    props.put("", "IXDV1210");

  35. Anyone solved the issue of Form definition could not be found error. I have done everthing as explained without success

  36. Hi Martin, thanks for the great tutorial! I followed each and every step and tried testing the send email action. The problem is I am getting this error:
    Cannot locate template processor for template com/mycompany/sendemail/sendDocInEmail.get.html

    Can you please advise?

  37. This comment has been removed by the author.

  38. Juilee, in the services-context.xml the bean id you mentioned should be in the format of - "webscript"."folder path where you put your email desc.xml"."get",once you set this right, you will see no log mentioning get.html.... hope this helps