ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
ContentAssembler.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
40 
42 {
51  public const string GENERAL = 'general';
52  public const string KEYWORDS = 'keywords';
53 
54  public const string CLASSIFICATION = 'classification';
55  public const string LEARNING_RESOURCE_TYPE = 'learning_resource_type';
56  public const string DISCIPLINE = 'discipline';
57 
58  public const string AUTHORS = 'authors';
59  public const string FIRST_AUTHOR = 'first_author';
60  public const string SECOND_AUTHOR = 'second_author';
61  public const string THIRD_AUTHOR = 'third_author';
62  public const string PUBLISHER = 'publisher';
63 
64  public const string RIGHTS = 'rights';
65  public const string CUSTOM_CP = 'custom_cp';
66  public const string CUSTOM_CP_DESCRIPTION = 'custom_cp_description';
67  public const string OER_BLOCKED = 'oer_blocked_';
68 
72  protected Refinery $refinery;
78  protected VocabularyAdapter $vocabulary_adapter;
79 
80  public function __construct(
81  PathFactory $path_factory,
82  NavigatorFactoryInterface $navigator_factory,
83  UIFactory $factory,
84  Refinery $refinery,
85  PresenterInterface $presenter,
86  PathCollection $path_collection,
87  LinkFactory $link_factory,
88  CopyrightHandler $copyright_handler,
89  DataHelperInterface $data_helper,
90  VocabularyAdapter $vocabulary_adapter
91  ) {
92  $this->path_factory = $path_factory;
93  $this->navigator_factory = $navigator_factory;
94  $this->ui_factory = $factory;
95  $this->refinery = $refinery;
96  $this->presenter = $presenter;
97  $this->path_collection = $path_collection;
98  $this->link_factory = $link_factory;
99  $this->copyright_handler = $copyright_handler;
100  $this->data_helper = $data_helper;
101  $this->vocabulary_adapter = $vocabulary_adapter;
102  }
103 
107  public function get(
108  SetInterface $set,
109  ?RequestForFormInterface $request = null
110  ): \Generator {
111  $sections = [
112  self::GENERAL => $this->getGeneralSection($set),
113  self::CLASSIFICATION => $this->getClassificationSection($set),
114  self::AUTHORS => $this->getAuthorsSection($set)
115  ];
116  foreach ($this->getCopyrightContent($set) as $type => $entity) {
117  if ($type === ContentType::FORM) {
118  $sections[self::RIGHTS] = $entity;
119  continue;
120  }
121  yield $type => $entity;
122  }
123  $form = $this->ui_factory->input()->container()->form()->standard(
124  (string) $this->link_factory->custom(Command::UPDATE_DIGEST)->get(),
125  $sections
126  );
127 
128  if (isset($request)) {
129  $form = $request->applyRequestToForm($form);
130  }
131  yield ContentType::FORM => $form;
132  }
133 
134  protected function getGeneralSection(
135  SetInterface $set
136  ): Section {
137  $ff = $this->ui_factory->input()->field();
138  $root = $set->getRoot();
139  $inputs = [];
140 
141  $title_el = $this->navigator_factory->navigator(
142  $path = $this->path_collection->title(),
143  $root
144  )->lastElementAtFinalStep();
145  $inputs[$path->toString()] = $ff
146  ->text($this->presenter->utilities()->txt('meta_title'))
147  ->withRequired(true)
148  ->withValue($title_el?->getData()?->value() ?? '');
149 
150  $descr_els = $this->navigator_factory->navigator(
151  $descr_path = $this->path_collection->descriptions(),
152  $root
153  )->elementsAtFinalStep();
154  $descr_els = iterator_to_array($descr_els);
155  $label = $this->presenter->utilities()->txt('meta_description');
156  $empty_descr = true;
157  foreach ($descr_els as $el) {
158  $empty_descr = false;
159  $label_with_lang = $label;
160  foreach ($el->getSuperElement()->getSubElements() as $sub) {
161  if (
162  $sub->getDefinition()->name() !== 'language' ||
163  ($value = $sub->getData()->value()) === ''
164  ) {
165  continue;
166  }
167  $label_with_lang .= ' (' . $this->presenter->data()->language($value) . ')';
168  }
169  $inputs[$this->path_factory->toElement($el, true)->toString()] = $ff
170  ->textarea($label_with_lang)
171  ->withValue($el->getData()->value());
172  }
173  if ($empty_descr) {
174  $inputs[$descr_path->toString()] = $ff
175  ->textarea($label);
176  }
177 
178  $langs = [];
179  foreach ($this->data_helper->getAllLanguages() as $key) {
180  $langs[$key] = $this->presenter->data()->language($key);
181  }
182  $lang_input = $ff->select(
183  $this->presenter->utilities()->txt('meta_language'),
184  $langs
185  );
186  $lang_els = $this->navigator_factory->navigator(
187  $langs_path = $this->path_collection->languages(),
188  $root
189  )->elementsAtFinalStep();
190  $empty_langs = true;
191  foreach ($lang_els as $el) {
192  $empty_langs = false;
193  $inputs[$this->path_factory->toElement($el, true)->toString()] = (clone $lang_input)
194  ->withValue($el->getData()->value());
195  }
196  if ($empty_langs) {
197  $inputs[$langs_path->toString()] = clone $lang_input;
198  }
199 
200  $keywords = [];
201  $keyword_els = $this->navigator_factory->navigator(
202  $keywords_path = $this->path_collection->keywords(),
203  $root
204  )->elementsAtFinalStep();
205  foreach ($keyword_els as $el) {
206  if (!$el->isScaffold()) {
207  $keywords[] = $el->getData()->value();
208  }
209  }
210  $inputs[self::KEYWORDS] = $ff->tag(
211  $this->presenter->utilities()->txt('keywords'),
212  $keywords
213  )->withValue($keywords);
214 
215  return $ff->section(
216  $inputs,
217  $this->presenter->utilities()->txt('meta_general')
218  );
219  }
220 
221  protected function getClassificationSection(
222  SetInterface $set
223  ): Section {
224  $ff = $this->ui_factory->input()->field();
225  $inputs = [];
226 
227  $type_el = $this->navigator_factory->navigator(
228  $this->path_collection->firstLearningResourceType(),
229  $set->getRoot()
230  )->lastElementAtFinalStep();
231  $data = !$type_el?->isScaffold() ? $type_el?->getData()?->value() : null;
232  $values = [];
233  $raw_values = $this->vocabulary_adapter->valuesInVocabulariesForSlot(
234  SlotIdentifier::EDUCATIONAL_LEARNING_RESOURCE_TYPE,
235  $data
236  );
237  foreach ($this->presenter->data()->vocabularyValues(
238  SlotIdentifier::EDUCATIONAL_LEARNING_RESOURCE_TYPE,
239  ...$raw_values
240  ) as $labelled_value) {
241  $values[$labelled_value->value()] = $labelled_value->label();
242  }
243  $input = $this->ui_factory->input()->field()->select(
244  $this->presenter->utilities()->txt('meta_learning_resource_type'),
245  $values
246  );
247  if (isset($data)) {
248  $input = $input->withValue($data);
249  }
250  $inputs[self::LEARNING_RESOURCE_TYPE] = $input;
251 
252  $discipline_el = $this->navigator_factory->navigator(
253  $this->path_collection->firstDiscipline(),
254  $set->getRoot()
255  )->lastElementAtFinalStep();
256  $inputs[self::DISCIPLINE] = $this->buildStringFromControlledVocabInput(
257  $this->presenter->utilities()->txt('meta_discipline'),
259  !$discipline_el?->isScaffold() ? $discipline_el?->getData()?->value() : null
260  );
261 
262  return $ff->section(
263  $inputs,
264  $this->presenter->utilities()->txt('meta_classification')
265  );
266  }
267 
268  protected function getAuthorsSection(
269  SetInterface $set
270  ): Section {
271  $ff = $this->ui_factory->input()->field();
272  $inputs = [];
273 
274  $paths = [
275  $this->path_collection->firstAuthor(),
276  $this->path_collection->secondAuthor(),
277  $this->path_collection->thirdAuthor()
278  ];
279  $labels = [
280  $this->presenter->utilities()->txt('meta_first_author'),
281  $this->presenter->utilities()->txt('meta_second_author'),
282  $this->presenter->utilities()->txt('meta_third_author')
283  ];
284  $post_keys = [
285  self::FIRST_AUTHOR,
286  self::SECOND_AUTHOR,
287  self::THIRD_AUTHOR,
288  self::PUBLISHER
289  ];
290  foreach ($paths as $path) {
291  $el = $this->navigator_factory->navigator(
292  $path,
293  $set->getRoot()
294  )->lastElementAtFinalStep();
295  $inputs[array_shift($post_keys)] = $ff
296  ->text(array_shift($labels))
297  ->withValue($el?->getData()?->value() ?? '');
298  }
299 
300  $publisher_el = $this->navigator_factory->navigator(
301  $this->path_collection->firstPublisher(),
302  $set->getRoot()
303  )->lastElementAtFinalStep();
304  $inputs[self::PUBLISHER] = $this->buildStringFromControlledVocabInput(
305  $this->presenter->utilities()->txt('meta_publisher'),
306  SlotIdentifier::LIFECYCLE_CONTRIBUTE_PUBLISHER,
307  $publisher_el?->getData()?->value()
308  );
309 
310  return $ff->section(
311  $inputs,
312  $this->presenter->utilities()->txt('meta_authors')
313  );
314  }
315 
319  protected function getCopyrightContent(
320  SetInterface $set
321  ): \Generator {
322  if (!$this->copyright_handler->isCPSelectionActive()) {
323  return;
324  }
325  $modal = $this->getChangeCopyrightModal(false);
326  $modal_with_oer_warning = $this->getChangeCopyrightModal(true);
327  $signal = $modal->getShowSignal();
328  $signal_with_oer_warning = $modal_with_oer_warning->getShowSignal();
329 
330  yield ContentType::MODAL => $modal;
331  yield ContentType::MODAL => $modal_with_oer_warning;
332  yield ContentType::JS_SOURCE => 'assets/js/ilMetaCopyrightListener.js';
333  yield ContentType::FORM => $this->getCopyrightSection(
334  $set,
335  $signal,
336  $signal_with_oer_warning
337  );
338  }
339 
340  protected function getChangeCopyrightModal(bool $with_oer_warning): InterruptiveModal
341  {
342  $message = $this->presenter->utilities()->txt("meta_copyright_change_info");
343  if ($with_oer_warning) {
344  $message .= "<br/><br/>" . $this->presenter->utilities()->txt("meta_copyright_change_oer_info");
345  }
346  $modal = $this->ui_factory->modal()->interruptive(
347  $this->presenter->utilities()->txt("meta_copyright_change_warning_title"),
348  $message,
349  (string) $this->link_factory->custom(Command::UPDATE_DIGEST)->get()
350  );
351 
352  return $modal;
353  }
354 
355  protected function getCopyrightSection(
356  SetInterface $set,
357  Signal $signal,
358  Signal $signal_with_oer_warning
359  ): Section {
360  $ff = $this->ui_factory->input()->field();
361 
362  $cp_description_el = $this->navigator_factory->navigator(
363  $this->path_collection->copyright(),
364  $set->getRoot()
365  )->lastElementAtFinalStep();
366  $cp_description = $cp_description_el?->getData()->value();
367 
368  $current_id = $this->copyright_handler->extractCPEntryID((string) $cp_description);
369  $default_id = 0;
370  $options = [];
371  $outdated = [];
372  $potential_oer_values = [];
373  $current_id_exists = false;
374  $is_custom = !is_null($cp_description) && !$current_id;
375 
376  foreach ($this->copyright_handler->getCPEntries() as $entry) {
377  if ($entry->isDefault()) {
378  $default_id = $entry->id();
379  }
380  if ($current_id === $entry->id()) {
381  $current_id_exists = true;
382  }
383 
384  $identifier = $this->copyright_handler->createIdentifierForID($entry->id());
385 
386  //give the option to block harvesting
387  $sub_inputs = [];
388  if (
389  $this->copyright_handler->isObjectTypeHarvested($set->getRessourceID()->type()) &&
390  $this->copyright_handler->isCopyrightTemplateActive($entry)
391  ) {
392  $sub_inputs[self::OER_BLOCKED] = $ff
393  ->checkbox(
394  $this->presenter->utilities()->txt('meta_oer_blocked'),
395  $this->presenter->utilities()->txt('meta_oer_blocked_info')
396  )
397  ->withValue(
398  $this->copyright_handler->isOerHarvesterBlocked($set->getRessourceID()->objID())
399  );
400  $potential_oer_values[] = $identifier;
401  }
402 
403  $option = $ff->group($sub_inputs, $entry->title(), $entry->description());
404 
405  // outdated entries throw an error when selected
406  if ($entry->isOutdated()) {
407  $option = $option->withLabel(
408  '(' . $this->presenter->utilities()->txt('meta_copyright_outdated') .
409  ') ' . $entry->title()
410  )->withDisabled(true);
411  $outdated[] = $identifier;
412  }
413  $options[$identifier] = $option;
414  }
415 
416  //custom input as the last option
417  $custom_text = $ff
418  ->textarea($this->presenter->utilities()->txt('meta_description'))
419  ->withValue($is_custom ? (string) $cp_description : '');
420  $custom = $ff->group(
421  [self::CUSTOM_CP_DESCRIPTION => $custom_text],
422  $this->presenter->utilities()->txt('meta_cp_own')
423  );
424  $options[self::CUSTOM_CP] = $custom;
425 
426  $value = self::CUSTOM_CP;
427  if (!$is_custom) {
428  $id = ($current_id && $current_id_exists) ? $current_id : $default_id;
429  $value = $this->copyright_handler->createIdentifierForID($id);
430  }
431 
432  $copyright = $ff
433  ->switchableGroup(
434  $options,
435  $this->presenter->utilities()->txt('meta_copyright')
436  )
437  ->withValue($value)
438  ->withAdditionalOnLoadCode(
439  function ($id) use ($signal, $signal_with_oer_warning, $potential_oer_values) {
440  return 'il.MetaDataCopyrightListener.init(\'' .
441  $signal . '\',\'' .
442  $signal_with_oer_warning . '\',\'' .
443  json_encode($potential_oer_values) . '\',\'' .
444  $id . '\');';
445  }
446  )->withAdditionalTransformation(
447  $this->refinery->custom()->constraint(
448  function ($v) use ($outdated) {
449  if (in_array($v[0], $outdated, true)) {
450  return false;
451  }
452  return true;
453  },
454  $this->presenter->utilities()->txt('meta_copyright_outdated_error')
455  )
456  );
457 
458  return $ff->section(
459  [$copyright],
460  $this->presenter->utilities()->txt('meta_rights')
461  );
462  }
463 
464  protected function buildStringFromControlledVocabInput(
465  string $label,
466  SlotIdentifier $slot,
467  ?string $data = null
468  ): Input {
469  if (!$this->vocabulary_adapter->doesSlotHaveVocabularies($slot)) {
470  $input = $this->ui_factory->input()->field()->text($label);
471  if (isset($data)) {
472  $input = $input->withValue($data);
473  }
474  return $input;
475  }
476 
477  $values = [];
478  $raw_values = iterator_to_array($this->vocabulary_adapter->valuesInVocabulariesForSlot(
479  $slot,
480  !$this->vocabulary_adapter->doesSlotAllowCustomInput($slot) ? $data : null
481  ));
482  foreach ($this->presenter->data()->vocabularyValues($slot, ...$raw_values) as $labelled_value) {
483  $values[$labelled_value->value()] = $labelled_value->label();
484  }
485 
486  if (!$this->vocabulary_adapter->doesSlotAllowCustomInput($slot)) {
487  $input = $this->ui_factory->input()->field()->select($label, $values);
488  if (isset($data)) {
489  $input = $input->withValue($data);
490  }
491  return $input;
492  }
493 
494  $value_label = $this->presenter->utilities()->txt('md_editor_value');
495  $text_input = $this->ui_factory->input()->field()->text($value_label);
496  $select_input = $this->ui_factory->input()->field()->select($value_label, $values);
497 
498  $radio_value = 'from_vocab';
499  if (isset($data)) {
500  if (in_array($data, $raw_values)) {
501  $select_input = $select_input->withValue($data);
502  $radio_value = 'from_vocab';
503  } else {
504  $text_input = $text_input->withValue($data);
505  $radio_value = 'custom';
506  }
507  }
508 
509  $input = $this->ui_factory->input()->field()->switchableGroup(
510  [
511  'from_vocab' => $this->ui_factory->input()->field()->group(
512  ['value' => $select_input],
513  $this->presenter->utilities()->txt('md_editor_from_vocab_input')
514  ),
515  'custom' => $this->ui_factory->input()->field()->group(
516  ['value' => $text_input],
517  $this->presenter->utilities()->txt('md_editor_custom_input')
518  )
519  ],
520  $label
521  );
522  if (isset($radio_value)) {
523  $input = $input->withValue($radio_value);
524  }
525  return $input->withAdditionalTransformation(
526  $this->refinery->custom()->transformation(function ($vs) {
527  return $vs[1]['value'] ?? null;
528  })
529  );
530  }
531 }
__construct(PathFactory $path_factory, NavigatorFactoryInterface $navigator_factory, UIFactory $factory, Refinery $refinery, PresenterInterface $presenter, PathCollection $path_collection, LinkFactory $link_factory, CopyrightHandler $copyright_handler, DataHelperInterface $data_helper, VocabularyAdapter $vocabulary_adapter)
buildStringFromControlledVocabInput(string $label, SlotIdentifier $slot, ?string $data=null)
getCopyrightSection(SetInterface $set, Signal $signal, Signal $signal_with_oer_warning)
$path
Definition: ltiservices.php:29
getRoot()
Returns the root element of the metadata set.
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
getRessourceID()
Contains the information needed to identify the ILIAS object this metadata set belongs to...
withValue($value)
Get an input like this with another value displayed on the client side.
Definition: Group.php:61
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
custom()
expected output: > ILIAS shows a base horizontal bar chart but customized with e.g.
Definition: custom.php:33
$message
Definition: xapiexit.php:31