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