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