TYPO3 CMS  TYPO3_7-6
FlexFormSectionContainer.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 
23 
33 {
39  public function render()
40  {
42  $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
43  $languageService = $this->getLanguageService();
44 
45  $flexFormFieldsArray = $this->data['flexFormDataStructureArray'];
46  $flexFormRowData = $this->data['flexFormRowData'];
47  $flexFormFieldIdentifierPrefix = $this->data['flexFormFieldIdentifierPrefix'];
48  $flexFormSectionType = $this->data['flexFormSectionType'];
49  $flexFormSectionTitle = $this->data['flexFormSectionTitle'];
50 
51  $userHasAccessToDefaultLanguage = $this->getBackendUserAuthentication()->checkLanguageAccess(0);
52 
53  $resultArray = $this->initializeResultArray();
54 
55  // Creating IDs for form fields:
56  // It's important that the IDs "cascade" - otherwise we can't dynamically expand the flex form
57  // because this relies on simple string substitution of the first parts of the id values.
58  $flexFormFieldIdentifierPrefix = $flexFormFieldIdentifierPrefix . '-' . GeneralUtility::shortMd5(uniqid('id', true));
59 
60  // Render each existing container
61  foreach ($flexFormRowData as $flexFormContainerCounter => $existingSectionContainerData) {
62  // @todo: This relies on the fact that "_TOGGLE" is *below* the real data in the saved xml structure
63  if (is_array($existingSectionContainerData)) {
64  $existingSectionContainerDataStructureType = key($existingSectionContainerData);
65  $existingSectionContainerData = $existingSectionContainerData[$existingSectionContainerDataStructureType];
66  $containerDataStructure = $flexFormFieldsArray[$existingSectionContainerDataStructureType];
67  // There may be cases where a field is still in DB but does not exist in definition
68  if (is_array($containerDataStructure)) {
69  $sectionTitle = '';
70  if (!empty(trim($containerDataStructure['title']))) {
71  $sectionTitle = $languageService->sL(trim($containerDataStructure['title']));
72  }
73 
74  $options = $this->data;
75  $options['flexFormRowData'] = $existingSectionContainerData['el'];
76  $options['flexFormDataStructureArray'] = $containerDataStructure['el'];
77  $options['flexFormFieldIdentifierPrefix'] = $flexFormFieldIdentifierPrefix;
78  $options['flexFormFormPrefix'] = $this->data['flexFormFormPrefix'] . '[' . $flexFormSectionType . ']' . '[el]';
79  $options['flexFormContainerName'] = $existingSectionContainerDataStructureType;
80  $options['flexFormContainerCounter'] = $flexFormContainerCounter;
81  $options['flexFormContainerTitle'] = $sectionTitle;
82  $options['flexFormContainerElementCollapsed'] = (bool)$existingSectionContainerData['el']['_TOGGLE'];
83  $options['renderType'] = 'flexFormContainerContainer';
84  $flexFormContainerContainerResult = $this->nodeFactory->create($options)->render();
85  $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $flexFormContainerContainerResult);
86  }
87  }
88  }
89 
90  // "New container" handling: Creates a "template" of each possible container and stuffs it
91  // somewhere into DOM to be handled with JS magic.
92  // Fun part: Handle the fact that such things may be set for children
93  $containerTemplatesHtml = [];
94  foreach ($flexFormFieldsArray as $flexFormContainerName => $flexFormFieldDefinition) {
95  $containerTemplateHtml = [];
96  $sectionTitle = '';
97  if (!empty(trim($flexFormFieldDefinition['title']))) {
98  $sectionTitle = $languageService->sL(trim($flexFormFieldDefinition['title']));
99  }
100 
101  $options = $this->data;
102  // @todo: this should use the prepared templateRow parallel to the single elements to have support of default values!
103  $options['flexFormRowData'] = [];
104  $options['flexFormDataStructureArray'] = $flexFormFieldDefinition['el'];
105  $options['flexFormFieldIdentifierPrefix'] = $flexFormFieldIdentifierPrefix;
106  $options['flexFormFormPrefix'] = $this->data['flexFormFormPrefix'] . '[' . $flexFormSectionType . ']' . '[el]';
107  $options['flexFormContainerName'] = $flexFormContainerName;
108  $options['flexFormContainerCounter'] = $flexFormFieldIdentifierPrefix . '-form';
109  $options['flexFormContainerTitle'] = $sectionTitle;
110  $options['flexFormContainerElementCollapsed'] = false;
111  $options['renderType'] = 'flexFormContainerContainer';
112  $flexFormContainerContainerTemplateResult = $this->nodeFactory->create($options)->render();
113 
114  // Extract the random identifier used by the ExtJS tree. This is used later on in the onClick handler
115  // to dynamically modify the javascript code and instanciate a unique ExtJS tree instance per section.
116  $treeElementIdentifier = '';
117  if (!empty($flexFormContainerContainerTemplateResult['extJSCODE'])) {
118  if (preg_match('/StandardTreeItemData\["([a-f0-9]{32})"\]/', $flexFormContainerContainerTemplateResult['extJSCODE'], $matches)) {
119  $treeElementIdentifier = $matches[1];
120  }
121  }
122 
123  $uniqueId = StringUtility::getUniqueId('idvar');
124  $identifierPrefixJs = 'replace(/' . $flexFormFieldIdentifierPrefix . '-/g,"' . $flexFormFieldIdentifierPrefix . '-"+' . $uniqueId . '+"-")';
125  $identifierPrefixJs .= '.replace(/(tceforms-(datetime|date)field-)/g,"$1" + (new Date()).getTime())';
126 
127  if (!empty($treeElementIdentifier)) {
128  $identifierPrefixJs .= '.replace(/(tree_?)?' . $treeElementIdentifier . '/g,"$1" + (' . $uniqueId . '))';
129  }
130 
131  $onClickInsert = [];
132  $onClickInsert[] = 'var ' . $uniqueId . ' = "' . 'idx"+(new Date()).getTime();';
133  $onClickInsert[] = 'TYPO3.jQuery("#' . $flexFormFieldIdentifierPrefix . '").append(TYPO3.jQuery(' . json_encode($flexFormContainerContainerTemplateResult['html']) . '.' . $identifierPrefixJs . '));';
134  $onClickInsert[] = 'TYPO3.jQuery("#' . $flexFormFieldIdentifierPrefix . '").t3FormEngineFlexFormElement();';
135  $onClickInsert[] = 'eval(unescape("' . rawurlencode(implode(';', $flexFormContainerContainerTemplateResult['additionalJavaScriptPost'])) . '").' . $identifierPrefixJs . ');';
136  if (!empty($treeElementIdentifier)) {
137  $onClickInsert[] = 'eval(unescape("' . rawurlencode($flexFormContainerContainerTemplateResult['extJSCODE']) . '").' . $identifierPrefixJs . ');';
138  }
139  $onClickInsert[] = 'TBE_EDITOR.addActionChecks("submit", unescape("' . rawurlencode(implode(';', $flexFormContainerContainerTemplateResult['additionalJavaScriptSubmit'])) . '").' . $identifierPrefixJs . ');';
140  $onClickInsert[] = 'TYPO3.FormEngine.reinitialize();';
141  $onClickInsert[] = 'TYPO3.FormEngine.Validation.initializeInputFields();';
142  $onClickInsert[] = 'TYPO3.FormEngine.Validation.validate();';
143  $onClickInsert[] = 'return false;';
144 
145  $containerTemplateHtml[] = '<a href="#" class="btn btn-default" onclick="' . htmlspecialchars(implode(LF, $onClickInsert)) . '">';
146  $containerTemplateHtml[] = $iconFactory->getIcon('actions-document-new', Icon::SIZE_SMALL)->render();
147  $containerTemplateHtml[] = htmlspecialchars(GeneralUtility::fixed_lgd_cs($sectionTitle, 30));
148  $containerTemplateHtml[] = '</a>';
149  $containerTemplatesHtml[] = implode(LF, $containerTemplateHtml);
150 
151  $flexFormContainerContainerTemplateResult['html'] = '';
152  $flexFormContainerContainerTemplateResult['additionalJavaScriptPost'] = [];
153  $flexFormContainerContainerTemplateResult['additionalJavaScriptSubmit'] = [];
154 
155  $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $flexFormContainerContainerTemplateResult);
156  }
157 
158  // Create new elements links
159  $createElementsHtml = [];
160  if ($userHasAccessToDefaultLanguage) {
161  $createElementsHtml[] = '<div class="t3-form-field-add-flexsection">';
162  $createElementsHtml[] = '<div class="btn-group">';
163  $createElementsHtml[] = implode('|', $containerTemplatesHtml);
164  $createElementsHtml[] = '</div>';
165  $createElementsHtml[] = '</div>';
166  }
167 
168  // Wrap child stuff
169  $toggleAll = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.toggleall', true);
170  $html = [];
171  $html[] = '<div class="panel panel-tab">';
172  $html[] = '<div class="panel-body">';
173  $html[] = '<div class="t3-form-field-container t3-form-flex">';
174  $html[] = '<div class="t3-form-field-label-flexsection">';
175  $html[] = '<h4>';
176  $html[] = htmlspecialchars($flexFormSectionTitle);
177  $html[] = '</h4>';
178  $html[] = '</div>';
179  $html[] = '<div class="t3js-form-field-toggle-flexsection t3-form-flexsection-toggle">';
180  $html[] = '<a class="btn btn-default" href="#" title="' . $toggleAll . '">';
181  $html[] = $iconFactory->getIcon('actions-move-right', Icon::SIZE_SMALL)->render() . $toggleAll;
182  $html[] = '</a>';
183  $html[] = '</div>';
184  $html[] = '<div';
185  $html[] = 'id="' . $flexFormFieldIdentifierPrefix . '"';
186  $html[] = 'class="panel-group panel-hover t3-form-field-container-flexsection t3-flex-container"';
187  $html[] = 'data-t3-flex-allow-restructure="' . ($userHasAccessToDefaultLanguage ? '1' : '0') . '"';
188  $html[] = '>';
189  $html[] = $resultArray['html'];
190  $html[] = '</div>';
191  $html[] = implode(LF, $createElementsHtml);
192  $html[] = '</div>';
193  $html[] = '</div>';
194  $html[] = '</div>';
195 
196  $resultArray['html'] = implode(LF, $html);
197 
198  return $resultArray;
199  }
200 
204  protected function getBackendUserAuthentication()
205  {
206  return $GLOBALS['BE_USER'];
207  }
208 
212  protected function getLanguageService()
213  {
214  return $GLOBALS['LANG'];
215  }
216 }
mergeChildReturnIntoExistingResult(array $existing, array $childReturn)
static fixed_lgd_cs($string, $chars, $appendString='...')
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']