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