ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
LSOAdaptiveContent.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
23use ilCtrl;
35use ilLanguage;
36use ilLink;
37use ilObject;
44use Psr\Http\Message\ServerRequestInterface;
45
50{
52
60 protected ilLanguage $lng;
62 protected ilCtrl $ctrl;
64 protected ServerRequestInterface $request;
68 protected int $ref_id;
70 protected int $obj_id;
72 private ?ilObjLearningSequence $lso = null;
73
77 public function __construct(
83 ServerRequestInterface $request,
85 int $ref_id,
86 int $obj_id
87 ) {
88 $this->parent_gui = $parent_gui;
89 $this->ui_factory = $ui_factory;
90 $this->ui_renderer = $ui_renderer;
91 $this->lng = $lng;
92 $this->ctrl = $ctrl;
93 $this->request = $request;
94 $this->tpl = $tpl;
95 $this->ref_id = $ref_id;
96 $this->obj_id = $obj_id;
97 }
98
104 public function getSupportedCommands(): array
105 {
106 return [
113 ];
114 }
115
119 public function manageContent(): void
120 {
121 // Restore the "add new object" drilldown in the toolbar so that new
122 // objects can be created directly from the content management view.
123 $this->parent_gui->showPossibleSubObjects();
124
125 $lso = $this->getLSO();
126 $items = $lso->getLSItems();
127 $navigator = new AdaptiveNavigator($lso->getDI()["conditions.factory"]);
128 $navigator->preload($items);
129 $structural_successors = [];
130 foreach ($items as $item) {
131 $structural_successors[$item->getRefId()] = array_map(
132 static fn(\LSItem $successor): int => $successor->getRefId(),
133 $navigator->getStructuralSuccessors($items, $item)
134 );
135 }
136 $filter_gui = new LSOAdaptiveFilter($this->ui_factory, $this->lng, $this->ctrl, $this->parent_gui);
137 $filter = $filter_gui->getFilter(
138 'manageContent',
141 )->withRequest($this->request);
142
143 $filter_data = $filter->getData();
144 $analyzer = new StaticInputConfigurationAnalyzer();
145 $static_input_configuration_issues = $this->getStaticInputConfigurationIssues($items, $navigator);
146 $misconfigured_ref_ids = array_fill_keys(
147 $analyzer->getAffectedRefIds($static_input_configuration_issues),
148 true
149 );
150 $issue_details_by_ref_id = $analyzer->getIssueDetailsByRefId($static_input_configuration_issues);
151 $data = $this->getTableData(
152 $items,
153 $filter_data,
154 $structural_successors,
155 $misconfigured_ref_ids,
156 $issue_details_by_ref_id
157 );
158
159 $boundaries_db = $this->getBoundaries();
160 $boundary_data = $boundaries_db->getBoundariesFor($this->obj_id);
161
162 $missing_hints = [];
163 if ((int) $boundary_data['start_ref_id'] === 0) {
164 $missing_hints[] = $this->lng->txt('lso_adaptive_missing_start_object');
165 }
166 if ((int) $boundary_data['end_ref_id'] === 0) {
167 $missing_hints[] = $this->lng->txt('lso_adaptive_missing_end_object');
168 }
169 if ($missing_hints !== []) {
170 $this->tpl->setOnScreenMessage('info', implode('<br>', $missing_hints));
171 }
172
173 $table = new LSOAdaptiveTable(
174 $this->ui_factory,
175 $this->ui_renderer,
176 $data,
177 $filter,
178 (int) $boundary_data['start_ref_id'],
179 (int) $boundary_data['end_ref_id'],
180 $this->parent_gui,
181 $this->ref_id,
182 $this->lng,
183 $this->ctrl,
184 $this->request,
185 $this->tpl
186 );
187
188 $this->parent_gui->setContent(
189 $table->render()
190 );
191 }
192
197 protected function getStaticInputConfigurationIssues(array $items, AdaptiveNavigator $navigator): array
198 {
199 $conditions_by_ref_id = [];
200 foreach ($items as $item) {
201 $conditions_by_ref_id[$item->getRefId()] = $navigator->getInputConditions($item);
202 }
203
204 return (new StaticInputConfigurationAnalyzer())->getIssues($conditions_by_ref_id);
205 }
206
210 protected function isMisconfiguredRefId(int $ref_id, array $misconfigured_ref_ids): bool
211 {
212 return isset($misconfigured_ref_ids[$ref_id]);
213 }
214
220 protected function getObjectTitleList(array $ref_ids): string
221 {
222 $titles = [];
223 foreach (array_unique($ref_ids) as $ref_id) {
225 }
226 sort($titles);
227
228 if ($titles === []) {
229 return $this->lng->txt('no_conditions');
230 }
231
232 return implode(', ', $titles);
233 }
234
245 protected function getTableData(
246 array $items,
247 ?array $filter_data,
248 array $structural_successors,
249 array $misconfigured_ref_ids,
250 array $issue_details_by_ref_id
251 ): array {
252 $boundaries_db = $this->getBoundaries();
253 $boundary_data = $boundaries_db->getBoundariesFor($this->obj_id);
254 $start_ref_id = $boundary_data['start_ref_id'];
255 $end_ref_id = $boundary_data['end_ref_id'];
256
257 $structural_predecessors = [];
258 foreach ($structural_successors as $source_ref_id => $successor_ref_ids) {
259 foreach ($successor_ref_ids as $successor_ref_id) {
260 $structural_predecessors[$successor_ref_id][] = (int) $source_ref_id;
261 }
262 }
263
264 $name_filter = $filter_data['name'] ?? '';
265 $input_filter = $filter_data['input_conditions'] ?? [];
266 $output_filter = $filter_data['output_conditions'] ?? [];
267 $online_filter = $filter_data['online_status'] ?? null;
268 $position_filter = $filter_data['position'] ?? [];
269
270 usort($items, function ($a, $b) use ($start_ref_id, $end_ref_id) {
271 if ($a->getRefId() === $start_ref_id) {
272 return -1;
273 }
274 if ($b->getRefId() === $start_ref_id) {
275 return 1;
276 }
277 if ($a->getRefId() === $end_ref_id) {
278 return 1;
279 }
280 if ($b->getRefId() === $end_ref_id) {
281 return -1;
282 }
283 return $a->getOrderNumber() <=> $b->getOrderNumber();
284 });
285
286 $condition_handler = $this->getLSO()->getDI()["conditions.handler"];
287 $lso_ref_id = $this->ref_id;
288
289 $data = [];
290 foreach ($items as $index => $item) {
291 $ref_id = $item->getRefId();
294
295 if ($name_filter !== '' && mb_stripos($title, $name_filter) === false) {
296 continue;
297 }
298
299 if ($online_filter !== null && $online_filter !== '') {
300 $is_online = $item->isOnline();
301 if ($online_filter === 'online' && !$is_online) {
302 continue;
303 }
304 if ($online_filter === 'offline' && $is_online) {
305 continue;
306 }
307 }
308
309 if (count($position_filter) > 0) {
310 $is_start = ($ref_id === $start_ref_id);
311 $is_end = ($ref_id === $end_ref_id);
312 $matches_position =
313 (in_array('start', $position_filter, true) && $is_start)
314 || (in_array('end', $position_filter, true) && $is_end);
315 if (!$matches_position) {
316 continue;
317 }
318 }
319
320 $type = $item->getType();
321
322 $input_conditions = [];
323 $db_input_conditions = $condition_handler->getInputConditionsByRefId($lso_ref_id, $ref_id);
324 foreach ($db_input_conditions as $db_cond) {
325 $input_conditions[] = new ilObjLearningSequenceConditionData(
326 title: $db_cond['title'],
327 value: $db_cond['value'],
328 internal_name: $db_cond['internal_name']
329 );
330 }
331
332 $output_conditions = [];
333 $db_output_conditions = $condition_handler->getOutputConditionsByRefId($lso_ref_id, $ref_id);
334 foreach ($db_output_conditions as $db_cond) {
335 $output_conditions[] = new ilObjLearningSequenceConditionData(
336 title: $db_cond['title'],
337 value: $db_cond['value'],
338 internal_name: $db_cond['internal_name']
339 );
340 }
341
342 if (count($input_filter) > 0) {
343 $found = false;
344 foreach ($input_conditions as $ic) {
345 if (in_array($ic->internal_name, $input_filter)) {
346 $found = true;
347 break;
348 }
349 }
350 if (!$found) {
351 continue;
352 }
353 }
354
355 if (count($output_filter) > 0) {
356 $found = false;
357 foreach ($output_conditions as $oc) {
358 if (in_array($oc->internal_name, $output_filter)) {
359 $found = true;
360 break;
361 }
362 }
363 if (!$found) {
364 continue;
365 }
366 }
367
368 // Information
369 $prev_title = $this->getObjectTitleList($structural_predecessors[$ref_id] ?? []);
370 $next_title = $this->getObjectTitleList($structural_successors[$ref_id] ?? []);
371
372 $icon_path = ilObject::_getIcon($obj_id, "small", $type);
373 $actions = $this->parent_gui->getTableActionHandler()->collectActions(
374 $ref_id,
375 $this->getSpecificActions(
376 $ref_id,
377 $start_ref_id,
378 $end_ref_id
379 ),
380 $item->isOnline()
381 );
382
384 $ref_id,
385 $obj_id,
386 $title,
388 $type,
389 $icon_path,
390 ilLink::_getLink($ref_id, $type),
391 $item->isOnline(),
392 ($ref_id === $start_ref_id) ? 'Start' : '',
393 ($ref_id === $end_ref_id) ? 'End' : '',
394 $prev_title,
395 $next_title,
396 $input_conditions,
397 $output_conditions,
398 $this->isMisconfiguredRefId($ref_id, $misconfigured_ref_ids),
399 $issue_details_by_ref_id[$ref_id] ?? [],
400 ($structural_predecessors[$ref_id] ?? []) !== [],
401 ($structural_successors[$ref_id] ?? []) !== [],
402 $actions
403 );
404
405 $data[] = $row;
406 }
407
408 return $data;
409 }
410
416 protected function getInputConditionOptions(): array
417 {
418 return $this->buildConditionOptions(
419 $this->getConditionTypes()->getAllInputConditions()
420 );
421 }
422
428 protected function getOutputConditionOptions(): array
429 {
430 return $this->buildConditionOptions(
431 $this->getConditionTypes()->getAllOutputConditions()
432 );
433 }
434
439 {
440 return $this->getLSO()->getDI()["conditions.types"];
441 }
442
447 {
448 return $this->getLSO()->getDI()["db.boundaries"];
449 }
450
456 private function getLSO(): ilObjLearningSequence
457 {
458 if ($this->lso === null) {
460 $lso = ilObjLearningSequence::getInstanceByRefId($this->ref_id);
461 $this->lso = $lso;
462 }
463 return $this->lso;
464 }
465
474 private function buildConditionOptions(array $condition_classes): array
475 {
476 $options = [];
477 foreach ($condition_classes as $class) {
479 $condition = new $class();
480 $options[AbstractCondition::getIdentifierForClass($class)] = $this->lng->txt((string) $condition->getName());
481 }
482 return $options;
483 }
484
490 public function getSpecificActions(int $ref_id, int $start_ref_id, int $end_ref_id): array
491 {
492 $specific_actions = [];
493
494 // 1. Conditions
495 $this->ctrl->setParameterByClass(ilObjLearningSequenceConditionsGUI::class, 'item_ref_id', $ref_id);
496 $link = $this->ctrl->getLinkTargetByClass(ilObjLearningSequenceConditionsGUI::class, 'manageConditions');
497 $specific_actions['conditions'] = new ilObjLearningSequenceActionData(
498 label: $this->lng->txt('conditions'),
499 link: $link
500 );
501
502 // 2. Start / End Switcher
503 if ($ref_id > 0) {
504 if ($ref_id === $start_ref_id) {
505 $label = $this->lng->txt('unset_start_object');
507 $id = 'unset_start';
508 } else {
509 $label = $this->lng->txt('set_start_object');
511 $id = 'set_start';
512 }
513 $this->ctrl->setParameter($this->parent_gui, 'item_ref_id', $ref_id);
514 $link = $this->ctrl->getLinkTarget($this->parent_gui, $link);
515 $specific_actions[$id] = new ilObjLearningSequenceActionData(label: $label, link: $link);
516
517 if ($ref_id === $end_ref_id) {
518 $label = $this->lng->txt('unset_end_object');
520 $id = 'unset_end';
521 } else {
522 $label = $this->lng->txt('set_end_object');
524 $id = 'set_end';
525 }
526 $this->ctrl->setParameter($this->parent_gui, 'item_ref_id', $ref_id);
527 $link = $this->ctrl->getLinkTarget($this->parent_gui, $link);
528 $specific_actions[$id] = new ilObjLearningSequenceActionData(label: $label, link: $link);
529 } else {
530 // Template
531 $specific_actions['set_start'] = new ilObjLearningSequenceActionData(
532 label: $this->lng->txt('set_start_object'),
534 );
535 $specific_actions['unset_start'] = new ilObjLearningSequenceActionData(
536 label: $this->lng->txt('unset_start_object'),
538 );
539 $specific_actions['set_end'] = new ilObjLearningSequenceActionData(
540 label: $this->lng->txt('set_end_object'),
542 );
543 $specific_actions['unset_end'] = new ilObjLearningSequenceActionData(
544 label: $this->lng->txt('unset_end_object'),
546 );
547 }
548
549 $this->ctrl->setParameterByClass(ilObjLearningSequenceConditionsGUI::class, 'item_ref_id', '');
550 return $specific_actions;
551 }
552
556 public function setStartObject(): void
557 {
558 $ref_id = $this->parent_gui->extractItemRefId();
559 if ($ref_id > 0) {
560 $boundaries = $this->getBoundaries();
561 $current = $boundaries->getBoundariesFor($this->obj_id);
562 if ((int) $current['end_ref_id'] === $ref_id) {
563 // An object must never be start and end object at the same time.
564 $this->tpl->setOnScreenMessage(
565 'failure',
566 $this->lng->txt('lso_adaptive_start_end_not_same'),
567 true
568 );
569 $this->ctrl->redirect($this->parent_gui, ilObjLearningSequenceContentGUI::CMD_MANAGE_CONTENT);
570 }
571 $boundaries->setStartRefId($this->obj_id, $ref_id);
572 $this->tpl->setOnScreenMessage('success', $this->lng->txt('msg_obj_modified'), true);
573 }
574 $this->ctrl->redirect($this->parent_gui, ilObjLearningSequenceContentGUI::CMD_MANAGE_CONTENT);
575 }
576
580 public function unsetStartObject(): void
581 {
582 $boundaries = $this->getBoundaries();
583 $boundaries->unsetStartRefId($this->obj_id);
584 $this->tpl->setOnScreenMessage('success', $this->lng->txt('msg_obj_modified'), true);
585
586 $this->enforceOfflineForMissingBoundaries($this->getLSO());
587 $this->ctrl->redirect($this->parent_gui, ilObjLearningSequenceContentGUI::CMD_MANAGE_CONTENT);
588 }
589
593 public function setEndObject(): void
594 {
595 $ref_id = $this->parent_gui->extractItemRefId();
596 if ($ref_id > 0) {
597 $boundaries = $this->getBoundaries();
598 $current = $boundaries->getBoundariesFor($this->obj_id);
599 if ((int) $current['start_ref_id'] === $ref_id) {
600 // An object must never be start and end object at the same time.
601 $this->tpl->setOnScreenMessage(
602 'failure',
603 $this->lng->txt('lso_adaptive_start_end_not_same'),
604 true
605 );
606 $this->ctrl->redirect($this->parent_gui, ilObjLearningSequenceContentGUI::CMD_MANAGE_CONTENT);
607 }
608 $boundaries->setEndRefId($this->obj_id, $ref_id);
609 $this->tpl->setOnScreenMessage('success', $this->lng->txt('msg_obj_modified'), true);
610 }
611 $this->ctrl->redirect($this->parent_gui, ilObjLearningSequenceContentGUI::CMD_MANAGE_CONTENT);
612 }
613
617 public function unsetEndObject(): void
618 {
619 $boundaries = $this->getBoundaries();
620 $boundaries->unsetEndRefId($this->obj_id);
621 $this->tpl->setOnScreenMessage('success', $this->lng->txt('msg_obj_modified'), true);
622
623 $this->enforceOfflineForMissingBoundaries($this->getLSO());
624 $this->ctrl->redirect($this->parent_gui, ilObjLearningSequenceContentGUI::CMD_MANAGE_CONTENT);
625 }
626}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
Builds a Color from either hex- or rgb values.
Definition: Factory.php:31
Stores start and end object boundaries of adaptive learning sequences.
Controls adaptive learning sequence content management.
__construct(ilObjLearningSequenceContentGUI $parent_gui, Factory $ui_factory, Renderer $ui_renderer, ilLanguage $lng, ilCtrl $ctrl, ServerRequestInterface $request, ilGlobalTemplateInterface $tpl, int $ref_id, int $obj_id)
Creates the adaptive content controller.
setEndObject()
Sets the selected item as the end object.
ilObjLearningSequenceContentGUI $parent_gui
Parent content GUI.
manageContent()
Renders the adaptive content management view.
getConditionTypes()
Gets the condition types contributed to the learning sequence.
getOutputConditionOptions()
Gets output condition filter options.
getTableData(array $items, ?array $filter_data, array $structural_successors, array $misconfigured_ref_ids, array $issue_details_by_ref_id)
Gets the filtered content table data.
getStaticInputConfigurationIssues(array $items, AdaptiveNavigator $navigator)
getSupportedCommands()
Gets the commands supported by the controller.
int $ref_id
Learning sequence repository reference ID.
getBoundaries()
Gets the repository of the start and end objects.
getObjectTitleList(array $ref_ids)
Builds a readable list of object titles for the given ref ids.
getInputConditionOptions()
Gets input condition filter options.
isMisconfiguredRefId(int $ref_id, array $misconfigured_ref_ids)
setStartObject()
Sets the selected item as the start object.
getSpecificActions(int $ref_id, int $start_ref_id, int $end_ref_id)
Gets actions specific to an adaptive learning sequence item.
ilObjLearningSequence $lso
Learning sequence object, loaded on first use.
Creates the filter for adaptive learning sequence content.
Renders the adaptive learning sequence content table.
static getIdentifierForClass(string $class)
Extracts the identifier for the condition type from the class name.
Provides condition-based navigation through an adaptive learning sequence.
Data holding class LSItem .
Definition: LSItem.php:25
getRefId()
Definition: LSItem.php:93
Class ilCtrl provides processing control methods.
language handling
Data transfer object for an action in the learning sequence content table.
Data transfer object for a learning sequence condition.
@ilCtrl_isCalledBy ilObjLearningSequenceConditionsGUI: ilObjLearningSequenceContentGUI
Data transfer object for an item in the learning sequence content table.
static getInstanceByRefId(int $ref_id)
Class ilObject Basic functions for all objects.
static _getIcon(int $obj_id=0, string $size="big", string $type="", bool $offline=false)
Get icon for repository item.
static _lookupObjId(int $ref_id)
static _lookupTitle(int $obj_id)
static _lookupDescription(int $obj_id)
All condition types known to the system.
Contract for the mode specific content controllers (adaptive / sequential).
This is how the factory for UI elements looks.
Definition: Factory.php:38
An entity that renders components to a string output.
Definition: Renderer.php:31
enforceOfflineForMissingBoundaries(\ilObjLearningSequence $lso)
An adaptive learning sequence may only be online if it has a start AND an end object.
link(string $caption, string $href, bool $new_viewport=false)
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
if(!file_exists('../ilias.ini.php'))
$data
Definition: storeScorm.php:45