ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
AccessFileUploadAnswer.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
26 use ilObjTest;
27 use ilObject;
28 use ilSession;
29 use ilTestSession;
30 use ilTestAccess;
31 use ilDBInterface;
32 use ilObjUser;
33 use Closure;
34 
36 {
38  private readonly Closure $object_id_of_test_id;
40  private readonly Closure $references_of;
42  private readonly Closure $session;
44  private readonly Closure $checkResultsAccess;
45  private readonly Incident $incident;
46 
53  public function __construct(
54  private readonly ilObjUser $user,
55  private readonly ilDBInterface $database,
56  private readonly Readable $readable,
57  $object_id_of_test_id = [ilObjTest::class, '_getObjectIDFromTestID'],
58  $references_of = [ilObject::class, '_getAllReferences'],
59  $session = [ilSession::class, 'get'],
60  ?callable $checkResultsAccess = null,
61  ?Incident $incident = null
62  ) {
63  $this->incident = $incident ?? new Incident();
64  $this->object_id_of_test_id = Closure::fromCallable($object_id_of_test_id);
65  $this->references_of = Closure::fromCallable($references_of);
66  $this->session = Closure::fromCallable($session);
67  $checkResultsAccess = $checkResultsAccess ?? static function (int $reference, int $test_id, int $active_id): bool {
68  $access = new ilTestAccess($reference);
69  return $access->checkResultsAccessForActiveId($active_id, $test_id);
70  };
71  $this->checkResultsAccess = Closure::fromCallable($checkResultsAccess);
72  }
73 
74  public function isPermitted(string $path): Result
75  {
76  $path_and_test = $this->pathAndTestId($path);
77 
78  if (!$path_and_test) {
79  return new Error('Not a file upload path of test answers.');
80  }
81  if (!$path_and_test['test']) {
82  return new Ok(false);
83  }
84 
85  $object_id = (int) ($this->object_id_of_test_id)($path_and_test['test']);
86  if (!$object_id) {
87  return new Ok(false);
88  }
89 
90  $references = ($this->references_of)($object_id);
91 
92  return new Ok($this->readable->references($references) && $this->roleBasedCheck($path_and_test['test'], $references, $path_and_test['path']));
93  }
94 
95  private function isAnonymous(): bool
96  {
97  return $this->user->isAnonymous() || !$this->user->getId();
98  }
99 
100  private function accessCodeOk(string $file, int $test_id): bool
101  {
102  $code = ($this->session)(ilTestSession::ACCESS_CODE_SESSION_INDEX)[$test_id] ?? false;
103 
104  return $code && $this->userDidUpload($test_id, $file, $code);
105  }
106 
107  private function userDidUpload(int $test_id, string $file, ?string $code = null): bool
108  {
109  $where = [
110  'active_id = active_fi',
111  'user_fi = %s',
112  'value1 = %s',
113  'anonymous_id ' . (null === $code ? 'IS' : '=') . ' %s',
114  'test_fi = %s',
115  ];
116 
117  $result = $this->database->queryF(
118  'SELECT 1 FROM tst_solutions WHERE EXISTS (SELECT 1 FROM tst_active WHERE ' . implode(' AND ', $where) . ')',
119  ['integer', 'text', 'text', 'integer'],
120  [$this->user->getId(), $file, $code, $test_id]
121  );
122 
123  return (bool) $this->database->numRows($result);
124  }
125 
126  private function activeIdOfFile(string $file, int $test): ?int
127  {
128  $is_upload_question = 'EXISTS (SELECT 1 FROM qpl_qst_type INNER JOIN qpl_questions ON question_type_id = question_type_fi WHERE type_tag = %s AND tst_solutions.question_fi = qpl_questions.question_id)';
129  $is_in_test = 'EXISTS (SELECT 1 FROM tst_active WHERE test_fi = %s AND active_id = active_fi)';
130 
131  $result = $this->database->queryF(
132  "SELECT active_fi, value1 FROM tst_solutions WHERE $is_upload_question AND $is_in_test",
133  ['text', 'integer'],
134  ['assFileUpload', $test]
135  );
136 
137  while (($row = $this->database->fetchAssoc($result))) {
138  if ($row['value1'] === $file) {
139  return (int) $row['active_fi'];
140  }
141  }
142 
143  return null;
144  }
145 
153  private function roleBasedCheck(int $test_id, array $references, string $file): bool
154  {
155  return $this->isAnonymous() ? $this->accessCodeOk($file, $test_id) : $this->canAccessResults($test_id, $references, $file) || $this->userDidUpload($test_id, $file);
156  }
157 
165  private function canAccessResults(int $test_id, array $references, string $file): bool
166  {
167  $active_id = $this->activeIdOfFile($file, $test_id);
168  if (!$active_id) {
169  return false;
170  }
171 
172  return $this->incident->any(fn(int $reference): bool => (
173  ($this->checkResultsAccess)($reference, $test_id, $active_id)
174  ), $references);
175  }
176 
182  private function pathAndTestId(string $path): ?array
183  {
184  $results = [];
185  if (!preg_match(':/assessment/tst_(\d+)/.*/([^/]+)$:', $path, $results)) {
186  return null;
187  }
188 
189  return [
190  'test' => (int) $results[1],
191  'path' => $results[2],
192  ];
193  }
194 }
$path
Definition: ltiservices.php:29
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
__construct(private readonly ilObjUser $user, private readonly ilDBInterface $database, private readonly Readable $readable, $object_id_of_test_id=[ilObjTest::class, '_getObjectIDFromTestID'], $references_of=[ilObject::class, '_getAllReferences'], $session=[ilSession::class, 'get'], ?callable $checkResultsAccess=null, ?Incident $incident=null)
roleBasedCheck(int $test_id, array $references, string $file)
A result encapsulates a value or an error and simplifies the handling of those.
Definition: Ok.php:30
$results
userDidUpload(int $test_id, string $file, ?string $code=null)
canAccessResults(int $test_id, array $references, string $file)