Did you know there is a right way and a wrong way to rename nodes (documents, folders, etc.) in Alfresco? The most common way to rename a node is by changing the cm:name property. It might come as a surprise that this is actually the wrong way, for reasons that we will get into in a moment. We’ll also explain why, and look at a way to find nodes that have been renamed incorrectly so they can be fixed.
The Right Way
So, what is the right way to rename a node? In the Alfresco JavaScript API, the ScriptNode object has the method void setName(String name). This is the method to use when renaming a node in JavaScript:
var myNode; myNode.setName("Fronkensteen");
FileInfo rename(NodeRef fileFolderRef, String newName). This is the method to use when renaming a node in Java:
NodeRef myNode; FileFolderService fileFolderService; FileInfo renamedFileInfo = fileFolderService.rename(myNode, "Fronkensteen");
The Wrong Way
The wrong way to rename a node is to just set the cm:name property to the new name:
JavaScript:
// NOTE - WRONG WAY - DON'T DO THIS (THOUGH EVERYONE DOES) document.properties["cm:name"] = "Fronkensteen"; document.save();
Java:
// ALSO THE WRONG WAY (YEAH WE'VE ALL DONE THIS) NodeRef myNode; NodeService nodeService; nodeService.setProperty(myNode, ContentModel.PROP_NAME, "Fronkensteen");
Why is this wrong?
In Alfresco, the node name is also used in the XPath. When you change the cm:name property, the XPath remains the same as it was before the name property change, and now you can’t find your node with a path search.
For example, if you have a folder node with the following display path and name:
/Company Home/Experiments/Frankenstein
The XPath query for this folder is:
/app:company_home/cm:Experiments/cm:Frankenstein
(NOTE: you can paste this into the Alfresco Node Browser and select “XPath” as the query language, and get the NodeRef of the “Frankenstein” folder)
If you then rename the folder “Frankenstein” just by changing cm:name as in the “wrong” examples above, and then try your XPath search with the new name (“Fronkensteen”):
/app:company_home/cm:Experiments/cm:Fronkensteen
This XPath search will fail.
It fails because the XPath is derived from the name property of the association which links parent nodes to child nodes, and not the cm:name property of the child node itself. When you create a node and give it a name, the association name property and the node name property are the same, but when you set the node name property, you are not changing the XPath. And since the association name property is not visible (at least to an end user), any XPath search functionality based on the name that is visible (the cm:name property of the node) won’t work after the rename of the node.
In the Share property editor, changing the name property will actually call the FileFolderService.rename(), which will update both the cm:name property of the node as well as the association name property.
Oops! I’ve been doing it this way all along…
Don’t worry, me too. One of the reasons is that the Alfresco JavaScript API documentation doesn’t mention the setName method at all. You have to dig into the Javadoc for the underlying Java implementation of the JavaScript API (as I did above) to even find the setName method.
We’ve been renaming nodes the wrong way forever and XPath searches aren’t working.
If there are lots of nodes that have been renamed the wrong way, finding them might present a problem. Fortunately there is a database query that can figure this out. This query is specific to nodes of type cm:folder:
select s.protocol || '://' || s.identifier || '/' || n.uuid as noderef, ca.qname_localname XPath_assoc_name, np.string_value name_property_value from alf_node n, alf_child_assoc ca, alf_qname qn, alf_node_properties np, alf_namespace ns, alf_store s where s.protocol = 'workspace' -- regular nodes (not archived, etc.) and np.string_value <> ca.qname_localname -- the association name and node name differ and s.id = n.store_id and ca.child_node_id = n.id and ca.qname_ns_id in -- just want associations in the cm namespace (select id from alf_namespace where uri = 'https://www.alfresco.org/model/content/1.0') and np.node_id = n.id and np.qname_id in -- get the cm:name property of the node (select qn1.id from alf_qname qn1, alf_namespace ns1 where qn1.local_name = 'name' and qn1.ns_id = ns1.id and ns1.uri = 'https://www.alfresco.org/model/content/1.0') and qn.id = n.type_qname_id and qn.local_name = 'folder' -- the specific content type and namespace and ns.uri = 'https://www.alfresco.org/model/content/1.0' and qn.ns_id = ns.id order by n.id asc;
To query cm:content nodes, substitute ‘content’ for ‘folder’ in the qn.local_name clause. If you have a custom type, use that and the fully-qualified namespace in the qn.local_name clause and the ns.uri clause.
Running this query after the wrong JavaScript name change example (from Frankenstein to Fronkensteen) results in this (note your noderefs will be different):
noderef | XPath_assoc_name | name_property_value --------------------------------------------------------------+----------------------+------------------------ workspace://SpacesStore/26deaced-7455-4ee9-8446-6f3821e03775 | Imap Home | IMAP Home workspace://SpacesStore/832e719d-02d1-4305-98e9-21dfbaab11b8 | workflownotification | Workflow Notification workspace://SpacesStore/2134080b-18e1-42d4-8665-bce08e002361 | webscripts | Web Scripts workspace://SpacesStore/106f10b4-2db3-4d69-b919-33157a4a6175 | extensionwebscripts | Web Scripts Extensions workspace://SpacesStore/68615384-daf9-4a77-83c2-e5ffcb2e53dd | Frankenstein | Fronkensteen (5 rows)
The last record in the query results is “Frankenstein” which is now named “Fronkensteen,” but the XPath still has “Frankenstein.”
Also note the first four records in the query results. These are built-in Alfresco nodes and are created out of the box with mismatches between the XPath and the node names. Note that there are more nodes than this, but these are the ones that are in the cm (https://www.alfresco.org/model/content/1.0) namespace. The others are in the app namespace (https://www.alfresco.org/model/application/1.0) which is excluded from the query.
Now, armed with the NodeRefs for your offending nodes, you are ready to correct them!
So, how can I fix this?
Never fear, once you’ve identified the nodes that have this problem, the solution is pretty straightforward: if you call the correct methods to change the name again, that will fix it.
You do have to change the name to something even newer (for example from “Fronkensteen” to “Eyegore”), because the underlying FileFolderService implementation will not go to the trouble of actually renaming a node and the association/XPath name if the new name is the same as the old name (as defined by the cm:name property).
There are a few ways to do this for a large number of nodes. You could write a simple JavaScript program to read a file with all the NodeRefs and their new names, and call setName() for each node. Or, you could write a JavaScript-backed or even a Java-backed webscript to do it, calling FileFolderService.rename() for each node. However you implement the fix, be sure not to process too many nodes in a single transaction. Hmm, maybe we should have a blog post about that.