[BUGFIX] Fix path to language file in SelectTreeElement
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / SelectTreeElement.php
index d3464f3..a830ec5 100644 (file)
@@ -14,227 +14,160 @@ namespace TYPO3\CMS\Backend\Form\Element;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Backend\Utility\BackendUtility;
-use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Tree\TableConfiguration\ExtJsArrayTreeRenderer;
-use TYPO3\CMS\Core\Tree\TableConfiguration\TableConfigurationTree;
-use TYPO3\CMS\Core\Tree\TableConfiguration\TreeDataProviderFactory;
-use TYPO3\CMS\Core\Type\Bitmask\JsConfirmation;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-
 /**
  * Render data as a tree.
  *
- * Typically rendered for config [type=select, renderMode=tree
+ * Typically rendered for config type=select, renderType=selectTree
  */
-class SelectTreeElement extends AbstractFormElement {
+class SelectTreeElement extends AbstractFormElement
+{
+    /**
+     * Default number of tree nodes to show (determines tree height)
+     * when no ['config']['size'] is set
+     *
+     * @var int
+     */
+    protected $itemsToShow = 15;
 
-       /**
-        * Render tree widget
-        *
-        * @return array As defined in initializeResultArray() of AbstractNode
-        */
-       public function render() {
-               $table = $this->data['tableName'];
-               $field = $this->data['fieldName'];
-               $row = $this->data['databaseRow'];
-               $parameterArray = $this->data['parameterArray'];
+    /**
+     * Number of items to show at last
+     * e.g. when you have only 2 items in a tree
+     *
+     * @var int
+     */
+    protected $minItemsToShow = 5;
 
-               // Field configuration from TCA:
-               $config = $parameterArray['fieldConf']['config'];
+    /**
+     * Pixel height of a single tree node
+     *
+     * @var int
+     */
+    protected $itemHeight = 20;
 
-               $possibleSelectboxItems = $config['items'];
+    /**
+     * Render tree widget
+     *
+     * @return array As defined in initializeResultArray() of AbstractNode
+     * @see AbstractNode::initializeResultArray()
+     */
+    public function render()
+    {
+        $resultArray = $this->initializeResultArray();
+        $parameterArray = $this->data['parameterArray'];
+        $formElementId = md5($parameterArray['itemFormElName']);
 
-               $selectedNodes = $parameterArray['itemFormElValue'];
-               if ($config['maxitems'] === 1 && count($selectedNodes) === 1 && empty($selectedNodes[0])) {
-                       $selectedNodes = array();
-               }
+        // Field configuration from TCA:
+        $config = $parameterArray['fieldConf']['config'];
+        $readOnly = !empty($config['readOnly']) ? 'true' : 'false';
+        $exclusiveKeys = !empty($config['exclusiveKeys']) ? $config['exclusiveKeys'] : '';
+        $exclusiveKeys = $exclusiveKeys . ',';
+        $appearance = !empty($config['treeConfig']['appearance']) ? $config['treeConfig']['appearance'] : [];
+        $expanded = !empty($appearance['expandAll']);
+        $showHeader = !empty($appearance['showHeader']);
+        if (isset($config['size']) && (int)$config['size'] > 0) {
+            $height = max($this->minItemsToShow, (int)$config['size']);
+        } else {
+            $height = $this->itemsToShow;
+        }
+        $heightInPx = $height * $this->itemHeight;
+        $treeWrapperId = 'tree_' . $formElementId;
 
-               $selectedNodesForApi = array();
-               foreach ($selectedNodes as $selectedNode) {
-                       // @todo: this is ugly - the "old" pipe based value|label syntax is re-created here at the moment
-                       foreach ($possibleSelectboxItems as $possibleSelectboxItem) {
-                               if ((string)$possibleSelectboxItem[1] === (string)$selectedNode) {
-                                       $selectedNodesForApi[] = $selectedNode . '|' . rawurlencode($possibleSelectboxItem[0]);
-                               }
-                       }
-               }
+        $fieldName = $this->data['fieldName'];
 
-               $allowedUids = array();
-               foreach ($possibleSelectboxItems as $item) {
-                       if ((int)$item[1] > 0) {
-                               $allowedUids[] = $item[1];
-                       }
-               }
-               $treeDataProvider = TreeDataProviderFactory::getDataProvider($config, $table, $field, $row);
-               $treeDataProvider->setSelectedList(implode(',', $selectedNodes));
-               $treeDataProvider->setItemWhiteList($allowedUids);
-               $treeDataProvider->initializeTreeData();
-               $treeRenderer = GeneralUtility::makeInstance(ExtJsArrayTreeRenderer::class);
-               $tree = GeneralUtility::makeInstance(TableConfigurationTree::class);
-               $tree->setDataProvider($treeDataProvider);
-               $tree->setNodeRenderer($treeRenderer);
-               $treeData = $tree->render();
-               $itemArray = array();
+        $dataStructureIdentifier = '';
+        $flexFormSheetName = '';
+        $flexFormFieldName = '';
+        $flexFormContainerName = '';
+        $flexFormContainerIdentifier = '';
+        $flexFormContainerFieldName = '';
+        $flexFormSectionContainerIsNew = false;
+        if ($this->data['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') {
+            $dataStructureIdentifier = $this->data['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'];
+            if (isset($this->data['flexFormSheetName'])) {
+                $flexFormSheetName = $this->data['flexFormSheetName'];
+            }
+            if (isset($this->data['flexFormFieldName'])) {
+                $flexFormFieldName = $this->data['flexFormFieldName'];
+            }
+            if (isset($this->data['flexFormContainerName'])) {
+                $flexFormContainerName = $this->data['flexFormContainerName'];
+            }
+            if (isset($this->data['flexFormContainerFieldName'])) {
+                $flexFormContainerFieldName = $this->data['flexFormContainerFieldName'];
+            }
+            if (isset($this->data['flexFormContainerIdentifier'])) {
+                $flexFormContainerIdentifier = $this->data['flexFormContainerIdentifier'];
+            }
+            // Add a flag this is a tree in a new flex section container element. This is needed to initialize
+            // the databaseRow with this container again so the tree data provider is able to calculate tree items.
+            if (!empty($this->data['flexSectionContainerPreparation'])) {
+                $flexFormSectionContainerIsNew = true;
+            }
+        }
 
-               /**
-                * @todo: Small bug here: In the past, this was the "not processed list" of default items, but now it is
-                * @todo: a full list of elements. This needs to be fixed later, so "additional" default items are shown again.
-               if (is_array($config['items'])) {
-                       foreach ($config['items'] as $additionalItem) {
-                               if ($additionalItem[1] !== '--div--') {
-                                       $item = new \stdClass();
-                                       $item->uid = $additionalItem[1];
-                                       $item->text = $this->getLanguageService()->sL($additionalItem[0]);
-                                       $item->selectable = TRUE;
-                                       $item->leaf = TRUE;
-                                       $item->checked = in_array($additionalItem[1], $selectedNodes);
-                                       if (file_exists(PATH_typo3 . $additionalItem[3])) {
-                                               $item->icon = $additionalItem[3];
-                                       } elseif (trim($additionalItem[3]) !== '') {
-                                               $item->iconCls = IconUtility::getSpriteIconClasses($additionalItem[3]);
-                                       }
-                                       $itemArray[] = $item;
-                               }
-                       }
-               }
-               */
+        $fieldInformationResult = $this->renderFieldInformation();
+        $fieldInformationHtml = $fieldInformationResult['html'];
+        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
 
-               $itemArray[] = $treeData;
-               $treeData = json_encode($itemArray);
-               $id = md5($parameterArray['itemFormElName']);
-               if (isset($config['size']) && (int)$config['size'] > 0) {
-                       $height = (int)$config['size'] * 20;
-               } else {
-                       $height = 280;
-               }
-               $autoSizeMax = NULL;
-               if (isset($config['autoSizeMax']) && (int)$config['autoSizeMax'] > 0) {
-                       $autoSizeMax = (int)$config['autoSizeMax'] * 20;
-               }
-               $header = FALSE;
-               $expanded = FALSE;
-               $width = 280;
-               $appearance = $config['treeConfig']['appearance'];
-               if (is_array($appearance)) {
-                       $header = (bool)$appearance['showHeader'];
-                       $expanded = (bool)$appearance['expandAll'];
-                       if (isset($appearance['width'])) {
-                               $width = (int)$appearance['width'];
-                       }
-               }
-               $onChange = '';
-               if ($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']) {
-                       $onChange = $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
-               }
-               // Create a JavaScript code line which will ask the user to save/update the form due to changing the element.
-               // This is used for eg. "type" fields and others configured with "requestUpdate"
-               if (
-                       !empty($GLOBALS['TCA'][$table]['ctrl']['type'])
-                       && $field === $GLOBALS['TCA'][$table]['ctrl']['type']
-                       || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate'])
-                       && GeneralUtility::inList(str_replace(' ', '', $GLOBALS['TCA'][$table]['ctrl']['requestUpdate']), $field)
-               ) {
-                       if ($this->getBackendUserAuthentication()->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
-                               $onChange .= 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && ' . 'TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
-                       } else {
-                               $onChange .= 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
-                       }
-               }
-               $html = '
-                       <div class="typo3-tceforms-tree">
-                               <input class="treeRecord" type="hidden" '
-                                       .  $this->getValidationDataAsDataAttribute($config)
-                                       . ' data-formengine-input-name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'
-                                       . ' data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'
-                                       . ' name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" id="treeinput' . $id . '" value="' . htmlspecialchars(implode(',', $selectedNodesForApi)) . '" />
-                       </div>
-                       <div id="tree_' . $id . '">
+        $html = [];
+        $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
+        if (!$readOnly) {
+            $html[] = $fieldInformationHtml;
+        }
+        $html[] =   '<div class="form-control-wrap">';
+        $html[] =       '<div class="typo3-tceforms-tree">';
+        $html[] =           '<input class="treeRecord" type="hidden"';
+        $html[] =               ' data-formengine-validation-rules="' . htmlspecialchars($this->getValidationDataAsJsonString($config)) . '"';
+        $html[] =               ' data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '"';
+        $html[] =               ' data-tablename="' . htmlspecialchars($this->data['tableName']) . '"';
+        $html[] =               ' data-fieldname="' . htmlspecialchars($this->data['fieldName']) . '"';
+        $html[] =               ' data-uid="' . (int)$this->data['vanillaUid'] . '"';
+        $html[] =               ' data-recordtypevalue="' . htmlspecialchars($this->data['recordTypeValue']) . '"';
+        $html[] =               ' data-datastructureidentifier="' . htmlspecialchars($dataStructureIdentifier) . '"';
+        $html[] =               ' data-flexformsheetname="' . htmlspecialchars($flexFormSheetName) . '"';
+        $html[] =               ' data-flexformfieldname="' . htmlspecialchars($flexFormFieldName) . '"';
+        $html[] =               ' data-flexformcontainername="' . htmlspecialchars($flexFormContainerName) . '"';
+        $html[] =               ' data-flexformcontaineridentifier="' . htmlspecialchars($flexFormContainerIdentifier) . '"';
+        $html[] =               ' data-flexformcontainerfieldname="' . htmlspecialchars($flexFormContainerFieldName) . '"';
+        $html[] =               ' data-flexformsectioncontainerisnew="' . htmlspecialchars($flexFormSectionContainerIsNew) . '"';
+        $html[] =               ' data-command="' . htmlspecialchars($this->data['command']) . '"';
+        $html[] =               ' data-read-only="' . $readOnly . '"';
+        $html[] =               ' data-tree-exclusive-keys="' . htmlspecialchars($exclusiveKeys) . '"';
+        $html[] =               ' data-tree-expand-up-to-level="' . ($expanded ? '999' : '1') . '"';
+        $html[] =               ' data-tree-show-toolbar="' . $showHeader . '"';
+        $html[] =               ' name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"';
+        $html[] =               ' id="treeinput' . $formElementId . '"';
+        $html[] =               ' value=""';
+        $html[] =           '/>';
+        $html[] =       '</div>';
+        $html[] =       '<div id="' . $treeWrapperId . '" class="svg-tree-wrapper" style="height: ' . $heightInPx . 'px;"></div>';
+        $html[] =       '<script type="text/javascript">var ' . $treeWrapperId . ' = ' . $this->getTreeOnChangeJs() . '</script>';
+        $html[] =   '</div>';
+        $html[] = '</div>';
 
-                       </div>';
+        $resultArray['html'] = implode(LF, $html);
 
-               // Wizards:
-               if (empty($config['readOnly'])) {
-                       $html = $this->renderWizards(
-                               array($html),
-                               $config['wizards'],
-                               $table,
-                               $row,
-                               $field,
-                               $parameterArray,
-                               $parameterArray['itemFormElName'],
-                               BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras'])
-                       );
-               }
-               $resultArray = $this->initializeResultArray();
-               $resultArray['extJSCODE'] .= LF .
-                       'Ext.onReady(function() {
-                       TYPO3.Components.Tree.StandardTreeItemData["' . $id . '"] = ' . $treeData . ';
-                       var tree' . $id . ' = new TYPO3.Components.Tree.StandardTree({
-                               id: "' . $id . '",
-                               showHeader: ' . (int)$header . ',
-                               onChange: "' . $onChange . '",
-                               countSelectedNodes: ' . count($selectedNodes) . ',
-                               width: ' . $width . ',
-                               listeners: {
-                                       click: function(node, event) {
-                                               if (typeof(node.attributes.checked) == "boolean") {
-                                                       node.attributes.checked = ! node.attributes.checked;
-                                                       node.getUI().toggleCheck(node.attributes.checked);
-                                               }
-                                       },
-                                       dblclick: function(node, event) {
-                                               if (typeof(node.attributes.checked) == "boolean") {
-                                                       node.attributes.checked = ! node.attributes.checked;
-                                                       node.getUI().toggleCheck(node.attributes.checked);
-                                               }
-                                       },
-                                       checkchange: TYPO3.Components.Tree.TcaCheckChangeHandler,
-                                       collapsenode: function(node) {
-                                               if (node.id !== "root") {
-                                                       top.TYPO3.Storage.Persistent.removeFromList("tcaTrees." + this.ucId, node.attributes.uid);
-                                               }
-                                       },
-                                       expandnode: function(node) {
-                                               if (node.id !== "root") {
-                                                       top.TYPO3.Storage.Persistent.addToList("tcaTrees." + this.ucId, node.attributes.uid);
-                                               }
-                                       },
-                                       beforerender: function(treeCmp) {
-                                               // Check if that tree element is already rendered. It is appended on the first tceforms_inline call.
-                                               if (Ext.fly(treeCmp.getId())) {
-                                                       return false;
-                                               }
-                                       }' . ($expanded ? ',
-                                       afterrender: function(treeCmp) {
-                                               treeCmp.expandAll();
-                                       }' : '') . '
-                               },
-                               tcaMaxItems: ' . ($config['maxitems'] ? (int)$config['maxitems'] : 99999) . ',
-                               tcaSelectRecursiveAllowed: ' . ($appearance['allowRecursiveMode'] ? 'true' : 'false') . ',
-                               tcaSelectRecursive: false,
-                               tcaExclusiveKeys: "' . ($config['exclusiveKeys'] ? $config['exclusiveKeys'] : '') . '",
-                               ucId: "' . md5(($table . '|' . $field)) . '",
-                               selModel: TYPO3.Components.Tree.EmptySelectionModel,
-                               disabled: ' . ($config['readOnly'] ? 'true' : 'false') . '
-                       });' . LF .
-                       ($autoSizeMax
-                               ? 'tree' . $id . '.bodyStyle = "max-height: ' . $autoSizeMax . 'px;min-height: ' . $height . 'px;";'
-                               : 'tree' . $id . '.height = ' . $height . ';'
-                       ) . LF .
-                       'window.setTimeout(function() {
-                               tree' . $id . '.render("tree_' . $id . '");
-                       }, 200);
-               });';
-               $resultArray['html'] = $html;
+        // add necessary labels for tree header
+        if ($showHeader) {
+            $resultArray['additionalInlineLanguageLabelFiles'][] = 'EXT:core/Resources/Private/Language/locallang_csh_corebe.xlf';
+        }
+        $resultArray['requireJsModules']['selectTreeElement'] = [
+            'TYPO3/CMS/Backend/FormEngine/Element/SelectTreeElement' => 'function (SelectTreeElement) { SelectTreeElement.initialize(); }'
+        ];
 
-               return $resultArray;
-       }
+        return $resultArray;
+    }
 
-       /**
-        * @return BackendUserAuthentication
-        */
-       protected function getBackendUserAuthentication() {
-               return $GLOBALS['BE_USER'];
-       }
+    /**
+     * Generates JS code triggered on change of the tree
+     *
+     * @return string
+     */
+    protected function getTreeOnChangeJs()
+    {
+        $parameterArray = $this->data['parameterArray'];
+        $onChange = !empty($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']) ? $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] : '';
+        $onChange .= !empty($parameterArray['fieldChangeFunc']['alert']) ? $parameterArray['fieldChangeFunc']['alert'] : '';
+        return 'function () {' . $onChange . '}';
+    }
 }