Index: main/plugin-odf-sync/plugin.xml
===================================================================
--- main/plugin-odf-sync/plugin.xml	(revision 41919)
+++ main/plugin-odf-sync/plugin.xml	(working copy)
@@ -365,11 +365,65 @@
         	<extension point="org.ametys.plugins.odfsync.ODFSyncManager"
 			           id="org.ametys.plugins.odfsync.cdmfr.RemoteCDMFrODFSyncManager"
 			           class="org.ametys.plugins.odfsync.cdmfr.RemoteCDMFrODFSyncManager">
-           </extension>  
-		</extensions>
+           </extension>
+        </extensions>
 	</feature>
 	
 	<!-- +
+         | Co Accredited Remote CDM-fr implementation
+         + -->
+    <feature name="synchronization.cdm-fr.co.accredited.remote">
+        <config>
+            <param type="string" id="org.ametys.plugins.odfsync.ip">
+                <label i18n="true">PLUGINS_ODF_CONFIG_REMOTE_IP_LABEL</label>
+                <description i18n="true">PLUGINS_ODF_CONFIG_REMOTE_IP_DESCRIPTION</description>
+                <category i18n="true">plugin.odf:PLUGINS_ODF_CONFIG_CATEGORY</category>
+                <group i18n="true">PLUGINS_ODF_IMPORT_CONFIG_GROUP</group>
+                <widget>textarea</widget>
+            </param>
+            <param-ref id="odf.import.force.catalog"/>
+            <param-ref id="odf.import.default.catalog"/>
+            <param-ref id="odf.import.auto-validation"/>
+        </config>
+        
+        <components>
+            <component class="org.ametys.plugins.odfsync.cdmfr.CoAccreditedRemoteCDMFRImportManager" 
+                       role="org.ametys.plugins.odfsync.cdmfr.CoAccreditedRemoteCDMFRImportManager">
+                <program-workflow-name>program</program-workflow-name>
+                <course-workflow-name>course</course-workflow-name>
+                <orgunit-workflow-name>orgunit</orgunit-workflow-name>  
+                <person-workflow-name>person</person-workflow-name>
+                
+                <mention>
+                    <metadata-to-copy>
+                        <metadata-ref path="presentation" />
+                    </metadata-to-copy>
+                </mention>
+                
+                <shared-with>
+                    <metadata-to-merge>
+                        <metadata-ref path="orgUnit"/>
+                        <metadata-ref path="keywords"/>
+                        <metadata-ref path="partnerSchools"/>
+                        <metadata-ref path="personInCharge"/>
+                    </metadata-to-merge>
+                </shared-with>      
+            </component>
+            
+            <component class="org.ametys.plugins.odfsync.cdmfr.MergeMetadataForSharedProgramHelper" 
+                       role="org.ametys.plugins.odfsync.cdmfr.MergeMetadataForSharedProgramHelper">
+            </component>
+        </components>
+        
+        <extensions>
+           <extension point="org.ametys.plugins.odfsync.ODFSyncManager"
+                       id="org.ametys.plugins.odfsync.cdmfr.CoAccreditedRemoteCDMFrODFSyncManager"
+                       class="org.ametys.plugins.odfsync.cdmfr.CoAccreditedRemoteCDMFrODFSyncManager">
+           </extension>   
+        </extensions>
+    </feature>
+	
+	<!-- +
 		 | Ametys implementation
 		 + -->
 	<feature name="synchronization.ametys">
Index: main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/AbstractCDMFrManager.java
===================================================================
--- main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/AbstractCDMFrManager.java	(revision 41919)
+++ main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/AbstractCDMFrManager.java	(working copy)
@@ -297,7 +297,7 @@
             }
             else
             {
-                hasChanges = synchronizeMetadata (doc, content, metadataNode, report, logger, importMode, programLanguage, remoteChildCodes) || hasChanges;
+                hasChanges = synchronizeMetadata (contentNode, doc, content, metadataNode, report, logger, importMode, programLanguage, remoteChildCodes) || hasChanges;
             }
         }
         
@@ -445,6 +445,7 @@
     
     /**
      * Synchronize content metadata
+     * @param contentNode the content node
      * @param doc The root document
      * @param content The content to synchronize
      * @param metadataNode the DOM for metadata
@@ -452,10 +453,11 @@
      * @param logger The logger
      * @param importMode <code>true</code> if it is import mode
      * @param programLanguage Parent program language (to select the good courses in the CDM-FR file)
+     * @param remoteChildCodes 
      * @return <code>true</code> if changes were made
      * @throws WorkflowException
      */
-    protected boolean synchronizeMetadata (Document doc, SynchronizableContent content, Node metadataNode, SynchronizationReport report, Logger logger, boolean importMode, String programLanguage, Set<String> remoteChildCodes) throws WorkflowException
+    protected boolean synchronizeMetadata (Node contentNode, Document doc, SynchronizableContent content, Node metadataNode, SynchronizationReport report, Logger logger, boolean importMode, String programLanguage, Set<String> remoteChildCodes) throws WorkflowException
     {
         boolean hasChanges = false;
         
@@ -466,17 +468,18 @@
         
         if (isMultiple)
         {
-            hasChanges = synchronizeMultipleMetadata (doc, content, type, metadataName, metadataNode, report, logger, importMode, programLanguage) || hasChanges;
+            hasChanges = synchronizeMultipleMetadata (contentNode, doc, content, type, metadataName, metadataNode, report, logger, importMode, programLanguage) || hasChanges;
         }
         else
         {
-            hasChanges = synchronizeSingleMetadata(doc, content, type, metadataName, metadataNode, importMode, programLanguage, remoteChildCodes, report, logger) || hasChanges;
+            hasChanges = synchronizeSingleMetadata(contentNode, doc, content, type, metadataName, metadataNode, importMode, programLanguage, remoteChildCodes, report, logger) || hasChanges;
         }
         return hasChanges;
     }
 
     /**
      * Import a multiple field 
+     * @param contentNode the content node
      * @param doc The root document
      * @param content The content to synchronize
      * @param type The metadata type
@@ -489,14 +492,14 @@
      * @return <code>true</code> if changes were made
      * @throws WorkflowException
      */
-    protected boolean synchronizeMultipleMetadata (Document doc, SynchronizableContent content, String type, String metadataName, Node metadataNode, SynchronizationReport report, Logger logger, boolean importMode, String programLanguage) throws WorkflowException
+    protected boolean synchronizeMultipleMetadata (Node contentNode, Document doc, SynchronizableContent content, String type, String metadataName, Node metadataNode, SynchronizationReport report, Logger logger, boolean importMode, String programLanguage) throws WorkflowException
     {
         boolean hasChanges = false;
         
         if (type.equals(CDMFRConstant.IMPORT_TYPE_STRING))
         {
             // Keywords, siseCode, ...
-            hasChanges = CDMSynchronizationHelper.synchronizeStringArrayMetadata(metadataNode, content, metadataName, _enumerationHelper, importMode);
+            hasChanges = CDMSynchronizationHelper.synchronizeStringArrayMetadata(contentNode, metadataNode, content, metadataName, _enumerationHelper, _xPathProcessor, importMode);
         }
         else if (type.equals("person"))
         {
@@ -655,6 +658,7 @@
     
     /**
      * Import a single field (metadata or sub-node)
+     * @param contentNode the content node
      * @param doc The root document
      * @param content The synchronized content
      * @param type The metadata type
@@ -668,11 +672,11 @@
      * @return <code>true</code> if changes were made
      * @throws WorkflowException
      */
-    protected boolean synchronizeSingleMetadata(Document doc, SynchronizableContent content, String type, String name, Node metadataNode, boolean importMode, String programLanguage, Set<String> remoteChildCodes, SynchronizationReport report, Logger logger) throws WorkflowException
+    protected boolean synchronizeSingleMetadata(Node contentNode, Document doc, SynchronizableContent content, String type, String name, Node metadataNode, boolean importMode, String programLanguage, Set<String> remoteChildCodes, SynchronizationReport report, Logger logger) throws WorkflowException
     {
         if (type.equals(CDMFRConstant.IMPORT_TYPE_STRING))
         {
-            return CDMSynchronizationHelper.synchronizeStringMetadata (metadataNode, content, name, _enumerationHelper, importMode);
+            return CDMSynchronizationHelper.synchronizeStringMetadata (contentNode, metadataNode, content, name, _enumerationHelper, _xPathProcessor, importMode);
         }
         else if (type.equals(CDMFRConstant.IMPORT_TYPE_DATE))
         {
@@ -692,7 +696,7 @@
         }
         else if (type.equals(CDMFRConstant.IMPORT_TYPE_FILE))
         {
-            return CDMSynchronizationHelper.synchronizeFileMetadata (metadataNode, content, name, _context, importMode);
+            return CDMSynchronizationHelper.synchronizeFileMetadata (contentNode, metadataNode, content, name, _context, importMode);
         }
         else if (type.equals(CDMFRConstant.IMPORT_TYPE_GEOCODE))
         {
@@ -700,7 +704,7 @@
         }
         else if (type.equals(CDMFRConstant.IMPORT_TYPE_REPEATER))
         {
-            return synchronizeRepeaterMetadata (doc, metadataNode, content, name, report, logger, importMode);
+            return synchronizeRepeaterMetadata (contentNode, doc, metadataNode, content, name, report, logger, importMode);
         }
         else if (type.equals(CDMFRConstant.IMPORT_TYPE_COURSE_LIST))
         {
@@ -800,6 +804,7 @@
     
     /**
      * Synchronize a repeater metadata
+     * @param contentNode the content node
      * @param doc The root document
      * @param metadataNode The metadata DOM node
      * @param content The content to synchronize
@@ -810,7 +815,7 @@
      * @return <code>true</code> if changes were made
      * @throws WorkflowException 
      */
-    public boolean synchronizeRepeaterMetadata (Document doc, Node metadataNode, SynchronizableContent content, String metadataName, SynchronizationReport report, Logger logger, boolean importMode) throws WorkflowException
+    public boolean synchronizeRepeaterMetadata (Node contentNode, Document doc, Node metadataNode, SynchronizableContent content, String metadataName, SynchronizationReport report, Logger logger, boolean importMode) throws WorkflowException
     {
         if (metadataName.equals("lomSheets"))
         {
@@ -818,7 +823,7 @@
         }
         else
         {
-            return CDMSynchronizationHelper.synchronizeRepeaterMetadata(metadataNode, content, metadataName, _enumerationHelper, _xPathProcessor, _context);
+            return CDMSynchronizationHelper.synchronizeRepeaterMetadata(contentNode, metadataNode, content, metadataName, _enumerationHelper, _xPathProcessor, _context);
         }
     }
 
@@ -1165,20 +1170,30 @@
      */
     protected SynchronizableContent getOrCreateSubProgram (Node spNode, TraversableAmetysObject parentContent, String lang, SynchronizationReport report, Logger logger)
     {
-        String contentTitle = _xPathProcessor.evaluateAsString(spNode, "title");
-        String contentName = _xPathProcessor.evaluateAsString(spNode, "cdmCode");
-        
-        AmetysObjectIterable<AmetysObject> children = parentContent.getChildren();
-        for (AmetysObject ao : children)
+        SynchronizableContent subProgram = _getSubProgram(spNode, parentContent, report, logger);
+        if (subProgram != null)
         {
-            if (ao instanceof SubProgram)
-            {
-                if (_xPathProcessor.evaluateAsString(spNode, "code").equals(((SubProgram) ao).getCode()))
-                {
-                    return (SynchronizableContent) ao;
-                }
-            }
+            return subProgram;
         }
+        else
+        {
+            return _createSubProgram(spNode, parentContent, lang, report, logger);
+        }
+    }
+    
+    /**
+     * Create a sub-program 
+     * @param spNode the DOM for sub-program
+     * @param parentContent the parent content
+     * @param lang The content language
+     * @param report the report for logs
+     * @param logger The logger
+     * @return the sub-program (return null if no exist)
+     */
+    protected SynchronizableContent _createSubProgram (Node spNode, TraversableAmetysObject parentContent, String lang, SynchronizationReport report, Logger logger)
+    {
+        String contentTitle = _xPathProcessor.evaluateAsString(spNode, "title");
+        String contentName = _xPathProcessor.evaluateAsString(spNode, "cdmCode");
         
         try
         {
@@ -1199,6 +1214,32 @@
     }
     
     /**
+    * Get a sub-program 
+    * @param spNode the DOM for sub-program
+    * @param parentContent the parent content
+    * @param report the report for logs
+    * @param logger The logger
+    * @return the sub-program (return null if no exist)
+    */
+    
+    protected SynchronizableContent _getSubProgram (Node spNode, TraversableAmetysObject parentContent, SynchronizationReport report, Logger logger)
+    {
+        AmetysObjectIterable<AmetysObject> children = parentContent.getChildren();
+        for (AmetysObject ao : children)
+        {
+            if (ao instanceof SubProgram)
+            {
+                if (_xPathProcessor.evaluateAsString(spNode, "code").equals(((SubProgram) ao).getCode()))
+                {
+                    return (SynchronizableContent) ao;
+                }
+            }
+        }
+        
+        return null;
+    }
+    
+    /**
      * Get a container or create it if not exists
      * @param cNode the DOM for container
      * @param parentContent the parent content
Index: main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CDMFrImportManager.java
===================================================================
--- main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CDMFrImportManager.java	(revision 41919)
+++ main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CDMFrImportManager.java	(working copy)
@@ -108,8 +108,6 @@
      */
     protected List<Program> importPrograms(InputStream stream, SynchronizationReport report, Logger logger) throws WorkflowException, AmetysRepositoryException
     {
-        String defaultCatalog = getDefaultCatalog();
-        
         List<Program> importedPrograms = new ArrayList<Program>();
         try
         {
@@ -145,44 +143,7 @@
                     {
                         try
                         {
-                            // Create program
-                            String contentTitle = _xPathProcessor.evaluateAsString(contentNode, "title");
-                            
-                            String infoMsg = "Création de la formation " + contentName;
-                            report.info(infoMsg);
-                            logger.info(infoMsg);
-                            
-                            String contentId = createContent(ProgramFactory.PROGRAM_CONTENT_TYPE, contentLang, contentName, contentTitle, _programWorkflowName, 1, null);
-                            content = _resolver.resolveById(contentId);
-                            synchronizeContent(doc, content, contentNode, report, logger, true, contentLang);
-                            
-                            String catalog = ((Program) content).getCatalog();
-                            if (StringUtils.isEmpty(catalog) && defaultCatalog != null)
-                            {
-                                // Set the default catalog
-                                ((Program) content).setCatalog(defaultCatalog);
-                            }
-                            else if (StringUtils.isNotEmpty(catalog))
-                            {
-                                if (_catalogsManager.getCatalog(catalog) == null)
-                                {
-                                    // Catalog does not exits ...
-                                    if (defaultCatalog != null)
-                                    {
-                                        // ... and there is a default catalog, set the default catalog
-                                        ((Program) content).setCatalog(defaultCatalog);
-                                    }
-                                    else
-                                    {
-                                        // ... and there is no default catalog, remove it
-                                        content.getMetadataHolder().removeMetadata(Program.CATALOG);
-                                    }
-                                }
-                            }
-                                
-                            content.saveChanges();
-                            content.checkpoint();
-                            
+                            content = createProgram(contentNode, doc, contentName, contentLang, report, logger);
                             importedPrograms.add((Program) content);
                         }
                         catch (WorkflowException e) 
@@ -227,6 +188,71 @@
         return importedPrograms;
     }
     
+    /**
+     * Create the program content
+     * @param contentNode the content node
+     * @param doc the document
+     * @param contentName the content name
+     * @param contentLang the content lang
+     * @param report the logger report
+     * @param logger the logger
+     * @return the created program
+     * @throws WorkflowException if an error occurred
+     */
+    protected SynchronizableContent createProgram(Node contentNode, Document doc, String contentName, String contentLang, SynchronizationReport report, Logger logger) throws WorkflowException
+    {
+        // Create program
+        String contentTitle = _xPathProcessor.evaluateAsString(contentNode, "title");
+        
+        String infoMsg = "Création de la formation " + contentName;
+        report.info(infoMsg);
+        logger.info(infoMsg);
+        
+        String contentId = createContent(ProgramFactory.PROGRAM_CONTENT_TYPE, contentLang, contentName, contentTitle, _programWorkflowName, 1, null);
+        SynchronizableContent content = _resolver.resolveById(contentId);
+        synchronizeContent(doc, content, contentNode, report, logger, true, contentLang);
+        
+        setProgramCatalog(content);
+            
+        content.saveChanges();
+        content.checkpoint();
+        
+        return content;
+    }
+    
+    /**
+     * Set the catalog to the program
+     * @param content the program
+     */
+    protected void setProgramCatalog(SynchronizableContent content)
+    {
+        String defaultCatalog = getDefaultCatalog();
+        
+        String catalog = ((Program) content).getCatalog();
+        if (StringUtils.isEmpty(catalog) && defaultCatalog != null)
+        {
+            // Set the default catalog
+            ((Program) content).setCatalog(defaultCatalog);
+        }
+        else if (StringUtils.isNotEmpty(catalog))
+        {
+            if (_catalogsManager.getCatalog(catalog) == null)
+            {
+                // Catalog does not exits ...
+                if (defaultCatalog != null)
+                {
+                    // ... and there is a default catalog, set the default catalog
+                    ((Program) content).setCatalog(defaultCatalog);
+                }
+                else
+                {
+                    // ... and there is no default catalog, remove it
+                    content.getMetadataHolder().removeMetadata(Program.CATALOG);
+                }
+            }
+        }
+    }
+    
     @Override
     public List< ? extends SearchRemoteItem> searchRemotePrograms(Map<String, String> params)
     {
Index: main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CDMSynchronizationHelper.java
===================================================================
--- main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CDMSynchronizationHelper.java	(revision 41919)
+++ main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CDMSynchronizationHelper.java	(working copy)
@@ -49,33 +49,32 @@
 import org.w3c.dom.NodeList;
 
 import org.ametys.odf.SynchronizableContent;
+import org.ametys.odf.enumeration.OdfEnumerationConstant;
 import org.ametys.odf.enumeration.OdfEnumerationHelper;
+import org.ametys.odf.program.Program;
 import org.ametys.plugins.repository.metadata.ModifiableBinaryMetadata;
 import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
 import org.ametys.plugins.repository.metadata.ModifiableFile;
 import org.ametys.plugins.repository.metadata.ModifiableResource;
 import org.ametys.plugins.repository.metadata.ModifiableRichText;
 import org.ametys.plugins.repository.metadata.UnknownMetadataException;
+import org.ametys.runtime.config.Config;
 import org.ametys.runtime.util.LoggerFactory;
 
 /**
  * Helper for CDM-fr synchronization or import
  *
  */
-public final class CDMSynchronizationHelper
+public class CDMSynchronizationHelper
 {
     private static final Pattern __URL_REGEXP = Pattern.compile("^(https?:\\/\\/.+)$");
     private static SimpleDateFormat _dateFormat = new SimpleDateFormat("yyyy-MM-dd");
     
     private static Logger _logger = LoggerFactory.getLoggerFor(CDMSynchronizationHelper.class);
     
-    private CDMSynchronizationHelper ()
-    {
-        // Nothing
-    }
-    
     /**
      * Synchronize a repeater metadata
+     * @param contentNode the content node
      * @param metadataNode The metadata DOM node
      * @param content The synchronized content
      * @param metadataName The metadata name
@@ -84,7 +83,7 @@
      * @param cocoonContext The cocoon context
      * @return <code>true</code> if changes were made
      */
-    public static boolean synchronizeRepeaterMetadata (Node metadataNode, SynchronizableContent content, String metadataName, OdfEnumerationHelper enumerationHelper, XPathProcessor xpathProcessor, Context cocoonContext)
+    public static boolean synchronizeRepeaterMetadata (Node contentNode, Node metadataNode, SynchronizableContent content, String metadataName, OdfEnumerationHelper enumerationHelper, XPathProcessor xpathProcessor, Context cocoonContext)
     {
         boolean hasChanges = false;
         
@@ -118,7 +117,7 @@
                 boolean isMultiple = multipleItem != null && "true".equals(multipleItem.getTextContent().trim());
                 
                 // Metadata of repeater can not be synchronizable for now
-                hasChanges = synchronizeNoSynchronizableMetadata(content, entry, type, subMetadataName, childNode, enumerationHelper, xpathProcessor, cocoonContext, isMultiple)  || hasChanges;
+                hasChanges = synchronizeNoSynchronizableMetadata(contentNode, content, entry, type, subMetadataName, childNode, enumerationHelper, xpathProcessor, cocoonContext, isMultiple)  || hasChanges;
             }
         }
         
@@ -128,6 +127,7 @@
     
     /**
      * Synchronize a no-synchronizable metadata
+     * @param contentNode the content node
      * @param content The synchronized content
      * @param compositeMetadata The composite metadata
      * @param type The type
@@ -139,17 +139,17 @@
      * @param multiple <code>true</code> if metadata is multiple
      * @return <code>true</code> if changes were made
      */
-    public static boolean synchronizeNoSynchronizableMetadata (SynchronizableContent content, ModifiableCompositeMetadata compositeMetadata, String type, String metadataName, Node metadataNode, OdfEnumerationHelper enumerationHelper, XPathProcessor xpathProcessor, Context cocoonContext, boolean multiple)
+    public static boolean synchronizeNoSynchronizableMetadata (Node contentNode, SynchronizableContent content, ModifiableCompositeMetadata compositeMetadata, String type, String metadataName, Node metadataNode, OdfEnumerationHelper enumerationHelper, XPathProcessor xpathProcessor, Context cocoonContext, boolean multiple)
     {
         if (type.equals(CDMFRConstant.IMPORT_TYPE_STRING))
         {
             if (multiple)
             {
-                return synchronizeNoSynchronizableStringArrayMetadata(metadataNode, compositeMetadata, metadataName, enumerationHelper);
+                return synchronizeNoSynchronizableStringArrayMetadata(contentNode, metadataNode, compositeMetadata, metadataName, enumerationHelper, xpathProcessor);
             }
             else
             {
-                return synchronizeNoSynchronizableStringMetadata (metadataNode, compositeMetadata, metadataName, enumerationHelper);
+                return synchronizeNoSynchronizableStringMetadata (contentNode, metadataNode, compositeMetadata, metadataName, enumerationHelper, xpathProcessor);
             }
         }
         else if (type.equals(CDMFRConstant.IMPORT_TYPE_DATE))
@@ -166,7 +166,7 @@
         }
         else if (type.equals(CDMFRConstant.IMPORT_TYPE_FILE))
         {
-            return synchronizeNoSynchronizableFileMetadata(metadataNode, compositeMetadata, metadataName, cocoonContext);
+            return synchronizeNoSynchronizableFileMetadata(contentNode, metadataNode, compositeMetadata, metadataName, cocoonContext);
         }
         else if (type.equals(CDMFRConstant.IMPORT_TYPE_RICH_TEXT))
         {
@@ -186,18 +186,20 @@
     
     /**
      * Synchronize a String metadata
+     * @param contentNode the content node
      * @param metadataNode The metadata DOM node
      * @param content The content 
      * @param metadataName The metadata name
      * @param enumerationHelper the {@link OdfEnumerationHelper}.
+     * @param xpathProcessor the xpath processor
      * @param importMode <code>true</code> if it is import mode
      * @return <code>true</code> if changes were made
      */
-    public static boolean synchronizeStringMetadata(Node metadataNode, SynchronizableContent content, String metadataName, OdfEnumerationHelper enumerationHelper, boolean importMode)
+    public static boolean synchronizeStringMetadata(Node contentNode, Node metadataNode, SynchronizableContent content, String metadataName, OdfEnumerationHelper enumerationHelper, XPathProcessor xpathProcessor, boolean importMode)
     {
         boolean isSynchonizable = isSynchronizable(metadataNode);
         
-        String constant = getConstant(metadataNode);
+        String constant = getConstant(contentNode, metadataNode, enumerationHelper, xpathProcessor);
         String value = metadataNode.getTextContent().trim();
         if (constant != null)
         {
@@ -231,15 +233,17 @@
     
     /**
      * Synchronize a no-synchronizable String metadata. This method is used for metadata in a repeater for example
+     * @param contentNode the content node
      * @param metadataNode The metadata DOM node
      * @param compositeMetadata The composite metadata
      * @param metadataName The metadata name
      * @param enumerationHelper the {@link OdfEnumerationHelper}.
+     * @param xpathProcessor the xpath processor
      * @return <code>true</code> if changes were made
      */
-    public static boolean synchronizeNoSynchronizableStringMetadata(Node metadataNode, ModifiableCompositeMetadata compositeMetadata, String metadataName, OdfEnumerationHelper enumerationHelper)
+    public static boolean synchronizeNoSynchronizableStringMetadata(Node contentNode, Node metadataNode, ModifiableCompositeMetadata compositeMetadata, String metadataName, OdfEnumerationHelper enumerationHelper, XPathProcessor xpathProcessor)
     {
-        String constant = getConstant(metadataNode);
+        String constant = getConstant(contentNode, metadataNode, enumerationHelper, xpathProcessor);
         String value = metadataNode.getTextContent().trim();
         if (constant != null)
         {
@@ -258,18 +262,20 @@
     
     /**
      * Synchronize a String metadata
+     * @param contentNode the content node
      * @param metadataNode The metadata DOM node
      * @param content The content 
      * @param metadataName The metadata name
      * @param enumerationHelper The enumeration helper
+     * @param xpathProcessor the xpath processor
      * @param importMode <code>true</code> if it is import mode
      * @return <code>true</code> if changes were made
      */
-    public static boolean synchronizeStringArrayMetadata(Node metadataNode, SynchronizableContent content, String metadataName, OdfEnumerationHelper enumerationHelper, boolean importMode)
+    public static boolean synchronizeStringArrayMetadata(Node contentNode, Node metadataNode, SynchronizableContent content, String metadataName, OdfEnumerationHelper enumerationHelper, XPathProcessor xpathProcessor, boolean importMode)
     {
         boolean isSynchonizable = isSynchronizable(metadataNode);
         
-        String constant = getConstant(metadataNode);
+        String constant = getConstant(contentNode, metadataNode, enumerationHelper, xpathProcessor);
         
         NodeList itemNodes = metadataNode.getChildNodes();
         List<String> values = new ArrayList<String>();
@@ -313,15 +319,17 @@
     
     /**
      * Synchronize a String metadata
+     * @param contentNode the content node
      * @param metadataNode The metadata DOM node
      * @param compositeMetadata The composite metadata 
      * @param metadataName The metadata name
      * @param enumerationHelper The enumeration helper
+     * @param xpathProcessor the xpath processor
      * @return <code>true</code> if changes were made
      */
-    public static boolean synchronizeNoSynchronizableStringArrayMetadata(Node metadataNode, ModifiableCompositeMetadata compositeMetadata, String metadataName, OdfEnumerationHelper enumerationHelper)
+    public static boolean synchronizeNoSynchronizableStringArrayMetadata(Node contentNode, Node metadataNode, ModifiableCompositeMetadata compositeMetadata, String metadataName, OdfEnumerationHelper enumerationHelper, XPathProcessor xpathProcessor)
     {
-        String constant = getConstant(metadataNode);
+        String constant = getConstant(contentNode, metadataNode, enumerationHelper, xpathProcessor);
         
         NodeList itemNodes = metadataNode.getChildNodes();
         List<String> values = new ArrayList<String>();
@@ -349,6 +357,7 @@
     
     /**
      * Synchronize a File metadata
+     * @param contentNode the content node
      * @param metadataNode The metadata DOM node
      * @param content The content 
      * @param metadataName The metadata name
@@ -356,7 +365,7 @@
      * @param importMode <code>true</code> if it is import mode
      * @return <code>true</code> if changes were made
      */
-    public static boolean synchronizeFileMetadata(Node metadataNode, SynchronizableContent content, String metadataName, Context cocoonContext, boolean importMode)
+    public static boolean synchronizeFileMetadata(Node contentNode, Node metadataNode, SynchronizableContent content, String metadataName, Context cocoonContext, boolean importMode)
     {
         String value = metadataNode.getTextContent().trim();
         boolean isSynchronizable = isSynchronizable(metadataNode);
@@ -411,7 +420,7 @@
                 }
             }
             
-            return synchronizeStringArrayMetadata(metadataNode, content, metadataName, null, importMode);
+            return synchronizeStringArrayMetadata(contentNode, metadataNode, content, metadataName, null, null, importMode);
         }
     }
     
@@ -495,13 +504,14 @@
     
     /**
      * Synchronize a no-synchronizable Binary metadata. This method is used for metadata in a repeater for example
+     * @param contentNode the content node
      * @param metadataNode The metadata DOM node
      * @param compositeMetadata The composite metadata
      * @param metadataName The metadata name
      * @param cocoonContext The cocoon context
      * @return <code>true</code> if changes were made
      */
-    public static boolean synchronizeNoSynchronizableFileMetadata(Node metadataNode, ModifiableCompositeMetadata compositeMetadata, String metadataName, Context cocoonContext)
+    public static boolean synchronizeNoSynchronizableFileMetadata(Node contentNode, Node metadataNode, ModifiableCompositeMetadata compositeMetadata, String metadataName, Context cocoonContext)
     {
         String value = metadataNode.getTextContent().trim();
         
@@ -533,7 +543,7 @@
                 // Do nothing
             }
             
-            return synchronizeNoSynchronizableStringMetadata(metadataNode, compositeMetadata, metadataName, null);
+            return synchronizeNoSynchronizableStringMetadata(contentNode, metadataNode, compositeMetadata, metadataName, null, null);
         }
     }
     
@@ -1067,15 +1077,43 @@
     
     /**
      * Determines if field is a constant
-     * @param node the DOM
+     * @param contentNode the content node
+     * @param metadataNode the metadata node
+     * @param enumerationHelper The enumeration helper
+     * @param xpathProcessor The xpath processor
      * @return true if the field is synchronizable
      */
-    public static String getConstant (Node node)
+    public static String getConstant (Node contentNode, Node metadataNode, OdfEnumerationHelper enumerationHelper, XPathProcessor xpathProcessor)
     {
-        Node constant = node.getAttributes().getNamedItem("constant");
+        Node constant = metadataNode.getAttributes().getNamedItem("constant");
         if (constant != null)
         {
-            return constant.getTextContent().trim();
+            String constantAsString = constant.getTextContent().trim();
+            if (constantAsString.equals("MENTION"))
+            {
+                String degree = xpathProcessor.evaluateAsString(contentNode, Program.DEGREE); 
+                String degreeCode = enumerationHelper.getItemCode(OdfEnumerationConstant.DEGREE, degree);
+                
+                String mentionKey = null;
+                if (degreeCode.equals(Config.getInstance().getValueAsString("odf.programs.degree.license")))
+                {
+                    mentionKey = OdfEnumerationConstant.MENTION_LICENCE;
+                }
+                else if (degreeCode.equals(Config.getInstance().getValueAsString("odf.programs.degree.master")))
+                {
+                    mentionKey = OdfEnumerationConstant.MENTION_MASTER;
+                }
+                else if (degreeCode.equals(Config.getInstance().getValueAsString("odf.programs.degree.licensepro")))
+                {
+                    mentionKey = OdfEnumerationConstant.MENTION_LICENCEPRO;
+                }
+                
+                return mentionKey;
+            }
+            else
+            {
+                return constantAsString;
+            }
         }
         return null;
     }
Index: main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CoAccreditedRemoteCDMFRImportManager.java
===================================================================
--- main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CoAccreditedRemoteCDMFRImportManager.java	(revision 0)
+++ main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CoAccreditedRemoteCDMFRImportManager.java	(revision 0)
@@ -0,0 +1,771 @@
+/*
+ *  Copyright 2016 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.odfsync.cdmfr;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import org.ametys.cms.contenttype.ContentType;
+import org.ametys.cms.observation.Event;
+import org.ametys.cms.observation.ObservationConstants;
+import org.ametys.cms.repository.ContentTypeExpression;
+import org.ametys.cms.repository.ContentTypeHelper;
+import org.ametys.cms.repository.LanguageExpression;
+import org.ametys.odf.SynchronizableContent;
+import org.ametys.odf.SynchronizeExpression;
+import org.ametys.odf.enumeration.OdfEnumerationConstant;
+import org.ametys.odf.program.AbstractProgram;
+import org.ametys.odf.program.Program;
+import org.ametys.odf.program.ProgramFactory;
+import org.ametys.odf.program.SubProgram;
+import org.ametys.plugins.odfsync.SynchronizationReport;
+import org.ametys.plugins.repository.AmetysObject;
+import org.ametys.plugins.repository.AmetysObjectIterable;
+import org.ametys.plugins.repository.AmetysRepositoryException;
+import org.ametys.plugins.repository.metadata.UnknownMetadataException;
+import org.ametys.plugins.repository.query.QueryHelper;
+import org.ametys.plugins.repository.query.expression.AndExpression;
+import org.ametys.plugins.repository.query.expression.Expression;
+import org.ametys.plugins.repository.query.expression.Expression.Operator;
+import org.ametys.runtime.config.Config;
+import org.ametys.runtime.util.I18nUtils;
+import org.ametys.runtime.util.I18nizableText;
+
+import com.opensymphony.workflow.WorkflowException;
+
+/**
+ * Class to import or synchronize ODF contents from CDMFR stream and handle co-accredited programs
+ *
+ */
+public class CoAccreditedRemoteCDMFRImportManager extends RemoteCDMFRImportManager
+{
+    /** Avalon Role */
+    @SuppressWarnings("hiding")
+    public static final String ROLE = CoAccreditedRemoteCDMFRImportManager.class.getName();
+    
+    /** The i18n translator */
+    protected I18nUtils _i18nUtils;
+    
+    /** The list of metadata to copy for mention program */
+    protected Set<String> _mentionMetadataPaths;
+    
+    /** The list of metadata to merge */
+    protected Set<String> _metadataPathsToMerge;
+    
+    /** The helper to merge shared metadata */
+    protected MergeMetadataForSharedProgramHelper _mergeMetadataHelper;
+    
+    
+    @Override
+    public void configure(Configuration configuration) throws ConfigurationException
+    {
+        super.configure(configuration);
+
+        _mentionMetadataPaths = new HashSet<String>();
+        Configuration mentionConf = configuration.getChild("mention");
+        if (mentionConf != null)
+        {
+            Configuration metadatas = mentionConf.getChild("metadata-to-copy");
+            if (metadatas != null)
+            {
+                for (Configuration metadataConf : metadatas.getChildren())
+                {
+                    String metadataPath = metadataConf.getAttribute("path");
+                    _mentionMetadataPaths.add(metadataPath);
+                }
+            }
+        }
+        
+        _metadataPathsToMerge = new HashSet<String>();
+        Configuration sharedWithConf = configuration.getChild("shared-with");
+        if (sharedWithConf != null)
+        {
+            Configuration metadatas = sharedWithConf.getChild("metadata-to-merge");
+            if (metadatas != null)
+            {
+                for (Configuration metadataConf : metadatas.getChildren())
+                {
+                    String metadataPath = metadataConf.getAttribute("path");
+                    _metadataPathsToMerge.add(metadataPath);
+                }
+            }
+        }
+    }
+    
+    @Override
+    public void service(ServiceManager manager) throws ServiceException
+    {
+        super.service(manager);
+        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
+        _mergeMetadataHelper = (MergeMetadataForSharedProgramHelper) manager.lookup(MergeMetadataForSharedProgramHelper.ROLE);
+    }
+    
+    @Override
+    protected List<Program> importPrograms(InputStream stream, SynchronizationReport report, Logger logger) throws WorkflowException, AmetysRepositoryException
+    {
+        List<Program> importedPrograms = new ArrayList<Program>();
+        Map<Program, Boolean> updatedMentions = new HashMap<Program, Boolean>();
+        
+        try
+        {
+            // XML to Content
+            Document doc = _domParser.parseDocument(new InputSource(stream));
+            doc = transformDocument(doc, new HashMap<String, Object>(), report, logger);
+
+            String lang = _xPathProcessor.evaluateAsString(doc, "CDM/@language");
+            if (StringUtils.isEmpty(lang))
+            {
+                lang = Config.getInstance().getValueAsString("odf.programs.lang");
+            }
+            if (doc != null)
+            {
+                NodeList nodes = doc.getElementsByTagName("program");
+                
+                for (int i = 0; i < nodes.getLength(); i++)
+                {
+                    report.info("-------------------------------------------------------------------------------");
+                    Node contentNode = nodes.item(i);
+                    // String cdmId = contentNode.getAttributes().getNamedItem("CDMid").getTextContent();
+                    String contentName = _xPathProcessor.evaluateAsString(contentNode, "code");
+                    String contentLang = _xPathProcessor.evaluateAsString(contentNode, "@language");
+                    if (StringUtils.isEmpty(contentLang))
+                    {
+                        contentLang = lang;
+                    }
+                    
+                    contentLang = StringUtils.substring(contentLang, 0, 2).toLowerCase(); // on keep the language from the locale
+                    
+                    String educationKind = _xPathProcessor.evaluateAsString(contentNode, Program.EDUCATION_KIND);
+                    String mention = _xPathProcessor.evaluateAsString(contentNode, Program.MENTION);
+                    
+                    if (StringUtils.isNotBlank(mention) && "parcours".equals(educationKind))
+                    {
+                        Program coAccreditedProgram = importCoAccreditedProgram(contentNode, doc, contentLang, contentName, mention, updatedMentions, report, logger);
+                        if (coAccreditedProgram != null)
+                        {
+                            importedPrograms.add(coAccreditedProgram);
+                        }
+                    }
+                    else
+                    {
+                        SynchronizableContent content = getContent(ProgramFactory.PROGRAM_CONTENT_TYPE, contentLang, "code", contentName);
+                        if (content == null)
+                        {
+                            try
+                            {
+                                content = createProgram(contentNode, doc, contentName, contentLang, report, logger);
+                                importedPrograms.add((Program) content);
+                            }
+                            catch (WorkflowException e) 
+                            {
+                                String errorMsg = "Une erreur est survenue lors de la création du contenu " + contentName;
+                                report.error(errorMsg, e);
+                                logger.error(errorMsg, e);
+                            }
+                        }
+                        else
+                        {
+                            String infoMsg = "Synchronisation de la formation \"" + content.getTitle() + "\"";
+                            report.info(infoMsg);
+                            logger.info(infoMsg);
+                            if (synchronizeContent(doc, content, contentNode, report, logger, false, contentLang))
+                            {
+                                applyChanges(content, report, logger);
+                            }
+                            else
+                            {
+                                infoMsg = "Aucune modification détectée sur la formation \"" + content.getTitle() + "\" depuis la dernière synchronisation";
+                                report.info(infoMsg);
+                                logger.info(infoMsg);
+                            }
+                        }
+                    }
+                }
+            }
+
+            applyChangesToMentions(updatedMentions, doc, report, logger);
+        }
+        catch (IOException e)
+        {
+            throw new AmetysRepositoryException("Unable to read the input stream", e);
+        }
+        catch (SAXException e)
+        {
+            throw new AmetysRepositoryException("A problem occured while parsing the input stream.", e);
+        }
+        catch (ProcessingException e)
+        {
+            throw new AmetysRepositoryException("A problem occured while transforming the document.", e);
+        }
+        
+        return importedPrograms;
+    }
+    
+    /**
+     * Import co-accredited program
+     * @param contentNode the content node
+     * @param doc the document
+     * @param contentLang the content lang
+     * @param contentName the content name
+     * @param mentionLabel the mention label
+     * @param updatedMentions the mentions to update
+     * @param report the report logger
+     * @param logger the logger
+     * @return return the imported program
+     * @throws UnknownMetadataException if an unknown metadata error occurred
+     * @throws AmetysRepositoryException if an ametys repository error occurred
+     * @throws WorkflowException if a workflow error occurred
+     */
+    protected Program importCoAccreditedProgram(Node contentNode, Document doc, String contentLang, String contentName, String mentionLabel, Map<Program, Boolean> updatedMentions, SynchronizationReport report, Logger logger) throws UnknownMetadataException, AmetysRepositoryException, WorkflowException
+    {
+        Program importedProgram = null;
+        _mergeMetadataHelper.initialize(this);
+
+        String degree = _xPathProcessor.evaluateAsString(contentNode, Program.DEGREE);
+        String degreeCode = _enumerationHelper.getItemCode(OdfEnumerationConstant.DEGREE, degree);
+
+        String mentionKey = CDMSynchronizationHelper.getConstant(contentNode, _xPathProcessor.selectSingleNode(contentNode, Program.MENTION), _enumerationHelper, _xPathProcessor);
+        String mentionCode = _enumerationHelper.getItemCode(mentionKey, mentionLabel);
+        
+        if (mentionCode != null)
+        {
+            // Get or create the root program (mention) with the given mention's code and degree code
+            Program mentionProgram = getMentionProgram(contentLang, mentionCode, degreeCode);
+            if (mentionProgram == null)
+            {
+                try
+                {
+                    // The root program does not exist yet, create it
+                    String contentTitle = getMentionProgramTitle(mentionProgram, doc, contentNode, contentLang, report, logger);
+
+                    String infoMsg = "Création de la mention " + contentTitle;
+                    report.info(infoMsg);
+                    logger.info(infoMsg);
+
+                    String contentId = createContent(ProgramFactory.PROGRAM_CONTENT_TYPE, contentLang, contentName, contentTitle, _programWorkflowName, 1, null);
+                    mentionProgram = _resolver.resolveById(contentId);
+                    updatedMentions.put(mentionProgram, true);
+
+                    // Set the common and additional metadata from the imported co-accredited program. This is done only on root program (mention) creation
+                    setMentionCommonMetadata(mentionProgram, doc, contentNode, report, logger);
+                    setMentionAdditionalMetadata(mentionProgram, doc, contentNode, contentLang, report, logger);
+
+                    importedProgram = mentionProgram;
+                }
+                catch (WorkflowException e)
+                {
+                    String errorMsg = "Une erreur est survenue lors de la création de la mention " + contentName;
+                    report.error(errorMsg, e);
+                    logger.error(errorMsg, e);
+                }
+            }
+
+            // Is imported program already exist as a subprogram ?
+            SynchronizableContent content = _getSubProgram(contentNode, mentionProgram, report, logger);
+            
+            if (content == null)
+            {
+                // The imported program does not exist as subprogram, create it except if it is a shared subprogram
+                List<String> sharedWith = getSharedWithAsString(contentNode, report, logger);
+                if (sharedWith.isEmpty())
+                {
+                    // Search if there is a main subprogram shared with it.
+                    SubProgram mainSubProgram = getMainSubProgram(mentionProgram, contentNode, report, logger);
+                    if (mainSubProgram != null)
+                    {
+                        // The imported program is a shared subprogram, it will not be imported nor synchronized, only the main subprogram will be updated
+                        try
+                        {
+                            //Merge shared program metadata
+                            _mergeMetadataHelper.synchronizeSharedProgramNode(doc, mainSubProgram, contentNode, _metadataPathsToMerge, contentLang, report, logger);
+                            
+                            //The merge main program
+                            _mergeMetadataHelper.synchronizeMainProgram(mainSubProgram, _metadataPathsToMerge, report, logger);
+                        }
+                        catch (RepositoryException e)
+                        {
+                            String warnMsg = "Impossible de synchronizer les metadatas pour le programme principal \"" + mainSubProgram.getTitle() + "\"";
+                            report.info(warnMsg);
+                            logger.warn(warnMsg);
+                        }
+                    }
+                    else
+                    {
+                        // This a non-shared subprogram or the main shared subprogram was not imported yet, create it
+                        content = _createSubProgram(contentNode, mentionProgram, contentLang, report, logger);
+                        
+                        // Then synchronize it
+                        _synchronizeSubProgram(content, contentLang, mentionProgram, contentNode, doc, updatedMentions, true, report, logger);
+                    }
+                }
+                else
+                {
+                    // This is a main subprogram in a co-accredited program : create the subprogram and synchronize it
+                    content = _createSubProgram(contentNode, mentionProgram, contentLang, report, logger);
+                    
+                    // Then synchronize it
+                    _synchronizeSubProgram(content, contentLang, mentionProgram, contentNode, doc, updatedMentions, true, report, logger);
+                    
+                    // Browse existing shared subprograms to first merge them then delete them.
+                    for (String code : sharedWith)
+                    {
+                        SubProgram sharedSubProgram = getSharedSubProgram(mentionProgram, code, report, logger);
+                        if (sharedSubProgram != null)
+                        {
+                            String infoMsg = "Fusion avec le parcours partagé \"" + sharedSubProgram.getTitle() + "\"";
+                            report.info(infoMsg);
+                            logger.info(infoMsg);
+                            
+                            try
+                            {
+                                _mergeMetadataHelper.synchronizeSharedProgramNode((SubProgram) content, sharedSubProgram, _metadataPathsToMerge, report, logger);
+                            }
+                            catch (RepositoryException e)
+                            {
+                                String warnMsg = "Impossible de synchronizer les metadatas du programme partagé \"" + sharedSubProgram.getTitle() + "\" avec le programme principal \"" + content.getTitle() + "\"";
+                                report.warn(warnMsg);
+                                logger.warn(warnMsg);
+                            }
+                            
+                            infoMsg = "Suppression du parcours partagé secondaire \"" + sharedSubProgram.getTitle() + "\"";
+                            report.info(infoMsg);
+                            logger.info(infoMsg);
+                            
+                            // Then delete the secondary shared subprogram
+                            deleteSharedSubProgram(sharedSubProgram, report, logger);
+                        }
+                    }
+                    
+                    try
+                    {
+                        //Then merge main program
+                        _mergeMetadataHelper.synchronizeMainProgram((SubProgram) content, _metadataPathsToMerge, report, logger);
+                    }
+                    catch (RepositoryException e)
+                    {
+                        String warnMsg = "Impossible de synchronizer les metadatas du programme principal \"" + content.getTitle() + "\"";
+                        report.warn(warnMsg);
+                        logger.warn(warnMsg);
+                    }
+                }
+            }
+            else
+            {
+                // The imported program exists as subprogram
+                
+                List<String> sharedWith = getSharedWithAsString(contentNode, report, logger);
+                if (sharedWith.isEmpty())
+                {
+                    // Simply synchronize it 
+                    _synchronizeSubProgram(content, contentLang, mentionProgram, contentNode, doc, updatedMentions, false, report, logger);
+                }
+                else
+                {
+                    // This is a main subprogram
+                    // Synchronize subprogram from remote node
+                    _synchronizeSubProgram(content, contentLang, mentionProgram, contentNode, doc, updatedMentions, false, report, logger);
+                    
+                    try
+                    {
+                        //Then merge main program
+                        _mergeMetadataHelper.synchronizeMainProgram((SubProgram) content, _metadataPathsToMerge, report, logger);
+                    }
+                    catch (RepositoryException e)
+                    {
+                        String warnMsg = "Impossible de synchronizer les metadatas du programme principal \"" + content.getTitle() + "\"";
+                        report.warn(warnMsg);
+                        logger.warn(warnMsg);
+                    }
+                }
+            }
+        }
+        else
+        {
+            String error = "Il n'y a pas de code associée à la mention : " + mentionLabel + ". La formation n'a pas été importée.";
+            logger.error(error);
+            report.error(error);
+        }
+
+        return importedProgram;
+    }
+    
+    /**
+     * Synchronize a subprogram of a co-accredited mention
+     * @param content The subprogram to synchronize
+     * @param contentLang The content language
+     * @param mentionProgram The parent mention
+     * @param contentNode The node representing the subprogram
+     * @param doc The document
+     * @param updatedMentions The update mentions and their import mode
+     * @param importMode The import mode
+     * @param report The report
+     * @param logger The logger
+     * @throws UnknownMetadataException if an error occurred
+     * @throws AmetysRepositoryException if an ametys repository error occurred
+     * @throws WorkflowException if a workflow repository error occurred
+     */
+    protected void _synchronizeSubProgram (SynchronizableContent content, String contentLang, Program mentionProgram, Node contentNode, Document doc, Map<Program, Boolean> updatedMentions, boolean importMode, SynchronizationReport report, Logger logger) throws UnknownMetadataException, AmetysRepositoryException, WorkflowException
+    {
+        // Then synchronize subprogram from remote node
+        String infoMsg = "Synchronisation du parcours \"" + content.getTitle() + "\"";
+        report.info(infoMsg);
+        logger.info(infoMsg);
+        if (synchronizeContent(doc, content, contentNode, report, logger, importMode, contentLang))
+        {
+            if (!updatedMentions.containsKey(mentionProgram))
+            {
+                updatedMentions.put(mentionProgram, false);
+            }
+        }
+        else
+        {
+            infoMsg = "Aucune modification détectée sur le parcours \"" + content.getTitle() + "\" depuis la dernière synchronisation";
+            report.info(infoMsg);
+            logger.info(infoMsg);
+        }
+    }
+    
+    /**
+     * Delete a shared secondary subprogram.
+     * This subprogram was created by a precede import before the main subprogram was imported
+     * @param sharedProgram the shared program
+     * @param report the report logger
+     * @param logger the logger
+     */
+    protected void deleteSharedSubProgram(SynchronizableContent sharedProgram, SynchronizationReport report, Logger logger)
+    {
+        String programId = sharedProgram.getId();
+        String programName = sharedProgram.getName();
+        _observationManager.notify(new Event(_currentUserProvider.getUser(), ObservationConstants.CONTENT_DELETING, sharedProgram, new String[]{programName}));
+        
+        sharedProgram.remove();
+        sharedProgram.saveChanges();
+        
+        _observationManager.notify(new Event(_currentUserProvider.getUser(), ObservationConstants.CONTENT_DELETED, programId, new String[]{programName}));
+    }
+    
+    /**
+     * Get the main subprogram shared with the program representing by the content node
+     * @param mentionProgram the root mention program
+     * @param contentNode the content node
+     * @param report the report logger
+     * @param logger the logger
+     * @return the main subprogram if the program is a shared subprogram or <code>null</code> otherwise
+     */
+    protected SubProgram getMainSubProgram(Program mentionProgram, Node contentNode, SynchronizationReport report, Logger logger)
+    {
+        String programCode = _xPathProcessor.evaluateAsString(contentNode, Program.CODE);
+        
+        for (AmetysObject child : mentionProgram.getChildren())
+        {
+            if (child instanceof SubProgram)
+            {
+                SubProgram subProgram  = (SubProgram) child;
+                List<String> sharedProgram = Arrays.asList(subProgram.getSharedWith());
+                if (sharedProgram.contains(programCode))
+                {
+                    return subProgram;
+                }
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Get the shared subprogram with the given code
+     * @param mentionProgram The root mention program
+     * @param code The CDMfr code
+     * @param report the report logger
+     * @param logger the logger
+     * @return The shared subprogram if exist or <code>null</code> if not found
+     */
+    protected SubProgram getSharedSubProgram (Program mentionProgram, String code, SynchronizationReport report, Logger logger)
+    {
+        for (AmetysObject child : mentionProgram.getChildren())
+        {
+            if (child instanceof SubProgram)
+            {
+                SubProgram subProgram  = (SubProgram) child;
+                if (subProgram.getCode().equals(code))
+                {
+                    return subProgram;
+                }
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Get the list of "shared with" program from the remote document
+     * @param contentNode The content node
+     * @param report the report logger
+     * @param logger the logger
+     * @return The CDM code of shared program
+     */
+    protected List<String> getSharedWithAsString (Node contentNode, SynchronizationReport report, Logger logger)
+    {
+        List<String> codes = new ArrayList<String>();
+        
+        Node sharedWithNode = _xPathProcessor.selectSingleNode(contentNode, AbstractProgram.SHARED_WITH);
+        
+        if (sharedWithNode != null)
+        {
+            NodeList itemNodes = sharedWithNode.getChildNodes();
+            for (int j = 0; j < itemNodes.getLength(); j++)
+            {
+                String code = itemNodes.item(j).getTextContent().trim();
+                if (StringUtils.isNotBlank(code))
+                {
+                    codes.add(code);
+                }
+            }
+        }
+        
+        return codes;
+    }
+    
+    /**
+     * Apply changes to parent mention
+     * Default implementation updates the OrgUnits of the mention from its child subprograms.
+     * @param updatedMentions the mentions to update
+     * @param doc the document
+     * @param report the report logger
+     * @param logger the logger
+     */
+    protected void applyChangesToMentions(Map<Program, Boolean> updatedMentions, Document doc, SynchronizationReport report, Logger logger)
+    {
+        for (Program mention : updatedMentions.keySet())
+        {
+            boolean importMode = updatedMentions.get(mention);
+            applyChangesToMention(mention, importMode, doc, report, logger);
+        }
+    }
+    
+    /**
+     * Apply change for one mention program
+     * @param mention the mention program
+     * @param importMode true if the mention is imported
+     * @param doc the document
+     * @param report the report logger
+     * @param logger the logger
+     */
+    protected void applyChangesToMention (Program mention, boolean importMode, Document doc, SynchronizationReport report, Logger logger)
+    {
+        updateMentionOrgUnits(mention, doc, importMode);
+        applyChanges(mention, report, logger);
+    }
+    
+    /**
+     * Update the mention orgUnits
+     * @param mention the mention program
+     * @param doc the document
+     * @param importMode true if the mention is imported
+     * @return true if there are some changes
+     */
+    protected boolean updateMentionOrgUnits (Program mention, Document doc, boolean importMode)
+    {
+        boolean hasChanged = false;
+        Set<String> childOrgUnits = new HashSet<String>();
+        for (AmetysObject content : mention.getChildren())
+        {
+            if (content instanceof SubProgram)
+            {
+                childOrgUnits.addAll(((SubProgram) content).getOrgUnits());
+            }
+        }
+        
+        NodeList nodes = doc.getElementsByTagName("program");
+        if (nodes.getLength() > 0)
+        {
+            Node programNode = nodes.item(0);
+            Node orgUnitNode = _xPathProcessor.selectSingleNode(programNode, Program.ORG_UNITS_REFERENCES);
+            
+            if (CDMSynchronizationHelper.isSynchronizable(orgUnitNode))
+            {
+                if (importMode || !mention.getMetadataHolder().hasMetadata(Program.ORG_UNITS_REFERENCES + SynchronizableContent.REMOTE_SUFFIX))
+                {
+                    mention.synchronizeMetadata(Program.ORG_UNITS_REFERENCES, true);
+                    hasChanged = true;
+                }
+                
+                if (!ArrayUtils.isEquals(childOrgUnits.toArray(new String[childOrgUnits.size()]), mention.getRemoteStringArray(Program.ORG_UNITS_REFERENCES)))
+                {
+                    mention.setRemoteMetadata(Program.ORG_UNITS_REFERENCES, childOrgUnits.toArray(new String[childOrgUnits.size()]));
+                    hasChanged = true;
+                }
+            }
+            else
+            {
+                if (!ArrayUtils.isEquals(childOrgUnits.toArray(new String[childOrgUnits.size()]), mention.getStringArray(Program.ORG_UNITS_REFERENCES)))
+                {
+                    mention.getMetadataHolder().setMetadata(Program.ORG_UNITS_REFERENCES, childOrgUnits.toArray(new String[childOrgUnits.size()]));
+                    hasChanged = true;
+                }
+            }
+        }
+        
+        
+        return hasChanged;
+    }
+
+    /**
+     * Get the mention program
+     * @param lang the content lang
+     * @param mentionValue the mention value
+     * @param degreeValue the degree value
+     * @return the mention program (null if it doesn't exist)
+     */
+    protected Program getMentionProgram(String lang, String mentionValue, String degreeValue)
+    {
+        Expression cTypeExpr = new ContentTypeExpression(Operator.EQ, ProgramFactory.PROGRAM_CONTENT_TYPE);
+        Expression langExpr = null;
+        if (lang != null)
+        {
+            langExpr = new LanguageExpression(Operator.EQ, lang);
+        }
+        Expression mentionExpr = new SynchronizeExpression(Program.MENTION, mentionValue);
+        Expression degreeExpr = new SynchronizeExpression(Program.DEGREE, degreeValue);
+        Expression expr = new AndExpression(cTypeExpr, langExpr, mentionExpr, degreeExpr);
+        
+        String jcrQuery = QueryHelper.getXPathQuery(null, "ametys:content", expr, null);
+        AmetysObjectIterable<Program> contents = _resolver.query(jcrQuery);
+        
+        if (contents.hasNext())
+        {
+            return contents.next();
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Get the mention title
+     * @param mentionProgram the mention program
+     * @param doc the document
+     * @param contentNode the content node
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return the mention title
+     */
+    protected String getMentionProgramTitle(SynchronizableContent mentionProgram, Document doc, Node contentNode, String lang, SynchronizationReport report, Logger logger)
+    {
+        String degree = _xPathProcessor.evaluateAsString(contentNode, Program.DEGREE); 
+        String degreeCode = _enumerationHelper.getItemCode(OdfEnumerationConstant.DEGREE, degree);
+        
+        String mentionKey = CDMSynchronizationHelper.getConstant(contentNode, _xPathProcessor.selectSingleNode(contentNode, Program.MENTION), _enumerationHelper, _xPathProcessor);
+        
+        String mentionLabel = _xPathProcessor.evaluateAsString(contentNode, Program.MENTION);
+        String mentionCode = _enumerationHelper.getItemCode(mentionKey, mentionLabel);
+        
+        I18nizableText mentionI18n = _enumerationHelper.getItemLabel(mentionKey, mentionCode);
+        I18nizableText degreeI18n = _enumerationHelper.getItemLabel(OdfEnumerationConstant.DEGREE, degreeCode);
+        
+        return _i18nUtils.translate(degreeI18n, lang) + " " + _i18nUtils.translate(mentionI18n, lang); 
+    }
+    
+    /**
+     * Set the common metadata for mention program
+     * @param mentionProgram the mention program
+     * @param doc the document
+     * @param contentNode the content node
+     * @param report the report logger
+     * @param logger the logger
+     */
+    protected void setMentionCommonMetadata(SynchronizableContent mentionProgram, Document doc, Node contentNode, SynchronizationReport report, Logger logger)
+    {
+        String degree = _xPathProcessor.evaluateAsString(contentNode, Program.DEGREE); 
+        String degreeCode = _enumerationHelper.getItemCode(OdfEnumerationConstant.DEGREE, degree);
+
+        String mentionKey = CDMSynchronizationHelper.getConstant(contentNode, _xPathProcessor.selectSingleNode(contentNode, Program.MENTION), _enumerationHelper, _xPathProcessor);
+        
+        String mentionLabel = _xPathProcessor.evaluateAsString(contentNode, Program.MENTION);
+        String mentionCode = _enumerationHelper.getItemCode(mentionKey, mentionLabel);
+
+        String domain = _xPathProcessor.evaluateAsString(contentNode, Program.DOMAIN);
+        String domainCode = _enumerationHelper.getItemCode(OdfEnumerationConstant.DOMAIN, domain);
+        
+        setProgramCatalog(mentionProgram);
+        mentionProgram.synchronizeMetadata(Program.DEGREE, true);
+        mentionProgram.setRemoteMetadata(Program.DEGREE, degreeCode);
+        
+        mentionProgram.synchronizeMetadata(Program.MENTION, true);
+        mentionProgram.setRemoteMetadata(Program.MENTION, mentionCode);
+        
+        mentionProgram.synchronizeMetadata(Program.DOMAIN, true);
+        mentionProgram.setRemoteMetadata(Program.DOMAIN, domainCode);
+    }
+    
+    /**
+     * Synchronized specific metadata to mention program
+     * @param mentionProgram the mention program
+     * @param doc the document
+     * @param contentNode the content node
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     */
+    protected void setMentionAdditionalMetadata(SynchronizableContent mentionProgram, Document doc, Node contentNode, String lang, SynchronizationReport report, Logger logger)
+    {
+        for (String metadataPath : _mentionMetadataPaths)
+        {
+            ContentType cType = _cTypeEP.getExtension(ProgramFactory.PROGRAM_CONTENT_TYPE);
+            if (ContentTypeHelper.getMetadataDefinitionByPath(cType, metadataPath) == null)
+            {
+                String warn = "Le chemin de la metadata " + metadataPath + " du programme n'est pas définie.";
+                report.warn(warn);
+                logger.warn(warn);
+            }
+            else
+            {
+                Node metadataNode = _xPathProcessor.selectSingleNode(contentNode, metadataPath);
+                try
+                {
+                    synchronizeMetadata(contentNode, doc, mentionProgram, metadataNode, report, logger, true, lang, null);
+                }
+                catch (WorkflowException e)
+                {
+                    String error = "Une erreur est survenue durant la synchronization de la metadata : " + metadataPath;
+                    logger.error(error);
+                    report.error(error);
+                }
+            }
+        }
+    }
+}
Index: main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CoAccreditedRemoteCDMFrODFSyncManager.java
===================================================================
--- main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CoAccreditedRemoteCDMFrODFSyncManager.java	(revision 0)
+++ main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CoAccreditedRemoteCDMFrODFSyncManager.java	(revision 0)
@@ -0,0 +1,52 @@
+/*
+ *  Copyright 2016 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.odfsync.cdmfr;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+
+import org.ametys.plugins.odfsync.ImportManager;
+import org.ametys.plugins.odfsync.ODFSyncManager;
+import org.ametys.plugins.odfsync.SynchronizationManager;
+
+/**
+ * Implementation to import ODF contents from co accredited remote CDM-fr input stream
+ *
+ */
+public class CoAccreditedRemoteCDMFrODFSyncManager implements ODFSyncManager, Serviceable
+{
+    private CoAccreditedRemoteCDMFRImportManager _importManager;
+    
+    @Override
+    public void service(ServiceManager smanager) throws ServiceException
+    {
+        _importManager = (CoAccreditedRemoteCDMFRImportManager) smanager.lookup(CoAccreditedRemoteCDMFRImportManager.ROLE);
+    }
+    
+    @Override
+    public ImportManager getImportManager()
+    {
+        return _importManager;
+    }
+
+    @Override
+    public SynchronizationManager getSynchronizationManager()
+    {
+        return null;
+    }
+    
+}
Index: main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/MergeMetadataForSharedProgramHelper.java
===================================================================
--- main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/MergeMetadataForSharedProgramHelper.java	(revision 0)
+++ main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/MergeMetadataForSharedProgramHelper.java	(revision 0)
@@ -0,0 +1,1276 @@
+/*
+ *  Copyright 2016 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.ametys.plugins.odfsync.cdmfr;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.excalibur.xml.xpath.XPathProcessor;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+
+import org.ametys.cms.contenttype.ContentType;
+import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
+import org.ametys.cms.contenttype.MetadataDefinition;
+import org.ametys.cms.contenttype.MetadataType;
+import org.ametys.cms.contenttype.RepeaterDefinition;
+import org.ametys.odf.SynchronizableContent;
+import org.ametys.odf.orgunit.OrgUnit;
+import org.ametys.odf.person.Person;
+import org.ametys.odf.program.Program;
+import org.ametys.odf.program.SubProgram;
+import org.ametys.odf.program.SubProgramFactory;
+import org.ametys.plugins.odfsync.SynchronizationReport;
+import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.plugins.repository.RepositoryConstants;
+import org.ametys.plugins.repository.metadata.CompositeMetadata;
+import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
+
+/**
+ * Helper for merging metadata from shared programs
+ */
+public class MergeMetadataForSharedProgramHelper implements Serviceable, Component
+{
+    /** The avalon role */
+    public static final String ROLE = MergeMetadataForSharedProgramHelper.class.getName();
+
+    /** The name of the shared programs */
+    public static final String SHARED_PROGRAMS_JCR_NODE = "shared-programs";
+
+    /** The content type extension point */
+    protected ContentTypeExtensionPoint _cTypeE;
+    
+    /** The xpath processor */
+    protected XPathProcessor _xPathProcessor;
+    
+    /** The ametys object resolver */
+    protected AmetysObjectResolver _resolver;
+    
+    /** The ametys object resolver */
+    protected CDMFrImportManager _importManager;
+    
+    @Override
+    public void service(ServiceManager manager) throws ServiceException
+    {
+        _cTypeE = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);        
+        _xPathProcessor = (XPathProcessor) manager.lookup(XPathProcessor.ROLE);
+        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
+    }
+    
+    /**
+     * Initialize the component with the import manager
+     * @param importManager the import manager
+     */
+    public void initialize(CDMFrImportManager importManager)
+    {
+        _importManager = importManager;
+    }
+    
+    /**
+     * Synchronize the main program with shared metadata
+     * @param mainProgram the main program
+     * @param metadataToMerge the metadata to merge
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some changes
+     * @throws RepositoryException if an error occurred
+     */
+    public boolean synchronizeMainProgram(SubProgram mainProgram, Set<String> metadataToMerge, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        if (!_checkMetadataPath(metadataToMerge, report, logger))
+        {
+            return false;
+        }
+        
+        boolean hasChanged = false;
+        for (String metadataPath : metadataToMerge)
+        {
+            ContentType cType = _cTypeE.getExtension(SubProgramFactory.SUBPROGRAM_CONTENT_TYPE);
+            String[] pathSegments = StringUtils.split(metadataPath, '/');
+            MetadataDefinition metadataDefinition = cType.getMetadataDefinition(pathSegments[0]);
+            if (metadataDefinition.isMultiple())
+            {
+                hasChanged = synchronizeMultipleMetadataForMainProgram(metadataDefinition, mainProgram, metadataPath, report, logger) || hasChanged;
+            }
+            else
+            {
+                hasChanged = synchronizeSingleMetadataForMainProgram(metadataDefinition, mainProgram, metadataPath, report, logger) || hasChanged;
+            }
+        }
+        
+        if (hasChanged)
+        {
+            mainProgram.saveChanges();
+        }
+        
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize multiple metadata for main program
+     * @param metadataDefinition the metadata definition
+     * @param mainProgram the main program
+     * @param metadataPath the metadata path
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some changes
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeMultipleMetadataForMainProgram(MetadataDefinition metadataDefinition, SubProgram mainProgram, String metadataPath, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        MetadataType type = metadataDefinition.getType();
+        List<Value> metadataValues = getMetadataListValues(mainProgram, metadataPath, report, logger);
+        switch (type)
+        {
+            case STRING:
+            case REFERENCE:
+            case USER:
+                Set<String> allStringValue = new HashSet<String>();
+                String[] stringArray = mainProgram.getStringArray(metadataPath);
+                allStringValue.addAll(Arrays.asList(stringArray));
+                
+                for (Value stringValue : metadataValues)
+                {
+                    allStringValue.add(stringValue.getString());
+                }
+                
+                if (mainProgram.getMetadataHolder().getBoolean(metadataPath  + SynchronizableContent.SYNC_SUFFIX, false))
+                {
+                    mainProgram.setRemoteMetadata(metadataPath, allStringValue.toArray(new String[allStringValue.size()]));
+                }
+                else
+                {
+                    mainProgram.getMetadataHolder().setMetadata(metadataPath, allStringValue.toArray(new String[allStringValue.size()]));
+                }
+                return true;
+            case DOUBLE:
+                double[] doubleArray = mainProgram.getDoubleArray(metadataPath);
+                Set<Double> allDoubleArray = new HashSet<Double>();
+                allDoubleArray.addAll(Arrays.asList(Arrays.asList(doubleArray).toArray(new Double[doubleArray.length])));
+                
+                for (Value stringValue : metadataValues)
+                {
+                    allDoubleArray.add(stringValue.getDouble());
+                }
+                
+                if (mainProgram.getMetadataHolder().getBoolean(metadataPath  + SynchronizableContent.SYNC_SUFFIX, false))
+                {
+                    mainProgram.setRemoteMetadata(metadataPath, allDoubleArray.toArray(new String[allDoubleArray.size()]));
+                }
+                else
+                {
+                    mainProgram.getMetadataHolder().setMetadata(metadataPath, allDoubleArray.toArray(new String[allDoubleArray.size()]));
+                }
+                return true;
+            case LONG:
+                long[] longArray = mainProgram.getLongArray(metadataPath);
+                Set<Long> allLongArray = new HashSet<Long>();
+                allLongArray.addAll(Arrays.asList(Arrays.asList(longArray).toArray(new Long[longArray.length])));
+                
+                for (Value stringValue : metadataValues)
+                {
+                    allLongArray.add(stringValue.getLong());
+                }
+                
+                if (mainProgram.getMetadataHolder().getBoolean(metadataPath  + SynchronizableContent.SYNC_SUFFIX, false))
+                {
+                    mainProgram.setRemoteMetadata(metadataPath, allLongArray.toArray(new String[allLongArray.size()]));
+                }
+                else
+                {
+                    mainProgram.getMetadataHolder().setMetadata(metadataPath, allLongArray.toArray(new String[allLongArray.size()]));
+                }
+                return true;
+            default:
+                String warnMessage = "La fusion des métadonnées n'est pas gérée pour le type '" + type.toString() + "'";
+                report.warn(warnMessage);
+                logger.warn(warnMessage);
+                return false;
+        }
+    }
+    
+    /**
+     * Synchronize single metadata for main program
+     * @param metadataDefinition the metadata definition
+     * @param mainProgram the main program
+     * @param metadataPath the metadata path
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some changes
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeSingleMetadataForMainProgram(MetadataDefinition metadataDefinition, SubProgram mainProgram, String metadataPath, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        MetadataType type = metadataDefinition.getType();
+        switch (type)
+        {
+            case COMPOSITE:
+                if (metadataDefinition instanceof RepeaterDefinition)
+                {
+                    return synchronizeMainProgramRepeater(metadataDefinition, mainProgram, metadataPath, report, logger);
+                }
+                else
+                {    
+                    String warnMessage = "La fusion des métadonnées n'est pas gérée pour les valeurs simple";
+                    report.warn(warnMessage);
+                    logger.warn(warnMessage);
+                }
+                return false;
+            default:
+                String warnMessage = "La fusion des métadonnées n'est pas gérée pour le type '" + type.toString() + "'";
+                report.warn(warnMessage);
+                logger.warn(warnMessage);
+                return false;
+        }
+    }
+    
+    /**
+     * Get the metadata list values from shared program
+     * @param mainProgram the main program
+     * @param metadataPath the metadata path
+     * @param report the report logger
+     * @param logger the logger
+     * @return the list of the metadata values
+     * @throws RepositoryException if an error occurred
+     */
+    protected List<Value> getMetadataListValues(SubProgram mainProgram, String metadataPath, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        String[] sharedProgramList = mainProgram.getSharedWith();
+        Node mainProgramNode = mainProgram.getNode();
+        List<Value> metadataValues = new ArrayList<Value>();
+        if (mainProgramNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":" + SHARED_PROGRAMS_JCR_NODE))
+        {
+            Node sharedProgramsNode = mainProgramNode.getNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":" + SHARED_PROGRAMS_JCR_NODE);
+            for (String programCode : sharedProgramList)
+            {
+                if (sharedProgramsNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + programCode))
+                {
+                    Node sharedProgramNode = sharedProgramsNode.getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + programCode);
+                    Property property = sharedProgramNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath);
+                    metadataValues.addAll(Arrays.asList(property.getValues()));
+                }
+            }
+        }
+        
+        return metadataValues;
+    }
+    
+    /**
+     * Get the list of repeater entry from shared program
+     * @param mainProgram the main program
+     * @param metadataPath the metadata path
+     * @param report the report logger
+     * @param logger the logger
+     * @return the list of the repeater entry node
+     * @throws RepositoryException if an error occurred
+     */
+    protected List<Node> getMetadataRepeaterListNodes(SubProgram mainProgram, String metadataPath, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        List<Node> repeaterEntryNodeList = new ArrayList<Node>();
+        
+        String[] sharedProgramList = mainProgram.getSharedWith();
+        Node mainProgramNode = mainProgram.getNode();
+        if (mainProgramNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":" + SHARED_PROGRAMS_JCR_NODE))
+        {
+            Node sharedProgramsNode = mainProgramNode.getNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":" + SHARED_PROGRAMS_JCR_NODE);
+            for (String programCode : sharedProgramList)
+            {
+                if (sharedProgramsNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + programCode))
+                {
+                    Node sharedProgramNode = sharedProgramsNode.getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + programCode);
+                    if (sharedProgramNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath))
+                    {
+                        Node repeaterNode = sharedProgramNode.getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath);
+                        NodeIterator entryNodes = repeaterNode.getNodes();
+                        while (entryNodes.hasNext())
+                        {
+                            repeaterEntryNodeList.add((Node) entryNodes.next());
+                        }
+                    }
+                }
+            }
+        }
+        
+        return repeaterEntryNodeList;
+    }
+    
+    /**
+     * Merge all repeater entry to the main program
+     * @param metadataDefinition the metadata definition
+     * @param mainProgram the main program
+     * @param metadataPath the metadata path
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some changes
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeMainProgramRepeater(MetadataDefinition metadataDefinition, SubProgram mainProgram, String metadataPath, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        boolean hasChanged = false;
+        List<Node> repeaterEntryNodeList = getMetadataRepeaterListNodes(mainProgram, metadataPath, report, logger);
+        
+        Node mainProgramNode = mainProgram.getNode();
+        if (mainProgramNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath))
+        {
+            mainProgramNode.getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath).remove();
+        }
+        
+        mainProgramNode.addNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath, "ametys:compositeMetadata");
+        
+        int nbEntry = 1;
+        Node repeaterNode = mainProgramNode.getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath);
+        for (Node entryNode : repeaterEntryNodeList)
+        {
+            Node repeaterEntryNode = repeaterNode.addNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + nbEntry, "ametys:compositeMetadata");
+            nbEntry++;
+            
+            PropertyIterator properties = entryNode.getProperties();
+            while (properties.hasNext())
+            {
+                Property property = (Property) properties.next();
+                if (property.getName().startsWith(RepositoryConstants.NAMESPACE_PREFIX + ":"))
+                {
+                    MetadataDefinition metadataRepeaterEntryDefinition = metadataDefinition.getMetadataDefinition(StringUtils.substringAfter(property.getName(), RepositoryConstants.NAMESPACE_PREFIX + ":"));
+                    if (metadataRepeaterEntryDefinition.isMultiple())
+                    {
+                        repeaterEntryNode.setProperty(property.getName(), property.getValues());
+                        hasChanged = true;
+                    }
+                    else
+                    {
+                        repeaterEntryNode.setProperty(property.getName(), property.getValue());
+                        hasChanged = true;
+                    }
+                }
+            }
+        }
+        
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize the shared program node for main program
+     * @param mainProgram the main program
+     * @param sharedProgram the shared program
+     * @param metadataToMerge the set of metadata to merge
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    public boolean synchronizeSharedProgramNode(SubProgram mainProgram, SubProgram sharedProgram, Set<String> metadataToMerge, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        if (!_checkMetadataPath(metadataToMerge, report, logger))
+        {
+            return false;
+        }
+        
+        boolean hasChanged = false;
+        String sharedProgramCode = sharedProgram.getCode();
+        Node sharedProgramNode = getSharedProgramNode(mainProgram, sharedProgramCode, report, logger);
+        
+        hasChanged = synchronizeAllMetadataForSharedProgram(sharedProgramNode, sharedProgram, metadataToMerge, report, logger);
+        
+        if (hasChanged)
+        {
+            mainProgram.getNode().getSession().save();
+        }
+        
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize the shared program node for main program
+     * @param doc the document
+     * @param mainProgram the main program node
+     * @param sharedProgramDom the dom of the shared program
+     * @param metadataToMerge the set of metadata to merge
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    public boolean synchronizeSharedProgramNode(Document doc, SubProgram mainProgram, org.w3c.dom.Node sharedProgramDom, Set<String> metadataToMerge, String lang, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        if (!_checkMetadataPath(metadataToMerge, report, logger))
+        {
+            return false;
+        }
+        
+        boolean hasChanged = false;
+        String sharedProgramCode = _xPathProcessor.evaluateAsString(sharedProgramDom, Program.CODE);
+        Node sharedProgramNode = getSharedProgramNode(mainProgram, sharedProgramCode, report, logger);
+        
+        hasChanged = synchronizeAllMetadataForSharedProgram(doc, sharedProgramNode, sharedProgramDom, metadataToMerge, lang, report, logger);
+        
+        if (hasChanged)
+        {
+            mainProgram.getNode().getSession().save();
+        }
+        
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize all the metadata 
+     * @param sharedProgramNode the shared program node
+     * @param sharedProgram the shared program
+     * @param metadataToMerge the metadata to merge
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeAllMetadataForSharedProgram(Node sharedProgramNode, SubProgram sharedProgram, Set<String> metadataToMerge, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        boolean hasChanged = false;
+        for (String metadataPath : metadataToMerge)
+        {
+            ContentType cType = _cTypeE.getExtension(SubProgramFactory.SUBPROGRAM_CONTENT_TYPE);
+            String[] pathSegments = StringUtils.split(metadataPath, '/');
+            MetadataDefinition metadataDefinition = cType.getMetadataDefinition(pathSegments[0]);
+            
+            hasChanged = synchronizeMetadataForSharedProgram(metadataDefinition, sharedProgramNode, sharedProgram, metadataPath, report, logger) || hasChanged;
+        }
+        
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize all the metadata 
+     * @param doc the document
+     * @param sharedProgramNode the shared program node
+     * @param sharedProgramDom the dom of the shared program
+     * @param metadataToMerge the metadata to merge
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeAllMetadataForSharedProgram(Document doc, Node sharedProgramNode, org.w3c.dom.Node sharedProgramDom, Set<String> metadataToMerge, String lang, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        boolean hasChanged = false;
+        for (String metadataPath : metadataToMerge)
+        {
+            ContentType cType = _cTypeE.getExtension(SubProgramFactory.SUBPROGRAM_CONTENT_TYPE);
+            String[] pathSegments = StringUtils.split(metadataPath, '/');
+            MetadataDefinition metadataDefinition = cType.getMetadataDefinition(pathSegments[0]);
+            
+            hasChanged = synchronizeMetadataForSharedProgram(doc, metadataDefinition, sharedProgramNode, sharedProgramDom, metadataPath, lang, report, logger) || hasChanged;
+        }
+        
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize metadata for shared program
+     * @param metadataDefinition the metadata definition
+     * @param sharedProgramNode the jcr node of the shared program
+     * @param sharedProgram the shared program
+     * @param metadataPath the metadata path
+     * @param report the report logger
+     * @param logger the logger
+     * @return true is there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeMetadataForSharedProgram(MetadataDefinition metadataDefinition, Node sharedProgramNode, SubProgram sharedProgram, String metadataPath, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        if (metadataDefinition.isMultiple())
+        {
+            return synchronizeMetadataMultipleForSharedProgram(metadataDefinition, sharedProgramNode, sharedProgram, metadataPath, report, logger);
+        }
+        else
+        {
+            return synchronizeMetadataSingleForSharedProgram(metadataDefinition, sharedProgramNode, sharedProgram, metadataPath, report, logger);
+        }
+    }
+    
+    /**
+     * Synchronize metadata for shared program
+     * @param doc the document
+     * @param metadataDefinition the metadata definition
+     * @param sharedProgramNode the jcr node of the shared program
+     * @param sharedProgramDom the dom of the shared program
+     * @param metadataPath the metadata path
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return true is there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeMetadataForSharedProgram(Document doc, MetadataDefinition metadataDefinition, Node sharedProgramNode, org.w3c.dom.Node sharedProgramDom, String metadataPath, String lang, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        if (metadataDefinition.isMultiple())
+        {
+            return synchronizeMetadataMultipleForSharedProgram(doc, metadataDefinition, sharedProgramNode, sharedProgramDom, metadataPath, lang, report, logger);
+        }
+        else
+        {
+            return synchronizeMetadataSingleForSharedProgram(doc, metadataDefinition, sharedProgramNode, sharedProgramDom, metadataPath, lang, report, logger);
+        }
+    }
+    
+    /**
+     * Synchronize multiple metadata for shared program
+     * @param metadataDefinition the metadata definition
+     * @param sharedProgramNode the jcr node of the shared program
+     * @param sharedProgram the shared program
+     * @param metadataPath the metadata path
+     * @param report the report logger
+     * @param logger the logger
+     * @return true is there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeMetadataMultipleForSharedProgram(MetadataDefinition metadataDefinition, Node sharedProgramNode, SubProgram sharedProgram, String metadataPath, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        boolean hasChanged = false;
+        MetadataType type = metadataDefinition.getType();
+        Session session = sharedProgramNode.getSession();
+        ValueFactory valueFactory = session.getValueFactory();
+        switch (type)
+        {
+            case STRING:
+            case REFERENCE:
+            case USER:
+                Value[] stringValueArray = getStringArrayValue(metadataPath, sharedProgram, valueFactory, report, logger);
+                hasChanged = synchronizeArrayMetadata(sharedProgramNode, metadataPath, stringValueArray, report, logger);
+                break;
+            case DOUBLE:
+                Value[] doubleValueArray = getDoubleArrayValue(metadataPath, sharedProgram, valueFactory, report, logger);
+                hasChanged = synchronizeArrayMetadata(sharedProgramNode, metadataPath, doubleValueArray, report, logger);
+                break;
+            case LONG:
+                Value[] longValueArray = getLongArrayValue(metadataPath, sharedProgram, valueFactory, report, logger);
+                hasChanged = synchronizeArrayMetadata(sharedProgramNode, metadataPath, longValueArray, report, logger);
+                break;
+            default:
+                String warnMessage = "La fusion des métadonnées n'est pas gérée pour le type '" + type.toString() + "'";
+                report.warn(warnMessage);
+                logger.warn(warnMessage);
+                break;
+        }
+        
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize multiple metadata for shared program
+     * @param doc the document
+     * @param metadataDefinition the metadata definition
+     * @param sharedProgramNode the jcr node of the shared program
+     * @param sharedProgramDom the dom of the shared program
+     * @param metadataPath the metadata path
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return true is there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeMetadataMultipleForSharedProgram(Document doc, MetadataDefinition metadataDefinition, Node sharedProgramNode, org.w3c.dom.Node sharedProgramDom, String metadataPath, String lang, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        boolean hasChanged = false;
+        MetadataType type = metadataDefinition.getType();
+        Session session = sharedProgramNode.getSession();
+        ValueFactory valueFactory = session.getValueFactory();
+        switch (type)
+        {
+            case STRING:
+            case REFERENCE:
+            case USER:
+                Value[] stringValueArray = getStringArrayValue(doc, metadataPath, sharedProgramDom, valueFactory, lang, report, logger);
+                hasChanged = synchronizeArrayMetadata(sharedProgramNode, metadataPath, stringValueArray, report, logger);
+                break;
+            case DOUBLE:
+                Value[] doubleValueArray = getDoubleArrayValue(metadataPath, sharedProgramDom, valueFactory, report, logger);
+                hasChanged = synchronizeArrayMetadata(sharedProgramNode, metadataPath, doubleValueArray, report, logger);
+                break;
+            case LONG:
+                Value[] longValueArray = getLongArrayValue(metadataPath, sharedProgramDom, valueFactory, report, logger);
+                hasChanged = synchronizeArrayMetadata(sharedProgramNode, metadataPath, longValueArray, report, logger);
+                break;
+            default:
+                String warnMessage = "La fusion des métadonnées n'est pas gérée pour le type '" + type.toString() + "'";
+                report.warn(warnMessage);
+                logger.warn(warnMessage);
+                break;
+        }
+        
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize single metadata for shared program
+     * @param metadataDefinition the metadata definition
+     * @param sharedProgramNode the jcr node of the shared program
+     * @param sharedProgram the shared program
+     * @param metadataPath the metadata path
+     * @param report the report logger
+     * @param logger the logger
+     * @return true is there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeMetadataSingleForSharedProgram(MetadataDefinition metadataDefinition, Node sharedProgramNode, SubProgram sharedProgram, String metadataPath, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        boolean hasChanged = false;
+        MetadataType type = metadataDefinition.getType();
+        Session session = sharedProgramNode.getSession();
+        ValueFactory valueFactory = session.getValueFactory();
+        switch (type)
+        {
+            case STRING:
+            case REFERENCE:
+            case USER:
+                Value stringValue = valueFactory.createValue(sharedProgram.getString(metadataPath));
+                hasChanged = synchronizeMetadata(sharedProgramNode, metadataPath, stringValue, report, logger);
+                break;
+            case DOUBLE:
+                Value doubleValue = valueFactory.createValue(sharedProgram.getDouble(metadataPath));
+                hasChanged = synchronizeMetadata(sharedProgramNode, metadataPath, doubleValue, report, logger);
+                break;
+            case LONG:
+                Value longValue = valueFactory.createValue(sharedProgram.getLong(metadataPath));
+                hasChanged = synchronizeMetadata(sharedProgramNode, metadataPath, longValue, report, logger);
+                break;
+            case BOOLEAN:
+                Value booleanValue = valueFactory.createValue(sharedProgram.getBoolean(metadataPath));
+                hasChanged = synchronizeMetadata(sharedProgramNode, metadataPath, booleanValue, report, logger);
+                break;
+            case COMPOSITE:
+                if (metadataDefinition instanceof RepeaterDefinition)
+                {
+                    hasChanged = synchronizeRepeaterMetadata(metadataDefinition, sharedProgramNode, sharedProgram, metadataPath, report, logger);
+                }
+                else
+                {    
+                    String warnMessage = "La fusion des métadonnées n'est pas gérée pour le type '" + type.toString() + "'";
+                    report.warn(warnMessage);
+                    logger.warn(warnMessage);
+                }
+                break;
+            default:
+                String warnMessage = "La fusion des métadonnées n'est pas gérée pour le type '" + type.toString() + "'";
+                report.warn(warnMessage);
+                logger.warn(warnMessage);
+                break;
+        }
+        
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize single metadata for shared program
+     * @param doc the document
+     * @param metadataDefinition the metadata definition
+     * @param sharedProgramNode the jcr node of the shared program
+     * @param sharedProgramDom the dom of the shared program
+     * @param metadataPath the metadata path
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return true is there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeMetadataSingleForSharedProgram(Document doc, MetadataDefinition metadataDefinition, Node sharedProgramNode, org.w3c.dom.Node sharedProgramDom, String metadataPath, String lang, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        boolean hasChanged = false;
+        MetadataType type = metadataDefinition.getType();
+        Session session = sharedProgramNode.getSession();
+        ValueFactory valueFactory = session.getValueFactory();
+        switch (type)
+        {
+            case STRING:
+            case REFERENCE:
+            case USER:
+                Value stringValue = valueFactory.createValue(_xPathProcessor.evaluateAsString(sharedProgramDom, metadataPath));
+                hasChanged = synchronizeMetadata(sharedProgramNode, metadataPath, stringValue, report, logger);
+                break;
+            case DOUBLE:
+                Value doubleValue = valueFactory.createValue(_xPathProcessor.evaluateAsNumber(sharedProgramDom, metadataPath).doubleValue());
+                hasChanged = synchronizeMetadata(sharedProgramNode, metadataPath, doubleValue, report, logger);
+                break;
+            case LONG:
+                Value longValue = valueFactory.createValue(_xPathProcessor.evaluateAsNumber(sharedProgramDom, metadataPath).longValue());
+                hasChanged = synchronizeMetadata(sharedProgramNode, metadataPath, longValue, report, logger);
+                break;
+            case BOOLEAN:
+                Value booleanValue = valueFactory.createValue(_xPathProcessor.evaluateAsBoolean(sharedProgramDom, metadataPath));
+                hasChanged = synchronizeMetadata(sharedProgramNode, metadataPath, booleanValue, report, logger);
+                break;
+            case COMPOSITE:
+                if (metadataDefinition instanceof RepeaterDefinition)
+                {
+                    hasChanged = synchronizeRepeaterMetadata(doc, metadataDefinition, sharedProgramNode, sharedProgramDom, metadataPath, lang, report, logger);
+                }
+                else
+                {    
+                    String warnMessage = "La fusion des métadonnées n'est pas gérée pour le type '" + type.toString() + "'";
+                    report.warn(warnMessage);
+                    logger.warn(warnMessage);
+                }
+                break;
+            default:
+                String warnMessage = "La fusion des métadonnées n'est pas gérée pour le type '" + type.toString() + "'";
+                report.warn(warnMessage);
+                logger.warn(warnMessage);
+                break;
+        }
+        
+        return hasChanged;
+    }
+    
+    /**
+     * Get string array value from the shared program
+     * @param metadataPath the metadata path
+     * @param sharedProgram the shared program
+     * @param valueFactory the value factory
+     * @param report the report logger
+     * @param logger the logger
+     * @return the string array
+     */
+    protected Value[] getStringArrayValue(String metadataPath, SubProgram sharedProgram, ValueFactory valueFactory, SynchronizationReport report, Logger logger)
+    {
+        String[] stringArray = sharedProgram.getStringArray(metadataPath);
+        Value[] stringValues = new Value[stringArray.length];
+        for (int i = 0; i < stringArray.length; i++)
+        {
+            stringValues[i] = valueFactory.createValue(stringArray[i]);
+        }
+        
+        return stringValues;
+    }
+    
+    /**
+     * Get string array value from the dom shared program
+     * @param doc the document
+     * @param metadataPath the metadata path
+     * @param sharedProgramDom the dom shared program
+     * @param valueFactory the value factory
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return the string array
+     */
+    protected Value[] getStringArrayValue(Document doc, String metadataPath, org.w3c.dom.Node sharedProgramDom, ValueFactory valueFactory, String lang, SynchronizationReport report, Logger logger)
+    {
+        List<String> listString = new ArrayList<String>();
+        org.w3c.dom.Node metadataNode = _xPathProcessor.selectSingleNode(sharedProgramDom, metadataPath);
+        String type = metadataNode.getAttributes().getNamedItem("type").getTextContent().trim();
+        
+        NodeList itemNodes = metadataNode.getChildNodes();
+        for (int j = 0; j < itemNodes.getLength(); j++)
+        {
+            // Specific for orgunit, need to get id from code (create it if no exist)
+            if (type.equals("orgunit"))
+            {
+                org.w3c.dom.Node itemNode = itemNodes.item(j);
+                OrgUnit ouContent = getOrCreateOrgUnitContent(doc, itemNode, lang, report, logger);
+                if (ouContent != null)
+                {
+                    listString.add(ouContent.getId());
+                }
+            }
+            else if (type.equals("person")) // Specific for person, need to get id from code (create it if no exist)
+            {
+                org.w3c.dom.Node itemNode = itemNodes.item(j);
+                Person personContent = getOrCreatePersonContent(doc, itemNode, lang, report, logger);
+                if (personContent != null)
+                {
+                    listString.add(personContent.getId());
+                }
+            }
+            else
+            {
+                String stringValue = itemNodes.item(j).getTextContent().trim();
+                if (StringUtils.isNotBlank(stringValue))
+                {
+                    listString.add(stringValue);
+                }
+            }
+        }
+        
+        Value[] stringValues = new Value[listString.size()];
+        for (int i = 0; i < listString.size(); i++)
+        {
+            stringValues[i] = valueFactory.createValue(listString.get(i));
+        }
+        
+        return stringValues;
+    }
+    
+    /** 
+     * Get or create orgUnit content
+     * @param doc the document
+     * @param itemNode the item node
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return the orgUnit
+     */
+    protected OrgUnit getOrCreateOrgUnitContent(Document doc, org.w3c.dom.Node itemNode, String lang, SynchronizationReport report, Logger logger)
+    {
+        boolean ouImportMode = false;
+        String cdmCode = itemNode.getTextContent().trim();
+        org.w3c.dom.Node ouNode = _xPathProcessor.selectSingleNode(doc.getFirstChild(), "orgunit[@CDMid ='" + cdmCode + "']");
+        OrgUnit ouContent = _importManager.getLocalOrgUnitIfExists(ouNode, null, report, logger);
+        if (ouContent == null)
+        {
+            ouContent = _importManager.createOrgUnit(ouNode, null, report, logger); // default language
+            ouImportMode = true;
+        }
+        
+        if (ouContent != null)
+        {
+            try
+            {
+                if (!_importManager._synchronizedContents.contains(ouContent.getId()))
+                {
+                    // Synchronize OrgUnit
+                    String infoMsg = "Synchronisation de la composante \"" + ouContent.getTitle() + " (" + ouContent.getUAICode() + ")\"";
+                    report.info(infoMsg);
+                    logger.info(infoMsg);
+                    
+                    if (_importManager.synchronizeContent(doc, ouContent, ouNode, report, logger, ouImportMode, lang))
+                    {
+                        _importManager.applyChanges(ouContent, report, logger);
+                    }
+                    else
+                    {
+                        infoMsg = "Aucune modification détectée sur la composante \"" + ouContent.getTitle() + "\" depuis la dernière synchronisation";
+                        report.info(infoMsg);
+                        logger.info(infoMsg);
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                String errorMsg = "Une erreur est survenue lors la synchronization de l'établissement '" + ouContent.getTitle() + "'";
+                report.info(errorMsg);
+                logger.info(errorMsg);
+            }
+            
+            if (ouImportMode)
+            {
+                // Direct validation if permitted
+                _importManager.validateContent(ouContent, report, logger);
+            }
+        }
+        
+        return ouContent;
+    }
+    
+    /** 
+     * Get or create person content
+     * @param doc the document
+     * @param itemNode the item node
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return the person
+     */
+    protected Person getOrCreatePersonContent(Document doc, org.w3c.dom.Node itemNode, String lang, SynchronizationReport report, Logger logger)
+    {
+        boolean personImportMode = false;
+        String cdmCode = itemNode.getTextContent().trim();
+        org.w3c.dom.Node personNode = _xPathProcessor.selectSingleNode(doc.getFirstChild(), "person[@CDMid ='" + cdmCode + "']");
+        Person personContent = _importManager.getLocalPersonIfExists(personNode, null, report, logger);
+        if (personContent == null)
+        {
+            personContent = _importManager.createPerson(personNode, null, report, logger); // default language
+            personImportMode = true;
+        }
+        
+        if (personContent != null)
+        {
+            try
+            {
+                if (!_importManager._synchronizedContents.contains(personContent.getId()))
+                {
+                    // Synchronize person
+                    String infoMsg = "Synchronisation de la personne \"" + personContent.getName() + "\"";
+                    report.info(infoMsg);
+                    logger.info(infoMsg);
+                    
+                    if (_importManager.synchronizeContent(doc, personContent, personNode, report, logger, personImportMode, lang))
+                    {
+                        _importManager.applyChanges(personContent, report, logger);
+                    }
+                    else
+                    {
+                        infoMsg = "Aucune modification détectée sur la personne \"" + personContent.getName() + "\" depuis la dernière synchronisation";
+                        report.info(infoMsg);
+                        logger.info(infoMsg);
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                String errorMsg = "Une erreur est survenue lors la synchronization de la person '" + personContent.getTitle() + "'";
+                report.info(errorMsg);
+                logger.info(errorMsg);
+            }
+            
+            if (personImportMode)
+            {
+                // Direct validation if permitted
+                _importManager.validateContent(personContent, report, logger);
+            }
+        }
+        
+        return personContent;
+    }
+    
+    /**
+     * Get long array value from the shared program
+     * @param metadataPath the metadata path
+     * @param sharedProgram the shared program
+     * @param valueFactory the value factory
+     * @param report the report logger
+     * @param logger the logger
+     * @return the long array
+     */
+    protected Value[] getLongArrayValue(String metadataPath, SubProgram sharedProgram, ValueFactory valueFactory, SynchronizationReport report, Logger logger)
+    {
+        long[] longArray = sharedProgram.getLongArray(metadataPath);
+        Value[] longValues = new Value[longArray.length];
+        for (int i = 0; i < longArray.length; i++)
+        {
+            longValues[i] = valueFactory.createValue(longArray[i]);
+        }
+        
+        return longValues;
+    }
+    
+    /**
+     * Get long array value from the dom shared program
+     * @param metadataPath the metadata path
+     * @param sharedProgramDom the dom shared program
+     * @param valueFactory the value factory
+     * @param report the report logger
+     * @param logger the logger
+     * @return the long array
+     */
+    protected Value[] getLongArrayValue(String metadataPath, org.w3c.dom.Node sharedProgramDom, ValueFactory valueFactory, SynchronizationReport report, Logger logger)
+    {
+        List<Long> listLong = new ArrayList<Long>();
+        org.w3c.dom.Node metadataNode = _xPathProcessor.selectSingleNode(sharedProgramDom, metadataPath);
+        
+        NodeList itemNodes = metadataNode.getChildNodes();
+        for (int j = 0; j < itemNodes.getLength(); j++)
+        {
+            String longValueAsString = itemNodes.item(j).getTextContent().trim();
+            if (StringUtils.isNotBlank(longValueAsString))
+            {
+                listLong.add(Long.valueOf(longValueAsString));
+            }
+        }
+        
+        Value[] longValues = new Value[listLong.size()];
+        for (int i = 0; i < listLong.size(); i++)
+        {
+            longValues[i] = valueFactory.createValue(listLong.get(i));
+        }
+        
+        return longValues;
+    }
+    
+    /**
+     * Get double array value from the shared program
+     * @param metadataPath the metadata path
+     * @param sharedProgram the shared program
+     * @param valueFactory the value factory
+     * @param report the report logger
+     * @param logger the logger
+     * @return the double array
+     */
+    protected Value[] getDoubleArrayValue(String metadataPath, SubProgram sharedProgram, ValueFactory valueFactory, SynchronizationReport report, Logger logger)
+    {
+        double[] doubleArray = sharedProgram.getDoubleArray(metadataPath);
+        Value[] doubleValues = new Value[doubleArray.length];
+        for (int i = 0; i < doubleArray.length; i++)
+        {
+            doubleValues[i] = valueFactory.createValue(doubleArray[i]);
+        }
+        
+        return doubleValues;
+    }
+    
+    /**
+     * Get double array value from the dom shared program
+     * @param metadataPath the metadata path
+     * @param sharedProgramDom the dom shared program
+     * @param valueFactory the value factory
+     * @param report the report logger
+     * @param logger the logger
+     * @return the double array
+     */
+    protected Value[] getDoubleArrayValue(String metadataPath, org.w3c.dom.Node sharedProgramDom, ValueFactory valueFactory, SynchronizationReport report, Logger logger)
+    {
+        List<Double> listDouble = new ArrayList<Double>();
+        org.w3c.dom.Node metadataNode = _xPathProcessor.selectSingleNode(sharedProgramDom, metadataPath);
+        
+        NodeList itemNodes = metadataNode.getChildNodes();
+        for (int j = 0; j < itemNodes.getLength(); j++)
+        {
+            String doubleValueAsString = itemNodes.item(j).getTextContent().trim();
+            if (StringUtils.isNotBlank(doubleValueAsString))
+            {
+                listDouble.add(Double.valueOf(doubleValueAsString));
+            }
+        }
+        
+        Value[] doubleValues = new Value[listDouble.size()];
+        for (int i = 0; i < listDouble.size(); i++)
+        {
+            doubleValues[i] = valueFactory.createValue(listDouble.get(i));
+        }
+        
+        return doubleValues;
+    }
+    
+    /**
+     * Synchronize array metadata
+     * @param sharedProgramNode the shared program node
+     * @param metadataPath the metadata path
+     * @param valueArray the array of value
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeArrayMetadata(Node sharedProgramNode, String metadataPath, Value[] valueArray, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        boolean hasChanged = false;
+        if (sharedProgramNode.hasProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath))
+        {
+            Property property = sharedProgramNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath);
+            if (!ArrayUtils.isEquals(valueArray, property.getValues()))
+            {
+                sharedProgramNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath, valueArray);
+                hasChanged = true;
+            }
+        }
+        else
+        {
+            sharedProgramNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath, valueArray);
+            hasChanged = true;
+        }
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize metadata
+     * @param sharedProgramNode the shared program node
+     * @param fullMetadataPath the metadata path
+     * @param value the array of value
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some change
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeMetadata(Node sharedProgramNode, String fullMetadataPath, Value value, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        String metadataPath = fullMetadataPath;
+        if (StringUtils.contains(fullMetadataPath, "/"))
+        {
+            metadataPath = StringUtils.substringAfterLast(fullMetadataPath, "/");
+        }
+        
+        boolean hasChanged = false;
+        if (sharedProgramNode.hasProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath))
+        {
+            Property property = sharedProgramNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath);
+            if (!value.equals(property.getValue()))
+            {
+                sharedProgramNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath, value);
+                hasChanged = true;
+            }
+        }
+        else
+        {
+            sharedProgramNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath, value);
+            hasChanged = true;
+        }
+        return hasChanged;
+    }
+    
+    /**
+     * Synchronize repeater metadata for shared program
+     * @param metadataDefinition the metadata definition
+     * @param sharedProgramNode the jcr node of the shared program
+     * @param sharedProgram the shared program
+     * @param metadataPath the metadata path
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some changed (always true for repeater)
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeRepeaterMetadata(MetadataDefinition metadataDefinition, Node sharedProgramNode, SubProgram sharedProgram, String metadataPath, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        if (sharedProgramNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath))
+        {
+            sharedProgramNode.getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath).remove();
+        }
+        
+        Node repeaterNode = sharedProgramNode.addNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath, "ametys:compositeMetadata");
+        
+        ModifiableCompositeMetadata metadataHolder = sharedProgram.getMetadataHolder();
+        if (metadataHolder.hasMetadata(metadataPath))
+        {
+            ModifiableCompositeMetadata compositeMetadata = metadataHolder.getCompositeMetadata(metadataPath);
+            for (String name : compositeMetadata.getMetadataNames())
+            {
+                Node repeaterEntryNode = repeaterNode.addNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + name, "ametys:compositeMetadata");
+                CompositeMetadata metadataRepeater = compositeMetadata.getCompositeMetadata(name);
+                
+                for (String metadataRepeaterPath : metadataRepeater.getMetadataNames())
+                {
+                    String fullMetadataPath = metadataPath + "/" + name + "/" + metadataRepeaterPath;
+                    
+                    MetadataDefinition metadataRepeaterDefinition = metadataDefinition.getMetadataDefinition(metadataRepeaterPath);
+                    synchronizeMetadataForSharedProgram(metadataRepeaterDefinition, repeaterEntryNode, sharedProgram, fullMetadataPath, report, logger);
+                }                            
+            }
+        }
+        
+        return true;
+    }
+    
+    /**
+     * Synchronize repeater metadata for shared program
+     * @param doc the document
+     * @param metadataDefinition the metadata definition
+     * @param sharedProgramNode the jcr node of the shared program
+     * @param sharedProgramDom the dom of the shared program
+     * @param metadataPath the metadata path
+     * @param lang the lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there are some changed (always true for repeater)
+     * @throws RepositoryException if an error occurred
+     */
+    protected boolean synchronizeRepeaterMetadata(Document doc, MetadataDefinition metadataDefinition, Node sharedProgramNode, org.w3c.dom.Node sharedProgramDom, String metadataPath, String lang, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        if (sharedProgramNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath))
+        {
+            sharedProgramNode.getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath).remove();
+        }
+        
+        Node repeaterNode = sharedProgramNode.addNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + metadataPath, "ametys:compositeMetadata");
+        
+        org.w3c.dom.Node repeaterDom = _xPathProcessor.selectSingleNode(sharedProgramDom, metadataPath);
+        NodeList entryNodes = repeaterDom.getChildNodes();
+        
+        for (int i = 0; i < entryNodes.getLength(); i++)
+        {
+            org.w3c.dom.Node entryNode = entryNodes.item(i);
+            String entryName = entryNode.getAttributes().getNamedItem("name").getTextContent().trim();
+            
+            Node repeaterEntryNode = repeaterNode.addNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + entryName, "ametys:compositeMetadata");
+            
+            NodeList childNodes = entryNode.getChildNodes();
+            for (int j = 0; j < childNodes.getLength(); j++)
+            {
+                org.w3c.dom.Node childNode = childNodes.item(j);
+                String subMetadataName = childNode.getLocalName();
+                MetadataDefinition metadataRepeaterDefinition = metadataDefinition.getMetadataDefinition(subMetadataName);
+                
+                synchronizeMetadataForSharedProgram(doc, metadataRepeaterDefinition, repeaterEntryNode, entryNode, subMetadataName, lang, report, logger);
+            }
+        }
+        
+        return true;
+    }
+    
+    /**
+     * Get the shared program node (create it if no exist)
+     * @param mainProgram the main program
+     * @param subProgramCode the sub program code
+     * @param report the report logger
+     * @param logger the logger
+     * @return the shared program node
+     * @throws RepositoryException if an error occurred
+     */
+    protected Node getSharedProgramNode(SubProgram mainProgram, String subProgramCode, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        Node mainProgramNode = mainProgram.getNode();
+        Node rootNode = null;
+        if (mainProgramNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":" + SHARED_PROGRAMS_JCR_NODE))
+        {
+            rootNode = mainProgramNode.getNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":" + SHARED_PROGRAMS_JCR_NODE);
+        }
+        else
+        {
+            rootNode = mainProgramNode.addNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":" + SHARED_PROGRAMS_JCR_NODE, "ametys:compositeMetadata");
+        }
+        
+        if (rootNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + subProgramCode))
+        {
+            return rootNode.getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + subProgramCode);
+        }
+        else
+        {
+            return rootNode.addNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + subProgramCode, "ametys:compositeMetadata");
+        }
+    }
+    
+    private boolean _checkMetadataPath(Set<String> metadataToMerge, SynchronizationReport report, Logger logger)
+    {
+        ContentType subProgramContentType = _cTypeE.getExtension(SubProgramFactory.SUBPROGRAM_CONTENT_TYPE);
+        for (String metadataPath : metadataToMerge)
+        {
+            if (StringUtils.contains(metadataPath, "/"))
+            {
+                String warnMessage = "Les metadonnées de type composite ne sont pas gérées : '" + metadataPath + "'";
+                report.warn(warnMessage);
+                logger.warn(warnMessage);
+                
+                return false;
+            }
+            
+            if (subProgramContentType.getMetadataDefinition(metadataPath) == null)
+            {
+                String warnMessage = "La metadonnée '" + metadataPath + "' est invalide pour un sous-programme";
+                report.warn(warnMessage);
+                logger.warn(warnMessage);
+                
+                return false;
+            }
+        }
+        
+        return true;
+    }
+}
Index: main/plugin-odf-sync/stylesheets/cdm11/cdmfr2content.xsl
===================================================================
--- main/plugin-odf-sync/stylesheets/cdm11/cdmfr2content.xsl	(revision 41919)
+++ main/plugin-odf-sync/stylesheets/cdm11/cdmfr2content.xsl	(working copy)
@@ -167,7 +167,8 @@
 		<xsl:call-template name="program-cdmCode"/>
 		<xsl:call-template name="program-title"/>
 		<xsl:call-template name="program-mention"/>
-		<xsl:call-template name="program-speciality"/>
+		<xsl:call-template name="program-sharedWith"/>
+        <xsl:call-template name="program-speciality"/>
 		<xsl:call-template name="program-educationLevel"/>
 		<xsl:call-template name="program-degree"/>
 		<xsl:call-template name="program-jointOrgUnit"/>
Index: main/plugin-odf-sync/stylesheets/cdm11/program2content.xsl
===================================================================
--- main/plugin-odf-sync/stylesheets/cdm11/program2content.xsl	(revision 41919)
+++ main/plugin-odf-sync/stylesheets/cdm11/program2content.xsl	(working copy)
@@ -301,20 +301,29 @@
 		</xsl:if>
 	</xsl:template>
 	
-	<!-- Mention -->
-	<xsl:template name="program-mention">
-        <mention type="string">
+   <!-- Mention -->
+    <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/@fieldNameCode"/>
-        		</xsl:when>
+                <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>
+    </xsl:template>
 	
+	<!-- Partagé avec -->
+    <xsl:template name="program-sharedWith">
+        <sharedWith type="string" multiple="true">
+            <xsl:for-each select="cdm:infoBlock[@userDefined = 'ametys-extension']/cdm:extension/ametys-cdm:sharedWith">
+                <item><xsl:value-of select="."/></item>
+            </xsl:for-each>
+        </sharedWith>
+    </xsl:template>
+    
 	<!-- Spécialité -->
 	<xsl:template name="program-speciality">
 		<speciality type="string"><xsl:value-of select="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:speciality/cdmfr:specialityName|/cdm:CDM/cdmfr:habilitation/cdmfr:licPro/cdmfr:specialite/cdm:text"/></speciality>
Index: main/plugin-odf-sync/stylesheets/cdm112/cdmfr2content.xsl
===================================================================
--- main/plugin-odf-sync/stylesheets/cdm112/cdmfr2content.xsl	(revision 41919)
+++ main/plugin-odf-sync/stylesheets/cdm112/cdmfr2content.xsl	(working copy)
@@ -167,6 +167,7 @@
 		<xsl:call-template name="program-cdmCode"/>
 		<xsl:call-template name="program-title"/>
 		<xsl:call-template name="program-mention"/>
+		<xsl:call-template name="program-sharedWith"/>
 		<xsl:call-template name="program-speciality"/>
 		<xsl:call-template name="program-educationLevel"/>
 		<xsl:call-template name="program-degree"/>
Index: main/plugin-odf-sync/stylesheets/cdm112/program2content.xsl
===================================================================
--- main/plugin-odf-sync/stylesheets/cdm112/program2content.xsl	(revision 41919)
+++ main/plugin-odf-sync/stylesheets/cdm112/program2content.xsl	(working copy)
@@ -303,10 +303,10 @@
 	
 	<!-- Mention -->
 	<xsl:template name="program-mention">
-        <mention type="string">
+        <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/@fieldNameCode"/>
+                    <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"/>
@@ -315,6 +315,15 @@
         </mention>
 	</xsl:template>
 	
+	<!-- Partagé avec -->
+    <xsl:template name="program-sharedWith">
+        <sharedWith type="string" multiple="true">
+            <xsl:for-each select="cdm:infoBlock[@userDefined = 'ametys-extension']/cdm:extension/ametys-cdm:sharedWith">
+                <item><xsl:value-of select="."/></item>
+            </xsl:for-each>
+        </sharedWith>
+    </xsl:template>
+	
 	<!-- Spécialité -->
 	<xsl:template name="program-speciality">
 		<speciality type="string"><xsl:value-of select="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:speciality/cdmfr:specialityName|/cdm:CDM/cdmfr:habilitation/cdmfr:licPro/cdmfr:specialite/cdm:text"/></speciality>
Index: main/plugin-odf-sync/stylesheets/cdmfr2content.xsl
===================================================================
--- main/plugin-odf-sync/stylesheets/cdmfr2content.xsl	(revision 41919)
+++ main/plugin-odf-sync/stylesheets/cdmfr2content.xsl	(working copy)
@@ -167,7 +167,8 @@
 		<xsl:call-template name="program-cdmCode"/>
 		<xsl:call-template name="program-title"/>
 		<xsl:call-template name="program-mention"/>
-		<xsl:call-template name="program-speciality"/>
+		<xsl:call-template name="program-sharedWith"/>
+        <xsl:call-template name="program-speciality"/>
 		<xsl:call-template name="program-educationLevel"/>
 		<xsl:call-template name="program-degree"/>
 		<xsl:call-template name="program-jointOrgUnit"/>
Index: main/plugin-odf-sync/stylesheets/program2content.xsl
===================================================================
--- main/plugin-odf-sync/stylesheets/program2content.xsl	(revision 41919)
+++ main/plugin-odf-sync/stylesheets/program2content.xsl	(working copy)
@@ -301,20 +301,29 @@
 		</xsl:if>
 	</xsl:template>
 	
-	<!-- Mention -->
-	<xsl:template name="program-mention">
-        <mention type="string">
+    <!-- Mention -->
+    <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/@fieldNameCode"/>
-        		</xsl:when>
+                <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>
+    </xsl:template>
 	
+	<!-- Partagé avec -->
+    <xsl:template name="program-sharedWith">
+        <sharedWith type="string" multiple="true">
+            <xsl:for-each select="cdm:infoBlock[@userDefined = 'ametys-extension']/cdm:extension/ametys-cdm:sharedWith">
+                <item><xsl:value-of select="."/></item>
+            </xsl:for-each>
+        </sharedWith>
+    </xsl:template>
+    
 	<!-- Spécialité -->
 	<xsl:template name="program-speciality">
 	   <speciality type="string"><xsl:value-of select="/cdm:CDM/cdmfr:habilitation/cdmfr:field/cdmfr:speciality/cdmfr:specialityName|/cdm:CDM/cdmfr:habilitation/cdmfr:licPro/cdmfr:specialite/cdm:text"/></speciality>
