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