ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilCourseXMLParser.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=0);
27 {
28  private bool $in_meta_data = false;
29  private bool $in_availability = false;
30  private bool $in_registration = false;
31  private bool $in_period = false;
32  private bool $in_period_with_time = false;
33 
34  private ?ilDateTime $period_start = null;
35  private ?ilDateTime $period_end = null;
36 
37  private string $cdata = '';
38 
39  private string $current_container_setting = ''; // current meta data object
41  private ?ilLogger $log;
42  protected ilSetting $setting;
46  protected ?ilMD $md_obj = null;
51  protected array $course_members_array = [];
52 
53  public function __construct(ilObjCourse $a_course_obj, string $a_xml_file = '')
54  {
55  global $DIC;
56 
57  parent::__construct($a_xml_file);
58  $this->sax_controller = new ilSaxController();
59  $this->log = $DIC->logger()->crs();
60  $this->setting = $DIC->settings();
61  $this->course_obj = $a_course_obj;
62  $this->course_members = ilCourseParticipants::_getInstanceByObjId($this->course_obj->getId());
63  $this->course_waiting_list = new ilCourseWaitingList($this->course_obj->getId());
64  // flip the array so we can use array_key_exists
65  $this->course_members_array = array_flip($this->course_members->getParticipants());
66 
67  $this->md_obj = new ilMD($this->course_obj->getId(), 0, 'crs');
68  $this->setMDObject($this->md_obj);
69  }
70 
71  public function startParsing(): void
72  {
73  parent::startParsing();
74 
75  // rewrite autogenerated entry
76  $general = $this->getMDObject()->getGeneral();
77  $identifier_ids = $general->getIdentifierIds();
78  if (!isset($identifier_ids[0])) {
79  return;
80  }
81  $identifier = $general->getIdentifier($identifier_ids[0]);
82  $identifier->setEntry(
83  'il__' . $this->getMDObject()->getObjType() . '_' . $this->getMDObject()->getObjId()
84  );
85  $identifier->update();
86  }
87 
91  public function setHandlers($a_xml_parser): void
92  {
93  $this->sax_controller->setHandlers($a_xml_parser);
94  $this->sax_controller->setDefaultElementHandler($this);
95  $this->sax_controller->setElementHandler(
96  $this->adv_md_handler = new ilAdvancedMDValueParser($this->course_obj->getId()),
97  'AdvancedMetaData'
98  );
99  }
100 
104  public function handlerBeginTag($a_xml_parser, string $a_name, array $a_attribs): void
105  {
106  $a_attribs = $this->trimAndStripAttribs($a_attribs);
107  if ($this->in_meta_data) {
108  parent::handlerBeginTag($a_xml_parser, $a_name, $a_attribs);
109  return;
110  }
111 
112  switch ($a_name) {
113  case 'Course':
114  if (strlen($a_attribs['importId'] ?? '')) {
115  $this->log->write("CourseXMLParser: importId = " . $a_attribs['importId']);
116  $this->course_obj->setImportId($a_attribs['importId']);
117  ilObject::_writeImportId($this->course_obj->getId(), $a_attribs['importId']);
118  }
119  if (strlen($a_attribs['showMembers'] ?? '')) {
120  $this->course_obj->setShowMembers(
121  $a_attribs['showMembers'] == 'Yes'
122  );
123  }
124  break;
125 
126  case 'Admin':
127  if ($id_data = $this->__parseId($a_attribs['id'] ?? '')) {
128  if ($id_data['local'] or $id_data['imported']) {
129  $this->handleAdmin($a_attribs, $id_data);
130  }
131  }
132  break;
133 
134  case 'Tutor':
135  if ($id_data = $this->__parseId($a_attribs['id'] ?? '')) {
136  if ($id_data['local'] or $id_data['imported']) {
137  $this->handleTutor($a_attribs, $id_data);
138  }
139  }
140  break;
141 
142  case 'Member':
143  if ($id_data = $this->__parseId($a_attribs['id'] ?? '')) {
144  if ($id_data['local'] or $id_data['imported']) {
145  $this->handleMember($a_attribs, $id_data);
146  }
147  }
148  break;
149 
150  case 'Subscriber':
151  if ($id_data = $this->__parseId($a_attribs['id'] ?? '')) {
152  if ($id_data['local'] or $id_data['imported']) {
153  $this->handleSubscriber($a_attribs, $id_data);
154  }
155  }
156  break;
157 
158  case 'WaitingList':
159  if ($id_data = $this->__parseId($a_attribs['id'] ?? '')) {
160  if ($id_data['local'] or $id_data['imported']) {
161  $this->handleWaitingList($a_attribs, $id_data);
162  }
163  }
164  break;
165 
166  case 'Owner':
167  if ($id_data = $this->__parseId($a_attribs['id'] ?? '')) {
168  if ($id_data['local'] or $id_data['imported']) {
169  $this->course_obj->setOwner((int) ($id_data['usr_id'] ?? 0));
170  $this->course_obj->updateOwner();
171  }
172  }
173  break;
174 
175  case 'Settings':
176  break;
177  case 'Availability':
178  $this->in_availability = true;
179  break;
180 
181  case 'NotAvailable':
182  if ($this->in_availability) {
183  $this->course_obj->setOfflineStatus(true);
184  } elseif ($this->in_registration) {
185  $this->course_obj->setSubscriptionLimitationType(ilCourseConstants::IL_CRS_SUBSCRIPTION_DEACTIVATED);
186  }
187 
188  break;
189 
190  case 'Unlimited':
191  if ($this->in_availability) {
192  $this->course_obj->setOfflineStatus(false);
193  } elseif ($this->in_registration) {
194  $this->course_obj->setSubscriptionLimitationType(ilCourseConstants::IL_CRS_SUBSCRIPTION_UNLIMITED);
195  }
196 
197  break;
198  case 'TemporarilyAvailable':
199  if ($this->in_availability) {
200  $this->course_obj->setOfflineStatus(false);
201  } elseif ($this->in_registration) {
202  $this->course_obj->setSubscriptionLimitationType(ilCourseConstants::IL_CRS_SUBSCRIPTION_LIMITED);
203  }
204  break;
205 
206  case 'Start':
207  break;
208 
209  case 'End':
210  break;
211 
212  case 'Syllabus':
213  break;
214 
215  case 'TargetGroup':
216  break;
217 
218  case 'Contact':
219  break;
220 
221  case 'Name':
222  case 'Responsibility':
223  case 'Phone':
224  case 'Email':
225  case 'Consultation':
226  break;
227 
228  case 'Registration':
229  $this->in_registration = true;
230 
231  switch ($a_attribs['registrationType'] ?? '') {
232  case 'Confirmation':
233  $this->course_obj->setSubscriptionType(ilCourseConstants::IL_CRS_SUBSCRIPTION_CONFIRMATION);
234  break;
235 
236  case 'Direct':
237  $this->course_obj->setSubscriptionType(ilCourseConstants::IL_CRS_SUBSCRIPTION_DIRECT);
238  break;
239 
240  case 'Password':
241  $this->course_obj->setSubscriptionType(ilCourseConstants::IL_CRS_SUBSCRIPTION_PASSWORD);
242  break;
243  }
244 
245  $this->course_obj->setSubscriptionMaxMembers((int) ($a_attribs['maxMembers'] ?? 0));
246  $this->course_obj->enableSubscriptionMembershipLimitation($this->course_obj->getSubscriptionMaxMembers() > 0);
247  $this->course_obj->enableWaitingList(($a_attribs['waitingList'] ?? null) == 'Yes' ? true : false);
248  break;
249 
250  case 'Sort':
251  ilContainerSortingSettings::_importContainerSortingSettings($a_attribs, $this->course_obj->getId());
252 
253  //#17837
254  $this->course_obj->setOrderType(
255  ilContainerSortingSettings::getInstanceByObjId($this->course_obj->getId())->getSortMode()
256  );
257  break;
258 
259  case 'Disabled':
260  $this->course_obj->setSubscriptionLimitationType(ilCourseConstants::IL_CRS_SUBSCRIPTION_DEACTIVATED);
261  break;
262 
263  case "MetaData":
264  $this->in_meta_data = true;
265  parent::handlerBeginTag($a_xml_parser, $a_name, $a_attribs);
266  break;
267 
268  case 'ContainerSetting':
269  $this->current_container_setting = ($a_attribs['id'] ?? '');
270  break;
271 
272  case 'Period':
273  $this->in_period = true;
274  $this->in_period_with_time = (bool) ($a_attribs['withTime'] ?? false);
275  break;
276 
277  case 'WaitingListAutoFill':
278  case 'CancellationEnd':
279  case 'MinMembers':
280  case 'StatusDetermination':
281  case 'MailToMembersType':
282  break;
283 
284  case 'WelcomeMail':
285  if (array_key_exists('status', $a_attribs)) {
286  $this->course_obj->setAutoNotification((bool) $a_attribs['status']);
287  }
288  break;
289 
290  case 'CourseMap':
291  $this->course_obj->setEnableCourseMap((bool) $a_attribs['enabled'] ?? false);
292  $this->course_obj->setLatitude((string) $a_attribs['latitude'] ?? '');
293  $this->course_obj->setLongitude((string) $a_attribs['longitude'] ?? '');
294  $this->course_obj->setLocationZoom((int) $a_attribs['location_zoom'] ?? 0);
295  break;
296 
297  case 'SessionLimit':
298  if (isset($a_attribs['active'])) {
299  $this->course_obj->enableSessionLimit((bool) $a_attribs['active']);
300  }
301  if (isset($a_attribs['previous'])) {
302  $this->course_obj->setNumberOfPreviousSessions((int) $a_attribs['previous']);
303  }
304  if (isset($a_attribs['next'])) {
305  $this->course_obj->setNumberOfNextSessions((int) $a_attribs['next']);
306  }
307  break;
308  }
309  }
310 
311  public function __parseId($a_id): array
312  {
313  $fields = explode('_', $a_id);
314 
315  if (!is_array($fields) or
316  $fields[0] != 'il' or
317  !is_numeric($fields[1]) or
318  $fields[2] != 'usr' or
319  !is_numeric($fields[3])) {
320  return [];
321  }
322  if ($id = ilObjUser::_getImportedUserId($a_id)) {
323  return array('imported' => true,
324  'local' => false,
325  'usr_id' => $id
326  );
327  }
328  if ($fields[1] == $this->setting->get('inst_id', '0') && strlen(ilObjUser::_lookupLogin((int) $fields[3]))) {
329  return array('imported' => false,
330  'local' => true,
331  'usr_id' => (int) $fields[3]
332  );
333  }
334  return [];
335  }
336 
340  private function handleAdmin(array $a_attribs, array $id_data): void
341  {
342  if (!isset($a_attribs['action']) || $a_attribs['action'] == 'Attach') {
343  // if action not set, or attach
344  if (!array_key_exists($id_data['usr_id'], $this->course_members_array)) {
345  // add only if member is not assigned yet
346  $this->course_members->add($id_data['usr_id'], ilParticipants::IL_CRS_ADMIN);
347  if (isset($a_attribs['notification']) && $a_attribs['notification'] == 'Yes') {
348  $this->course_members->updateNotification($id_data['usr_id'], true);
349  }
350  if (isset($a_attribs['passed']) && $a_attribs['passed'] == 'Yes') {
351  $this->course_members->updatePassed($id_data['usr_id'], true);
352  }
353  if (isset($a_attribs['contact']) && $a_attribs['contact'] == 'Yes') {
354  // default for new course admin/tutors is "no contact"
355  $this->course_members->updateContact($id_data['usr_id'], true);
356  }
357  $this->course_members_array[$id_data['usr_id']] = "added";
358  } else {
359  // update
360  if (isset($a_attribs['notification']) && $a_attribs['notification'] == 'Yes') {
361  $this->course_members->updateNotification($id_data['usr_id'], true);
362  }
363  if (isset($a_attribs['passed']) && $a_attribs['passed'] == 'Yes') {
364  $this->course_members->updatePassed($id_data['usr_id'], true);
365  }
366  if (isset($a_attribs['contact']) && $a_attribs['contact'] == 'Yes') {
367  $this->course_members->updateContact($id_data['usr_id'], true);
368  } elseif (isset($a_attribs['contact']) && $a_attribs['contact'] == 'No') {
369  $this->course_members->updateContact($id_data['usr_id'], false);
370  }
371  $this->course_members->updateBlocked($id_data['usr_id'], false);
372  }
373  } elseif (isset($a_attribs['action']) && $a_attribs['action'] == 'Detach' && $this->course_members->isAdmin($id_data['usr_id'])) {
374  // if action set and detach and is admin of course
375  $this->course_members->delete($id_data['usr_id']);
376  }
377  }
378 
379  private function handleTutor(array $a_attribs, array $id_data): void
380  {
381  if (!isset($a_attribs['action']) || $a_attribs['action'] == 'Attach') {
382  // if action not set, or attach
383  if (!array_key_exists($id_data['usr_id'], $this->course_members_array)) {
384  // add only if member is not assigned yet
385  $this->course_members->add($id_data['usr_id'], ilParticipants::IL_CRS_TUTOR);
386  if (isset($a_attribs['notification']) && $a_attribs['notification'] == 'Yes') {
387  $this->course_members->updateNotification($id_data['usr_id'], true);
388  }
389  if (isset($a_attribs['passed']) && $a_attribs['passed'] == 'Yes') {
390  $this->course_members->updatePassed($id_data['usr_id'], true);
391  }
392  if (isset($a_attribs['contact']) && $a_attribs['contact'] == 'Yes') {
393  // default for new course admin/tutors is "no contact"
394  $this->course_members->updateContact($id_data['usr_id'], true);
395  }
396  $this->course_members_array[$id_data['usr_id']] = "added";
397  } else {
398  if (isset($a_attribs['notification']) && $a_attribs['notification'] == 'Yes') {
399  $this->course_members->updateNotification($id_data['usr_id'], true);
400  }
401  if (isset($a_attribs['passed']) && $a_attribs['passed'] == 'Yes') {
402  $this->course_members->updatePassed($id_data['usr_id'], true);
403  }
404  if (isset($a_attribs['contact']) && $a_attribs['contact'] == 'Yes') {
405  $this->course_members->updateContact($id_data['usr_id'], true);
406  } elseif (isset($a_attribs['contact']) && $a_attribs['contact'] == 'No') {
407  $this->course_members->updateContact($id_data['usr_id'], false);
408  }
409  $this->course_members->updateBlocked($id_data['usr_id'], false);
410  }
411  } elseif (isset($a_attribs['action']) && $a_attribs['action'] == 'Detach' && $this->course_members->isTutor($id_data['usr_id'])) {
412  // if action set and detach and is tutor of course
413  $this->course_members->delete($id_data['usr_id']);
414  }
415  }
416 
420  private function handleMember(array $a_attribs, array $id_data): void
421  {
422  if (!isset($a_attribs['action']) || $a_attribs['action'] == 'Attach') {
423  // if action not set, or set and attach
424  if (!array_key_exists($id_data['usr_id'], $this->course_members_array)) {
425  // add only if member is not yet assigned as tutor or admin
426  $this->course_members->add($id_data['usr_id'], ilParticipants::IL_CRS_MEMBER);
427  if (isset($a_attribs['blocked']) && $a_attribs['blocked'] == 'Yes') {
428  $this->course_members->updateBlocked($id_data['usr_id'], true);
429  }
430  if (isset($a_attribs['passed']) && $a_attribs['passed'] == 'Yes') {
431  $this->course_members->updatePassed($id_data['usr_id'], true);
432  }
433  $this->course_members_array[$id_data['usr_id']] = "added";
434  } else {
435  // the member does exist. Now update status etc. only
436  if (isset($a_attribs['blocked']) && $a_attribs['blocked'] == 'Yes') {
437  $this->course_members->updateBlocked($id_data['usr_id'], true);
438  }
439  if (isset($a_attribs['passed']) && $a_attribs['passed'] == 'Yes') {
440  $this->course_members->updatePassed($id_data['usr_id'], true);
441  }
442  }
443  } elseif (isset($a_attribs['action']) && $a_attribs['action'] == 'Detach' && $this->course_members->isMember($id_data['usr_id'])) {
444  // if action set and detach and is member of course
445  $this->course_members->delete($id_data['usr_id']);
446  }
447  }
448 
452  private function handleSubscriber(array $a_attribs, array $id_data): void
453  {
454  if (!isset($a_attribs['action']) || $a_attribs['action'] == 'Attach') {
455  // if action not set, or attach
456  if (!$this->course_members->isSubscriber($id_data['usr_id'])) {
457  // add only if not exist
458  $this->course_members->addSubscriber($id_data['usr_id']);
459  }
460  $this->course_members->updateSubscriptionTime($id_data['usr_id'], (int) ($a_attribs['subscriptionTime'] ?? 0));
461  } elseif (isset($a_attribs['action']) && $a_attribs['action'] == 'Detach' && $this->course_members->isSubscriber($id_data['usr_id'])) {
462  // if action set and detach and is subscriber
463  $this->course_members->deleteSubscriber($id_data["usr_id"]);
464  }
465  }
466 
470  private function handleWaitingList(array $a_attribs, array $id_data): void
471  {
472  if (!isset($a_attribs['action']) || $a_attribs['action'] == 'Attach') {
473  // if action not set, or attach
474  if (!$this->course_waiting_list->isOnList($id_data['usr_id'])) {
475  // add only if not exists
476  $this->course_waiting_list->addToList($id_data['usr_id']);
477  }
478  $this->course_waiting_list->updateSubscriptionTime($id_data['usr_id'], (int) ($a_attribs['subscriptionTime'] ?? 0));
479  } elseif (isset($a_attribs['action']) && $a_attribs['action'] == 'Detach' && $this->course_waiting_list->isOnList($id_data['usr_id'])) {
480  // if action set and detach and is on list
481  $this->course_waiting_list->removeFromList($id_data['usr_id']);
482  }
483  }
484 
488  public function handlerEndTag($a_xml_parser, string $a_name): void
489  {
490  $this->cdata = $this->trimAndStrip((string) $this->cdata);
491  if ($this->in_meta_data) {
492  parent::handlerEndTag($a_xml_parser, $a_name);
493  }
494 
495  switch ($a_name) {
496  case 'Course':
497 
498  $this->log->write('CourseXMLParser: import_id = ' . $this->course_obj->getImportId());
499  /*
500  * This needs to be before MDUpdateListener, since otherwise container settings are
501  * overwritten by ilContainer::update in MDUpdateListener, see #24733.
502  */
503  $this->course_obj->readContainerSettings();
504  $this->course_obj->MDUpdateListener('General');
505  $this->course_obj->update();
506  $this->adv_md_handler->save();
507  break;
508 
509  case 'Settings':
510  break;
511 
512  case 'Availability':
513  $this->in_availability = false;
514  break;
515 
516  case 'Registration':
517  $this->in_registration = false;
518  break;
519 
520  case 'Start':
521  if ($this->in_availability) {
522  $this->course_obj->setActivationStart((int) trim($this->cdata));
523  }
524  if ($this->in_registration) {
525  $this->course_obj->setSubscriptionStart((int) trim($this->cdata));
526  }
527  if ($this->in_period) {
528  if ((int) $this->cdata) {
529  if ($this->in_period_with_time) {
530  $this->period_start = new \ilDateTime((int) $this->cdata, IL_CAL_UNIX);
531  } else {
532  $this->period_start = new \ilDate((int) $this->cdata, IL_CAL_UNIX);
533  }
534  }
535  }
536  break;
537 
538  case 'End':
539  if ($this->in_availability) {
540  $this->course_obj->setActivationEnd((int) trim($this->cdata));
541  }
542  if ($this->in_registration) {
543  $this->course_obj->setSubscriptionEnd((int) trim($this->cdata));
544  }
545  if ($this->in_period) {
546  if ((int) $this->cdata) {
547  if ($this->in_period_with_time) {
548  $this->period_end = new \ilDateTime((int) $this->cdata, IL_CAL_UNIX);
549  } else {
550  $this->period_end = new \ilDate((int) $this->cdata, IL_CAL_UNIX);
551  }
552  }
553  }
554  break;
555 
556  case 'Syllabus':
557  $this->course_obj->setSyllabus(trim($this->cdata));
558  break;
559 
560  case 'TargetGroup':
561  $this->course_obj->setTargetGroup(trim($this->cdata));
562  break;
563 
564  case 'ImportantInformation':
565  $this->course_obj->setImportantInformation(trim($this->cdata));
566  break;
567 
568  case 'ViewMode':
569  $this->course_obj->setViewMode((int) trim($this->cdata));
570  break;
571 
572  case 'Name':
573  $this->course_obj->setContactName(trim($this->cdata));
574  break;
575 
576  case 'Responsibility':
577  $this->course_obj->setContactResponsibility(trim($this->cdata));
578  break;
579 
580  case 'Phone':
581  $this->course_obj->setContactPhone(trim($this->cdata));
582  break;
583 
584  case 'Email':
585  $this->course_obj->setContactEmail(trim($this->cdata));
586  break;
587 
588  case 'Consultation':
589  $this->course_obj->setContactConsultation(trim($this->cdata));
590  break;
591 
592  case 'Password':
593  $this->course_obj->setSubscriptionPassword(trim($this->cdata));
594  break;
595 
596  case 'MetaData':
597  $this->in_meta_data = false;
598  parent::handlerEndTag($a_xml_parser, $a_name);
599  break;
600 
601  case 'ContainerSetting':
602  if ($this->current_container_setting) {
604  $this->course_obj->getId(),
607  );
608  }
609  break;
610 
611  case 'Period':
612  $this->in_period = false;
613  try {
614  $this->course_obj->setCoursePeriod($this->period_start, $this->period_end);
615  } catch (Exception $e) {
616  $this->log->warning('invalid course period given');
617  }
618  break;
619 
620  case 'WaitingListAutoFill':
621  $this->course_obj->setWaitingListAutoFill((bool) $this->cdata);
622  break;
623 
624  case 'CancellationEnd':
625  if ((int) $this->cdata) {
626  $this->course_obj->setCancellationEnd(new ilDate((int) $this->cdata, IL_CAL_UNIX));
627  }
628  break;
629 
630  case 'MinMembers':
631  if ((int) $this->cdata) {
632  $this->course_obj->setSubscriptionMinMembers((int) $this->cdata);
633  }
634  break;
635 
636  case 'TimingMode':
637  $this->course_obj->setTimingMode((int) $this->cdata);
638  break;
639 
640  case 'StatusDetermination':
641  $this->course_obj->setStatusDetermination((int) $this->cdata);
642  break;
643 
644  case 'MailToMembersType':
645  $this->course_obj->setMailToMembersType((int) $this->cdata);
646  break;
647  }
648  $this->cdata = '';
649  }
650 
651  // PRIVATE
652 
653  public function handlerCharacterData($a_xml_parser, string $a_data): void
654  {
655  // call meta data handler
656  if ($this->in_meta_data) {
657  parent::handlerCharacterData($a_xml_parser, $a_data);
658  }
659  if ($a_data != "\n") {
660  // Replace multiple tabs with one space
661  $a_data = preg_replace("/\t+/", " ", $a_data);
662 
663  $this->cdata .= $a_data;
664  }
665  }
666 }
static _writeImportId(int $obj_id, string $import_id)
write import id to db (static)
handlerBeginTag($a_xml_parser, string $a_name, array $a_attribs)
trimAndStrip(string $input)
handleWaitingList(array $a_attribs, array $id_data)
attach or detach members from waitinglist
handleAdmin(array $a_attribs, array $id_data)
attach or detach admin from course member
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
trimAndStripAttribs(array $attribs)
ilCourseWaitingList $course_waiting_list
static _importContainerSortingSettings(array $attibs, int $obj_id)
sorting import for all container objects
const IL_CAL_UNIX
handleSubscriber(array $a_attribs, array $id_data)
attach or detach members from subscribers
global $DIC
Definition: feed.php:28
static _getInstanceByObjId(int $a_obj_id)
ilSaxController $sax_controller
ilAdvancedMDValueParser $adv_md_handler
handlerEndTag($a_xml_parser, string $a_name)
handleTutor(array $a_attribs, array $id_data)
static _writeContainerSetting(int $a_id, string $a_keyword, string $a_value)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Interface definition for sax subset parsers.
Controller class for sax element handlers.
static _getImportedUserId(string $i2_id)
__construct(Container $dic, ilPlugin $plugin)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
handleMember(array $a_attribs, array $id_data)
attach or detach user/member/admin from course member
ilCourseParticipants $course_members
__construct(ilObjCourse $a_course_obj, string $a_xml_file='')
handlerCharacterData($a_xml_parser, string $a_data)
Character data handler.
static _lookupLogin(int $a_user_id)