TYPO3 CMS  TYPO3_8-7
NodeFactory.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Backend\Form;
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  'soloFieldContainer' => Container\SoloFieldContainer::class,
67  'tabsContainer' => Container\TabsContainer::class,
68 
69  // Default single element classes
70  'check' => Element\CheckboxElement::class,
71  'group' => Element\GroupElement::class,
72  'input' => Element\InputTextElement::class,
73  'inputDateTime' => Element\InputDateTimeElement::class,
74  'inputLink' => Element\InputLinkElement::class,
75  'hidden' => Element\InputHiddenElement::class,
76  // rsaInput is defined with a fallback so extensions can use it even if ext:rsaauth is not loaded
77  'rsaInput' => Element\InputTextElement::class,
78  'imageManipulation' => Element\ImageManipulationElement::class,
79  'none' => Element\NoneElement::class,
80  'radio' => Element\RadioElement::class,
81  'selectCheckBox' => Element\SelectCheckBoxElement::class,
82  'selectMultipleSideBySide' => Element\SelectMultipleSideBySideElement::class,
83  'selectTree' => Element\SelectTreeElement::class,
84  'selectSingle' => Element\SelectSingleElement::class,
85  'selectSingleBox' => Element\SelectSingleBoxElement::class,
86  'colorpicker' => Element\InputColorPickerElement::class,
87  // t3editor is defined with a fallback so extensions can use it even if ext:t3editor is not loaded
88  't3editor' => Element\TextElement::class,
89  'text' => Element\TextElement::class,
90  'textTable' => Element\TextTableElement::class,
91  'unknown' => Element\UnknownElement::class,
92  'user' => Element\UserElement::class,
93  'fileInfo' => Element\FileInfoElement::class,
94 
95  // Default classes to enrich single elements
96  'fieldControl' => NodeExpansion\FieldControl::class,
97  'fieldInformation' => NodeExpansion\FieldInformation::class,
98  'fieldWizard' => NodeExpansion\FieldWizard::class,
99 
100  // Element wizards
101  'defaultLanguageDifferences' => FieldWizard\DefaultLanguageDifferences::class,
102  'fileThumbnails' => FieldWizard\FileThumbnails::class,
103  'fileTypeList' => FieldWizard\FileTypeList::class,
104  'fileUpload' => FieldWizard\FileUpload::class,
105  'localizationStateSelector' => FieldWizard\LocalizationStateSelector::class,
106  'otherLanguageContent' => FieldWizard\OtherLanguageContent::class,
107  'recordsOverview' => FieldWizard\RecordsOverview::class,
108  'selectIcons' => FieldWizard\SelectIcons::class,
109  'tableList' => FieldWizard\TableList::class,
110 
111  // Element controls
112  'addRecord' => FieldControl\AddRecord::class,
113  'editPopup' => FieldControl\EditPopup::class,
114  'elementBrowser' => FieldControl\ElementBrowser::class,
115  'insertClipboard' => FieldControl\InsertClipboard::class,
116  'linkPopup' => FieldControl\LinkPopup::class,
117  'listModule' => FieldControl\ListModule::class,
118  'resetSelection' => FieldControl\ResetSelection::class,
119  'tableWizard' => FieldControl\TableWizard::class,
120  ];
121 
125  public function __construct()
126  {
128  $this->initializeNodeResolver();
129  }
130 
138  public function create(array $data)
139  {
140  if (empty($data['renderType'])) {
141  throw new Exception(
142  'Missing "renderType" in TCA of field "[' . $data['tableName'] . '][' . $data['fieldName'] . ']".',
143  1431452406
144  );
145  }
146  $type = $data['renderType'];
147 
148  $className = isset($this->nodeTypes[$type]) ? $this->nodeTypes[$type] : $this->nodeTypes['unknown'];
149 
150  if (!empty($this->nodeResolver[$type])) {
151  // Resolver with highest priority is called first. If it returns with a new class name,
152  // it will be taken and loop is aborted, otherwise resolver with next lower priority is called.
153  foreach ($this->nodeResolver[$type] as $priority => $resolverClassName) {
155  $resolver = $this->instantiate($resolverClassName, $data);
156  if (!$resolver instanceof NodeResolverInterface) {
157  throw new Exception(
158  'Node resolver for type ' . $type . ' at priority ' . $priority . ' must implement NodeResolverInterface',
159  1433157422
160  );
161  }
162  // Resolver classes do NOT receive the name of the already resolved class. Single
163  // resolvers should not have dependencies to each other or the default implementation,
164  // so they also shouldn't know the output of a different resolving class.
165  // Additionally, the globalOptions array is NOT given by reference here, changing config is a
166  // task of container classes alone and must not be abused here.
167  $newClassName = $resolver->resolve();
168  if ($newClassName !== null) {
169  $className = $newClassName;
170  break;
171  }
172  }
173  }
174 
176  $nodeInstance = $this->instantiate($className, $data);
177  if (!$nodeInstance instanceof NodeInterface) {
178  throw new Exception('Node of type ' . get_class($nodeInstance) . ' must implement NodeInterface', 1431872546);
179  }
180  return $nodeInstance;
181  }
182 
191  {
192  // List of additional or override nodes
193  $registeredTypeOverrides = $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'];
194  // Sanitize input array
195  $registeredPrioritiesForNodeNames = [];
196  foreach ($registeredTypeOverrides as $override) {
197  if (!isset($override['nodeName']) || !isset($override['class']) || !isset($override['priority'])) {
198  throw new Exception(
199  'Key class, nodeName or priority missing for an entry in $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'formEngine\'][\'nodeRegistry\']',
200  1432207533
201  );
202  }
203  if ($override['priority'] < 0 || $override['priority'] > 100) {
204  throw new Exception(
205  'Priority of element ' . $override['nodeName'] . ' with class ' . $override['class'] . ' is ' . $override['priority'] . ', but must between 0 and 100',
206  1432223531
207  );
208  }
209  if (isset($registeredPrioritiesForNodeNames[$override['nodeName']][$override['priority']])) {
210  throw new Exception(
211  'Element ' . $override['nodeName'] . ' already has an override registered with priority ' . $override['priority'],
212  1432223893
213  );
214  }
215  $registeredPrioritiesForNodeNames[$override['nodeName']][$override['priority']] = '';
216  }
217  // Add element with highest priority to registry
218  $highestPriority = [];
219  foreach ($registeredTypeOverrides as $override) {
220  if (!isset($highestPriority[$override['nodeName']]) || $override['priority'] > $highestPriority[$override['nodeName']]) {
221  $highestPriority[$override['nodeName']] = $override['priority'];
222  $this->nodeTypes[$override['nodeName']] = $override['class'];
223  }
224  }
225  }
226 
233  protected function initializeNodeResolver()
234  {
235  // List of node resolver
236  $registeredNodeResolvers = $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeResolver'];
237  $resolversByType = [];
238  foreach ($registeredNodeResolvers as $nodeResolver) {
239  if (!isset($nodeResolver['nodeName']) || !isset($nodeResolver['class']) || !isset($nodeResolver['priority'])) {
240  throw new Exception(
241  'Key class, nodeName or priority missing for an entry in $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'formEngine\'][\'nodeResolver\']',
242  1433155522
243  );
244  }
245  if ($nodeResolver['priority'] < 0 || $nodeResolver['priority'] > 100) {
246  throw new Exception(
247  'Priority of element ' . $nodeResolver['nodeName'] . ' with class ' . $nodeResolver['class'] . ' is ' . $nodeResolver['priority'] . ', but must between 0 and 100',
248  1433155563
249  );
250  }
251  if (isset($resolversByType[$nodeResolver['nodeName']][$nodeResolver['priority']])) {
252  throw new Exception(
253  'Element ' . $nodeResolver['nodeName'] . ' already has a resolver registered with priority ' . $nodeResolver['priority'],
254  1433155705
255  );
256  }
257  $resolversByType[$nodeResolver['nodeName']][$nodeResolver['priority']] = $nodeResolver['class'];
258  }
259  $sortedResolversByType = [];
260  foreach ($resolversByType as $nodeName => $prioritiesAndClasses) {
261  krsort($prioritiesAndClasses);
262  $sortedResolversByType[$nodeName] = $prioritiesAndClasses;
263  }
264  $this->nodeResolver = $sortedResolversByType;
265  }
266 
274  protected function instantiate($className, array $data)
275  {
276  return GeneralUtility::makeInstance($className, $this, $data);
277  }
278 }
static makeInstance($className,... $constructorArguments)
instantiate($className, array $data)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']