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