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;
node.save();

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 {
  behaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE);
                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'>
<beans>
    <bean id="com.formtek.RunAsSystem" parent="baseJavaScriptExtension" class="com.formtek.example.util.RunAsSystem">
      <property name="extensionName">
          <value>ftk</value>
      </property>
   <property name="behaviourFilter" ref="policyBehaviourFilter" />
    </bean>
</beans>

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;
 node.save();
}
ftk.exec(doit);

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.