Nodetype migration best practices
This topic has best practices of what to do if you must modify your nodetypes.
For information on changing existing node types, see Modifying node type definitions.
Don’ts
Do not change a property type
For example, do not change a boolean property type to a long.
Don't change:
[jnt:displayLatestNews] > jnt:content, jmix:basicContent
- autoRefresh (boolean)
To:
[jnt:displayLatestNews] > jnt:content, jmix:basicContent
- autoRefresh (long)
Do not change a property name
For example, do not change a property name from desc to description.
Don't change:
[jnt:desc] > jnt:content, jmix:basicContent
- desc (string)
To:
[jnt:desc] > jnt:content, jmix:basicContent
- description (string)
Do not toggle internationalized flag on a property
For example, do not add or remove the internationalized flag.
Don't change:
[jnt:desc] > jnt:content, jmix:basicContent
- description (string)
To:
[jnt:desc] > jnt:content, jmix:basicContent
- description (string) internationalized
Or
From:
[jnt:desc] > jnt:content, jmix:basicContent
- description (string) internationalized
To:
[jnt:desc] > jnt:content, jmix:basicContent
- description (string)
Migrating properties from one to another
To make your migration easier, always add a new property instead of changing an existing property.
For example, change:
[jnt:desc] > jnt:content, jmix:basicContent
- description (string)
To:
[jnt:desc] > jnt:content, jmix:basicContent
- description (string) internationalized
Remove most flags from existing properties and flag them as hidden (this way it won’t show in in edit mode for editors).
[jnt:desc] > jnt:content, jmix:basicContent
- description (string) hidden
- descriptionI18N (string) internationalized
Update your resource bundle to use same label on a new property.
jnt_desc.description = Description field (deprecated)
jnt_desc.descriptionI18N = Description field
Use a Groovy script to migrate existing properties from one to another. You must run the script from Jahia Tools>Administration and Guidance>Groovy console. The script must use a System Session that is run as the root user. Here is a skeleton example of how to achieve that.
JCRTemplate.getInstance().doExecuteWithSystemSessionAsUser(ServicesRegistry.getInstance().getJahiaUserManagerService().lookupRootUser().getJahiaUser(), "default", null, new JCRCallback<Object>() {
public Object doInJCR(JCRSessionWrapper session) {
// Session is now a system session not hindered by locks, or protected properties
return null;
}
});
Sample Groovy script
This script runs and copies the value of description properties for all declared languages on the site associated with the node being updated.
import javax.jcr.NodeIterator
import javax.jcr.RepositoryException
import javax.jcr.query.Query
import org.jahia.registries.ServicesRegistry
import org.jahia.services.content.JCRCallback
import org.jahia.services.content.JCRNodeWrapper
import org.jahia.services.content.JCRSessionWrapper
import org.jahia.services.content.JCRTemplate
import org.jahia.services.query.QueryWrapper
def log = log;
JCRTemplate.getInstance().doExecuteWithSystemSessionAsUser(ServicesRegistry.getInstance().getJahiaUserManagerService().lookupRootUser().getJahiaUser(), "default", null, new JCRCallback<Object>() {
public Object doInJCR(JCRSessionWrapper session) {
log.info("Copying description into descriptionI18N")
final String stmt = "SELECT * FROM [jnt:desc] WHERE ISDESCENDANTNODE('/sites') AND [description] is not null";
try {
QueryWrapper queryInit = session.getWorkspace().getQueryManager().createQuery(stmt, Query.JCR_SQL2);
NodeIterator iteratorSitesInit = queryInit.execute().getNodes();
def i = 0;
while (iteratorSitesInit.hasNext()) {
JCRNodeWrapper node = (JCRNodeWrapper) iteratorSitesInit.nextNode();
// Check if nose is locked
if (node.isLocked()) {
log.warn("Node " + node.getPath() + " is locked from " + node.getPropertyAsString("jcr:lockOwner") + " for reason " + node.getPropertyAsString("j:lockTypes"));
}
def description = node.getProperty("description").getString();
// let's copy the value of description in all available languages
node.getResolveSite().getActiveLiveLanguagesAsLocales().forEach({locale ->
node.getOrCreateI18N(locale).setProperty("descriptionI18N", description)
})
i++;
if (i % 500 == 0) {
session.save();
}
}
session.save()
} catch (RepositoryException ex) {
log.info("Exception while executing migration script {}",ex.message)
}
return null;
}
});
After validation, you can delete the property description using a script and then remove it from the definition to avoid keeping deprecated properties longer than needed.
import javax.jcr.NodeIterator
import javax.jcr.RepositoryException
import javax.jcr.query.Query
import org.jahia.registries.ServicesRegistry
import org.jahia.services.content.JCRCallback
import org.jahia.services.content.JCRNodeWrapper
import org.jahia.services.content.JCRSessionWrapper
import org.jahia.services.content.JCRTemplate
import org.jahia.services.query.QueryWrapper
def log = log;
JCRTemplate.getInstance().doExecuteWithSystemSessionAsUser(ServicesRegistry.getInstance().getJahiaUserManagerService().lookupRootUser().getJahiaUser(), "default", null, new JCRCallback<Object>() {
public Object doInJCR(JCRSessionWrapper session) {
log.info("Removing description from existing node")
final String stmt = "SELECT * FROM [jnt:desc] WHERE ISDESCENDANTNODE('/sites') AND [description] is not null";
try {
QueryWrapper queryInit = session.getWorkspace().getQueryManager().createQuery(stmt, Query.JCR_SQL2);
NodeIterator iteratorSitesInit = queryInit.execute().getNodes();
def i = 0;
while (iteratorSitesInit.hasNext()) {
JCRNodeWrapper node = (JCRNodeWrapper) iteratorSitesInit.nextNode();
// Check if nose is locked
if (node.isLocked()) {
log.warn("Node " + node.getPath() + " is locked from " + node.getPropertyAsString("jcr:lockOwner") + " for reason " + node.getPropertyAsString("j:lockTypes"));
}
node.getProperty("description").remove();
i++;
if (i % 500 == 0) {
session.save();
}
}
session.save()
} catch (RepositoryException ex) {
log.info("Exception while executing migration script {}",ex.message)
}
return null;
}
});
Property flags that do not need migration
Flag | Operation | Update |
---|---|---|
mandatory, autocreated | added | Existing nodes need to be updated with a value if property doesn't exist, either by editing or by a script |
mandatory, autocreated | removed | No impact |
indexed=no, fulltextsearchable=no, etc. (all index relatable flag) | added/removed | Requires a reindexation from Jahia Tools>Administration and Guidance>Search engine management |
hidden, protected, primary | added/removed | No impact |