Page content check for markets and solutions (vitec_pagecheck)
Sibling of the product check (decision Harry 2026-11-13): new content element vitec_pagecheck with a FlexForm scope (markets | solutions) and PageCheckService/PageCheckJsonRenderer. Overview = one row per page of the tree (11 markets + 24 sub-markets, 8 solution groups + 39 sub-solutions) with 18 checks taken from the page elements (hero image and subline, lead, intro text/image/CTA, cards with visuals, testimonials, products, insights text/cover/CTA, logos, CTA banner), the page properties (meta description, OG image, page media) and the market/solution record behind the page; columns a page type does not have come as ok null. ?check=<pageUid> delivers the page detail (elements in page order with texts, links, images, FlexForm settings; SEO, media, record, children). Always USER_INT, check is cHash-excluded already. New command vitec:pagecheck-pages created /markets/market-check (608) and /solutions/solution-check (609) from the product-check page properties; both live on DEV, rendering is front end work. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
47c8e2902f
commit
20bcbeb731
9 files changed
+913
No files matched your search
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Evomedien\Vitec\Command;
|
||||
|
||||
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;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\DataHandling\DataHandler;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Creates the two internal audit pages for the page content check (decision
|
||||
* Harry 2026-11-13, sibling of /products/product-check):
|
||||
*
|
||||
* /markets/market-check vitec_pagecheck, scope markets
|
||||
* /solutions/solution-check vitec_pagecheck, scope solutions
|
||||
*
|
||||
* Page properties are taken from the product check page (/products/
|
||||
* product-check: doktype, backend layout, no_index, nav_hide, nav_title
|
||||
* handling) so all three audit pages behave alike. Idempotent: an existing
|
||||
* slug is reported and skipped.
|
||||
*
|
||||
* vendor/bin/typo3 vitec:pagecheck-pages # dry run (default)
|
||||
* vendor/bin/typo3 vitec:pagecheck-pages --apply # creates
|
||||
*/
|
||||
#[AsCommand(
|
||||
name: 'vitec:pagecheck-pages',
|
||||
description: 'Create /markets/market-check and /solutions/solution-check with the vitec_pagecheck element (dry-run by default)'
|
||||
)]
|
||||
final class CreateCheckPagesCommand extends Command
|
||||
{
|
||||
private const TEMPLATE_SLUG = '/products/product-check';
|
||||
|
||||
private const PAGES = [
|
||||
['parentSlug' => '/markets', 'title' => 'Market Check', 'slug' => '/markets/market-check', 'header' => 'Market Content Check', 'scope' => 'markets'],
|
||||
['parentSlug' => '/solutions', 'title' => 'Solution Check', 'slug' => '/solutions/solution-check', 'header' => 'Solution Content Check', 'scope' => 'solutions'],
|
||||
];
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->addOption('apply', null, InputOption::VALUE_NONE, 'Create the pages (default: dry run)');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
Bootstrap::initializeBackendAuthentication();
|
||||
$apply = (bool)$input->getOption('apply');
|
||||
$db = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
|
||||
|
||||
$template = $db->fetchAssociative(
|
||||
'SELECT uid, doktype, backend_layout, backend_layout_next_level, no_index, no_follow, nav_hide, no_search, seo_title, description FROM pages WHERE deleted = 0 AND slug = ?',
|
||||
[self::TEMPLATE_SLUG]
|
||||
);
|
||||
if ($template === false) {
|
||||
$output->writeln('<error>Template page ' . self::TEMPLATE_SLUG . ' not found.</error>');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
$output->writeln(sprintf(
|
||||
'Template %s (uid %d): doktype %d, layout "%s", no_index %d, nav_hide %d, no_search %d',
|
||||
self::TEMPLATE_SLUG, $template['uid'], $template['doktype'], $template['backend_layout'], $template['no_index'], $template['nav_hide'], $template['no_search']
|
||||
));
|
||||
$output->writeln($apply ? '<comment>APPLY</comment>' : 'DRY RUN - nothing is written (--apply to create)');
|
||||
|
||||
$created = 0;
|
||||
foreach (self::PAGES as $def) {
|
||||
$existing = $db->fetchAssociative('SELECT uid FROM pages WHERE deleted = 0 AND slug = ?', [$def['slug']]);
|
||||
if ($existing !== false) {
|
||||
$output->writeln(sprintf(' %s exists (uid %d) - skipped', $def['slug'], $existing['uid']));
|
||||
continue;
|
||||
}
|
||||
$parent = $db->fetchAssociative('SELECT uid FROM pages WHERE deleted = 0 AND sys_language_uid = 0 AND slug = ?', [$def['parentSlug']]);
|
||||
if ($parent === false) {
|
||||
$output->writeln(sprintf(' <error>parent %s not found - %s skipped</error>', $def['parentSlug'], $def['slug']));
|
||||
continue;
|
||||
}
|
||||
$output->writeln(sprintf(' create %s "%s" under %s (pid %d) + vitec_pagecheck "%s" (scope %s)', $def['slug'], $def['title'], $def['parentSlug'], $parent['uid'], $def['header'], $def['scope']));
|
||||
$created++;
|
||||
if (!$apply) {
|
||||
continue;
|
||||
}
|
||||
$pageId = 'NEWpage' . $def['scope'];
|
||||
$datamap = [
|
||||
'pages' => [
|
||||
$pageId => [
|
||||
'pid' => -(int)$this->lastChild((int)$parent['uid'], (int)$parent['uid']),
|
||||
'title' => $def['title'],
|
||||
'slug' => $def['slug'],
|
||||
'doktype' => (int)$template['doktype'],
|
||||
'backend_layout' => (string)$template['backend_layout'],
|
||||
'backend_layout_next_level' => (string)$template['backend_layout_next_level'],
|
||||
'no_index' => (int)$template['no_index'],
|
||||
'no_follow' => (int)$template['no_follow'],
|
||||
'nav_hide' => (int)$template['nav_hide'],
|
||||
'no_search' => (int)$template['no_search'],
|
||||
'hidden' => 0,
|
||||
],
|
||||
],
|
||||
'tt_content' => [
|
||||
'NEWce' . $def['scope'] => [
|
||||
'pid' => $pageId,
|
||||
'colPos' => 0,
|
||||
'sys_language_uid' => 0,
|
||||
'CType' => 'vitec_pagecheck',
|
||||
'header' => $def['header'],
|
||||
'pi_flexform' => ['data' => ['sDEF' => ['lDEF' => ['settings.scope' => ['vDEF' => $def['scope']]]]]],
|
||||
],
|
||||
],
|
||||
];
|
||||
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
|
||||
$dataHandler->start($datamap, []);
|
||||
$dataHandler->process_datamap();
|
||||
if ($dataHandler->errorLog !== []) {
|
||||
$output->writeln(' <error>DataHandler: ' . implode(' | ', $dataHandler->errorLog) . '</error>');
|
||||
return Command::FAILURE;
|
||||
}
|
||||
foreach ($dataHandler->substNEWwithIDs as $new => $uid) {
|
||||
$output->writeln(sprintf(' written: %s -> #%d', $new, $uid));
|
||||
}
|
||||
}
|
||||
$output->writeln(sprintf('%s: %d page(s).', $apply ? 'DONE' : 'DRY RUN - would create', $created));
|
||||
if ($apply && $created > 0) {
|
||||
$output->writeln('Afterwards: vendor/bin/typo3 cache:flush');
|
||||
}
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/** uid of the last live child page (to append after it), or the parent itself when there is none */
|
||||
private function lastChild(int $parentUid, int $fallback): int
|
||||
{
|
||||
$uid = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages')->fetchOne(
|
||||
'SELECT uid FROM pages WHERE deleted = 0 AND sys_language_uid = 0 AND pid = ? ORDER BY sorting DESC LIMIT 1',
|
||||
[$parentUid]
|
||||
);
|
||||
return $uid === false ? $fallback : (int)$uid;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user