Tuesday, November 13, 2018

Set Up and Configure FTPS with Alfresco

There are a number of ways to access Alfresco in addition to the common usage of the Share UI. One of the ways that Alfresco exposes itself is through FTP. Often however, organizations prefer to secure their FTP connection into Alfresco using certificates and keys (FTPS). This knowledge base article aims to show one how to configure Alfresco for FTPS access.

There are four areas to focus to accomplish this:

1. Alfresco Keystore and Truststore
2. FTP and FTPS configurations in Alfresco's global properties
3. FTP client connection instructions
4. Debugging in case you run into issues

First, we'll start with creating the keystore and cacerts file. Here we'll create a self-signed certificate for this KB articles' example.

Part 1. Create the keystore and cacerts file.

In this simple example, you will create a self-signed certificate for both Java keystore and cacert file.

Run the following command to create a key for Alfresco:

# <Alfresco Install Dir>/java/bin keytool -genkey -alias tomcat -keypass secret -keyalg RSA

You will be prompted for a password (use "secret" as shown from the command above). Also, give your name, the hostname, ogranization, city, state and country code.

This command will by default create a .keystore file in your user's home directory. In my example this will write to the .keystore file in /root/ directory (/root/.keystore).

Next, we'll export the generated key into a server.crt file

# <Alfresco Install Dir>/java/bin keytool -export -alias tomcat -keypass secret -file server.crt

After entering the password you will see the response:

Certificate stored in file <server.crt>

Now, import the server.crt into this Alfresco keystore:

# <Alfresco Install Dir>/bin keytool -import -file server.crt -keypass secret -keystore /root/.truststore

Provide the password (default "secret") and confirm the certificate. If all goes well, you will see this response:

Certificate was added to keystore

Part 2: Configure the alfresco-global.properties file

Open the file <Alfresco Install Dir>/tomcat/shared/classes/alfresco-global.properties and add the following settings:

### FTP Server Configuration ###
ftp.port=21 (Change to a different port if running Alfresco as a non-root user)

Make sure the keyStore and trustStore paths are absolute or Java will not find the certificate at runtime.

Part 3: Connecting to Alfresco FTP server using FileZilla FTP client

On a client (Windows) open FileZilla and go to File > Site Manager. In the General tab, enter the Alfresco hostname and the ftp port number as it was set in Part 2.

Make sure these client configurations are used within Filezilla:

  • Protocol "FTP - File Transfer Protocol"
  • Encyption "Require explicit FTP over TLS"
  • Login Type "Normal"

Use your Alfresco user's username and password and then click Connect. You should see something similar in Filezilla that will confirm the FTPS connection to Alfresco is working:

Status: Resolving address of <alfresco_host>
Status: Connecting to
Status: Connection established, waiting for welcome message...
Status: Initializing TLS...
Status: Verifying certificate...
Status: TLS connection established.
Status: Logged in
Status: Retrieving directory listing...
Status: Directory listing of "/" successful

Keep in mind that during the first connection, Filezilla will prompt you to accept an unknown Certificate from Alfresco. Select "Always trust this certificate in future sessions". Subsequent connections will not show this dialogue. If there is any error connection, you should see something similar to the following in FileZilla:

Command: AUTH SSL
Response: 534 SSL/TLS sessions not available
Error: Critical error: Could not connect to server
Status: Disconnected from server

If you do see errors, double-check your Alfresco global properties file. If needed, have a look at Part 4 and follow steps to debug the connection. There should then be more information in Alfresco's log files.

Part 4: Configure and Enable DEBUG logging for Alfresco FTP

Add the following to <Alfresco Install Dir>/tomcat/webapps/alfresco/WEB-INF/classes/log4j.properties and restart Alfresco:

# File servers

# FTP server debugging

Monday, October 22, 2018

Creating an Ephesoft Batch Class for Export into Alfresco

This knowledge base article will show you how to set up an Ephesoft batch class that will export a batch document to an Alfresco server from Ephesoft.

1. First off, install and configure a 5.2.1 Enterprise version of Alfresco. Also, install Ephesoft 4.5.0. These can run on the same Windows server but be sure that either Ephesoft or Alfresco uses alternative ports as both make use of Tomcat. Out of the box, both use port 8080 for web consoles. Change one of them to use port 8081 for example.

2. In the Alfresco repository using Share, create a toplevel folder (I called our folder, "SimpleExport").

3. As the ephesoft user, log into Ephesoft.

4. In the Batch Class Management section, select the MailroomAutomationTemplate and click Copy. We'll use this template and extend it just enough to allow for an Alfresco export.

5. Give the new batch class a name (I called mine, SimpleExport).

6. Open the new batch class by double-clicking on it inside the list.

7. We'll need to add a new document type. In the left-hand corner, click on Document Type and click Add (and choose Add Locally). Simply adding it should be enough. For this example, we won't need index fields.

8. You can call it SimpleExportableDocument and use Simple Exportable Document Type as the description. Click Apply and Deploy.

9. Expand Modules > Page Process > RECOSTAR_HOCR and set Recostar_Deskew_Switch and Recostar_Font_Switch to On.

10. Expand Modules > Export > CREATEMULTIPAGE_FILES and set Multipage File Export Process to ITEXT-SEARCHABLE and Searchable Output PDF to True

11. Go to CMIS section of Export and fill in according to the screenshot. Afterwards, click on Apply and Deploy.

12. Go to Upload Batch and select Select Files at the bottom.

13. Select the uploaded batch, ensure the correct batch class is selected and click on Start Batch.

14. Go to Batch Instance Management.

15. Go to Review/Validate.

16. Click Review button.

17. Click Ok to confirm.

18. Wait for export to complete.

19. Check Alfresco to make sure this worked.

Thursday, October 4, 2018

Working with Alfresco 6.0 in Docker Containers

For the purpose of this knowledge base article, Alfresco 6.0 as part of a docker container was installed on Ubuntu 18.04. Below are links to instructions on how to install Docker and Docker-Componse. Should you need to install Docker and Docker-Compose for other operating systems, Docker's install section provides instructions for those as well. After docker and docker-compose are installed, you should be able to follow the rest with your own workstation no matter the OS.

Install Docker


Install Docker-Compose


To download the needed docker-compose.yml file, you can request a 30-day trial here:


Once you get the docker-compose.yml file, you can create a directory and place the file there.

First, make sure that the docker service is running:

# sudo service docker start

You then start up the containerized Alfresco using this command run in the same directory that contains docker-compose.yml:

$ sudo docker-compose up
Creating network "docker-compose_default" with the default driver
Creating docker-compose_libreoffice_1           ... done
Creating docker-compose_solr6_1                 ... done
Creating docker-compose_tika_1                  ... done
Creating docker-compose_alfresco-pdf-renderer_1 ... done
Creating docker-compose_postgres_1              ... done
Creating docker-compose_shared-file-store_1     ... done
Creating docker-compose_share_1                 ... done
Creating docker-compose_activemq_1              ... done
Creating docker-compose_alfresco_1              ... done
Creating docker-compose_imagemagick_1           ... done
Attaching to docker-compose_tika_1, docker-compose_postgres_1, docker-compose_alfresco-pdf-renderer_1, docker-compose_shared-file-store_1, docker-compose_solr6_1, docker-compose_share_1, docker-compose_libreoffice_1, docker-compose_imagemagick_1, docker-compose_alfresco_1, docker-compose_activemq_1

You will see Alfresco (and the other components) go through its startup messages.

Once you see a message similar to this in the logs:

alfresco_1 | 03-Oct-2018 21:28:47.888 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 232188 ms

You can then go to the UI in your browser:


And log in with "admin" as the username and "admin" as the password.

If you ever need to have a deeper look into the Alfresco system itself as it's running in the containers, you can still directly access things like the Alfresco database or the filesystem where Alfresco resides.

To access the database, you can install a Postgresql client like PGAdmin3 and use the following connection endpoints:

Hostname: localhost
Port: 5432
Database: alfresco
Username: alfresco
Password: admin

To access the filesystem, you can use these commands to log into the container's shells (you should see something similar to the following):

$ docker ps 
CONTAINER ID        IMAGE                                            COMMAND                  CREATED             STATUS              PORTS                                                                                                                     NAMES
5528be5a1e5a        alfresco/alfresco-content-repository:6.1.0-EA1   "catalina.sh run -se…"   6 minutes ago       Up 6 minutes>8080/tcp                                                                                                    docker-compose_alfresco_1
1716ad9c4e82        alfresco/alfresco-share:6.0                      "/usr/local/tomcat/s…"   6 minutes ago       Up 6 minutes>8080/tcp                                                                                                    docker-compose_share_1
d6485df206f8        alfresco/alfresco-shared-file-store:0.1          "/bin/sh -c 'java -j…"   6 minutes ago       Up 6 minutes>8099/tcp                                                                                                    docker-compose_shared-file-store_1
588003fbf981        webcenter/activemq:5.14.3                        "/app/run.sh"            6 minutes ago       Up 6 minutes>5672/tcp,>8161/tcp, 1883/tcp,>61613/tcp, 61614/tcp,>61616/tcp   docker-compose_activemq_1
58443c670b7c        alfresco/alfresco-pdf-renderer:1.3               "/bin/sh -c 'java $J…"   6 minutes ago       Up 6 minutes>8090/tcp                                                                                                    docker-compose_alfresco-pdf-renderer_1
99582bc076ed        alfresco/alfresco-search-services:1.1.1          "/opt/alfresco-searc…"   6 minutes ago       Up 6 minutes>8983/tcp                                                                                                    docker-compose_solr6_1
8bb3bb72d73c        postgres:10.1                                    "docker-entrypoint.s…"   6 minutes ago       Up 6 minutes>5432/tcp                                                                                                    docker-compose_postgres_1
ec543faec7e8        alfresco/alfresco-libreoffice:1.3                "/bin/sh -c 'java $J…"   6 minutes ago       Up 6 minutes>8090/tcp                                                                                                    docker-compose_libreoffice_1
e8073fe6a48f        alfresco/alfresco-tika:1.3                       "/bin/sh -c 'java -j…"   6 minutes ago       Up 6 minutes>8090/tcp                                                                                                    docker-compose_tika_1
9d2bb4a7c605        alfresco/alfresco-imagemagick:1.3                "/bin/sh -c 'java $J…"   6 minutes ago       Up 6 minutes>8090/tcp                                                                                                    docker-compose_imagemagick_1

Looking at the list we can see that the container id for the running Alfresco image is "5528be5a1e5a".

We can then use the docker interactive command to get access to the filesystem:

$ sudo docker exec -it 5528be5a1e5a /bin/bash

And now we have an interactive session. Below you can see that the login directory is in /usr/local/tomcat. As root however, you should be able to change to any other directory in this container if needed.

[root@5528be5a1e5a tomcat]# pwd


If you ever need to make an edit to the running container's Alfresco settings (in alfresco-global.properties for example), you can do the following:

# vi shared/classes/alfresco-global.properties 

Just keep in mind that whatever changes you make will not persist should you turn off the container. To persist any changes to the system that will survive a restart, you'll need to come out of this interactive shell and run a docker commit command to save its state. To test this, you can do the following:

# touch test 

This will create an empty file called test in /usr/local/tomcat.

Next, commit the change by issuing the following command outside the container and on your local workstation:

# docker commit 5528be5a1e5a alfresco/alfresco-content-repository:6.1.0-EA1 

The format is:

# docker commit [container id] [image id]

After this, go ahead and run the local stop command to stop Alfresco.

$ sudo docker-compose down
Stopping docker-compose_alfresco_1              ... done
Stopping docker-compose_share_1                 ... done
Stopping docker-compose_shared-file-store_1     ... done
Stopping docker-compose_activemq_1              ... done
Stopping docker-compose_alfresco-pdf-renderer_1 ... done
Stopping docker-compose_solr6_1                 ... done
Stopping docker-compose_postgres_1              ... done
Stopping docker-compose_libreoffice_1           ... done
Stopping docker-compose_tika_1                  ... done
Stopping docker-compose_imagemagick_1           ... done
Removing docker-compose_alfresco_1              ... done
Removing docker-compose_share_1                 ... done
Removing docker-compose_shared-file-store_1     ... done
Removing docker-compose_activemq_1              ... done
Removing docker-compose_alfresco-pdf-renderer_1 ... done
Removing docker-compose_solr6_1                 ... done
Removing docker-compose_postgres_1              ... done
Removing docker-compose_libreoffice_1           ... done
Removing docker-compose_tika_1                  ... done
Removing docker-compose_imagemagick_1           ... done
Removing network docker-compose_default

Now, to see if our change persisted, go ahead and start the Alfresco containers:

$ sudo docker-compose up
Creating network "docker-compose_default" with the default driver
Creating docker-compose_tika_1                  ... done
Creating docker-compose_imagemagick_1           ... done
Creating docker-compose_libreoffice_1           ... done
Creating docker-compose_alfresco-pdf-renderer_1 ... done
Creating docker-compose_solr6_1                 ... done
Creating docker-compose_postgres_1              ... done
Creating docker-compose_activemq_1              ... done
Creating docker-compose_share_1                 ... done
Creating docker-compose_shared-file-store_1     ... done
Creating docker-compose_alfresco_1              ... done


Get the container id:

$ docker ps
CONTAINER ID        IMAGE                                            COMMAND                  CREATED             STATUS              PORTS                                                                                                                     NAMES
8275de822b40        alfresco/alfresco-content-repository:6.1.0-EA1   "catalina.sh run -se…"   3 minutes ago       Up 3 minutes>8080/tcp                                                                                                    docker-compose_alfresco_1

Attach to the running container:

$ sudo docker exec -it 8275de822b40 /bin/bash

And if you run an "ls", you should see that your "test" file has persisted. Keep in mind that you can do the same with the database image if you don't want to have Alfresco reinstall its repository on every startup. Also, when you make commits, by default, older images are kept. So, if you need to, you can revert your current set of images back to the original ones.

Monday, October 1, 2018

Configuring Alfresco Behaviors for Associations

Alfresco metadata is stored as properties and also associations. Associations show relationships between source and target objects in the Alfresco repository.  When querying metadata via the API, or when viewing the metadata in the Node Browser, properties and associations are separated.

Even when defining an Alfresco behavior, there are differences in how a behavior is defined, depending on whether properties or associations are the source of the behavior action.

There are a number of tutorials or examples in blog articles that describe how to define a behavior in Alfresco, but most focus on behaviors that involve properties and not associations.  In this post, let's look at what is involved in creating a behavior triggered by the addition or deletion of an association.

First, we create a custom content model, called AspectTest.xml, as follows:

<?xml version="1.0" encoding="UTF-8"?>
<model xmlns="http://www.alfresco.org/model/dictionary/1.0" name="aspt:AspectTest">
    <description>Aspect Test</description>
        <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
        <import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
        <namespace uri="aspecttest" prefix="aspt"/>
        <aspect name="aspt:ATest">
            <title>ATest Aspect</title>
    <property name="aspt:projManager">
    <title>Project Manager</title>
    <index enabled="true">
                <association name="aspt:projectManager">
                    <title>Project Manager</title>

Put it into the alfresco/extension/models directory. This defines an association and a text field.
Then create the file to tell Alfresco to use the custom content model. Create the file testaspect-context.xml and place it in the alfresco/extension directory.

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
    <!-- Registration of new models --> 
    <bean id="testAspect.dictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap">
        <property name="models">
    <bean id="onDeletePrjMgrProperty" class="org.alfresco.repo.policy.registration.AssociationPolicyRegistration" parent="policyRegistration">
        <property name="policyName">
        <property name="className">
        <property name="associationType">
        <property name="behaviour">
            <bean class="org.alfresco.repo.jscript.ScriptBehaviour" parent="scriptBehaviour">
                <property name="location">
                    <bean class="org.alfresco.repo.jscript.ClasspathScriptLocation">
    <bean id="onAddPrjMgrProperty" class="org.alfresco.repo.policy.registration.AssociationPolicyRegistration" parent="policyRegistration">
        <property name="policyName">
        <property name="className">
        <property name="associationType">
        <property name="behaviour">
            <bean class="org.alfresco.repo.jscript.ScriptBehaviour" parent="scriptBehaviour">
                <property name="location">
                    <bean class="org.alfresco.repo.jscript.ClasspathScriptLocation">

This file will wire in the custom model.  It also defines the behaviors. The last two bean definitions define behaviors that will run in Javascript for our newly defined association.  Note the reference to the Java class

The first of these invokes the behavior when associations are deleted, and the second is invoked when associations are added.
The behaviors call the Javascript function.  It is called alfresco/scripts/onAssocUpdate.js.

if (behaviour == null) 
    scriptFailed = true;

// Check the name of the behaviour
if (behaviour.name == null || behaviour.args[0].getType() != "{aspecttest}projectManager" || 
   (behaviour.name != "onCreateAssociation" && behaviour.name != "onDeleteAssociation") )
    scriptFailed = true;
 // Check the arguments
 if (behaviour.args == null || ('source' in behaviour.args[0]) == false || ('target' in behaviour.args[0]) == false) 
  scriptFailed = true;
  if (behaviour.args.length == 1) 
   var source = behaviour.args[0].source;
    var assocs = behaviour.args[0].source.assocs["{aspecttest}projectManager"];
    var textVal = "";
     for(var i=0; i<assocs.length; i++)
      if(i!=0) textVal += ",";
      textVal += assocs[i].properties["cm:userName"];
    source.properties["aspt:projManager"] = textVal;
    scriptFailed = true;
   scriptFailed = true;

The example Javascript code behavior will concatenate the list of users selected by the association into a text string that is populated into the new text property called aspt:projManager.

One of the peculiarities of Alfresco associations is that they are not searchable by queries.  As a workaround, this behavior will enable the search of user names selected by an association picker and stored the picked results in a text field.  The text field can be searched, even though the association cannot.

That's almost all.  But there is also some code involved in setting up the picker on an Alfresco search form.   For example, we can add the following extension configuration into Share to enable the assignment of our new association and also the display of it on a form.

      <!-- Form configuration section - aspect -->
      <config condition="aspt:ATest" evaluator="aspect">
                  <show id="aspt:projectManager" />
                  <field id="aspt:projectManager">
                     <control template="/org/alfresco/components/form/controls/authority.ftl" />
      <!-- Document Library config section -->
      <config evaluator="string-compare" condition="DocumentLibrary">
               <aspect name="aspt:ATest" />

The picker control for the association in Share will look like this:

And, using the above example picker results, user mjackson can be searched for as follows by searching on the text property which is synced with the values in the association (aspt:projManager:mjackson):

Wednesday, September 19, 2018

How to access properties defined in alfresco-global.properties file without writing custom Java code

The properties in alfresco-global.properties file are only accessible from code within the Alfresco Repository classpath. These properties cannot be directly accessed from Share Web Scripts or YUI client code.

The examples on the Web show you how to access these properties using a custom repo Java bean but there is a way to access these properties without writing any custom Java code. You can access these properties using a Javascript backed repo Web Script.

In the JavaScript backed webscript first get the Spring root web application context. After getting the Spring root web application context get the instance of the “global-properties” bean from it. The “global-properties” bean is a java.util.Properties type so now the repo Web Script should be able to access all the properties from the alfresco-global.properties file.

The Javascript backed repo Web Script can now expose or return these property values from alfresco-global.properties file. The repo Web Script returns the data as JSON.

Share Web Scripts and the YUI client can now access these properties defined in alfresco-global.properties file by calling the Javascript backed repo Web Script. 

Example of how the Share Web Scripts can consume the repo Web Script:

var url = "/api/formtek/edm/get-default-preview-option";
var result = remote.connect("alfresco").get(url);
if (result.status == 200)
     model.previewOptionValue = JSON.parse(result).defaultPreviewOptionValue;

Example of how the YUI client can consume the repo Web Script:

       method: Alfresco.util.Ajax.GET,
       requestContentType: Alfresco.util.Ajax.JSON,
       url: Alfresco.constants.PROXY_URI + "api/formtek/edm/get-default-preview-option,
       successCallback: {
             fn: function (res) {

             scope: this
        failureCallback: {
             fn: function (res) {

             scope: this

Friday, August 17, 2018

How to Increase the Timeout for Generating the Alfresco PDF Preview

The Formtek EDM Module for Alfresco provides custom transformers to convert several CAD file formats (such as AutoCAD DWG) to PDF for previewing in Alfresco Share. The default time allowed for this transformation to complete is 120,000 milliseconds or 2 minutes. For most drawings, this is more than enough time to create the preview PDF. However, some complex drawings may need a little more time. You can change the timeout period on the transformer itself, but there are a few other timeout settings you'll to change as well to make it all work properly.

Here are the steps for increasing the time allowed for transforming DWG to PDF using the custom transformer provided by the Formek EDM Module. In this example, the time is being increased to 3.5 minutes (210,000 ms):

1) Add the following properties to the alfresco-global.properties file to change both the specific transformer timeout and the system-wide timeout and error time values:

# ==========================================================================
# DWG to PDF transformer timeout defaults to 180000 ms in Formtek EDM Module 
# and 120000 ms in previous releases
# ==========================================================================

# =======================================================
# System-wide timeout and error time default to 120000 ms
# =======================================================

Although these settings (after an Alfresco restart) do provide a DWG file more time to complete its transformation, the following odd behavior is observed:

  • If the transformation continues beyond 2 minutes, a second transformation attempt is surprisingly triggered. Sometimes, but not always, that second attempt is started before the first one finishes.
  • So now there may be two transformation processes running at the same time for the same file, and competing for same system resources until the first transformation completes.
  • Furthermore, the "busy" indicator in the Alfresco Share preview window does not go away until the second 2-minute period (or a total of 4 minutes) elapses, thus making you wait longer than necessary to see the PDF preview.
  • But wait. Instead of loading the newly generated PDF preview, the following message is displayed leading you to believe that the transformation attempt may have failed:
  • A manual window refresh does load the PDF preview (once the second transformation attempt completes), but that's a not-so-obvious detail.

Here's an example of the two transformation attempts for doc2.dwg when the logger property, org.alfresco.repo.content.transform.TransformerDebug, is set to DEBUG. In this example, the second attempts starts one second after the first one completes:

2018-08-14 09:38:39,171 DEBUG [org.alfresco.repo.content.transform.TransformerDebug] [pool-13-thread-2] 11             dwg  pdf  doc2.dwg 2.5 MB -- pdf -- ContentService.transform(...)
2018-08-14 09:38:39,177 DEBUG [org.alfresco.repo.content.transform.TransformerDebug] [pool-13-thread-2] 11             workspace://SpacesStore/28a8aa18-9681-417e-83ec-0fa8de0efbd1
2018-08-14 09:38:39,178 DEBUG [org.alfresco.repo.content.transform.TransformerDebug] [pool-13-thread-2] 11             **a)  [50] dwg2pdf1<<Runtime>>           140,966 ms
2018-08-14 09:38:39,178 DEBUG [org.alfresco.repo.content.transform.TransformerDebug] [pool-13-thread-2] 11.1           dwg  pdf  doc2.dwg 2.5 MB dwg2pdf1<<Runtime>>
2018-08-14 09:41:02,873 DEBUG [org.alfresco.repo.content.transform.TransformerDebug] [pool-13-thread-2] 11             Finished in 143,702 ms

2018-08-14 09:41:03,455 DEBUG [org.alfresco.repo.content.transform.TransformerDebug] [pool-13-thread-2] 12             dwg  pdf  doc2.dwg 2.5 MB -- pdf -- ContentService.transform(...)
2018-08-14 09:41:03,455 DEBUG [org.alfresco.repo.content.transform.TransformerDebug] [pool-13-thread-2] 12             workspace://SpacesStore/28a8aa18-9681-417e-83ec-0fa8de0efbd1
2018-08-14 09:41:03,455 DEBUG [org.alfresco.repo.content.transform.TransformerDebug] [pool-13-thread-2] 12             **a)  [50] dwg2pdf1<<Runtime>>           141,512 ms
2018-08-14 09:41:03,455 DEBUG [org.alfresco.repo.content.transform.TransformerDebug] [pool-13-thread-2] 12.1           dwg  pdf  doc2.dwg 2.5 MB dwg2pdf1<<Runtime>>
2018-08-14 09:43:22,587 DEBUG [org.alfresco.repo.content.transform.TransformerDebug] [pool-13-thread-2] 12             Finished in 139,133 ms

Even though the first transformation attempt completed in about 2 minutes 23 seconds (143,702 ms), I don't see the PDF preview in Share for about 4 minutes 43 seconds (after the second attempt completes). 

To avoid this second transformation attempt, you need to change the read timeout for the HTTP socket connection, which defaults to 120,000 ms. So, in addition to Step 1 above, you'll also need to complete Step 2 as follows:

2) Create the tomcat\shared\classes\alfresco\web-extension\custom-slingshot-application-context.xml file with the following content, setting the readTimeout value accordingly (in my case, 210,000 ms).

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
  <bean id="connector.remoteclient" parent="connector.remoteclient.abstract" 
  class="org.alfresco.web.scripts.SlingshotRemoteClient" scope="prototype" > 
<!-- the http.connection.timeout value in milliseconds to apply to HTTP connections --> 
    <property name="connectTimeout"><value>10000</value></property> 
<!-- the http.socket.timeout value in milliseconds to apply to HTTP connections --> 
    <property name="readTimeout"><value>210000</value></property> 

3) Finally, restart Alfresco so the changes take effect.

Now, when you preview a newly upload drawing that takes more than 2 minutes to convert to PDF, only a single transformation attempt occurs and you will see the drawing preview sooner and without having to manually refresh the window.

Also note that this same configuration can be applied to other content transformers. However, you will need to configure the property that corresponds to that specific transformer in Step 1 above.

Tuesday, July 24, 2018

How to Remove Versioning for a Share Site (and Keep it Enabled Elsewhere)

Some use cases may require versioning to be disabled in some sections of your Alfresco repository while enabled in other areas. A use case that comes to mind involves a Share site where many documents are constantly being written while there is no requirement to hold on to older versions.

By default, Alfresco has versioning enabled. Alfresco uses the versionable aspect (cm:versionable) as a flag of sorts to assign to documents that will retain older versions of content as it is changed. As long as versioning is enabled, Alfresco will require that the versionable aspect be retained for any document on creation and on update. If a document has the versionable aspect, all new updates to the document will be versioned out of the box with 1.0+ for major versions and 0.1+ for minor versions. In order to ensure the document however does not increment by version, you'll need to make sure the versionable aspect is not assigned to the document.

In fact, it's very simple to create a Share rule that looks like this:

Here we create a rule that removes the versionable aspect as soon as the document is created. This is very simple to do and works on Share UI upload or if you use one of Alfresco's file servers to add the document. But, the real test for this process is when the document is updated with new content. When a document has its content updated Alfresco adds the versionable aspect back. In order to counteract this behavior, you have to create another rule that removes the versionable aspect on *update* like so:

At this point, whenever a document is updated via the "Upload New Version" action or via FTP or WebDAV, a new "version" is created but this document no longer retains its old versions. The old versions then become orphan nodes. At this point, when the Contentstore Cleaner job is run (out of the box this runs every morning at 4:00 am local time) the orphan nodes are then removed. All orphaned content at this point are then moved to the contentstore.deleted folder where they can either be safely removed from the file system or restored if needed.

Now, it's very likely that you may have pre-existing documents that will already have the versionable aspect added along with multiple versions at this point. If you would like to keep these from being versionable going forward and delete the old versions you can write a JS script like so:

// Set the name of the Share Site:
var siteName = "site1";

logger.log("This script removes versionable aspects for all documents in the " + siteName + " Share site.");

// Query used to return all documents (cm:content) from the documentLibrary folder and all subfolders:
var query = 'PATH:"/app:company_home/st:sites/cm:'+siteName+'/cm:documentLibrary//*" AND TYPE:\"cm:content\"';
logger.log("The query being used is: " + query);

// "documents" is our array of document results
var documents = search.luceneSearch(query);

logger.log("The number of documents found is: " + documents.length + ".");

// Iterate through the document list:
for (var i=0; i < documents.length; i++) {
  logger.log("Document name: " + documents[i].name);

  // If the document has the versionable aspect, then remove it:
  if (documents[i].hasAspect("cm:versionable")) {
    logger.log(" * " + documents[i].name + " has the versionable aspect. It will be removed.");
  } else {
    logger.log(" * " + documents[i].name + " does *not* have the versionable aspect.");

This script should have the .js extension and placed in the /Data Dictionary/Scripts folder. It can be run once using a Share rule. Just make sure the mimetype is set to JavaScript in the advanced properties for the document details for the file.

This process will allow for you to keep certain parts of your repository from keeping unneeded versions and keep your file system from filling up.

Tuesday, June 19, 2018

Alfresco: How to Overwrite Node Created Date

The Alfresco cm:created property is a protected field.  It normally can't be changed.
For example, the following simple JavaScript code tries to change the created date property to January 31, 2012.  It actually runs with no error, but when you check for the value of the property on the node during the same transaction, it appears to be set.

But query the property again or look at the value in the Node Browser and you'll find that it hasn't changed.

var dir = "Sites/swsdp/documentLibrary/Presentations/Project Overview.ppt";
var node = companyhome.childByNamePath(dir);

// Change the created date
var jan312012 = new Date(2012, 0, 31);
node.properties["cm:created"] = jan312012;

Sometimes it's desirable to be able to change the created date of the node.  This is especially useful when content is being migrated from another source into Alfresco and the creation date from the other system needs to be preserved in Alfresco.

Using the Alfresco Java API, it's possible to work around the problem.  To do that, we'll create the following Java method which can be called from Alfresco JavaScript. It will allow us to run any JavaScript method as user System.  It's a dangerous method to have around since it can run with no restriction, so it would be good practice  to package it separately and use it only for situations that warrant its use.

Create and compile the Java file com/formtek/example/util/RunAsSystem.java:

package com.formtek.example.util;

import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.mozilla.javascript.Context;
import org.alfresco.model.ContentModel;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;

import org.alfresco.repo.policy.BehaviourFilter;

public class RunAsSystem extends BaseScopableProcessorExtension 
    protected BehaviourFilter behaviourFilter;
    public void setBehaviourFilter(BehaviourFilter behaviourFilter)
      this.behaviourFilter = behaviourFilter;

    public void exec(final Function func) {
        final Context cx = Context.getCurrentContext();
        final Scriptable scope = getScope();
        RunAsWork<Object> raw = new RunAsWork<Object>() {
            public Object doWork() throws Exception {
                func.call(cx, scope, scope, new Object[] {});
                return null;
        AuthenticationUtil.runAs(raw, AuthenticationUtil.getSystemUserName());

Then wire this file into Alfresco with the file alfresco/extension/runas-context.xml:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
    <bean id="com.formtek.RunAsSystem" parent="baseJavaScriptExtension" class="com.formtek.example.util.RunAsSystem">
      <property name="extensionName">
   <property name="behaviourFilter" ref="policyBehaviourFilter" />

With that in place, it is possible then to write the following JavaScript:

function doit()
 // Find a test node
 var dir = "Sites/swsdp/documentLibrary/Presentations/Project Overview.ppt";
     var node = companyhome.childByNamePath(dir);
 // Change the created date
 var jan312009 = new Date(2009, 0, 31);
 node.properties["cm:created"] = jan312009;

The method ftk.exec() will run any JavaScript method as user System.
In this example, it changes the creation dates on one of the standard sample documents that comes with Alfresco.