2708c5078911d08c99a1fcacb849f331cb379892
[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\Type\Bitmask\JsConfirmation;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21
22 /**
23 * Render data as a tree.
24 *
25 * Typically rendered for config [type=select, renderMode=tree
26 */
27 class SelectTreeElement extends AbstractFormElement
28 {
29
30 /**
31 * Default height of the tree in pixels.
32 *
33 * @const
34 */
35 const DEFAULT_HEIGHT = 280;
36
37 /**
38 * Default width of the tree in pixels.
39 *
40 * @const
41 */
42 const DEFAULT_WIDTH = 280;
43
44 /**
45 * Render tree widget
46 *
47 * @return array As defined in initializeResultArray() of AbstractNode
48 * @see AbstractNode::initializeResultArray()
49 */
50 public function render()
51 {
52 $resultArray = $this->initializeResultArray();
53 $parameterArray = $this->data['parameterArray'];
54 $formElementId = md5($parameterArray['itemFormElName']);
55
56 // Field configuration from TCA:
57 $config = $parameterArray['fieldConf']['config'];
58
59 $resultArray['extJSCODE'] .= LF . $this->generateJavascript($formElementId);
60
61 $html = [];
62 $html[] = '<div class="typo3-tceforms-tree">';
63 $html[] = ' <input class="treeRecord" type="hidden"';
64 $html[] = ' ' . $this->getValidationDataAsDataAttribute($parameterArray['fieldConf']['config']);
65 $html[] = ' data-formengine-input-name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"';
66 $html[] = ' data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '"';
67 $html[] = ' name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"';
68 $html[] = ' id="treeinput' . $formElementId . '"';
69 $html[] = ' value="' . htmlspecialchars(implode(',', $config['treeData']['selectedNodes'])) . '"';
70 $html[] = ' />';
71 $html[] = '</div>';
72 $html[] = '<div id="tree_' . $formElementId . '"></div>';
73
74 $resultArray['html'] = implode(LF, $html);
75
76 // Wizards:
77 if (empty($config['readOnly'])) {
78 $resultArray['html'] = $this->renderWizards(
79 [$resultArray['html']],
80 $config['wizards'],
81 $this->data['tableName'],
82 $this->data['databaseRow'],
83 $this->data['fieldName'],
84 $parameterArray,
85 $parameterArray['itemFormElName'],
86 BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras'])
87 );
88 }
89
90 return $resultArray;
91 }
92
93 /**
94 * Generates the Ext JS tree JavaScript code.
95 *
96 * @param string $formElementId The HTML element ID of the tree select field.
97 * @return string
98 */
99 protected function generateJavascript($formElementId)
100 {
101 $table = $this->data['tableName'];
102 $field = $this->data['fieldName'];
103 $parameterArray = $this->data['parameterArray'];
104 $config = $parameterArray['fieldConf']['config'];
105
106 $selectedNodes = $this->data['databaseRow'][$this->data['fieldName']];
107
108 $disabled = !empty($config['readOnly']) ? 'true' : 'false';
109 $maxItems = $config['maxitems'] ? (int)$config['maxitems'] : 99999;
110 $exclusiveKeys = !empty($config['exclusiveKeys']) ? $config['exclusiveKeys'] : '';
111
112 $appearance = !empty($config['treeConfig']['appearance']) ? $config['treeConfig']['appearance'] : [];
113 $width = isset($appearance['width']) ? (int)$appearance['width'] : static::DEFAULT_WIDTH;
114 if (isset($config['size']) && (int)$config['size'] > 0) {
115 $height = (int)$config['size'] * 20;
116 } else {
117 $height = static::DEFAULT_HEIGHT;
118 }
119 $showHeader = !empty($appearance['showHeader']);
120 $expanded = !empty($appearance['expandAll']);
121 $allowRecursiveMode = !empty($appearance['allowRecursiveMode']) ? 'true' : 'false';
122
123 $autoSizeMax = null;
124 if (isset($config['autoSizeMax']) && (int)$config['autoSizeMax'] > 0) {
125 $autoSizeMax = (int)$config['autoSizeMax'] * 20;
126 }
127
128 $onChange = !empty($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']) ? $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] : '';
129 $onChange .= !empty($parameterArray['fieldChangeFunc']['alert']) ? $parameterArray['fieldChangeFunc']['alert'] : '';
130
131 // Create a JavaScript code line which will ask the user to save/update the form due to changing the element.
132 // This is used for eg. "type" fields and others configured with "requestUpdate"
133 if (
134 !empty($GLOBALS['TCA'][$table]['ctrl']['type'])
135 && $field === $GLOBALS['TCA'][$table]['ctrl']['type']
136 || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'])
137 && GeneralUtility::inList(str_replace(' ', '', $GLOBALS['TCA'][$table]['ctrl']['requestUpdate']), $field)
138 ) {
139 if ($this->getBackendUserAuthentication()->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
140 $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(); });';
141 } else {
142 $onChange .= 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
143 }
144 }
145
146 $javascript = [];
147 $javascript[] = 'Ext.onReady(function() {';
148 $javascript[] = ' TYPO3.Components.Tree.StandardTreeItemData["' . $formElementId . '"] = ' . json_encode($config['treeData']['items']) . ';';
149 $javascript[] = ' var tree' . $formElementId . ' = new TYPO3.Components.Tree.StandardTree({';
150 $javascript[] = ' id: "' . $formElementId . '",';
151 $javascript[] = ' stateful: true,';
152 $javascript[] = ' stateId: "tcaTrees." + this.ucId,';
153 $javascript[] = ' stateEvents: [],';
154 $javascript[] = ' showHeader: ' . (int)$showHeader . ',';
155 $javascript[] = ' onChange: ' . GeneralUtility::quoteJSvalue($onChange) . ',';
156 $javascript[] = ' countSelectedNodes: ' . count($selectedNodes) . ',';
157 $javascript[] = ' width: ' . $width . ',';
158 $javascript[] = ' rendering: false,';
159 $javascript[] = ' listeners: {';
160 $javascript[] = ' click: function(node, event) {';
161 $javascript[] = ' if (typeof(node.attributes.checked) == "boolean") {';
162 $javascript[] = ' node.attributes.checked = ! node.attributes.checked;';
163 $javascript[] = ' node.getUI().toggleCheck(node.attributes.checked);';
164 $javascript[] = ' }';
165 $javascript[] = ' },';
166 $javascript[] = ' dblclick: function(node, event) {';
167 $javascript[] = ' if (typeof(node.attributes.checked) == "boolean") {';
168 $javascript[] = ' node.attributes.checked = ! node.attributes.checked;';
169 $javascript[] = ' node.getUI().toggleCheck(node.attributes.checked);';
170 $javascript[] = ' }';
171 $javascript[] = ' },';
172 $javascript[] = ' checkchange: TYPO3.Components.Tree.TcaCheckChangeHandler,';
173 $javascript[] = ' collapsenode: function(node) {';
174 $javascript[] = ' if (node.id !== "root" && !this.rendering) {';
175 $javascript[] = ' top.TYPO3.Storage.Persistent.removeFromList("tcaTrees." + this.ucId, node.attributes.uid);';
176 $javascript[] = ' }';
177 $javascript[] = ' },';
178 $javascript[] = ' expandnode: function(node) {';
179 $javascript[] = ' if (node.id !== "root" && !this.rendering) {';
180 $javascript[] = ' top.TYPO3.Storage.Persistent.addToList("tcaTrees." + this.ucId, node.attributes.uid);';
181 $javascript[] = ' }';
182 $javascript[] = ' },';
183 $javascript[] = ' beforerender: function(treeCmp) {';
184 $javascript[] = ' this.rendering = true';
185 $javascript[] = ' // Check if that tree element is already rendered. It is appended on the first tceforms_inline call.';
186 $javascript[] = ' if (Ext.fly(treeCmp.getId())) {';
187 $javascript[] = ' return false;';
188 $javascript[] = ' }';
189 $javascript[] = ' },';
190 $javascript[] = ' afterrender: function(treeCmp) {';
191 if ($expanded) {
192 $javascript[] = ' treeCmp.expandAll();';
193 }
194 $javascript[] = ' this.rendering = false;';
195 $javascript[] = ' }';
196 $javascript[] = ' },';
197 $javascript[] = ' tcaMaxItems: ' . $maxItems . ',';
198 $javascript[] = ' tcaSelectRecursiveAllowed: ' . $allowRecursiveMode . ',';
199 $javascript[] = ' tcaSelectRecursive: false,';
200 $javascript[] = ' tcaExclusiveKeys: "' . $exclusiveKeys . '",';
201 $javascript[] = ' ucId: "' . md5(($table . '|' . $field)) . '",';
202 $javascript[] = ' selModel: TYPO3.Components.Tree.EmptySelectionModel,';
203 $javascript[] = ' disabled: ' . $disabled;
204 $javascript[] = ' });';
205
206 if ($autoSizeMax) {
207 $javascript[] = ' tree' . $formElementId . '.bodyStyle = "max-height: ' . $autoSizeMax . 'px;min-height: ' . $height . 'px;";';
208 } else {
209 $javascript[] = ' tree' . $formElementId . '.height = ' . $height . ';';
210 }
211
212 $javascript[] = ' window.setTimeout(function() {';
213 $javascript[] = ' tree' . $formElementId . '.render("tree_' . $formElementId . '");';
214 $javascript[] = ' }, 200);';
215 $javascript[] = '});';
216
217 return implode(LF, $javascript);
218 }
219
220 /**
221 * @return BackendUserAuthentication
222 */
223 protected function getBackendUserAuthentication()
224 {
225 return $GLOBALS['BE_USER'];
226 }
227 }