001/*
002 *  Copyright 2014 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.odf.cdmfr;
017
018import java.text.SimpleDateFormat;
019import java.time.LocalDate;
020import java.util.Date;
021import java.util.HashMap;
022import java.util.HashSet;
023import java.util.List;
024import java.util.Map;
025import java.util.Map.Entry;
026import java.util.Optional;
027import java.util.Set;
028
029import org.apache.avalon.framework.component.Component;
030import org.apache.avalon.framework.context.Context;
031import org.apache.avalon.framework.context.ContextException;
032import org.apache.avalon.framework.context.Contextualizable;
033import org.apache.avalon.framework.service.ServiceException;
034import org.apache.avalon.framework.service.ServiceManager;
035import org.apache.avalon.framework.service.Serviceable;
036import org.apache.cocoon.components.ContextHelper;
037import org.apache.cocoon.environment.Request;
038import org.apache.cocoon.xml.AttributesImpl;
039import org.apache.cocoon.xml.XMLUtils;
040import org.apache.commons.collections4.MapUtils;
041import org.apache.commons.lang3.ArrayUtils;
042import org.apache.commons.lang3.StringUtils;
043import org.apache.excalibur.source.SourceResolver;
044import org.xml.sax.ContentHandler;
045import org.xml.sax.SAXException;
046
047import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
048import org.ametys.cms.contenttype.ContentTypesHelper;
049import org.ametys.cms.data.ContentDataHelper;
050import org.ametys.cms.data.ContentValue;
051import org.ametys.cms.data.RichText;
052import org.ametys.cms.data.type.ModelItemTypeConstants;
053import org.ametys.cms.repository.Content;
054import org.ametys.core.util.DateUtils;
055import org.ametys.odf.NoLiveVersionException;
056import org.ametys.odf.ODFHelper;
057import org.ametys.odf.course.Course;
058import org.ametys.odf.course.LOMSheet;
059import org.ametys.odf.courselist.CourseList;
060import org.ametys.odf.courselist.CourseList.ChoiceType;
061import org.ametys.odf.coursepart.CoursePart;
062import org.ametys.odf.enumeration.OdfReferenceTableEntry;
063import org.ametys.odf.enumeration.OdfReferenceTableHelper;
064import org.ametys.odf.orgunit.OrgUnit;
065import org.ametys.odf.orgunit.RootOrgUnitProvider;
066import org.ametys.odf.person.ContactData;
067import org.ametys.odf.person.Person;
068import org.ametys.odf.program.AbstractProgram;
069import org.ametys.odf.program.Container;
070import org.ametys.odf.program.Program;
071import org.ametys.odf.program.ProgramFactory;
072import org.ametys.odf.program.ProgramPart;
073import org.ametys.odf.program.TraversableProgramPart;
074import org.ametys.odf.program.WebsiteLink;
075import org.ametys.plugins.repository.AmetysObjectResolver;
076import org.ametys.plugins.repository.UnknownAmetysObjectException;
077import org.ametys.plugins.repository.jcr.DefaultAmetysObject;
078import org.ametys.runtime.model.type.DataContext;
079import org.ametys.runtime.plugin.component.AbstractLogEnabled;
080
081/**
082 * Component able to generate CDM-fr for a {@link CDMEntity}.
083 */
084public class ExportCDMfrManager extends AbstractLogEnabled implements Component, Serviceable, Contextualizable
085{
086    /** The Avalon role */
087    public static final String ROLE = ExportCDMfrManager.class.getName();
088    
089    /** Request attribute to get if the export is for Ametys application */
090    public static final String REQUEST_ATTRIBUTE_EXPORT_FOR_AMETYS = ExportCDMfrManager.class.getName() + "$forAmetys";
091    
092    private static final String[] __FIXED_DOMAIN_NAMES = new String[]{"ALL", "DEG", "SHS", "STS", "STA"};
093    
094    /** The Ametys object resolver */
095    protected AmetysObjectResolver _resolver;
096    /** The source resolver */
097    protected SourceResolver _sourceResolver;
098    /** The ODF enumeration helper */
099    protected OdfReferenceTableHelper _refTableHelper;
100    /** The root orgunit provider */
101    protected RootOrgUnitProvider _rootOrgUnitProvider;
102    /** The avalon context */
103    protected Context _context;
104    /** The CDMfr extension point */
105    protected CDMfrExtensionPoint _cdmFrExtensionPoint;
106    /** The content type extension point */
107    protected ContentTypeExtensionPoint _cTypeEP;
108    /** Helper for content types */
109    protected ContentTypesHelper _cTypeHelper;
110    /** The ODF helper */
111    protected ODFHelper _odfHelper;
112    
113    @Override
114    public void service(ServiceManager smanager) throws ServiceException
115    {
116        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
117        _sourceResolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
118        _refTableHelper = (OdfReferenceTableHelper) smanager.lookup(OdfReferenceTableHelper.ROLE);
119        _rootOrgUnitProvider = (RootOrgUnitProvider) smanager.lookup(RootOrgUnitProvider.ROLE);
120        _cdmFrExtensionPoint = (CDMfrExtensionPoint) smanager.lookup(CDMfrExtensionPoint.ROLE);
121        _cTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE);
122        _cTypeHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE);
123        _odfHelper = (ODFHelper) smanager.lookup(ODFHelper.ROLE);
124    }
125    
126    @Override
127    public void contextualize(Context context) throws ContextException
128    {
129        _context = context;
130    }
131    
132    /**
133     * Entry point to generate the CDM for a program
134     * @param contentHandler The content handler to sax into
135     * @param program The program
136     * @throws SAXException if failed to generate CDM
137     */
138    public void generateCDM(ContentHandler contentHandler, Program program) throws SAXException
139    {
140        contentHandler.startDocument();
141        contentHandler.startPrefixMapping("", "http://cdm-fr.fr/2013/CDM");
142        contentHandler.startPrefixMapping("xsi", "http://www.w3.org/2001/XMLSchema-instance");
143        contentHandler.startPrefixMapping("cdmfr", "http://cdm-fr.fr/2013/CDM-frSchema");
144        contentHandler.startPrefixMapping("xhtml", "http://www.w3.org/1999/xhtml");
145        contentHandler.startPrefixMapping("ametys-cdm", "http://www.ametys.org/cdm/1.0");
146            
147        AttributesImpl attrs = new AttributesImpl();
148        attrs.addCDATAAttribute("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "xsi:schemaLocation", "http://cdm-fr.fr/2013/CDM http://cdm-fr.fr/2013/schemas/CDMFR.xsd");
149        attrs.addCDATAAttribute("profile", "CDM-fr");
150        attrs.addCDATAAttribute("language", program.getLanguage());
151        XMLUtils.startElement(contentHandler, "CDM", attrs);
152        
153        saxHabilitation(contentHandler, program);
154        
155        XMLUtils.startElement(contentHandler, "properties");
156        XMLUtils.createElement(contentHandler, "datasource", "Ametys v4.2");
157        attrs = new AttributesImpl();
158        attrs.addCDATAAttribute("date", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
159        XMLUtils.createElement(contentHandler, "datetime", attrs);
160        XMLUtils.endElement(contentHandler, "properties");
161        
162        program2CDM(contentHandler, program);
163        
164        XMLUtils.endElement(contentHandler, "CDM");
165        
166        contentHandler.endDocument();
167    }
168    
169    /**
170     * Switch to Live version if it was required
171     * @param ao the Ametys object
172     * @return true if the ametys object is switched
173     */
174    protected boolean switchToLiveVersionIfNeeded(DefaultAmetysObject ao)
175    {
176        try
177        {
178            _odfHelper.switchToLiveVersionIfNeeded(ao);
179            return true;
180        }
181        catch (NoLiveVersionException e) 
182        {
183            getLogger().warn("Cannot export content to CDM : Live label is required but there is no Live version for content " + ao.getId());
184        }
185        
186        return false;
187    }
188    
189    /**
190     * Determines if this CDM export if for a Ametys application
191     * @return true if this CDM export if for a Ametys application
192     */
193    protected boolean isExportForAmetys()
194    {
195        Request request = ContextHelper.getRequest(_context);
196        return request.getAttribute(ExportCDMfrManager.REQUEST_ATTRIBUTE_EXPORT_FOR_AMETYS) != null;
197    }
198    
199    /**
200     * SAX CDMfr events for program's habilitation
201     * @param contentHandler the content handler to sax into
202     * @param program the program
203     * @throws SAXException if an error occurred
204     */
205    public void saxHabilitation(ContentHandler contentHandler, Program program) throws SAXException
206    {
207        /*
208         * <cdmfr:habilitation>
209         *      <cdmfr:habiliId>ID</cdmfr:habiliId>
210         *      <cdmfr:cohabilitation>
211         *          <cdmfr:exists>true|false</cdmfr:exists> // Habilité ou non
212         *          <cdmfr:listOfOrgUnit>
213         *              <cdmfr:habOrgUnit>
214         *                  <cdmfr:refOrgUnit> // Etablisssement cohabilité
215         *                  <cdmfr:domainName> 
216         *                      <cdmfr:fixedDomain> // Domaine concerné par cette habilitation
217         *                  </cdmfr:domainName>
218         *              </cdmfr:habOrgUnit>
219         *          </cdmfr:listOfOrgUnit>
220         *      </cdmfr:cohabilitation>
221         *      <cdmfr:field>
222         *          <fieldname>
223         *              <free>MENTION</free>
224         *          </fieldname>
225         *          <speciality>
226         *              <specialityId/>
227         *              <specialityName/>
228         *          </speciality>
229         *      </cdmfr:field>
230         *      <cdmfr:partnership>
231         *          <cdmfr:training>
232         *              <cdmfr:trainingStrategy /> // Stages
233         *          </cdmfr:training>
234         *      </cdmfr:partnership>
235         * </cdmfr:habilitation>
236         * 
237         */
238        
239        if (program.isPublishable() && switchToLiveVersionIfNeeded(program))
240        {
241            Request request = ContextHelper.getRequest(_context);
242            Content currentContent = (Content) request.getAttribute(Content.class.getName());
243
244            try
245            {
246                request.setAttribute(Content.class.getName(), program);
247                
248                AttributesImpl attrs = new AttributesImpl();
249                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, "");
250                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_DIPLOMA_TYPE, getDiplomaType(program));
251                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_HABILITATION, attrs);
252                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_HABILITATION_ID, program.getCDMId());
253                
254                _saxCohabilitation(contentHandler, program);
255                _saxHabilitationJointOrgUnit(contentHandler, program);
256                _saxHabilitationDiploma(contentHandler, program);
257                _saxHabilitationPartnerShip(contentHandler, program);
258                
259                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_HABILITATION);
260            }
261            finally
262            {
263                request.setAttribute(Content.class.getName(), currentContent);
264            }
265        }
266    }
267    
268    /**
269     * Sax habilitation informations of the diploma.
270     * @param contentHandler The content handler
271     * @param program The program to sax
272     * @throws SAXException if an error occurs
273     */
274    protected void _saxHabilitationDiploma(ContentHandler contentHandler, Program program) throws SAXException
275    {
276        String speciality = program.getSpeciality();
277        String mention = program.getMention();
278        
279        if (StringUtils.isNotEmpty(mention) || StringUtils.isNotEmpty(speciality))
280        {
281            // Formation LMD : Mention - Speciality
282            /*
283             * <cdmfr:field>
284             *      <cdmfr:fieldName>
285             *          <cdmfr:controlled fieldNameCode="code_mention">
286             *              <cdmfr:registeredName>Libelle mention</cdmfr:registeredName>
287             *          </cdmfr:controlled>
288             *      </cdmfr:fieldName>
289             *      <cdmfr:speciality>
290             *          <cdmfr:specialityName/>
291             *          <cdmfr:specialityId/>
292             *          <cdmfr:refProgram/>
293             *      </cdmfr:speciality>
294             * </cdmfr:field>
295             */
296            
297            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "field");
298            _saxHabilitationMention(contentHandler, program, mention);
299            _saxHabilitationSpeciality(contentHandler, program, speciality);
300            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "field");
301        }
302        else if (OdfReferenceTableHelper.MENTION_LICENCEPRO.equals(_getMentionType(program)))
303        {
304            _saxHabilitationLicPro(contentHandler, program);
305        }
306        else
307        {
308            _saxHabilitationOtherDiploma(contentHandler, program);
309        }
310    }
311    
312    /**
313     * Sax habilitation informations of professional license.
314     * @param contentHandler The content handler
315     * @param program The program to sax
316     * @throws SAXException if an error occurs
317     */
318    protected void _saxHabilitationLicPro(ContentHandler contentHandler, Program program) throws SAXException
319    {
320        // Licence professionelle
321        /*
322         * <cdmfr:licPro>
323         *      <cdmfr:specialite>
324         *          <cdm:text/>
325         *      </cdmfr:specialite>
326         * </cdmfr:licPro>
327         */
328        
329        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "licPro");
330        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "specialite");
331        XMLUtils.createElement(contentHandler, "text", program.getSpeciality());
332        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "specialite");
333        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "licPro");
334    }
335
336    /**
337     * Sax habilitation informations for another diploma : no mention, not a license.
338     * @param contentHandler The content handler
339     * @param program The program to sax
340     * @throws SAXException if an error occurs
341     */
342    protected void _saxHabilitationOtherDiploma(ContentHandler contentHandler, Program program) throws SAXException
343    {
344        // Autre diplôme habilité ne s'inscrivant pas dans une structure mention-spécialité
345        /*
346         * <cdmfr:otherDiploma>
347         *      <cdmfr:refProgram/>
348         * </cdmfr:otherDiploma>
349         */
350        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "otherDiploma");
351        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "refProgram", program.getCDMId());
352        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "otherDiploma");
353    }
354
355    /**
356     * Sax habilitation informations of mention.
357     * @param contentHandler The content handler
358     * @param program The program to sax
359     * @param mention The mention to sax
360     * @throws SAXException if an error occurs
361     */
362    protected void _saxHabilitationMention(ContentHandler contentHandler, Program program, String mention) throws SAXException
363    {
364        if (StringUtils.isNotEmpty(mention))
365        {
366            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "fieldName");
367            
368            if (ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID.equals(program.getType("mention").getId()))
369            {
370                OdfReferenceTableEntry entry = _refTableHelper.getItem(mention);
371                
372                String mentionLabel = entry.getCdmValue();
373                if (StringUtils.isEmpty(mentionLabel))
374                {
375                    mentionLabel = entry.getLabel(program.getLanguage());
376                }
377                
378                AttributesImpl attrs = new AttributesImpl();
379                attrs.addCDATAAttribute("fieldNameCode", entry.getCode());
380                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "controlled", attrs);
381                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "registeredName", mentionLabel);
382                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "controlled");
383            }
384            else
385            {
386                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "free", mention);
387            }
388            
389            
390            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "fieldName");
391        }
392    }
393
394    /**
395     * Sax habilitation informations of speciality.
396     * @param contentHandler The content handler
397     * @param program The program to sax
398     * @param speciality The speciality to sax
399     * @throws SAXException if an error occurs
400     */
401    protected void _saxHabilitationSpeciality(ContentHandler contentHandler, Program program, String speciality) throws SAXException
402    {
403        if (StringUtils.isNotEmpty(speciality))
404        {
405            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "speciality");
406            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "specialityName", speciality);
407            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "specialityId", program.getCDMId() + "SP01"); // FIXME "SP + n° ordre de la spécialité"
408            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "refProgram", program.getCDMId());
409            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "speciality");
410        }
411    }
412
413    /**
414     * Sax cohabilitation informations.
415     * @param contentHandler The content handler
416     * @param program The program to sax
417     * @throws SAXException if an error occurs
418     */
419    protected void _saxCohabilitation(ContentHandler contentHandler, Program program) throws SAXException
420    {
421        String[] domains = program.getDomain();
422        boolean isHabilited = false;
423        for (String domain : domains)
424        {
425            isHabilited |= !"HD".equals(_refTableHelper.getItemCode(domain)); 
426        }
427        
428        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "cohabilitation");
429        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "exists", String.valueOf(isHabilited));
430        
431        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "listOfOrgUnit");
432        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "habOrgUnit");
433        
434        String rneCode = _getRootOrgUnitId(program);
435        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "refOrgUnit", rneCode);
436        
437        _saxHabilitationDomains(contentHandler, domains);
438        
439        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "habOrgUnit");
440        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "listOfOrgUnit");
441        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "cohabilitation");
442    }
443
444    /**
445     * Sax habilitation informations of domains.
446     * @param contentHandler The content handler
447     * @param domains The domains to SAX
448     * @throws SAXException if an error occurs
449     */
450    protected void _saxHabilitationDomains(ContentHandler contentHandler, String[] domains) throws SAXException
451    {
452        for (String domain : domains)
453        {
454            Content content = _resolver.resolveById(domain);
455            OdfReferenceTableEntry entry = new OdfReferenceTableEntry(content);
456            String domainName = entry.getCdmValue();
457            String domainCode = entry.getCode();
458            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "domainName");
459            if (ArrayUtils.contains(__FIXED_DOMAIN_NAMES, domainCode))
460            {
461                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "fixedDomain", domainName);
462            }
463            else
464            {
465                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "openDomain", domainName);
466            }
467            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "domainName");
468        }
469    }
470
471    /**
472     * Sax habilitation informations of joint orgunits.
473     * @param contentHandler The content handler
474     * @param program The program to sax
475     * @throws SAXException if an error occurs
476     */
477    protected void _saxHabilitationJointOrgUnit(ContentHandler contentHandler, Program program) throws SAXException
478    {
479        // Etablissement cohabilité
480        String[] jointOrgUnit = program.getJointOrgUnit();
481        if (jointOrgUnit.length > 0)
482        {
483            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "cohabilitation");
484            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "exists", String.valueOf(true));
485            
486            AttributesImpl attrs = new AttributesImpl();
487            attrs.addCDATAAttribute("cohabilitationRole", "cohabilité");
488            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "listOfOrgUnit", attrs);
489            for (String id : jointOrgUnit)
490            {
491                if (StringUtils.isNotEmpty(id))
492                {
493                    String uaiCode = _refTableHelper.getItemCode(id);
494                    XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "habOrgUnit");
495                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "refOrgUnit", uaiCode);
496                    String ouLabel = _refTableHelper.getItemLabel(id, program.getLanguage());
497                    if (ouLabel != null)
498                    {
499                        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, ouLabel);
500                    }
501                    XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "habOrgUnit");
502                }
503            }
504            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "listOfOrgUnit");
505            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "cohabilitation");
506        }
507    }
508
509    /**
510     * Sax habilitation informations of partner ship.
511     * @param contentHandler The content handler
512     * @param program The program to sax
513     * @throws SAXException if an error occurs
514     */
515    protected void _saxHabilitationPartnerShip(ContentHandler contentHandler, Program program) throws SAXException
516    {
517        /*
518         * <cdmfr:partnership>
519         *     <cdmfr:training>
520         *          <cdmfr:trainingStrategy />
521         *     </cdmfr:training>
522         * </cdmfr:partnership>
523         */      
524        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "partnership");
525        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "training");
526        DataContext richTextContext = DataContext.newInstance()
527                                                 .withObjectId(program.getId())
528                                                 .withDataPath(AbstractProgram.TRAINING_STRATEGY);
529        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "trainingStrategy", program.getTrainingStrategy(), richTextContext, _sourceResolver, false, isExportForAmetys());
530        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "training");
531        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "partnership");
532    }
533    
534    /**
535     * Get the root organization unit uai code
536     * @param program the exported program
537     * @return the uai code of the root organization unit corresponding to the exported program
538     */
539    protected String _getRootOrgUnitId(Program program)
540    {
541        return _rootOrgUnitProvider.getRoot().getUAICode();
542    }
543    
544    /**
545     * Get the type of diploma
546     * @param program the program
547     * @return diploma type
548     */
549    protected String getDiplomaType (Program program)
550    {
551        String degreeMentionType = _getMentionType(program);
552        if (OdfReferenceTableHelper.MENTION_LICENCE.equals(degreeMentionType))
553        {
554            return "L";
555        }
556        else if (OdfReferenceTableHelper.MENTION_MASTER.equals(degreeMentionType))
557        {
558            return "M";
559        }
560        else if (OdfReferenceTableHelper.MENTION_LICENCEPRO.equals(degreeMentionType))
561        {
562            return "Lpro";
563        }
564        return "autre";
565    }
566
567    //***********************************************************************//
568    //                              PROGRAM                                  //
569    //***********************************************************************//
570    
571    /**
572     * Export this entity in CDM-fr format.
573     * @param contentHandler the target handler
574     * @param program the program
575     * @throws SAXException if an error occurs during export.
576     */
577    public void program2CDM(ContentHandler contentHandler, Program program) throws SAXException
578    {
579        boolean rootOrgUnitSaxed = false;
580        
581        Set<String> persons = new HashSet<>();
582        Set<String> orgUnits = new HashSet<>();
583        Set<String> coursesToSax = new HashSet<>();
584        
585        abstractProgram2CDM(contentHandler, program, persons, orgUnits, coursesToSax);
586        
587        Set<String> coursesAlreadySax = new HashSet<>();
588        
589        while (!coursesToSax.isEmpty())
590        {
591            Set<String> courses = new HashSet<>();
592            for (String courseId : coursesToSax)
593            {
594                coursesAlreadySax.add(courseId);
595                Course course = _resolver.resolveById(courseId);
596                courses.addAll(course2CDM(contentHandler, course, orgUnits, persons));
597            }
598            
599            coursesToSax.clear();
600            coursesToSax.addAll(courses);
601            coursesToSax.removeAll(coursesAlreadySax);
602        }
603
604        for (String orgUnitId : orgUnits)
605        {
606            OrgUnit orgUnit = _resolver.resolveById(orgUnitId);
607            if (orgUnitId.equals(_rootOrgUnitProvider.getRootId()))
608            {
609                rootOrgUnitSaxed = true;
610            }
611            
612            _orgunit2CDM(contentHandler, orgUnit, persons);
613        }
614        
615        if (!rootOrgUnitSaxed)
616        {
617            _orgunit2CDM(contentHandler, _rootOrgUnitProvider.getRoot(), persons);
618        }
619        
620        for (String personId : persons)
621        {
622            Person person = _resolver.resolveById(personId);
623            person2CDM(contentHandler, person);
624        }
625    }
626    
627    /**
628     * Send the CDM-fr representation of their ProgramPart to the given Contenthandler.<br>
629     * Also collects referenced orgUnits, persons and courses, so that they could be sent afterwards.
630     * @param contentHandler the receiving contentHandler.
631     * @param program the program
632     * @param persons collected {@link Person} ids.
633     * @param orgUnits collected {@link OrgUnit} ids.
634     * @param courses collected {@link Course} ids.
635     * @throws SAXException if an error occurs during CDM processing.
636     */
637    public void abstractProgram2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program, Set<String> persons, Set<String> orgUnits, Set<String> courses) throws SAXException
638    {
639        DataContext programRichTextsContext = DataContext.newInstance()
640                                                         .withObjectId(program.getId());
641        if (program.isPublishable() && switchToLiveVersionIfNeeded(program))
642        {
643            Request request = ContextHelper.getRequest(_context);
644            Content currentContent = (Content) request.getAttribute(Content.class.getName());
645            
646            try
647            {
648                request.setAttribute(Content.class.getName(), program);
649                
650                String tagName = program.getCDMTagName();
651            
652                AttributesImpl attrs = new AttributesImpl();
653                
654                // <program id="FRUAI{rootOrgUnitId}PR{code}" language="...">
655                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, program.getCDMId());
656                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LANGUAGE, program.getLanguage());
657                XMLUtils.startElement(contentHandler, tagName, attrs);
658                
659                // <programID>code</programID>
660                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PROGRAM_ID, program.getCDMId());
661        
662                // <programName><text>name</text></programName>
663                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_NAME);
664                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_TEXT, program.getTitle());
665                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_NAME);
666                
667                _abstractProgram2CDMCode(contentHandler, program);
668                
669                // <webLink>
670                //      <href>href</href>
671                //      <linkName>linkName</linkName>
672                // </webLink>
673                for (WebsiteLink website : program.getWebsiteLinks())
674                {
675                    website.toCDM(contentHandler);
676                }
677                
678                // <programDescription>...</programDescription>
679                attrs = new AttributesImpl();
680                String programType = program.getEducationKind();
681                if (StringUtils.isNotEmpty(programType))
682                {
683                    programType = _refTableHelper.getItemCDMfrValue(programType, false);
684                    _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_NATURE, programType);
685                }
686                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_DESCRIPTION, attrs);
687                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getPresentation(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.PRESENTATION), _sourceResolver, false, isExportForAmetys());
688                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_DESCRIPTION);
689                
690                // <qualification>...</qualification>
691                _qualification2CDM(contentHandler, program, programRichTextsContext);
692                
693                // <levelCode @codeSet="bac|bac+1|bac+2|bac+3|..."/>
694                // <level @level="L|M|Doc"/>
695                _level2CDM(contentHandler, program);
696                
697                // <learningObjectives>...</learningObjectives>
698                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_LEARNING_OBJECTIVES, program.getObjectives(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.OBJECTIVES), _sourceResolver, false, isExportForAmetys());
699                
700                // <admissionInfo>
701                //      <cdmfr:admissionDescription>...</cdmfr:admissionDescription>
702                // </admissionInfo>
703                _admissionInfo2CDM(contentHandler, program, programRichTextsContext);
704                
705                // <recommendedPrerequisites>...</recommendedPrerequisites>
706                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_RECOMMANDED_PREREQUISITES, program.getRecommendedPrerequisite(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.RECOMMENDED_PREREQUISITE), _sourceResolver, false, isExportForAmetys());
707        
708                // <formalPrerequisites>...</formalPrerequisites>
709                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_FORMAL_PREREQUISITES, program.getNeededPrerequisite(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.NEEDED_PREREQUISITE), _sourceResolver, true, isExportForAmetys());
710                
711                // <teachingPlace>...</teachingPlace>
712                _place2CDM(contentHandler, program);
713                
714                // <targetGroup>...</targetGroup>
715                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_TARGET_GROUP, program.getTargetGroup(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.TARGET_GROUP), _sourceResolver, false, isExportForAmetys());
716        
717                // <formOfTeaching org="" method=""/>
718                String formOfTeachingMethod = _refTableHelper.getItemCDMfrValue(program.getDistanceLearning(), false);
719                for (String formOfTeachingOrg: program.getFormOfTeachingOrgs())
720                {
721                    attrs = new AttributesImpl();
722                    String formOfTeachingOrgValue = _refTableHelper.getItemCDMfrValue(formOfTeachingOrg, false);
723                    _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_METHOD, formOfTeachingMethod);
724                    _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_ORG, formOfTeachingOrgValue);
725                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_FORM_OF_TEACHING, attrs);
726                }
727    
728                // <instructionLanguage teachingLang="fr"/>
729                String[] langs = program.getEducationLanguage();
730                for (String lang : langs)
731                {
732                    String teachingLanguageValue = _refTableHelper.getItemCDMfrValue(lang, true);
733                    attrs = new AttributesImpl();
734                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_TEACHING_LANG, teachingLanguageValue);
735                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INSTRUCTION_LANGUAGE, attrs);   
736                }
737                
738                // <studyAbroad>...</studoAbroad>
739                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_STUDY_ABROAD, program.getStudyAbroad(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.STUDY_ABROAD), _sourceResolver, false, isExportForAmetys());
740                
741                // <programDuration>...</programDuration>
742                String duration = program.getDuration();
743                if (StringUtils.isNotBlank(duration))
744                {
745                    String durationValue = _refTableHelper.getItemCDMfrValue(duration, true);
746                    if (durationValue != null)
747                    {
748                        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PROGRAM_DURATION, durationValue);
749                    }
750                }
751                
752                // <cdmfr:programStructure>...</cdmfr:programStructure>
753                RichText teachingOrganization = program.getTeachingOrganization();
754                if (teachingOrganization != null && teachingOrganization.getLength() > 0)
755                {
756                    XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_STRUCTURE);
757                    CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getTeachingOrganization(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.TEACHING_ORGANIZATION), _sourceResolver, false, isExportForAmetys());
758                    XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_STRUCTURE);
759                }
760                
761                // Sax courses list
762                ContentValue[] children = program.getValue(TraversableProgramPart.CHILD_PROGRAM_PARTS, false, new ContentValue[0]);
763                for (ContentValue child : children)
764                {
765                    Content content = child.getContent();
766                    if (content instanceof CourseList courseList)
767                    {
768                        courselist2CDM(contentHandler, courseList, courses);
769                    }
770                }
771                
772                // <regulations/>
773                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REGULATIONS);
774        
775                // <expenses>...</expenses>
776                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_EXPENSES, program.getExpenses(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.EXPENSES), _sourceResolver, false, isExportForAmetys());
777                
778                // <universalAjustment>...</universalAjustment>
779                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_UNIVERSAL_ADJUSTMENT, program.getUniversalAdjustment(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.UNIVERSAL_ADJUSTMENT), _sourceResolver, false, isExportForAmetys());
780        
781                // <contacts>
782                //      <refPerson idRef="ID"/>
783                //      <refOrgUnit idRef="ID"/>
784                // </contacts>
785                _abstractProgram2CDMContacts(contentHandler, program, persons, orgUnits);
786                
787                // <infoBlock>...</infoBlock>
788                attrs = new AttributesImpl();
789                if (tagName.equals(CDMFRTagsConstants.TAG_SUB_PROGRAM))
790                {
791                    attrs.addCDATAAttribute("userDefined", "subProgram");
792                }
793                
794                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getAdditionalInformations(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.ADDITIONNAL_INFORMATIONS), _sourceResolver, false, isExportForAmetys(), attrs);
795        
796                // <infoBlock userDefined="ametys-extension>...</infoBlock>
797                attrs = new AttributesImpl();
798                attrs.addCDATAAttribute("userDefined", "ametys-extension");
799                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
800                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
801                for (String id : _cdmFrExtensionPoint.getExtensionsIds())
802                {
803                    CDMfrExtension cdmFrExtension = _cdmFrExtensionPoint.getExtension(id);
804                    
805                    if (program instanceof Program)
806                    {
807                        cdmFrExtension.program2CDM(contentHandler, (Program) program, persons, orgUnits);
808                    }
809                    
810                    cdmFrExtension.abstractProgram2CDM(contentHandler, program, persons, orgUnits);
811                }
812                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
813                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK);
814                
815                // <programStructure>...</programStructure>
816                _programPart2CDMSubPrograms(contentHandler, program, persons, orgUnits, courses);
817                
818                // <searchword>keyword</searchword>
819                for (String keyword : program.getKeywords())
820                {
821                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_SEARCH_WORD, keyword);
822                }
823                
824                // </program>
825                XMLUtils.endElement(contentHandler, tagName); 
826            }
827            finally
828            {
829                request.setAttribute(Content.class.getName(), currentContent);
830            }
831        }
832    }
833
834    /**
835     * SAX CDM-fr teaching places
836     * @param contentHandler the content handler to sax into
837     * @param program the program or subprogram
838     * @throws SAXException if an error occurred
839     */
840    protected void _place2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
841    {
842        // <teachingPlace>
843        //      <cdmfr:refOrgUnit />
844        //      <adr>
845        //          <pcode></pcode>
846        //      </adr>
847        // </teachingPlace>
848        String[] places = program.getPlace();
849        for (String id : places)
850        {
851            if (StringUtils.isNotEmpty(id))
852            {
853                String postalCode = _refTableHelper.getItemCode(id);
854                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_PLACE);
855                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REF_ORG_UNIT);
856                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
857                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PCODE, postalCode);
858                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
859                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_PLACE);
860            }
861        }
862    }
863
864    /**
865     * SAX CDM-fr qualification
866     * @param contentHandler the content handler to sax into
867     * @param program the program or subprogram
868     * @param programRichTextsContext the context for program's rich texts
869     * @throws SAXException if an error occurred
870     */
871    protected void _qualification2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program, DataContext programRichTextsContext) throws SAXException
872    {
873        // <qualification>
874        //      <qualificatioName><text></text></qualificationName>
875        //      <qualificationDescription>
876        //          <infoBlock>...</infoBlock>
877        //          <statistics>success rate</statistics>
878        //      </qualificationDescription>
879        //      <degree degree=""/>
880        //      <profession>...</profession>
881        //      <studyQualification>...</studyQualification>
882        // </qualification>
883        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION);
884        
885        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION_NAME);
886        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEXT);
887        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION_NAME);
888        
889        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_QUALIFICATION_DESCRIPTION);
890        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getQualification(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.QUALIFICATION), _sourceResolver, true, isExportForAmetys());
891        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_STATISTICS, program.getSuccessRate());
892        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_QUALIFICATION_DESCRIPTION);
893        
894        AttributesImpl attrs = new AttributesImpl();
895        String ects = _refTableHelper.getItemCDMfrValue(program.getEcts(), true);
896        _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_ECTS_CREDITS, ects);
897        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS, attrs);
898        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getKnowledgeCheck(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.KNOWLEDGE_CHECK), _sourceResolver, true, isExportForAmetys());
899        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS);
900
901        _degree2CDM(contentHandler, program);
902
903        // <profession>
904        //      <infoBlock>...</infoBlock>
905        //      <romeData romeCode="code" romeLibel=""/>
906        // </profession>
907        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROFESSION);
908        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getJobOpportunities(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.JOB_OPPORTUNITIES), _sourceResolver, true, isExportForAmetys());
909        _romeCodes2CDM(contentHandler, program);
910        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROFESSION);
911        
912        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_STUDY_QUALIFICATION);
913        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getFurtherStudy(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.FURTHER_STUDY), _sourceResolver, true, isExportForAmetys());
914        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_COMPETENCE_DESC);
915        attrs = new AttributesImpl();
916        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIMITED, "false");
917        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_COMPETENCE_VALIDITY, attrs);
918        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_STUDY_QUALIFICATION);
919        
920        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION);
921    }
922    
923    /**
924     * SAX CDMfr degree
925     * @param contentHandler the content handler to sax into
926     * @param program the program or subprogram
927     * @throws SAXException if an error occurred
928     */
929    protected void _degree2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
930    {
931        if (program instanceof Program)
932        {
933            AttributesImpl attrs = new AttributesImpl();
934            String degreeId = ((Program) program).getDegree();
935            if (StringUtils.isNotEmpty(degreeId))
936            {
937                String cdmCode = _refTableHelper.getItemCDMfrValue(degreeId, false);
938                if (StringUtils.isNotEmpty(cdmCode))
939                {
940                    _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_DEGREE, cdmCode);
941                }
942                else
943                {
944                    _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_DEGREE_NOT_LMD, _refTableHelper.getItemCode(degreeId));
945                }
946                
947                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_DEGREE, attrs);
948            }
949        }
950    }
951
952    /**
953     * SAX CDMfr rome codes
954     * @param contentHandler the content handler to sax into
955     * @param program the program or subprogram
956     * @throws SAXException if an error occurred
957     */
958    protected void _romeCodes2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
959    {
960        String[] romeCode = program.getRomeCode();
961        for (String id : romeCode)
962        {
963            AttributesImpl attrs = new AttributesImpl();
964            String code = _refTableHelper.getItemCode(id);
965            attrs.addCDATAAttribute("romeCode", code);
966            String label = _refTableHelper.getItemCDMfrValue(id, false);
967            if (StringUtils.isNotBlank(label))
968            {
969                attrs.addCDATAAttribute("romeLibel", label);
970            }
971            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_ROME_DATA, attrs);
972        }
973    }
974
975    /**
976     * SAX CDMfr level
977     * @param contentHandler the content handler to sax into
978     * @param program the program or subprogram
979     * @throws SAXException if an error occurred
980     */
981    protected void _level2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
982    {
983        AttributesImpl attrs = new AttributesImpl();
984        String educationLevel = program.getEducationLevel();
985        _addNotNullAttribute(attrs, "codeSet", _refTableHelper.getItemCDMfrValue(educationLevel, true));
986        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LEVEL_CODE, attrs);
987        
988        attrs = new AttributesImpl();
989        String educationLevelCode = _refTableHelper.getItemCode(educationLevel);
990        if (educationLevelCode.startsWith("licence"))
991        {
992            _addNotNullAttribute(attrs, "level", "L");
993            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LEVEL, attrs);
994        }
995        else if (educationLevelCode.startsWith("master"))
996        {
997            _addNotNullAttribute(attrs, "level", "M");
998            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LEVEL, attrs);
999        }
1000        else if (educationLevelCode.startsWith("doctorat"))
1001        {
1002            _addNotNullAttribute(attrs, "level", "Doc");
1003            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LEVEL, attrs);
1004        }
1005    }
1006
1007    /**
1008     * SAX CDMfr admission info
1009     * @param contentHandler the content handler to sax into
1010     * @param program the program or subprogram
1011     * @param programRichTextsContext the context for program's rich texts
1012     * @throws SAXException if an error occurred
1013     */
1014    protected void _admissionInfo2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program, DataContext programRichTextsContext) throws SAXException
1015    {
1016        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
1017        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_ADMISSION_DESCRIPTION, program.getAccessCondition(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.ACCESS_CONDITION), _sourceResolver, false, isExportForAmetys());
1018        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_STUDENT_STATUS, "");
1019        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_ECTS_REQUIRED, "");
1020        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_STUDENT_PLACES, program.getEffectives(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.NUMBER_OF_STUDENTS), _sourceResolver, false, isExportForAmetys());
1021        
1022        LocalDate teachingStart = program.getTeachingStart();
1023        if (teachingStart != null)
1024        {
1025            CDMHelper.date2CDM(contentHandler, CDMFRTagsConstants.TAG_TEACHING_START, teachingStart);
1026        }
1027        
1028        LocalDate registrationDeadline = program.getRegistrationDeadline();
1029        if (registrationDeadline != null)
1030        {
1031            CDMHelper.date2CDM(contentHandler, CDMFRTagsConstants.TAG_REGISTRATION_DEADLINE, registrationDeadline);
1032        }
1033        
1034        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REGISTRATION_DETAIL);
1035        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REGISTRATION_PROCESS);
1036        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REGISTRATION_MODALITIES, program.getInscription(), programRichTextsContext.cloneContext().withDataPath(AbstractProgram.INSCRIPTION), _sourceResolver, false, isExportForAmetys());
1037        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REGISTRATION_PROCESS);
1038        LocalDate registrationStart = program.getRegistrationStart();
1039        if (registrationStart != null)
1040        {
1041            CDMHelper.date2CDM(contentHandler, CDMFRTagsConstants.TAG_REGISTRATION_START, registrationStart);
1042        }
1043        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REGISTRATION_DETAIL);
1044        
1045        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
1046    }
1047    
1048    /**
1049     * SAX CDMfr events for a {@link AbstractProgram} codes
1050     * @param contentHandler the content handler to sax into
1051     * @param program the program or subprogram
1052     * @throws SAXException if an error occurred
1053     */
1054    protected void _abstractProgram2CDMCode(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
1055    {
1056        AttributesImpl attrs = new AttributesImpl();
1057        
1058        // <programCode codeSet="userDefined">code</programCode>
1059        String code = program.getCode();
1060        if (StringUtils.isNotBlank(code))
1061        {
1062            attrs = new AttributesImpl();
1063            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "userDefined");
1064            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, code);
1065        }
1066        
1067        // <programCode codeSet="codeCNIS-NSF">code</programCode>
1068        // <programCode libSet="codeCNIS-NSF">label</programCode>
1069        String nsfId = ContentDataHelper.getContentIdFromContentData(program, "nsfCode");
1070        if (StringUtils.isNotBlank(nsfId))
1071        {
1072            attrs = new AttributesImpl();
1073            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "codeCNIS-NSF");
1074            
1075            String nsfCode = _refTableHelper.getItemCode(nsfId);
1076            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, nsfCode);
1077            
1078            String codeLib = _refTableHelper.getItemCDMfrValue(nsfId, false);
1079            if (StringUtils.isNotEmpty(codeLib))
1080            {
1081                attrs = new AttributesImpl();
1082                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "codeCNIS-NSF");
1083                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1084            }
1085        }
1086        
1087        // <programCode codeSet="sectDGESIP">code</programCode>
1088        // <programCode libSet="sectDGESIP">label</programCode>
1089        for (String id : program.getDGESIPCode())
1090        {
1091            attrs = new AttributesImpl();
1092            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "sectDGESIP");
1093            
1094            String dgesipCode = _refTableHelper.getItemCode(id);
1095            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, dgesipCode);
1096            
1097            String codeLib = _refTableHelper.getItemCDMfrValue(id, false);
1098            if (StringUtils.isNotEmpty(codeLib))
1099            {
1100                attrs = new AttributesImpl();
1101                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "sectDGESIP");
1102                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1103            }
1104        }
1105        
1106        // <programCode codeSet="ERASMUS">code</programCode>
1107        // <programCode libSet="ERASMUS">label</programCode>
1108        for (String id : program.getErasmusCode())
1109        {
1110            attrs = new AttributesImpl();
1111            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "ERASMUS");
1112            
1113            String erasmusCode = _refTableHelper.getItemCode(id);
1114            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, erasmusCode);
1115            
1116            String codeLib = _refTableHelper.getItemCDMfrValue(id, false);
1117            if (StringUtils.isNotEmpty(codeLib))
1118            {
1119                attrs = new AttributesImpl();
1120                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "ERASMUS");
1121                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1122            }
1123        }
1124        
1125        // <programCode codeSet="cite97">code</programCode>
1126        // <programCode libSet="cite97">label</programCode>
1127        for (String id : program.getCite97Code())
1128        {
1129            attrs = new AttributesImpl();
1130            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "cite97");
1131            String cite97Code = _refTableHelper.getItemCode(id);
1132            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, cite97Code);
1133            
1134            String codeLib = _refTableHelper.getItemCDMfrValue(id, false);
1135            if (StringUtils.isNotEmpty(codeLib))
1136            {
1137                attrs = new AttributesImpl();
1138                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "cite97");
1139                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1140            }
1141        }
1142        
1143        // <programCode codeSet="FAP">code</programCode>
1144        // <programCode libSet="FAP">label</programCode>
1145        for (String id : program.getFapCode())
1146        {
1147            attrs = new AttributesImpl();
1148            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "FAP");
1149            String fapCode = _refTableHelper.getItemCode(id);
1150            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, fapCode);
1151            
1152            String codeLib = _refTableHelper.getItemCDMfrValue(id, false);
1153            if (StringUtils.isNotEmpty(codeLib))
1154            {
1155                attrs = new AttributesImpl();
1156                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "FAP");
1157                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1158            }
1159        }
1160        
1161        // <programCode codeSet="codeSectDiscipSISE">code</programCode>
1162        // <programCode libSet="codeSectDiscipSISE">label</programCode>
1163        for (String id : program.getSiseCode())
1164        {
1165            attrs = new AttributesImpl();
1166            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "sectDiscipSISE");
1167            
1168            String siseCode = _refTableHelper.getItemCode(id);
1169            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, siseCode);
1170            
1171            String codeLib = _refTableHelper.getItemCDMfrValue(id, false);
1172            if (StringUtils.isNotEmpty(codeLib))
1173            {
1174                attrs = new AttributesImpl();
1175                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "sectDiscipSISE");
1176                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1177            }
1178        }
1179    }
1180    
1181    /**
1182     * SAX CDMfr events for a {@link AbstractProgram} contacts
1183     * @param contentHandler the content handler to sax into
1184     * @param program the program or subprogram
1185     * @param persons the contacts
1186     * @param orgUnits the orgunits
1187     * @throws SAXException if an error occurred
1188     */
1189    protected void _abstractProgram2CDMContacts(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program, Set<String> persons, Set<String> orgUnits) throws SAXException
1190    {
1191        AttributesImpl attrs = new AttributesImpl();
1192        
1193        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
1194        
1195        Map<String, List<String>> contactsByRole = program.getContactsByRole();
1196        
1197        for (Entry<String, List<String>> entry : contactsByRole.entrySet())
1198        {
1199            String role = entry.getKey();
1200            List<String> contactIds = entry.getValue();
1201            
1202            String roleCode = null;
1203            if (StringUtils.isNotEmpty(role))
1204            {
1205                roleCode = _refTableHelper.getItemCode(role);
1206            }
1207            
1208            for (String personID : contactIds)
1209            {
1210                try
1211                {
1212                    Person person = _resolver.resolveById(personID);
1213                    persons.add(person.getId());
1214                    attrs = new AttributesImpl();
1215                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, person.getCDMId());
1216                    if (roleCode != null)
1217                    {
1218                        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ROLE, roleCode);
1219                    }
1220                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_PERSON, attrs);
1221                }
1222                catch (UnknownAmetysObjectException e)
1223                {
1224                    // Contact does exist anymore, ignore it
1225                }
1226            }
1227        }
1228
1229        for (String orgUnitId : program.getOrgUnits())
1230        {
1231            if (!"".equals(orgUnitId))
1232            {
1233                try
1234                {
1235                    OrgUnit orgUnit = _resolver.resolveById(orgUnitId);
1236                    orgUnits.add(orgUnit.getId());
1237                    attrs = new AttributesImpl();
1238                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, orgUnit.getCDMId());
1239                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_ORG_UNIT, attrs);
1240                }
1241                catch (UnknownAmetysObjectException e)
1242                {
1243                    // OrgUnit does exist anymore, ignore it
1244                }
1245            }
1246        }
1247        
1248        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
1249    }
1250    
1251    /**
1252     * SAX CDMfr events for a {@link ProgramPart} strcuture
1253     * @param contentHandler the content handler to sax into
1254     * @param programPart the program part
1255     * @param persons the contacts
1256     * @param orgUnits the orgunits
1257     * @param courses the courses
1258     * @throws SAXException if an error occurred
1259     */
1260    protected void _programPart2CDMSubPrograms(ContentHandler contentHandler, TraversableProgramPart programPart, Set<String> persons, Set<String> orgUnits, Set<String> courses) throws SAXException
1261    {
1262        ContentValue[] children = programPart.getValue(TraversableProgramPart.CHILD_PROGRAM_PARTS, false, new ContentValue[0]);
1263        for (ContentValue child : children)
1264        {
1265            Content content = child.getContent();
1266            if (content instanceof AbstractProgram)
1267            {
1268                abstractProgram2CDM(contentHandler, (AbstractProgram<?>) content, persons, orgUnits, courses);
1269            }
1270            else if (content instanceof Container)
1271            {
1272                container2CDM(contentHandler, (Container) content, persons, orgUnits, courses);
1273            }
1274        }
1275    }
1276    
1277    
1278    //***********************************************************************//
1279    //                             CONTAINER                                 //
1280    //***********************************************************************//
1281    
1282    /**
1283     * Send the CDM-fr representation of their ProgramPart to the given Contenthandler.<br>
1284     * Also collects referenced orgUnits, persons and courses, so that they could be sent afterwards.
1285     * @param contentHandler the receiving contentHandler.
1286     * @param container The container
1287     * @param persons collected {@link Person} ids.
1288     * @param orgUnits collected {@link OrgUnit} ids.
1289     * @param courses collected {@link Course} ids.
1290     * @throws SAXException if an error occurs during CDM processing.
1291     */
1292    public void container2CDM(ContentHandler contentHandler, Container container, Set<String> persons, Set<String> orgUnits, Set<String> courses) throws SAXException
1293    {
1294        if (container.isPublishable() && switchToLiveVersionIfNeeded(container))
1295        {
1296            Request request = ContextHelper.getRequest(_context);
1297            Content currentContent = (Content) request.getAttribute(Content.class.getName());
1298            
1299            try
1300            {
1301                request.setAttribute(Content.class.getName(), container);
1302                
1303                AttributesImpl attrs = new AttributesImpl();
1304                
1305                // <program id="FR_RNE_{rootOrgUnitId}_PR_{code}">
1306                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, container.getCDMId());
1307                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_SUB_PROGRAM, attrs);
1308                
1309                // <programID>code</programID>
1310                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PROGRAM_ID, container.getCDMId());
1311                
1312                // <programName><text>name</text></programName>
1313                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_NAME);
1314                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_TEXT, container.getTitle());
1315                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_NAME);
1316                
1317                // <programCode codeSet="userDefined">code</programCode>
1318                String code = container.getCode();
1319                if (StringUtils.isNotBlank(code))
1320                {
1321                    attrs = new AttributesImpl();
1322                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "userDefined");
1323                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, code);
1324                }
1325                
1326                // <programDescription>...</programDescription>
1327                attrs = new AttributesImpl();
1328                String nature = container.getNature();
1329                if (StringUtils.isNotEmpty(nature))
1330                {
1331                    nature = _refTableHelper.getItemCDMfrValue(nature, true);
1332                    _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_NATURE, nature);
1333                }
1334                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_DESCRIPTION, attrs);
1335                
1336                // <qualification>...</qualification>
1337                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION);
1338                _addPositiveAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_ECTS_CREDITS, container.getEcts());
1339                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS, attrs);
1340                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, null, null, null, true, isExportForAmetys());
1341                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS);
1342                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION);
1343                
1344                // <programStructure>...</programStructure>
1345                ContentValue[] children = container.getValue(TraversableProgramPart.CHILD_PROGRAM_PARTS, false, new ContentValue[0]);
1346                for (ContentValue child : children)
1347                {
1348                    Content content = child.getContent();
1349                    if (content instanceof CourseList courseList)
1350                    {
1351                        courselist2CDM(contentHandler, courseList, courses);
1352                    }
1353                }
1354                
1355                // <infoBlock>...</infoBlock>
1356                attrs = new AttributesImpl();
1357                attrs.addCDATAAttribute("userDefined", "container");
1358                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
1359                
1360                // <infoBlock userDefined="ametys-extension>...</infoBlock>
1361                attrs = new AttributesImpl();
1362                attrs.addCDATAAttribute("userDefined", "ametys-extension");
1363                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
1364                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
1365                for (String id : _cdmFrExtensionPoint.getExtensionsIds())
1366                {
1367                    CDMfrExtension cdmFrExtension = _cdmFrExtensionPoint.getExtension(id);
1368                    cdmFrExtension.container2CDM(contentHandler, container, persons, orgUnits);
1369                }
1370                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
1371                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK);
1372                
1373                // <subProgram>...</subProgram>
1374                _programPart2CDMSubPrograms(contentHandler, container, persons, orgUnits, courses);
1375                
1376                // </program>
1377                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_SUB_PROGRAM);
1378            }
1379            finally
1380            {
1381                request.setAttribute(Content.class.getName(), currentContent);
1382            }
1383        }
1384    }
1385    
1386    /**
1387     * SAX the course list structure
1388     * @param contentHandler The content handler to SAX into
1389     * @param cl The course list
1390     * @param courses References of saxed courses 
1391     * @throws SAXException if an error occurs
1392     */
1393    public void courselist2CDM (ContentHandler contentHandler, CourseList cl, Set<String> courses) throws SAXException
1394    {
1395        if (cl.isPublishable() && switchToLiveVersionIfNeeded(cl))
1396        {
1397            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_STRUCTURE);
1398            
1399            _courselist2CDM (contentHandler, cl);
1400            
1401            for (Course course  : cl.getCourses())
1402            {
1403                if (course.isPublishable())
1404                {
1405                    courses.add(course.getId());
1406                    AttributesImpl attrs = new AttributesImpl();
1407                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, course.getCDMId());
1408                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REF_COURSE, attrs);
1409                }
1410            }
1411            
1412            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_STRUCTURE);
1413        }
1414    }
1415    
1416    /**
1417     * Convert course list info to CDM (without child courses)
1418     * @param contentHandler The content handler to sax into
1419     * @param cl The course list
1420     * @throws SAXException if an error occurs
1421     */
1422    protected void _courselist2CDM (ContentHandler contentHandler, CourseList cl) throws SAXException
1423    {
1424        if (switchToLiveVersionIfNeeded(cl))
1425        {
1426            AttributesImpl attrs = new AttributesImpl();
1427            attrs.addCDATAAttribute("userDefined", "listCode");
1428            String code = cl.getCode();
1429            if (StringUtils.isEmpty(code))
1430            {
1431                code = org.ametys.core.util.StringUtils.generateKey().toUpperCase();
1432            }
1433            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs, code);
1434            
1435            attrs = new AttributesImpl();
1436            attrs.addCDATAAttribute("userDefined", "listName");
1437            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs, cl.getTitle());
1438            
1439            ChoiceType type = cl.getType();
1440            if (type != null)
1441            {
1442                attrs = new AttributesImpl();
1443                attrs.addCDATAAttribute("userDefined", "listChoiceType");
1444                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs, cl.getType().toString().toLowerCase());
1445            }
1446            
1447            if (type != null && type.equals(ChoiceType.CHOICE))
1448            {
1449                attrs = new AttributesImpl();
1450                attrs.addCDATAAttribute("userDefined", "listMinNumberOfCourses");
1451                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs, String.valueOf(cl.getMinNumberOfCourses()));
1452                
1453                attrs = new AttributesImpl();
1454                attrs.addCDATAAttribute("userDefined", "listMaxNumberOfCourses");
1455                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs, String.valueOf(cl.getMaxNumberOfCourses()));
1456            }
1457        }
1458    }
1459    
1460    //***********************************************************************//
1461    //                               COURSE                                  //
1462    //***********************************************************************//
1463    
1464    /**
1465     * Export this entity in CDM-fr format.
1466     * @param contentHandler the target handler
1467     * @param course The course
1468     * @throws SAXException if an error occurs during export.
1469     */
1470    public void course2CDM(ContentHandler contentHandler, Course course) throws SAXException
1471    {
1472        boolean rootOrgUnitSaxed = false;
1473        
1474        Set<String> orgUnits = new HashSet<>();
1475        Set<String> persons = new HashSet<>();
1476        
1477        Set<String> coursesAlreadySax = new HashSet<>();
1478        Set<String> coursesToSax = new HashSet<>();
1479        coursesToSax.add(course.getId());
1480        
1481        while (!coursesToSax.isEmpty())
1482        {
1483            Set<String> courses = new HashSet<>();
1484            for (String courseId : coursesToSax)
1485            {
1486                coursesAlreadySax.add(courseId);
1487                Course courseToSax = _resolver.resolveById(courseId);
1488                courses.addAll(course2CDM(contentHandler, courseToSax, orgUnits, persons));
1489            }
1490            coursesToSax.clear();
1491            coursesToSax.addAll(courses);
1492            coursesToSax.removeAll(coursesAlreadySax);
1493        }
1494        
1495        for (String orgUnitId : orgUnits)
1496        {
1497            OrgUnit orgUnit = _resolver.resolveById(orgUnitId);
1498            if (orgUnitId.equals(_rootOrgUnitProvider.getRootId()))
1499            {
1500                rootOrgUnitSaxed = true;
1501            }
1502            _orgunit2CDM(contentHandler, orgUnit, persons);
1503        }
1504        
1505        if (!rootOrgUnitSaxed)
1506        {
1507            OrgUnit orgUnit = _rootOrgUnitProvider.getRoot();
1508            _orgunit2CDM(contentHandler, orgUnit, persons);
1509        }
1510        
1511        for (String personId : persons)
1512        {
1513            Person person = _resolver.resolveById(personId);
1514            person2CDM(contentHandler, person);
1515        }
1516        
1517        // Set content in request
1518        ContextHelper.getRequest(_context).setAttribute(Content.class.getName(), course);
1519    }
1520    
1521    /**
1522     * Export this entity in CDM-fr format.
1523     * @param contentHandler the content handler to SAX into
1524     * @param course the course
1525     * @param orgUnits the org units
1526     * @param persons the persons
1527     * @return the saxed coursed
1528     * @throws SAXException if failed to generate CDM
1529     */
1530    public Set<String> course2CDM(ContentHandler contentHandler, Course course, Set<String> orgUnits, Set<String> persons) throws SAXException
1531    {
1532        DataContext courseRichTextsContext = DataContext.newInstance()
1533                                                        .withObjectId(course.getId());
1534
1535        if (!course.isPublishable() || !switchToLiveVersionIfNeeded(course))
1536        {
1537            return new HashSet<>();
1538        }
1539        
1540        Request request = ContextHelper.getRequest(_context);
1541        Content currentContent = (Content) request.getAttribute(Content.class.getName());
1542        
1543        try
1544        {
1545            request.setAttribute(Content.class.getName(), course);
1546            
1547            AttributesImpl attrs = new AttributesImpl();
1548            
1549            // <course id="FR_RNE_{rootOrgUnitId}_CO_{code}" language="...">
1550            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, course.getCDMId());
1551            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LANGUAGE, course.getLanguage());
1552            String erasmusCode = _refTableHelper.getItemCode(course.getErasmusCode());
1553            _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_IDENT, erasmusCode);
1554            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_COURSE, attrs);
1555            
1556            // <courseID>code</courseID>
1557            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_ID, course.getCDMId());
1558            
1559            // <courseName><text>name</text></courseName>
1560            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_NAME);
1561            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEXT, course.getTitle());
1562            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_NAME);
1563    
1564            // <courseCode codeSet="UE">code</couseCode>
1565            String courseType = _refTableHelper.getItemCode(course.getCourseType());
1566            attrs = new AttributesImpl();
1567            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, courseType);
1568            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_CODE, attrs, course.getCdmCode());
1569            
1570            // <webLink>
1571            //      <href>href</href>
1572            //      <linkName>linkName</linkName>
1573            // </webLink>
1574            String webLinkURL = course.getWebLinkUrl();
1575            if (StringUtils.isNotEmpty(webLinkURL))
1576            {
1577                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_WEB_LINK);
1578                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_HREF, webLinkURL);
1579                String webLinkLabel = course.getWebLinkLabel();
1580                if (StringUtils.isNotEmpty(webLinkLabel))
1581                {
1582                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_LINK_NAME, course.getWebLinkLabel());
1583                }
1584                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_WEB_LINK);
1585            }
1586    
1587            // <level>level</level>
1588            attrs = new AttributesImpl();
1589            String level = _refTableHelper.getItemCDMfrValue(course.getLevel(), true);
1590            _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_LEVEL, level);
1591            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LEVEL, attrs);
1592            
1593            // <courseDescription>...</courseDescription>
1594            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_DESCRIPTION);
1595            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_DIGITAL_USE);
1596            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PERCENTAGE, "0");
1597            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_LCMS);
1598            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_EXISTS, "false");
1599            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_RESOURCE_MGT);
1600            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PERC_OF_USE, "0");
1601            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_LCMS);
1602            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROD_PEDA);
1603            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_DIGIT_PROD_PERC, "0");
1604            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_DIGIT_PROD_UNT_PERC, "0");
1605            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROD_PEDA);
1606            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_DIGITAL_USE);
1607            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_INFO_BLOCK, course.getDescription(), courseRichTextsContext.cloneContext().withDataPath(Course.DESCRIPTION), _sourceResolver, true, isExportForAmetys());
1608            
1609            Set<String> courses = _courseLists2CDM(contentHandler, course);
1610            
1611            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_DESCRIPTION);
1612            
1613            // <teachingTerm term="" timeOfDay="" start=""/>
1614            attrs = new AttributesImpl();
1615            LocalDate startDate = course.getStartDate();
1616            if (startDate != null)
1617            {
1618                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_START, startDate.format(CDMHelper.CDM_DATE_FORMATTER));
1619            }
1620            
1621            String teachingTerm = _refTableHelper.getItemCDMfrValue(course.getTeachingTerm(), true);
1622            _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_TERM, teachingTerm);
1623            String timeSlot = _refTableHelper.getItemCDMfrValue(course.getTimeSlot(), true);
1624            _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_TIME_OF_DAY, timeSlot);
1625            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_TERM, attrs);
1626    
1627            // <credits ECTScredits="" totalWorkLoad="">
1628            //      <infoBlock>...</infoBlock>
1629            //      <globalVolume teachingType="CM/TP/TD">...</globalVolume>
1630            // </credits>
1631            _credits2CDM(contentHandler, course);
1632            
1633            // <learningObjectives>...</learningObjectives>
1634            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_LEARNING_OBJECTIVES);
1635            CDMHelper.richText2CDM(contentHandler, course.getObjectives(), courseRichTextsContext.cloneContext().withDataPath(Course.OBJECTIVES), _sourceResolver, isExportForAmetys());
1636            // <ametys-cdm:skills>...</ametys-cdm:skills>
1637            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "skills", course.getSkills(), courseRichTextsContext.cloneContext().withDataPath(Course.SKILLS), _sourceResolver, false, isExportForAmetys());
1638            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_LEARNING_OBJECTIVES);
1639            
1640            // <admissionInfo>
1641            //      <studentPlaces places=""/>
1642            //      <teachingStart date=""/>
1643            // </admissionInfo>
1644            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
1645    
1646            String maxNumberOfStudents = course.getMaxNumberOfStudents();
1647            if (StringUtils.isNotEmpty(maxNumberOfStudents))
1648            {
1649                attrs = new AttributesImpl();
1650                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_PLACES, maxNumberOfStudents);
1651                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_STUDENT_PLACES, attrs);
1652            }
1653            
1654            if (startDate != null)
1655            {
1656                attrs = new AttributesImpl();
1657                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_DATE, new SimpleDateFormat("yyyy-MM-dd").format(DateUtils.asDate(startDate)));
1658                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_START, attrs);
1659            }
1660            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
1661            
1662            // <formalPrerequisites>...</formalPrerequisites>
1663            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_FORMAL_PREREQUISITES, course.getNeededPrerequisite(), courseRichTextsContext.cloneContext().withDataPath(Course.NEEDED_PREREQUISITE), _sourceResolver, true, isExportForAmetys());
1664    
1665            // <teachingPlace>
1666            //      <cdmfr:refOrgUnit />
1667            //      <adr>
1668            //          <pcode></pcode>
1669            //      </adr>
1670            // </teachingPlace>
1671            _teachingLocation2CDM(contentHandler, course);
1672            
1673            // <formOfTeaching method="" org=""/>
1674            String formOfTeachingMethod = _refTableHelper.getItemCDMfrValue(course.getFormOfTeachingMethod(), false);
1675            for (String formOfTeachingOrg: course.getFormOfTeachingOrgs())
1676            {
1677                attrs = new AttributesImpl();
1678                String formOfTeachingOrgValue = _refTableHelper.getItemCDMfrValue(formOfTeachingOrg, false);
1679                _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_METHOD, formOfTeachingMethod);
1680                _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_ORG, formOfTeachingOrgValue);
1681                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_FORM_OF_TEACHING, attrs);
1682            }
1683            
1684            // <formOfAssessment>...</formOfAssessment>
1685            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_FORM_OF_ASSESSMENT, course.getFormOfAssessment(), courseRichTextsContext.cloneContext().withDataPath(Course.FORM_OF_ASSESSMENT), _sourceResolver, false, isExportForAmetys());
1686            
1687            // <instructionLanguage teachingLang=""/>
1688            String[] teachingLanguages = course.getTeachingLanguage();
1689            for (String teachingLanguage : teachingLanguages)
1690            {
1691                String teachingLanguageValue = _refTableHelper.getItemCDMfrValue(teachingLanguage, true);
1692                attrs = new AttributesImpl();
1693                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_TEACHING_LANG, teachingLanguageValue);
1694                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INSTRUCTION_LANGUAGE, attrs);
1695            }
1696            
1697            // <syllabus>
1698            // ...
1699            //   <webLink role="learningObjectMetadata">
1700            //      <href>...</href>
1701            //      <linkName>...</linkName>
1702            //   </webLink>
1703            // </syllabus>
1704            _syllabus2CDM(contentHandler, course, courseRichTextsContext);
1705            
1706            // <teachingActivity method=""/>
1707            attrs = new AttributesImpl();
1708            String teachingActivity = _refTableHelper.getItemCDMfrValue(course.getTeachingActivity(), true);
1709            _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_METHOD, teachingActivity);
1710            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_ACTIVITY, attrs);
1711            
1712            // <contacts>
1713            //      <refPerson idRef=ID/>
1714            // </contacts>
1715            _course2CDMContacts(contentHandler, course, persons, orgUnits);
1716            
1717            // <infoBlock>...</infoBlock>
1718            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, course.getAdditionalInformations(), courseRichTextsContext.cloneContext().withDataPath(Course.ADDITIONAL_INFORMATIONS), _sourceResolver, false, isExportForAmetys());
1719            
1720            // <infoBlock userDefined="ametys-extension>...</infoBlock>
1721            attrs = new AttributesImpl();
1722            attrs.addCDATAAttribute("userDefined", "ametys-extension");
1723            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
1724            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
1725            for (String id : _cdmFrExtensionPoint.getExtensionsIds())
1726            {
1727                CDMfrExtension cdmFrExtension = _cdmFrExtensionPoint.getExtension(id);
1728                cdmFrExtension.course2CDM(contentHandler, course, persons, orgUnits);
1729            }
1730            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
1731            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK);
1732            
1733            // <searchword>keyword</searchword>
1734            for (String keyword : course.getKeywords())
1735            {
1736                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_SEARCH_WORD, keyword);
1737            }
1738           
1739            // </course>
1740            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_COURSE);
1741
1742            return courses;
1743        }
1744        finally
1745        {
1746            request.setAttribute(Content.class.getName(), currentContent);
1747        }
1748    }
1749    
1750    /**
1751     * Sax the courses lists for a course.
1752     * @param contentHandler the content handler to SAX into
1753     * @param course The courselist container.
1754     * @return The list of courses into the course list.
1755     * @throws SAXException if failed to generate CDM
1756     */
1757    protected Set<String> _courseLists2CDM(ContentHandler contentHandler, Course course) throws SAXException
1758    {
1759        Set<String> courses = new HashSet<>();
1760        
1761        for (CourseList coursesList : course.getCourseLists())
1762        {
1763            if (coursesList.isPublishable())
1764            {
1765                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_COURSE_CONTENTS);
1766                
1767                // SAX infos on course list. This DO NOT respect the CDM-fr schema
1768                _courselist2CDM(contentHandler, coursesList);
1769                
1770                for (Course childCourse : coursesList.getCourses())
1771                {
1772                    if (childCourse.isPublishable())
1773                    {
1774                        courses.add(childCourse.getId());
1775                        AttributesImpl attrs = new AttributesImpl();
1776                        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, childCourse.getCDMId());
1777                        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REF_COURSE, attrs);
1778                    }
1779                }
1780                
1781                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_COURSE_CONTENTS);
1782            }
1783        }
1784        
1785        return courses;
1786    }
1787    
1788    /**
1789     * Sax the course credits.
1790     * @param contentHandler the content handler to SAX into
1791     * @param course The course to sax
1792     * @throws SAXException if failed to generate CDM
1793     */
1794    protected void _credits2CDM(ContentHandler contentHandler, Course course) throws SAXException
1795    {
1796        AttributesImpl attrs = new AttributesImpl();
1797        _addPositiveAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_TOTAL_WORK_LOAD, course.getNumberOfHours());
1798        _addPositiveAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_ECTS_CREDITS, course.getEcts());
1799        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS, attrs);
1800        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, null, null, _sourceResolver, true, isExportForAmetys());
1801        
1802        Map<String, Double> hoursByNature = new HashMap<>();
1803        List<CoursePart> courseParts = course.getCourseParts();
1804        for (CoursePart coursePart : courseParts)
1805        {
1806            String nature = coursePart.getNature();
1807            Double hours = MapUtils.getDouble(hoursByNature, nature, 0d) + coursePart.getNumberOfHours();
1808            if (hours > 0)
1809            {
1810                hoursByNature.put(nature, hours);
1811            }
1812        }
1813        
1814        for (String nature : hoursByNature.keySet())
1815        {
1816            attrs = new AttributesImpl();
1817            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_TEACHING_TYPE, _refTableHelper.getItemCDMfrValue(nature, true));
1818            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_GLOBAL_VOLUME, attrs, String.valueOf(hoursByNature.get(nature)));
1819        }
1820        
1821        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS);
1822    }
1823
1824    /**
1825     * SAX the syllabus elements of the course.
1826     * @param contentHandler The content handler
1827     * @param course The course to SAX
1828     * @param courseRichTextsContext the context for course's rich texts
1829     * @throws SAXException if an error occurs
1830     */
1831    protected void _syllabus2CDM(ContentHandler contentHandler, Course course, DataContext courseRichTextsContext) throws SAXException
1832    {
1833        RichText syllabus = course.getSyllabus();
1834        RichText bibliography = course.getBibliography();
1835        Set<LOMSheet> lomSheets = course.getLOMSheets();
1836        if (syllabus != null || bibliography != null || !lomSheets.isEmpty())
1837        {
1838            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_SYLLABUS);
1839            
1840            // Syllabus
1841            if (syllabus != null)
1842            {
1843                CDMHelper.richText2CDM(contentHandler, syllabus, courseRichTextsContext.cloneContext().withDataPath(Course.SYLLABUS), _sourceResolver, isExportForAmetys());
1844            }
1845            
1846            // Bibliography
1847            if (bibliography != null)
1848            {
1849                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + CDMFRTagsConstants.TAG_BIBLIOGRAPHY, bibliography, courseRichTextsContext.cloneContext().withDataPath(Course.BIBLIOGRAPHY), _sourceResolver, false, isExportForAmetys());
1850            }
1851            
1852            // LOM sheets
1853            for (LOMSheet lomSheet : lomSheets)
1854            {
1855                lomSheet.toCDM(contentHandler);
1856            }
1857            
1858            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_SYLLABUS);
1859        }
1860    }
1861    
1862    /**
1863     * Sax the course teaching locations.
1864     * @param contentHandler the content handler to SAX into
1865     * @param course The course to sax
1866     * @throws SAXException if failed to generate CDM
1867     */
1868    protected void _teachingLocation2CDM(ContentHandler contentHandler, Course course) throws SAXException
1869    {
1870        for (String id : course.getTeachingLocation())
1871        {
1872            if (StringUtils.isNotEmpty(id))
1873            {
1874                String postalCode = _refTableHelper.getItemCode(id);
1875                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_PLACE);
1876                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REF_ORG_UNIT);
1877                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
1878                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PCODE, postalCode);
1879                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
1880                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_PLACE);
1881            }
1882        }
1883    }
1884    
1885    /**
1886     * SAX CDMfr events for a {@link Course} contacts
1887     * @param contentHandler the content handler to sax into
1888     * @param course the course
1889     * @param persons the contacts
1890     * @param orgUnits the orgunits
1891     * @throws SAXException if an error occurred
1892     */
1893    protected void _course2CDMContacts(ContentHandler contentHandler, Course course, Set<String> persons, Set<String> orgUnits) throws SAXException
1894    {
1895        AttributesImpl attrs = new AttributesImpl();
1896        
1897        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
1898        
1899        Map<String, List<String>> contactsByRole = course.getContactsByRole();
1900        
1901        for (Entry<String, List<String>> entry : contactsByRole.entrySet())
1902        {
1903            String role = entry.getKey();
1904            List<String> contactIds = entry.getValue();
1905            
1906            String roleCode = null;
1907            if (StringUtils.isNotEmpty(role))
1908            {
1909                roleCode = _refTableHelper.getItemCode(role);
1910            }
1911            
1912            for (String personID : contactIds)
1913            {
1914                try
1915                {
1916                    Person person = _resolver.resolveById(personID);
1917                    persons.add(person.getId());
1918                    attrs = new AttributesImpl();
1919                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, person.getCDMId());
1920                    if (roleCode != null)
1921                    {
1922                        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ROLE, roleCode);
1923                    }
1924                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_PERSON, attrs);
1925                }
1926                catch (UnknownAmetysObjectException e)
1927                {
1928                    // Contact does exist anymore, ignore it
1929                }
1930            }
1931        }
1932        
1933        for (String orgUnitID : course.getOrgUnits())
1934        {
1935            if (!"".equals(orgUnitID))
1936            {
1937                try
1938                {
1939                    OrgUnit orgUnit = _resolver.resolveById(orgUnitID);
1940                    orgUnits.add(orgUnit.getId());
1941                    attrs = new AttributesImpl();
1942                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, orgUnit.getCDMId());
1943                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_ORG_UNIT, attrs);
1944                }
1945                catch (UnknownAmetysObjectException e)
1946                {
1947                    // OrgUnit does exist anymore, ignore it
1948                }
1949            }
1950        }
1951        
1952        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
1953    }
1954    
1955    
1956    //***********************************************************************//
1957    //                              ORGUNIT                                  //
1958    //***********************************************************************//
1959    
1960    /**
1961     * Export this entity in CDM-fr format.
1962     * @param contentHandler the target handler
1963     * @param orgunit the orgunit
1964     * @throws SAXException if an error occurs during export.
1965     */
1966    public void orgunit2CDM(ContentHandler contentHandler, OrgUnit orgunit) throws SAXException
1967    {   
1968        Set<String> persons = new HashSet<>();
1969        
1970        _orgunit2CDM(contentHandler, orgunit, persons, true);
1971        
1972        for (String personId : persons)
1973        {
1974            Person person = _resolver.resolveById(personId);
1975            person2CDM(contentHandler, person);
1976        }
1977        
1978        if (!orgunit.getId().equals(_rootOrgUnitProvider.getRootId()))
1979        {
1980            OrgUnit rootOrgunit = _resolver.resolveById(_rootOrgUnitProvider.getRootId());
1981            _orgunit2CDM(contentHandler, rootOrgunit, persons);
1982        }
1983        
1984        // Set content in request
1985        ContextHelper.getRequest(_context).setAttribute(Content.class.getName(), orgunit);
1986    }
1987    
1988    /**
1989     * SAX CDMfr event for a {@link OrgUnit}
1990     * @param contentHandler the content handler to sax into
1991     * @param orgunit the orgunit
1992     * @param persons the contacts of orgunit
1993     * @throws SAXException if an error occurred
1994     */
1995    protected void _orgunit2CDM(ContentHandler contentHandler, OrgUnit orgunit, Set<String> persons) throws SAXException
1996    {
1997        _orgunit2CDM(contentHandler, orgunit, persons, false);
1998    }
1999    
2000    /**
2001     * SAX CDMfr event for a {@link OrgUnit}
2002     * @param contentHandler the content handler to sax into
2003     * @param orgunit the orgunit
2004     * @param persons the contacts of orgunit
2005     * @param showChildren true to sax child orgunits
2006     * @throws SAXException if an error occurred
2007     */
2008    protected void _orgunit2CDM(ContentHandler contentHandler, OrgUnit orgunit, Set<String> persons, boolean showChildren) throws SAXException
2009    {
2010        Request request = ContextHelper.getRequest(_context);
2011        Content currentContent = (Content) request.getAttribute(Content.class.getName());
2012        DataContext orgunitRichTextsContext = DataContext.newInstance()
2013                                                         .withObjectId(orgunit.getId());
2014        
2015        try
2016        {
2017            _odfHelper.switchToLiveVersionIfNeeded(orgunit);
2018            
2019            request.setAttribute(Content.class.getName(), orgunit);
2020        
2021            AttributesImpl attrs = new AttributesImpl();
2022            
2023            // <orgunit id="FR_RNE_{rootOrgUnitId}_OR_{login}">
2024            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, orgunit.getCDMId());
2025            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT, attrs);
2026            
2027            // <orgUnitID>RNE</orgUnitID>
2028            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_ID, orgunit.getCDMId());
2029            
2030            // <orgUnitName><text>name</text></orgUnitID>
2031            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_NAME);
2032            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEXT, orgunit.getTitle());
2033            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_NAME);
2034            
2035            // <orgUnitAcronym>acronyme</orgUnitAcronym>
2036            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_ACRONYM, orgunit.getAcronym());
2037            
2038            // <orgUnitCode codeSet="codeUAI">code</orgUnitCode>
2039            attrs = new AttributesImpl();
2040            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "codeUAI");
2041            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_CODE, attrs, orgunit.getUAICode());
2042    
2043            // <orgUnitKind orgUnitKindCodeValueSet=""/>
2044            attrs = new AttributesImpl();
2045            String orgunitType = _refTableHelper.getItemCDMfrValue(orgunit.getType(), false);
2046            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ORG_UNIT_KIND_CODE_VALUESET, orgunitType);
2047            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_KIND);
2048            
2049            // <webLink>
2050            //      <href>href</href>
2051            //      <linkName>linkName</linkName>
2052            // </webLink>
2053            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_WEB_LINK);
2054            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_HREF, orgunit.getWebLinkURL());
2055            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_LINK_NAME, orgunit.getWebLinkLabel());
2056            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_WEB_LINK);
2057            
2058            // <orgUnitDescription>...</orgUnitDescription>
2059            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_DESCRIPTION, orgunit.getDescription(), orgunitRichTextsContext.cloneContext().withDataPath(OrgUnit.DESCRIPTION), _sourceResolver, false, isExportForAmetys());
2060            
2061            // <admissionInfo>
2062            //      <admissionDescription>...</admissionDescription>
2063            // </admissionInfo>
2064            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
2065            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_ADMISSION_DESCRIPTION, orgunit.getAdmissionInfo(), orgunitRichTextsContext.cloneContext().withDataPath(OrgUnit.ADMISSION_INFO), _sourceResolver);
2066            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
2067            
2068            // <regulations>...</regulations>
2069            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_REGULATIONS, orgunit.getRegulations(), orgunitRichTextsContext.cloneContext().withDataPath(OrgUnit.REGULATIONS), _sourceResolver, false, isExportForAmetys());
2070            
2071            // <expenses>...</expenses>
2072            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_EXPENSES, orgunit.getExpenses(), orgunitRichTextsContext.cloneContext().withDataPath(OrgUnit.EXPENSES), _sourceResolver, false, isExportForAmetys());
2073            
2074            // <studentFacilities>...</studentFacilities>
2075            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_STUDENT_FACILITIES, orgunit.getStudentFacilities(), orgunitRichTextsContext.cloneContext().withDataPath(OrgUnit.STUDENT_FACILITIES), _sourceResolver, false, isExportForAmetys());
2076            
2077            // <universalAjustment>...</universalAjustment>
2078            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_UNIVERSAL_ADJUSTMENT, orgunit.getUniversalAdjustment(), orgunitRichTextsContext.cloneContext().withDataPath(OrgUnit.UNIVERSAL_ADJUSTMENT), _sourceResolver, false, isExportForAmetys());
2079            
2080            // <contacts>
2081            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
2082            
2083            for (String contactID : orgunit.getContacts())
2084            {
2085                if (StringUtils.isNotEmpty(contactID))
2086                {
2087                    try
2088                    {
2089                        Person person = _resolver.resolveById(contactID);
2090                        persons.add(person.getId());
2091                        attrs = new AttributesImpl();
2092                        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, person.getCDMId());
2093                        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ROLE, "contact");
2094                        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_PERSON, attrs);
2095                    }
2096                    catch (UnknownAmetysObjectException e)
2097                    {
2098                        // Contact does exist anymore, ignore it
2099                    }
2100                }
2101            }
2102            
2103            // </contacts>
2104            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
2105            
2106            // <infoBlock>...</infoBlock>
2107            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, orgunit.getAdditionnalInfos(), orgunitRichTextsContext.cloneContext().withDataPath(OrgUnit.ADDITIONNAL_INFOS), _sourceResolver, false, isExportForAmetys());
2108    
2109            Set<OrgUnit> orgUnits = new HashSet<>();
2110            if (showChildren)
2111            {
2112                // <refOrgUnit IDref=""/>
2113                for (String orgUnitID : orgunit.getSubOrgUnits())
2114                {
2115                    try
2116                    {
2117                        OrgUnit orgUnit = _resolver.resolveById(orgUnitID);
2118                        orgUnits.add(orgUnit);
2119                        attrs = new AttributesImpl();
2120                        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, orgUnit.getCDMId());
2121                        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_ORG_UNIT, attrs);
2122                    }
2123                    catch (UnknownAmetysObjectException e)
2124                    {
2125                        // OrgUnit does exist anymore, ignore it
2126                    }
2127                }
2128            }
2129            
2130            // <infoBlock userDefined="ametys-extension>...</infoBlock>
2131            attrs = new AttributesImpl();
2132            attrs.addCDATAAttribute("userDefined", "ametys-extension");
2133            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
2134            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
2135            for (String id : _cdmFrExtensionPoint.getExtensionsIds())
2136            {
2137                CDMfrExtension cdmFrExtension = _cdmFrExtensionPoint.getExtension(id);
2138                cdmFrExtension.orgunit2CDM(contentHandler, orgunit);
2139            }
2140            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
2141            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK);
2142                    
2143            // </orgunit>
2144            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT);
2145    
2146            for (OrgUnit childOrgunit : orgUnits)
2147            {
2148                _orgunit2CDM(contentHandler, childOrgunit, persons);
2149            }
2150        }
2151        catch (NoLiveVersionException e)
2152        {
2153            getLogger().error("Orgunit {} ({}) is not validated so it cannot be exported to CDM-fr.", orgunit.getTitle(), orgunit.getId());
2154        }
2155        finally
2156        {
2157            request.setAttribute(Content.class.getName(), currentContent);
2158        }
2159    }
2160    
2161    
2162    //***********************************************************************//
2163    //                              PERSON                                   //
2164    //***********************************************************************//
2165    
2166    /**
2167     * Export this entity in CDM-fr format.
2168     * @param contentHandler the target handler
2169     * @param person the person
2170     * @throws SAXException if an error occurs during export.
2171     */
2172    public void person2CDM(ContentHandler contentHandler, Person person) throws SAXException
2173    {
2174        Request request = ContextHelper.getRequest(_context);
2175        Content currentContent = (Content) request.getAttribute(Content.class.getName());
2176        
2177        try
2178        {
2179            _odfHelper.switchToLiveVersionIfNeeded(person);
2180            
2181            request.setAttribute(Content.class.getName(), person);
2182            
2183            AttributesImpl attrs = new AttributesImpl();
2184            
2185            // <person id="FRUAI{rootOrgUnitId}PE{login}">
2186            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, person.getCDMId());
2187            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_PERSON, attrs);
2188            
2189            // <personID>login</personID>
2190            String login = person.getLogin();
2191            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PERSON_ID, StringUtils.isNotEmpty(login) ? login : person.getName());
2192            
2193            // <name>
2194            //      <given>prénom</given>
2195            //      <family>nom</family>
2196            // </name>
2197            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_NAME);
2198            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_GIVEN_NAME, person.getGivenName());
2199            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_FAMILY_NAME, person.getLastName());
2200            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_NAME);
2201    
2202            // <title><text>titre</text></title>
2203            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_TITLE);
2204            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEXT, person.getPersonTitle());
2205            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_TITLE);
2206            
2207            // <role><text>role</text></role>
2208            for (String role : person.getRole())
2209            {
2210                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ROLE);
2211                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEXT, role);
2212                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ROLE);
2213            }
2214            
2215            // <affiliation>
2216            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_AFFILIATION);
2217            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REF_ORG_UNIT);
2218            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROFESS_FIELD);
2219            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_SECT_CNU);
2220            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_AFFILIATION);
2221            
2222            
2223            // <contactData>
2224            ContactData contactData = person.getContactData();
2225            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_CONTACT_DATA);
2226            
2227            // <adr>
2228            //      <extadr>extadr</extadr>
2229            //      <street>address</street>
2230            //      <locality>locality</locality>
2231            //      <pcode>pcode</pcode>
2232            // </adr>
2233            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
2234            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_EXTADR, contactData.getAdditionalAddress());
2235            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_STREET, contactData.getAddress());
2236            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LOCALITY, contactData.getTown());
2237            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PCODE, contactData.getZipCode());
2238            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
2239            
2240            // <telephone>telephone</telephone>
2241            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PHONE, contactData.getPhone());
2242            
2243            // <fax>fax</fax>
2244            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_FAX, contactData.getFax());
2245            
2246            // <email>email</email>
2247            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_EMAIL, contactData.getMail());
2248            
2249            // <webLink>
2250            //      <href>href</href>
2251            //      <linkName>linkName</linkName>
2252            // </webLink>
2253            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_WEB_LINK);
2254            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_HREF, contactData.getWebLinkUrl());
2255            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LINK_NAME, contactData.getWebLinkLabel());
2256            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_WEB_LINK);
2257            
2258            // </contactData>
2259            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_CONTACT_DATA);
2260            
2261            // <infoBlock>...</infoBlock>
2262            DataContext context = DataContext.newInstance()
2263                                             .withObjectId(person.getId()).withDataPath(Person.ADDITIONAL_INFORMATIONS);
2264            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, person.getAdditionalInformations(), context, _sourceResolver, false, isExportForAmetys());
2265            
2266            // <infoBlock userDefined="ametys-extension>...</infoBlock>
2267            attrs = new AttributesImpl();
2268            attrs.addCDATAAttribute("userDefined", "ametys-extension");
2269            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
2270            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
2271            for (String id : _cdmFrExtensionPoint.getExtensionsIds())
2272            {
2273                CDMfrExtension cdmFrExtension = _cdmFrExtensionPoint.getExtension(id);
2274                cdmFrExtension.person2CDM(contentHandler, person);
2275            }
2276            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
2277            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK);
2278    
2279            // </person>
2280            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_PERSON);
2281        }
2282        catch (NoLiveVersionException e)
2283        {
2284            getLogger().error("Person {} ({}) is not validated so it cannot be exported to CDM-fr.", person.getTitle(), person.getId());
2285        }
2286        finally
2287        {
2288            request.setAttribute(Content.class.getName(), currentContent);
2289        }
2290    }
2291    
2292    private String _getMentionType(Program program)
2293    {
2294        return Optional.ofNullable(program.<ContentValue>getValue(AbstractProgram.DEGREE))
2295            .map(ContentValue::getContentIfExists)
2296            .filter(Optional::isPresent)
2297            .map(Optional::get)
2298            .map(c -> c.<String>getValue(OdfReferenceTableHelper.DEGREE_MENTION_TYPE))
2299            .orElse(null);
2300    }
2301    
2302    //***********************************************************************//
2303    //                               MISC                                    //
2304    //***********************************************************************//
2305
2306    /**
2307     * SAX value as attributes if not null nor empty
2308     * @param attrs the XML attributes
2309     * @param localName the local name
2310     * @param value the value
2311     */
2312    protected void _addNotNullAttribute(AttributesImpl attrs, String localName, String value)
2313    {
2314        if (StringUtils.isNotEmpty(value))
2315        {
2316            attrs.addCDATAAttribute(localName, value);
2317        }
2318    }
2319    
2320    /**
2321     * SAX value as attributes if not null nor empty
2322     * @param attrs the XML attributes
2323     * @param localName the local name
2324     * @param value the value
2325     */
2326    protected void _addPositiveAttribute(AttributesImpl attrs, String localName, double value)
2327    {
2328        if (value > 0)
2329        {
2330            attrs.addCDATAAttribute(localName, String.valueOf(value));
2331        }
2332    }
2333}