Migrating and modifying nodetypes
This topic has best practices of what to do if you must modify your nodetypes, information on property flags that do not need migration, and a summary table on 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
Don't change:
[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 |
Modifying node type definitions
Modify the definitions.cnd
file with caution. Content integrity issues can occur if content has already been created with existing node types definitions.
Type of modification | Operation | Comment |
---|---|---|
Namespace | Creation | Will not create a problem |
Namespace | Deletion | Should never be done. Instead of a deletion, stop using the previous namespace |
Namespace | Modification | Should never be done. Instead of a modification, create a new namespace and stop using the previous one |
Node type | Creation | Will not create a problem |
Node type | Deletion | Should never be done before having deleted all the instantiated nodes of this type from the template/site. Find and delete instances of this node using Jahia Tools>JCR Data>JCR console. You can also script this operation using Groovy scripts. |
Node type | Modification | Renaming a node type is similar to perform a deletion of the previous node type, and creating of a new one |
Property of a node type | Creation | Will not create problem |
Property of a node type | Deletion | Should never be done before having set the property to null on all the instantiated nodes. Otherwise, it will lead to publication issues. An alternative option is to declare this property as hidden and then stop using it. |
Property of a node type | Modification | Should never be done if there is node instantiated with this property. If necessary, create a new property and refer to "Deletion" section above |