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.time.LocalDate;
019import java.util.Set;
020
021import org.apache.avalon.framework.service.ServiceException;
022import org.apache.avalon.framework.service.ServiceManager;
023import org.apache.avalon.framework.service.Serviceable;
024import org.apache.cocoon.xml.AttributesImpl;
025import org.apache.cocoon.xml.XMLUtils;
026import org.apache.commons.lang3.ArrayUtils;
027import org.apache.commons.lang3.StringUtils;
028import org.apache.excalibur.source.SourceResolver;
029import org.xml.sax.ContentHandler;
030import org.xml.sax.SAXException;
031
032import org.ametys.cms.data.ContentValue;
033import org.ametys.cms.data.File;
034import org.ametys.cms.data.Geocode;
035import org.ametys.cms.data.RichText;
036import org.ametys.cms.data.type.ModelItemTypeConstants;
037import org.ametys.cms.repository.Content;
038import org.ametys.odf.catalog.Catalog;
039import org.ametys.odf.catalog.CatalogsManager;
040import org.ametys.odf.course.Course;
041import org.ametys.odf.coursepart.CoursePart;
042import org.ametys.odf.enumeration.MonthEnumerator;
043import org.ametys.odf.enumeration.OdfReferenceTableHelper;
044import org.ametys.odf.orgunit.OrgUnit;
045import org.ametys.odf.person.Person;
046import org.ametys.odf.program.AbstractProgram;
047import org.ametys.odf.program.Container;
048import org.ametys.odf.program.Program;
049import org.ametys.odf.program.ProgramFactory;
050import org.ametys.odf.program.SubProgram;
051import org.ametys.plugins.repository.AmetysObjectResolver;
052import org.ametys.plugins.repository.data.holder.group.ModelAwareRepeater;
053import org.ametys.plugins.repository.data.holder.group.ModelAwareRepeaterEntry;
054import org.ametys.plugins.repository.model.RepositoryDataContext;
055import org.ametys.runtime.model.ModelItem;
056import org.ametys.runtime.model.type.DataContext;
057
058/**
059 * base CDMfr extension for Ametys
060 */
061public class AmetysCDMfrExtension extends AbstractCDMfrExtension implements Serviceable
062{
063    private OdfReferenceTableHelper _refTableHelper;
064    private CatalogsManager _catalogsManager;
065    private SourceResolver _sourceResolver;
066    private AmetysObjectResolver _resolver;
067    private MonthEnumerator _monthEnumerator;
068    
069    @Override
070    public void service(ServiceManager manager) throws ServiceException
071    {
072        _refTableHelper = (OdfReferenceTableHelper) manager.lookup(OdfReferenceTableHelper.ROLE);
073        _catalogsManager = (CatalogsManager) manager.lookup(CatalogsManager.ROLE);
074        _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
075        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
076        _monthEnumerator = (MonthEnumerator) manager.lookup(MonthEnumerator.ROLE);
077    }
078    
079    @Override
080    public void abstractProgram2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program, Set<String> persons, Set<String> orgUnits) throws SAXException
081    {
082        AttributesImpl attrs = new AttributesImpl();
083        
084        // Certified
085        // <ametys-cdm:certified/>
086        if (program.isCertified())
087        {
088            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.CERTIFIED);
089        }
090        
091        // Passerelle et réorientation
092        // <ametys-cdm:reorientation>...</ametys-cdm:reorientation>
093        DataContext reorientationContext = RepositoryDataContext.newInstance()
094                                                                .withObject(program)
095                                                                .withDataPath(AbstractProgram.REORIENTATION);
096        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.REORIENTATION, program.getReorientation(), reorientationContext, _sourceResolver, attrs);
097        
098        
099        // Date de fin de la formation
100        // <ametys-cdm:teachingEnd>...</ametys-cdm:teachingEnd>
101        LocalDate teachingEnd = program.getTeachingEnd();
102        if (teachingEnd != null)
103        {
104            CDMHelper.date2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + CDMFRTagsConstants.TAG_TEACHING_END, teachingEnd);
105        }
106
107        // Ouvert en stage
108        // <ametys-cdm:internshipOpen/>
109        if (program.isInternshipOpen())
110        {
111            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.INTERNSHIP_OPEN);
112        }
113        
114        // Stages
115        // <ametys-cdm:training>
116        //      <ametys-cdm:trainingInfo value="internship_mandatory">Obligatoire</ametys-cdm:trainingInfo>
117        //      <ametys-cdm:trainingDuration>6 mois</ametys-cdm:trainingInfo>
118        //      <ametys-cdm:trainingAbroadInfo value="internship_mandatory">Obligatoire</ametys-cdm:trainingAbroadInfo>
119        //      <ametys-cdm:trainingAbroadDuration>6 mois</ametys-cdm:trainingAbroadDuration>
120        //      <ametys-cdm:trainingDetails>
121        //          <ametys-cdm:trainingDetail>
122        //              <ametys-cdm:title>...</ametys-cmd:title>
123        //              <ametys-cdm:duration>...</ametys-cmd:duration>
124        //              <ametys-cdm:period>...</ametys-cmd:period>
125        //              <ametys-cdm:kind>...</ametys-cmd:kind>
126        //          </ametys-cdm:trainingDetail>
127        //      </ametys-cdm:trainingDetails>
128        // </ametys-cdm:training>
129        saxTraining(contentHandler, program);
130        
131        // Etablissement partenaires
132        // <ametys-cdm:partnerSchools/>
133        saxPartnerSchools(contentHandler, program);
134        
135        // Laboratoires partenaires
136        // <ametys-cdm:partnerLaboratories/>
137        saxPartnerLaboratories(contentHandler, program);
138        
139        // Fichier attachés
140        // <ametys-cdm:attachments/>
141        saxAttachments(contentHandler, program);
142        
143        // Niveau RNCP
144        // <ametys-cdm:rncpLevel/>
145        if (program instanceof Program)
146        {
147            String[] rncpLevels = program.getRncpLevel();
148            
149            if (rncpLevels.length > 0)
150            {
151                for (String rncpLevel : rncpLevels)
152                {
153                    String rncpValue = _refTableHelper.getItemCDMfrValue(rncpLevel, true);
154                    String rncpLabel = _refTableHelper.getItemLabel(rncpLevel, program.getLanguage());
155                    
156                    attrs = new AttributesImpl();
157                    attrs.addCDATAAttribute("value", rncpValue);
158                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.RNCP_LEVEL, attrs, rncpLabel);
159                }
160            }
161            else
162            {
163                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.RNCP_LEVEL);
164            }
165        }
166        
167        // RNCP code
168        // <ametys-cdm:rncpCode>Code RNCP</ametys-cdm:rncpCode>
169        for (String rncpCode : program.getRncpCode())
170        {
171            if (StringUtils.isNotEmpty(rncpCode))
172            {
173                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "rncpCode", rncpCode);
174            }
175        }
176        
177        // Niveau(x) d'entrée
178        // <ametys-cdm:educationEntryLevel code="code">Label</ametys-cdm:educationEntryLevel>
179        _saxMultipleEnumeration(contentHandler, program.getEducationLevelEntry(), AbstractProgram.EDUCATION_ENTRY_LEVEL, program.getLanguage());
180        
181        // Campus
182        // <ametys-cdm:campus code="code">Label</ametys-cdm:campus>
183        _saxMultipleEnumeration(contentHandler, program.getCampus(), AbstractProgram.CAMPUS, program.getLanguage());
184        
185        // Lieu(x) à l'étranger
186        // <ametys-cdm:foreignPlace code="code">Label</ametys-cdm:foreignPlace>
187        _saxMultipleEnumeration(contentHandler, program.getForeignPlace(), AbstractProgram.FOREIGN_PLACE, program.getLanguage());
188        
189        // Champ(s) de formation
190        // <ametys-cdm:programField code="code">Label</ametys-cdm:programField>
191        _saxMultipleEnumeration(contentHandler, program.getProgramField(), AbstractProgram.PROGRAM_FIELD, program.getLanguage());
192        
193        // Disciplines
194        // <ametys-cdm:disciplines code="code">Label</ametys-cdm:disciplines>
195        _saxMultipleEnumeration(contentHandler, program.getDisciplines(), AbstractProgram.DISCIPLINES, program.getLanguage());
196        
197        // Compétences requises
198        // <ametys-cdm:requiredskills code="code">Label</ametys-cdm:disciplines>
199        _saxMultipleEnumeration(contentHandler, program.getRequiredSkills(), AbstractProgram.REQUIRED_SKILLS, program.getLanguage());
200        
201        // Poursuite d'études dans l'établissement
202        // <ametys-cdm:furtherStudyPrograms code="cdm-code">Label</ametys-cdm:furtherStudyPrograms>
203        for (String furtherStudyProgramId : program.getFurtherStudyPrograms())
204        {
205            AbstractProgram furtherStudyProgram  = _resolver.resolveById(furtherStudyProgramId);
206            attrs = new AttributesImpl();
207            attrs.addCDATAAttribute("code", furtherStudyProgram.getCdmCode());
208            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.FURTHER_STUDY_PROGRAMS, attrs, furtherStudyProgram.getTitle());
209        }
210        
211        // Secteurs d'activité
212        // <ametys-cdm:sectors code="code">Label</ametys-cdm:sectors>
213        _saxMultipleEnumeration(contentHandler, program.getSectors(), AbstractProgram.SECTORS, program.getLanguage());
214        
215        // Ouvert en alternance
216        // <ametys-cdm:apprenticeshipOpen />
217        if (program.isApprenticeshipOpen())
218        {
219            XMLUtils.createElement(contentHandler, AbstractProgram.APPRENTICESHIP_OPEN);
220        }
221        
222        // Rythme d'alternance
223        // <ametys-cdm:apprenticeshipPeriod>...</ametys-cdm:apprenticeshipPeriod>
224        DataContext apprenticeshipPeriodContext = RepositoryDataContext.newInstance()
225                                                                       .withObject(program)
226                                                                       .withDataPath(AbstractProgram.APPRENTICESHIP_PERIOD);
227        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.APPRENTICESHIP_PERIOD, program.getApprenticeshipPeriod(), apprenticeshipPeriodContext, _sourceResolver);
228        
229        // Modalitités d'alternance
230        // <ametys-cdm:apprenticeshipModalities>...</ametys-cdm:apprenticeshipModalities>
231        DataContext apprenticeshipModalitiesContext = RepositoryDataContext.newInstance()
232                                                                       .withObject(program)
233                                                                       .withDataPath(AbstractProgram.APPRENTICESHIP_MODALITIES);
234        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.APPRENTICESHIP_MODALITIES, program.getApprenticeshipModalities(), apprenticeshipModalitiesContext, _sourceResolver);
235        
236        // Type(s) de contrat
237        // <ametys-cdm:apprenticeshipContract code="code">Label</ametys-cdm:apprenticeshipContract>
238        _saxMultipleEnumeration(contentHandler, program.getApprenticeshipContract(), AbstractProgram.APPRENTICESHIP_CONTRACT, program.getLanguage());
239        
240        // Formation internationale
241        // <ametys-cdm:internationalEducation code="code">Label</ametys-cdm:internationalEducation>
242        _saxMultipleEnumeration(contentHandler, program.getInternationalEducation(), AbstractProgram.INTERNATIONAL_EDUCATION, program.getLanguage());
243        
244        // Dimension internationale
245        // <ametys-cdm:internationalDimension>...</ametys-cdm:internationalDimension>
246        DataContext internationalDimensionContext = RepositoryDataContext.newInstance()
247                                                                         .withObject(program)
248                                                                         .withDataPath(AbstractProgram.INTERNATIONAL_DIMENSION);
249        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.INTERNATIONAL_DIMENSION, program.getInternationalDimension(), internationalDimensionContext, _sourceResolver);
250        
251        // Géolocalisation
252        // <ametys-cdm:disciplines code="code">Label</ametys-cdm:disciplines>
253        Geocode geocode = program.getGeocode();
254        if (geocode != null)
255        {
256            Double latitude = geocode.getLatitude();
257            Double longitude = geocode.getLongitude();
258            if (latitude != null && longitude != null)
259            {
260                attrs = new AttributesImpl();
261                attrs.addCDATAAttribute("latitude", String.valueOf(latitude));
262                attrs.addCDATAAttribute("longitude", String.valueOf(longitude));
263                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.GEOCODE, attrs);
264            }
265        }
266        
267        // Autres structures partenaires
268        DataContext otherPartnersContext = RepositoryDataContext.newInstance()
269                                                                .withObject(program)
270                                                                .withDataPath(AbstractProgram.OTHER_PARTNERS);
271        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.OTHER_PARTNERS, program.getOtherPartners(), otherPartnersContext, _sourceResolver);
272        
273        // Certifications possibles
274        // <ametys-cdm:disciplines code="code">Label</ametys-cdm:disciplines>
275        _saxMultipleEnumeration(contentHandler, program.getAvailableCertification(), AbstractProgram.AVAILABLE_CERTIFICATION, program.getLanguage());
276        
277        // Autre contact
278        // <ametys-cdm:otherContact>...</ametys-cdm:otherContact>
279        RichText otherContact = program.getValue(AbstractProgram.OTHER_CONTACT, false, null);
280        if (otherContact != null)
281        {
282            DataContext otherContactContext = RepositoryDataContext.newInstance()
283                                                                   .withObject(program)
284                                                                   .withDataPath(AbstractProgram.OTHER_CONTACT);
285            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.OTHER_CONTACT, otherContact, otherContactContext, _sourceResolver);
286        }
287    }
288    
289    /**
290     * SAX a multiple metadata based on an reference table.
291     * &lt;ametys-cdm:tagName code="code"&gt;Label&lt;/ametys-cdm:tagName&gt;
292     * @param contentHandler The handler
293     * @param values Values to SAX
294     * @param tagName The tag name
295     * @param lang The language to translate the label
296     * @throws SAXException if an error occurs
297     */
298    private void _saxMultipleEnumeration(ContentHandler contentHandler, String[] values, String tagName, String lang) throws SAXException
299    {
300        for (String value : values)
301        {
302            _saxSingleEnumeration(contentHandler, value, tagName, lang);
303        }
304    }
305    
306    private void _saxSingleEnumeration(ContentHandler contentHandler, String value, String tagName, String lang) throws SAXException
307    {
308        String code = _refTableHelper.getItemCDMfrValue(value, true);
309        String label = _refTableHelper.getItemLabel(value, lang);
310        
311        AttributesImpl attrs = new AttributesImpl();
312        attrs.addCDATAAttribute("code", code);
313        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + tagName, attrs, label);
314    }
315    
316    /**
317     * Sax training and internship informations.
318     * @param contentHandler the receiving contentHandler.
319     * @param program the abstract program (common for program and subprogram)
320     * @throws SAXException if an error occurs during CDM processing.
321     */
322    protected void saxTraining(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
323    {
324        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "training");
325        
326        String internship = program.getInternship();
327        if (!internship.isEmpty())
328        {
329            AttributesImpl attrs = new AttributesImpl();
330            attrs.addCDATAAttribute("value", _refTableHelper.getItemCDMfrValue(internship, true));
331            String internshipLabel = _refTableHelper.getItemLabel(internship, program.getLanguage());
332            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "trainingInfo", attrs, internshipLabel);
333        }
334        else
335        {
336            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "trainingInfo");
337        }
338        
339        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "trainingDuration", program.getInternshipDuration());
340        
341        String internshipAbroad = program.getInternshipAbroad();
342        if (!internshipAbroad.isEmpty())
343        {
344            AttributesImpl attrs = new AttributesImpl();
345            attrs.addCDATAAttribute("value", _refTableHelper.getItemCDMfrValue(internshipAbroad, true));
346            String internshipLabel = _refTableHelper.getItemLabel(internshipAbroad, program.getLanguage());
347            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "trainingAbroadInfo", attrs, internshipLabel);
348        }
349        else
350        {
351            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "trainingAbroadInfo");
352        }
353        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "trainingAbroadDuration", program.getAbroadInternshipDuration());
354
355        if (program.hasValue(AbstractProgram.INTERNSHIP_DESCRIPTION))
356        {
357            ModelAwareRepeater internshipDescriptions = program.getRepeater(AbstractProgram.INTERNSHIP_DESCRIPTION);
358            if (internshipDescriptions.getSize() > 0)
359            {
360                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "trainingDetails");
361                for (ModelAwareRepeaterEntry internshipDescription : internshipDescriptions.getEntries())
362                {
363                    XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "trainingDetail");
364                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "title", internshipDescription.getValue(AbstractProgram.INTERNSHIP_DESCRIPTION_TITLE, false, StringUtils.EMPTY));
365                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "duration", internshipDescription.getValue(AbstractProgram.INTERNSHIP_DESCRIPTION_DURATION, false, StringUtils.EMPTY));
366
367                    for (String value : internshipDescription.getValue(AbstractProgram.INTERNSHIP_DESCRIPTION_PERIOD, false, ArrayUtils.EMPTY_STRING_ARRAY))
368                    {
369                        AttributesImpl attrs = new AttributesImpl();
370                        attrs.addCDATAAttribute("code", value);
371                        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "period", attrs, _monthEnumerator.getLabel(value, program.getLanguage()));
372                    }
373                    
374                    StringBuilder dataPath = new StringBuilder(AbstractProgram.INTERNSHIP_DESCRIPTION)
375                            .append("[")
376                            .append(internshipDescription.getPosition())
377                            .append("]")
378                            .append(ModelItem.ITEM_PATH_SEPARATOR)
379                            .append(AbstractProgram.INTERNSHIP_DESCRIPTION_KIND);
380                    DataContext internshipDescriptionKindContext = RepositoryDataContext.newInstance()
381                                                                                        .withObject(program)
382                                                                                        .withDataPath(dataPath.toString());
383                    CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.INTERNSHIP_DESCRIPTION_KIND, internshipDescription.getValue(AbstractProgram.INTERNSHIP_DESCRIPTION_KIND), internshipDescriptionKindContext, _sourceResolver);
384                    
385                    XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "trainingDetail");
386                }
387                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "trainingDetails");
388            }
389        }
390
391        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "training");
392    }
393    
394    /**
395     * Sax partner schools (&lt;ametys-cdm:partnerSchools/&gt;).
396     * @param contentHandler the receiving contentHandler.
397     * @param program the abstract program (common for program and subprogram)
398     * @throws SAXException if an error occurs during CDM processing.
399     */
400    protected void saxPartnerSchools(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
401    {
402        if (program.hasValue(AbstractProgram.PARTNER_SCHOOLS))
403        {
404            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.PARTNER_SCHOOLS);
405            
406            ModelAwareRepeater partnerSchools = program.getRepeater(AbstractProgram.PARTNER_SCHOOLS);
407            for (ModelAwareRepeaterEntry partnerSchool : partnerSchools.getEntries())
408            {
409                String linkUrl = partnerSchool.getValue(AbstractProgram.PARTNER_SCHOOLS_LINK_URL, false, StringUtils.EMPTY);
410                String linkLabel = partnerSchool.getValue(AbstractProgram.PARTNER_SCHOOLS_LINK_LABEL, false, StringUtils.EMPTY);
411                
412                if (!linkUrl.isEmpty() || !linkLabel.isEmpty())
413                {
414                    XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "partnerSchool");
415                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "linkUrl", linkUrl);
416                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "linkLabel", linkLabel);
417                    XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "partnerSchool");
418                }
419            }
420            
421            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.PARTNER_SCHOOLS);
422        }
423    }
424
425    /**
426     * Sax partner laboratories (&lt;ametys-cdm:partnerLaboratories/&gt;).
427     * @param contentHandler the receiving contentHandler.
428     * @param program the abstract program (common for program and subprogram)
429     * @throws SAXException if an error occurs during CDM processing.
430     */
431    protected void saxPartnerLaboratories(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
432    {
433        if (program.hasValue(AbstractProgram.PARTNER_LABORATORIES))
434        {
435            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.PARTNER_LABORATORIES);
436            
437            ModelAwareRepeater partnerLaboratories = program.getRepeater(AbstractProgram.PARTNER_LABORATORIES);
438            for (ModelAwareRepeaterEntry partnerLaboratory : partnerLaboratories.getEntries())
439            {
440                String linkUrl = partnerLaboratory.getValue(AbstractProgram.PARTNER_LABORATORIES_LINK_URL, false, StringUtils.EMPTY);
441                String linkLabel = partnerLaboratory.getValue(AbstractProgram.PARTNER_LABORATORIES_LINK_LABEL, false, StringUtils.EMPTY);
442                
443                if (!linkUrl.isEmpty() || !linkLabel.isEmpty())
444                {
445                    XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "partnerLaboratory");
446                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "linkUrl", linkUrl);
447                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "linkLabel", linkLabel);
448                    XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "partnerLaboratory");
449                }
450            }
451            
452            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM +  AbstractProgram.PARTNER_LABORATORIES);
453        }
454    }
455
456    /**
457     * Sax attachments (&lt;ametys-cdm:attachments/&gt;).
458     * @param contentHandler the receiving contentHandler.
459     * @param program the abstract program (common for program and subprogram)
460     * @throws SAXException if an error occurs during CDM processing.
461     */
462    protected void saxAttachments(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
463    {
464        if (program.hasValue(AbstractProgram.ATTACHMENTS))
465        {
466            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.ATTACHMENTS);
467            
468            ModelAwareRepeater attachments = program.getRepeater(AbstractProgram.ATTACHMENTS);
469            for (ModelAwareRepeaterEntry entry: attachments.getEntries())
470            {
471                if (entry.hasValue(AbstractProgram.ATTACHMENTS_ATTACHMENT))
472                {
473                    String url = null;
474                    if (ModelItemTypeConstants.FILE_ELEMENT_TYPE_ID.equals(entry.getType(AbstractProgram.ATTACHMENTS_ATTACHMENT).getId()))
475                    {
476                        File file = entry.getValue(AbstractProgram.ATTACHMENTS_ATTACHMENT);
477                        url = _getFileAbsoluteUrl(program, file, "attachments[" + entry.getPosition() + "]/attachment");
478                    }
479                    
480                    String text = entry.getValue(AbstractProgram.ATTACHMENTS_ATTACHMENT_TEXT, false, StringUtils.EMPTY);
481                    
482                    XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "attachment");
483                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "text", text);
484                    if (url != null)
485                    {
486                        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "href", url);
487                    }
488                    XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "attachment");
489                }
490            }
491            
492            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + AbstractProgram.ATTACHMENTS);
493        }
494    }
495    
496    /**
497     * Sax the acquired skills
498     * @param contentHandler the receiving contentHandler.
499     * @param course the course
500     * @throws SAXException if an error occurs during CDM processing.
501     */
502    protected void saxAcquiredSkills(ContentHandler contentHandler, Course course) throws SAXException
503    {
504        if (course.hasValue(Course.ACQUIRED_SKILLS))
505        {
506            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + Course.ACQUIRED_SKILLS);
507            
508            ModelAwareRepeater repeater = course.getRepeater(Course.ACQUIRED_SKILLS);
509            for (ModelAwareRepeaterEntry entry : repeater.getEntries())
510            {
511                ContentValue skillSet = entry.getValue(Course.ACQUIRED_SKILLS_SKILLSET, false, null);
512                if (skillSet != null)
513                {
514                    XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "acquiredSkill");
515                    
516                    _saxSingleEnumeration(contentHandler, skillSet.getContentId(), "skillSet", course.getLanguage());
517                    
518                    ModelAwareRepeater skillRep = entry.getRepeater(Course.ACQUIRED_SKILLS_SKILLS);
519                    if (skillRep != null)
520                    {
521                        for (ModelAwareRepeaterEntry skillEntry : skillRep.getEntries())
522                        {
523                            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "skill");
524                            
525                            ContentValue skill = skillEntry.getValue(Course.ACQUIRED_SKILLS_SKILLS_SKILL, false, null);
526                            if (skill != null)
527                            {
528                                _saxSingleEnumeration(contentHandler, skill.getContentId(), "skill", course.getLanguage());
529                            }
530                            ContentValue acquisitionLevel = skillEntry.getValue(Course.ACQUIRED_SKILLS_SKILLS_ACQUISITION_LEVEL, false, null);
531                            if (acquisitionLevel != null)
532                            {
533                                _saxSingleEnumeration(contentHandler, acquisitionLevel.getContentId(), "acquisitionLevel", course.getLanguage());
534                            }
535                            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "skill");
536                        }
537                    }
538                    
539                    XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "acquiredSkill");
540                    
541                }
542            }
543            
544            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + Course.ACQUIRED_SKILLS);
545        }
546    }
547    
548    @Override
549    public void program2CDM(ContentHandler contentHandler, Program program, Set<String> persons, Set<String> orgUnits) throws SAXException
550    {
551        // Catalogue de formation
552        String catalogCode = program.getCatalog();
553        if (StringUtils.isNotEmpty(catalogCode))
554        {
555            AttributesImpl attrs = new AttributesImpl();
556            attrs.addCDATAAttribute("value", catalogCode);
557            
558            Catalog catalog = _catalogsManager.getCatalog(catalogCode);
559            String catalogLabel = catalog != null ? catalog.getTitle() : catalogCode;
560            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "catalog", attrs, catalogLabel);
561        }
562    }
563    
564    public void subProgram2CDM(ContentHandler contentHandler, SubProgram subProgram, Set<String> persons, Set<String> orgUnits) throws SAXException
565    {
566        // nothing specific to subprograms in the default Ametys model
567    }
568
569    @Override
570    public void course2CDM(ContentHandler contentHandler, Course course, Set<String> persons, Set<String> orgUnits) throws SAXException
571    {
572        // Catalogue de formation
573        String catalogCode = course.getCatalog();
574        if (StringUtils.isNotEmpty(catalogCode))
575        {
576            AttributesImpl attrs = new AttributesImpl();
577            attrs.addCDATAAttribute("value", catalogCode);
578            
579            Catalog catalog = _catalogsManager.getCatalog(catalogCode);
580            String catalogLabel = catalog != null ? catalog.getTitle() : catalogCode;
581            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "catalog", attrs, catalogLabel);
582        }
583        
584        // Ouvert aux étudiants en échange
585        // <ametys-cdm:openToExchangeStudents />
586        if (course.isOpenToExchangeStudents())
587        {
588            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + Course.OPEN_TO_EXCHANGE_STUDENTS);
589        }
590        
591        // <ametys-cdm:requiredSkills/>
592        _saxMultipleEnumeration(contentHandler, course.getRequiredSkills(), Course.REQUIRED_SKILLS, course.getLanguage());
593        
594        // Compétence acquises
595        saxAcquiredSkills(contentHandler, course);
596        
597        // Course parts
598        for (CoursePart coursePart : course.getCourseParts())
599        {
600            _coursePart2CDM(contentHandler, coursePart);
601        }
602    }
603    
604    /**
605     * Sax a {@link CoursePart} with the following structure :
606     * &lt;ametys-cdm:coursePart @ametys-cdm:code="code"&gt;
607     *     &lt;ametys-cdm:title&gt;Title&lt;/ametys-cdm:title&gt;
608     *     &lt;ametys-cdm:nature&gt;Nature CDM-fr value or code&lt;/ametys-cdm:nature&gt;
609     *     &lt;ametys-cdm:nbHours&gt;Number of hours&lt;/ametys-cdm:nbHours&gt;
610     *     &lt;ametys-cdm:courseHolder&gt;CDM ID of the course holder&lt;/ametys-cdm:courseHolder&gt;
611     * &lt;/ametys-cdm:coursePart&gt;
612     * @param contentHandler The content handler
613     * @param coursePart The {@link CoursePart} to sax
614     * @throws SAXException if an error occurs
615     */
616    protected void _coursePart2CDM(ContentHandler contentHandler, CoursePart coursePart) throws SAXException
617    {
618        AttributesImpl attrs = new AttributesImpl();
619        attrs.addCDATAAttribute("code", coursePart.getCode());
620        
621        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "coursePart", attrs);
622        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + Content.ATTRIBUTE_TITLE, coursePart.getTitle());
623        String nature = _refTableHelper.getItemCDMfrValue(coursePart.getNature(), true);
624        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + CoursePart.NATURE, nature);
625        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + CoursePart.NB_HOURS, String.valueOf(coursePart.getNumberOfHours()));
626        
627        Course courseHolder = coursePart.getCourseHolder();
628        if (courseHolder != null)
629        {
630            // it is not a normal case, but sometimes, the courseHolder references a not existing course, and we don't want to fail here
631            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + CoursePart.COURSE_HOLDER, courseHolder.getCDMId());
632        }
633        
634        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "coursePart");
635    }
636
637    @Override
638    public void orgunit2CDM(ContentHandler contentHandler, OrgUnit orgunit) throws SAXException
639    {
640        // nothing
641    }
642
643    @Override
644    public void person2CDM(ContentHandler contentHandler, Person person) throws SAXException
645    {
646        // Titre du contenu (à ne pas confondre avec personTitle)
647        String title = person.getTitle();
648        if (StringUtils.isNotEmpty(title))
649        {
650            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "title", title);
651        }
652    }
653
654    @Override
655    public void container2CDM(ContentHandler contentHandler, Container container, Set<String> persons, Set<String> orgUnits) throws SAXException
656    {
657        // Période
658        // <ametys-cdm:period code="code">Label</ametys-cdm:period>
659        String period = container.getPeriod();
660        if (StringUtils.isNotEmpty(period))
661        {
662            String code = _refTableHelper.getItemCDMfrValue(period, true);
663            String label = _refTableHelper.getItemLabel(period, container.getLanguage());
664    
665            AttributesImpl attrs = new AttributesImpl();
666            attrs.addCDATAAttribute("code", code);
667            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + Container.PERIOD, attrs, label);
668        }
669    }
670}