‪TYPO3CMS  9.5
NodeFactory.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
18 
36 {
43  protected ‪$nodeResolver = [];
44 
50  protected ‪$nodeTypes = [
51  // Default container classes
52  'flex' => Container\FlexFormEntryContainer::class,
53  'flexFormContainerContainer' => Container\FlexFormContainerContainer::class,
54  'flexFormElementContainer' => Container\FlexFormElementContainer::class,
55  'flexFormNoTabsContainer' => Container\FlexFormNoTabsContainer::class,
56  'flexFormSectionContainer' => Container\FlexFormSectionContainer::class,
57  'flexFormTabsContainer' => Container\FlexFormTabsContainer::class,
58  'fullRecordContainer' => Container\FullRecordContainer::class,
59  'inline' => Container\InlineControlContainer::class,
60  'inlineRecordContainer' => Container\InlineRecordContainer::class,
61  'listOfFieldsContainer' => Container\ListOfFieldsContainer::class,
62  'noTabsContainer' => Container\NoTabsContainer::class,
63  'outerWrapContainer' => Container\OuterWrapContainer::class,
64  'paletteAndSingleContainer' => Container\PaletteAndSingleContainer::class,
65  'singleFieldContainer' => Container\SingleFieldContainer::class,
66  'tabsContainer' => Container\TabsContainer::class,
67 
68  // Default single element classes
69  'check' => Element\CheckboxElement::class,
70  'checkboxToggle' => Element\CheckboxToggleElement::class,
71  'checkboxLabeledToggle' => Element\CheckboxLabeledToggleElement::class,
72  'group' => Element\GroupElement::class,
73  'input' => Element\InputTextElement::class,
74  'inputDateTime' => Element\InputDateTimeElement::class,
75  'inputLink' => Element\InputLinkElement::class,
76  'hidden' => Element\InputHiddenElement::class,
77  // rsaInput is defined with a fallback so extensions can use it even if ext:rsaauth is not loaded
78  'rsaInput' => Element\InputTextElement::class,
79  'imageManipulation' => Element\ImageManipulationElement::class,
80  'none' => Element\NoneElement::class,
81  'radio' => Element\RadioElement::class,
82  'selectCheckBox' => Element\SelectCheckBoxElement::class,
83  'selectMultipleSideBySide' => Element\SelectMultipleSideBySideElement::class,
84  'selectTree' => Element\SelectTreeElement::class,
85  'selectSingle' => Element\SelectSingleElement::class,
86  'selectSingleBox' => Element\SelectSingleBoxElement::class,
87  'colorpicker' => Element\InputColorPickerElement::class,
88  // t3editor is defined with a fallback so extensions can use it even if ext:t3editor is not loaded
89  't3editor' => Element\TextElement::class,
90  'text' => Element\TextElement::class,
91  'textTable' => Element\TextTableElement::class,
92  'unknown' => Element\UnknownElement::class,
93  'user' => Element\UserElement::class,
94  // special renderType for type="user" on sys_file_storage is_public column
95  'userSysFileStorageIsPublic' => Element\UserSysFileStorageIsPublicElement::class,
96  'fileInfo' => Element\FileInfoElement::class,
97  'slug' => Element\InputSlugElement::class,
98  'passthrough' => Element\PassThroughElement::class,
99 
100  // Default classes to enrich single elements
101  'fieldControl' => NodeExpansion\FieldControl::class,
102  'fieldInformation' => NodeExpansion\FieldInformation::class,
103  'fieldWizard' => NodeExpansion\FieldWizard::class,
104 
105  // Element information
106  'tcaDescription' => FieldInformation\TcaDescription::class,
107  'adminIsSystemMaintainer' => FieldInformation\AdminIsSystemMaintainer::class,
108 
109  // Element wizards
110  'defaultLanguageDifferences' => FieldWizard\DefaultLanguageDifferences::class,
111  'fileThumbnails' => FieldWizard\FileThumbnails::class,
112  'fileTypeList' => FieldWizard\FileTypeList::class,
113  'fileUpload' => FieldWizard\FileUpload::class,
114  'localizationStateSelector' => FieldWizard\LocalizationStateSelector::class,
115  'otherLanguageContent' => FieldWizard\OtherLanguageContent::class,
116  'recordsOverview' => FieldWizard\RecordsOverview::class,
117  'selectIcons' => FieldWizard\SelectIcons::class,
118  'tableList' => FieldWizard\TableList::class,
119 
120  // Element controls
121  'addRecord' => FieldControl\AddRecord::class,
122  'editPopup' => FieldControl\EditPopup::class,
123  'elementBrowser' => FieldControl\ElementBrowser::class,
124  'insertClipboard' => FieldControl\InsertClipboard::class,
125  'linkPopup' => FieldControl\LinkPopup::class,
126  'listModule' => FieldControl\ListModule::class,
127  'resetSelection' => FieldControl\ResetSelection::class,
128  'tableWizard' => FieldControl\TableWizard::class,
129  ];
130 
134  public function ‪__construct()
135  {
137  $this->‪initializeNodeResolver();
138  }
139 
147  public function ‪create(array $data)
148  {
149  if (empty($data['renderType'])) {
150  throw new ‪Exception(
151  'Missing "renderType" in TCA of field "[' . ($data['tableName'] ?? 'unknown') . '][' . ($data['fieldName'] ?? 'unknown') . ']".',
152  1431452406
153  );
154  }
155  $type = $data['renderType'];
156 
157  $className = $this->nodeTypes[$type] ?? $this->nodeTypes['unknown'];
158 
159  if (!empty($this->nodeResolver[$type])) {
160  // Resolver with highest priority is called first. If it returns with a new class name,
161  // it will be taken and loop is aborted, otherwise resolver with next lower priority is called.
162  foreach ($this->nodeResolver[$type] as $priority => $resolverClassName) {
164  $resolver = $this->‪instantiate($resolverClassName, $data);
165  if (!$resolver instanceof ‪NodeResolverInterface) {
166  throw new ‪Exception(
167  'Node resolver for type ' . $type . ' at priority ' . $priority . ' must implement NodeResolverInterface',
168  1433157422
169  );
170  }
171  // Resolver classes do NOT receive the name of the already resolved class. Single
172  // resolvers should not have dependencies to each other or the default implementation,
173  // so they also shouldn't know the output of a different resolving class.
174  // Additionally, the globalOptions array is NOT given by reference here, changing config is a
175  // task of container classes alone and must not be abused here.
176  $newClassName = $resolver->resolve();
177  if ($newClassName !== null) {
178  $className = $newClassName;
179  break;
180  }
181  }
182  }
183 
185  $nodeInstance = $this->‪instantiate($className, $data);
186  if (!$nodeInstance instanceof ‪NodeInterface) {
187  throw new ‪Exception('Node of type ' . get_class($nodeInstance) . ' must implement NodeInterface', 1431872546);
188  }
189  return $nodeInstance;
190  }
191 
200  {
201  // List of additional or override nodes
202  $registeredTypeOverrides = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'];
203  // Sanitize input array
204  $registeredPrioritiesForNodeNames = [];
205  foreach ($registeredTypeOverrides as $override) {
206  if (!isset($override['nodeName']) || !isset($override['class']) || !isset($override['priority'])) {
207  throw new ‪Exception(
208  'Key class, nodeName or priority missing for an entry in $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'formEngine\'][\'nodeRegistry\']',
209  1432207533
210  );
211  }
212  if ($override['priority'] < 0 || $override['priority'] > 100) {
213  throw new ‪Exception(
214  'Priority of element ' . $override['nodeName'] . ' with class ' . $override['class'] . ' is ' . $override['priority'] . ', but must between 0 and 100',
215  1432223531
216  );
217  }
218  if (isset($registeredPrioritiesForNodeNames[$override['nodeName']][$override['priority']])) {
219  throw new ‪Exception(
220  'Element ' . $override['nodeName'] . ' already has an override registered with priority ' . $override['priority'],
221  1432223893
222  );
223  }
224  $registeredPrioritiesForNodeNames[$override['nodeName']][$override['priority']] = '';
225  }
226  // Add element with highest priority to registry
227  $highestPriority = [];
228  foreach ($registeredTypeOverrides as $override) {
229  if (!isset($highestPriority[$override['nodeName']]) || $override['priority'] > $highestPriority[$override['nodeName']]) {
230  $highestPriority[$override['nodeName']] = $override['priority'];
231  $this->nodeTypes[$override['nodeName']] = $override['class'];
232  }
233  }
234  }
235 
242  protected function ‪initializeNodeResolver()
243  {
244  // List of node resolver
245  $registeredNodeResolvers = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeResolver'];
246  $resolversByType = [];
247  foreach ($registeredNodeResolvers as ‪$nodeResolver) {
248  if (!isset(‪$nodeResolver['nodeName']) || !isset(‪$nodeResolver['class']) || !isset(‪$nodeResolver['priority'])) {
249  throw new ‪Exception(
250  'Key class, nodeName or priority missing for an entry in $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'formEngine\'][\'nodeResolver\']',
251  1433155522
252  );
253  }
254  if (‪$nodeResolver['priority'] < 0 || ‪$nodeResolver['priority'] > 100) {
255  throw new ‪Exception(
256  'Priority of element ' . ‪$nodeResolver['nodeName'] . ' with class ' . ‪$nodeResolver['class'] . ' is ' . ‪$nodeResolver['priority'] . ', but must between 0 and 100',
257  1433155563
258  );
259  }
260  if (isset($resolversByType[‪$nodeResolver['nodeName']][‪$nodeResolver['priority']])) {
261  throw new ‪Exception(
262  'Element ' . ‪$nodeResolver['nodeName'] . ' already has a resolver registered with priority ' . ‪$nodeResolver['priority'],
263  1433155705
264  );
265  }
266  $resolversByType[‪$nodeResolver['nodeName']][‪$nodeResolver['priority']] = ‪$nodeResolver['class'];
267  }
268  $sortedResolversByType = [];
269  foreach ($resolversByType as $nodeName => $prioritiesAndClasses) {
270  krsort($prioritiesAndClasses);
271  $sortedResolversByType[$nodeName] = $prioritiesAndClasses;
272  }
273  $this->nodeResolver = $sortedResolversByType;
274  }
275 
283  protected function ‪instantiate($className, array $data)
284  {
285  return GeneralUtility::makeInstance($className, $this, $data);
286  }
287 }
‪TYPO3\CMS\Backend\Form
Definition: AbstractNode.php:3
‪TYPO3\CMS\Backend\Form\NodeFactory\$nodeResolver
‪array $nodeResolver
Definition: NodeFactory.php:42
‪TYPO3\CMS\Backend\Form\NodeFactory\initializeNodeResolver
‪initializeNodeResolver()
Definition: NodeFactory.php:240
‪TYPO3\CMS\Backend\Form\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Backend\Form\NodeFactory\registerAdditionalNodeTypesFromConfiguration
‪registerAdditionalNodeTypesFromConfiguration()
Definition: NodeFactory.php:197
‪TYPO3\CMS\Backend\Form\NodeInterface
Definition: NodeInterface.php:21
‪TYPO3\CMS\Backend\Form\NodeFactory
Definition: NodeFactory.php:36
‪TYPO3\CMS\Backend\Form\NodeResolverInterface
Definition: NodeResolverInterface.php:21
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Backend\Form\NodeFactory\instantiate
‪object instantiate($className, array $data)
Definition: NodeFactory.php:281
‪TYPO3\CMS\Backend\Form\AbstractNode
Definition: AbstractNode.php:27
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Backend\Form\NodeFactory\$nodeTypes
‪array $nodeTypes
Definition: NodeFactory.php:48
‪TYPO3\CMS\Backend\Form\NodeFactory\create
‪AbstractNode create(array $data)
Definition: NodeFactory.php:145
‪TYPO3\CMS\Backend\Form\NodeFactory\__construct
‪__construct()
Definition: NodeFactory.php:132