[BUGFIX] need to parse field value for maxitems = 1 with MM table
[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 $treeData = json_encode($itemArray);
102 $id = md5($parameterArray['itemFormElName']);
103 if (isset($config['size']) && (int)$config['size'] > 0) {
104 $height = (int)$config['size'] * 20;
105 } else {
106 $height = 280;
107 }
108 $autoSizeMax = NULL;
109 if (isset($config['autoSizeMax']) && (int)$config['autoSizeMax'] > 0) {
110 $autoSizeMax = (int)$config['autoSizeMax'] * 20;
111 }
112 $header = FALSE;
113 $expanded = FALSE;
114 $width = 280;
115 $appearance = $config['treeConfig']['appearance'];
116 if (is_array($appearance)) {
117 $header = (bool)$appearance['showHeader'];
118 $expanded = (bool)$appearance['expandAll'];
119 if (isset($appearance['width'])) {
120 $width = (int)$appearance['width'];
121 }
122 }
123 $onChange = '';
124 if ($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']) {
125 $onChange = $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
126 }
127 // Create a JavaScript code line which will ask the user to save/update the form due to changing the element.
128 // This is used for eg. "type" fields and others configured with "requestUpdate"
129 if (
130 !empty($GLOBALS['TCA'][$table]['ctrl']['type'])
131 && $field === $GLOBALS['TCA'][$table]['ctrl']['type']
132 || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'])
133 && GeneralUtility::inList(str_replace(' ', '', $GLOBALS['TCA'][$table]['ctrl']['requestUpdate']), $field)
134 ) {
135 if ($this->getBackendUserAuthentication()->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
136 $onChange .= 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && ' . 'TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
137 } else {
138 $onChange .= 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
139 }
140 }
141 $html = '
142 <div class="typo3-tceforms-tree">
143 <input class="treeRecord" type="hidden" '
144 . $this->getValidationDataAsDataAttribute($config)
145 . ' data-formengine-input-name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'
146 . ' data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'
147 . ' name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" id="treeinput' . $id . '" value="' . htmlspecialchars(implode(',', $selectedNodesForApi)) . '" />
148 </div>
149 <div id="tree_' . $id . '">
150
151 </div>';
152
153 // Wizards:
154 if (empty($config['readOnly'])) {
155 $html = $this->renderWizards(
156 array($html),
157 $config['wizards'],
158 $table,
159 $row,
160 $field,
161 $parameterArray,
162 $parameterArray['itemFormElName'],
163 BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras'])
164 );
165 }
166 $resultArray = $this->initializeResultArray();
167 $resultArray['extJSCODE'] .= LF .
168 'Ext.onReady(function() {
169 TYPO3.Components.Tree.StandardTreeItemData["' . $id . '"] = ' . $treeData . ';
170 var tree' . $id . ' = new TYPO3.Components.Tree.StandardTree({
171 id: "' . $id . '",
172 showHeader: ' . (int)$header . ',
173 onChange: "' . $onChange . '",
174 countSelectedNodes: ' . count($selectedNodes) . ',
175 width: ' . $width . ',
176 listeners: {
177 click: function(node, event) {
178 if (typeof(node.attributes.checked) == "boolean") {
179 node.attributes.checked = ! node.attributes.checked;
180 node.getUI().toggleCheck(node.attributes.checked);
181 }
182 },
183 dblclick: function(node, event) {
184 if (typeof(node.attributes.checked) == "boolean") {
185 node.attributes.checked = ! node.attributes.checked;
186 node.getUI().toggleCheck(node.attributes.checked);
187 }
188 },
189 checkchange: TYPO3.Components.Tree.TcaCheckChangeHandler,
190 collapsenode: function(node) {
191 if (node.id !== "root") {
192 top.TYPO3.Storage.Persistent.removeFromList("tcaTrees." + this.ucId, node.attributes.uid);
193 }
194 },
195 expandnode: function(node) {
196 if (node.id !== "root") {
197 top.TYPO3.Storage.Persistent.addToList("tcaTrees." + this.ucId, node.attributes.uid);
198 }
199 },
200 beforerender: function(treeCmp) {
201 // Check if that tree element is already rendered. It is appended on the first tceforms_inline call.
202 if (Ext.fly(treeCmp.getId())) {
203 return false;
204 }
205 }' . ($expanded ? ',
206 afterrender: function(treeCmp) {
207 treeCmp.expandAll();
208 }' : '') . '
209 },
210 tcaMaxItems: ' . ($config['maxitems'] ? (int)$config['maxitems'] : 99999) . ',
211 tcaSelectRecursiveAllowed: ' . ($appearance['allowRecursiveMode'] ? 'true' : 'false') . ',
212 tcaSelectRecursive: false,
213 tcaExclusiveKeys: "' . ($config['exclusiveKeys'] ? $config['exclusiveKeys'] : '') . '",
214 ucId: "' . md5(($table . '|' . $field)) . '",
215 selModel: TYPO3.Components.Tree.EmptySelectionModel,
216 disabled: ' . ($config['readOnly'] ? 'true' : 'false') . '
217 });' . LF .
218 ($autoSizeMax
219 ? 'tree' . $id . '.bodyStyle = "max-height: ' . $autoSizeMax . 'px;min-height: ' . $height . 'px;";'
220 : 'tree' . $id . '.height = ' . $height . ';'
221 ) . LF .
222 'window.setTimeout(function() {
223 tree' . $id . '.render("tree_' . $id . '");
224 }, 200);
225 });';
226 $resultArray['html'] = $html;
227
228 return $resultArray;
229 }
230
231 /**
232 * @return BackendUserAuthentication
233 */
234 protected function getBackendUserAuthentication() {
235 return $GLOBALS['BE_USER'];
236 }
237 }