Data migration 4.1 to 4.3


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.   

  1. Merging contacts
  2. Management workflow does not version
  3. Period type
  4. Apogée fields migrated to reference table
  5. Course label management
  6. Competency-based approach

Merging contacts

For greater flexibility, course, training and ELP contacts have been merged into a single "repeater" of contacts with different roles:

  • on training courses and itineraries, the "personInCharge" repeater and the "contact" multiple field (administrative contacts) are replaced by the "contacts" repeater. Each repeater entry is made up of a role, and a multiple field for persons.
  • on ELPs, the multiple fields "personInCharge" (managers) and "contact" (administrative contacts) are replaced by the "contacts" repeater. Each repeater entry is made up of a role, and a multiple field for persons

Run the following script to migrate the contacts for your courses, pathways and ELPs.
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");                     

Management workflow does not version

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);                   
    }                   
});                   

Period type

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);   
    }   
}   

Apogée fields migrated to reference table

4 fields linked to the Apogée export have been changed from enumeration to reference table:

  • Cycle
  • Authorized listing plans
  • CIP list
  • Stage duration

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);             
            }             
        }             
    }             
}             

Course label management

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:

  • The "certified" field is set to true for all courses other than bachelor's, master's and professional bachelor's degrees.
  • The "certified" field is set to true for (direct) courses of a bachelor's, master's or professional license type.

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.`);          

Competency-based approach

The new competency-based approach is leading to changes in models:    

  • added "Acquisition level" reference table
  • suppression des liens Blocs de compétences <-> Compétences
  • Training/Course :
    • requiredSkillSets (multiple value of Skill Blocks) becomes requiredSkills (multiple value of Skills)
    • acquiredSkillSets is deleted
  • ELP
    • requiredSkillSets (multiple value of Skill Blocks) becomes requiredSkills (multiple value of Skills)
    • acquiredSkillSets (multiple value of Skill Blocks) becomes acquiredSkills, which is a repeater of a Skill Block, itself a repeater of a Skill with an acquisition level.

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

Back to top