ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilTestSession.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
31{
32 public const ACCESS_CODE_SESSION_INDEX = "tst_access_code";
33 public const ACCESS_CODE_CHAR_DOMAIN = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
34 public const ACCESS_CODE_LENGTH = 5;
35
36 private int $ref_id = 0;
37 private int $pass = 0;
38 public int $active_id = 0;
39 public int $user_id = 0;
40 public string $anonymous_id = '';
41 public int $test_id = 0;
42 public int $lastsequence = 0;
43 protected ?string $lastPresentationMode = null;
44 public bool $submitted = false;
45 public int $tstamp = 0;
46 public string $submittedTimestamp = '';
47 private ?int $objectiveOrientedContainerId = null;
48
49 private ?int $lastFinishedPass = null;
50 private ?int $lastStartedPass = null;
51
52
61 public function __construct(
62 protected ilDBInterface $db,
63 protected ilObjUser $user
64 ) {
65 }
66
67 public function setRefId(int $ref_id): void
68 {
69 $this->ref_id = $ref_id;
70 }
71
72 public function getRefId(): int
73 {
74 return $this->ref_id;
75 }
76
77 protected function activeIDExists(int $user_id, int $test_id): bool
78 {
79 if ($this->user->getId() === ANONYMOUS_USER_ID) {
80 return false;
81 }
82
83 $result = $this->db->queryF(
84 "SELECT * FROM tst_active WHERE user_fi = %s AND test_fi = %s",
85 ['integer','integer'],
87 );
88 if ($result->numRows()) {
89 $row = $this->db->fetchAssoc($result);
90 $this->active_id = (int) $row["active_id"];
91 $this->user_id = (int) $row["user_fi"];
92 $this->anonymous_id = $row["anonymous_id"] ?? '';
93 $this->test_id = (int) $row["test_fi"];
94 $this->lastsequence = (int) $row["lastindex"];
95 $this->pass = (int) $row["tries"];
96 $this->submitted = ($row["submitted"]) ? true : false;
97 $this->submittedTimestamp = (string) $row["submittimestamp"];
98 $this->tstamp = (int) $row["tstamp"];
99
100 $this->lastStartedPass = $row['last_started_pass'];
101 $this->lastFinishedPass = $row['last_finished_pass'];
102 $this->objectiveOrientedContainerId = $row['objective_container'];
103
104 return true;
105 }
106 return false;
107 }
108
109 public function increaseTestPass(): void
110 {
111 if (!$this->active_id) {
112 throw new ilTestException('missing active id on test pass increase!');
113 }
114
115 $this->increasePass();
116 $this->setLastSequence(0);
117 $submitted = ($this->isSubmitted()) ? 1 : 0;
118 $active = ilSession::get((string) $this->active_id);
119 if (!isset($active['tst_last_increase_pass']) || $active['tst_last_increase_pass'] !== null) {
120 $active['tst_last_increase_pass'] = 0;
121 }
122
123 // there has to be at least 10 seconds between new test passes (to ensure that noone double clicks the finish button and increases the test pass by more than 1)
124 if (time() - $active['tst_last_increase_pass'] > 10) {
125 $active['tst_last_increase_pass'] = time();
126 $this->tstamp = time();
127 $submittedtimestamp = $this->getSubmittedTimestamp() !== null && $this->getSubmittedTimestamp() !== '' ? $this->getSubmittedTimestamp() : null;
128 $this->db->update(
129 'tst_active',
130 [
131 'lastindex' => ['integer', $this->getLastSequence()],
132 'tries' => ['integer', $this->getPass()],
133 'submitted' => ['integer', $submitted],
134 'submittimestamp' => ['timestamp', $submittedtimestamp],
135 'tstamp' => ['integer', time()],
136 'last_finished_pass' => ['integer', $this->getLastFinishedPass()],
137 'last_started_pass' => ['integer', $this->getLastStartedPass()],
138 'objective_container' => ['integer', $this->getObjectiveOrientedContainerId()]
139 ],
140 [
141 'active_id' => ['integer', $this->getActiveId()]
142 ]
143 );
144 }
145 }
146
147 public function saveToDb(): void
148 {
149 $submitted = ($this->isSubmitted()) ? 1 : 0;
150 $submittedtimestamp = $this->getSubmittedTimestamp() !== null && $this->getSubmittedTimestamp() !== '' ? $this->getSubmittedTimestamp() : null;
151 if ($this->active_id > 0) {
152 $this->db->update(
153 'tst_active',
154 [
155 'lastindex' => ['integer', $this->getLastSequence()],
156 'tries' => ['integer', $this->getPass()],
157 'submitted' => ['integer', $submitted],
158 'submittimestamp' => ['timestamp', $submittedtimestamp],
159 'tstamp' => ['integer', time() - 10],
160 'last_finished_pass' => ['integer', $this->getLastFinishedPass()],
161 'last_started_pass' => ['integer', $this->getPass()],
162 'objective_container' => ['integer', $this->getObjectiveOrientedContainerId()]
163 ],
164 [
165 'active_id' => ['integer', $this->getActiveId()]
166 ]
167 );
168 return;
169 }
170
171 if (!$this->activeIDExists($this->getUserId(), $this->getTestId())) {
172 $anonymous_id = $this->getAnonymousId() ?: null;
173
174 $next_id = $this->db->nextId('tst_active');
175 $this->db->insert(
176 'tst_active',
177 [
178 'active_id' => ['integer', $next_id],
179 'user_fi' => ['integer', $this->getUserId()],
180 'anonymous_id' => ['text', $anonymous_id],
181 'test_fi' => ['integer', $this->getTestId()],
182 'lastindex' => ['integer', $this->getLastSequence()],
183 'tries' => ['integer', $this->getPass()],
184 'submitted' => ['integer', $submitted],
185 'submittimestamp' => ['timestamp', (strlen($this->getSubmittedTimestamp())) ? $this->getSubmittedTimestamp() : null],
186 'tstamp' => ['integer', time() - 10],
187 'last_finished_pass' => ['integer', $this->getLastFinishedPass()],
188 'last_started_pass' => ['integer', $this->getPass()],
189 'objective_container' => ['integer', $this->getObjectiveOrientedContainerId()]
190 ]
191 );
192 $this->active_id = $next_id;
193 }
194 }
195
196 public function loadTestSession(int $test_id, int $user_id = 0, ?string $anonymous_id = null): void
197 {
198 if ($user_id === 0) {
199 $user_id = $this->user->getId();
200 }
201 if (($this->user->getId() == ANONYMOUS_USER_ID) && $this->doesAccessCodeInSessionExists()) {
202 $result = $this->db->queryF(
203 "SELECT * FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
204 ['integer','integer','text'],
206 );
207 } elseif ($anonymous_id !== null) {
208 $result = $this->db->queryF(
209 "SELECT * FROM tst_active WHERE user_fi = %s AND test_fi = %s AND anonymous_id = %s",
210 ['integer','integer','text'],
212 );
213 } else {
214 if ($this->user->getId() == ANONYMOUS_USER_ID) {
215 return;
216 }
217 $result = $this->db->queryF(
218 "SELECT * FROM tst_active WHERE user_fi = %s AND test_fi = %s",
219 ['integer','integer'],
221 );
222 }
223
224 // TODO bheyser: Refactor
225 $this->user_id = $user_id;
226
227 if ($result->numRows()) {
228 $row = $this->db->fetchAssoc($result);
229 $this->active_id = $row["active_id"];
230 $this->user_id = $row["user_fi"];
231 $this->anonymous_id = $row["anonymous_id"] ?? '';
232 $this->test_id = $row["test_fi"];
233 $this->lastsequence = $row["lastindex"];
234 $this->pass = $row["tries"];
235 $this->submitted = ($row["submitted"]) ? true : false;
236 $this->submittedTimestamp = $row["submittimestamp"] ?? '';
237 $this->tstamp = $row["tstamp"];
238 $this->lastStartedPass = $row['last_started_pass'];
239 $this->lastFinishedPass = $row['last_finished_pass'];
240 $this->setObjectiveOrientedContainerId((int) $row['objective_container']);
241 } elseif ($this->doesAccessCodeInSessionExists()) {
243 }
244 }
245
246 public function loadFromDb(int $active_id): void
247 {
248 $result = $this->db->queryF(
249 "SELECT * FROM tst_active WHERE active_id = %s",
250 ['integer'],
251 [$active_id]
252 );
253 if ($result->numRows()) {
254 $row = $this->db->fetchAssoc($result);
255 $this->active_id = $row["active_id"];
256 $this->user_id = $row["user_fi"];
257 $this->anonymous_id = $row["anonymous_id"] ?? '';
258 $this->test_id = $row["test_fi"];
259 $this->lastsequence = $row["lastindex"];
260 $this->pass = $row["tries"];
261 $this->submitted = ($row["submitted"]) ? true : false;
262 $this->submittedTimestamp = $row["submittimestamp"] ?? '';
263 $this->tstamp = $row["tstamp"];
264
265 $this->lastStartedPass = $row['last_started_pass'];
266 $this->lastFinishedPass = $row['last_finished_pass'];
267 $this->setObjectiveOrientedContainerId((int) $row['objective_container']);
268 }
269 }
270
271 public function getActiveId(): int
272 {
273 return $this->active_id;
274 }
275
276 public function setUserId(int $user_id): void
277 {
278 $this->user_id = $user_id;
279 }
280
281 public function getUserId(): int
282 {
283 return $this->user_id;
284 }
285
286 public function setTestId(int $test_id): void
287 {
288 $this->test_id = $test_id;
289 }
290
291 public function getTestId(): int
292 {
293 return $this->test_id;
294 }
295
296 public function setAnonymousId(string $anonymous_id): void
297 {
298 $this->anonymous_id = $anonymous_id;
299 }
300
301 public function getAnonymousId(): string
302 {
303 return $this->anonymous_id;
304 }
305
306 public function setLastSequence(int $lastsequence): void
307 {
308 $this->lastsequence = $lastsequence;
309 }
310
311 public function getLastSequence(): int
312 {
313 return $this->lastsequence;
314 }
315
316 public function setPass(int $pass): void
317 {
318 $this->pass = $pass;
319 }
320
321 public function getPass(): int
322 {
323 return $this->pass;
324 }
325
326 public function increasePass()
327 {
328 $this->pass += 1;
329 }
330
331 public function isSubmitted(): bool
332 {
333 return $this->submitted;
334 }
335
336 public function setSubmitted(): void
337 {
338 $this->submitted = true;
339 }
340
341 public function getSubmittedTimestamp(): ?string
342 {
344 }
345
346 public function setSubmittedTimestamp(): void
347 {
348 $this->submittedTimestamp = date('Y-m-d H:i:s');
349 }
350
351 public function setLastFinishedPass(int $lastFinishedPass): void
352 {
353 $this->lastFinishedPass = $lastFinishedPass;
354 }
355
356 public function getLastFinishedPass(): ?int
357 {
359 }
360
361 public function setObjectiveOrientedContainerId(int $objectiveOriented): void
362 {
363 $this->objectiveOrientedContainerId = $objectiveOriented;
364 }
365
367 {
368 return $this->objectiveOrientedContainerId ?? 0;
369 }
370
374 public function getLastStartedPass(): ?int
375 {
377 }
378
379 public function setLastStartedPass(int $lastStartedPass): void
380 {
381 $this->lastStartedPass = $lastStartedPass;
382 }
383
384 public function isObjectiveOriented(): bool
385 {
386 return (bool) $this->getObjectiveOrientedContainerId();
387 }
388
389 public function persistTestStartLock(string $testStartLock): void
390 {
391 $this->db->update(
392 'tst_active',
393 ['start_lock' => ['text', $testStartLock]],
394 ['active_id' => ['integer', $this->getActiveId()]]
395 );
396 }
397
398 public function lookupTestStartLock(): ?string
399 {
400 $res = $this->db->queryF(
401 "SELECT start_lock FROM tst_active WHERE active_id = %s",
402 ['integer'],
403 [$this->getActiveId()]
404 );
405
406 while ($row = $this->db->fetchAssoc($res)) {
407 return $row['start_lock'];
408 }
409
410 return null;
411 }
412
413 public function setAccessCodeToSession(string $access_code): void
414 {
415 if (!is_array(ilSession::get(self::ACCESS_CODE_SESSION_INDEX))) {
416 ilSession::set(self::ACCESS_CODE_SESSION_INDEX, []);
417 }
418 $session_code = ilSession::get(self::ACCESS_CODE_SESSION_INDEX);
419 $session_code[$this->getTestId()] = $access_code;
420 ilSession::set(self::ACCESS_CODE_SESSION_INDEX, $session_code);
421 }
422
423 public function unsetAccessCodeInSession(): void
424 {
425 $session_code = ilSession::get(self::ACCESS_CODE_SESSION_INDEX);
426 unset($session_code[$this->getTestId()]);
427 ilSession::set(self::ACCESS_CODE_SESSION_INDEX, $session_code);
428 }
429
430 public function getAccessCodeFromSession(): ?string
431 {
432 if (!is_array(ilSession::get(self::ACCESS_CODE_SESSION_INDEX))) {
433 return null;
434 }
435 $session_code = ilSession::get(self::ACCESS_CODE_SESSION_INDEX);
436 if (!isset($session_code[$this->getTestId()])) {
437 return null;
438 }
439
440 return $session_code[$this->getTestId()];
441 }
442
443 public function doesAccessCodeInSessionExists(): bool
444 {
445 return is_array(ilSession::get(self::ACCESS_CODE_SESSION_INDEX)) && isset(ilSession::get(self::ACCESS_CODE_SESSION_INDEX)[$this->getTestId()]);
446 }
447
448 public function createNewAccessCode(): string
449 {
450 do {
451 $code = $this->buildAccessCode();
452 } while ($this->isAccessCodeUsed($code));
453
454 return $code;
455 }
456
457 public function isAccessCodeUsed(string $code): bool
458 {
459 $query = "SELECT anonymous_id FROM tst_active WHERE test_fi = %s AND anonymous_id = %s";
460
461 $result = $this->db->queryF(
462 $query,
463 ['integer', 'text'],
464 [$this->getTestId(), $code]
465 );
466
467 return ($result->numRows() > 0);
468 }
469
470 private function buildAccessCode(): string
471 {
472 // create a 5 character code
473 $codestring = self::ACCESS_CODE_CHAR_DOMAIN;
474
475 mt_srand();
476
477 $code = "";
478
479 for ($i = 1; $i <= self::ACCESS_CODE_LENGTH; $i++) {
480 $index = mt_rand(0, strlen($codestring) - 1);
481 $code .= substr($codestring, $index, 1);
482 }
483
484 return $code;
485 }
486
487 public function isAnonymousUser(): bool
488 {
489 return $this->getUserId() == ANONYMOUS_USER_ID;
490 }
491
492 public function isPasswordChecked(): bool
493 {
494 if (ilSession::get('pw_checked_' . $this->active_id) === null) {
495 return false;
496 }
497 return ilSession::get('pw_checked_' . $this->active_id);
498 }
499
500 public function setPasswordChecked(bool $value): void
501 {
502 ilSession::set('pw_checked_' . $this->active_id, $value);
503 }
504
505 private ?bool $reportable_results_available = null;
506
507 public function reportableResultsAvailable(ilObjTest $test_obj): ?bool
508 {
509 if ($this->reportable_results_available === null) {
510 $this->reportable_results_available = true;
511
512 if (!$this->getActiveId()) {
513 $this->reportable_results_available = false;
514 }
515
516 if (!$test_obj->canShowTestResults($this)) {
517 $this->reportable_results_available = false;
518 }
519 }
520
522 }
523
524 public function hasSinglePassReportable(ilObjTest $testObj): bool
525 {
526 $test_passes_selector = new ilTestPassesSelector($this->db, $testObj);
527 $test_passes_selector->setActiveId($this->getActiveId());
528 $test_passes_selector->setLastFinishedPass($this->getLastFinishedPass());
529
530 if (count($test_passes_selector->getReportablePasses()) == 1) {
531 return true;
532 }
533
534 return false;
535 }
536}
canShowTestResults(ilTestSession $test_session)
User class.
static get(string $a_var)
static set(string $a_var, $a_val)
Set a value.
Base Exception for all Exceptions relating to Modules/Test.
Test session handler.
setPasswordChecked(bool $value)
isAccessCodeUsed(string $code)
setRefId(int $ref_id)
reportableResultsAvailable(ilObjTest $test_obj)
setTestId(int $test_id)
setLastStartedPass(int $lastStartedPass)
setLastFinishedPass(int $lastFinishedPass)
setAnonymousId(string $anonymous_id)
setAccessCodeToSession(string $access_code)
loadFromDb(int $active_id)
setObjectiveOrientedContainerId(int $objectiveOriented)
persistTestStartLock(string $testStartLock)
__construct(protected ilDBInterface $db, protected ilObjUser $user)
ilTestSession constructor
setLastSequence(int $lastsequence)
activeIDExists(int $user_id, int $test_id)
setUserId(int $user_id)
loadTestSession(int $test_id, int $user_id=0, ?string $anonymous_id=null)
hasSinglePassReportable(ilObjTest $testObj)
const ANONYMOUS_USER_ID
Definition: constants.php:27
Interface ilDBInterface.
$res
Definition: ltiservices.php:69