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