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

FluidTYPO3 / flux / 17904759627

15 Sep 2025 08:47AM UTC coverage: 90.676% (-2.1%) from 92.767%
17904759627

push

github

NamelessCoder
[TASK] Set beta stability

6924 of 7636 relevant lines covered (90.68%)

9.49 hits per line

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

63.21
/Classes/Utility/MiscellaneousUtility.php
1
<?php
2
declare(strict_types=1);
3
namespace FluidTYPO3\Flux\Utility;
4

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

12
use DOMElement;
13
use DOMNode;
14
use DOMNodeList;
15
use FluidTYPO3\Flux\Enum\FormOption;
16
use FluidTYPO3\Flux\Form;
17
use TYPO3\CMS\Core\Imaging\Icon;
18
use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
19
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
20
use TYPO3\CMS\Core\Imaging\IconRegistry;
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)) {
9✔
37
            $iconOptionValue = $form->getOption(FormOption::ICON);
1✔
38
            return is_scalar($iconOptionValue) ? (string) $iconOptionValue : null;
1✔
39
        }
40
        if (true === $form->hasOption(FormOption::TEMPLATE_FILE)) {
8✔
41
            $extensionKey = ExtensionNamingUtility::getExtensionKey((string) $form->getExtensionName());
1✔
42
            $fullTemplatePathAndName = $form->getOption(FormOption::TEMPLATE_FILE);
1✔
43
            $templatePathParts = is_scalar($fullTemplatePathAndName)
1✔
44
                ? explode('/', (string) $fullTemplatePathAndName)
1✔
45
                : [];
×
46
            if (empty($templatePathParts)) {
1✔
47
                return null;
×
48
            }
49
            $templateName = pathinfo(array_pop($templatePathParts), PATHINFO_FILENAME);
1✔
50
            $controllerName = array_pop($templatePathParts);
1✔
51
            $relativeIconFolder = 'Resources/Public/Icons/' . $controllerName . '/';
1✔
52
            $iconFolder = ExtensionManagementUtility::extPath(
1✔
53
                $extensionKey,
1✔
54
                $relativeIconFolder
1✔
55
            );
1✔
56
            $iconPathAndName = $iconFolder . $templateName;
1✔
57
            $filesInFolder = array();
1✔
58
            if (true === is_dir($iconFolder)) {
1✔
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);
1✔
73
            $iconRelPathAndFilename = $iconFile
1✔
74
                ? 'EXT:' . $extensionKey . '/' . $relativeIconFolder . pathinfo($iconFile, PATHINFO_BASENAME)
×
75
                : null;
1✔
76
            return $iconRelPathAndFilename;
1✔
77
        }
78
        return null;
7✔
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);
2✔
88
        if ($iconRegistry->isRegistered($originalFile)) {
2✔
89
            return $originalFile;
2✔
90
        }
91

92
        if (strpos($originalFile, 'EXT:') === 0 || $originalFile[0] !== '/') {
×
93
            $originalFile = GeneralUtility::getFileAbsFileName($originalFile);
×
94
        }
95

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

106
        $iconIdentifier = $identifier ?? 'icon-' . md5($originalFile);
×
107
        $iconRegistry->registerIcon(
×
108
            $iconIdentifier,
×
109
            $iconProvider,
×
110
            ['source' => $originalFile, 'size' => Icon::SIZE_DEFAULT]
×
111
        );
×
112
        return $iconIdentifier;
×
113
    }
114

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

133
        foreach ($fieldNodesToRemove as $fieldNodeToRemove) {
6✔
134
            /** @var DOMNode $parent */
135
            $parent = $fieldNodeToRemove->parentNode;
3✔
136
            /** @var DOMElement $fieldNodeToRemove */
137
            $parent->removeChild($fieldNodeToRemove);
3✔
138
        }
139

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

182
        foreach ($nodesToBeRemoved as $node) {
6✔
183
            /** @var DOMNode $parent */
184
            $parent = $node->parentNode;
2✔
185
            /** @var DOMElement $node */
186
            $parent->removeChild($node);
2✔
187
        }
188

189
        // Return empty string in case remaining flexform XML is all empty
190
        /** @var DOMNodeList $dataNodes */
191
        $dataNodes = $dom->getElementsByTagName('data');
6✔
192
        /** @var DOMElement $dataNode */
193
        $dataNode = $dataNodes->item(0);
6✔
194
        $elements = $dataNode->getElementsByTagName('sheet');
6✔
195
        if (0 === $elements->length) {
6✔
196
            return '';
3✔
197
        }
198
        $xml = (string) $dom->saveXML();
3✔
199
        // hack-like pruning of empty-named node inserted when removing objects from a previously populated Section
200
        $xml = (string) preg_replace('#<el index="el">\s*</el>#', '', $xml);
3✔
201
        $xml = (string) preg_replace('#<field index="[^"]*">\s*</field>#', '', $xml);
3✔
202
        return $xml;
3✔
203
    }
204
}
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