Sub-solution workbook pipeline on layout 5, fix element order in both importers
- New, deliberately separate pipeline for sub-solutions (markets and solutions must never break each other): generator vitec:generate-subsolution-workbooks (list from the page tree below /solutions, 39 workbooks, every sub-solution matched to its record), SubsolutionWorkbookImporter + CLI vitec:import-subsolution-workbooks, and a "Sub-Solutions" tab in the VITEC Import module with its own inbox migrations/subsolutions_xlsx_in/ - Targets the existing template 5 "Solution Detail" without changing it: hero element in colPos 1, the remaining sections in fixed order in colPos 0, found again by CType on re-import; existing empty sub-solution pages are switched to layout 5, missing ones created hidden; detail_page set on the solution record - Workbook per the Figma "07 Solution Detail page": hero without video teaser, intro with headline and without learn-more/portfolio, extended icon list, own page-layout reference image - Shared content elements: market hero/intro/insights relabelled "Market/Solution", market intro gains the optional intro_headline field (new column, requires extension:setup) - Fix: DataHandler puts every NEW record with a positive pid at the top of its column, so pages built in one run came out reversed. Both importers now track the page order per colPos and write tt_content.sorting explicitly after the DataHandler run; re-applying repairs pages imported earlier (verified on the control-room sub-solution and sub-market page 443) - migrations/fill_test_subsolution.php: realistic test content from vitec.com copy and images for the control-room sub-solution - Docs section 5z
This commit is contained in:
1 parent
3d5459b8d2
commit
8e67f5db23
84 files changed
+1772
-35
No files matched your search
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Evomedien\Vitec\Command;
|
||||
|
||||
use Evomedien\Vitec\Import\SubsolutionWorkbookImporter;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use TYPO3\CMS\Core\Core\Bootstrap;
|
||||
|
||||
/**
|
||||
* CLI shell around SubsolutionWorkbookImporter - the deliberate sibling of
|
||||
* vitec:import-submarket-workbooks (markets and solutions evolve
|
||||
* independently; behavior and lessons are documented on the importer).
|
||||
*
|
||||
* vendor/bin/typo3 vitec:import-subsolution-workbooks --dir=migrations/subsolutions_xlsx_in
|
||||
* vendor/bin/typo3 vitec:import-subsolution-workbooks --dir=... --only=iptv --apply
|
||||
*/
|
||||
#[AsCommand(
|
||||
name: 'vitec:import-subsolution-workbooks',
|
||||
description: 'Import filled sub-solution workbooks into layout-5 pages (dry-run by default)'
|
||||
)]
|
||||
final class ImportSubsolutionWorkbooksCommand extends Command
|
||||
{
|
||||
public function __construct(private readonly SubsolutionWorkbookImporter $importer)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption('dir', null, InputOption::VALUE_REQUIRED, 'Directory with filled workbooks', 'migrations/subsolutions_xlsx_in');
|
||||
$this->addOption('only', null, InputOption::VALUE_REQUIRED, 'Only files whose name contains this substring');
|
||||
$this->addOption('apply', null, InputOption::VALUE_NONE, 'Write changes (default: dry run)');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
Bootstrap::initializeBackendAuthentication();
|
||||
$apply = (bool)$input->getOption('apply');
|
||||
$only = mb_strtolower((string)($input->getOption('only') ?? ''));
|
||||
$dir = rtrim((string)$input->getOption('dir'), '/');
|
||||
|
||||
if (!is_dir($dir)) {
|
||||
$output->writeln('<error>Directory not found: ' . $dir . '</error>');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$files = [];
|
||||
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir));
|
||||
foreach ($iterator as $fileInfo) {
|
||||
$name = $fileInfo->getFilename();
|
||||
if ($fileInfo->isFile() && str_ends_with($name, '.xlsx') && !str_starts_with($name, '~$')) {
|
||||
if ($only === '' || str_contains(mb_strtolower($name), $only)) {
|
||||
$files[] = $fileInfo->getPathname();
|
||||
}
|
||||
}
|
||||
}
|
||||
sort($files);
|
||||
if ($files === []) {
|
||||
$output->writeln('No workbooks found in ' . $dir);
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
$output->writeln(count($files) . ' workbook(s) · ' . ($apply ? 'APPLY' : 'DRY-RUN'));
|
||||
|
||||
$errors = 0;
|
||||
foreach ($files as $file) {
|
||||
try {
|
||||
$result = $this->importer->importFile($file, $apply);
|
||||
$output->writeln(sprintf("\n<info>%s</info> (%s / %s)", $result['file'], $result['group'], $result['subsolution']));
|
||||
foreach ($result['report'] as $line) {
|
||||
$output->writeln(' <comment>' . $line . '</comment>');
|
||||
}
|
||||
$output->writeln(sprintf(' page %s · %d element(s) touched · %s',
|
||||
$result['newPage'] && $result['pageUid'] === 0 ? 'NEW' : '#' . $result['pageUid'],
|
||||
$result['elements'],
|
||||
$apply ? 'written.' : 'dry run'));
|
||||
} catch (\Throwable $e) {
|
||||
$errors++;
|
||||
$output->writeln('<error>' . basename($file) . ': ' . $e->getMessage() . '</error>');
|
||||
}
|
||||
}
|
||||
if (!$apply) {
|
||||
$output->writeln("\nDRY RUN - nothing written. Add --apply to write.");
|
||||
}
|
||||
return $errors > 0 ? Command::FAILURE : Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user