diff --git a/Build/Resources/Public/Less/Component/svgTree.less b/Build/Resources/Public/Less/Component/svgTree.less index ba7848ef41ce939d2b30eff2abb35fdff68170c3..774efa7c204fb866f579b4c4e1291e90e63d7c22 100644 --- a/Build/Resources/Public/Less/Component/svgTree.less +++ b/Build/Resources/Public/Less/Component/svgTree.less @@ -8,10 +8,9 @@ stroke-width: 1; } - .node .chevron { - cursor: pointer; - } - .tree-check { - cursor: pointer; + .node { + .chevron, text, .tree-check { + cursor: pointer; + } } } \ No newline at end of file diff --git a/typo3/sysext/backend/Classes/Controller/SelectTreeController.php b/typo3/sysext/backend/Classes/Controller/SelectTreeController.php index 4dac0757876a7105127973727e598ac7d5428f6b..01a25f69ebd8019d0674a548e24b76a18f73383f 100644 --- a/typo3/sysext/backend/Classes/Controller/SelectTreeController.php +++ b/typo3/sysext/backend/Classes/Controller/SelectTreeController.php @@ -46,14 +46,48 @@ class SelectTreeController 'vanillaUid' => (int)$request->getQueryParams()['uid'], 'command' => $request->getQueryParams()['command'], ]; + $fieldName = $request->getQueryParams()['field']; $formData = $formDataCompiler->compile($formDataCompilerInput); - $treeData = $formData['processedTca']['columns'][$fieldName]['config']['treeData']; + + if ($formData['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') { + $flexFormFieldName = $request->getQueryParams()['flex_form_field_name']; + $value = $this->searchForFieldInFlexStructure($formData['processedTca']['columns'][$fieldName]['config'], $flexFormFieldName); + $treeData = $value['config']['treeData']; + } else { + $treeData = $formData['processedTca']['columns'][$fieldName]['config']['treeData']; + } + $json = json_encode($treeData['items']); $response->getBody()->write($json); return $response; } + /** + * A workaround for flexforms - there is no easy way to get flex field by key, so we need to search for it + * + * @todo remove me once flexforms are refactored + * + * @param array $array + * @param string $needle + * @return array + */ + protected function searchForFieldInFlexStructure(array $array, $needle) + { + $needle = trim($needle); + $iterator = new \RecursiveArrayIterator($array); + $recursive = new \RecursiveIteratorIterator( + $iterator, + \RecursiveIteratorIterator::SELF_FIRST + ); + foreach ($recursive as $key => $value) { + if ($key === $needle) { + return $value; + } + } + return []; + } + /** * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication */ diff --git a/typo3/sysext/backend/Classes/Form/Container/FlexFormElementContainer.php b/typo3/sysext/backend/Classes/Form/Container/FlexFormElementContainer.php index c6df908545098b728bff7171c2b4a24649f6eb9d..d141f618b62dda1d08daf08c0d447b40bfb13eaf 100644 --- a/typo3/sysext/backend/Classes/Form/Container/FlexFormElementContainer.php +++ b/typo3/sysext/backend/Classes/Form/Container/FlexFormElementContainer.php @@ -79,6 +79,8 @@ class FlexFormElementContainer extends AbstractContainer // Set up options for single element $fakeParameterArray = [ 'fieldConf' => [ + // @todo review this field during flex refactoring + 'flexFormFieldName' => $flexFormFieldName, 'label' => $languageService->sL(trim($flexFormFieldArray['label'])), 'config' => $flexFormFieldArray['config'], 'children' => $flexFormFieldArray['children'], diff --git a/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php b/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php index 71d7176d9a1f5f1de0893b7a28eb5d734e4fba4b..053356b0bc6401ae2a8d57e976313cbf1220deaa 100644 --- a/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php +++ b/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php @@ -26,11 +26,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 * - * @const + * @var int */ - const DEFAULT_HEIGHT = 280; + protected $itemsToShow = 280; + + /** + * Number of items to show at last + * e.g. when you have only 2 items in a tree + * + * @var int + */ + protected $minItemsToShow = 10; + + /** + * Pixel height of a single tree node + * + * @var int + */ + protected $itemHeight = 20; /** * Render tree widget @@ -53,21 +69,24 @@ class SelectTreeElement extends AbstractFormElement $expanded = !empty($appearance['expandAll']); $showHeader = !empty($appearance['showHeader']); if (isset($config['size']) && (int)$config['size'] > 0) { - $height = min(count($config['items']), (int)$config['size']) * 20; + $height = min(max(count($config['items']), $this->minItemsToShow), (int)$config['size']); } else { - $height = static::DEFAULT_HEIGHT; + $height = $this->itemsToShow; } + $heightInPx = $height * $this->itemHeight; $treeWrapperId = 'tree_' . $formElementId; + $flexFormFieldName = !empty($parameterArray['fieldConf']['flexFormFieldName']) ? htmlspecialchars($parameterArray['fieldConf']['flexFormFieldName']) : ''; $html = []; $html[] = '<div class="typo3-tceforms-tree">'; $html[] = ' <input class="treeRecord" type="hidden"'; - $html[] = ' ' . $this->getValidationDataAsDataAttribute($parameterArray['fieldConf']['config']); + $html[] = ' ' . $this->getValidationDataAsDataAttribute($config); $html[] = ' data-formengine-input-name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'; $html[] = ' data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'; $html[] = ' data-table="' . htmlspecialchars($this->data['tableName']) . '"'; $html[] = ' data-field="' . htmlspecialchars($this->data['fieldName']) . '"'; + $html[] = ' data-flex-form-field-name="' . $flexFormFieldName . '"'; $html[] = ' data-uid="' . (int)$this->data['vanillaUid'] . '"'; $html[] = ' data-command="' . htmlspecialchars($this->data['command']) . '"'; $html[] = ' data-read-only="' . $readOnly . '"'; @@ -79,7 +98,7 @@ class SelectTreeElement extends AbstractFormElement $html[] = ' value="' . htmlspecialchars(implode(',', $config['treeData']['selectedNodes'])) . '"'; $html[] = ' />'; $html[] = '</div>'; - $html[] = '<div id="' . $treeWrapperId . '" class="svg-tree-wrapper" style="height: ' . $height . 'px;"></div>'; + $html[] = '<div id="' . $treeWrapperId . '" class="svg-tree-wrapper" style="height: ' . $heightInPx . 'px;"></div>'; $html[] = '<script type="text/javascript">var ' . $treeWrapperId . ' = ' . $this->getTreeOnChangeJs() . '</script>'; $resultArray['html'] = implode(LF, $html); diff --git a/typo3/sysext/backend/Classes/Tree/Renderer/ExtJsJsonTreeRenderer.php b/typo3/sysext/backend/Classes/Tree/Renderer/ExtJsJsonTreeRenderer.php index 0202b443d142b07079af5f1ce2507244ce214966..553f2952229166ccd9dec96360e91ae8de332ffe 100644 --- a/typo3/sysext/backend/Classes/Tree/Renderer/ExtJsJsonTreeRenderer.php +++ b/typo3/sysext/backend/Classes/Tree/Renderer/ExtJsJsonTreeRenderer.php @@ -54,22 +54,28 @@ class ExtJsJsonTreeRenderer extends \TYPO3\CMS\Backend\Tree\Renderer\AbstractTre */ protected function getNodeArray(\TYPO3\CMS\Backend\Tree\TreeRepresentationNode $node) { - $overlayIcon = ''; - if (is_object($node->getIcon()->getOverlayIcon())) { - $overlayIcon = $node->getIcon()->getOverlayIcon()->getMarkup(SvgIconProvider::MARKUP_IDENTIFIER_INLINE); + $overlayIconMarkup = ''; + if (is_object($node->getIcon())) { + $iconMarkup = $node->getIcon()->getMarkup(SvgIconProvider::MARKUP_IDENTIFIER_INLINE); + if (is_object($node->getIcon()->getOverlayIcon())) { + $overlayIcon = $node->getIcon()->getOverlayIcon()->getMarkup(SvgIconProvider::MARKUP_IDENTIFIER_INLINE); + } + } else { + $iconMarkup = $node->getIcon(); } $nodeArray = array( - 'iconTag' => $node->getIcon()->render(), + 'iconTag' => $iconMarkup, 'text' => htmlspecialchars($node->getLabel()), 'leaf' => !$node->hasChildNodes(), 'id' => htmlspecialchars($node->getId()), 'uid' => htmlspecialchars($node->getId()), //svgtree - 'icon' => $node->getIcon()->getMarkup(SvgIconProvider::MARKUP_IDENTIFIER_INLINE), - 'overlayIcon' => $overlayIcon, + 'icon' => $iconMarkup, + 'overlayIcon' => $overlayIconMarkup, 'identifier' => htmlspecialchars($node->getId()), - 'name' => htmlspecialchars($node->getLabel()), + //no need for htmlspecialhars here as d3 is using 'textContent' property of the HTML DOM node + 'name' => $node->getLabel(), ); return $nodeArray; diff --git a/typo3/sysext/backend/Classes/Tree/TreeRepresentationNode.php b/typo3/sysext/backend/Classes/Tree/TreeRepresentationNode.php index fc4ae29b2f588405b5ac6eb3c46bdf3631b5d4e3..1a03e2da4c49c86c96df54e22279db3ecc5e7aa5 100644 --- a/typo3/sysext/backend/Classes/Tree/TreeRepresentationNode.php +++ b/typo3/sysext/backend/Classes/Tree/TreeRepresentationNode.php @@ -44,7 +44,7 @@ class TreeRepresentationNode extends \TYPO3\CMS\Backend\Tree\TreeNode /** * Node Icon * - * @var string + * @var string | Icon */ protected $icon = ''; diff --git a/typo3/sysext/backend/Resources/Public/Css/backend.css b/typo3/sysext/backend/Resources/Public/Css/backend.css index 3b9326de6e5103acacff297da05fd6248604b708..39a738f0ffe8295d16074176796b2934282cbec8 100644 --- a/typo3/sysext/backend/Resources/Public/Css/backend.css +++ b/typo3/sysext/backend/Resources/Public/Css/backend.css @@ -8047,10 +8047,9 @@ button.close { stroke: #ddd; stroke-width: 1; } -.svg-tree-wrapper .node .chevron { - cursor: pointer; -} -.svg-tree-wrapper .tree-check { +.svg-tree-wrapper .node .chevron, +.svg-tree-wrapper .node text, +.svg-tree-wrapper .node .tree-check { cursor: pointer; } /*! diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTreeElement.js b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTreeElement.js index d590b5e2eec688f6cb2d8d2b8997cd292c4d57a6..e1fe1c4fd5d2403058d279f130b058b89df823df 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTreeElement.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTreeElement.js @@ -30,6 +30,7 @@ define(['jquery', 'TYPO3/CMS/Backend/FormEngine/Element/SelectTree'], function ( table: treeInput.data('table'), field: treeInput.data('field'), uid: treeInput.data('uid'), + flex_form_field_name: treeInput.data('flex-form-field-name'), command: treeInput.data('command') }; var $wrapper = treeInput.parent().siblings('.svg-tree-wrapper'); diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SvgTree.js b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SvgTree.js index 1fd3d244be593225736d906514d69b93070bfaa6..0786ecc219c7bc3e89311d6b0e63da5729fce0f3 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SvgTree.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SvgTree.js @@ -390,7 +390,7 @@ define(['jquery', 'd3'], function ($, d3) { var nodeEnter = nodes .enter() .append('g') - .attr('class', 'node') + .attr('class', this.getNodeClass) .attr('transform', this.getNodeTransform); // append the chevron element @@ -453,6 +453,16 @@ define(['jquery', 'd3'], function ($, d3) { return node.data.name; }, + /** + * Computes the tree node class + * + * @param {Node} node + * @returns {String} + */ + getNodeClass: function (node) { + return 'node identifier-' + node.data.identifier; + }, + /** * Computes the tree item label based on the data * diff --git a/typo3/sysext/core/Tests/Acceptance/Backend/Formhandler/CategoryTreeCest.php b/typo3/sysext/core/Tests/Acceptance/Backend/Formhandler/CategoryTreeCest.php index afc7388aca8aae46d8b21634c3a53d9653946b8f..5f04404264ca96f573696752d69dd56403f8afff 100644 --- a/typo3/sysext/core/Tests/Acceptance/Backend/Formhandler/CategoryTreeCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Backend/Formhandler/CategoryTreeCest.php @@ -55,11 +55,11 @@ class CategoryTreeCest $I->click('#recordlist-sys_category tr[data-uid="7"] a[data-original-title="Edit record"]'); // Change title and level to root $I->fillField('input[data-formengine-input-name="data[sys_category][7][title]"]', 'level-1-4'); - $I->click('div[ext\:tree-node-id="7"]'); - $I->click('div[ext\:tree-node-id="3"]'); + $I->click('.identifier-7 text'); + $I->click('.identifier-3 text'); $I->click('button[name="_savedok"]'); // Wait for tree and check if isset level-1-4 - $I->waitForElement('.x-panel.x-tree'); - $I->see('level-1-4', '.x-panel.x-tree'); + $I->waitForElement('.svg-tree-wrapper svg'); + $I->see('level-1-4', '.svg-tree-wrapper svg .node text'); } }