ILIAS  trunk Revision v11.0_alpha-1731-gff9cd7e2bd3
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
Processor.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
26 use ILIAS\MetaData\Elements\Factory as ElementFactory;
41 
42 class Processor implements ProcessorInterface
43 {
44  protected ElementFactory $element_factory;
50  protected \ilLogger $logger;
51 
52  public function __construct(
53  ElementFactory $element_factory,
54  MarkerFactoryInterface $marker_factory,
55  StructureSetInterface $structure_set,
56  DataValidatorInterface $data_validator,
57  DictionaryInterface $dictionary,
58  ElementHelperInterface $element_vocab_helper,
59  \ilLogger $logger
60  ) {
61  $this->element_factory = $element_factory;
62  $this->marker_factory = $marker_factory;
63  $this->structure_set = $structure_set;
64  $this->data_validator = $data_validator;
65  $this->dictionary = $dictionary;
66  $this->element_vocab_helper = $element_vocab_helper;
67  $this->logger = $logger;
68  }
69 
71  {
72  return $this->element_factory->set(
73  $set->getRessourceID(),
74  $this->getCleanRoot($set)
75  );
76  }
77 
78  protected function getCleanRoot(
79  SetInterface $set
80  ): ElementInterface {
81  $root = $set->getRoot();
82  if (!$this->data_validator->isValid($root, true)) {
83  throw new \ilMDRepositoryException('Invalid data on root');
84  }
85  return $this->element_factory->root(
86  $root->getDefinition(),
87  ...$this->getFinishedAndCleanSubElements($root, 0)
88  );
89  }
90 
94  protected function getFinishedAndCleanSubElements(
95  ElementInterface $element,
96  int $depth
97  ): \Generator {
98  if ($depth > 20) {
99  throw new \ilMDStructureException('LOM Structure is nested to deep.');
100  }
101  $sub_names = [];
102  foreach ($element->getSubElements() as $sub) {
103  $name = $sub->getDefinition()->name();
104  if ($sub->isScaffold()) {
105  continue;
106  }
107  if ($sub->getDefinition()->unique() && in_array($name, $sub_names)) {
108  $this->throwErrorOrLog($sub, 'duplicate of unique element.');
109  continue;
110  }
111  if ($this->data_validator->isValid($sub, true)) {
112  $sub_names[] = $name;
113  yield $this->element_factory->element(
114  $sub->getMDID(),
115  $sub->getDefinition(),
116  $sub->getData()->value(),
117  $this->lookUpVocabSlotForElement($sub),
118  ...$this->getFinishedAndCleanSubElements($sub, $depth + 1)
119  );
120  continue;
121  }
122  $message = $sub->getData()->value() . ' is not valid as ' .
123  $sub->getData()->type()->value . ' data.';
124  $this->throwErrorOrLog($sub, $message);
125  }
126  }
127 
129  {
130  if (
131  $element->getDefinition()->dataType() !== Type::VOCAB_VALUE &&
132  $element->getDefinition()->dataType() !== Type::STRING
133  ) {
134  return SlotIdentifier::NULL;
135  }
136  return $this->element_vocab_helper->slotForElement($element);
137  }
138 
139  public function cleanMarkers(SetInterface $set): void
140  {
141  $this->checkMarkerOnElement($set->getRoot(), true, 0);
142  }
143 
144  public function checkMarkers(SetInterface $set): void
145  {
146  $this->checkMarkerOnElement($set->getRoot(), false, 0);
147  }
148 
149  protected function checkMarkerOnElement(
150  ElementInterface $element,
151  bool $replace_by_neutral,
152  int $depth
153  ): void {
154  if ($depth > 20) {
155  throw new \ilMDStructureException('LOM Structure is nested to deep.');
156  }
157  if (!($element instanceof MarkableInterface) || !$element->isMarked()) {
158  return;
159  }
160  $marker = $element->getMarker();
161  if (
162  $marker->action() === Action::CREATE_OR_UPDATE &&
163  !$this->data_validator->isValid($element, false)
164  ) {
165  $message = $marker->dataValue() . ' is not valid as ' .
166  $element->getDefinition()->dataType()->value . ' data.';
167  $this->throwErrorOrLog($element, $message, !$replace_by_neutral);
168  $element->mark($this->marker_factory, Action::NEUTRAL);
169  }
170  foreach ($this->dictionary->tagsForElement($element) as $tag) {
171  $this->checkMarkerAgainstTag($tag, $element, $marker, $replace_by_neutral);
172  }
173  foreach ($element->getSubElements() as $sub) {
174  $this->checkMarkerOnElement($sub, $replace_by_neutral, $depth + 1);
175  }
176  }
177 
178  protected function checkMarkerAgainstTag(
179  TagInterface $tag,
180  ElementInterface $element,
181  MarkerInterface $marker,
182  bool $replace_by_neutral
183  ): void {
184  switch ($tag->restriction()) {
185  case Restriction::PRESET_VALUE:
186  if (
187  $this->willBeCreated($element, $marker) &&
188  $marker->dataValue() !== $tag->value()
189  ) {
190  $this->throwErrorOrLog(
191  $element,
192  'can only be created with preset value ' . $tag->value(),
193  !$replace_by_neutral
194  );
195  $element->mark($this->marker_factory, Action::NEUTRAL);
196  }
197  break;
198 
199  case Restriction::NOT_DELETABLE:
200  if ($marker->action() === Action::DELETE) {
201  $this->throwErrorOrLog($element, 'cannot be deleted.', !$replace_by_neutral);
202  $element->mark($this->marker_factory, Action::NEUTRAL);
203  }
204  break;
205 
207  if (
208  $marker->action() === Action::CREATE_OR_UPDATE &&
209  $element->getMDID() !== NoID::SCAFFOLD
210  ) {
211  $this->throwErrorOrLog($element, 'cannot be edited.', !$replace_by_neutral);
212  $element->mark($this->marker_factory, Action::NEUTRAL);
213  }
214  break;
215  }
216  }
217 
218  protected function willBeCreated(
219  ElementInterface $element,
220  MarkerInterface $marker
221  ): bool {
222  return $element->getMDID() === NoID::SCAFFOLD && (
223  $marker->action() === Action::CREATE_OR_UPDATE ||
224  $marker->action() === Action::NEUTRAL
225  );
226  }
227 
228  protected function throwErrorOrLog(
229  ElementInterface $element,
230  string $message,
231  bool $throw_error = false
232  ): void {
233  $id = $element->getMDID();
234  $id = is_int($id) ? (string) $id : $id->value;
235  $message = $element->getDefinition()->name() . ' (ID ' . $id . '): ' . $message;
236  if ($super = $element->getSuperElement()) {
237  $message = $super->getDefinition()->name() . ': ' . $message;
238  }
239  if ($throw_error) {
240  throw new \ilMDRepositoryException('Invalid marker on element ' . $message);
241  }
242  $this->logger->info('Skipping element ' . $message);
243  }
244 }
finishAndCleanData(SetInterface $set)
Returns a new metadata set, identical to the one given but with vocab slots in data filled out...
Definition: Processor.php:70
__construct(ElementFactory $element_factory, MarkerFactoryInterface $marker_factory, StructureSetInterface $structure_set, DataValidatorInterface $data_validator, DictionaryInterface $dictionary, ElementHelperInterface $element_vocab_helper, \ilLogger $logger)
Definition: Processor.php:52
lookUpVocabSlotForElement(ElementInterface $element)
Definition: Processor.php:128
mark(MarkerFactoryInterface $factory, Action $action, string $data_value='')
Leaves a trail of markers from this element up to the root element, or up to the first already marked...
checkMarkers(SetInterface $set)
Checks whether the proposed manipulations on the set via markers are valid.
Definition: Processor.php:144
getRoot()
Returns the root element of the metadata set.
throwErrorOrLog(ElementInterface $element, string $message, bool $throw_error=false)
Definition: Processor.php:228
ElementHelperInterface $element_vocab_helper
Definition: Processor.php:49
getRessourceID()
Contains the information needed to identify the ILIAS object this metadata set belongs to...
getFinishedAndCleanSubElements(ElementInterface $element, int $depth)
Definition: Processor.php:94
checkMarkerAgainstTag(TagInterface $tag, ElementInterface $element, MarkerInterface $marker, bool $replace_by_neutral)
Definition: Processor.php:178
cleanMarkers(SetInterface $set)
Checks whether the proposed manipulations on the set via markers are valid.
Definition: Processor.php:139
getDefinition()
Defining properties of the metadata element.
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
isMarked()
Elements can be marked to be created, updated or deleted.
$message
Definition: xapiexit.php:31
checkMarkerOnElement(ElementInterface $element, bool $replace_by_neutral, int $depth)
Definition: Processor.php:149
willBeCreated(ElementInterface $element, MarkerInterface $marker)
Definition: Processor.php:218