ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
class.ilAssQuestionList.php
Go to the documentation of this file.
1<?php
2
21use ILIAS\Notes\Service as NotesService;
22use ILIAS\Refinery\Factory as Refinery;
23
31{
32 private array $parentObjIdsFilter = [];
33 private ?int $parentObjId = null;
34 private string $parentObjType = 'qpl';
35 private array $availableTaxonomyIds = [];
36 private array $fieldFilters = [];
37 private array $taxFilters = [];
39 private array $taxParentIds = [];
40 private array $taxParentTypes = [];
41 private ?int $answerStatusActiveId = null;
42 protected bool $join_obj_data = true;
43
47 public const QUESTION_ANSWER_STATUS_NON_ANSWERED = 'nonAnswered';
48 public const QUESTION_ANSWER_STATUS_WRONG_ANSWERED = 'wrongAnswered';
49 public const QUESTION_ANSWER_STATUS_CORRECT_ANSWERED = 'correctAnswered';
50
54 public const ANSWER_STATUS_FILTER_ALL_NON_CORRECT = 'allNonCorrect';
55 public const ANSWER_STATUS_FILTER_NON_ANSWERED_ONLY = 'nonAnswered';
56 public const ANSWER_STATUS_FILTER_WRONG_ANSWERED_ONLY = 'wrongAnswered';
57
58 private string $answerStatusFilter = '';
59
60 public const QUESTION_INSTANCE_TYPE_ORIGINALS = 'QST_INSTANCE_TYPE_ORIGINALS';
61 public const QUESTION_INSTANCE_TYPE_DUPLICATES = 'QST_INSTANCE_TYPE_DUPLICATES';
62 public const QUESTION_INSTANCE_TYPE_ALL = 'QST_INSTANCE_TYPE_ALL';
64
65 private array $includeQuestionIdsFilter = [];
66 private array $excludeQuestionIdsFilter = [];
67
68 public const QUESTION_COMMENTED_ONLY = '1';
69 public const QUESTION_COMMENTED_EXCLUDED = '2';
70 protected ?string $filter_comments = null;
71
72 protected array $questions = [];
73
74 private ?Order $order = null;
75 private ?string $order_field = null;
76 private ?string $order_direction = null;
77 private ?Range $range = null;
78
79 public function __construct(
80 private ilDBInterface $db,
81 private ilLanguage $lng,
82 private Refinery $refinery,
83 private ilComponentRepository $component_repository,
84 private ?NotesService $notes_service = null
85 ) {
86 }
87
88 public function setOrder(?Order $order = null): void
89 {
90 $this->order = $order;
91 $order_state = $this->getOrderFieldAndDirection($order);
92 $this->order_field = $order_state['order_field'];
93 $this->order_direction = $order_state['order_direction'];
94 }
95
96 public function setRange(?Range $range = null): void
97 {
98 $this->range = $range;
99 }
100
101 public function getParentObjId(): ?int
102 {
103 return $this->parentObjId;
104 }
105
106 public function setParentObjId(?int $parentObjId): void
107 {
108 $this->parentObjId = $parentObjId;
109 }
110
111 public function setParentObjectType(string $parentObjType): void
112 {
113 $this->parentObjType = $parentObjType;
114 }
115
116 public function setParentObjIdsFilter(array $parentObjIdsFilter): void
117 {
118 $this->parentObjIdsFilter = $parentObjIdsFilter;
119 }
120
122 {
123 $this->questionInstanceTypeFilter = (string) $questionInstanceTypeFilter;
124 }
125
126 public function setIncludeQuestionIdsFilter(array $questionIdsFilter): void
127 {
128 $this->includeQuestionIdsFilter = $questionIdsFilter;
129 }
130
132 {
133 $this->excludeQuestionIdsFilter = $excludeQuestionIdsFilter;
134 }
135
136 public function addFieldFilter(string $fieldName, mixed $fieldValue): void
137 {
138 $this->fieldFilters[$fieldName] = $fieldValue;
139 }
140
141 public function addTaxonomyFilter($taxId, $taxNodes, $parentObjId, $parentObjType): void
142 {
143 $this->taxFilters[$taxId] = $taxNodes;
144 $this->taxParentIds[$taxId] = $parentObjId;
145 $this->taxParentTypes[$taxId] = $parentObjType;
146 }
147
148 public function addTaxonomyFilterNoTaxonomySet(bool $flag): void
149 {
150 $this->taxFiltersExcludeAnyObjectsWithTaxonomies = $flag;
151 }
152
154 {
155 $this->availableTaxonomyIds = $availableTaxonomyIds;
156 }
157
159 {
160 $this->answerStatusActiveId = $answerStatusActiveId;
161 }
162
163 public function setAnswerStatusFilter(string $answerStatusFilter): void
164 {
165 $this->answerStatusFilter = $answerStatusFilter;
166 }
167
171 public function setJoinObjectData(bool $a_val): void
172 {
173 $this->join_obj_data = $a_val;
174 }
175
176 private function getParentObjFilterExpression(): ?string
177 {
178 if ($this->parentObjId) {
179 return "qpl_questions.obj_fi = {$this->db->quote($this->parentObjId, ilDBConstants::T_INTEGER)}";
180 }
181
182 if (!empty($this->parentObjIdsFilter)) {
183 return $this->db->in('qpl_questions.obj_fi', $this->parentObjIdsFilter, false, ilDBConstants::T_INTEGER);
184 }
185
186 return null;
187 }
188
189 private function getFieldFilterExpressions(): array
190 {
191 $expressions = [];
192
193 foreach ($this->fieldFilters as $fieldName => $fieldValue) {
194 switch ($fieldName) {
195 case 'title':
196 case 'description':
197 case 'author':
198 case 'lifecycle':
199 $expressions[] = $this->db->like("qpl_questions.$fieldName", ilDBConstants::T_TEXT, "%%$fieldValue%%");
200 break;
201 case 'type':
202 $expressions[] = "qpl_qst_type.type_tag = {$this->db->quote($fieldValue, ilDBConstants::T_TEXT)}";
203 break;
204 case 'question_id':
205 if ($fieldValue !== '' && !is_array($fieldValue)) {
206 $fieldValue = [$fieldValue];
207 }
208 $expressions[] = $this->db->in('qpl_questions.question_id', $fieldValue, false, ilDBConstants::T_INTEGER);
209 break;
210 case 'parent_title':
211 if ($this->join_obj_data) {
212 $expressions[] = $this->db->like('object_data.title', ilDBConstants::T_TEXT, "%%$fieldValue%%");
213 }
214 break;
215 }
216 }
217
218 return $expressions;
219 }
220
221 private function handleFeedbackJoin(string $tableJoin): string
222 {
223 $feedback_join = match ($this->fieldFilters['feedback'] ?? null) {
224 'true' => 'INNER',
225 'false' => 'LEFT',
226 default => null
227 };
228
229 if (isset($feedback_join)) {
230 $SQL = "$feedback_join JOIN qpl_fb_generic ON qpl_fb_generic.question_fi = qpl_questions.question_id ";
231 $tableJoin .= !str_contains($tableJoin, $SQL) ? $SQL : '';
232 }
233
234 return $tableJoin;
235 }
236
237 private function getTaxonomyFilterExpressions(): array
238 {
239 $expressions = $this->getFilterByAssignedTaxonomyIdsExpression();
240
241 $taxonomy_title = $this->fieldFilters['taxonomy_title'] ?? '';
242 $taxonomy_node_title = $this->fieldFilters['taxonomy_node_title'] ?? '';
243
244 if ($taxonomy_title === '' && $taxonomy_node_title === '') {
245 return $expressions;
246 }
247
248 $base = 'SELECT DISTINCT item_id FROM tax_node_assignment';
249
250 $like_taxonomy_title = $taxonomy_title !== ''
251 ? "AND {$this->db->like('object_data.title', ilDBConstants::T_TEXT, "%$taxonomy_title%", false)}"
252 : '';
253 $like_taxonomy_node_title = $taxonomy_node_title !== ''
254 ? "AND {$this->db->like('tax_node.title', ilDBConstants::T_TEXT, "%$taxonomy_node_title%", false)}"
255 : '';
256
257 $inner_join_object_data = "INNER JOIN object_data ON (object_data.obj_id = tax_node_assignment.tax_id AND object_data.type = 'tax' $like_taxonomy_title)";
258 $inner_join_tax_node = "INNER JOIN tax_node ON (tax_node.tax_id = tax_node_assignment.tax_id AND tax_node.type = 'taxn' AND tax_node_assignment.node_id = tax_node.obj_id $like_taxonomy_node_title)";
259
260 $expressions[] = "qpl_questions.question_id IN ($base $inner_join_object_data $inner_join_tax_node)";
261
262 return $expressions;
263 }
264
266 {
267 if ($this->taxFiltersExcludeAnyObjectsWithTaxonomies) {
268 return ['question_id NOT IN (SELECT DISTINCT item_id FROM tax_node_assignment)'];
269 }
270
271 $expressions = [];
272 foreach ($this->taxFilters as $tax_id => $tax_nodes) {
273 $question_ids = [];
274
275 if ($tax_nodes === []) {
276 continue;
277 }
278
279 foreach ($tax_nodes as $tax_node) {
280 $tax_items_by_tax_parent = $this->getTaxItems(
281 $this->taxParentTypes[$tax_id],
282 $this->taxParentIds[$tax_id],
283 $tax_id,
284 $tax_node
285 );
286
287 $tax_items_by_parent = $this->getTaxItems(
288 $this->parentObjType,
289 $this->parentObjId,
290 $tax_id,
291 $tax_node
292 );
293
294 $tax_items = array_merge($tax_items_by_tax_parent, $tax_items_by_parent);
295 foreach ($tax_items as $tax_item) {
296 $question_ids[$tax_item['item_id']] = $tax_item['item_id'];
297 }
298 }
299
300 $expressions[] = $this->db->in('question_id', $question_ids, false, ilDBConstants::T_INTEGER);
301 }
302
303 return $expressions;
304 }
305
306 protected function getTaxItems(string $parentType, int $parentObjId, int $taxId, int $taxNode): array
307 {
308 $taxTree = new ilTaxonomyTree($taxId);
309
310 $taxNodeAssignment = new ilTaxNodeAssignment(
311 $parentType,
313 'quest',
314 $taxId
315 );
316
317 $subNodes = $taxTree->getSubTreeIds($taxNode);
318 $subNodes[] = $taxNode;
319
320 return $taxNodeAssignment->getAssignmentsOfNode($subNodes);
321 }
322
323 private function getQuestionInstanceTypeFilterExpression(): ?string
324 {
325 return match ($this->questionInstanceTypeFilter) {
326 self::QUESTION_INSTANCE_TYPE_ORIGINALS => 'qpl_questions.original_id IS NULL',
327 self::QUESTION_INSTANCE_TYPE_DUPLICATES => 'qpl_questions.original_id IS NOT NULL',
328 default => null
329 };
330 }
331
332 private function getQuestionIdsFilterExpressions(): array
333 {
334 $expressions = [];
335
336 if (!empty($this->includeQuestionIdsFilter)) {
337 $expressions[] = $this->db->in(
338 'qpl_questions.question_id',
339 $this->includeQuestionIdsFilter,
340 false,
342 );
343 }
344
345 if (!empty($this->excludeQuestionIdsFilter)) {
346 $IN = $this->db->in(
347 'qpl_questions.question_id',
348 $this->excludeQuestionIdsFilter,
349 true,
351 );
352
353 $expressions[] = $IN === ' 1=2 ' ? ' 1=1 ' : $IN; // required for ILIAS < 5.0
354 }
355
356 return $expressions;
357 }
358
359 private function getAnswerStatusFilterExpressions(): array
360 {
361 return match ($this->answerStatusFilter) {
362 self::ANSWER_STATUS_FILTER_ALL_NON_CORRECT => ['
363 (tst_test_result.question_fi IS NULL OR tst_test_result.points < qpl_questions.points)
364 '],
365 self::ANSWER_STATUS_FILTER_NON_ANSWERED_ONLY => ['tst_test_result.question_fi IS NULL'],
366 self::ANSWER_STATUS_FILTER_WRONG_ANSWERED_ONLY => [
367 'tst_test_result.question_fi IS NOT NULL',
368 'tst_test_result.points < qpl_questions.points'
369 ],
370 default => [],
371 };
372 }
373
374 private function getTableJoinExpression(): string
375 {
376 $tableJoin = "INNER JOIN qpl_qst_type ON qpl_qst_type.question_type_id = qpl_questions.question_type_fi ";
377
378 if ($this->join_obj_data) {
379 $tableJoin .= '
380 INNER JOIN object_data ON object_data.obj_id = qpl_questions.obj_fi
381 INNER JOIN object_reference ON object_reference.obj_id = object_data.obj_id
382 ';
383 }
384
385 if (
386 $this->parentObjType === 'tst'
387 && $this->questionInstanceTypeFilter === self::QUESTION_INSTANCE_TYPE_ALL
388 ) {
389 $tableJoin .= 'INNER JOIN tst_test_question tstquest ON tstquest.question_fi = qpl_questions.question_id';
390 }
391
392 $tableJoin = $this->handleFeedbackJoin($tableJoin);
393
394 if ($this->answerStatusActiveId) {
395 $tableJoin .= "
396 LEFT JOIN tst_test_result
397 ON tst_test_result.question_fi = qpl_questions.question_id
398 AND tst_test_result.active_fi = {$this->db->quote($this->answerStatusActiveId, ilDBConstants::T_INTEGER)}
399 ";
400 }
401
402 return $tableJoin;
403 }
404
405 private function getConditionalFilterExpression(): string
406 {
407 $conditions = [];
408
409 if ($this->getQuestionInstanceTypeFilterExpression() !== null) {
410 $conditions[] = $this->getQuestionInstanceTypeFilterExpression();
411 }
412
413 if ($this->getParentObjFilterExpression() !== null) {
414 $conditions[] = $this->getParentObjFilterExpression();
415 }
416
417 $conditions = array_merge(
418 $conditions,
423 );
424
425 $conditions = implode(' AND ', $conditions);
426 return $conditions !== '' ? "AND $conditions" : '';
427 }
428
429 private function getSelectFieldsExpression(): string
430 {
431 $select_fields = [
432 'qpl_questions.*',
433 'qpl_qst_type.type_tag AS question_type',
434 'qpl_qst_type.plugin',
435 'qpl_qst_type.plugin_name',
436 'qpl_questions.points max_points'
437 ];
438
439 if ($this->join_obj_data) {
440 $select_fields[] = 'object_data.title parent_title, object_data.type parent_type, object_reference.ref_id parent_ref_id';
441 }
442
443 if ($this->answerStatusActiveId) {
444 $select_fields[] = 'tst_test_result.points reached_points';
445 $select_fields[] = "CASE
446 WHEN tst_test_result.points IS NULL THEN '" . self::QUESTION_ANSWER_STATUS_NON_ANSWERED . "'
447 WHEN tst_test_result.points < qpl_questions.points THEN '" . self::QUESTION_ANSWER_STATUS_WRONG_ANSWERED . "'
448 ELSE '" . self::QUESTION_ANSWER_STATUS_CORRECT_ANSWERED . "'
449 END question_answer_status
450 ";
451 }
452
453 $select_fields[] = $this->generateFeedbackSubquery();
454
455 $select_fields[] = $this->generateTaxonomySubquery();
456
457 $select_fields = implode(', ', $select_fields);
458 return "SELECT DISTINCT $select_fields";
459 }
460
461 private function generateFeedbackSubquery(): string
462 {
463 $cases = [];
464 $tables = ['qpl_fb_generic', 'qpl_fb_specific'];
465
466 foreach ($tables as $table) {
467 $subquery = "SELECT 1 FROM $table WHERE $table.question_fi = qpl_questions.question_id AND $table.feedback <> ''";
468 $cases[] = "WHEN EXISTS ($subquery) THEN TRUE";
469 }
470
471 $page_object_table = 'page_object';
472 foreach ($tables as $table) {
473 $subquery = sprintf(
474 "SELECT 1 FROM $table JOIN $page_object_table ON $page_object_table.page_id = $table.feedback_id WHERE $page_object_table.parent_type IN ('%s', '%s') AND $page_object_table.is_empty <> 1 AND $table.question_fi = qpl_questions.question_id",
477 );
478 $cases[] = "WHEN EXISTS ($subquery) THEN TRUE";
479 }
480
481 $feedback_case_subquery = implode(' ', $cases);
482 return "CASE $feedback_case_subquery ELSE FALSE END AS feedback";
483 }
484
485 private function generateTaxonomySubquery(): string
486 {
487 $tax_node_assignment_table = 'tax_node_assignment';
488 $tax_subquery = "SELECT 1 FROM $tax_node_assignment_table WHERE $tax_node_assignment_table.item_id = qpl_questions.question_id AND $tax_node_assignment_table.item_type = 'quest'";
489 return "CASE WHEN EXISTS ($tax_subquery) THEN TRUE ELSE FALSE END AS taxonomies";
490 }
491
492 private function buildBasicQuery(): string
493 {
494 return "{$this->getSelectFieldsExpression()} FROM qpl_questions {$this->getTableJoinExpression()} WHERE qpl_questions.tstamp > 0";
495 }
496
497 private function getHavingFilterExpression(): string
498 {
499 $expressions = [];
500
501 foreach ($this->fieldFilters as $fieldName => $fieldValue) {
502 if ($fieldName === 'feedback') {
503 $fieldValue = strtoupper($fieldValue);
504 if (in_array($fieldValue, ['TRUE', 'FALSE'], true)) {
505 $expressions[] = "feedback IS $fieldValue";
506 }
507 continue;
508
509 }
510 }
511
512 $having = implode(' AND ', $expressions);
513 return $having !== '' ? "HAVING $having" : '';
514 }
515
516 private function buildOrderQueryExpression(): string
517 {
518 return $this->order_field === null || $this->order_direction === null || $this->order_field === 'question_type'
519 ? ''
520 : " ORDER BY `$this->order_field` $this->order_direction";
521 }
522
523 private function buildLimitQueryExpression(): string
524 {
526 if ($order instanceof Order && $this->order_field === 'question_type') {
527 return '';
528 }
529
531 if ($range === null) {
532 return '';
533 }
534
535 $limit = max($range->getLength(), 0);
536 $offset = max($range->getStart(), 0);
537
538 return " LIMIT $limit OFFSET $offset";
539 }
540
541 private function getOrderFieldAndDirection(?Order $order): ?array
542 {
543 if ($order === null) {
544 return ['order_field' => null, 'order_direction' => null];
545 }
546
548 '',
549 static fn(string $index, string $key, string $value): array => [$key, $value]
550 );
551
553 if (!in_array($order_direction, [Order::ASC, Order::DESC], true)) {
554 $order_direction = Order::ASC;
555 }
556
557 return ['order_field' => $order_field, 'order_direction' => $order_direction];
558 }
559
560 private function buildQuery(): string
561 {
562 return implode(PHP_EOL, array_filter([
563 $this->buildBasicQuery(),
568 ]));
569 }
570
571 public function load(): void
572 {
573 $this->checkFilters();
574
575 $tags_trafo = $this->refinery->encode()->htmlSpecialCharsAsEntities();
576
577 $res = $this->db->query($this->buildQuery());
578 while ($row = $this->db->fetchAssoc($res)) {
580
581 if (!$this->isActiveQuestionType($row)) {
582 continue;
583 }
584
585 $row['title'] = $tags_trafo->transform($row['title'] ?? '&nbsp;');
586 $row['description'] = $tags_trafo->transform($row['description'] ?? '');
587 $row['author'] = $tags_trafo->transform($row['author']);
588 $row['taxonomies'] = $this->loadTaxonomyAssignmentData($row['obj_fi'], $row['question_id']);
589 $row['question_type'] = $this->lng->txt($row['question_type']);
590 $row['feedback'] = $row['feedback'] === 1;
591 $row['comments'] = $this->getNumberOfCommentsForQuestion($row['question_id']);
592
593 if (
594 $this->filter_comments === self::QUESTION_COMMENTED_ONLY && $row['comments'] === 0
595 || $this->filter_comments === self::QUESTION_COMMENTED_EXCLUDED && $row['comments'] > 0
596 ) {
597 continue;
598 }
599
600 $this->questions[$row['question_id']] = $row;
601 }
602
603 if ($this->order_field === 'question_type') {
604 $this->questions = $this->postLimit($this->postOrder($this->questions));
605 }
606 }
607
608 private function postOrder(array $questions): array
609 {
610 if ($this->order_field === 'question_type') {
611 usort($questions, fn(array $a, array $b): int => strcmp($a[$this->order_field], $b[$this->order_field]));
612 }
613 return $this->order_direction === Order::DESC ? array_reverse($questions) : $questions;
614 }
615
616 private function postLimit(array $questions): array
617 {
618 return $this->range instanceof Range
619 ? array_slice($questions, $this->range->getStart(), $this->range->getLength())
620 : $questions;
621 }
622
623 public function getTotalRowCount(
624 mixed $additional_viewcontrol_data,
625 mixed $filter_data,
626 mixed $additional_parameters
627 ): ?int {
628 $this->checkFilters();
629
630 $count = 'COUNT(*)';
631 $query = "SELECT $count FROM qpl_questions {$this->getTableJoinExpression()} WHERE qpl_questions.tstamp > 0 {$this->getConditionalFilterExpression()}";
632
633 return (int) ($this->db->query($query)->fetch()[$count] ?? 0);
634 }
635
636 protected function getNumberOfCommentsForQuestion(int $question_id): int
637 {
638 if ($this->notes_service === null) {
639 return 0;
640 }
641 $notes_context = $this->notes_service->data()->context(
642 $this->getParentObjId(),
643 $question_id,
644 'quest'
645 );
646 return $this->notes_service->domain()->getNrOfCommentsForContext($notes_context);
647 }
648
649 public function setCommentFilter(?int $commented = null): void
650 {
651 $this->filter_comments = $commented;
652 }
653
655 int $parent_obj_id,
656 int $question_id
657 ): array {
658 $tax_assignment_data = [];
659 foreach ($this->availableTaxonomyIds as $tax_id) {
660 $tax_tree = new ilTaxonomyTree($tax_id);
661
662 $tax_assignment = new ilTaxNodeAssignment('qpl', $parent_obj_id, 'quest', $tax_id);
663 $assignments = $tax_assignment->getAssignmentsOfItem($question_id);
664
665 foreach ($assignments as $ass_data) {
666 if (!isset($tax_assignment_data[$ass_data['tax_id']])) {
667 $tax_assignment_data[$ass_data['tax_id']] = [];
668 }
669
670 $ass_data['node_lft'] = $tax_tree->getNodeData($ass_data['node_id']);
671
672 $tax_assignment_data[$ass_data['tax_id']][$ass_data['node_id']] = $ass_data;
673 }
674 }
675
676 return $tax_assignment_data;
677 }
678
679 private function isActiveQuestionType(array $questionData): bool
680 {
681 if (!isset($questionData['plugin'])) {
682 return false;
683 }
684
685 if (!$questionData['plugin']) {
686 return true;
687 }
688
689 if (
690 !isset($questionData['plugin_name'])
691 || !$this->component_repository->getComponentByTypeAndName(
693 'TestQuestionPool'
694 )->getPluginSlotById('qst')->hasPluginName($questionData['plugin_name'])
695 ) {
696 return false;
697 }
698
699 return $this->component_repository
700 ->getComponentByTypeAndName(ilComponentInfo::TYPE_COMPONENT, 'TestQuestionPool')
701 ->getPluginSlotById('qst')
702 ->getPluginByName($questionData['plugin_name'])
703 ->isActive();
704 }
705
706 public function getDataArrayForQuestionId(int $questionId): array
707 {
708 return $this->questions[$questionId];
709 }
710
711 public function getQuestionDataArray(): array
712 {
713 return $this->questions;
714 }
715
716 public function isInList(int $questionId): bool
717 {
718 return isset($this->questions[$questionId]);
719 }
720
730 public function getTitle(string $a_comp_id, string $a_item_type, int $a_item_id): string
731 {
732 if ($a_comp_id !== 'qpl' || $a_item_type !== 'quest' || !$a_item_id) {
733 return '';
734 }
735
736 return $this->questions[$a_item_id]['title'] ?? '';
737 }
738
739 private function checkFilters(): void
740 {
741 if ($this->answerStatusFilter !== '' && !$this->answerStatusActiveId) {
743 'No active id given! You cannot use the answer status filter without giving an active id.'
744 );
745 }
746 }
747}
Builds data types.
Definition: Factory.php:36
Both the subject and the direction need to be specified when expressing an order.
Definition: Order.php:29
join($init, callable $fn)
Definition: Order.php:75
A simple class to express a naive range of whole positive numbers.
Definition: Range.php:29
const PAGE_OBJECT_TYPE_GENERIC_FEEDBACK
type for generic feedback page objects
const PAGE_OBJECT_TYPE_SPECIFIC_FEEDBACK
type for specific feedback page objects
setQuestionInstanceTypeFilter(?string $questionInstanceTypeFilter)
isActiveQuestionType(array $questionData)
setParentObjId(?int $parentObjId)
const ANSWER_STATUS_FILTER_ALL_NON_CORRECT
answer status filter value domain
setCommentFilter(?int $commented=null)
getTotalRowCount(mixed $additional_viewcontrol_data, mixed $filter_data, mixed $additional_parameters)
handleFeedbackJoin(string $tableJoin)
postLimit(array $questions)
setParentObjIdsFilter(array $parentObjIdsFilter)
getDataArrayForQuestionId(int $questionId)
getNumberOfCommentsForQuestion(int $question_id)
setOrder(?Order $order=null)
loadTaxonomyAssignmentData(int $parent_obj_id, int $question_id)
setIncludeQuestionIdsFilter(array $questionIdsFilter)
setAvailableTaxonomyIds(array $availableTaxonomyIds)
addTaxonomyFilter($taxId, $taxNodes, $parentObjId, $parentObjType)
setJoinObjectData(bool $a_val)
Set if object data table should be joined.
__construct(private ilDBInterface $db, private ilLanguage $lng, private Refinery $refinery, private ilComponentRepository $component_repository, private ?NotesService $notes_service=null)
setExcludeQuestionIdsFilter(array $excludeQuestionIdsFilter)
postOrder(array $questions)
addFieldFilter(string $fieldName, mixed $fieldValue)
getTaxItems(string $parentType, int $parentObjId, int $taxId, int $taxNode)
getOrderFieldAndDirection(?Order $order)
setAnswerStatusActiveId(?int $answerStatusActiveId)
getTitle(string $a_comp_id, string $a_item_type, int $a_item_id)
Get title of an assigned item.
setRange(?Range $range=null)
setParentObjectType(string $parentObjType)
setAnswerStatusFilter(string $answerStatusFilter)
addTaxonomyFilterNoTaxonomySet(bool $flag)
const QUESTION_ANSWER_STATUS_NON_ANSWERED
answer status domain for single questions
static completeMissingPluginName(array $question_type_data)
language handling
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Readable part of repository interface to ilComponentDataDB.
Interface ilDBInterface.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$res
Definition: ltiservices.php:69
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
global $lng
Definition: privfeed.php:31