Before migrating Ametys ODF , you need to migrate Ametys CMS 4 .2.x to 4.3.x
This guide covers data migration from Ametys ODF 4.1.x to 4.3.0.
See also the technical migration guide and the graphical migration guide.
For greater flexibility, course, training and ELP contacts have been merged into a single "repeater" of contacts with different roles:
Run the following script to migrate the contacts for your courses, pathways and ELP.
Customize the code and title for the role of administrative contacts and the code and title for the role of course leaders.
// Administrative role (A PERSONNALISER SI BESOIN)
var administrativeRoleCode = "contact";
var administrativeRoleTitle = new java.util.HashMap();
administrativeRoleTitle.put('fr', 'Contact administratif');
administrativeRoleTitle.put('en', 'Administrative contact');
var StringArray = Java.type("java.lang.String[]");
var administrativeRoleId = _createRoleIfNeeded(administrativeRoleTitle, administrativeRoleCode);
// Responsable pédagogique pour les ELPs (A PERSONNALISER SI BESOIN)
var respPRoleCode = "RespP";
var respPRoleTitle = new java.util.HashMap();
respPRoleTitle.put('fr', 'Responsable pédagogique');
respPRoleTitle.put('en', 'Education manager');
var respPRoleId = _createRoleIfNeeded(respPRoleTitle, respPRoleCode);
var t0 = new java.util.Date().getTime();
java.lang.System.out.println(new java.util.Date() + "----------------------- DEBUT SCRIPT : MIGRATION CONTACTS ----------------------- "
+ " en " +((new java.util.Date().getTime()) - t0)/1000.0 + "seconds "
+ " - le " + (new java.util.Date())
)
function _createRoleIfNeeded(roleTitle, roleCode)
{
var qm = session.getWorkspace().getQueryManager();
var query = qm.createQuery("//element(*, ametys:content)[@ametys-internal:contentType = 'odf-enumeration.PersonRole' and @ametys:code='" + roleCode + "']", javax.jcr.query.Query.XPATH);
var nodes = query.execute().getNodes();
if (nodes.hasNext())
{
print("Found role with code '" + roleCode + "'");
return "content://" + nodes.next().getUUID();
}
else
{
var contentWorkflowHelper= serviceManager.lookup('org.ametys.cms.workflow.ContentWorkflowHelper');
var contentTypes = new StringArray(1);
contentTypes[0] = 'odf-enumeration.PersonRole';
var result = contentWorkflowHelper.createContent("reference-table", 1, roleTitle.get('fr'), roleTitle, contentTypes, new StringArray(0), null, null, new java.util.HashMap());
var roleId = result.contentId;
var content = result.get('org.ametys.cms.repository.Content');
content.getNode().setProperty("ametys:code", roleCode);
contentWorkflowHelper.doAction(content, 22);
session.save();
print("Create new role with code '" + roleCode + "'");
return content.getId();
}
}
function _moveContactToRepeater(content, oldPropertyName, newRoleId)
{
if (content.getNode().hasProperty(oldPropertyName))
{
var values = content.getNode().getProperty(oldPropertyName).getValues();
if (values.length > 0)
{
hasChanges = true;
var repeaterNode = null;
if (content.getNode().hasNode("ametys:contacts"))
{
repeaterNode = content.getNode().getNode("ametys:contacts");
}
else
{
repeaterNode = content.getNode().addNode("ametys:contacts", "ametys:compositeMetadata");
}
var size = repeaterNode.getNodes().getSize();
var entryNode = repeaterNode.addNode("ametys:" + (size+1), "ametys:compositeMetadata");
// Move values
entryNode.setProperty("ametys:persons", values);
if (newRoleId)
{
entryNode.setProperty("ametys:role", newRoleId);
}
}
// remove old property
content.getNode().getProperty(oldPropertyName).remove();
return true;
}
return false;
}
function _contactMigration(content)
{
var hasChanges = false;
// Move repeater 'ametys:personInCharge' to 'ametys:contact'
if (content.getNode().hasNode("ametys:personInCharge"))
{
var node = content.getNode().getNode("ametys:personInCharge");
session.move(node.getPath(), node.getParent().getPath() + '/ametys:contacts');
hasChanges = true;
}
// Move 'contact' into a new entry of 'contacts' repeater with the administrative role
hasChanges = _moveContactToRepeater(content, "ametys:contact", administrativeRoleId) || hasChanges;
if (hasChanges) count++;
}
function _contactMigrationForCourses(content)
{
var hasChanges = _moveContactToRepeater(content, "ametys:contact", administrativeRoleId);
hasChanges = _moveContactToRepeater(content, "ametys:personInCharge", respPRoleId) || hasChanges;
if (hasChanges) count++;
}
var outgoingReferencesExtractor = serviceManager.lookup("org.ametys.cms.content.references.OutgoingReferencesExtractor");
function _updateOutgoingReferences(content)
{
var outgoingReferencesByPath = outgoingReferencesExtractor.getOutgoingReferences(content);
content.setOutgoingReferences(outgoingReferencesByPath);
content.saveChanges();
}
var totalCount = 0;
var count = 0;
// Migrate programs
jcrXPathQuery("//element(*, ametys:programContent)").forEach(function (content) {
migrateContent(content, [_contactMigration], true /* old versions incompatible */, null /* no tag */, false /* not verbose */);
_updateOutgoingReferences(content);
});
print(count + " programs have been migrated");
totalCount += count;
// Migrate subprograms
count = 0;
jcrXPathQuery("//element(*, ametys:subProgramContent)").forEach(function (content) {
migrateContent(content, [_contactMigration], true /* old versions incompatible */, null /* no tag */, false /* not verbose */);
_updateOutgoingReferences(content);
});
print(count + " subprograms have been migrated");
totalCount += count;
// Migrate courses
count = 0;
jcrXPathQuery("//element(*, ametys:courseContent)[@ametys:contact or @ametys:personInCharge]").forEach(function (content) {
migrateContent(content, [_contactMigrationForCourses], true /* old versions incompatible */, null /* no tag */, false /* not verbose */);
_updateOutgoingReferences(content);
});
print(count + " courses have been migrated");
totalCount += count;
java.lang.System.out.println(new java.util.Date() + "----------------------- FIN SCRIPT : MIGRATION CONTACTS ----------------------- "
+ " en " +((new java.util.Date().getTime()) - t0)/1000.0 + "seconds "
+ " - le " + (new java.util.Date())
)
if (totalCount > 0)
print(" => BUILD LIVE WORKSPACE"); var ArrayUtils = Java.type("org.apache.commons.lang3.ArrayUtils");
jcrXPathQuery("//element(*, ametys:programContent)").forEach(function(program) {
var currentVersionIsLive = ArrayUtils.contains(program.getLabels(), "Live");
program.checkpoint();
// Move the Live label if the last version was validated.
if (currentVersionIsLive)
{
program.addLabel("Live", true);
}
}); The "Period type" field in the "Period" reference table becomes a reference table.
To migrate data from the "Period" reference table, run the following script migration (it transforms the existing strings into entries in the new "Period type" reference table and makes the link with the "Periode" reference table)
// Imports
var ArrayList = Java.type('java.util.ArrayList');
var HashMap = Java.type('java.util.HashMap');
var AbstractWorkflowComponent = Java.type('org.ametys.plugins.workflow.AbstractWorkflowComponent');
var AbstractContentWorkflowComponent = Java.type('org.ametys.cms.workflow.AbstractContentWorkflowComponent');
// Components
var refTableHelper = serviceManager.lookup("org.ametys.odf.enumeration.OdfReferenceTableHelper");
var workflowHelper = serviceManager.lookup("org.ametys.cms.workflow.ContentWorkflowHelper");
var workflowProvider = serviceManager.lookup("org.ametys.plugins.workflow.support.WorkflowProvider");
var count = 0;
jcrXPathQuery("//element(*, ametys:content)[@ametys-internal:contentType = 'odf-enumeration.Period']").forEach(function(period)
{
if (updatePeriod(period))
{
count++;
}
});
print("[info] " + count + " periods have been migrated");
// Update the type field of a period entry from String to Content
function updatePeriod(period)
{
var periodNode = period.getNode();
if (periodNode.hasProperty("ametys:type"))
{
var periodTypeCode = periodNode.getProperty("ametys:type").getString();
if (periodTypeCode.length > 0 && !periodTypeCode.startsWith("content://"))
{
var periodType = getOrCreatePeriodType(periodTypeCode);
if (periodType != null)
{
period.setValue("type", periodType);
period.saveChanges();
doAction(period, 2);
return true;
}
else
{
print("[WARN] Impossible to get or create the period type with the code '" + periodeTypeCode + "'.");
}
}
}
return false;
}
// Create an entry in the PeriodType table ref
function getOrCreatePeriodType(code)
{
var periodTypeEntry = refTableHelper.getItemFromCode("odf-enumeration.PeriodType", code);
if (periodTypeEntry != null)
{
return periodTypeEntry.getContent();
}
var result = workflowHelper.createContent("reference-table", 1, code, code, ["odf-enumeration.PeriodType"], [], "fr");
var periodType = result.get(AbstractContentWorkflowComponent.CONTENT_KEY);
periodType.setValue("code", code);
periodType.saveChanges();
doAction(periodType, 22);
print("[INFO] Period type entry '" + code + "' created");
return periodType;
}
// Update the current workflow state
function doAction(content, actionId)
{
var inputs = new HashMap();
inputs.put(AbstractWorkflowComponent.RESULT_MAP_KEY, new HashMap());
inputs.put(AbstractContentWorkflowComponent.CONTENT_KEY, content);
inputs.put(AbstractWorkflowComponent.FAIL_CONDITIONS_KEY, new ArrayList());
inputs.put(AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY, new HashMap());
try
{
var workflow = workflowProvider.getAmetysObjectWorkflow(content);
workflow.doAction(content.getWorkflowId(), actionId, inputs);
}
catch (e)
{
print("[ERROR] An error occured while do workflow action '" + actionId + "' on content '" + content.getId() + "'", e);
}
} 4 fields linked to the Apogée export have been changed from enumeration to reference table:
The following script must therefore be passed (the data in the reference tables can be adapted by modifying the tables at script)
const filterNameHelper = Java.type("org.ametys.cms.FilterNameHelper");
const odfTableRefHelper = Ametys.serviceManager.lookup("org.ametys.odf.enumeration.OdfReferenceTableHelper");
var cycles = {
'0': 'Pré-universitaire',
'1': 'Premier cycle',
'2': 'Deuxième cycle',
'3': 'Troisième cycle'
};
var inscriptions = {
'1': 'Régime initial',
'2': 'Régime continu'
};
var cips = {
'A': 'CIP Apogée',
'G': 'Eco-Gest'
};
var durations = {
'AN': 'Année',
'PA': 'Pluri-annuelle',
'SE': 'Semestre',
'TR': 'Trimestre'
};
function createTableRef(mapping, contentType)
{
var count = 0;
for (var code in mapping)
{
var item = odfTableRefHelper.getItemFromCode(contentType, code);
if (item == null)
{
var tableRefContent = Content.create({
contentTypes: contentType,
contentTitle: {
fr: mapping[code],
},
contentName: filterNameHelper.filterName(mapping[code]),
workflowName: "reference-table"
});
tableRefContent.setValue("code", code);
tableRefContent.setValue("codeApogee", code);
Content.save(tableRefContent);
count++;
}
}
print(count + " contenu(s) créé(s) pour la table de référence " + contentType);
}
createTableRef(cycles, "odf-enumeration.CycleApogee");
createTableRef(inscriptions, "odf-enumeration.InscriptionType");
createTableRef(cips, "odf-enumeration.Cips");
createTableRef(durations, "odf-enumeration.DurationApogee");
print("Migration des programmes");
migrateContents('org.ametys.plugins.odf.Content.program');
print("Migration des sous-programmes");
migrateContents('org.ametys.plugins.odf.Content.subProgram');
print("Migration des conteneurs");
migrateContents('org.ametys.plugins.odf.Content.container');
print("Migration des ELPs");
migrateContents('org.ametys.plugins.odf.Content.course');
function migrateContents(contentType)
{
var count = {
existing: 0,
done: 0
};
Repository.query("//element(*, ametys:content)[@ametys-internal:contentType = '" + contentType + "']").forEach(function(content) {
count.existing++;
Content.migrate(content,
migrateODFContent,
false,
null,
false);
count.done++;
});
print(count.done + " contents migrated on " + count.existing + " existing contents");
}
function migrateODFContent(content)
{
migrateSingleAttribute(content, "cycleApogee", "odf-enumeration.CycleApogee");
migrateMultipleAttribute(content, "inscription-types", "odf-enumeration.InscriptionType");
migrateMultipleAttribute(content, "cips", "odf-enumeration.Cips");
migrateSingleAttribute(content, "duration-apogee", "odf-enumeration.DurationApogee");
}
function migrateSingleAttribute(content, attributeName, attributeContentType)
{
if (content.hasValue(attributeName))
{
var node = content.getNode();
if (node.isLocked())
{
content.unlock();
}
if (node.hasProperty("ametys:" + attributeName))
{
var property = node.getProperty("ametys:" + attributeName);
var oldVal = property.getString();
var item = odfTableRefHelper.getItemFromCode(attributeContentType, oldVal);
property.remove();
if (item != null)
{
content.setValue(attributeName, item.getId());
}
}
}
}
function migrateMultipleAttribute(content, attributeName, attributeContentType)
{
if (content.hasValue(attributeName))
{
var node = content.getNode();
if (node.isLocked())
{
content.unlock();
}
if (node.hasProperty("ametys:" + attributeName))
{
var newValues = [];
var property = node.getProperty("ametys:" + attributeName);
var values = property.getValues();
for (var i in values)
{
var item = odfTableRefHelper.getItemFromCode(attributeContentType, values[i].getString());
if (item != null)
{
newValues.push(item.getId());
}
}
property.remove();
if (newValues.length != 0)
{
Repository.helper.setProperty(node, attributeName, newValues);
}
}
}
} Recently, state-controlled training courses have been required to bear a label.
The list of existing labels is imposed by the Ministry of Education: https://www.enseignementsup-recherche.gouv.fr/cid141235/labels-des-formations-controlees-par-l-etat. html
Initializing the diploma table
You can run the following script to position a label according to diploma type.
Please note that script is just an example, and the __MAPPING object must be adapted to suit your own diploma types.
// Table de correspondance entre code diplome -> code labelisation
// A PERSONNALISER EN FONCTION DES DIPLOMES EXISTANT
const __MAPPING = {
"XA" : "DNL", // Licence
"XB" : "DNM", // Master
"YA" : "DND", // Doctorat
"YB" : "DND", // Doctorat
"DP" : "LP", // Licence professionnelle
"CB" : "DUT", // DUT
"ZF" : "DCG", // DCG
"ZG" : "DCG", // DCG
"CD" : "DEUST", // DEUST
"FJ" : "Dip_Etat", // Diplôme d'état (chirurgie dentaire)
"IB" : "Dip_Etat", // Diplôme d'état(medecine)
"GD" : "Dip_Etat", // Diplôme d'état (pharmacie)
"FI" : "Dip_Ing", // Diplome d'ingénieur,
"YI" : "Dip_Ing", // Diplome d'ingénieur,
"RD" : "Dip_vise", // Diplôme visé (bac+3)
"RE" : "Dip_vise", // Diplôme visé (bac+2)
"RC" : "Dip_vise", // Diplôme visé (bac+4)
"RB" : "Dip_vise", // Diplôme visé (bac+5)
"RA" : "Dip_vise", // Diplôme visé (bac+5 grade master)
// "__" : "Classe_Prepa", // Classe préparatoire
// "__" : "Classe_Prepa_sans", // Classe préparatoire universitaire
// "__" : "BTS", // BTS
// "__" : "DTS", // DTS,
// "__" : "DMA", // DMA (métiers d'art)
// "__" : "DNMADE", // DNMADE
// "__" : "Grade_L", // Grade licence
// "__" : "Grade_M", // Grade master
}
const CertificationLabelsType = Java.type("org.ametys.odf.enumeration.CertificationLabels");
const CertificationLabels = Ametys.serviceManager.lookup(CertificationLabelsType.ROLE);
let nbcontent = 0;
function _setCertificationLabel(content)
{
var code = content.getValue("code");
if (__MAPPING[code])
{
nbcontent++;
var label = CertificationLabels.getLabelTitle(__MAPPING[code]).getLabel();
print(`#${content.getTitle()} (${code}) -> ${label} (${__MAPPING[code]})`);
content.setValue("certificationLabel", __MAPPING[code]);
}
}
Repository.query("//element(*, ametys:content)[not(@ametys:certificationLabel) and @ametys-internal:contentType='odf-enumeration.Degree']")
.forEach(function(content)
{
Content.migrate(
content,
[_setCertificationLabel],
false /* old versions are still compatible */,
null /* no tag */,
false /* not verbose */
);
}
);
Ametys.console.info(`${nbcontent} degree(s) has been updated`);
Ametys.console.info(`Live workspace has to be rebuilt.`); Initialization of existing training courses
To identify state-controlled training courses, a new boolean "certified" field has been added to the training courses and itineraries.
If most of the training courses are state-controlled, it will be tedious to go through all the training courses to change this field to true.
Below is an example of script to position this field at true for training courses and itineraries according to the following rules:
You can adapt script to suit your own needs.
const RefTableHelperType = Java.type("org.ametys.odf.enumeration.OdfReferenceTableHelper");
const refTableHelper = Ametys.serviceManager.lookup(RefTableHelperType.ROLE);
// Licence
const licenceId = refTableHelper.getItemFromCode("odf-enumeration.Degree", "XA").getId();
// Master
const masterId = refTableHelper.getItemFromCode("odf-enumeration.Degree", "XB").getId();
// Licence pro
const licenceProId = refTableHelper.getItemFromCode("odf-enumeration.Degree", "DP").getId()
function _setCertification(content)
{
content.setValue("certified", true);
}
// Itère sur toutes les formations SAUF les licences, master et licences professionnelles
// et positionne le champ "formation controlée par l'état" à true
let nbcontent = 0;
const predicate = "@ametys:degree != '" + licenceId + "' and @ametys:degree != '" + masterId + "' and @ametys:degree != '" + licenceProId + "'";
Repository.query("//element(*, ametys:programContent)[not(@ametys:certified) and " + predicate + "]")
.forEach(function(content) {
nbcontent++;
Content.migrate(
content,
[_setCertification],
false /* old versions are still compatible */,
null /* no tag */,
false /* not verbose */
);
}
);
Ametys.console.info(`${nbcontent} programs(s) has been marked as certified`);
// Itère sur tous les parcours des licences, master et licences professionnelles
// et positionne le champ "formation controlée par l'état" à true
nbcontent = 0;
const predicate2 = "@ametys:degree = '" + licenceId + "' or @ametys:degree = '" + masterId + "' or @ametys:degree = '" + licenceProId + "'";
Repository.query("//element(*, ametys:programContent)[" + predicate2 + "]")
.forEach(function(program)
{
program.getProgramPartChildren().forEach(function(child){
if (child.getClass().getName() == 'org.ametys.odf.program.SubProgram' && !child.hasValue("certified"))
{
nbcontent++;
Content.migrate(
child,
[_setCertification],
false /* old versions are still compatible */,
null /* no tag */,
false /* not verbose */
);
}
}
);
});
Ametys.console.info(`${nbcontent} subprograms(s) has been marked as certified`);
Ametys.console.info(`Live workspace has to be rebuilt.`); The new competency-based approach is leading to changes in models:
First, run the following script to migrate skills to your ODF content:
const RefTableHelperType = Java.type("org.ametys.odf.enumeration.OdfReferenceTableHelper");
const refTableHelper = Ametys.serviceManager.lookup(RefTableHelperType.ROLE);
const ContentDataHelper = Java.type("org.ametys.cms.data.ContentDataHelper");
const ConsoleHelper = Java.type("org.ametys.workspaces.repository.ConsoleHelper");
function _getSkillsId(content, attributeName)
{
var skillIds = [];
if (content.getNode().hasProperty('ametys:' + attributeName))
{
var skillSets = content.getNode().getProperty('ametys:' + attributeName).getValues();
if (skillSets.length > 0)
{
for (var i in skillSets)
{
var skillSetId = skillSets[i].getString();
var skillSet = Repository.resolver.resolveById(skillSetId);
if (skillSet.getNode().hasProperty('ametys:skills'))
{
var skills = skillSet.getNode().getProperty('ametys:skills').getValues();
for (var j in skills)
{
skillIds.push(skills[j].getString());
}
}
}
}
}
return skillIds;
}
function _setSkills(content, attributeName, skillIds)
{
if (skillIds.length > 0)
{
ConsoleHelper.setProperty(content.getNode(), "ametys:" + attributeName, skillIds);
}
}
function _removeSkillSets(content, attributeName)
{
if (content.getNode().hasProperty('ametys:' + attributeName))
{
content.getNode().getProperty('ametys:' + attributeName).remove();
}
}
function _migrateSkills(content)
{
var requiredSkills = _getSkillsId(content, 'requiredSkillSets');
_setSkills(content, 'requiredSkills', requiredSkills);
_removeSkillSets(content, 'requiredSkillSets');
var acquiredSkills = _getSkillsId(content, 'acquiredSkillSets');
_setSkills(content, 'acquiredSkills', acquiredSkills);
_removeSkillSets(content, 'acquiredSkillSets');
}
function _migrateCourseSkills(content)
{
var requiredSkills = _getSkillsId(content, 'requiredSkillSets');
_setSkills(content, 'requiredSkills', requiredSkills);
_removeSkillSets(content, 'requiredSkillSets');
_setRepeaterSkillSet(content, 'acquiredSkillSets', 'acquiredSkills');
_removeSkillSets(content, 'acquiredSkillSets');
}
function _setRepeaterSkillSet(content, attributeName, repeaterName)
{
var node = content.getNode();
if (node.hasProperty('ametys:' + attributeName))
{
var skillSets = node.getProperty('ametys:' + attributeName).getValues();
if (skillSets.length > 0)
{
if (node.hasNode('ametys:' + repeaterName))
{
node.getNode('ametys:' + repeaterName).remove();
}
var repeater = node.addNode('ametys:' + repeaterName, 'ametys:compositeMetadata');
var index1 = 1;
for (var i in skillSets)
{
var skillSetId = skillSets[i].getString();
var entry = repeater.addNode('ametys:' + index1++, 'ametys:compositeMetadata');
entry.setProperty('ametys:skillSet', skillSetId);
var skillSet = Repository.resolver.resolveById(skillSetId);
if (skillSet.getNode().hasProperty('ametys:skills'))
{
var skills = skillSet.getNode().getProperty('ametys:skills').getValues();
var skillRep = entry.addNode('ametys:skills', 'ametys:compositeMetadata');
var index2 = 1;
for (var j in skills)
{
var skillEntry = skillRep.addNode('ametys:' + index2++, 'ametys:compositeMetadata');
skillEntry.setProperty("ametys:skill", skills[j].getString());
}
}
}
}
}
}
let nbcontent = 0;
Repository.query("//element(*, ametys:programContent)[@ametys:requiredSkillSets or @ametys:acquiredSkillSets]")
.forEach(function(content) {
nbcontent++;
Content.migrate(
content,
[_migrateSkills],
false /* old versions are still compatible */,
null /* no tag */,
false /* not verbose */
);
}
);
Ametys.console.info(`${nbcontent} programs(s) has been migrated`);
nbcontent = 0;
Repository.query("//element(*, ametys:subProgramContent)[@ametys:requiredSkillSets or @ametys:acquiredSkillSets]")
.forEach(function(content) {
nbcontent++;
Content.migrate(
content,
[_migrateSkills],
false /* old versions are still compatible */,
null /* no tag */,
false /* not verbose */
);
}
);
Ametys.console.info(`${nbcontent} subprograms(s) has been migrated`);
nbcontent = 0;
Repository.query("//element(*, ametys:courseContent)[@ametys:requiredSkillSets or @ametys:acquiredSkillSets]")
.forEach(function(content) {
nbcontent++;
Content.migrate(
content,
[_migrateCourseSkills],
false /* old versions are still compatible */,
null /* no tag */,
false /* not verbose */
);
}
);
Ametys.console.info(`${nbcontent} course(s) has been migrated`);
Ametys.console.info(`Live workspace has to be rebuilt.`); Then run the following script (only if the 1st script was successful!) to remove the link between the Skills and Skill Blocks reference tables:
let nbcontent = 0;
function _removeSkills(content)
{
if (content.hasValue('ametys:skills'))
{
content.getProperty('ametys:skills').remove();
nbcontent++;
}
}
function _removeSkillSets(content)
{
if (content.hasValue('ametys:skillSets'))
{
content.getProperty('ametys:skillSets').remove();
}
}
Repository.query("//element(*, ametys:content)[@ametys-internal:contentType = 'odf-enumeration.SkillSet']")
.forEach(function(content) {
nbcontent++;
Content.migrate(
content,
[_removeSkills],
false /* old versions are still compatible */,
null /* no tag */,
false /* not verbose */
);
}
);
Ametys.console.info(`${nbcontent} skill set(s) has been migrated`);
Repository.query("//element(*, ametys:content)[@ametys-internal:contentType = 'odf-enumeration.Skill']")
.forEach(function(content) {
nbcontent++;
Content.migrate(
content,
[_removeSkillSets],
false /* old versions are still compatible */,
null /* no tag */,
false /* not verbose */
);
}
);
Ametys.console.info(`${nbcontent} skill(s) has been migrated`); After applying these scripts, run a Live