[!!!][TASK] The FormEngine is dead, long live the FormEngine!
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / SelectTreeElement.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\Element;
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
17 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Backend\Utility\IconUtility;
20 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21 use TYPO3\CMS\Core\Tree\TableConfiguration\ExtJsArrayTreeRenderer;
22 use TYPO3\CMS\Core\Tree\TableConfiguration\TableConfigurationTree;
23 use TYPO3\CMS\Core\Tree\TableConfiguration\TreeDataProviderFactory;
24 use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26 use TYPO3\CMS\Lang\LanguageService;
27
28 /**
29 * Render data as a tree.
30 *
31 * Typically rendered for config [type=select, renderMode=tree
32 */
33 class SelectTreeElement extends AbstractFormElement {
34
35 /**
36 * @var array
37 */
38 protected $resultArray;
39
40 /**
41 * Render tree widget
42 *
43 * @return array As defined in initializeResultArray() of AbstractNode
44 */
45 public function render() {
46 $table = $this->data['tableName'];
47 $field = $this->data['fieldName'];
48 $row = $this->data['databaseRow'];
49 $parameterArray = $this->data['parameterArray'];
50
51 // Field configuration from TCA:
52 $config = $parameterArray['fieldConf']['config'];
53 $disabled = '';
54 if ($config['readOnly']) {
55 $disabled = ' disabled="disabled"';
56 }
57
58 $this->resultArray = $this->initializeResultArray();
59
60 $selItems = $parameterArray['fieldConf']['config']['items'];
61
62 $html = $this->renderField($table, $field, $row, $parameterArray, $config, $selItems);
63
64 // Wizards:
65 if (!$disabled) {
66 // "Extra" configuration; Returns configuration for the field based on settings found in the "types" fieldlist.
67 $specConf = BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']);
68 $html = $this->renderWizards(array($html), $config['wizards'], $table, $row, $field, $parameterArray, $parameterArray['itemFormElName'], $specConf);
69 }
70 $this->resultArray['html'] = $html;
71 return $this->resultArray;
72 }
73
74 /**
75 * Renders the tree
76 *
77 * @param string $table The table name of the record
78 * @param string $field The field name which this element is supposed to edit
79 * @param array $row The record data array where the value(s) for the field can be found
80 * @param array $PA An array with additional configuration options.
81 * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
82 * @param array $possibleSelectboxItems Items available for selection
83 * @return string The HTML code for the TCEform field
84 * @todo: This thing needs to be split and the data fetching should be moved to a data provider
85 */
86 protected function renderField($table, $field, $row, &$PA, $config, $possibleSelectboxItems) {
87 $backendUserAuthentication = $this->getBackendUserAuthentication();
88
89 $selectedNodes = [];
90 if (!empty($PA['itemFormElValue'])) {
91 $selectedNodes = $PA['itemFormElValue'];
92 }
93
94 $selectedNodesForApi = array();
95 foreach ($selectedNodes as $selectedNode) {
96 // @todo: this is ugly - the "old" pipe based value|label syntax is re-created here at the moment
97 foreach ($possibleSelectboxItems as $possibleSelectboxItem) {
98 if ((string)$possibleSelectboxItem[1] === (string)$selectedNode) {
99 $selectedNodesForApi[] = $selectedNode . '|' . rawurlencode($possibleSelectboxItem[0]);
100 }
101 }
102 }
103
104 $allowedUids = array();
105 foreach ($possibleSelectboxItems as $item) {
106 if ((int)$item[1] > 0) {
107 $allowedUids[] = $item[1];
108 }
109 }
110 $treeDataProvider = TreeDataProviderFactory::getDataProvider($config, $table, $field, $row);
111 $treeDataProvider->setSelectedList(implode(',', $selectedNodes));
112 $treeDataProvider->setItemWhiteList($allowedUids);
113 $treeDataProvider->initializeTreeData();
114 $treeRenderer = GeneralUtility::makeInstance(ExtJsArrayTreeRenderer::class);
115 $tree = GeneralUtility::makeInstance(TableConfigurationTree::class);
116 $tree->setDataProvider($treeDataProvider);
117 $tree->setNodeRenderer($treeRenderer);
118 $treeData = $tree->render();
119 $itemArray = array();
120 /**
121 * @todo: Small bug here: In the past, this was the "not processed list" of default items, but now it is
122 * @todo: a full list of elements. This needs to be fixed later, so "additional" default items are shown again.
123 if (is_array($PA['fieldConf']['config']['items'])) {
124 foreach ($PA['fieldConf']['config']['items'] as $additionalItem) {
125 if ($additionalItem[1] !== '--div--') {
126 $item = new \stdClass();
127 $item->uid = $additionalItem[1];
128 $item->text = $this->getLanguageService()->sL($additionalItem[0]);
129 $item->selectable = TRUE;
130 $item->leaf = TRUE;
131 $item->checked = in_array($additionalItem[1], $selectedNodes);
132 if (file_exists(PATH_typo3 . $additionalItem[3])) {
133 $item->icon = $additionalItem[3];
134 } elseif (trim($additionalItem[3]) !== '') {
135 $item->iconCls = IconUtility::getSpriteIconClasses($additionalItem[3]);
136 }
137 $itemArray[] = $item;
138 }
139 }
140 }
141 */
142 $itemArray[] = $treeData;
143 $treeData = json_encode($itemArray);
144 $id = md5($PA['itemFormElName']);
145 if (isset($PA['fieldConf']['config']['size']) && (int)$PA['fieldConf']['config']['size'] > 0) {
146 $height = (int)$PA['fieldConf']['config']['size'] * 20;
147 } else {
148 $height = 280;
149 }
150 $autoSizeMax = NULL;
151 if (isset($PA['fieldConf']['config']['autoSizeMax']) && (int)$PA['fieldConf']['config']['autoSizeMax'] > 0) {
152 $autoSizeMax = (int)$PA['fieldConf']['config']['autoSizeMax'] * 20;
153 }
154 $header = FALSE;
155 $expanded = FALSE;
156 $width = 280;
157 $appearance = $PA['fieldConf']['config']['treeConfig']['appearance'];
158 if (is_array($appearance)) {
159 $header = (bool)$appearance['showHeader'];
160 $expanded = (bool)$appearance['expandAll'];
161 if (isset($appearance['width'])) {
162 $width = (int)$appearance['width'];
163 }
164 }
165 $onChange = '';
166 if ($PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged']) {
167 $onChange = $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
168 }
169 // Create a JavaScript code line which will ask the user to save/update the form due to changing the element.
170 // This is used for eg. "type" fields and others configured with "requestUpdate"
171 if (
172 !empty($GLOBALS['TCA'][$table]['ctrl']['type'])
173 && $field === $GLOBALS['TCA'][$table]['ctrl']['type']
174 || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'])
175 && GeneralUtility::inList(str_replace(' ', '', $GLOBALS['TCA'][$table]['ctrl']['requestUpdate']), $field)
176 ) {
177 if ($backendUserAuthentication->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
178 $onChange .= 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && ' . 'TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
179 } else {
180 $onChange .= 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
181 }
182 }
183 $this->resultArray['extJSCODE'] .= LF .
184 'Ext.onReady(function() {
185 TYPO3.Components.Tree.StandardTreeItemData["' . $id . '"] = ' . $treeData . ';
186 var tree' . $id . ' = new TYPO3.Components.Tree.StandardTree({
187 id: "' . $id . '",
188 showHeader: ' . (int)$header . ',
189 onChange: "' . $onChange . '",
190 countSelectedNodes: ' . count($selectedNodes) . ',
191 width: ' . $width . ',
192 listeners: {
193 click: function(node, event) {
194 if (typeof(node.attributes.checked) == "boolean") {
195 node.attributes.checked = ! node.attributes.checked;
196 node.getUI().toggleCheck(node.attributes.checked);
197 }
198 },
199 dblclick: function(node, event) {
200 if (typeof(node.attributes.checked) == "boolean") {
201 node.attributes.checked = ! node.attributes.checked;
202 node.getUI().toggleCheck(node.attributes.checked);
203 }
204 },
205 checkchange: TYPO3.Components.Tree.TcaCheckChangeHandler,
206 collapsenode: function(node) {
207 if (node.id !== "root") {
208 top.TYPO3.Storage.Persistent.removeFromList("tcaTrees." + this.ucId, node.attributes.uid);
209 }
210 },
211 expandnode: function(node) {
212 if (node.id !== "root") {
213 top.TYPO3.Storage.Persistent.addToList("tcaTrees." + this.ucId, node.attributes.uid);
214 }
215 },
216 beforerender: function(treeCmp) {
217 // Check if that tree element is already rendered. It is appended on the first tceforms_inline call.
218 if (Ext.fly(treeCmp.getId())) {
219 return false;
220 }
221 }' . ($expanded ? ',
222 afterrender: function(treeCmp) {
223 treeCmp.expandAll();
224 }' : '') . '
225 },
226 tcaMaxItems: ' . ($PA['fieldConf']['config']['maxitems'] ? (int)$PA['fieldConf']['config']['maxitems'] : 99999) . ',
227 tcaSelectRecursiveAllowed: ' . ($appearance['allowRecursiveMode'] ? 'true' : 'false') . ',
228 tcaSelectRecursive: false,
229 tcaExclusiveKeys: "' . ($PA['fieldConf']['config']['exclusiveKeys'] ? $PA['fieldConf']['config']['exclusiveKeys'] : '') . '",
230 ucId: "' . md5(($table . '|' . $field)) . '",
231 selModel: TYPO3.Components.Tree.EmptySelectionModel,
232 disabled: ' . ($PA['fieldConf']['config']['readOnly'] ? 'true' : 'false') . '
233 });' . LF .
234 ($autoSizeMax
235 ? 'tree' . $id . '.bodyStyle = "max-height: ' . $autoSizeMax . 'px;min-height: ' . $height . 'px;";'
236 : 'tree' . $id . '.height = ' . $height . ';'
237 ) . LF .
238 'window.setTimeout(function() {
239 tree' . $id . '.render("tree_' . $id . '");
240 }, 200);
241 });';
242 $formField = '
243 <div class="typo3-tceforms-tree">
244 <input class="treeRecord" type="hidden" name="' . htmlspecialchars($PA['itemFormElName']) . '" id="treeinput' . $id . '" value="' . htmlspecialchars(implode(',', $selectedNodesForApi)) . '" />
245 </div>
246 <div id="tree_' . $id . '">
247
248 </div>';
249 return $formField;
250 }
251
252 /**
253 * @return LanguageService
254 */
255 protected function getLanguageService() {
256 return $GLOBALS['LANG'];
257 }
258
259 /**
260 * @return BackendUserAuthentication
261 */
262 protected function getBackendUserAuthentication() {
263 return $GLOBALS['BE_USER'];
264 }
265
266 }