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