[BUGFIX] Fix path to language file in SelectTreeElement
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / SelectTreeElement.php
index a2a6905..a830ec5 100644 (file)
@@ -14,11 +14,6 @@ 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\Type\Bitmask\JsConfirmation;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-
 /**
  * Render data as a tree.
  *
@@ -27,11 +22,27 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 class SelectTreeElement extends AbstractFormElement
 {
     /**
-     * Default height of the tree in pixels.
+     * Default number of tree nodes to show (determines tree height)
+     * when no ['config']['size'] is set
+     *
+     * @var int
+     */
+    protected $itemsToShow = 15;
+
+    /**
+     * Number of items to show at last
+     * e.g. when you have only 2 items in a tree
+     *
+     * @var int
+     */
+    protected $minItemsToShow = 5;
+
+    /**
+     * Pixel height of a single tree node
      *
-     * @const
+     * @var int
      */
-    const DEFAULT_HEIGHT = 280;
+    protected $itemHeight = 20;
 
     /**
      * Render tree widget
@@ -47,157 +58,116 @@ class SelectTreeElement extends AbstractFormElement
 
         // 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;
+
+        $fieldName = $this->data['fieldName'];
+
+        $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;
+            }
+        }
 
-        $resultArray['extJSCODE'] .= LF . $this->generateJavascript($formElementId);
+        $fieldInformationResult = $this->renderFieldInformation();
+        $fieldInformationHtml = $fieldInformationResult['html'];
+        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
 
         $html = [];
-        $html[] = '<div class="typo3-tceforms-tree">';
-        $html[] = '    <input class="treeRecord" type="hidden"';
-        $html[] = '           ' . $this->getValidationDataAsDataAttribute($parameterArray['fieldConf']['config']);
-        $html[] = '           data-formengine-input-name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"';
-        $html[] = '           data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '"';
-        $html[] = '           name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"';
-        $html[] = '           id="treeinput' . $formElementId . '"';
-        $html[] = '           value="' . htmlspecialchars(implode(',', $config['treeData']['selectedNodes'])) . '"';
-        $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>';
-        $html[] = '<div id="tree_' . $formElementId . '"></div>';
 
         $resultArray['html'] = implode(LF, $html);
 
-        // Wizards:
-        if (empty($config['readOnly'])) {
-            $resultArray['html'] = $this->renderWizards(
-                [$resultArray['html']],
-                $config['wizards'],
-                $this->data['tableName'],
-                $this->data['databaseRow'],
-                $this->data['fieldName'],
-                $parameterArray,
-                $parameterArray['itemFormElName'],
-                BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras'])
-            );
+        // 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;
     }
 
     /**
-     * Generates the Ext JS tree JavaScript code.
+     * Generates JS code triggered on change of the tree
      *
-     * @param string $formElementId The HTML element ID of the tree select field.
      * @return string
      */
-    protected function generateJavascript($formElementId)
+    protected function getTreeOnChangeJs()
     {
-        $table = $this->data['tableName'];
-        $field = $this->data['fieldName'];
         $parameterArray = $this->data['parameterArray'];
-        $config = $parameterArray['fieldConf']['config'];
-
-        $disabled = !empty($config['readOnly']) ? 'true' : 'false';
-        $maxItems = $config['maxitems'] ? (int)$config['maxitems'] : 99999;
-        $exclusiveKeys = !empty($config['exclusiveKeys']) ? $config['exclusiveKeys'] : '';
-
-        $appearance = !empty($config['treeConfig']['appearance']) ? $config['treeConfig']['appearance'] : [];
-        if (isset($config['size']) && (int)$config['size'] > 0) {
-            $height = (int)$config['size'] * 20;
-        } else {
-            $height = static::DEFAULT_HEIGHT;
-        }
-        $showHeader = !empty($appearance['showHeader']);
-        $expanded = !empty($appearance['expandAll']);
-
         $onChange = !empty($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']) ? $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'] : '';
         $onChange .= !empty($parameterArray['fieldChangeFunc']['alert']) ? $parameterArray['fieldChangeFunc']['alert'] : '';
-
-        // 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 = '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(); });';
-            } else {
-                $onChange .= 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
-            }
-        }
-
-        $javascript = [];
-        $javascript[] = 'Ext.onReady(function() {';
-        $javascript[] = '    TYPO3.Components.Tree.StandardTreeItemData["' . $formElementId . '"] = ' . json_encode($config['treeData']['items']) . ';';
-        $javascript[] = '    var tree' . $formElementId . ' = new TYPO3.Components.Tree.StandardTree({';
-        $javascript[] = '        id: "' . $formElementId . '",';
-        $javascript[] = '        stateful: true,';
-        $javascript[] = '        stateId: "tcaTrees." + this.ucId,';
-        $javascript[] = '        stateEvents: [],';
-        $javascript[] = '        showHeader: ' . (int)$showHeader . ',';
-        $javascript[] = '        onChange: ' . GeneralUtility::quoteJSvalue($onChange) . ',';
-        $javascript[] = '        countSelectedNodes: ' . count($config['treeData']['selectedNodes']) . ',';
-        $javascript[] = '        rendering: false,';
-        $javascript[] = '        listeners: {';
-        $javascript[] = '            click: function(node, event) {';
-        $javascript[] = '                if (typeof(node.attributes.checked) == "boolean") {';
-        $javascript[] = '                    node.attributes.checked = ! node.attributes.checked;';
-        $javascript[] = '                    node.getUI().toggleCheck(node.attributes.checked);';
-        $javascript[] = '                }';
-        $javascript[] = '            },';
-        $javascript[] = '            dblclick: function(node, event) {';
-        $javascript[] = '                if (typeof(node.attributes.checked) == "boolean") {';
-        $javascript[] = '                    node.attributes.checked = ! node.attributes.checked;';
-        $javascript[] = '                    node.getUI().toggleCheck(node.attributes.checked);';
-        $javascript[] = '                }';
-        $javascript[] = '            },';
-        $javascript[] = '            checkchange: TYPO3.Components.Tree.TcaCheckChangeHandler,';
-        $javascript[] = '            collapsenode: function(node) {';
-        $javascript[] = '                if (node.id !== "root" && !this.rendering) {';
-        $javascript[] = '                    top.TYPO3.Storage.Persistent.removeFromList("tcaTrees." + this.ucId, node.attributes.uid);';
-        $javascript[] = '                }';
-        $javascript[] = '            },';
-        $javascript[] = '            expandnode: function(node) {';
-        $javascript[] = '                if (node.id !== "root" && !this.rendering) {';
-        $javascript[] = '                    top.TYPO3.Storage.Persistent.addToList("tcaTrees." + this.ucId, node.attributes.uid);';
-        $javascript[] = '                }';
-        $javascript[] = '            },';
-        $javascript[] = '            beforerender: function(treeCmp) {';
-        $javascript[] = '                this.rendering = true';
-        $javascript[] = '                // Check if that tree element is already rendered. It is appended on the first tceforms_inline call.';
-        $javascript[] = '                if (Ext.fly(treeCmp.getId())) {';
-        $javascript[] = '                    return false;';
-        $javascript[] = '                }';
-        $javascript[] = '            },';
-        $javascript[] = '            afterrender: function(treeCmp) {';
-        if ($expanded) {
-            $javascript[] = '                treeCmp.expandAll();';
-        }
-        $javascript[] = '                this.rendering = false;';
-        $javascript[] = '            }';
-        $javascript[] = '        },';
-        $javascript[] = '        tcaMaxItems: ' . $maxItems . ',';
-        $javascript[] = '        tcaExclusiveKeys: "' . $exclusiveKeys . '",';
-        $javascript[] = '        ucId: "' . md5(($table . '|' . $field)) . '",';
-        $javascript[] = '        selModel: TYPO3.Components.Tree.EmptySelectionModel,';
-        $javascript[] = '        disabled: ' . $disabled;
-        $javascript[] = '    });';
-
-        $javascript[] = '    tree' . $formElementId . '.bodyStyle = "max-height: ' . $height . 'px;min-height: ' . self::DEFAULT_HEIGHT . 'px;";';
-
-        $javascript[] = '    window.setTimeout(function() {';
-        $javascript[] = '        tree' . $formElementId . '.render("tree_' . $formElementId . '");';
-        $javascript[] = '    }, 200);';
-        $javascript[] = '});';
-
-        return implode(LF, $javascript);
-    }
-
-    /**
-     * @return BackendUserAuthentication
-     */
-    protected function getBackendUserAuthentication()
-    {
-        return $GLOBALS['BE_USER'];
+        return 'function () {' . $onChange . '}';
     }
 }