Avant d'appliquer les migrations Ametys ODF, vous devez appliquer les migrations Ametys CMS 4.2.x vers 4.3.x
Ce guide traite de la migration des données d'une version Ametys ODF 4.1.x à 4.3.0.
Reportez-vous également au guide de migration technique et au guide de migration graphique.
Pour plus de souplesse, les contacts des formations, parcours et ELPs ont été fusionnés dans un seul "repeater" de contacts avec des rôles différents:
Exécutez le script suivant pour migrer les contacts de vos formations, parcours et ELP.
Personnalisez si besoin le code et le titre pour le role des contacts administratifs ainsi que le code et le titre pour le role des responsables pédagogique.
// 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);
}
}); Le champ "Type de période" de la table de référence "Période" devient une table de référence.
Pour migrer les données de la table de référence "Période", exécutez le script de migration suivant (il transforme les chaînes de caractères existantes en entrée dans la nouvelle table de référence "Type de période" et fait le lien avec la table de référence "Periode")
// 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 champs liés à l'Export Apogée sont passés de énumération à table de référence :
Il faut donc passer le script suivant (les données des tables de référence peuvent être adaptées en modifiant les tableaux de 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);
}
}
}
} Depuis peu, il est nécessaire d'apposer un label aux formations contrôlée par l'Etat.
La liste des labels existant est imposée par le ministère de l'Enseignement: https://www.enseignementsup-recherche.gouv.fr/cid141235/labels-des-formations-controlees-par-l-etat.html
Initialisation de la table des diplômes
Vous pouvez exécuter le script suivant pour positionner un label en fonction du type de diplôme.
Attention ce script n'est qu'un exemple, l'objet __MAPPING doit être adapté en fonction de vos propres types de diplômes.
// 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.`); Initialisation des formations/parcours existants
Pour identifier les formations contrôlées par l'Etat, un nouveau champ "certified" de type boolean a été ajouté au niveau des formations et parcours.
Si la plupart des formations sont des formations contrôlées par l'Etat, il sera fastidieux de passer sur toutes les formations pour modifier ce champ à true.
Voici ci-dessous un exemple de script pour positionner ce champ à true pour les formations et parcours selon les règles suivantes :
Vous pouvez adapter ce script selon vos propres besoins.
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.`); La nouvelle approche par compétence entraîne des modifications de modèles :
En premieu lieu, exécutez le script suivant pour migrer les compétences sur vos contenus ODF :
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.`); Puis exécutez le script suivant (uniquement si le 1er script s'est bien déroulé !) pour supprimer le lien entre les tables de référence Compétences et Blocs de compétences :
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`); Après l'application de ces scripts, lancez une reconstruction du Live