Tip: How to move an Alfresco category

Alfresco has a category hierarchy that can be used to categorize content. Unlike tags, which are not in a hierarchy and can be created by anyone, categories must be managed by an administrator. But even an administrator cannot move a category from one parent to another through the user interface. When it is time to reorganize your categories, how do you do it?

It’s actually pretty easy to do this with code. Your first thought might be, “Well, a category is a node and server-side JavaScript can work with nodes, so I’ll just write a quick script to do the move,” and who could blame you–that’s a well-reasoned line of thinking. But if you look at the source for the current JavaScript API ScriptNode object’s “move” method, you’ll see that it assumes that the association it is creating is “cm:contains”. Categories are associated with an association called “cm:subcategories”, not “cm:contains”. So, at least in this release, JavaScript can’t help us.

Luckily, Alfresco’s Java API is up to the task. When you call the NodeService’s moveNode method you have full control over the new relationship that gets created.

For stuff like this it is often handy to whip up a little Java-backed web script. All the web script needs is the source node reference that needs to be moved and a target node reference to move the source node reference to. Here’s the class with debug and parameter checking removed:

public class MoveCategory extends DeclarativeWebScript {

  // Dependencies
  private NodeService nodeService;

  @Override
  protected Map<String, Object> executeImpl(WebScriptRequest req, Status status) {

    final String sourceNodeRefString = req.getParameter("sourceNodeRef");
    final String targetNodeRefString = req.getParameter("targetNodeRef");

    // snag the nodes
    NodeRef sourceNodeRef = new NodeRef(sourceNodeRefString);
    String sourceName = (String) nodeService.getProperty(
                                   sourceNodeRef,
                                   ContentModel.PROP_NAME);
    NodeRef targetNodeRef = new NodeRef(targetNodeRefString);
    String targetName = (String) nodeService.getProperty(
                                   targetNodeRef,
                                   ContentModel.PROP_NAME);

    // move the source node to the target
    nodeService.moveNode(sourceNodeRef,
                         targetNodeRef,
                         ContentModel.ASSOC_SUBCATEGORIES,
                         QName.createQName(
                           NamespaceService.CONTENT_MODEL_1_0_URI,
                           sourceName));

    // set up the model
    Map<String, Object> model = new HashMap<String, Object>();
    model.put("sourceNodeRef", sourceNodeRefString);
    model.put("sourceName", sourceName);
    model.put("targetNodeRef", targetNodeRefString);
    model.put("targetName", targetName);

    return model;
  }

  public void setNodeService(NodeService nodeService) {
    this.nodeService = nodeService;
  }

}

If you need to see how to wire up the web script with Spring configuration, or you want to see the rest of the web script files (like the descriptor & view), you can take a look at this zipped up Eclipse project.

I should mention that in my web script descriptor, I configured the web script to require an authenticated user, but I set it to run as admin. That gives non-admin users the ability to move categories around using this web script. You may or may not want to do that.

Also, because this is just an example, I’m using the zip overlay method of deployment. For production, you should be using AMPs.

For more Java-backed web script examples, see this wiki page. And if you would prefer to use Maven to develop your Java-backed web scripts, take a look at this project on Google Code.

5 comments

  1. jpotts says:

    You cannot create categories via CMIS because cm:category inherits from cm:object. You can only use CMIS to create instances of cm:content, cm:folder, or their descendant types.

  2. jpotts says:

    Yes, of course, but not using CMIS. You could write a web script that does it, and then invoke that web script from your application. Your web script controller can either be JavaScript or Java because both of those APIs are able to assign categories to documents. To assign a category all you have to do is add the nodeRef of the category node to the multi-valued property called cm:categories. This assumes you have already added the cm:generalclassifiable aspect to the node.

  3. Hi,
    Thank you for a great post.
    You can use GroovyRunner (http://www.open-t.nl/projects/groovyrunner) for moving categories. All you have to do is to install groovyrunner and run the following:
    withTransaction {
    sourceNodeRefString = “workspace://SpacesStore/…”
    NodeRef sourceNodeRef = new NodeRef(sourceNodeRefString)
    sourceName = (String) nodeService.getProperty(sourceNodeRef,ContentModel.PROP_NAME)
    println “Moving category node: ” + sourceName

    NodeRef targetNodeRef = new NodeRef(“workspace://SpacesStore/…”)
    targetName = (String) nodeService.getProperty(targetNodeRef, ContentModel.PROP_NAME)
    println “To be a child of node: ” + targetName
    nodeService.moveNode(sourceNodeRef, targetNodeRef,ContentModel.ASSOC_SUBCATEGORIES, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,sourceName))
    }

Comments are closed.