ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilObjCmiXapi.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
30 class ilObjCmiXapi extends ilObject2
31 {
32  public const PLUGIN = false;
33 
34  public const DB_TABLE_NAME = 'cmix_settings';
35  public const DB_USERS_TABLE_NAME = 'cmix_users';
36  public const DB_RESULTS_TABLE_NAME = 'cmix_results';
37 
41  protected ?bool $activationLimited = null;
42  protected ?int $activationStartingTime = null;
43  protected ?int $activationEndingTime = null;
44  protected ?bool $activationVisibility = null;
45 
46  protected ?int $lrsTypeId;
47 
49 
50  protected string $contentType;
51  public const CONT_TYPE_GENERIC = 'generic';
52  public const CONT_TYPE_CMI5 = 'cmi5';
53 
54  protected string $sourceType;
55  public const SRC_TYPE_REMOTE = 'remoteSource';
56  public const SRC_TYPE_LOCAL = 'localSource';
57  public const SRC_TYPE_EXTERNAL = 'externalSource';
58 
59  protected string $activityId;
60 
61  protected string $publisherId;
62 
63  protected string $instructions;
64 
65  protected string $launchUrl;
66 
67  protected string $launchParameters;
68 
69  protected string $moveOn;
70 
71  protected string $entitlementKey;
72 
73  protected bool $authFetchUrlEnabled = false;
74 
75  protected bool $anonymousHomePage = false;
76  public const ANONYMOUS_HOMEPAGE = 'https://example.org';
77 
78  protected string $launchMethod;
79  public const LAUNCH_METHOD_OWN_WIN = 'ownWin';
80  public const LAUNCH_METHOD_NEW_WIN = 'newWin';
81  public const LAUNCH_METHOD_IFRAME = 'iframe';
82 
83  protected string $launchMode;
84  public const LAUNCH_MODE_NORMAL = 'Normal';
85  public const LAUNCH_MODE_BROWSE = 'Browse';
86  public const LAUNCH_MODE_REVIEW = 'Review';
87 
88  protected bool $switchToReviewEnabled = false;
89 
90  protected float $masteryScore;
91  public const LMS_MASTERY_SCORE = 0.7;
92 
93  protected bool $keepLpStatusEnabled = false;
94 
95  protected int $userIdent;
98  public const PRIVACY_IDENT_IL_UUID_LOGIN = 2;
99  public const PRIVACY_IDENT_REAL_EMAIL = 3;
103 
104  protected int $userName;
105  public const PRIVACY_NAME_NONE = 0;
106  public const PRIVACY_NAME_FIRSTNAME = 1;
107  public const PRIVACY_NAME_LASTNAME = 2;
108  public const PRIVACY_NAME_FULLNAME = 3;
109 
110  protected string $userPrivacyComment = "";
111 
112  protected bool $statementsReportEnabled = false;
113 
114  protected string $xmlManifest = "";
115 
116  protected int $version;
117 
118  protected bool $bypassProxyEnabled = false;
119 
120  protected bool $only_moveon = false;
121 
122  protected bool $achieved = true;
123 
124  protected bool $answered = true;
125 
126  protected bool $completed = true;
127 
128  protected bool $failed = true;
129 
130  protected bool $initialized = true;
131 
132  protected bool $passed = true;
133 
134  protected bool $progressed = true;
135 
136  protected bool $satisfied = true;
137 
138  protected bool $terminated = true;
139 
140  protected bool $hide_data = false;
141 
142  protected bool $timestamp = false;
143 
144  protected bool $duration = true;
145 
146  protected bool $no_substatements = false;
147 
148  protected ?ilCmiXapiUser $currentCmixUser = null;
149 
151 
155  public function __construct(int $a_id = 0, bool $a_reference = true)
156  {
157  global $DIC;
158  $this->database = $DIC->database();
159 
160  $this->lrsTypeId = 0;
161 
162  $this->contentType = self::CONT_TYPE_GENERIC;
163  $this->sourceType = self::SRC_TYPE_REMOTE;
164 
165  $this->activityId = '';
166 
167  $this->publisherId = '';
168 
169  $this->instructions = '';
170 
171  $this->launchUrl = '';
172  $this->launchParameters = '';
173  $this->moveOn = '';
174  $this->entitlementKey = '';
175 
176  $this->authFetchUrlEnabled = false;
177 
178  $this->launchMethod = self::LAUNCH_METHOD_NEW_WIN;
179  $this->launchMode = self::LAUNCH_MODE_NORMAL;
180 
181  $this->switchToReviewEnabled = true;
182 
183  $this->masteryScore = self::LMS_MASTERY_SCORE;
184  $this->keepLpStatusEnabled = true;
185 
186  $this->userIdent = self::PRIVACY_IDENT_IL_UUID_USER_ID;
187  $this->userName = self::PRIVACY_NAME_NONE;
188  $this->userPrivacyComment = '';
189 
190  $this->currentCmixUser = null;
191 
192  $this->statementsReportEnabled = false;
193 
194  $this->xmlManifest = '';
195  $this->version = 0;
196 
197  $this->bypassProxyEnabled = false;
198 
199  parent::__construct($a_id, $a_reference);
200  }
201 
202 
203  public static function getInstance(int $a_id = 0, bool $a_reference = true): \ilObjCmiXapi
204  {
205  return new self($a_id, $a_reference);
206  }
207 
208  protected function initType(): void
209  {
210  $this->type = "cmix";
211  }
212 
213  public function getLrsTypeId(): ?int
214  {
215  return $this->lrsTypeId;
216  }
217 
218  public function setLrsTypeId(int $lrsTypeId): void
219  {
220  $this->lrsTypeId = $lrsTypeId;
221  }
222 
223  public function getLrsType(): \ilCmiXapiLrsType
224  {
225  return $this->lrsType;
226  }
227 
228  public function setLrsType(\ilCmiXapiLrsType $lrsType): void
229  {
230  $this->lrsType = $lrsType;
231  }
232 
233  public function initLrsType(): void
234  {
235  $this->setLrsType(new ilCmiXapiLrsType($this->getLrsTypeId()));
236  }
237 
238  public function getContentType(): string
239  {
240  return $this->contentType;
241  }
242 
243  public function setContentType(string $contentType): void
244  {
245  //bug before 21-07-24
246  if ($contentType == "learning") {
247  $contentType = self::CONT_TYPE_GENERIC;
248  }
249  $this->contentType = $contentType;
250  }
251 
252  public function isMixedContentType(): bool
253  {
254  // after 21-07-24 and before cmi5 refactoring
255  // launched before cmi5 refactoring ident in: statement.actor.mbox
256  // launched after cmi5 refactoring ident in: statement.actor.account.name
257  return (($this->getContentType() == self::CONT_TYPE_CMI5) && empty($this->getPublisherId()));
258  }
259 
260  public function getSourceType(): string
261  {
262  return $this->sourceType;
263  }
264 
265  public function isSourceTypeRemote(): bool
266  {
267  return $this->sourceType == self::SRC_TYPE_REMOTE;
268  }
269 
270  public function isSourceTypeExternal(): bool
271  {
272  return $this->sourceType == self::SRC_TYPE_EXTERNAL;
273  }
274 
275  public function setSourceType(string $sourceType): void
276  {
277  $this->sourceType = $sourceType;
278  }
279 
280  public function getActivityId(): string
281  {
282  return $this->activityId;
283  }
284 
285  public function setActivityId(string $activityId): void
286  {
287  $this->activityId = $activityId;
288  }
289 
290  public function getPublisherId(): string
291  {
292  return $this->publisherId;
293  }
294 
295  public function setPublisherId(string $publisherId): void
296  {
297  $this->publisherId = $publisherId;
298  }
299 
300  public function getInstructions(): string
301  {
302  return $this->instructions;
303  }
304 
305  public function setInstructions(string $instructions): void
306  {
307  $this->instructions = $instructions;
308  }
309 
310  public function getLaunchUrl(): string
311  {
312  return $this->launchUrl;
313  }
314 
315  public function setLaunchUrl(string $launchUrl): void
316  {
317  $this->launchUrl = $launchUrl;
318  }
319 
320  public function getLaunchParameters(): string
321  {
323  }
324 
325  public function setLaunchParameters(string $launchParameters): void
326  {
327  $this->launchParameters = $launchParameters;
328  }
329 
334  public function getMoveOn(): string
335  {
336  return $this->moveOn;
337  }
338 
343  public function setMoveOn(string $moveOn): void
344  {
345  $this->moveOn = $moveOn;
346  }
347 
352  public function getLPMode(): int
353  {
354  $olp = ilObjectLP::getInstance($this->getId());
355  return $olp->getCurrentMode();
356  }
357 
362  public function getLMSMoveOn(): string
363  {
365  switch ($this->getLPMode()) {
368  break;
372  break;
375  $moveOn = ilCmiXapiLP::MOVEON_PASSED;
376  break;
380  break;
381  }
382  return $moveOn;
383  }
384 
385  public function getEntitlementKey(): string
386  {
387  return $this->entitlementKey;
388  }
389 
390  public function setEntitlementKey(string $entitlementKey): void
391  {
392  $this->entitlementKey = $entitlementKey;
393  }
394 
395  public function isAuthFetchUrlEnabled(): bool
396  {
398  }
399 
400  public function setAuthFetchUrlEnabled(bool $authFetchUrlEnabled): void
401  {
402  $this->authFetchUrlEnabled = $authFetchUrlEnabled;
403  }
404 
405  public function getLaunchMethod(): string
406  {
407  return $this->launchMethod;
408  }
409 
410  public function setLaunchMethod(string $launchMethod): void
411  {
412  $this->launchMethod = $launchMethod;
413  }
414 
415  public function getLaunchMode(): string
416  {
417  return ucfirst($this->launchMode);
418  }
419 
420  public function setLaunchMode(string $launchMode): void
421  {
422  $this->launchMode = ucfirst($launchMode);
423  }
424 
425  public function isSwitchToReviewEnabled(): bool
426  {
428  }
429 
430  public function getSwitchToReviewEnabled(): bool
431  {
433  }
434 
435  public function setSwitchToReviewEnabled(bool $switchToReviewEnabled): void
436  {
437  $this->switchToReviewEnabled = $switchToReviewEnabled;
438  }
439 
440  public function getMasteryScore(): float
441  {
442  return $this->masteryScore;
443  }
444 
445  public function setMasteryScore(float $masteryScore): void
446  {
447  $this->masteryScore = $masteryScore;
448  }
449 
450  public function getMasteryScorePercent(): float
451  {
452  return $this->masteryScore * 100;
453  }
454 
455  public function setMasteryScorePercent(float $masteryScorePercent): void
456  {
457  $this->masteryScore = $masteryScorePercent / 100;
458  }
459 
460  public function isKeepLpStatusEnabled(): bool
461  {
463  }
464 
465  public function setKeepLpStatusEnabled(bool $keepLpStatusEnabled): void
466  {
467  $this->keepLpStatusEnabled = $keepLpStatusEnabled;
468  }
469 
470  public function getPrivacyIdent(): int
471  {
472  return $this->userIdent;
473  }
474 
475  public function setPrivacyIdent(int $userIdent): void
476  {
477  $this->userIdent = $userIdent;
478  }
479 
480  public function getPrivacyName(): int
481  {
482  return $this->userName;
483  }
484 
485  public function setPrivacyName(int $userName): void
486  {
487  $this->userName = $userName;
488  }
489 
490  public function getOnlyMoveon(): bool
491  {
492  return $this->only_moveon;
493  }
494 
495  public function setOnlyMoveon(bool $only_moveon): void
496  {
497  $this->only_moveon = $only_moveon;
498  }
499 
500  public function getAchieved(): bool
501  {
502  return $this->achieved;
503  }
504 
505  public function setAchieved(bool $achieved): void
506  {
507  $this->achieved = $achieved;
508  }
509 
510  public function getAnswered(): bool
511  {
512  return $this->answered;
513  }
514 
515  public function setAnswered(bool $answered): void
516  {
517  $this->answered = $answered;
518  }
519 
520  public function getCompleted(): bool
521  {
522  return $this->completed;
523  }
524 
525  public function setCompleted(bool $completed): void
526  {
527  $this->completed = $completed;
528  }
529 
530  public function getFailed(): bool
531  {
532  return $this->failed;
533  }
534 
535  public function setFailed(bool $failed): void
536  {
537  $this->failed = $failed;
538  }
539 
540  public function getInitialized(): bool
541  {
542  return $this->initialized;
543  }
544 
545  public function setInitialized(bool $initialized): void
546  {
547  $this->initialized = $initialized;
548  }
549 
550  public function getPassed(): bool
551  {
552  return $this->passed;
553  }
554 
555  public function setPassed(bool $passed): void
556  {
557  $this->passed = $passed;
558  }
559 
560  public function getProgressed(): bool
561  {
562  return $this->progressed;
563  }
564 
565  public function setProgressed(bool $progressed): void
566  {
567  $this->progressed = $progressed;
568  }
569 
570  public function getSatisfied(): bool
571  {
572  return $this->satisfied;
573  }
574 
575  public function setSatisfied(bool $satisfied): void
576  {
577  $this->satisfied = $satisfied;
578  }
579 
580  public function getTerminated(): bool
581  {
582  return $this->terminated;
583  }
584 
585  public function setTerminated(bool $terminated): void
586  {
587  $this->terminated = $terminated;
588  }
589 
590  public function getHideData(): bool
591  {
592  return $this->hide_data;
593  }
594 
595  public function setHideData(bool $hide_data): void
596  {
597  $this->hide_data = $hide_data;
598  }
599 
600  public function getTimestamp(): bool
601  {
602  return $this->timestamp;
603  }
604 
605  public function setTimestamp(bool $timestamp): void
606  {
607  $this->timestamp = $timestamp;
608  }
609 
610  public function getDuration(): bool
611  {
612  return $this->duration;
613  }
614 
615  public function setDuration(bool $duration): void
616  {
617  $this->duration = $duration;
618  }
619 
620  public function getNoSubstatements(): bool
621  {
623  }
624 
625  public function setNoSubstatements(bool $no_substatements): void
626  {
627  $this->no_substatements = $no_substatements;
628  }
629 
630  public function getUserPrivacyComment(): string
631  {
633  }
634 
635  public function setUserPrivacyComment(string $userPrivacyComment): void
636  {
637  $this->userPrivacyComment = $userPrivacyComment;
638  }
639 
640  public function isStatementsReportEnabled(): bool
641  {
643  }
644 
645  public function setStatementsReportEnabled(bool $statementsReportEnabled): void
646  {
647  $this->statementsReportEnabled = $statementsReportEnabled;
648  }
649 
650  public function getXmlManifest(): string
651  {
652  return $this->xmlManifest;
653  }
654 
655  public function setXmlManifest(string $xmlManifest): void
656  {
657  $this->xmlManifest = $xmlManifest;
658  }
659 
660  public function getVersion(): int
661  {
662  return $this->version;
663  }
664 
665  public function setVersion(int $version): void
666  {
667  $this->version = $version;
668  }
669 
670  public function isBypassProxyEnabled(): bool
671  {
673  }
674 
675  public function setBypassProxyEnabled(bool $bypassProxyEnabled): void
676  {
677  $this->bypassProxyEnabled = $bypassProxyEnabled;
678  }
679 
680  //todo?
681  protected function doRead(): void
682  {
683  $this->load();
684  }
685 
686  protected function load(): void
687  {
688  $query = "SELECT * FROM " . self::DB_TABLE_NAME . " WHERE obj_id = %s";
689  $res = $this->database->queryF($query, ['integer'], [$this->getId()]);
690 
691  while ($row = $this->database->fetchAssoc($res)) {
692  if ($row['lrs_type_id']) {
693  $this->setLrsTypeId((int) $row['lrs_type_id']);
694  $this->initLrsType();
695  }
696 
697  $this->setContentType((string) $row['content_type']);
698  $this->setSourceType((string) $row['source_type']);
699 
700  $this->setActivityId((string) $row['activity_id']);
701  $this->setPublisherId((string) $row['publisher_id']);
702  $this->setInstructions((string) $row['instructions']);
703 
704  $this->setLaunchUrl((string) $row['launch_url']);
705  $this->setLaunchParameters((string) $row['launch_parameters']);
706  $this->setMoveOn((string) $row['moveon']);
707  $this->setEntitlementKey((string) $row['entitlement_key']);
708  $this->setAuthFetchUrlEnabled((bool) $row['auth_fetch_url']);
709 
710  $this->setLaunchMethod((string) $row['launch_method']);
711 
712  $this->setLaunchMode((string) $row['launch_mode']);
713  $this->setSwitchToReviewEnabled((bool) $row['switch_to_review']);
714  $this->setMasteryScore((float) $row['mastery_score']);
715  $this->setKeepLpStatusEnabled((bool) $row['keep_lp']);
716 
717  $this->setPrivacyIdent((int) $row['privacy_ident']);
718  $this->setPrivacyName((int) $row['privacy_name']);
719 
720  $this->setOnlyMoveon((bool) $row['only_moveon']);
721  $this->setAchieved((bool) $row['achieved']);
722  $this->setAnswered((bool) $row['answered']);
723  $this->setCompleted((bool) $row['completed']);
724  $this->setFailed((bool) $row['failed']);
725  $this->setInitialized((bool) $row['initialized']);
726  $this->setPassed((bool) $row['passed']);
727  $this->setProgressed((bool) $row['progressed']);
728  $this->setSatisfied((bool) $row['satisfied']);
729  $this->setTerminated((bool) $row['c_terminated']);
730  $this->setHideData((bool) $row['hide_data']);
731  $this->setTimestamp((bool) $row['c_timestamp']);
732  $this->setDuration((bool) $row['duration']);
733  $this->setNoSubstatements((bool) $row['no_substatements']);
734 
735  $this->setUserPrivacyComment((string) $row['usr_privacy_comment']);
736 
737  $this->setStatementsReportEnabled((bool) $row['show_statements']);
738 
739  $this->setXmlManifest((string) $row['xml_manifest']);
740  $this->setVersion((int) $row['version']);
741 
742  $this->setBypassProxyEnabled((bool) $row['bypass_proxy']);
743 
744  $this->setHighscoreEnabled((bool) $row['highscore_enabled']);
745  $this->setHighscoreAchievedTS((bool) $row['highscore_achieved_ts']);
746  $this->setHighscorePercentage((bool) $row['highscore_percentage']);
747  $this->setHighscoreWTime((bool) $row['highscore_wtime']);
748  $this->setHighscoreOwnTable((bool) $row['highscore_own_table']);
749  $this->setHighscoreTopTable((bool) $row['highscore_top_table']);
750  $this->setHighscoreTopNum((int) $row['highscore_top_num']);
751  }
752 
754  }
755 
756  //todo?
757  protected function doUpdate(): void
758  {
759  $this->save();
760  }
761 
762  public function save(): void
763  {
764  global $DIC; /* @var \ILIAS\DI\Container $DIC */
765  // not possible: Move Global Access to Constructor
766  $DIC->database()->replace(self::DB_TABLE_NAME, [
767  'obj_id' => ['integer', $this->getId()]
768  ], [
769  'lrs_type_id' => ['integer', $this->getLrsTypeId()],
770  'content_type' => ['text', $this->getContentType()],
771  'source_type' => ['text', $this->getSourceType()],
772  'activity_id' => ['text', $this->getActivityId()],
773  'publisher_id' => ['text', $this->getPublisherId()],
774  'instructions' => ['text', $this->getInstructions()],
775  'launch_url' => ['text', $this->getLaunchUrl()],
776  'launch_parameters' => ['text', $this->getLaunchParameters()],
777  'moveon' => ['text', $this->getMoveOn()],
778  'entitlement_key' => ['text', $this->getEntitlementKey()],
779  'auth_fetch_url' => ['integer', (int) $this->isAuthFetchUrlEnabled()],
780  'launch_method' => ['text', $this->getLaunchMethod()],
781  'launch_mode' => ['text', $this->getLaunchMode()],
782  'switch_to_review' => ['integer', (int) $this->isSwitchToReviewEnabled()],
783  'mastery_score' => ['float', $this->getMasteryScore()],
784  'keep_lp' => ['integer', (int) $this->isKeepLpStatusEnabled()],
785  'privacy_ident' => ['integer', $this->getPrivacyIdent()],
786  'privacy_name' => ['integer', $this->getPrivacyName()],
787  'usr_privacy_comment' => ['text', $this->getUserPrivacyComment()],
788  'show_statements' => ['integer', (int) $this->isStatementsReportEnabled()],
789  'xml_manifest' => ['text', $this->getXmlManifest()],
790  'version' => ['integer', $this->getVersion()],
791  'bypass_proxy' => ['integer', (int) $this->isBypassProxyEnabled()],
792  'highscore_enabled' => ['integer', (int) $this->getHighscoreEnabled()],
793  'highscore_achieved_ts' => ['integer', (int) $this->getHighscoreAchievedTS()],
794  'highscore_percentage' => ['integer', (int) $this->getHighscorePercentage()],
795  'highscore_wtime' => ['integer', (int) $this->getHighscoreWTime()],
796  'highscore_own_table' => ['integer', (int) $this->getHighscoreOwnTable()],
797  'highscore_top_table' => ['integer', (int) $this->getHighscoreTopTable()],
798  'highscore_top_num' => ['integer', $this->getHighscoreTopNum()],
799  'only_moveon' => ['integer', (int) $this->getOnlyMoveon()],
800  'achieved' => ['integer', (int) $this->getAchieved()],
801  'answered' => ['integer', (int) $this->getAnswered()],
802  'completed' => ['integer', (int) $this->getCompleted()],
803  'failed' => ['integer', (int) $this->getFailed()],
804  'initialized' => ['integer', (int) $this->getInitialized()],
805  'passed' => ['integer', (int) $this->getPassed()],
806  'progressed' => ['integer', (int) $this->getProgressed()],
807  'satisfied' => ['integer', (int) $this->getSatisfied()],
808  'c_terminated' => ['integer', (int) $this->getTerminated()],
809  'hide_data' => ['integer', (int) $this->getHideData()],
810  'c_timestamp' => ['integer', (int) $this->getTimestamp()],
811  'duration' => ['integer', (int) $this->getDuration()],
812  'no_substatements' => ['integer', (int) $this->getNoSubstatements()]
813  ]);
814 
816  }
817 
818  protected function loadRepositoryActivationSettings(): void
819  {
820  if ($this->ref_id) {
821  $activation = ilObjectActivation::getItem($this->ref_id);
822  switch ($activation["timing_type"]) {
824  $this->setActivationLimited(true);
825  if (!is_null($activation["timing_start"])) {
826  $activation["timing_start"] = (int) $activation["timing_start"];
827  }
828  $this->setActivationStartingTime($activation["timing_start"]);
829  if (!is_null($activation["timing_end"])) {
830  $activation["timing_end"] = (int) $activation["timing_end"];
831  }
832  $this->setActivationEndingTime($activation["timing_end"]);
833  $this->setActivationVisibility((bool) $activation["visible"]);
834  break;
835 
836  default:
837  $this->setActivationLimited(false);
838  break;
839  }
840  }
841  }
842 
843  protected function saveRepositoryActivationSettings(): void
844  {
845  if ($this->ref_id) {
846  ilObjectActivation::getItem($this->ref_id);
847 
848  $item = new ilObjectActivation();
849  if (!$this->isActivationLimited()) {
850  $item->setTimingType(ilObjectActivation::TIMINGS_DEACTIVATED);
851  } else {
852  $item->setTimingType(ilObjectActivation::TIMINGS_ACTIVATION);
853  $item->setTimingStart($this->getActivationStartingTime());
854  $item->setTimingEnd($this->getActivationEndingTime());
855  $item->toggleVisible($this->getActivationVisibility());
856  }
857 
858  $item->update($this->ref_id);
859  }
860  }
861 
862  public static function updatePrivacySettingsFromLrsType(ilCmiXapiLrsType $lrsType): void
863  {
864  global $DIC; /* @var \ILIAS\DI\Container $DIC */
865  //not possible: Move Global Access to Constructor
866  $tableName = self::DB_TABLE_NAME;
867 
868  $query = "
869  UPDATE {$tableName}
870  SET privacy_ident = %s,
871  privacy_name = %s,
872  only_moveon = %s,
873  achieved = %s,
874  answered = %s,
875  completed = %s,
876  failed = %s,
877  initialized = %s,
878  passed = %s,
879  progressed = %s,
880  satisfied = %s,
881  c_terminated = %s,
882  hide_data = %s,
883  c_timestamp = %s,
884  duration = %s,
885  no_substatements = %s
886  WHERE lrs_type_id = %s
887  ";
888 
889  $DIC->database()->manipulateF(
890  $query,
891  ['integer',
892  'integer',
893  'integer',
894  'integer',
895  'integer',
896  'integer',
897  'integer',
898  'integer',
899  'integer',
900  'integer',
901  'integer',
902  'integer',
903  'integer',
904  'integer',
905  'integer',
906  'integer',
907  'integer'
908  ],
909  [$lrsType->getPrivacyIdent(),
910  $lrsType->getPrivacyName(),
911  $lrsType->getOnlyMoveon(),
912  $lrsType->getAchieved(),
913  $lrsType->getAnswered(),
914  $lrsType->getCompleted(),
915  $lrsType->getFailed(),
916  $lrsType->getInitialized(),
917  $lrsType->getPassed(),
918  $lrsType->getProgressed(),
919  $lrsType->getSatisfied(),
920  $lrsType->getTerminated(),
921  $lrsType->getHideData(),
922  $lrsType->getTimestamp(),
923  $lrsType->getDuration(),
924  $lrsType->getNoSubstatements(),
925  $lrsType->getTypeId()
926  ]
927  );
928  }
929 
930  public static function updateByPassProxyFromLrsType(ilCmiXapiLrsType $lrsType): void
931  {
932  global $DIC; /* @var \ILIAS\DI\Container $DIC */
933  // not possible: Move Global Access to Constructor
934  $tableName = self::DB_TABLE_NAME;
935 
936  $query = "
937  UPDATE {$tableName}
938  SET bypass_proxy = %s
939  WHERE lrs_type_id = %s
940  ";
941 
942  $DIC->database()->manipulateF(
943  $query,
944  ['integer', 'integer'],
945  [$lrsType->isBypassProxyEnabled(), $lrsType->getTypeId()]
946  );
947  }
948 
953  {
954  global $DIC; /* @var \ILIAS\DI\Container $DIC */
955  // not possible: Move Global Access to Constructor
956  $query = "
957  SELECT DISTINCT s.obj_id FROM " . self::DB_TABLE_NAME . " s
958  INNER JOIN " . self::DB_USERS_TABLE_NAME . " u ON u.obj_id = s.obj_id
959  WHERE bypass_proxy = %s
960  ";
961 
962  $res = $DIC->database()->queryF($query, array('integer'), array(1));
963 
964  $objects = array();
965 
966  while ($row = $DIC->database()->fetchAssoc($res)) {
967  $objects[] = (int) $row['obj_id'];
968  }
969 
970  return $objects;
971  }
972 
973 
974 
977 
978  protected bool $_highscore_enabled = false;
979 
980  protected int $anonymity = 0;
981 
982  protected bool $_highscore_achieved_ts = true;
983 
984  protected bool $_highscore_percentage = true;
985 
986  protected bool $_highscore_wtime = true;
987 
988  protected bool $_highscore_own_table = true;
989 
990  protected bool $_highscore_top_table = true;
991 
992  protected int $_highscore_top_num = 10;
993 
994  public const HIGHSCORE_SHOW_ALL_TABLES = 1;
995  public const HIGHSCORE_SHOW_TOP_TABLE = 2;
996  public const HIGHSCORE_SHOW_OWN_TABLE = 3;
997 
1001  public function setHighscoreEnabled(bool $a_enabled): void
1002  {
1003  $this->_highscore_enabled = $a_enabled;
1004  }
1005 
1010  public function getHighscoreEnabled(): bool
1011  {
1013  }
1014 
1018  public function setHighscoreAchievedTS(bool $a_achieved_ts): void
1019  {
1020  $this->_highscore_achieved_ts = $a_achieved_ts;
1021  }
1022 
1027  public function getHighscoreAchievedTS(): bool
1028  {
1030  }
1031 
1035  public function setHighscorePercentage(bool $a_percentage): void
1036  {
1037  $this->_highscore_percentage = $a_percentage;
1038  }
1039 
1044  public function getHighscorePercentage(): bool
1045  {
1047  }
1048 
1052  public function setHighscoreWTime(bool $a_wtime): void
1053  {
1054  $this->_highscore_wtime = $a_wtime;
1055  }
1056 
1061  public function getHighscoreWTime(): bool
1062  {
1063  return $this->_highscore_wtime;
1064  }
1065 
1070  public function setHighscoreOwnTable(bool $a_own_table): void
1071  {
1072  $this->_highscore_own_table = $a_own_table;
1073  }
1074 
1079  public function getHighscoreOwnTable(): bool
1080  {
1082  }
1083 
1087  public function setHighscoreTopTable(bool $a_top_table): void
1088  {
1089  $this->_highscore_top_table = $a_top_table;
1090  }
1091 
1096  public function getHighscoreTopTable(): bool
1097  {
1099  }
1100 
1106  public function setHighscoreTopNum(int $a_top_num): void
1107  {
1108  $this->_highscore_top_num = $a_top_num;
1109  }
1110 
1117  public function getHighscoreTopNum(?int $a_retval = 10): ?int
1118  {
1119  $retval = $a_retval;
1120  if ($this->_highscore_top_num != 0) {
1121  $retval = $this->_highscore_top_num;
1122  }
1123 
1124  return $retval;
1125  }
1126 
1127  public function getHighscoreMode(): int
1128  {
1129  switch (true) {
1130  case $this->getHighscoreOwnTable() && $this->getHighscoreTopTable():
1131  return self::HIGHSCORE_SHOW_ALL_TABLES;
1132 
1133  case $this->getHighscoreTopTable():
1134  return self::HIGHSCORE_SHOW_TOP_TABLE;
1135 
1136  case $this->getHighscoreOwnTable():
1137  default:
1138  return self::HIGHSCORE_SHOW_OWN_TABLE;
1139  }
1140  }
1141 
1142 
1143  public function setHighscoreMode(int $mode): void
1144  {
1145  switch ($mode) {
1146  case self::HIGHSCORE_SHOW_ALL_TABLES:
1147  $this->setHighscoreTopTable(true);
1148  $this->setHighscoreOwnTable(true);
1149  break;
1150 
1151  case self::HIGHSCORE_SHOW_TOP_TABLE:
1152  $this->setHighscoreTopTable(true);
1153  $this->setHighscoreOwnTable(false);
1154  break;
1155 
1156  case self::HIGHSCORE_SHOW_OWN_TABLE:
1157  default:
1158  $this->setHighscoreTopTable(false);
1159  $this->setHighscoreOwnTable(true);
1160  break;
1161  }
1162  }
1163 
1164  /* End GET/SET for highscore feature */
1165 
1169  public function getDataSetMapping(): array
1170  {
1171  if (null === ($lrsTypeId = $this->getLrsTypeId())) {
1172  $this->doRead();
1173  }
1174  return [
1175  'obj_id' => $this->getId(),
1176  'lrs_type_id' => $this->getLrsTypeId(),
1177  'content_type' => $this->getContentType(),
1178  'source_type' => $this->getSourceType(),
1179  'activity_id' => $this->getActivityId(),
1180  'publisher_id' => $this->getPublisherId(),
1181  'instructions' => $this->getInstructions(),
1182  'launch_url' => $this->getLaunchUrl(),
1183  'launch_parameters' => $this->getLaunchParameters(),
1184  'moveon' => $this->getMoveOn(),
1185  'entitlement_key' => $this->getEntitlementKey(),
1186  'auth_fetch_url' => (int) $this->isAuthFetchUrlEnabled(),
1187  'launch_method' => $this->getLaunchMethod(),
1188  'launch_mode' => $this->getLaunchMode(),
1189  'switch_to_review' => (int) $this->isSwitchToReviewEnabled(),
1190  'mastery_score' => $this->getMasteryScore(),
1191  'keep_lp' => (int) $this->isKeepLpStatusEnabled(),
1192  'privacy_ident' => $this->getPrivacyIdent(),
1193  'privacy_name' => $this->getPrivacyName(),
1194  'usr_privacy_comment' => $this->getUserPrivacyComment(),
1195  'show_statements' => (int) $this->isStatementsReportEnabled(),
1196  'xml_manifest' => $this->getXmlManifest(),
1197  'version' => $this->getVersion(),
1198  'highscore_enabled' => (int) $this->getHighscoreEnabled(),
1199  'highscore_achieved_ts' => (int) $this->getHighscoreAchievedTS(),
1200  'highscore_percentage' => (int) $this->getHighscorePercentage(),
1201  'highscore_wtime' => (int) $this->getHighscoreWTime(),
1202  'highscore_own_table' => (int) $this->getHighscoreOwnTable(),
1203  'highscore_top_table' => (int) $this->getHighscoreTopTable(),
1204  'highscore_top_num' => $this->getHighscoreTopNum(),
1205  'only_moveon' => (int) $this->getOnlyMoveon(),
1206  'achieved' => (int) $this->getAchieved(),
1207  'answered' => (int) $this->getAnswered(),
1208  'completed' => (int) $this->getCompleted(),
1209  'failed' => (int) $this->getFailed(),
1210  'initialized' => (int) $this->getInitialized(),
1211  'passed' => (int) $this->getPassed(),
1212  'progressed' => (int) $this->getProgressed(),
1213  'satisfied' => (int) $this->getSatisfied(),
1214  'c_terminated' => (int) $this->getTerminated(),
1215  'hide_data' => (int) $this->getHideData(),
1216  'c_timestamp' => (int) $this->getTimestamp(),
1217  'duration' => (int) $this->getDuration(),
1218  'no_substatements' => (int) $this->getNoSubstatements()
1219  //'bypass_proxy' => (int) $this->isBypassProxyEnabled()
1220  ];
1221  }
1222 
1228  protected function doCloneObject(ilObject2 $new_obj, int $a_target_id, ?int $a_copy_id = null): void
1229  {
1230  assert($new_obj instanceof ilObjCmiXapi);
1231 
1232  $this->cloneMetaData($new_obj);
1233 
1234  $new_obj->setLrsTypeId($this->getLrsTypeId());
1235  $new_obj->setContentType($this->getContentType());
1236  $new_obj->setSourceType($this->getSourceType());
1237  $new_obj->setActivityId($this->getActivityId());
1238  $new_obj->setPublisherId($this->getPublisherId());
1239  $new_obj->setInstructions($this->getInstructions());
1240  $new_obj->setLaunchUrl($this->getLaunchUrl());
1241  $new_obj->setLaunchParameters($this->getLaunchParameters());
1242  $new_obj->setMoveOn($this->getMoveOn());
1243  $new_obj->setEntitlementKey($this->getEntitlementKey());
1244  $new_obj->setAuthFetchUrlEnabled($this->isAuthFetchUrlEnabled());
1245  $new_obj->setLaunchMethod($this->getLaunchMethod());
1246  $new_obj->setLaunchMode($this->getLaunchMode());
1247  $new_obj->setSwitchToReviewEnabled($this->isSwitchToReviewEnabled());
1248  $new_obj->setMasteryScore($this->getMasteryScore());
1249  $new_obj->setKeepLpStatusEnabled($this->isKeepLpStatusEnabled());
1250  $new_obj->setPrivacyIdent($this->getPrivacyIdent());
1251  $new_obj->setPrivacyName($this->getPrivacyName());
1252  $new_obj->setUserPrivacyComment($this->getUserPrivacyComment());
1253  $new_obj->setStatementsReportEnabled($this->isStatementsReportEnabled());
1254  $new_obj->setXmlManifest($this->getXmlManifest());
1255  $new_obj->setVersion($this->getVersion());
1256  $new_obj->setHighscoreEnabled($this->getHighscoreEnabled());
1257  $new_obj->setHighscoreAchievedTS($this->getHighscoreAchievedTS());
1258  $new_obj->setHighscorePercentage($this->getHighscorePercentage());
1259  $new_obj->setHighscoreWTime($this->getHighscoreWTime());
1260  $new_obj->setHighscoreOwnTable($this->getHighscoreOwnTable());
1261  $new_obj->setHighscoreTopTable($this->getHighscoreTopTable());
1262  $new_obj->setHighscoreTopNum($this->getHighscoreTopNum());
1263  $new_obj->setBypassProxyEnabled($this->isBypassProxyEnabled());
1264  $new_obj->setOnlyMoveon($this->getOnlyMoveon());
1265  $new_obj->setAchieved($this->getAchieved());
1266  $new_obj->setAnswered($this->getAnswered());
1267  $new_obj->setCompleted($this->getCompleted());
1268  $new_obj->setFailed($this->getFailed());
1269  $new_obj->setInitialized($this->getInitialized());
1270  $new_obj->setPassed($this->getPassed());
1271  $new_obj->setProgressed($this->getProgressed());
1272  $new_obj->setSatisfied($this->getSatisfied());
1273  $new_obj->setTerminated($this->getTerminated());
1274  $new_obj->setHideData($this->getHideData());
1275  $new_obj->setTimestamp($this->getTimestamp());
1276  $new_obj->setDuration($this->getDuration());
1277  $new_obj->setNoSubstatements($this->getNoSubstatements());
1278  $new_obj->update();
1279 
1280  if ($this->getSourceType() == self::SRC_TYPE_LOCAL) {
1281  $dirUtil = new ilCmiXapiContentUploadImporter($new_obj);
1282  $dirUtil->ensureCreatedObjectDirectory();
1283  $newDir = implode(DIRECTORY_SEPARATOR, [ilFileUtils::getWebspaceDir(), $dirUtil->getWebDataDirRelativeObjectDirectory()]);
1284  $dirUtil = new ilCmiXapiContentUploadImporter($this);
1285  $thisDir = implode(DIRECTORY_SEPARATOR, [ilFileUtils::getWebspaceDir(), $dirUtil->getWebDataDirRelativeObjectDirectory()]);
1286  ilFileUtils::rCopy($thisDir, $newDir);
1287  }
1288  }
1289 
1290  protected function doDelete(): void
1291  {
1292  // delete file data entry
1293  $query = "DELETE FROM " . self::DB_TABLE_NAME . " WHERE obj_id = " . $this->database->quote($this->getId(), 'integer');
1294  $this->database->manipulate($query);
1296 
1297  // delete entire directory and its content
1298  $dirUtil = new ilCmiXapiContentUploadImporter($this);
1299  $thisDir = implode(DIRECTORY_SEPARATOR, [ilFileUtils::getWebspaceDir(), $dirUtil->getWebDataDirRelativeObjectDirectory()]);
1300  if (is_dir($thisDir)) {
1301  ilFileUtils::delDir($thisDir);
1302  }
1303 
1304  // delete meta data
1305  $this->deleteMetaData();
1306 
1307  //delete results
1308  $query = "DELETE FROM " . self::DB_RESULTS_TABLE_NAME .
1309  " WHERE obj_id = " . $this->database->quote($this->getId(), 'integer');
1310  $this->database->manipulate($query);
1311 
1312  // TODO check xapidel
1313  }
1314 
1315  // /**
1316  // * @return string[]
1317  // */
1318  // public function getRegistrations() : array
1319  // {
1320  // global $DIC;
1321  // $res = $DIC->database()->queryF(
1322  // "SELECT DISTINCT registration FROM " . self::DB_USERS_TABLE_NAME . " WHERE obj_id = %s",
1323  // array('text'),
1324  // array($this->getId())
1325  // );
1326  // $ret = [];
1327  // while ($row = $DIC->database()->fetchAssoc($res)) {
1328  // $ret[] = (string) $row['registration'];
1329  // }
1330  // return $ret;
1331  // }
1332 
1336  public static function guidv4(?string $data = null): string
1337  {
1338  // Generate 16 bytes (128 bits) of random data or use the data passed into the function.
1339  $data ??= random_bytes(16);
1340  assert(strlen($data) == 16);
1341 
1342  // Set version to 0100
1343  $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
1344  // Set bits 6-7 to 10
1345  $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
1346 
1347  // Output the 36 character UUID.
1348  return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
1349  }
1350 
1351  public function getCurrentCmixUser(): \ilCmiXapiUser
1352  {
1353  global $DIC;
1354  if (null === $this->currentCmixUser) {
1355  $this->currentCmixUser = new ilCmiXapiUser($this->getId(), $DIC->user()->getId(), $this->getPrivacyIdent());
1356  }
1357  return $this->currentCmixUser;
1358  }
1359 
1363  public function getSessionId(?ilCmiXapiUser $cmixUser = null): string
1364  {
1365  if (null === $cmixUser) {
1366  $cmixUser = $this->getCurrentCmixUser();
1367  }
1368  return ilCmiXapiAuthToken::getCmi5SessionByUsrIdAndObjIdAndRefId($cmixUser->getUsrId(), $this->getId(), $this->getRefId());
1369  }
1370 
1375  public function getLaunchData(?ilCmiXapiUser $cmixUser = null, string $lang = 'en', ?int $launchedByRefId = null): array
1376  {
1377  global $DIC;
1378  if (null === $cmixUser) {
1379  $cmixUser = $this->getCurrentCmixUser();
1380  }
1381  // ToDo
1382  $moveOn = $this->getLMSMoveOn();
1383  if (!$moveOn || $moveOn == '') {
1384  $moveOn = 'Completed';
1385  }
1386  $launchMode = $this->getLaunchMode();
1387  // only check switch if self::LAUNCH_MODE_NORMAL
1388  if ($launchMode == self::LAUNCH_MODE_NORMAL) {
1389  if ($cmixUser->getSatisfied() && $this->isSwitchToReviewEnabled()) {
1390  $launchMode = self::LAUNCH_MODE_REVIEW;
1391  }
1392  }
1393  $ctxTemplate = [
1394  "contextTemplate" => $this->getLaunchedContextTemplate($cmixUser),
1395  "launchMode" => ucfirst($launchMode),
1396  "launchMethod" => "OwnWindow",
1397  "moveOn" => $moveOn
1398  ];
1399  $lmsLaunchMethod = $this->getLaunchMethod();
1400  if ($lmsLaunchMethod === "ownWin") {
1401  if (is_int($launchedByRefId)) {
1402  $href = ilLink::_getStaticLink(
1403  $launchedByRefId,
1405  );
1406  } else {
1407  $href = ilLink::_getStaticLink(
1408  $this->getRefId(),
1409  $this->getType()
1410  );
1411  }
1412  $ctxTemplate['returnURL'] = $href;
1413  } else {
1414  $ctxTemplate['returnURL'] = ILIAS_HTTP_PATH . "/Modules/CmiXapi/xapiexit.php?lang={$lang}";
1415  }
1416  if (!empty($this->getMasteryScore())) {
1417  $ctxTemplate['masteryScore'] = $this->getMasteryScore();
1418  }
1419  if (!empty($this->getLaunchParameters())) {
1420  $ctxTemplate['launchParameters'] = $this->getLaunchParameters();
1421  }
1422  if (!empty($this->getEntitlementKey())) {
1423  $ctxTemplate['entitlementKey'] = array("courseStructure" => $this->getEntitlementKey());
1424  }
1425  return $ctxTemplate;
1426  }
1427 
1431  public function getLaunchedContextTemplate(?ilCmiXapiUser $cmixUser = null): array
1432  {
1433  if (null === $cmixUser) {
1434  $cmixUser = $this->getCurrentCmixUser();
1435  }
1436  $launchMode = $this->getLaunchMode();
1437  // only check switch if self::LAUNCH_MODE_NORMAL
1438  if ($launchMode == self::LAUNCH_MODE_NORMAL) {
1439  if ($cmixUser->getSatisfied() && $this->isSwitchToReviewEnabled()) {
1440  $launchMode = self::LAUNCH_MODE_REVIEW;
1441  }
1442  }
1443  $extensions = $this->getStatementExtensions($cmixUser);
1444  $extensions['https://w3id.org/xapi/cmi5/context/extensions/launchmode'] = $launchMode;
1445  if (!empty($this->getLMSMoveOn())) {
1446  $extensions['https://w3id.org/xapi/cmi5/context/extensions/moveon'] = $this->getLMSMoveOn();
1447  }
1448  if (!empty($this->getLaunchParameters())) {
1449  $extensions['https://w3id.org/xapi/cmi5/context/extensions/launchparameters'] = $this->getLaunchParameters();
1450  }
1451  if (!empty($this->getMasteryScore())) {
1452  $extensions['https://w3id.org/xapi/cmi5/context/extensions/masteryscore'] = $this->getMasteryScore();
1453  }
1454  return array(
1455  "contextActivities" => $this->getStatementContextActivities(),
1456  "extensions" => $extensions
1457  );
1458  }
1459 
1465  public function getStatement(string $verb, ?ilCmiXapiUser $cmixUser = null): array
1466  {
1467  if (null === $cmixUser) {
1468  $cmixUser = $this->getCurrentCmixUser();
1469  }
1470  $id = self::guidv4();
1471  $actor = $this->getStatementActor($cmixUser);
1472  $verbUri = ilCmiXapiVerbList::getInstance()->getVerbUri($verb);
1473  $extensions = $this->getStatementExtensions($cmixUser);
1474  $registration = $cmixUser->getRegistration();
1475  $contextActivities = $this->getStatementContextActivities();
1476  $object = $this->getStatementObject();
1477  return array(
1478  'id' => $id,
1479  'actor' => $actor,
1480  'verb' =>
1481  array(
1482  'id' => $verbUri
1483  ),
1484  'context' =>
1485  array(
1486  'extensions' => $extensions,
1487  'registration' => $registration,
1488  'contextActivities' => $contextActivities
1489  ),
1490  'object' => $object
1491  );
1492  }
1493 
1498  public function getStatementActor(?ilCmiXapiUser $cmixUser = null): array
1499  {
1500  global $DIC;
1501  if (null === $cmixUser) {
1502  $cmixUser = $this->getCurrentCmixUser();
1503  }
1504  $user = new ilObjUser($cmixUser->getUsrId()); // ToDo: Caching Names
1506  if ($name == '') {
1507  $this->log()->error('error: no name in cmixuser');
1508  $name = 'UNDEFINED';
1509  }
1510  $homePage = ($this->anonymousHomePage == true) ? self::ANONYMOUS_HOMEPAGE : self::iliasUrl();
1511  if ($this->getContentType() == self::CONT_TYPE_CMI5) {
1512  $actor = [
1513  'objectType' => 'Agent',
1514  'account' => [
1515  'homePage' => $homePage,
1516  'name' => $cmixUser->getUsrIdent()
1517  ]
1518  ];
1519  if ($name !== '') {
1520  $actor['name'] = $name;
1521  }
1522  } else {
1523  $actor = [
1524  'objectType' => 'Agent',
1525  'mbox' => 'mailto:' . $cmixUser->getUsrIdent()
1526  ];
1527  if ($name !== '') {
1528  $actor['name'] = $name;
1529  }
1530  }
1531  return $actor;
1532  }
1533 
1539  public function getStatementExtensions(?ilCmiXapiUser $cmixUser = null): array
1540  {
1541  if (null === $cmixUser) {
1542  $cmixUser = $this->getCurrentCmixUser();
1543  }
1544  return array(
1545  'https://w3id.org/xapi/cmi5/context/extensions/sessionid' => $this->getSessionId($cmixUser),
1546  'https://ilias.de/cmi5/activityid' => $this->getActivityId()
1547  );
1548  }
1549 
1554  public function getStatementContextActivities(): array
1555  {
1556  $publisherId = $this->getPublisherId();
1557  $activityId = $this->getActivityId();
1558  if (empty($publisherId)) {
1559  $publisherId = $activityId;
1560  }
1561  return array(
1562  "grouping" => [
1563  [
1564  "objectType" => "Activity",
1565  "id" => "{$publisherId}",
1566  'definition' =>
1567  array(
1568  'name' =>
1569  array(
1570  'de-DE' => $this->getTitle(),
1571  'en-US' => $this->getTitle()
1572  ),
1573  'description' =>
1574  array(
1575  'de-DE' => $this->getDescription(),
1576  'en-US' => $this->getDescription()
1577  )
1578  )]
1579  ],
1580  "category" => [
1581  [
1582  "id" => "https://w3id.org/xapi/cmi5/context/categories/cmi5",
1583  "objectType" => "Activity"
1584  ]
1585  ]
1586  );
1587  }
1588 
1592  public function getStatementObject(): array
1593  {
1594  return array(
1595  'id' => $this->getActivityId(),
1596  'definition' =>
1597  array(
1598  'name' =>
1599  array(
1600  'de-DE' => $this->getTitle(),
1601  'en-US' => $this->getTitle()
1602  ),
1603  'description' =>
1604  array(
1605  'de-DE' => $this->getDescription(),
1606  'en-US' => $this->getDescription()
1607  )
1608  )
1609  );
1610  }
1611 
1616  public function getLaunchedStatement(?ilCmiXapiUser $cmixUser = null): array
1617  {
1618  if (null === $cmixUser) {
1619  $cmixUser = $this->getCurrentCmixUser();
1620  }
1621  $launchMode = $this->getLaunchMode();
1622  // only check switch if self::LAUNCH_MODE_NORMAL
1623  if ($launchMode == self::LAUNCH_MODE_NORMAL) {
1624  if ($cmixUser->getSatisfied() && $this->isSwitchToReviewEnabled()) {
1625  $launchMode = self::LAUNCH_MODE_REVIEW;
1626  }
1627  }
1628 
1629  $statement = $this->getStatement('launched', $cmixUser);
1630  $statement['context']['extensions']['https://w3id.org/xapi/cmi5/context/extensions/launchmode'] = $launchMode;
1631  if (!empty($this->getLMSMoveOn())) {
1632  $statement['context']['extensions']['https://w3id.org/xapi/cmi5/context/extensions/moveon'] = $this->getLMSMoveOn();
1633  }
1634  if (!empty($this->getLaunchParameters())) {
1635  $statement['context']['extensions']['https://w3id.org/xapi/cmi5/context/extensions/launchparameters'] = $this->getLaunchParameters();
1636  }
1637  if (!empty($this->getMasteryScore())) {
1638  $statement['context']['extensions']['https://w3id.org/xapi/cmi5/context/extensions/masteryscore'] = $this->getMasteryScore();
1639  }
1640  return $statement;
1641  }
1642 
1647  public function getAbandonedStatement(?string $sessionId, ?string $duration, ?ilCmiXapiUser $cmixUser = null): array
1648  {
1649  if (null === $cmixUser) {
1650  $cmixUser = $this->getCurrentCmixUser();
1651  }
1652  $statement = $this->getStatement('abandoned', $cmixUser);
1653  // overwrite session with abandoned oldSession
1654  $statement['context']['extensions']['https://w3id.org/xapi/cmi5/context/extensions/sessionid'] = $sessionId;
1655  $statement['result'] = array(
1656  'duration' => $duration
1657  );
1658  return $statement;
1659  }
1660 
1665  public function getSatisfiedStatement(?ilCmiXapiUser $cmixUser = null): array
1666  {
1667  if (null === $cmixUser) {
1668  $cmixUser = $this->getCurrentCmixUser();
1669  }
1670  $statement = $this->getStatement('satisfied', $cmixUser);
1671  // add type, see https://aicc.github.io/CMI-5_Spec_Current/samples/scenarios/16-not_applicable-no_launch/#satisfied-statement
1672  // see also: https://github.com/AICC/CMI-5_Spec_Current/blob/quartz/cmi5_spec.md#verbs_satisfied
1673  $type = "https://w3id.org/xapi/cmi5/activitytype/course";
1674  $statement['object']['definition']['type'] = $type;
1675  $statement['context']['contextActivities']['grouping'][0]['definition']['type'] = $type;
1676  return $statement;
1677  }
1678 
1683  public function getLastStatement(string $sess)
1684  {
1685  global $DIC;
1686  $lrsType = $this->getLrsType();
1687 
1688  //$this->getLrsEndpoint())) . '/api/' . self::ENDPOINT_AGGREGATE_SUFFIX;
1689  $defaultLrs = $lrsType->getLrsEndpointStatementsAggregationLink();
1690  //$fallbackLrs = $lrsType->getLrsFallbackEndpoint();
1691  $defaultBasicAuth = $lrsType->getBasicAuth();
1692  //$fallbackBasicAuth = $lrsType->getFallbackBasicAuth();
1693  $defaultHeaders = [
1694  'X-Experience-API-Version' => '1.0.3',
1695  'Authorization' => $defaultBasicAuth,
1696  'Cache-Control' => 'no-cache, no-store, must-revalidate'
1697  ];
1698  /*
1699  $fallbackHeaders = [
1700  'X-Experience-API-Version' => '1.0.3',
1701  'Authorization' => $fallbackBasicAuth,
1702  'Content-Type' => 'application/json;charset=utf-8',
1703  'Cache-Control' => 'no-cache, no-store, must-revalidate'
1704  ];
1705  */
1706  $pipeline = json_encode($this->getLastStatementPipline($sess));
1707  $defaultLastStatementUrl = $defaultLrs . "?pipeline=" . urlencode($pipeline);
1708  $client = new GuzzleHttp\Client();
1709  $req_opts = array(
1710  GuzzleHttp\RequestOptions::VERIFY => true,
1711  GuzzleHttp\RequestOptions::CONNECT_TIMEOUT => 10,
1712  GuzzleHttp\RequestOptions::HTTP_ERRORS => false
1713  );
1714  $defaultLastStatementRequest = new GuzzleHttp\Psr7\Request(
1715  'GET',
1716  $defaultLastStatementUrl,
1717  $defaultHeaders
1718  );
1719  $promises = array();
1720  $promises['defaultLastStatement'] = $client->sendAsync($defaultLastStatementRequest, $req_opts);
1721  try {
1722  $responses = GuzzleHttp\Promise\Utils::settle($promises)->wait();
1723  $body = '';
1724  ilCmiXapiAbstractRequest::checkResponse($responses['defaultLastStatement'], $body, [200]);
1725  return json_decode($body, (bool) JSON_OBJECT_AS_ARRAY);
1726  } catch (Exception $e) {
1727  $this->log()->error('error:' . $e->getMessage());
1728  return null;
1729  }
1730  }
1731 
1735  public function getLastStatementPipline(string $sess): array
1736  {
1737  global $DIC;
1738  $pipeline = array();
1739 
1740  // filter activityId
1741  $match = array();
1742  $match['statement.object.objectType'] = 'Activity';
1743  $match['statement.actor.objectType'] = 'Agent';
1744 
1745  $activityId = array();
1746 
1747  if ($this->getContentType() == ilObjCmiXapi::CONT_TYPE_CMI5 && !$this->isMixedContentType()) {
1748  // https://github.com/AICC/CMI-5_Spec_Current/blob/quartz/cmi5_spec.md#963-extensions
1749  $activityId['statement.context.extensions.https://ilias&46;de/cmi5/activityid'] = $this->getActivityId();
1750  } else {
1751  $activityQuery = [
1752  '$regex' => '^' . preg_quote($this->getActivityId()) . ''
1753  ];
1754  $activityId['$or'] = [];
1755  $activityId['$or'][] = ['statement.object.id' => $activityQuery];
1756  $activityId['$or'][] = ['statement.context.contextActivities.parent.id' => $activityQuery];
1757  }
1758 
1759  $sessionId = array();
1760  $sessionId['statement.context.extensions.https://w3id&46;org/xapi/cmi5/context/extensions/sessionid'] = $sess;
1761  $match['$and'] = array();
1762  $match['$and'][] = $activityId;
1763  $match['$and'][] = $sessionId;
1764  $sort = array('statement.timestamp' => -1);
1765  $project = array('statement.timestamp' => 1, 'statement.verb.id' => 1);
1766  $pipeline[] = array('$match' => $match);
1767  $pipeline[] = array('$sort' => $sort);
1768  $pipeline[] = array('$limit' => 1);
1769  $pipeline[] = array('$project' => $project);
1770 
1771  return $pipeline;
1772  }
1773 
1774  public static function iliasUrl(): string
1775  {
1776  global $DIC;
1777  $regex = '/^(https?:\/\/[^\/]+).*/';
1778  preg_match($regex, (string) $DIC->http()->request()->getUri(), $request_parts);
1779  return $request_parts[1];
1780  }
1781 
1782  public static function log(): ilLogger
1783  {
1784  if (self::PLUGIN) {
1785  global $log;
1786  return $log;
1787  } else {
1788  return \ilLoggerFactory::getLogger('cmix');
1789  }
1790  }
1791 
1792 
1793  public function isActivationLimited(): ?bool
1794  {
1795  return $this->activationLimited;
1796  }
1797 
1798 
1799  public function setActivationLimited(bool $activationLimited): void
1800  {
1801  $this->activationLimited = $activationLimited;
1802  }
1803 
1804  public function getActivationStartingTime(): ?int
1805  {
1807  }
1808 
1809  public function setActivationStartingTime(?int $activationStartingTime = null): void
1810  {
1811  $this->activationStartingTime = $activationStartingTime;
1812  }
1813 
1814  public function getActivationEndingTime(): ?int
1815  {
1817  }
1818 
1819  public function setActivationEndingTime(?int $activationEndingTime = null): void
1820  {
1821  $this->activationEndingTime = $activationEndingTime;
1822  }
1823 
1824  public function getActivationVisibility(): ?bool
1825  {
1827  }
1828 
1829  public function setActivationVisibility(bool $activationVisibility): void
1830  {
1831  $this->activationVisibility = $activationVisibility;
1832  }
1833 }
getAbandonedStatement(?string $sessionId, ?string $duration, ?ilCmiXapiUser $cmixUser=null)
static getWebspaceDir(string $mode="filesystem")
get webspace directory
setDuration(bool $duration)
setPublisherId(string $publisherId)
doCloneObject(ilObject2 $new_obj, int $a_target_id, ?int $a_copy_id=null)
setPrivacyIdent(int $userIdent)
setContentType(string $contentType)
$res
Definition: ltiservices.php:69
string $type
const PRIVACY_IDENT_IL_UUID_SHA256URL
setXmlManifest(string $xmlManifest)
getSatisfiedStatement(?ilCmiXapiUser $cmixUser=null)
static getObjectsHavingBypassProxyEnabledAndRegisteredUsers()
getLaunchedContextTemplate(?ilCmiXapiUser $cmixUser=null)
getHighscoreTopTable()
Gets, if the top-rankings table should be shown.
bool $activationLimited
repository object activation settings (handled by ilObject)
const HIGHSCORE_SHOW_OWN_TABLE
cloneMetaData(ilObject $target_obj)
getStatementExtensions(?ilCmiXapiUser $cmixUser=null)
Minimal extensions.
setEntitlementKey(string $entitlementKey)
getHighscoreWTime()
Gets if the column with the workingtime should be shown.
static getCmi5SessionByUsrIdAndObjIdAndRefId(int $usrId, int $objId, ?int $refId=null)
const MOVEON_NOT_APPLICABLE
setActivationStartingTime(?int $activationStartingTime=null)
setNoSubstatements(bool $no_substatements)
setOnlyMoveon(bool $only_moveon)
setAchieved(bool $achieved)
setSourceType(string $sourceType)
setHighscoreWTime(bool $a_wtime)
Sets if the workingtime of the scores should be shown.
setAuthFetchUrlEnabled(bool $authFetchUrlEnabled)
getSessionId(?ilCmiXapiUser $cmixUser=null)
setHighscoreOwnTable(bool $a_own_table)
Sets if the table with the own ranking should be shown.
static getInstance(int $a_id=0, bool $a_reference=true)
static rCopy(string $a_sdir, string $a_tdir, bool $preserveTimeAttributes=false)
Copies content of a directory $a_sdir recursively to a directory $a_tdir.
getLastStatement(string $sess)
get latest statement from session
setKeepLpStatusEnabled(bool $keepLpStatusEnabled)
setLaunchMode(string $launchMode)
const PRIVACY_IDENT_IL_UUID_RANDOM
static updatePrivacySettingsFromLrsType(ilCmiXapiLrsType $lrsType)
bool $_highscore_enabled
HIGHSCORE.
getHighscoreTopNum(?int $a_retval=10)
Gets the number of entries which are to be shown in the top-rankings table.
const PRIVACY_IDENT_IL_UUID_EXT_ACCOUNT
setSwitchToReviewEnabled(bool $switchToReviewEnabled)
getHighscoreOwnTable()
Gets if the own rankings table should be shown.
static _lookupObjId(int $ref_id)
getMoveOn()
Attention: this is the original imported moveOn for using in LaunchData and LaunchStatement use getLM...
const PRIVACY_IDENT_REAL_EMAIL
setBypassProxyEnabled(bool $bypassProxyEnabled)
ilDBInterface $database
global $DIC
Definition: feed.php:28
$client
setLaunchMethod(string $launchMethod)
if($format !==null) $name
Definition: metadata.php:247
ilCmiXapiLrsType $lrsType
setTerminated(bool $terminated)
const PRIVACY_IDENT_IL_UUID_SHA256
setFailed(bool $failed)
static _removeEntriesForObject(int $a_obj_id)
remove all history entries for an object
setActivityId(string $activityId)
getLPMode()
only for internal LMS usage
setMasteryScore(float $masteryScore)
setHighscoreMode(int $mode)
setPassed(bool $passed)
getHighscoreEnabled()
Gets the setting which determines if the highscore feature is enabled.
getStatementActor(?ilCmiXapiUser $cmixUser=null)
statement actor
setCompleted(bool $completed)
setHighscorePercentage(bool $a_percentage)
Sets if the percentages of the scores pass should be shown.
static delDir(string $a_dir, bool $a_clean_only=false)
removes a dir and all its content (subdirs and files) recursively
getLastStatementPipline(string $sess)
setMoveOn(string $moveOn)
Attention: this is the original moveOn from course import should only be set on import! ...
setStatementsReportEnabled(bool $statementsReportEnabled)
getLaunchData(?ilCmiXapiUser $cmixUser=null, string $lang='en', ?int $launchedByRefId=null)
LMS.LaunchData.
$query
__construct(int $a_id=0, bool $a_reference=true)
ilObjCmiXapi constructor.
getLaunchedStatement(?ilCmiXapiUser $cmixUser=null)
const PRIVACY_IDENT_IL_UUID_LOGIN
setInitialized(bool $initialized)
setVersion(int $version)
const PRIVACY_IDENT_IL_UUID_USER_ID
setProgressed(bool $progressed)
static guidv4(?string $data=null)
const HIGHSCORE_SHOW_ALL_TABLES
setUserPrivacyComment(string $userPrivacyComment)
$lang
Definition: xapiexit.php:26
setSatisfied(bool $satisfied)
setActivationLimited(bool $activationLimited)
static getItem(int $ref_id)
setPrivacyName(int $userName)
setLaunchParameters(string $launchParameters)
getStatement(string $verb, ?ilCmiXapiUser $cmixUser=null)
blueprint statement
__construct(Container $dic, ilPlugin $plugin)
getLMSMoveOn()
for CMI5 statements | state moveOn values
setTimestamp(bool $timestamp)
const LP_MODE_CMIX_COMPL_OR_PASSED_WITH_FAILED
setAnswered(bool $answered)
getStatementContextActivities()
Minimal statementActivities.
getHighscorePercentage()
Gets if the percentage column should be shown.
setLaunchUrl(string $launchUrl)
setMasteryScorePercent(float $masteryScorePercent)
ilLogger $log
setLrsType(\ilCmiXapiLrsType $lrsType)
setHideData(bool $hide_data)
static updateByPassProxyFromLrsType(ilCmiXapiLrsType $lrsType)
setHighscoreAchievedTS(bool $a_achieved_ts)
Sets if the date and time of the scores achievement should be displayed.
static _lookupType(int $id, bool $reference=false)
const HIGHSCORE_SHOW_TOP_TABLE
setActivationEndingTime(?int $activationEndingTime=null)
static getInstance(int $obj_id)
const MOVEON_COMPLETED_OR_PASSED
Class ilObjectActivation.
setInstructions(string $instructions)
setHighscoreTopTable(bool $a_top_table)
Sets if the top-rankings table should be shown.
static checkResponse(array $response, &$body, array $allowedStatus=[200, 204])
setLrsTypeId(int $lrsTypeId)
setActivationVisibility(bool $activationVisibility)
getHighscoreAchievedTS()
Returns if date and time of the scores achievement should be displayed.
static getName(int $userNameMode, ilObjUser $user)
ilObjUser $user
ilCmiXapiUser $currentCmixUser
setHighscoreEnabled(bool $a_enabled)
Sets if the highscore feature should be enabled.
setHighscoreTopNum(int $a_top_num)
Sets the number of entries which are to be shown in the top-rankings table.