ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
QuestionsBrowserTable.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
23use ILIAS\Taxonomy\DomainService as TaxonomyService;
24use GuzzleHttp\Psr7\ServerRequest;
25use ILIAS\Data\Factory as DataFactory;
33use ILIAS\UI\Factory as UIFactory;
34use ILIAS\UI\Renderer as UIRenderer;
37use Psr\Http\Message\ServerRequestInterface;
39
41{
42 public const ACTION_INSERT = 'insert';
43
44 public function __construct(
45 private readonly string $table_id,
46 private readonly \ilObjUser $current_user,
47 private readonly UIFactory $ui_factory,
48 private readonly UIRenderer $ui_renderer,
49 private readonly \ilLanguage $lng,
50 private readonly \ilCtrl $ctrl,
51 private readonly DataFactory $data_factory,
52 private readonly \ilAssQuestionList $question_list,
53 private readonly TaxonomyService $taxonomy,
54 private readonly string $parent_title
55 ) {
56 }
57
58 public function getComponent(ServerRequestInterface $request, ?array $filter): Data
59 {
60 return $this->ui_factory->table()->data(
61 $this,
62 $this->lng->txt('list_of_questions'),
63 $this->getColumns(),
64 )->withId($this->table_id)
65 ->withActions($this->getActions())
66 ->withRequest($request)
67 ->withFilter($filter);
68 }
69
70 public function getColumns(): array
71 {
72 $column_factory = $this->ui_factory->table()->column();
73 $icon_factory = $this->ui_factory->symbol()->icon();
74 $iconYes = $icon_factory->custom('assets/images/standard/icon_checked.svg', 'yes');
75 $iconNo = $icon_factory->custom('assets/images/standard/icon_unchecked.svg', 'no');
76
77 $columns = [
78 'title' => $column_factory->text(
79 $this->lng->txt('tst_question_title')
80 )->withIsOptional(false, true),
81 'description' => $column_factory->text(
82 $this->lng->txt('description')
83 )->withIsOptional(true, true),
84 'question_type' => $column_factory->text(
85 $this->lng->txt('tst_question_type')
86 )->withIsOptional(false, true),
87 'points' => $column_factory->number(
88 $this->lng->txt('points')
89 )->withIsOptional(false, true),
90 'author' => $column_factory->text(
91 $this->lng->txt('author')
92 )->withIsOptional(true, false),
93 'lifecycle' => $column_factory->text(
94 $this->lng->txt('qst_lifecycle')
95 )->withIsOptional(true, false),
96 'parent_title' => $column_factory->link(
97 $this->lng->txt($this->parent_title)
98 )->withIsOptional(false, true),
99 'taxonomies' => $column_factory->text(
100 $this->lng->txt('qpl_settings_subtab_taxonomies')
101 )->withIsOptional(false, true),
102 'feedback' => $column_factory->boolean(
103 $this->lng->txt('tst_feedback'),
104 $iconYes,
105 $iconNo
106 )->withIsOptional(true, false),
107 'created' => $column_factory->date(
108 $this->lng->txt('created'),
109 $this->current_user->getDateTimeFormat()
110 )->withIsOptional(true, false),
111 'tstamp' => $column_factory->date(
112 $this->lng->txt('updated'),
113 $this->current_user->getDateTimeFormat()
114 )->withIsOptional(true, false)
115 ];
116
117 return array_map(static fn(Column $column): Column => $column->withIsSortable(true), $columns);
118 }
119
120 public function getActions(): array
121 {
122 return [self::ACTION_INSERT => $this->getInsertAction()];
123 }
124
125 private function getInsertAction(): TableAction
126 {
127 $url_builder = new URLBuilder($this->data_factory->uri(
128 ILIAS_HTTP_PATH . "/{$this->ctrl->getLinkTargetByClass(
129 ilTestQuestionBrowserTableGUI::class,
130 ilTestQuestionBrowserTableGUI::CMD_INSERT_QUESTIONS
131 )}"
132 ));
133
134 [$url_builder, $row_id_token] = $url_builder->acquireParameters(['qlist'], 'q_id');
135
136 return $this->ui_factory->table()->action()->standard(
137 $this->lng->txt('tst_insert_in_test'),
138 $url_builder,
139 $row_id_token
140 );
141 }
142
143 public function getRows(
144 DataRowBuilder $row_builder,
145 array $visible_column_ids,
147 Order $order,
148 mixed $additional_viewcontrol_data,
149 mixed $filter_data,
150 mixed $additional_parameters
151 ): \Generator {
152 $timezone = new \DateTimeZone($this->current_user->getTimeZone());
153 foreach ($this->loadRecords($filter_data ?? [], $order, $range) as $record) {
154 $question_id = $record['question_id'];
155
156 $record['question_type'] = $record['question_type'];
157 $record['complete'] = (bool) $record['complete'];
158 $record['lifecycle'] = \ilAssQuestionLifecycle::getInstance($record['lifecycle'])->getTranslation($this->lng) ?? '';
159
160 $record['created'] = (new \DateTimeImmutable("@{$record['created']}"))->setTimezone($timezone);
161 $record['tstamp'] = (new \DateTimeImmutable("@{$record['tstamp']}"))->setTimezone($timezone);
162 $record['taxonomies'] = $this->resolveTaxonomiesRowData($record['obj_fi'], $question_id);
163 $record['parent_title'] = $this->getParentObjectLink($record);
164
165 yield $row_builder->buildDataRow((string) $question_id, $record);
166 }
167 }
168
169 private function getParentObjectLink(array $record): Standard
170 {
171 [$parent_class, $parent_command] = match($record['parent_type']) {
172 'qpl' => [\ilObjQuestionPoolGUI::class, \ilObjQuestionPoolGUI::DEFAULT_CMD],
173 'tst' => [\ilObjTestGUI::class, \ilObjTestGUI::SHOW_QUESTIONS_CMD],
174 default => throw new \RuntimeException("Unsupported parent type {$record['parent_type']}"),
175 };
176
177 $this->ctrl->setParameterByClass($parent_class, 'ref_id', $record['parent_ref_id']);
178 $link = $this->ui_factory->link()->standard(
179 $record['parent_title'],
180 $this->ctrl->getLinkTargetByClass($parent_class, $parent_command),
181 );
182 $this->ctrl->setParameterByClass($parent_class, 'ref_id', null);
183
184 return $link;
185 }
186
187 public function getTotalRowCount(
188 mixed $additional_viewcontrol_data,
189 mixed $filter_data,
190 mixed $additional_parameters
191 ): int {
192 $filter_data ??= [];
193 $this->addFiltersToQuestionList($filter_data);
194 return $this->question_list->getTotalRowCount($additional_viewcontrol_data, $filter_data, $additional_parameters);
195 }
196
197 public function loadRecords(array $filters = [], ?Order $order = null, ?Range $range = null): array
198 {
199 $this->addFiltersToQuestionList($filters);
200
201 $this->question_list->setOrder($order);
202 $this->question_list->setRange($range);
203 $this->question_list->load();
204
205 return $this->question_list->getQuestionDataArray();
206 }
207
208 private function addFiltersToQuestionList(array $filters): void
209 {
210 foreach (array_filter($filters) as $key => $filter) {
211 if ($key === 'commented') {
212 $this->question_list->setCommentFilter((int) $filter);
213 continue;
214 }
215
216 $this->question_list->addFieldFilter($key, $filter);
217 }
218 }
219
220 private function resolveTaxonomiesRowData(int $obj_fi, int $questionId): string
221 {
222 $available_taxonomy_ids = $this->taxonomy->getUsageOfObject($obj_fi);
223 $data = $this->loadTaxonomyAssignmentData($obj_fi, $questionId, $available_taxonomy_ids);
224
225 $taxonomies = [];
226
227 foreach ($data as $taxonomyId => $taxData) {
228 $taxonomies[] = \ilObject::_lookupTitle($taxonomyId);
229 $taxonomies[] = $this->ui_renderer->render(
230 $this->ui_factory->listing()->unordered(
231 array_map(static function ($node) {
232 return \ilTaxonomyNode::_lookupTitle($node['node_id']);
233 }, $taxData)
234 )
235 );
236 }
237
238 return implode('', $taxonomies);
239 }
240
241 private function loadTaxonomyAssignmentData(int $parentObjId, int $questionId, array $available_taxonomy_ids): array
242 {
243 $taxonomyAssignmentData = [];
244
245 foreach ($available_taxonomy_ids as $taxId) {
246 $taxTree = new \ilTaxonomyTree($taxId);
247 $assignments = (new \ilTaxNodeAssignment(
248 'qpl',
249 $parentObjId,
250 'quest',
251 $taxId
252 ))->getAssignmentsOfItem($questionId);
253
254 foreach ($assignments as $assData) {
255 $taxId = $assData['tax_id'];
256 if (!isset($taxonomyAssignmentData[$taxId])) {
257 $taxonomyAssignmentData[$taxId] = [];
258 }
259
260 $nodeId = $assData['node_id'];
261 $assData['node_lft'] = $taxTree->getNodeData($nodeId)['lft'];
262 $taxonomyAssignmentData[$taxId][$nodeId] = $assData;
263 }
264 }
265
266 return $taxonomyAssignmentData;
267 }
268}
Builds a Color from either hex- or rgb values.
Definition: Factory.php:31
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
A simple class to express a naive range of whole positive numbers.
Definition: Range.php:29
loadTaxonomyAssignmentData(int $parentObjId, int $questionId, array $available_taxonomy_ids)
getTotalRowCount(mixed $additional_viewcontrol_data, mixed $filter_data, mixed $additional_parameters)
Mainly for the purpose of pagination-support, it is important to know about the total number of recor...
getRows(DataRowBuilder $row_builder, array $visible_column_ids, Range $range, Order $order, mixed $additional_viewcontrol_data, mixed $filter_data, mixed $additional_parameters)
This is called by the table to retrieve rows; map data-records to rows using the $row_builder e....
__construct(private readonly string $table_id, private readonly \ilObjUser $current_user, private readonly UIFactory $ui_factory, private readonly UIRenderer $ui_renderer, private readonly \ilLanguage $lng, private readonly \ilCtrl $ctrl, private readonly DataFactory $data_factory, private readonly \ilAssQuestionList $question_list, private readonly TaxonomyService $taxonomy, private readonly string $parent_title)
loadRecords(array $filters=[], ?Order $order=null, ?Range $range=null)
getComponent(ServerRequestInterface $request, ?array $filter)
Class ilCtrl provides processing control methods.
language handling
User class.
static _lookupTitle(int $obj_id)
@ilCtrl_Calls ilTestQuestionBrowserTableGUI: ilFormPropertyDispatchGUI
A Column describes the form of presentation for a certain aspect of data, i.e.
Definition: Column.php:28
buildDataRow(string $id, array $record)
This describes a Data Table.
Definition: Data.php:33
withId(string $id)
The DataTable comes with a storage to keep e.g.
An entity that renders components to a string output.
Definition: Renderer.php:31
global $lng
Definition: privfeed.php:31