]*>(?:(?!<\/p>).)*btn(?:(?!<\/p>).)*<\/p>/is', '', (string)$ce['bodytext']) ?? ''; if (trim(strip_tags($body)) !== '') { $specs[] = ['CType' => 'text', 'fields' => ['header' => '', 'bodytext' => trim($body)], 'files' => []]; } } } foreach ($roots as $ce) { if ((int)$ce['colPos'] === 0) { $this->transformCe($ce, $specs); } } return $specs; } private function transformCe(array $ce, array &$specs): void { $ctype = (string)$ce['CType']; if (in_array($ctype, self::SKIP_CTYPES, true)) { return; } if (in_array($ctype, self::CONTAINER_CTYPES, true)) { $this->transformContainer($ce, $specs); return; } switch ($ctype) { case 'text': $specs[] = $this->textOrQuoteSpec($ce); break; case 'image': case 'textmedia': $field = $ctype === 'image' ? 'image' : 'assets'; $spec = [ 'CType' => $ctype, 'fields' => array_filter([ 'header' => (string)($ce['header'] ?? ''), 'header_layout' => (int)($ce['header_layout'] ?? 0), 'bodytext' => (string)($ce['bodytext'] ?? ''), ], static fn($v) => $v !== '' && $v !== 0), 'files' => [], ]; $refs = $this->refs('tt_content', (int)$ce['uid'], $field); if ($refs !== []) { $spec['files'][] = ['field' => $field, 'refs' => $refs]; } if ($spec['files'] !== [] || ($spec['fields']['bodytext'] ?? '') !== '') { $specs[] = $spec; } break; case 'html': if (!empty($ce['bodytext'])) { $specs[] = ['CType' => 'html', 'fields' => ['bodytext' => (string)$ce['bodytext']], 'files' => []]; } break; default: // unknown → keep text-ish content if any if (!empty($ce['bodytext']) || !empty($ce['header'])) { $specs[] = ['CType' => 'text', 'fields' => [ 'header' => (string)($ce['header'] ?? ''), 'bodytext' => (string)($ce['bodytext'] ?? ''), ], 'files' => []]; } } } /** Two clean columns -> vitec_columns; everything else flattens in order. */ private function transformContainer(array $ce, array &$specs): void { $children = $this->childrenByParent[(int)$ce['uid']] ?? []; if ($children === []) { return; } $byCol = []; foreach ($children as $child) { $byCol[(int)$child['colPos'] % 100][] = $child; } ksort($byCol); $isSimple = static fn(array $c): bool => in_array($c['CType'], ['text', 'image'], true) && stripos((string)($c['bodytext'] ?? ''), '
$ok && $isSimple($c), true); if ($allSimple) { $items = []; foreach ($byCol as $colIdx => $colCes) { $side = $colIdx === $cols[0] ? 'left' : 'right'; foreach ($colCes as $c) { $t = $this->collectionTable; if ($c['CType'] === 'text') { $items[] = ['fields' => [ $this->cbCol($t, 'column') => $side, $this->cbCol($t, 'item_type') => 'text', $this->cbCol($t, 'headline') => (string)($c['header'] ?? ''), $this->cbCol($t, 'text') => (string)($c['bodytext'] ?? ''), ], 'files' => []]; } else { $refs = $this->refs('tt_content', (int)$c['uid'], 'image'); $items[] = ['fields' => [ $this->cbCol($t, 'column') => $side, $this->cbCol($t, 'item_type') => 'image', $this->cbCol($t, 'headline') => (string)($c['header'] ?? ''), ], 'files' => $refs !== [] ? [['field' => $this->cbCol($t, 'image'), 'refs' => array_slice($refs, 0, 1)]] : []]; } } } $specs[] = [ 'CType' => 'vitec_columns', 'fields' => ['header' => (string)($ce['header'] ?? ''), $this->cbCol('tt_content', 'layout') => 'cols_50_50'], 'files' => [], 'items' => $items, ]; return; } // flatten: children first; the container header only becomes a heading // when the container actually contributed content (skips e.g. the old // "Related Success Stories" section whose children are all shortcuts). $childSpecs = []; foreach ($byCol as $colCes) { foreach ($colCes as $child) { $this->transformCe($child, $childSpecs); } } $header = trim((string)($ce['header'] ?? '')); if ($childSpecs !== [] && $header !== '' && !str_starts_with($header, '///')) { $specs[] = ['CType' => 'text', 'fields' => ['header' => $header], 'files' => []]; } foreach ($childSpecs as $cs) { $specs[] = $cs; } } /** blockquote text CEs become vitec_quotation; the rest stay text. */ private function textOrQuoteSpec(array $ce): array { $body = (string)($ce['bodytext'] ?? ''); $header = (string)($ce['header'] ?? ''); if (stripos($body, ']*>(.*)<\/blockquote>/is', $body, $m)) { $inner = $m[1]; // attribution paragraph:— Name
if (preg_match('/
Position\s*\s*(?:—|—|-)?\s*(.*?)<\/strong>\s*(?:
)?\s*(.*?)<\/p>\s*$/is', $inner, $a)) { $name = trim(strip_tags($a[1])); $position = trim(strip_tags($a[2])); $inner = (string)preg_replace('/\s*\s*(?:—|—|-)?.*?<\/p>\s*$/is', '', $inner); } $quote = trim(strip_tags($inner)); $quote = trim($quote, "“”\"' \u{201C}\u{201D}"); } return ['CType' => 'vitec_quotation', 'fields' => [ $this->cbCol('tt_content', 'quote') => $quote, $this->cbCol('tt_content', 'quote_name') => $name, $this->cbCol('tt_content', 'quote_position') => $position, ], 'files' => []]; } return ['CType' => 'text', 'fields' => array_filter([ 'header' => strcasecmp($header, 'Quote') === 0 ? '' : $header, 'header_layout' => (int)($ce['header_layout'] ?? 0), 'bodytext' => $body, ], static fn($v) => $v !== '' && $v !== 0), 'files' => []]; } // ==================================================================== files /** Old-site identifier -> new-site sys_file uid (staged under FILES_BASE). */ private function fileUidFor(string $identifier): ?int { if (isset($this->fileUidCache[$identifier])) { return $this->fileUidCache[$identifier] ?: null; } try { $file = GeneralUtility::makeInstance(ResourceFactory::class) ->getFileObjectFromCombinedIdentifier('1:' . self::FILES_BASE . $identifier); $uid = $file !== null ? (int)$file->getUid() : 0; } catch (\Throwable $e) { $uid = 0; } $this->fileUidCache[$identifier] = $uid; return $uid ?: null; } }