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