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