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