Alfresco is an enterprise document management system. There is a free Community Edition that is open source, but its web interface pulls in an image from their official website that can be used to remotely track usage. This tracking image is added via Javascript and cannot be removed by simply changing a template. It is hard coded into a core class. This tutorial goes through the steps needed to patch the Alfresco WAR file in order to remove the tracking image. It has been tested for Alfresco 4.2c and may need to be adjusted for other versions.

Patching

A simple patch is available on github with both source and compiled class files. Simply clone the git repository, copy the share.war into the repository directory and run the patch. Then redeploy the share.war to your application server.

git clone https://github.com/sumdog/alfresco-tracking-removal
cd alfresco-tracking-removal
cp /path/to/share.war .
./patch.sh

Testing

After logging into Alfresco Share, viewing the source code of the main dashboard will show several Javascript files in the header. One is called messages_XXXXX.js where XXXXXX is a generated unique ID.

source code showing Alfresco Tracking Image

Viewing this specific Javascript file shows us the tracking image that’s injected onto the page in the fourth line of code.

Alfresco Tracking Image jQuery Injection

After our patch is applied, viewing this same Javascript file will show that the line of code that injects the tracking image is now gone.

Alfresco Tracking Image Removed

How it Works

To remove the tracking image, the class we need to modify is an org.springframework.extensioons.webscripts.MessageWebScript object. The following is a straight copy of the default version found in the Alfresco SDK with the tracking image lines commented out:

/*
 * Copyright (C) 2005-2010 Alfresco Software Limited.
 * This file is part of Alfresco
 *
 * Modified to get rid of the tracking PNG - Sumit <sumit@penguindreams.org>
 */
package org.penguindreams.alfresco;

/**
 * WebScript responsible for returning a JavaScript response containing a JavaScript
 * associative array of all I18N messages name/key pairs installed on the web-tier.
 * <p>
 * The JavaScript object is created as 'Alfresco.messages' - example usage:
 * 
 * var msg = Alfresco.messages["messageid"];
 * 
 *
 * @author Kevin Roast
 */
public class TrackingImageRemovalMessagesWebScript extends org.springframework.extensions.webscripts.MessagesWebScript
{
    /**
     * Generate the message for a given locale.
     *
     * @param locale    Java locale format
     *
     * @return messages as JSON string
     *
     * @throws IOException
     */
    @Override
    protected String generateMessages(WebScriptRequest req, WebScriptResponse res, String locale) throws IOException
    {
        Writer writer = new StringBuilderWriter(8192);
        writer.write("if (typeof Alfresco == \"undefined\" || !Alfresco) {var Alfresco = {};}\r\n");
        writer.write("Alfresco.messages = Alfresco.messages || {global: null, scope: {}}\r\n");
        writer.write("Alfresco.messages.global = ");
        JSONWriter out = new JSONWriter(writer);

        try
        {
            out.startObject();
            Map<String, String> messages = I18NUtil.getAllMessages(I18NUtil.parseLocale(locale));
            for (Map.Entry<String, String> entry : messages.entrySet())
            {
                out.writeValue(entry.getKey(), entry.getValue());
            }
            out.endObject();
        }
        catch (IOException jsonErr)
        {
            throw new WebScriptException("Error building messages response.", jsonErr);
        }
        writer.write(";\r\n");

        // start logo
        // community logo

        //Sumit - PenguinDreams - Edited to remove tracking image.
        //  It's in two places; removed below as well

        //final String serverPath = req.getServerPath();
        //final int schemaIndex = serverPath.indexOf(':');
        //writer.write("window.setTimeout(function(){(document.getElementById('alfresco-yuiloader')||document.createElement('div')).innerHTML = '<img src=\"");
        //writer.write(serverPath.substring(0, schemaIndex));
        //writer.write("://www.alfresco.com/assets/images/logos/community-4.0-share.png\" alt=\"*\" style=\"display:none\"/>\'}, 100);\r\n");
        // end logo

        return writer.toString();
    }

    @Override
    protected String getMessagesPrefix(WebScriptRequest req, WebScriptResponse res, String locale) throws IOException
    {
        return "if (typeof Alfresco == \"undefined\" || !Alfresco) {var Alfresco = {};}\r\nAlfresco.messages = Alfresco.messages || {global: null, scope: {}}\r\nAlfresco.messages.global = ";
    }

    @Override
    protected String getMessagesSuffix(WebScriptRequest req, WebScriptResponse res, String locale) throws IOException
    {
        StringBuilder sb = new StringBuilder();
        sb.append(";\r\n");

        //Sumit - PenguinDreams - removed; see above

        // start logo
        // community logo
        //final String serverPath = req.getServerPath();
        //final int schemaIndex = serverPath.indexOf(':');
        //sb.append("window.setTimeout(function(){(document.getElementById('alfresco-yuiloader')||document.createElement('div')).innerHTML = '<img src=\"");
        //sb.append(serverPath.substring(0, schemaIndex));
        //sb.append("://www.alfresco.com/assets/images/logos/community-4.0-share.png\" alt=\"*\" style=\"display:none\"/>\'}, 100);\r\n");
        // end logo
        return sb.toString();
    }
}

You’ll notice in the above Java file, the tracking image appears in two places and is commented out in both. In order to replace the default version of this WebScript class with our custom version, we’ll have to modify the custom application context file custom-sliingshot-application-context.xml. This file can be found in the standard Alfresco stand-alone package.

<?xml version='1.0' encoding='UTF-8'?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:hz="http://www.hazelcast.com/schema/config"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                http://www.hazelcast.com/schema/config
                http://www.hazelcast.com/schema/config/hazelcast-spring.xsd">




    <bean id="webscript.org.springframework.extensions.messages.get" parent="webscript" class="org.penguindreams.alfresco.TrackingImageRemovalMessagesWebScript">
        <property name="webFrameworkConfigElement" ref="webframework.config.element"/>
        <property name="dependencyHandler"         ref="dependency.handler"/>
    </bean>

    <bean id="webscript.org.springframework.extensions.messages.post" parent="webscript" class="org.penguindreams.alfresco.TrackingImageRemovalMessagesWebScript" />

</beans>

The original class is used as two different dependencies in the standard slingshot-application-context.xml, so adding the two bean definitions above will cause the Alfresco Spring Context loader to pull in our new beans from the custom context and override those that are in the default context.

Since this custom application context resides outside the WAR file in the shares/classes/alfresco/web-extension directory within Tomcat, one would think you could just add both this XML file and the compiled class into the shares/classes folder and Tomcat apply those classes in the class loading process. Unfortunately, our custom MessageWebScript is dependent on many jars located within the Alfresco web application. Those jars are not available outside the WAR file and must be replicated on the Tomcat classpath for TrackingPixelRemovalMessagesWebScript to work. Therefore, it’s easier to compile the class and add both it and the application context directly to the war file itself.

Building

You might want to build the patch file yourself. When I initially built the TrackingPixelRemovalMessagesWebScript Java class, I had the Alfresco Maven development environment setup. Within that environment, I place the two files mentioned like so:

share/src/main/java/org/penguindreams/alfresco/TrackingImageRemovalMessagesWebScript.java
share/src/main/resources/alfresco/web-extension/custom-slingshot-application-context.xml

After the files are placed within those locations in your Maven build, doing an mvn install will produce a WAR file with the appropriate compiled TrackingImageRemovalMessagesWebScript.class. It is possible to build the required class file without a maven environment. After all, it’s just one java file. However, I had trouble assembling all the dependencies required, such as the Spring Surf libraries, which are currently still in incubator and currently have no release version (Alfresco uses the snapshots as dependencies for its production release).

Final Notes

Alfresco is an example of an open source project which doesn’t really encompass any of the philosophies behind the free software movement. It’s a commercial product which releases a slightly crippled open source version in order to gain free improvements from a wider community. It’s documentation isn’t always clear or up to date, and developing within it can prove quite challenging. The tracking image should be an optional, opt-in flag, rather than a hard-coded element that is intentionally difficult to remove. These instructions may need to be updated for future versions of Alfresco. Comments and pull requests to the git repository are welcome.