Technical migration 4.0 to 4.1

  1. Exclusion of the "PJ modification" ribbon action
  2. Republish CDM-fr training courses on portals or shared folders
  3. Separation of teaching hours
    1. Data
    2. Workflow
    3. Import CDM-fr
    4. Apogée import
  4. Catalog copy
  5. Hierarchical reference table categories with multilingual titles
  6. Service migration
  7. Export folder CDM-fr
  8. Modification of urls for training pages and courses

Exclusion of the "PJ modification" ribbon action

The "Edit PJs" action appears in the ribbon if you haven't updated your workflow, taking the template ODF or ODF-Web as your model.

Action 12 must be excluded from buttons with the identifier org.ametys.odf.course.workflow.WorkflowSteps, org.ametys.odf.program.workflow.WorkflowSteps, org.ametys.odf.subprogram.workflow.WorkflowSteps in the workflow-actions tag if it is in exclude mode.

Republish CDM-fr training courses on portals or shared folders

This migration is only necessary if you want to add a republish button CDM-fr from training courses to portals or shared folders, or if you want to replace a republish button you had defined at project level with what is proposed at kernel level.

This option is automatically disabled if the configuration does not send CDM-fr to a remote server or deposit CDM-fr files in a folder.

To benefit from it, you must do the following:

  • In the plugin.xml file of plugin default-odf-workflow, add the following two features (The first defines the button, the second calls the ribbon that places the button in the right place):
<feature name="workflow.republish">       
        <extension id="org.ametys.odf.program.workflow.Republish"       
            <class name="Ametys.plugins.cms.content.controller.SmartContentController">       
                <label i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH</label>       
                <description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION</description>       
                <allright-start-description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION_START</allright-start-description>       
                <allright-end-description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION_END</allright-end-description>       
                <allright-content-description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION_CONTENT</allright-content-description>       
                <error-description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION_ERROR</error-description>       
                <workflowaction-start-description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION_WORKFLOWACTION_START</workflowaction-start-description>       
                <workflowaction-end-description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION_WORKFLOWACTION_END</workflowaction-end-description>       
                <workflowaction-content-description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION_WORKFLOWACTION_CONTENT</workflowaction-content-description>       
                <locked-start-description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION_LOCKED_START</locked-start-description>       
                <locked-end-description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION_LOCKED_END</locked-end-description>       
                <locked-content-description i18n="true">plugin.odf:PLUGINS_ODF_REPUBLISH_DESCRIPTION_LOCKED_CONTENT</locked-content-description>       
                <file plugin="cms">js/Ametys/plugins/cms/content/controller/SmartContentController.js</file>       
                <file plugin="cms">js/Ametys/plugins/cms/content/actions/WorkflowAction.js</file>       
                <file plugin="odf">js/Ametys/plugins/odf/program/ProgramActions.js</file>       

<feature name="org.ametys.plugins.odf.workflow.ribbon.imports">       
        <extension id="org.ametys.plugins.odf.workflow.ribbon.Imports"       
            <workspace match="cms|web">       
  • In plugin default-odf-workflow, add the file ribbon/cms-ribbon.xml:
<?xml version="1.0" encoding="UTF-8"?>       
<ribbon xmlns:xsi="" xsi:noNamespaceSchemaLocation="">       
        <tab label="plugin.odf:RIBBON_TABS_TAB_ODF_PROGRAM_LABEL" override="true">       
                <group label="plugin.odf:RIBBON_TABS_TAB_CONTENT_GROUPS_GROUP_WORKFLOW_LABEL" override="true" controlsOrder="5">       
                    <layout size="small">       
                        <control id="org.ametys.odf.program.workflow.Republish"/>       
  • In the I18N files of plugin default-odf-workflow (i18n/messages_*.xml), add the following keys (English and French) useful for the additional workflow action (see below):
<message key="WORKFLOW_ACTION_REPUBLISH">Republier</message>       
<message key="WORKFLOW_ACTION_REPUBLISH_DESCRIPTION">Force la republication du contenu sur le portail distant.</message>       
<message key="WORKFLOW_ACTION_REPUBLISH_ACTION_DESCRIPTION">&lt;strong&gt;{user}&lt;/strong&gt; a republié le contenu sur le portail.</message>       
<message key="WORKFLOW_ACTION_REPUBLISH">Republish</message>       
<message key="WORKFLOW_ACTION_REPUBLISH_DESCRIPTION">Republish the selected content(s) on portal.</message>       
<message key="WORKFLOW_ACTION_REPUBLISH_ACTION_DESCRIPTION">&lt;strong&gt;{user}&lt;/strong&gt; has republished the content.</message>       
  • Finally, in the training workflow named program.xml or workflow-program.xml in WEB-INF/param[/workflows], it is necessary to add the republishing action 9001 in the validated state 3 :
<!-- Republish Action -->       
<action id="9001" name="plugin.default-odf-workflow:WORKFLOW_ACTION_REPUBLISH">       
    <restrict-to>         <conditions type="AND">       
            <condition type="avalon">       
                <arg name="role">org.ametys.cms.workflow.ContentCheckRightsCondition</arg>       
                <arg name="right">ODF_Rights_Program_Validate</arg>       
            <condition type="avalon">       
                <arg name="role">org.ametys.cms.workflow.LockCondition</arg>       
            <condition type="avalon">       
                <arg name="role">org.ametys.cms.workflow.ValidateMetadataCondition</arg>       
        <unconditional-result old-status=" " status=" " step="3" />       
        <function type="avalon">       
            <arg name="role">org.ametys.odf.cdmfr.SendCDMFRFunction</arg>       
        <function type="avalon">       
            <arg name="role">org.ametys.odf.cdmfr.DepositCDMFRFunction</arg>       

Separation of teaching hours

As the totalDurationOf* data has completely disappeared, all references to it must be migrated. In particular, this means that if you have overloaded the ELP content type (race), you must delete the references to the totalDurationOf* data and replace them with a reference to the courseParts metadata.


Data migration is performed automatically via an initialization class.

It can be deactivated once the data has been migrated, using the odf-web/init.course-part feature, but is not essential.
Rebuild indexes and live after the first M3 boot.


There's a new worfklow, the teaching hours workflow (or CoursePart), in the file workflows/course-part.xml. You also need to add its label to the application.xml :

<message key="WORKFLOW_coursepart">Heures d'enseignement</message>                      

The ELP workflow (workflows/course.xml) has been updated to add, after the validation of an ELP content, the validation of the teaching hours attached to it.
You must therefore add to workflows/course.xml, after each call to the org.ametys.odf.workflow.ValidateODFContentFunction, the call to the org.ametys.odf.workflow.MoveLiveTagOnCoursePartFunction :

<function type="avalon">                              
    <arg name="role">org.ametys.odf.workflow.MoveLiveTagOnCoursePartFunction</arg>                              

Import CDM-fr

For CDM-fr imports not Ametys or prior to Ametys v4.2 (ODF 4.1), if in the CDM-fr synchronization, you have overloaded the templates named course-totalDurationOf*, they must be modified so that they import CoursePart in the following form:

<xsl:template name="course-totalDurationOfCM">                              
    <xsl:if test="cdmfr:credits/cdmfr:globalVolume[@teachingtype='CM']">                              
            <nbHours><xsl:value-of select="cdmfr:credits/cdmfr:globalVolume[@teachingtype='CM']"/></nbHours>                              

To import Ametys v4.2 (ODF 4.1), use the template named course-courseParts.

Apogée import

A query has been added to the apogee-requests file.xml :

<select id="getCourseParts" parameterType="java.util.Map" resultType="map">                             
    <include refid="Functions.isNotBlank" />                              
    SELECT DISTINCT                              
    FROM ELP_CHG_TYP_HEU                              
    WHERE upper(COD_ELP)=upper(#{apogeeSyncCode})                              
    <if test="#fn = isNotBlank, #fn(year)">                              
        AND (COD_ANU IS NULL OR COD_ANU = #{year})                              

At the same time, the query that retrieves data from an ELP with hourly volumes has been simplified. Hourly volumes are no longer stored in the same place, and can now be multiple for the same type of teaching. All references to the ELP_CHARGE_ENS table have therefore been removed from the searchCourses query.

The associated mapping in apogee-mapping.xml also :


So if you have overloaded apogee-mapping.xml and/or apogee-requests.xml, you will need to compare them with the new kernel versions.

Catalog copy

A new streamlined workflow action has been created for catalog copying (and translation) to reduce copying time. Action 210 must be added to step 1 of the program, subprogram, container, courselist, course and course-part workflows:

<!-- Edit by copy Action -->                   
<action id="210" name="plugin.default-odf-workflow:WORKFLOW_ACTION_EDIT_BY_COPY">                   
            <condition type="avalon">                   
                <arg name="role">org.ametys.cms.workflow.LockCondition</arg>                   
        <unconditional-result old-status=" " status=" " step="1" />                   
        <function type="avalon">                   
            <arg name="role">org.ametys.cms.workflow.ExtractOutgoingReferencesFunction</arg>                   
        <function type="avalon">                   
            <arg name="role">org.ametys.odf.workflow.copy.NotifyCopyFunction</arg>                   

Cette action est une interne qui doit être exclue des menus de workflows. Pour cela dans le plugin.xml de votre plugin default-odf-workflow, ajoutez <action>210</action> dans la section <workflow-actions mode="exclude"> pour la déclaration des menus suivants :

  • org.ametys.odf.course.workflow.WorkflowSteps
  • org.ametys.odf.program.workflow.WorkflowSteps
  • org.ametys.odf.subprogram.workflow.WorkflowSteps
  • org.ametys.odf.container.workflow.WorkflowSteps
  • org.ametys.odf.courselist.workflow.WorkflowSteps

If in doubt, refer to plugin default-odf-workflow for templates ODF and ODF web.

Hierarchical reference table categories with multilingual titles

Reference table category titles are now multilingual.

Run the following script to migrate existing categories:

var count = 0;                
function _titleMigration(content)                
    var titleProp = content.getNode().getProperty("ametys:title");                
    var title = titleProp.getString();                
    content.getMetadataHolder().setMetadata("title", title, new java.util.Locale("fr"));                
jcrXPathQuery("//element(*, ametys:content)[(@ametys-internal:contentType = 'odf-enumeration.CodeErasmusCategory' or @ametys-internal:contentType = 'odf-enumeration.CodeFapCategory' or @ametys-internal:contentType = 'odf-enumeration.EnseignementNatureCategory') and @ametys:title]")                
    .forEach(function (content)                
            true /* old versions incompatible */,                
            null /* no tag */,                
            false /* not verbose */                
print(count + " reference table entries have been migrated");                

Service migration

ODF services, like all other services, need to be migrated following changes to the storage format.
Go to script on the page dedicated to migrating services.

Export folder CDM-fr

If you have activated CDM-fr export to disk when validating courses (in the configuration parameters), the export folder is no longer configurable.

The CDM-fr files are now stored in the $AMETYS_HOME/data/odf/cdmfr folder.

You can move your existing files from the old export directory to this new folder.

Please note that if you have a third-party application that uses this folder, you'll need to modify it.

Modification of urls for training pages and courses

Previously, URLs for virtual pages were made up of the course title, followed by the content name (unique in Ametys).
For example: catalogue/master-XB/droit-economie-gestion-DEG/master-droit-program-fruai07517h489j10u.html

They are now based on the course title, followed by the course code (just like the ELPs)
For example: catalogue/master-XB/droit-economie-gestion-DEG/master-droit-h489j10u.html

Your old URL will be automatically redirected to the new URL.

Consequently, the hyphen "-" is forbidden in the code for courses and itineraries. To ensure that the code is correct for these elements, go to script , which will modify them if necessary.

Download script (1.1 KB)

var count = 0;    
function _updateCode(content)    
    var code = content.getMetadataHolder().getString("code");    
    code = code.replaceAll("-", "_");    
    content.getMetadataHolder().setMetadata("code", code);       

jcrXPathQuery("//element(*, ametys:programContent)[jcr:contains(@ametys:code, '*-*')]").forEach(function(content) {                        
          false /* old versions incompatible */,               
          null /* no tag */,               
          false /* not verbose */               

print(count + " programs has been updated");    

count = 0;    

jcrXPathQuery("//element(*, ametys:subProgramContent)[jcr:contains(@ametys:code, '*-*')]").forEach(function(content) {                        
          false /* old versions incompatible */,               
          null /* no tag */,               
          false /* not verbose */               

print(count + " subprograms has been updated");    

If script indicates that more than one course has been modified, rebuild the complete Live.


Back to top