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