001/*
002 *  Copyright 2010 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.course;
017
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.Collections;
021import java.util.Date;
022import java.util.HashSet;
023import java.util.List;
024import java.util.Set;
025
026import javax.jcr.Node;
027
028import org.apache.commons.lang3.StringUtils;
029import org.apache.commons.lang3.ArrayUtils;
030
031import org.ametys.cms.content.external.ExternalizableMetadataHelper;
032import org.ametys.cms.content.external.ExternalizableMetadataProvider.ExternalizableMetadataStatus;
033import org.ametys.cms.repository.ModifiableDefaultContent;
034import org.ametys.odf.ProgramItem;
035import org.ametys.odf.courselist.CourseList;
036import org.ametys.odf.courselist.CourseListContainer;
037import org.ametys.odf.coursepart.CoursePart;
038import org.ametys.odf.program.Program;
039import org.ametys.plugins.repository.AmetysRepositoryException;
040import org.ametys.plugins.repository.UnknownAmetysObjectException;
041import org.ametys.plugins.repository.metadata.CompositeMetadata;
042import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
043import org.ametys.plugins.repository.metadata.RichText;
044import org.ametys.plugins.repository.metadata.UnknownMetadataException;
045
046/**
047 * Class representing a {@link Course}
048 */
049public class Course extends ModifiableDefaultContent<CourseFactory> implements CourseListContainer, ProgramItem
050{
051    /** Name of metadata for parent course lists */
052    public static final String METADATA_PARENT_COURSE_LISTS = "parentCourseLists";
053    
054    /** Name of metadata for parent course lists */
055    public static final String METADATA_CHILD_COURSE_LISTS = "courseLists";
056    
057    /** Mandatory Identifier to generate the CDM-fr id */
058    public static final String METADATA_CDM_CODE = "cdmCode";
059    
060    /** Constants for ects Metadata* */
061    public static final String METADATA_ECTS = "ects";
062
063    /** Constants for level Metadata* */
064    public static final String METADATA_LEVEL = "level";
065
066    /** Constants for description Metadata* */
067    public static final String METADATA_DESCRIPTION = "description";
068
069    /** Constants for objectives Metadata* */
070    public static final String METADATA_OBJECTIVES = "objectives";
071
072    /** Constants for nbHours Metadata* */
073    public static final String METADATA_NUMBER_OF_HOURS = "nbHours";
074
075    /** Constants for neededPrerequisite Metadata* */
076    public static final String METADATA_NEEDED_PREREQUISITE = "neededPrerequisite";
077
078    /** Constants for formOfAssessment Metadata* */
079    public static final String METADATA_FORM_OF_ASSESSMENT = "formOfAssessment";
080    
081    /** Constants for syllabus Metadata* */
082    public static final String METADATA_SYLLABUS = "syllabus";
083
084    /** Constants for additionalInformations Metadata* */
085    public static final String METADATA_ADDITIONAL_INFORMATIONS = "additionalInformations";
086
087    /** Constants for erasmusCode Metadata* */
088    public static final String METADATA_ERASMUS_CODE = "erasmusCode";
089
090    /** Constants for teachingLocation Metadata* */
091    public static final String METADATA_TEACHING_LOCATION = "teachingLocation";
092
093    /** Constants for maxNumberOfStudents Metadata* */
094    public static final String METADATA_MAX_NUMBER_OF_STUDENTS = "maxNumberOfStudents";
095
096    /** Constants for teachingTerm Metadata* */
097    public static final String METADATA_TEACHING_TERM = "teachingTerm";
098
099    /** Constants for timeSlot Metadata* */
100    public static final String METADATA_TIME_SLOT = "timeSlot";
101
102    /** Constants for trainingCourseDuration Metadata* */
103    public static final String METADATA_TRAINING_COURSE_DURATION = "trainingCourseDuration";
104
105    /** Constants for teachingMethod Metadata* */
106    public static final String METADATA_FORMODFTEACHING_METHOD = "formofteachingMethod";
107    
108    /** Constants for formofteachingOrg Metadata* */
109    public static final String METADATA_FORMOFTEACHING_ORG = "formofteachingOrg";
110
111    /** Constants for formOfTeaching Metadata* */
112    public static final String METADATA_TEACHING_ACTIVITY = "teachingActivity";
113
114    /** Constants for teachingLanguage Metadata* */
115    public static final String METADATA_TEACHING_LANGUAGE = "teachingLanguage";
116
117    /** Constants for startDate Metadata* */
118    public static final String METADATA_START_DATE = "startDate";
119
120    /** Constants for keywords Metadata* */
121    public static final String METADATA_KEYWORDS = "keywords";
122
123    /** Constants for webLinkLabel Metadata* */
124    public static final String METADATA_WEB_LINK_LABEL = "webLinkLabel";
125
126    /** Constants for webLinkUrl Metadata* */
127    public static final String METADATA_WEB_LINK_URL = "webLinkUrl";
128
129    /** Constants for lomSheets Metadata */
130    public static final String METADATA_LOM_SHEETS = "lomSheets";
131
132    /** Constants for LOM Sheet URL metadata */
133    public static final String METADATA_LOM_SHEET_URL = "linkUrl";
134    
135    /** Constants for LOM Sheet label metadata */
136    public static final String METADATA_LOM_SHEET_LABEL = "linkLabel";
137    
138    /** Constants for contentType Metadata* */
139    public static final String METADATA_ORG_UNITS = "orgUnit";
140    
141    /** Constants for metadata 'personInCharge' */
142    public static final String METADATA_PERSONS_IN_CHARGE = "personInCharge";
143
144    /** Constants for metadata 'contact' */
145    public static final String METADATA_CONTACTS = "contact";
146    
147    /** Constants for metadata 'courseType' */
148    public static final String METADATA_COURSE_TYPE = "courseType";
149    
150    /** Constants for metadata 'bibliography' */
151    public static final String METADATA_BIBLIOGRAPHY = "bibliography";
152
153    /** Constants for metadata 'skills' */
154    public static final String METADATA_SKILLS = "skills";
155
156    /** Constants for metadata 'openToExchangeStudents' */
157    public static final String METADATA_OPEN_TO_EXCHANGE_STUDENTS = "openToExchangeStudents";
158    
159    /** Constants for metadata 'courseParts' */
160    public static final String METADATA_CHILD_COURSE_PARTS = "courseParts";
161
162    private String _contextPath;
163
164    /**
165     * Constructor.
166     * @param node the JCR Node.
167     * @param parentPath the parent path
168     * @param factory the corresponding factory.
169     */
170    public Course(Node node, String parentPath, CourseFactory factory)
171    {
172        super(node, parentPath, factory);
173    }
174
175    @Override
176    public List<CourseList> getCourseLists()
177    {
178        List<CourseList> children = new ArrayList<>();
179        
180        String[] childIds = getMetadataHolder().getStringArray(METADATA_CHILD_COURSE_LISTS, new String[0]);
181        for (String id : childIds)
182        {
183            try
184            {
185                CourseList child = _getFactory()._getResolver().resolveById(id);
186                children.add(child);
187            }
188            catch (UnknownAmetysObjectException e)
189            {
190                // Nothing
191            }
192        }
193        
194        return children;
195    }
196    
197    /**
198     * Get the parent course lists
199     * @return The parent course lists
200     */
201    public List<CourseList> getParentCourseLists()
202    {
203        List<CourseList> children = new ArrayList<>();
204        
205        String[] childIds = getMetadataHolder().getStringArray(METADATA_PARENT_COURSE_LISTS, new String[0]);
206        for (String id : childIds)
207        {
208            try
209            {
210                CourseList child = _getFactory()._getResolver().resolveById(id);
211                children.add(child);
212            }
213            catch (UnknownAmetysObjectException e)
214            {
215                // Nothing
216            }
217        }
218        
219        return children;
220    }
221
222    @Override
223    public boolean hasCourseLists()
224    {
225        return getMetadataHolder().getStringArray(METADATA_CHILD_COURSE_LISTS, new String[0]).length > 0;
226    }
227    
228    @Override
229    public boolean containsCourseList(String clId)
230    {
231        return ArrayUtils.contains(getMetadataHolder().getStringArray(METADATA_CHILD_COURSE_LISTS, new String[0]), clId);
232    }
233    
234    @Override
235    public Set<Program> getRootPrograms()
236    {
237        Set<Program> programs = new HashSet<>();
238        
239        String[] parentIds = getMetadataHolder().getStringArray(METADATA_PARENT_COURSE_LISTS, new String[0]);
240        for (String parentId : parentIds)
241        {
242            try
243            {
244                CourseList parentCL = _getFactory()._getResolver().resolveById(parentId);
245                programs.addAll(parentCL.getRootPrograms());
246            }
247            catch (UnknownAmetysObjectException e)
248            {
249                // parent not found 
250            }
251            
252        }
253        
254        return programs;
255    }
256    
257    /**
258     * Remove reference from local and remote metadata
259     * @param metadataName The metadata name
260     * @param value The value of reference to remove 
261     */
262    public void removeReference (String metadataName, String value)
263    {
264        String[] references = getMetadataHolder().getStringArray(metadataName, new String[0]);
265        List<String> refList = new ArrayList<>(Arrays.asList(references));
266        if (refList.contains(value))
267        {
268            refList.remove(value);
269        }
270        getMetadataHolder().setMetadata(metadataName, refList.toArray(new String[refList.size()]));
271        
272        String[] altReferences = getMetadataHolder().getStringArray(metadataName + ExternalizableMetadataHelper.ALTERNATIVE_SUFFIX, new String[0]);
273        List<String> altList = new ArrayList<>(Arrays.asList(altReferences));
274        if (altList.contains(value))
275        {
276            altList.remove(value);
277        }
278        getMetadataHolder().setMetadata(metadataName + ExternalizableMetadataHelper.ALTERNATIVE_SUFFIX, altList.toArray(new String[altList.size()]));
279    }
280    
281    // --------------------------------------------------------------------------------------//
282    // CONTACTS
283    // --------------------------------------------------------------------------------------//
284
285    /**
286     * Return the ids of "person in charge" contents binded to this course 
287     * @return The id of contents
288     */
289    public List<String> getPersonsInCharge()
290    {
291        String[] contacts = getMetadataHolder().getStringArray(METADATA_PERSONS_IN_CHARGE, new String[0]);
292        return new ArrayList<>(Arrays.asList(contacts));
293    }
294    
295    /**
296     * Return the ids of "contact" contents binded to this course 
297     * @return The id of contents
298     */
299    public List<String> getContacts()
300    {
301        String[] contacts = getMetadataHolder().getStringArray(METADATA_CONTACTS, new String[0]);
302        return new ArrayList<>(Arrays.asList(contacts));
303    }
304   
305    
306    // --------------------------------------------------------------------------------------//
307    // CONTEXT
308    // --------------------------------------------------------------------------------------//
309    /**
310     * Set the parent path for links and breadcrumb
311     * @param path the parent path
312     */
313    public void setContextPath (String path)
314    {
315        _contextPath = path;
316    }
317    
318    /**
319     * Get the parent path. Can be null.
320     * @return the parent path
321     */
322    public String getContextPath ()
323    {
324        return _contextPath;
325    }
326    
327    // --------------------------------------------------------------------------------------//
328    // ORGUNITS
329    // --------------------------------------------------------------------------------------//
330    /**
331     * Return the id of orgunit content binded to this course
332     * @return The id of contents
333     */
334    public List<String> getOrgUnits()
335    {
336        String[] orgunits = getMetadataHolder().getStringArray(METADATA_ORG_UNITS, new String[0]);
337        return new ArrayList<>(Arrays.asList(orgunits));
338    }
339
340    /**
341     * Add a orgunit to the referenced orgunits
342     * @param orgUnitId the id of orgunit content
343     */
344    public void addOrgUnit(String orgUnitId)
345    {
346        String[] orgunits = new String[0];
347        try
348        {
349            orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), METADATA_ORG_UNITS, ExternalizableMetadataStatus.LOCAL);
350        }
351        catch (UnknownMetadataException e)
352        {
353            // Nothing
354        }
355        
356        List<String> orgunitsList = new ArrayList<>(Arrays.asList(orgunits));
357        orgunitsList.add(orgUnitId);
358        
359        ExternalizableMetadataHelper.setLocalMetadata(getMetadataHolder(), METADATA_ORG_UNITS, orgunitsList.toArray(new String[orgunitsList.size()]), ExternalizableMetadataStatus.LOCAL);
360    }
361
362    /**
363     * Remove a orgunit from referenced orgunits
364     * @param orgUnitId the id of orgunit content
365     * @throws AmetysRepositoryException if failed to remove orgunit
366     */
367    public void removeOrgUnit(String orgUnitId) throws AmetysRepositoryException
368    {
369        String[] orgunits = new String[0];
370        try
371        {
372            orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), METADATA_ORG_UNITS, ExternalizableMetadataStatus.LOCAL);
373        }
374        catch (UnknownMetadataException e)
375        {
376            // Nothing
377        }
378        
379        List<String> orgunitsList = new ArrayList<>(Arrays.asList(orgunits));
380        if (orgunitsList.contains(orgUnitId))
381        {
382            orgunitsList.remove(orgUnitId);
383        }
384        
385        ExternalizableMetadataHelper.setLocalMetadata(getMetadataHolder(), METADATA_ORG_UNITS, orgunitsList.toArray(new String[orgunitsList.size()]), ExternalizableMetadataStatus.LOCAL);
386    }
387
388    /**
389     * Return the id of local orgunit contents binded to this course
390     * @return The id of contents
391     */
392    public List<String> getLocalOrgUnits()
393    {
394        try
395        {
396            String[] orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), METADATA_ORG_UNITS, ExternalizableMetadataStatus.LOCAL);
397            return new ArrayList<>(Arrays.asList(orgunits));
398        }
399        catch (UnknownMetadataException e)
400        {
401            return Collections.EMPTY_LIST;
402        }
403    }
404
405    /**
406     * Return the id of remote orgunit contents binded to this course
407     * @return The id of contents
408     */
409    public List<String> getRemoteOrgUnits()
410    {
411        try
412        {
413            String[] orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), METADATA_ORG_UNITS, ExternalizableMetadataStatus.EXTERNAL);
414            return new ArrayList<>(Arrays.asList(orgunits));
415        }
416        catch (UnknownMetadataException e)
417        {
418            return Collections.EMPTY_LIST;
419        }
420    }
421    
422    // --------------------------------------------------------------------------------------//
423    // GETTERS AND SETTERS
424    // --------------------------------------------------------------------------------------//
425    @Override
426    public String getCatalog()
427    {
428        return getMetadataHolder().getString(METADATA_CATALOG, null);
429    }
430    
431    @Override
432    public void setCatalog(String catalog) throws AmetysRepositoryException
433    {
434        getMetadataHolder().setMetadata(METADATA_CATALOG, catalog);
435    }
436    
437    @Override
438    public String getCode()
439    {
440        return getMetadataHolder().getString(METADATA_CODE, "");
441    }
442    
443    @Override
444    public void setCode(String code) throws AmetysRepositoryException
445    {
446        getMetadataHolder().setMetadata(METADATA_CODE, code);
447    }
448    
449    /**
450     * Get the description
451     * @return the description or null
452     */
453    public RichText getDescription()
454    {
455        try
456        {
457            return getMetadataHolder().getRichText(METADATA_DESCRIPTION);
458        }
459        catch (UnknownMetadataException e)
460        {
461            return null;
462        }
463    }
464
465    /**
466     * Get the objectives
467     * @return objectives
468     */
469    public RichText getObjectives()
470    {
471        try
472        {
473            return getMetadataHolder().getRichText(METADATA_OBJECTIVES);
474        }
475        catch (UnknownMetadataException e)
476        {
477            return null;
478        }
479    }
480    
481    /**
482     * Get the course parts.
483     * @return The {@link List} of attached {@link CoursePart}s
484     */
485    public List<CoursePart> getCourseParts()
486    {
487        List<CoursePart> courseParts = new ArrayList<>();
488        
489        String[] coursePartIds = getMetadataHolder().getStringArray(METADATA_CHILD_COURSE_PARTS, new String[0]);
490        for (String id : coursePartIds)
491        {
492            try
493            {
494                CoursePart coursePart = _getFactory()._getResolver().resolveById(id);
495                courseParts.add(coursePart);
496            }
497            catch (UnknownAmetysObjectException e)
498            {
499                // Nothing
500            }
501        }
502        
503        return courseParts;
504    }
505    
506    /**
507     * Get the needed prerequisites
508     * @return the needed prerequisites or null if not set
509     */
510    public RichText getNeededPrerequisite()
511    {
512        try
513        {
514            return getMetadataHolder().getRichText(METADATA_NEEDED_PREREQUISITE);
515        }
516        catch (UnknownMetadataException e)
517        {
518            return null;
519        }
520    }
521
522    /**
523     * Get the formOfAssessment metadata
524     * @return the formOfAssessment or null if not set
525     */
526    public RichText getFormOfAssessment()
527    {
528        try
529        {
530            return getMetadataHolder().getRichText(METADATA_FORM_OF_ASSESSMENT);
531        }
532        catch (UnknownMetadataException e)
533        {
534            return null;
535        }
536    }
537    
538    /**
539     * Get the syllabus
540     * @return the syllabus or null if not set
541     */
542    public RichText getSyllabus()
543    {
544        try
545        {
546            return getMetadataHolder().getRichText(METADATA_SYLLABUS);
547        }
548        catch (UnknownMetadataException e)
549        {
550            return null;
551        }
552    }
553    
554    /**
555     * Get the list of LOM sheets
556     * @return the list of LOMsheets or an empty list
557     */
558    public Set<LOMSheet> getLOMSheets()
559    {
560        Set<LOMSheet> lomSheets = new HashSet<>();
561        try
562        {
563            CompositeMetadata metaLomSheets = getMetadataHolder().getCompositeMetadata(METADATA_LOM_SHEETS);
564            for (String metadataName : metaLomSheets.getMetadataNames())
565            {
566                CompositeMetadata metaLomSheet = metaLomSheets.getCompositeMetadata(metadataName);
567                LOMSheet lomSheet = new LOMSheet(metaLomSheet.getString(METADATA_LOM_SHEET_URL, ""), metaLomSheet.getString(METADATA_LOM_SHEET_LABEL, ""));
568                lomSheets.add(lomSheet);
569            }
570        }
571        catch (UnknownMetadataException e)
572        {
573            // Nothing to do
574        }
575        return lomSheets;
576    }
577    
578    /**
579     * Set the LOM sheets
580     * @param sheets The LOM sheets
581     */
582    public void setLOMSheets (Set<LOMSheet> sheets)
583    {
584        ModifiableCompositeMetadata metaLomSheets = getMetadataHolder().getCompositeMetadata(METADATA_LOM_SHEETS, true);
585        
586        // Remove old LOM sheets if exist
587        String[] chilNames = metaLomSheets.getMetadataNames();
588        for (String chilName : chilNames)
589        {
590            metaLomSheets.removeMetadata(chilName);
591        }
592        
593        int i = 1;
594        for (LOMSheet lomSheet : sheets)
595        {
596            ModifiableCompositeMetadata metaLomSheet = metaLomSheets.getCompositeMetadata(String.valueOf(i), true);
597            metaLomSheet.setMetadata(METADATA_LOM_SHEET_URL, lomSheet.getUrl());
598            metaLomSheet.setMetadata(METADATA_LOM_SHEET_LABEL, lomSheet.getLabel());
599            i++;
600        }
601    }
602    
603    /**
604     * Determines if the course has LOM sheet
605     * @param lomSheet The LOM sheet to test
606     * @return <code>true</code> if the course has LOM sheet
607     */
608    public boolean hasLOMSheet (LOMSheet lomSheet)
609    {
610        for (LOMSheet lom : getLOMSheets())
611        {
612            if (lom.getUrl().equals(lomSheet.getUrl()) && lom.getLabel().equals(lomSheet.getLabel()))
613            {
614                return true;
615            }
616        }
617        
618        return false;
619    }
620    
621    /**
622     * Get the additional information
623     * @return the additional information
624     */
625    public RichText getAdditionalInformations()
626    {
627        try
628        {
629            return getMetadataHolder().getRichText(METADATA_ADDITIONAL_INFORMATIONS);
630        }
631        catch (UnknownMetadataException e)
632        {
633            return null;
634        }
635    }
636
637    /**
638     * Get the Erasmus code
639     * @return the Erasmus code
640     */
641    public String getErasmusCode()
642    {
643        return getMetadataHolder().getString(METADATA_ERASMUS_CODE, StringUtils.EMPTY);
644    }
645
646    /**
647     * Get the teaching location
648     * @return the teaching location
649     */
650    public String[] getTeachingLocation()
651    {
652        return getMetadataHolder().getStringArray(METADATA_TEACHING_LOCATION, ArrayUtils.EMPTY_STRING_ARRAY);
653    }
654
655    /**
656     * Set the teaching location
657     * @param teachingLocation the teaching location to set
658     * @throws AmetysRepositoryException if failed to set metadata
659     */
660    public void setTeachingLocation(String[] teachingLocation) throws AmetysRepositoryException
661    {
662        getMetadataHolder().setMetadata(Course.METADATA_TEACHING_LOCATION, teachingLocation);
663    }
664
665    /**
666     * Get the ECTS
667     * @return the ECTS
668     */
669    public double getEcts()
670    {
671        return getMetadataHolder().getDouble(METADATA_ECTS, 0);
672    }
673
674    /**
675     * Get the number of hours
676     * @return the number of hours
677     */
678    public double getNumberOfHours()
679    {
680        return getMetadataHolder().getDouble(METADATA_NUMBER_OF_HOURS, 0);
681    }
682
683    /**
684     * Get the effectives
685     * @return the effectives
686     * @throws AmetysRepositoryException if failed to get metadata
687     */
688    public String getMaxNumberOfStudents()
689    {
690        return getMetadataHolder().getString(METADATA_MAX_NUMBER_OF_STUDENTS, StringUtils.EMPTY);
691    }
692
693    /**
694     * Get the teaching term
695     * @return the teaching term
696     */
697    public String getTeachingTerm()
698    {
699        return getMetadataHolder().getString(METADATA_TEACHING_TERM, StringUtils.EMPTY);
700    }
701
702    /**
703     * Get the level
704     * @return the level
705     */
706    public String getLevel()
707    {
708        return getMetadataHolder().getString(METADATA_LEVEL, StringUtils.EMPTY);
709    }
710
711    /**
712     * Get the teaching method
713     * @return the teaching method
714     */
715    public String getFormOfTeachingMethod()
716    {
717        return getMetadataHolder().getString(METADATA_FORMODFTEACHING_METHOD, StringUtils.EMPTY);
718    }
719
720    /**
721     * Get the teaching organizations
722     * @return the teaching organizations
723     */
724    public String[] getFormOfTeachingOrgs()
725    {
726        return getMetadataHolder().getStringArray(METADATA_FORMOFTEACHING_ORG, ArrayUtils.EMPTY_STRING_ARRAY);
727    }
728
729    /**
730     * Get the teaching activity
731     * @return the teaching activity
732     */
733    public String getTeachingActivity()
734    {
735        return getMetadataHolder().getString(METADATA_TEACHING_ACTIVITY, StringUtils.EMPTY);
736    }
737
738    /**
739     * Get the teaching language
740     * @return the teaching language
741     */
742    public String[] getTeachingLanguage()
743    {
744        return getMetadataHolder().getStringArray(METADATA_TEACHING_LANGUAGE, ArrayUtils.EMPTY_STRING_ARRAY);
745    }
746
747    /**
748     * Get the start date
749     * @return the start date
750     */
751    public Date getStartDate()
752    {
753        return getMetadataHolder().getDate(METADATA_START_DATE, null);
754    }
755
756    /**
757     * Get the time slot
758     * @return the time slot
759     */
760    public String getTimeSlot()
761    {
762        return getMetadataHolder().getString(METADATA_TIME_SLOT, StringUtils.EMPTY);
763    }
764
765    /**
766     * Get the keywords
767     * @return the keywords
768     */
769    public String[] getKeywords()
770    {
771        return getMetadataHolder().getStringArray(METADATA_KEYWORDS, ArrayUtils.EMPTY_STRING_ARRAY);
772    }
773
774    /**
775     * Get the web link label
776     * @return the web link label
777     */
778    public String getWebLinkLabel()
779    {
780        return getMetadataHolder().getString(METADATA_WEB_LINK_LABEL, StringUtils.EMPTY);
781    }
782
783    /**
784     * Get the web link URL
785     * @return the web link URL
786     */
787    public String getWebLinkUrl()
788    {
789        return getMetadataHolder().getString(METADATA_WEB_LINK_URL, StringUtils.EMPTY);
790    }
791
792    /**
793     * Get the course type (nature)
794     * @return the course type
795     */
796    public String getCourseType()
797    {
798        return getMetadataHolder().getString(METADATA_COURSE_TYPE, StringUtils.EMPTY);
799    }
800
801    /**
802     * Get the bibliography
803     * @return the bibliography
804     */
805    public RichText getBibliography()
806    {
807        try
808        {
809            return getMetadataHolder().getRichText(METADATA_BIBLIOGRAPHY);
810        }
811        catch (UnknownMetadataException e)
812        {
813            return null;
814        }
815    }
816    
817    /**
818     * Get the skills
819     * @return the skills
820     */
821    public RichText getSkills()
822    {
823        try
824        {
825            return getMetadataHolder().getRichText(METADATA_SKILLS);
826        }
827        catch (UnknownMetadataException e)
828        {
829            return null;
830        }
831    }
832    
833    /**
834     * Is open to exchange students
835     * @return <code>true</code> if the course is open to exchange students
836     */
837    public boolean isOpenToExchangeStudents()
838    {
839        return getMetadataHolder().getBoolean(METADATA_OPEN_TO_EXCHANGE_STUDENTS, false);
840    }
841    
842    // --------------------------------------------------------------------------------------//
843    // CDM-fr
844    // --------------------------------------------------------------------------------------//
845    @Override
846    public String getCDMId()
847    {
848        return "FRUAI" + _getFactory()._getRootOrgUnitRNE() + "CO" + getCode();
849    }
850    
851    @Override
852    public String getCdmCode()
853    {
854        return getMetadataHolder().getString(CDM_CODE, "");
855    }
856    
857    @Override
858    public void setCdmCode(String cdmCode) 
859    {
860        getMetadataHolder().setMetadata(CDM_CODE, cdmCode);
861    }
862}