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