From 3f25ae953cb63c7d1037cf5a67db9488d68fd383 Mon Sep 17 00:00:00 2001 From: Tymoteusz Motylewski <t.motylewski@gmail.com> Date: Tue, 30 Aug 2016 15:27:08 +0200 Subject: [PATCH] [BUGFIX] Fix selectTree in FlexForms Now SVG tree loads correctly in FlexForms. It also fixes and issue with tree nodes displaying & instead of & Additionally acceptance test for category tree is fixed. Add pointer cursor for tree item label. Add min height for a tree (used when you have just a few categories) Check for icon being an object before treating it as such. Resolves: #77681 Releases: master Change-Id: I33ec8c50419063ca9ef55d1cf804bde617e32554 Reviewed-on: https://review.typo3.org/49638 Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl> Tested-by: Wouter Wolters <typo3@wouterwolters.nl> Tested-by: Bamboo TYPO3com <info@typo3.com> Reviewed-by: Georg Ringer <georg.ringer@gmail.com> Tested-by: Georg Ringer <georg.ringer@gmail.com> --- .../Public/Less/Component/svgTree.less | 9 +++-- .../Controller/SelectTreeController.php | 36 ++++++++++++++++++- .../Container/FlexFormElementContainer.php | 2 ++ .../Form/Element/SelectTreeElement.php | 33 +++++++++++++---- .../Tree/Renderer/ExtJsJsonTreeRenderer.php | 20 +++++++---- .../Classes/Tree/TreeRepresentationNode.php | 2 +- .../backend/Resources/Public/Css/backend.css | 7 ++-- .../FormEngine/Element/SelectTreeElement.js | 1 + .../JavaScript/FormEngine/Element/SvgTree.js | 12 ++++++- .../Backend/Formhandler/CategoryTreeCest.php | 8 ++--- 10 files changed, 100 insertions(+), 30 deletions(-) diff --git a/Build/Resources/Public/Less/Component/svgTree.less b/Build/Resources/Public/Less/Component/svgTree.less index ba7848ef41ce..774efa7c204f 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 4dac0757876a..01a25f69ebd8 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 c6df90854509..d141f618b62d 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 71d7176d9a1f..053356b0bc64 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 0202b443d142..553f29522291 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 fc4ae29b2f58..1a03e2da4c49 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 3b9326de6e51..39a738f0ffe8 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 d590b5e2eec6..e1fe1c4fd5d2 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 1fd3d244be59..0786ecc219c7 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 afc7388aca8a..5f04404264ca 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'); } } -- GitLab