Index: main/plugin-odf-sync/i18n/messages_en.xml
===================================================================
--- main/plugin-odf-sync/i18n/messages_en.xml	(revision 56734)
+++ main/plugin-odf-sync/i18n/messages_en.xml	(working copy)
@@ -53,6 +53,10 @@
     <message key="PLUGINS_ODF_IMPORT_DEFAULT_CATALOG_DESC">Code of the default catalog when importing programs.&lt;br/&gt;Leave blank if you do not want to automatically associate catalog during import.&lt;br/&gt;If the application has only one catalog, the programs will be automatically associated with it whatever the value of this parameter.</message>
     <message key="PLUGINS_ODF_IMPORT_FORCE_CATALOG">Catalog for import</message>
     <message key="PLUGINS_ODF_IMPORT_FORCE_CATALOG_DESC">Code of the forced catalog when importing programs.&lt;br/&gt;Leave blank if you do not want do force catalog.&lt;br/&gt;If empty, the value of \"Default catalog\" parameter will be used.</message>
+    <message key="PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_LABEL">Shared subprograms</message>
+    <message key="PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_DESC">Select how subprograms will be shared.&lt;br/&gt;With the field "Shared with" : a subprogram will be shared with other subprograms of the same mention whose code will be set in this field.&lt;br/&gt;With the field "Title" : a subprogram will be shared with all subprograms of the same mention with the same title.</message>
+    <message key="PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_SHARED_METADATA_OPTION">With the field "Shared with"</message>
+    <message key="PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_SAME_TITLE_OPTION">With the field "Title"</message>
     
     <!-- +
     	 | WORKFLOW
Index: main/plugin-odf-sync/i18n/messages_fr.xml
===================================================================
--- main/plugin-odf-sync/i18n/messages_fr.xml	(revision 56734)
+++ main/plugin-odf-sync/i18n/messages_fr.xml	(working copy)
@@ -53,6 +53,10 @@
     <message key="PLUGINS_ODF_IMPORT_DEFAULT_CATALOG_DESC">Code du catalogue par défaut lors de l'import de formations.&lt;br/&gt;Laissez vide si vous ne souhaitez pas associer automatiquement de catalogue lors de l'import.&lt;br/&gt;Si l'application ne comporte qu'un seul catalogue, les formations seront automatiquement associées à ce dernier quelque soit la valeur de ce paramètre.</message>
     <message key="PLUGINS_ODF_IMPORT_FORCE_CATALOG">Catalogue d'import</message>
     <message key="PLUGINS_ODF_IMPORT_FORCE_CATALOG_DESC">Code du catalogue lors de l'import de formations.&lt;br/&gt;Laissez vide si vous ne souhaitez pas forcer le catalogue lors de l'import.&lt;br/&gt;Si vide, la valeur du paramètre \"Catalogue par défaut\" sera utilisée.</message>
+    <message key="PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_LABEL">Parcours partagés</message>
+    <message key="PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_DESC">Sélectionnez la méthode de partage de vos parcours :&lt;br/&gt;Avec le champs \"Partagé avec\" : un parcours sera partagé avec les autres parcours de la même mention dont le code a été renseigné dans ce champs.&lt;br/&gt;Avec le champs \"Titre\" : un parcours sera partagé avec tous les parcours de la même mention ayant le même titre.</message>
+    <message key="PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_SHARED_METADATA_OPTION">Avec le champs \"Partagé avec\"</message>
+    <message key="PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_SAME_TITLE_OPTION">Avec le champs \"Titre\"</message>
     
     <!-- +
          | WORKFLOW
Index: main/plugin-odf-sync/plugin.xml
===================================================================
--- main/plugin-odf-sync/plugin.xml	(revision 56734)
+++ main/plugin-odf-sync/plugin.xml	(working copy)
@@ -381,6 +381,23 @@
                 <group i18n="true">PLUGINS_ODF_IMPORT_CONFIG_GROUP</group>
                 <widget>textarea</widget>
             </param>
+            <param type="string" id="org.ametys.plugins.odfsync.shared.subprogram">
+                <label i18n="true">PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_LABEL</label>
+                <description i18n="true">PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_DESC</description>
+                <enumeration>
+                    <entry>
+                        <label i18n="true">PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_SHARED_METADATA_OPTION</label>
+                        <value>WITH_SHARED_METADATA</value>
+                    </entry>
+                    <entry>
+                        <label i18n="true">PLUGINS_ODF_CONFIG_REMOTE_SHARED_PROGRAM_SAME_TITLE_OPTION</label>
+                        <value>WITH_SAME_TITLE</value>
+                    </entry>
+                </enumeration>
+                <default-value>WITH_SAME_TITLE</default-value>
+                <category i18n="true">plugin.odf:PLUGINS_ODF_CONFIG_CATEGORY</category>
+                <group i18n="true">PLUGINS_ODF_IMPORT_CONFIG_GROUP</group>
+            </param>
             <param-ref id="odf.import.force.catalog"/>
             <param-ref id="odf.import.default.catalog"/>
             <param-ref id="odf.import.auto-validation"/>
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 56734)
+++ main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/CoAccreditedRemoteCDMFRImportManager.java	(working copy)
@@ -92,6 +92,22 @@
     protected MergeMetadataForSharedProgramHelper _mergeMetadataHelper;
     
     
+    /**
+     * Enum to define the way to detect shared subProgram
+     */
+    public enum SharedWithType
+    {
+        /**
+         * The main subProgram set the other shared program in the metadata "shared-with"
+         */
+        WITH_SHARED_METADATA,
+       
+        /**
+         * All subProgram with the same title are shared. The first imported subProgram is the main subProgram.
+         */
+        WITH_SAME_TITLE
+    }
+    
     @Override
     public void configure(Configuration configuration) throws ConfigurationException
     {
@@ -314,11 +330,10 @@
             if (content == null)
             {
                 // 2.1- 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())
+                if (isSecondarySharedSubPrograms(mentionProgram, contentNode, contentLang, report, logger))
                 {
                     // Search if there is a main subprogram shared with it.
-                    SubProgram mainSubProgram = getMainSubProgram(mentionProgram, contentNode, report, logger);
+                    SubProgram mainSubProgram = getMainSharedSubProgram(mentionProgram, contentNode, contentLang, 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
@@ -376,19 +391,25 @@
                     // Then synchronize it
                     _synchronizeSubProgram(content, contentLang, catalogName, mentionProgram, contentNode, doc, updatedMentions, true, report, logger);
                     
-                    // Browse existing shared subprograms to first merge them then delete them.
-                    synchronizeAndDeleteSharedSubPrograms(mentionProgram, (SubProgram) content, sharedWith, report, logger);
-                    
-                    try
-                    {
-                        // Then merge main subprogram
-                        _mergeMetadataHelper.mergeSharedMetadata((SubProgram) content, contentNode, _metadataPathsToMerge, false, report, logger);
-                    }
-                    catch (RepositoryException e)
+                    // Browse existing shared subprograms to first merge them then delete them. 
+                    // When we shared subprograms with the title, it can't have other created shared subPrograms because the first one is the main shared subprogram 
+                    SharedWithType sharedWithType = getSharedWithType();
+                    if (SharedWithType.WITH_SHARED_METADATA == sharedWithType)
                     {
-                        String warnMsg = "Impossible de synchronizer les metadatas du programme principal \"" + content.getTitle() + "\"";
-                        report.warn(warnMsg);
-                        logger.warn(warnMsg);
+                        List<String> sharedWith = getSharedWithAsString(contentNode, report, logger);
+                        synchronizeAndDeleteSharedSubPrograms(mentionProgram, (SubProgram) content, sharedWith, report, logger);
+                        
+                        try
+                        {
+                            // Then merge main subprogram
+                            _mergeMetadataHelper.mergeSharedMetadata((SubProgram) content, contentNode, _metadataPathsToMerge, false, report, logger);
+                        }
+                        catch (RepositoryException e)
+                        {
+                            String warnMsg = "Impossible de synchronizer les metadatas du programme principal \"" + content.getTitle() + "\"";
+                            report.warn(warnMsg);
+                            logger.warn(warnMsg);
+                        }
                     }
                     
                     if (!updatedMentions.containsKey(mentionProgram))
@@ -400,8 +421,7 @@
             else
             {
                 // The imported program exists as subprogram
-                List<String> sharedWith = getSharedWithAsString(contentNode, report, logger);
-                if (sharedWith.isEmpty())
+                if (isSecondarySharedSubPrograms(mentionProgram, contentNode, contentLang, report, logger))
                 {
                     // Simply synchronize it 
                     if (_synchronizeSubProgram(content, contentLang, catalogName, mentionProgram, contentNode, doc, updatedMentions, false, report, logger))
@@ -418,8 +438,15 @@
                     // Synchronize subprogram from remote node
                     boolean hasChanged = _synchronizeSubProgram(content, contentLang, catalogName, mentionProgram, contentNode, doc, updatedMentions, false, report, logger);
                     
-                    // Browse existing shared subprograms to first merge them then delete them.
-                    hasChanged = synchronizeAndDeleteSharedSubPrograms(mentionProgram, (SubProgram) content, sharedWith, report, logger) || hasChanged;
+                    // When we shared subprograms with the title, it can't have other created shared subPrograms because the first one is the main shared subprogram 
+                    SharedWithType sharedWithType = getSharedWithType();
+                    if (SharedWithType.WITH_SHARED_METADATA == sharedWithType)
+                    {
+                        List<String> sharedWith = getSharedWithAsString(contentNode, report, logger);
+
+                        // Browse existing shared subprograms to first merge them then delete them.
+                        hasChanged = synchronizeAndDeleteSharedSubPrograms(mentionProgram, (SubProgram) content, sharedWith, report, logger) || hasChanged;
+                    }
                     
                     try
                     {
@@ -456,6 +483,82 @@
     }
     
     /**
+     * Get the defined way to detect shared program
+     * @return shared with type
+     */
+    protected SharedWithType getSharedWithType()
+    {
+        String sharedSubProgramType = Config.getInstance().getValueAsString("org.ametys.plugins.odfsync.shared.subprogram");
+        return SharedWithType.valueOf(sharedSubProgramType);
+    }
+    
+    /**
+     * True if the subProgram node is shared with a main subProgram.
+     * False if the subProgram node is not shared or if the main subProgram is not already imported
+     * @param mentionProgram the mention program
+     * @param contentNode the content node
+     * @param contentLang the content lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if it's a secondary subProgram
+     */
+    protected boolean isSecondarySharedSubPrograms(Program mentionProgram, Node contentNode, String contentLang, SynchronizationReport report, Logger logger)
+    {
+        SharedWithType sharedWithType = getSharedWithType();
+        switch (sharedWithType)
+        {
+            case WITH_SHARED_METADATA:
+                List<String> sharedWith = getSharedWithAsString(contentNode, report, logger);
+                return sharedWith.isEmpty();
+            case WITH_SAME_TITLE:
+                return hasSubProgramWithSameTitle(mentionProgram, contentNode, contentLang, report, logger);
+            default:
+                return false;
+        }
+    }
+    
+    /**
+     * True if there is a subProgram with the same title of the content node
+     * @param mentionProgram the mention program
+     * @param contentNode the content node
+     * @param contentLang the content lang
+     * @param report the report logger
+     * @param logger the logger
+     * @return true if there is a subProgram with the same title of the content node
+     */
+    protected boolean hasSubProgramWithSameTitle(Program mentionProgram, Node contentNode, String contentLang, SynchronizationReport report, Logger logger)
+    {
+        SubProgram subProgram = getSubProgramWithSameTitle(mentionProgram, contentNode, contentLang);
+        return subProgram != null;
+    }
+    
+    
+    /**
+     * Get the subProgram with the same title
+     * @param mentionProgram the mention program
+     * @param contentNode the content node
+     * @param lang the lang
+     * @return the subProgram with the same title. null if there are no subProgram with same title.
+     */
+    protected SubProgram getSubProgramWithSameTitle(Program mentionProgram, Node contentNode, String lang)
+    {
+        String contentTitle = _xPathProcessor.evaluateAsString(contentNode, "title");
+        
+        for (AmetysObject child : mentionProgram.getChildren())
+        {
+            if (child instanceof SubProgram)
+            {
+                SubProgram subProgram  = (SubProgram) child;
+                if (subProgram.getTitle().equals(contentTitle))
+                {
+                    return subProgram;
+                }
+            }
+        }
+        return null;
+    }
+    
+    /**
      * Synchronize the shared metadata of a shared subprogram before deleting the subprogram
      * @param rootProgram The root mention
      * @param mainSubProgram The main subprogram
@@ -561,11 +664,34 @@
      * 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 lang the content lang
+     * @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 getMainSharedSubProgram(Program mentionProgram, Node contentNode, String lang, SynchronizationReport report, Logger logger)
+    {
+        SharedWithType sharedWithType = getSharedWithType();
+        switch (sharedWithType)
+        {
+            case WITH_SHARED_METADATA:
+                return getMainSharedWithSubProgram(mentionProgram, contentNode, report, logger);
+            case WITH_SAME_TITLE:
+                return getSubProgramWithSameTitle(mentionProgram, contentNode, lang);
+            default:
+                return null;
+        }
+    }
+    
+    /**
+     * 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)
+    protected SubProgram getMainSharedWithSubProgram(Program mentionProgram, Node contentNode, SynchronizationReport report, Logger logger)
     {
         String programCode = _xPathProcessor.evaluateAsString(contentNode, AbstractProgram.CODE);
         
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 56734)
+++ main/plugin-odf-sync/src/org/ametys/plugins/odfsync/cdmfr/MergeMetadataForSharedProgramHelper.java	(working copy)
@@ -21,6 +21,7 @@
 import java.util.List;
 import java.util.Set;
 
+import javax.jcr.NodeIterator;
 import javax.jcr.RepositoryException;
 
 import org.apache.avalon.framework.component.Component;
@@ -70,15 +71,8 @@
     /** The name of the JCR node holding the shared metadata  */
     public static final String SHARED_PROGRAMS_NODE_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":shared-programs";
 
-    /** The content type extension point */
-    protected ContentTypeExtensionPoint _cTypeE;
-    
     /** The xpath processor */
     protected static XPathProcessor _xPathProcessor;
-    
-    /** The ametys object resolver */
-    protected AmetysObjectResolver _resolver;
-    
     /** The ametys object resolver */
     protected static CDMFrImportManager _importManager;
     /** The ODF enumeration helper */
@@ -86,6 +80,11 @@
     /** The cocoon context */
     protected static Context _cocoonContext;
     
+    /** The content type extension point */
+    protected ContentTypeExtensionPoint _cTypeE;
+    /** The ametys object resolver */
+    protected AmetysObjectResolver _resolver;
+
     @Override
     public void service(ServiceManager manager) throws ServiceException
     {
@@ -224,6 +223,7 @@
                 {
                     return mergeSharedRepeaterMetadata(metadataDefinition, mainProgram, metadataPath, report, logger);
                 }
+                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);
@@ -239,18 +239,21 @@
      * @param report The report logger
      * @param logger the logger
      * @return the merge metadata in a String array
-     * @throws RepositoryException
+     * @throws RepositoryException if an error occurred
      */
     protected String[] getMergeSharedStringArrayMetadata (SubProgram mainSubProgram, String metadataName, SynchronizationReport report, Logger logger) throws RepositoryException
     {
         Set<String> values = new HashSet<String>();
         
-        String[] sharedSubProgramCodes = mainSubProgram.getSharedWith();
-        String[] allSharedSubProgramCodes = (String[]) ArrayUtils.add(sharedSubProgramCodes, mainSubProgram.getCode());
-        
         // Merge values of all shared programs
-        for (String code : allSharedSubProgramCodes)
+        javax.jcr.Node sharedProgramRootNode = getSharedProgramRootNode(mainSubProgram, logger);
+        NodeIterator nodes = sharedProgramRootNode.getNodes();
+        
+        while (nodes.hasNext())
         {
+            javax.jcr.Node sharedProgramNode = (javax.jcr.Node) nodes.next();
+            String code = sharedProgramNode.getName();
+            
             ModifiableCompositeMetadata sharedMetaHolder = getSharedMetadataHolder(mainSubProgram, code, report, logger);
             
             String[] sharedValues = sharedMetaHolder.getStringArray(metadataName, new String[0]);
@@ -284,11 +287,14 @@
         ModifiableCompositeMetadata mergedRepeater = null;
         int index = 1;
         
-        String[] sharedSubProgramCodes = mainSubProgram.getSharedWith();
-        String[] allSharedSubProgramCodes = (String[]) ArrayUtils.add(sharedSubProgramCodes, mainSubProgram.getCode());
+        javax.jcr.Node sharedProgramRootNode = getSharedProgramRootNode(mainSubProgram, logger);
+        NodeIterator nodes = sharedProgramRootNode.getNodes();
         
-        for (String code : allSharedSubProgramCodes)
+        while (nodes.hasNext())
         {
+            javax.jcr.Node sharedProgramNode = (javax.jcr.Node) nodes.next();
+            String code = sharedProgramNode.getName();
+            
             ModifiableCompositeMetadata sharedMetaHolder = getSharedMetadataHolder(mainSubProgram, code, report, logger);
             
             if (sharedMetaHolder.hasMetadata(metadataName))
@@ -362,7 +368,7 @@
      * @param logger the logger
      * @return true if there are some change
      * @throws RepositoryException if an error occurred
-     * @throws WorkflowException 
+     * @throws WorkflowException if an error occurred 
      */
     public boolean synchronizeSharedMetadataFromDOM(Document doc, SubProgram mainSubProgram, Node sharedProgramDom, Set<String> metadataToMerge, String lang, boolean importMode, SynchronizationReport report, Logger logger) throws RepositoryException, WorkflowException
     {
@@ -423,7 +429,7 @@
      * @param logger the logger
      * @return true is there are some change
      * @throws RepositoryException if an error occurred
-     * @throws WorkflowException 
+     * @throws WorkflowException if an error occurred
      */
     protected boolean synchronizeSharedMetadataFromDOM(Document doc, MetadataDefinition metadataDefinition, ModifiableCompositeMetadata sharedMetaHolder, Node sharedProgramNode, String metadataPath, String lang, boolean importMode, SynchronizationReport report, Logger logger) throws RepositoryException, WorkflowException
     {
@@ -543,7 +549,7 @@
      * @param report The synchronization report
      * @param logger The logger
      * @return <code>true</code> if changes were made
-     * @throws WorkflowException 
+     * @throws WorkflowException if an error occurred 
      */
     public boolean synchronizeOrgUnitMetadata (Document doc, Node contentNode, Node metadataNode, ModifiableCompositeMetadata sharedMetaHolder, String metadataName, String lang, SynchronizationReport report, Logger logger) throws WorkflowException
     {
@@ -582,7 +588,7 @@
      * @param report The synchronization report
      * @param logger The logger
      * @return <code>true</code> if changes were made
-     * @throws WorkflowException 
+     * @throws WorkflowException if an error occurred 
      */
     public boolean synchronizePersonMetadata (Document doc, Node contentNode, Node metadataNode, ModifiableCompositeMetadata sharedMetaHolder, String metadataName, String lang, SynchronizationReport report, Logger logger) throws WorkflowException
     {
@@ -783,6 +789,7 @@
                 {
                     return synchronizeRepeaterMetadata(metadataDefinition, sharedMetaHolder, sharedProgram, metadataPath, report, logger);
                 }
+                return false;
             default:
                 String warn = "Le type de métadonnée " + type.toString() + " n'est pas supporté pour la fusion des parcours partagées. La métadonnée '" + metadataPath + "' est ignorée.";
                 report.warn(warn);
@@ -855,15 +862,13 @@
     }
     
     /**
-     * Get the composite metadata holding the shared metadata for subprogram of given code
+     * Get the shared program node holding the shared subprogram node
      * @param mainProgram the main program
-     * @param subProgramCode the sub program code
-     * @param report the report logger
      * @param logger the logger
-     * @return the shared composite metadata
+     * @return root node
      * @throws RepositoryException if an error occurred
      */
-    protected ModifiableCompositeMetadata getSharedMetadataHolder(SubProgram mainProgram, String subProgramCode, SynchronizationReport report, Logger logger) throws RepositoryException
+    protected javax.jcr.Node getSharedProgramRootNode(SubProgram mainProgram, Logger logger) throws RepositoryException
     {
         javax.jcr.Node node = mainProgram.getNode();
         
@@ -877,6 +882,23 @@
             rootNode = node.addNode(SHARED_PROGRAMS_NODE_NAME, "ametys:compositeMetadata");
         }
         
+        return rootNode;
+    }
+    
+    /**
+     * Get the composite metadata holding the shared metadata for subprogram of given code
+     * @param mainProgram the main program
+     * @param subProgramCode the sub program code
+     * @param report the report logger
+     * @param logger the logger
+     * @return the shared composite metadata
+     * @throws RepositoryException if an error occurred
+     */
+    protected ModifiableCompositeMetadata getSharedMetadataHolder(SubProgram mainProgram, String subProgramCode, SynchronizationReport report, Logger logger) throws RepositoryException
+    {
+        javax.jcr.Node rootNode = getSharedProgramRootNode(mainProgram, logger);
+
+        javax.jcr.Node node = null;
         if (rootNode.hasNode(subProgramCode))
         {
             node = rootNode.getNode(subProgramCode);
