Manuel de migration technique de la version 2.5.3 vers la version 2.6.0

  1. Modification de l'import de champ "mention" par CDMfr
  2. Publication vers un portail
  3. Duplication d'un catalogue
  4. Dépublier une formation depuis un dispositif de saisie ODF non web
    1. 1) Ajoutez le droit dépublication
    2. 2) Ajoutez l'action de workflow
    3. Ajoutez le bouton de dépublication
  5. Dépublier une formation sur depuis un dispositif de saisie ODF-web
  6. Eléments pédagogiques par catalogue de formation
    1. Ajout de la métadonnée "catalog" sur les ELPs
    2. Ajout de l'action de workflow pour créer un ELP par copie
    3. Migration des ELPs existants
    4. Ajout du critère et de la colonne résultat "Catalogue" sur l'outil de recherche des ELPs
    5. Choix du catalogue pour le service de recherche des éléments pédagogiques
    6. Synchronisation du catalogue entre un dispositif de saisie et un portail ODF

Modification de l'import de champ "mention" par CDMfr

Le format XML attendu pour le traitement de la mention a été modifié :

Format attendu en 2.5.3Format attendu en 2.5.4
<mention type="string">Libellé de la mention</mention>
<mention type="string" constant="MENTION">CODE_MENTION</mention>

Si vous avez surchargé les XSL d'import pour le format CDMfr, assurez-vous de fournir la mention au format attendu

Traitement XSL de la mention en 2.5.3Traitement XSL de la mention en 2.5.4
<xsl:template name="program-mention">
        <mention type="string">
            <xsl:choose>
                <xsl:when test="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:fieldName/cdmfr:controlled">
                    <xsl:value-of select="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:fieldName/cdmfr:controlled/@fieldNameCode"/>
                </xsl:when>
                <xsl:when test="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:fieldName/cdmfr:free">
                    <xsl:value-of select="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:fieldName/cdmfr:free"/>
                </xsl:when>
            </xsl:choose>
        </mention>
</xsl:template>
<xsl:template name="program-mention">
        <mention type="string" constant="MENTION">
            <xsl:choose>
                <xsl:when test="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:fieldName/cdmfr:controlled">
                    <xsl:value-of select="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:fieldName/cdmfr:controlled/cdmfr:registeredName"/>
                </xsl:when>
                <xsl:when test="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:fieldName/cdmfr:free">
                    <xsl:value-of select="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:fieldName/cdmfr:free"/>
                </xsl:when>
            </xsl:choose>
        </mention>
    </xsl:template>

Publication vers un portail

Si vous publiez votre offre de formation depuis un dispositif de saisie ODF-Web vers un portail ODF, un bug faisait que les liens vers les pages internes du dispositif de saisie ne fonctionnait pas sur le portail : https://issues.ametys.org/browse/ODF-1297

Pour corriger cela, vous devez modifier les fichiers WEB-INF/param/workflow-program.xml de vos applications ODF-Web (attention, les applications ODF seul ne doivent pas être modifiées).

Dans ces fichiers recherchez et remplacez org.ametys.odf.cdmfr.SendCDMFRFunction par org.ametys.plugins.odfweb.workflow.SendCDMFRFunction

Duplication d'un catalogue

Afin de corriger le bug https://issues.ametys.org/browse/ODF-1314, vous devez modifier le fichier de workflow WEB-INF/param/workflow-program.xml en ajoutant l'action suivante dans la section <initial-actions>

<initial-actions>
	[...]
	<!-- Create container or subprogram by copy -->
	<action id="100" name="plugin.odf:WORKFLOW_ACTION_CREATE">
   		<results>
       		<unconditional-result old-status=" " status=" " step="0" />
   		</results>
	</action>
</initial-actions>

Dépublier une formation depuis un dispositif de saisie ODF non web

Cette section concerne uniquement les dispositifs de saisie ODF non Web, c'est à dire, n'utilisant pas le plugin ODF-Web

Une action de workflow de dépublication d'une formation a été ajoutée afin de pouvoir dépublier une formation publiée sur un portail ODF. Cette nouvelle action n'a de sens que si le dispositif de saisie publie ses formations sur un portail ODF.
Il est cependant nécessaire de suivre les étapes suivantes dans tous les cas :

1) Ajoutez le droit dépublication

Dans le fichier WEB-INF/param/rights.xml, ajoutez le droit Workflow_Rights_Unpublish

<right id="Workflow_Rights_Unpublish">
	<label>APPLICATION_RIGHTS_UNPUBLISH_LABEL</label>
	<description>APPLICATION_RIGHTS_UNPUBLISH_DESCRIPTION</description>
	<category>plugin.cms:PLUGINS_CMS_RIGHTS_CONTENT_CATEGORY</category>
</right>

Ajoutez les traductions du libellé et de la description du droit dans les fichiers WEB-INF/i18n/application.xml et WEB-INF/i18n/application_en.xml

application.xml

<message key="APPLICATION_RIGHTS_UNPUBLISH_LABEL">Dépublication</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_DESCRIPTION">Autorise à dépublier un contenu</message> 

application_en.xml

<message key="APPLICATION_RIGHTS_UNPUBLISH_LABEL">Unpublish</message>
<message key="APPLICATION_RIGHTS_UNPUBLISH_DESCRIPTION">Allow unpublish action on contents</message> 
2) Ajoutez l'action de workflow

Ajoutez ensuite l'action de workflow n°10 permettant la dépublication d'une formation dans le fichier WEB-INF/param/workflow-program.xml
L'action est disponible depuis les états brouillon, proposé ou validé.

<common-actions>
   [...]
   <action id="10" name="plugin.odf:WORKFLOW_ACTION_UNPUBLISH">
   		<restrict-to>
        	<conditions type="AND">
            	<condition type="avalon">
                	<arg name="role">org.ametys.odf.workflow.ODFContentCheckRightsCondition</arg>
                    <arg name="right">Workflow_Rights_Unpublish</arg>
                </condition>
                <condition type="avalon">
                	<arg name="role">org.ametys.cms.workflow.LockCondition</arg>
                </condition>
                <condition type="avalon">
                	<arg name="role">org.ametys.odf.workflow.ODFContentPublishedCondition</arg>
                </condition>
            </conditions>
		</restrict-to>
        <pre-functions>
        	<function type="avalon">
            	<arg name="role">org.ametys.odf.workflow.UnpublishODFContentFunction</arg>
            </function>
       	</pre-functions>
        <results>
        	<unconditional-result old-status=" " status=" " step="1" />
        </results>
        <post-functions>
        	<function type="avalon">
            	<arg name="role">org.ametys.odf.cdmfr.DeleteRemoteProgramFunction</arg>
            </function>
       </post-functions>
	</action>
</common-actions>
<step id="1" name="plugin.odf:WORKFLOW_STATE_DRAFT">
	<actions>
		[...]
		<!-- Unpublish action -->
        <common-action id="10" />
		[...]
	</actions>
</step>
<step id="2" name="plugin.odf:WORKFLOW_STATE_PROPOSED">
	<actions>
		[...]
		<!-- Unpublish action -->
        <common-action id="10" />
		[...]
	</actions>
</step>
<step id="3" name="plugin.odf:WORKFLOW_STATE_VALIDATED">
	<actions>
		[...]
		<!-- Unpublish action -->
        <common-action id="10" />
		[...]
	</actions>
</step>
Ajoutez le bouton de dépublication

Dans le fichier plugin.xml de votre plugin default-odf-workflow, rajoutez enfin la déclaration du bouton de dépublication :

<extension id="org.ametys.odf.program.workflow.Unpublish"
           point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
           class="org.ametys.cms.clientsideelement.SmartContentClientSideElement">
    <action class="org.ametys.cms.ribbon.button.SmartContentButton">
         <param name="action">org.ametys.odf.program.Unpublish</param>
         <param name="enable-multiselection">true</param>
         <param name="workflow-name">.*</param>
                
         <param name="label" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_LABEL</param>
         <param name="default-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION</param>
                    
         <param name="icon-small" file="true" plugin="odf">img/actions/unpublish_16.png</param>
         <param name="icon-medium" file="true" plugin="odf">img/actions/unpublish_32.png</param>
         <param name="icon-large" file="true" plugin="odf">img/actions/unpublish_48.png</param>
                    
         <param name="allright-start-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION_START</param>
         <param name="allright-end-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION_END</param>
         <param name="allright-content-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION_CONTENT</param>
         <param name="error-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION_ERROR</param>
                    
         <param name="enabled-on-workflow-action-only">10</param>
         <param name="workflowaction-start-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION_WORKFLOWACTION_START</param>
         <param name="workflowaction-end-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION_WORKFLOWACTION_END</param>
         <param name="workflowaction-content-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION_WORKFLOWACTION_CONTENT</param>
                    
         <param name="enabled-on-unlock-only">true</param>
         <param name="locked-start-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION_LOCKED_START</param>
         <param name="locked-end-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION_LOCKED_END</param>
         <param name="locked-content-description" i18n="true">plugin.odf:PLUGINS_ODF_UNPUBLISH_DESCRIPTION_LOCKED_CONTENT</param>
   </action>
   <scripts>
         <file plugin="cms">js/org/ametys/cms/ribbon/button/SmartContentButton.js</file>
         <file plugin="odf">js/org/ametys/odf/program/ProgramActions.i18n.js</file>
   </scripts>
   <right>Workflow_Rights_Unpublish</right>
</extension>

Le bouton sera disponible si le contributeur a le droit de dépublication et que la formation a été validée au moins une fois.

Pensez à ajouter le droit de dépublication à vos contributeurs !

Dépublier une formation sur depuis un dispositif de saisie ODF-web

Cette section concerne uniquement les dispositifs de saisie ODF Web, qui publie leur offre de formation sur un portail.
Si vous avez un dispositif de saisie ODF non web, vous devez suivre les indications du chapitre précédent

L'action de workflow de dépublication existe déjà sur les applications ODF-Web.

Si les formations sont publiées sur un portail, vous devez simplement rajouter une instructions dans le fichier de workflow afin de supprimer une formation sur le portail, suite à sa dépublication depuis le dispositif de saisie.

Pour cela, dans le fichier de workflow WEB-INF/param/workflow-program.xml du dispositif,rajoutez la post-fonction org.ametys.odf.cdmfr.DeleteRemoteProgramFunction à l'action de dépublication n°10

<action id="10" name="plugin.web:WORKFLOW_ACTION_UNPUBLISH">
	[...]
	<results>
       <unconditional-result old-status=" " status=" " step="1" />
    </results>
	<post-functions>
        <function type="avalon">
        	<arg name="role">org.ametys.odf.cdmfr.DeleteRemoteProgramFunction</arg>
        </function>
    </post-functions>
</action>

Eléments pédagogiques par catalogue de formation

A partir de la version 2.6.0, les éléments pédagogiques doivent nécessairement appartenir à un catalogue, tout comme les formations.

Ainsi la duplication de catalogue duplique également les éléments pédagogiques (les éléments pédagogiques se sont plus partagés entre catalogue).

Ajout de la métadonnée "catalog" sur les ELPs

Le modèle des ELPs (ou UE) doit nécessairement contenir une métadonnée "catalog". Si vous avez surchargé le modèle des ELPs (type de contenu org.ametys.plugins.odf.Content.course), vous devez ajouter cette métadonnée.

Si vous utilisez le type de contenu standard pour les ELPs, passez à la section suivante.

<cms:metadata name="catalog" type="string">
    <label i18n="true">PLUGINS_ODF_PROGRAM_CATALOG</label>
    <description i18n="true">PLUGINS_ODF_PROGRAM_CATALOG_DESC</description>    
    <widget>sorted-enumeration</widget>    
    <widget-params>
         <param name="forceItemSelection">true</param>
    </widget-params>
    <enumeration>
    	<custom-enumerator class="org.ametys.odf.catalog.CatalogEnumerator"/>
    </enumeration>
</cms:metadata>        

Attention, n'ajoutez pas la référence à cette nouvelle métadonnée dans la vue d'édition. En effet le catalogue ne doit pas être modifier depuis le formulaire d'édition car il nécessite des vérifications.

Ajoutez la référence à cette métadonnée dans les vues "details", "index" et "main" en mode "view"

<cms:metadata-set name="index" type="view">
    <cms:metadata-ref name="title" />
    <cms:metadata-ref name="keywords" />
    <cms:metadata-ref name="catalog" />
    <cms:metadata-ref name="teachingActivity" />
    <cms:metadata-ref name="formofteachingOrg" />
    <cms:metadata-ref name="formofteachingMethod" />
    <cms:metadata-ref name="orgUnit" />
    <cms:metadata-ref name="teachingTerm" />
    <cms:metadata-ref name="erasmusCode" />
    <cms:metadata-ref name="timeSlot" />
    <cms:metadata-ref name="teachingLocation" />
    <cms:metadata-ref name="level" />
</cms:metadata-set>

Ajout de l'action de workflow pour créer un ELP par copie

Pour pouvoir copier les ELPs lors de la duplication d'un catalogue de formation, modifiez le fichier de workflow WEB-INF/param/workflow-course.xml pour y ajouter l'action de création par copie dans la section <initial-actions>

<initial-actions>
	[...]
	<!-- Create course by copy -->
    <action id="100" name="plugin.odf:WORKFLOW_ACTION_CREATE">
    	<results>
        	<unconditional-result old-status=" " status=" " step="1" />
        </results>
    </action>
</initial-actions>

 

Migration des ELPs existants

Exécutez le script suivant pour automatiquement positionner le nom du catalogue d'appartenance sur l'ensemble des ELPs. Le catalogue de l'ELP est récupéré à partir des formations d'appartenance.

Si un ELP est attaché à plusieurs formations de catalogues différents, un message d'erreur sera affiché. La migration doit alors se faire manuellement. Il sera nécessaire de supprimer l'ELP d'une ou une plusieurs formations de manière à ce que l'ELP n'appartienne qu'à des formations un seul et même catalogue. Vous pourrez alors ré-exécuter ce script.

  
var qm = session.getWorkspace().getQueryManager();
    
var getCatalogs = function (clContainer)
{
  var parentNode = clContainer;
  var catalog = null;
    
  while (parentNode != null && catalog == null)
  {
      if (parentNode.getPrimaryNodeType().getName() == 'ametys:courseContent')
      {
         var query = qm.createQuery("//element(*, ametys:courseList)[@ametys:coursesReferences = 'courseContent://" + parentNode.getUUID() + "']",   javax.jcr.query.Query.XPATH);
         var clNodes = query.execute().getNodes();
   
         var catalogs = [];
         while (clNodes.hasNext())
         {
            var clCatalogs = getCatalogs(clNodes.next().getParent());
            for (var i=0; clCatalogs.length; i++)
            {
                if (catalogs.indexOf(clCatalogs[i]) == -1)
                {
                    catalogs.push(clCatalogs[i]);
                }
            }
         }
         return catalogs;
      }
      else if (parentNode.getPrimaryNodeType().getName() == 'ametys:programContent')
      {
          catalog = parentNode.getProperty("ametys:catalog").getString() + '';
      }
      else
      {
          parentNode = parentNode.getParent();
      }
  }
    
  return catalog != null ? [catalog] : [];
}
   
var processCourse = function (course)
{
    if (!course.hasProperty("ametys:catalog"))
    {
        var query = qm.createQuery("//element(*, ametys:courseList)[@ametys:coursesReferences = 'courseContent://" + course.getUUID() + "']",   javax.jcr.query.Query.XPATH);
        var clNodes = query.execute().getNodes();
   
        var catalog = null;
        while (clNodes.hasNext())
        {
            var clCatalogs = getCatalogs(clNodes.next().getParent());
            if (catalog == null && clCatalogs.length == 1)
            {
                catalog = clCatalogs[0];
            }
            else if (clCatalogs.length > 1 || clCatalogs[0] != catalog)
            {
				var title = course.getProperty("ametys:title").getString();
                var code = course.getProperty("ametys:elpCode").getString();
				print("ERROR >> L'ELP " + title + " (" + code  + ") est référencée par plusieurs listes appartenant à des catalogues différents => migration impossible");
                catalog = null;
                break;
            }
        }
   
        if (catalog != null)
        {
          course.setProperty("ametys:catalog", catalog);
          course.save();
          return true;
        }
          
    }
    return false;
}
    
// Default workspace
var query = qm.createQuery("//element(*, ametys:courseContent)[not(@ametys:catalog)]", javax.jcr.query.Query.XPATH);
var nodes = query.execute().getNodes();
        
var count = 0;
while (nodes.hasNext())
{
    if (processCourse(nodes.next()))
    {
        count++;
    }
}
print(count + " ELPs ont été mises à jour dans le workspace 'default'");

Après avoir exécuter le script, allez dans le back office et assurez-vous que toutes les ELPs sont dans l'état "Validé".
Valider toutes les ELPs qui ne sont pas dans l'état validé !

Puis exécuter le script suivant afin de répercuter correctement les modifications pour le label "Live".

var query = session.getWorkspace().getQueryManager().createQuery("//element(*, ametys:courseContent)", javax.jcr.query.Query.XPATH);
var nodes = query.execute().getNodes();
     
var count = 0;
while (nodes.hasNext())
{
    var node = nodes.next();
    node.checkin();
    node.checkout();
       
    var versionName = node.getBaseVersion().getName();
    node.getVersionHistory().addVersionLabel(versionName, "Live", true);
    count++;
}
print(count + " courses have been checkpoint");

Ajout du critère et de la colonne résultat "Catalogue" sur l'outil de recherche des ELPs

Si vous avez surchargé le modèle de recherche BO sur les éléments pédagogiques, vous devez ajouter le critère de recherche "Catalogue" ainsi que la colonne résultat.

Si vous utilisez le moteur de recherche standard, passez à la section suivante.

Définition du critère et colonne de recherche

<search-criteria>
	[...]
	<criteria name="catalog" type="string" column="1">
   		<label i18n="true">PLUGINS_ODF_UITOOL_PROGRAM_CATALOG</label>
   		<test-operator>eq</test-operator>
   		<enumeration>
       		<custom-enumerator class="org.ametys.odf.catalog.CatalogEnumerator">
           		<all-option>blank</all-option>
       		</custom-enumerator>
   		</enumeration>
   		<widget>sorted-enumeration</widget>
	</criteria>
	[...]
<search-criteria>
<property-mapping>
    <property id="id" path="@id"/>
    <property id="language" path="@language"/>
    <property id="language-image" path="@language-image"/>
    <property id="title" path="metadata/title"/>
    <property id="catalog" path="metadata/catalog"/>
	[...]
</property-mapping>
<columns>
	[...]
	<column id="catalog">
       <label i18n="true">PLUGINS_ODF_UITOOL_PROGRAM_CATALOG</label>
       <width>150</width>
    </column>
	[...]
</columns>

Choix du catalogue pour le service de recherche des éléments pédagogiques

Si vous utilisez le service "Rechercher un élément pédagogiques", vous devez le paramétrer pour définir le catalogue de recherche

Synchronisation du catalogue entre un dispositif de saisie et un portail ODF

Le nom du catalogue lié à un élément pédagogique est disponible dans l'export CDM-fr Ametys

<course id="FRUAI3182988BCOH6YRY93H" language="fr">
    <courseID>FRUAI3182988BCOH6YRY93H</courseID>
    <courseName>
      <text>Histoire ancienne 3</text>
    </courseName>
    [...]
    <infoBlock userDefined="ametys-extension">
      <extension>
        <ametys-cdm:catalog value="2013-2017">Catalogue 2013-2017</ametys-cdm:catalog>
      </extension>
    </infoBlock>
</course>

Lors d'un import de formation depuis un flux XML CDM-fr, les règles suivantes sont appliquées :

  • si le catalogue n'est pas présent ou vide dans le CDM-fr, l'ELP est directement affecté au catalogue de la formation importée ou synchronisée
  • si le catalogue est non vide,
    • et qu'il est identique à celui de la formation importée, l'ELP est importée ou synchronisé
    • et qu'il est différent de celui de la formation importée, une erreur est levée et l'ELP n'est ni importée ni synchronisé

A noter que si vous êtes dans un mode de synchronisation "remote" (Portail), et que vous avez forcé le catalogue d'import dans les paramètres de configuration, la valeur contenu dans le CDM-fr sera ignorée. Les formations et les ELPs seront automatiquement affecté au catalogue défini dans le paramètre de configuration.