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