ILIAS  trunk Revision v12.0_alpha-1540-g00f839d5fa1
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 return $this->db->fetchObject(
460 $this->db->queryF(
461 'SELECT COUNT(anonymous_id) cnt FROM tst_active WHERE test_fi = %s AND anonymous_id = %s',
463 [$this->getTestId(), $code]
464 )
465 )->cnt > 0;
466 }
467
468 private function buildAccessCode(): string
469 {
470 // create a 5 character code
471 $codestring = self::ACCESS_CODE_CHAR_DOMAIN;
472
473 mt_srand();
474
475 $code = "";
476
477 for ($i = 1; $i <= self::ACCESS_CODE_LENGTH; $i++) {
478 $index = mt_rand(0, strlen($codestring) - 1);
479 $code .= substr($codestring, $index, 1);
480 }
481
482 return $code;
483 }
484
485 public function isAnonymousUser(): bool
486 {
487 return $this->getUserId() == ANONYMOUS_USER_ID;
488 }
489
490 public function isPasswordChecked(): bool
491 {
492 if (ilSession::get('pw_checked_' . $this->active_id) === null) {
493 return false;
494 }
495 return ilSession::get('pw_checked_' . $this->active_id);
496 }
497
498 public function setPasswordChecked(bool $value): void
499 {
500 ilSession::set('pw_checked_' . $this->active_id, $value);
501 }
502
503 private ?bool $reportable_results_available = null;
504
505 public function reportableResultsAvailable(ilObjTest $test_obj): ?bool
506 {
507 if ($this->reportable_results_available === null) {
508 $this->reportable_results_available = true;
509
510 if (!$this->getActiveId()) {
511 $this->reportable_results_available = false;
512 }
513
514 if (!$test_obj->canShowTestResults($this)) {
515 $this->reportable_results_available = false;
516 }
517 }
518
520 }
521
522 public function hasSinglePassReportable(ilObjTest $testObj): bool
523 {
524 $test_passes_selector = new ilTestPassesSelector($this->db, $testObj);
525 $test_passes_selector->setActiveId($this->getActiveId());
526 $test_passes_selector->setLastFinishedPass($this->getLastFinishedPass());
527
528 if (count($test_passes_selector->getReportablePasses()) == 1) {
529 return true;
530 }
531
532 return false;
533 }
534}
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