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