• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FluidTYPO3 / flux / 27753814608

18 Jun 2026 10:39AM UTC coverage: 89.162% (-3.5%) from 92.646%
27753814608

Pull #2288

github

web-flow
Merge 37edf9f2e into 2614049c6
Pull Request #2288: [FEATURE] Prepare for v14 support

210 of 348 new or added lines in 56 files covered. (60.34%)

121 existing lines in 9 files now uncovered.

6228 of 6985 relevant lines covered (89.16%)

40.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.39
/Classes/Utility/MiscellaneousUtility.php
1
<?php
2
namespace FluidTYPO3\Flux\Utility;
3

4
/*
5
 * This file is part of the FluidTYPO3/Flux project under GPLv2 or later.
6
 *
7
 * For the full copyright and license information, please read the
8
 * LICENSE.md file that was distributed with this source code.
9
 */
10

11
use DOMElement;
12
use DOMNode;
13
use DOMNodeList;
14
use FluidTYPO3\Flux\Enum\FormOption;
15
use FluidTYPO3\Flux\Form;
16
use TYPO3\CMS\Core\Imaging\Icon;
17
use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
18
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
19
use TYPO3\CMS\Core\Imaging\IconRegistry;
20
use TYPO3\CMS\Core\Imaging\IconSize;
21
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
22
use TYPO3\CMS\Core\Utility\GeneralUtility;
23

24
class MiscellaneousUtility
25
{
26
    private static array $allowedIconTypes = ['svg', 'png', 'gif'];
27

28
    /**
29
     * Returns the icon for a template
30
     * - checks and returns if manually set as option or
31
     * - checks and returns Icon if it exists by convention in
32
     *   EXT:$extensionKey/Resources/Public/Icons/$controllerName/$templateName.(png|gif)
33
     */
34
    public static function getIconForTemplate(Form $form): ?string
35
    {
36
        if (true === $form->hasOption(FormOption::ICON)) {
36✔
37
            $iconOptionValue = $form->getOption(FormOption::ICON);
4✔
38
            return is_scalar($iconOptionValue) ? (string) $iconOptionValue : null;
4✔
39
        }
40
        if (true === $form->hasOption(FormOption::TEMPLATE_FILE)) {
32✔
41
            $extensionKey = ExtensionNamingUtility::getExtensionKey((string) $form->getExtensionName());
4✔
42
            $fullTemplatePathAndName = $form->getOption(FormOption::TEMPLATE_FILE);
4✔
43
            $templatePathParts = is_scalar($fullTemplatePathAndName)
4✔
44
                ? explode('/', (string) $fullTemplatePathAndName)
4✔
UNCOV
45
                : [];
×
46
            if (empty($templatePathParts)) {
4✔
47
                return null;
×
48
            }
49
            $templateName = pathinfo(array_pop($templatePathParts), PATHINFO_FILENAME);
4✔
50
            $controllerName = array_pop($templatePathParts);
4✔
51
            $relativeIconFolder = 'Resources/Public/Icons/' . $controllerName . '/';
4✔
52
            $iconFolder = ExtensionManagementUtility::extPath(
4✔
53
                $extensionKey,
4✔
54
                $relativeIconFolder
4✔
55
            );
4✔
56
            $iconPathAndName = $iconFolder . $templateName;
4✔
57
            $filesInFolder = array();
4✔
58
            if (true === is_dir($iconFolder)) {
4✔
59
                if (true === defined('GLOB_BRACE')) {
×
60
                    $allowedExtensions = implode(',', self::$allowedIconTypes);
×
61
                    $iconMatchPattern = $iconPathAndName . '.{' . $allowedExtensions . '}';
×
62
                    $filesInFolder = glob($iconMatchPattern, GLOB_BRACE);
×
63
                } else {
64
                    foreach (self::$allowedIconTypes as $allowedIconType) {
×
65
                        $filesInFolder = array_merge(
×
66
                            $filesInFolder,
×
67
                            glob($iconPathAndName . '.' . $allowedIconType) ?: []
×
68
                        );
×
69
                    }
70
                }
71
            }
72
            $iconFile = (is_array($filesInFolder) && 0 < count($filesInFolder) ? reset($filesInFolder) : null);
4✔
73
            $iconRelPathAndFilename = $iconFile
4✔
74
                ? 'EXT:' . $extensionKey . '/' . $relativeIconFolder . pathinfo($iconFile, PATHINFO_BASENAME)
×
75
                : null;
4✔
76
            return $iconRelPathAndFilename;
4✔
77
        }
78
        return null;
28✔
79
    }
80

81
    /**
82
     * Returns a generated icon file into typo3temp/pics
83
     */
84
    public static function createIcon(string $originalFile, ?string $identifier = null): string
85
    {
86
        /** @var IconRegistry $iconRegistry */
87
        $iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
8✔
88
        if ($iconRegistry->isRegistered($originalFile)) {
8✔
89
            return $originalFile;
8✔
90
        }
91

NEW
92
        if (VersionUtility::isCoreAtLeast14()) {
×
NEW
93
            $iconSizeConstant = IconSize::DEFAULT;
×
94
        } else {
NEW
95
            $iconSizeConstant = Icon::SIZE_DEFAULT;
×
96
        }
97

98
        $extension = pathinfo($originalFile, PATHINFO_EXTENSION);
×
99
        switch (strtolower($extension)) {
×
100
            case 'svg':
×
101
            case 'svgz':
×
102
                $iconProvider = SvgIconProvider::class;
×
103
                break;
×
104
            default:
105
                $iconProvider = BitmapIconProvider::class;
×
106
        }
107

108
        $iconIdentifier = $identifier ?? 'icon-' . md5($originalFile);
×
109
        $iconRegistry->registerIcon(
×
110
            $iconIdentifier,
×
111
            $iconProvider,
×
NEW
112
            ['source' => $originalFile, 'size' => $iconSizeConstant]
×
113
        );
×
114
        return $iconIdentifier;
×
115
    }
116

117
    /**
118
     * Cleans flex form XML, removing any field nodes identified
119
     * in $removals and trimming the result to avoid empty containers.
120
     */
121
    public static function cleanFlexFormXml(string $xml, array $removals = []): string
122
    {
123
        $dom = new \DOMDocument();
24✔
124
        $dom->loadXML($xml);
24✔
125
        $dom->preserveWhiteSpace = false;
24✔
126
        $dom->formatOutput = true;
24✔
127
        $fieldNodesToRemove = [];
24✔
128
        foreach ($dom->getElementsByTagName('field') as $fieldNode) {
24✔
129
            /** @var DOMElement $fieldNode */
130
            if (true === in_array($fieldNode->getAttribute('index'), $removals)) {
16✔
131
                $fieldNodesToRemove[] = $fieldNode;
12✔
132
            }
133
        }
134

135
        foreach ($fieldNodesToRemove as $fieldNodeToRemove) {
24✔
136
            /** @var DOMNode $parent */
137
            $parent = $fieldNodeToRemove->parentNode;
12✔
138
            /** @var DOMElement $fieldNodeToRemove */
139
            $parent->removeChild($fieldNodeToRemove);
12✔
140
        }
141

142
        // Assign a hidden ID to all container-type nodes, making the value available in templates etc.
143
        foreach ($dom->getElementsByTagName('el') as $containerNode) {
24✔
144
            /** @var DOMElement $containerNode */
145
            $hasIdNode = false;
12✔
146
            if ($containerNode->attributes instanceof \DOMNamedNodeMap && 0 < count($containerNode->attributes)) {
12✔
147
                // skip <el> tags reserved for other purposes by attributes; only allow pure <el> tags.
148
                continue;
12✔
149
            }
150
            foreach ($containerNode->childNodes as $fieldNodeInContainer) {
4✔
151
                /** @var DOMNode $fieldNodeInContainer */
152
                if (false === $fieldNodeInContainer instanceof DOMElement) {
4✔
153
                    continue;
4✔
154
                }
155
                $isFieldNode = ('field' === $fieldNodeInContainer->tagName);
4✔
156
                $isIdField = ('id' === $fieldNodeInContainer->getAttribute('index'));
4✔
157
                if ($isFieldNode && $isIdField) {
4✔
158
                    $hasIdNode = true;
4✔
159
                    break;
4✔
160
                }
161
            }
162
            if (false === $hasIdNode) {
4✔
163
                $idNode = $dom->createElement('field');
×
164
                $idIndexAttribute = $dom->createAttribute('index');
×
165
                $idIndexAttribute->nodeValue = 'id';
×
166
                $idNode->appendChild($idIndexAttribute);
×
167
                $valueNode = $dom->createElement('value');
×
168
                $valueIndexAttribute = $dom->createAttribute('index');
×
169
                $valueIndexAttribute->nodeValue = 'vDEF';
×
170
                $valueNode->appendChild($valueIndexAttribute);
×
171
                $valueNode->nodeValue = sha1(uniqid('container_', true));
×
172
                $idNode->appendChild($valueNode);
×
173
                $containerNode->appendChild($idNode);
×
174
            }
175
        }
176
        // Remove all sheets that no longer contain any fields.
177
        $nodesToBeRemoved = [];
24✔
178
        foreach ($dom->getElementsByTagName('sheet') as $sheetNode) {
24✔
179
            if (0 === $sheetNode->getElementsByTagName('field')->length) {
16✔
180
                $nodesToBeRemoved[] = $sheetNode;
8✔
181
            }
182
        }
183

184
        foreach ($nodesToBeRemoved as $node) {
24✔
185
            /** @var DOMNode $parent */
186
            $parent = $node->parentNode;
8✔
187
            /** @var DOMElement $node */
188
            $parent->removeChild($node);
8✔
189
        }
190

191
        // Return empty string in case remaining flexform XML is all empty
192
        /** @var DOMNodeList $dataNodes */
193
        $dataNodes = $dom->getElementsByTagName('data');
24✔
194
        /** @var DOMElement $dataNode */
195
        $dataNode = $dataNodes->item(0);
24✔
196
        if ($dataNode === null) {
24✔
197
            return '';
×
198
        }
199
        $elements = $dataNode->getElementsByTagName('sheet');
24✔
200
        if (0 === $elements->length) {
24✔
201
            return '';
12✔
202
        }
203
        $xml = (string) $dom->saveXML();
12✔
204
        // hack-like pruning of empty-named node inserted when removing objects from a previously populated Section
205
        $xml = (string) preg_replace('#<el index="el">\s*</el>#', '', $xml);
12✔
206
        $xml = (string) preg_replace('#<field index="[^"]*">\s*</field>#', '', $xml);
12✔
207
        return $xml;
12✔
208
    }
209
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc