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