Files
VITEC-website/packages/vitec/Classes/Command/BuildContentPagesCommand.php
T
orascheandClaude Fable 5 77661bf0b6 Datapath stories, grid repairs, menu v2 pure, hero placeholders, fec test
Batch of 2026-10-28/29 plus 2026-11-03:

- NEW vitec:import-datapath-stories: 22 story packages from the agency
  dump (docx + Links/ assets) as usecase records with content, quotes,
  images, customer logos, download cards; --repair-quotes normalizes
  attribution lines; asset backfill for existing stories
- /products grid incident repaired (fix_products_grid, fix_graphics_grid,
  check_grid_page): containers unhidden, 46 duplicate cards soft-deleted,
  section order restored; BuildProductPortfolio and
  ImportProductCategoryPages now GUARD against re-runs on pages that
  already carry vitec_cols_% containers
- ' double-encoding fixed: render-pipeline importers write literal
  apostrophes (ENT_NOQUOTES), fix_double_entities literalizes existing rows
- ticket quick wins (ticket_quickwins_20261028), broadcast market page
  cleanup, AttachCardImages market-card handling, SubmarketWorkbook
  META REPAIR, story content/video fixes (fix_story_*)
- ProvideProductCatalog: transitional v1 fields removed (categories
  uid/link, legacyGroupFields, item products teaser) - pure v2 shape
  (ticket 0024-FE); search renderer keeps its teaser
- BuildProductOverviews: heroes get the numbered placeholder background
  (placeholders/pages/<uid>-01.png, wide grey fallback) - backfill on
  existing pages with empty hero_bgimage, attached at creation for new
  pages; existing references always win (ticket 0026-FE); 40 placeholder
  PNGs shipped to fileadmin (not tracked)
- AttachPageMedia: --depth=3 with sysfolders as pass-through parents for
  the overview pages' -08 menu tiles
- NEW migrations/fec_isolation_test.sh: two-user live test proving
  fe_comment ticket isolation (backend verified tight, cross-user
  sighting traced to the FE page-JSON cache, ticket 0027-BE)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-11-03 18:29:56 +01:00

906 lines
74 KiB
PHP

<?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;
/**
* Builds and fills the three content-page branches from the agency
* workbooks (Dropbox delivery 2026-10-23, all "Draft for review"):
*
* Insights (7) /insights + news, events, who-we-are, sustainability,
* careers, awards
* Support (9) /support (NEW branch) + login-to-helpdesk, support-plans,
* training, aetria-on-site-training, vsn-system-training,
* commissioning, supported-devices, materials-compliance
* Contact (5) /contact + contact-form, request-a-demo, locations,
* showrooms
*
* The copy is embedded (fixed approved text, pattern of
* ImportMarketsMainPageCommand); instruction rows of the workbooks
* ("validate against approved form schema" etc.) are deliberately NOT
* imported. Structural moves in the same run:
* - the news plugin moves from /insights onto the new /insights/news
* - page /events moves under /insights (slug + 301 redirect + layout 19)
* - the contact-form container (with the vitec_contactform inside) moves
* from /contact onto the new /contact/contact-form
* - the "Get Support" meta-nav shortcut is repointed to /support
* - three Datapath offices become HIDDEN location records (workbook:
* confirm branding/contacts before publication)
*
* New pages are created HIDDEN; --publish unhides them after review.
* Two DataHandler passes per apply: pages first (link targets need real
* uids), then elements/records/moves.
*
* vendor/bin/typo3 vitec:build-content-pages [--branch=insights|support|contact]
* vendor/bin/typo3 vitec:build-content-pages --apply [--publish]
*/
#[AsCommand(
name: 'vitec:build-content-pages',
description: 'Build and fill the Insights / Support / Contact branches from the agency workbooks (dry-run by default)'
)]
final class BuildContentPagesCommand extends Command
{
private const LOCATION_TABLE = 'tx_vitec_domain_model_location';
/** @var array<string,array<string|int,array<string,mixed>>> */
private array $datamap = [];
/** @var array<string,array<int,array<string,mixed>>> */
private array $cmdmap = [];
/** @var array<string,array<string,int[]>> pageKey => colPos => slot uids */
private array $slotCursor = [];
/** @var array<int,array<int,string[]>> pageUid(or 0 for NEW pages)=>colPos=>ids */
private array $sequence = [];
/** @var array<string,string> plan key => page uid or NEW id */
private array $pageIds = [];
private int $newCounter = 0;
private OutputInterface $output;
protected function configure(): void
{
$this->addOption('apply', null, InputOption::VALUE_NONE, 'Write changes (default: dry run)');
$this->addOption('publish', null, InputOption::VALUE_NONE, 'Unhide the branch pages after filling');
$this->addOption('branch', null, InputOption::VALUE_REQUIRED, 'insights | support | contact (default: all)', '');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
Bootstrap::initializeBackendAuthentication();
$this->output = $output;
$apply = (bool)$input->getOption('apply');
$publish = (bool)$input->getOption('publish');
$branchFilter = (string)$input->getOption('branch');
$branches = $branchFilter === '' ? ['insights', 'support', 'contact'] : [$branchFilter];
$pool = GeneralUtility::makeInstance(ConnectionPool::class);
$pages = $pool->getConnectionForTable('pages');
$insightsUid = $this->pageUid($pages, '/insights');
$contactUid = $this->pageUid($pages, '/contact');
if (!$insightsUid || !$contactUid) {
$output->writeln('<error>/insights or /contact not found.</error>');
return Command::FAILURE;
}
$siteRootPid = (int)$pages->fetchOne('SELECT pid FROM pages WHERE uid = ?', [$insightsUid]);
$output->writeln(($apply ? 'APPLY' : 'DRY-RUN') . sprintf(' · branches: %s · /insights #%d · /contact #%d · site root pid %d',
implode(',', $branches), $insightsUid, $contactUid, $siteRootPid));
// ---- phase A: the page tree -------------------------------------------------
$plan = $this->plan();
$this->pageIds['insights'] = $this->pageIds['insights-root'] = (string)$insightsUid;
$this->pageIds['contact'] = $this->pageIds['contact-root'] = (string)$contactUid;
$supportUid = $this->pageUid($pages, '/support');
$this->pageIds['support'] = $this->pageIds['support-root'] = $supportUid > 0 ? (string)$supportUid : $this->newId();
if ($supportUid === 0 && in_array('support', $branches, true)) {
$this->datamap['pages'][$this->pageIds['support']] = [
'pid' => $siteRootPid, 'title' => 'Support', 'slug' => '/support',
'doktype' => 1, 'hidden' => 1, 'layout' => 11,
];
$output->writeln(' + page /support (NEW branch root, hidden, layout 11 Support Overview)');
}
$eventsUid = $this->pageUid($pages, '/events');
$pageDatamapA = [];
foreach ($plan as $key => $page) {
if (!in_array($page['branch'], $branches, true)) {
continue;
}
if (isset($this->pageIds[$key])) {
continue; // branch roots handled above
}
$parentId = $this->pageIds[$page['branch']];
$slug = '/' . trim((string)$page['slug'], '/');
$fullSlug = ($page['branch'] === 'insights' ? '/insights' : ($page['branch'] === 'contact' ? '/contact' : '/support')) . $slug;
$existing = $this->pageUid($pages, $fullSlug);
if ($existing > 0) {
$this->pageIds[$key] = (string)$existing;
$output->writeln(sprintf(' = page %s exists (#%d)', $fullSlug, $existing));
continue;
}
if ($key === 'insights/events' && $eventsUid > 0) {
// the standalone /events page MOVES under /insights
$this->pageIds[$key] = (string)$eventsUid;
$this->cmdmap['pages'][$eventsUid]['move'] = $insightsUid;
$this->datamap['pages'][$eventsUid]['slug'] = $fullSlug;
$this->datamap['pages'][$eventsUid]['layout'] = (int)$page['layout'];
$this->datamap['pages'][$eventsUid]['title'] = (string)$page['title'];
$this->queueRedirect('/events', $eventsUid);
$output->writeln(sprintf(' ~ page /events #%d: MOVE under /insights -> %s (layout %d, 301 redirect queued)', $eventsUid, $fullSlug, (int)$page['layout']));
continue;
}
$id = $this->newId();
$this->pageIds[$key] = $id;
if (str_starts_with((string)$parentId, 'NEW')) {
$pageDatamapA[$id] = ['pidKey' => $page['branch']];
}
$this->datamap['pages'][$id] = [
'pid' => str_starts_with((string)$parentId, 'NEW') ? 0 : (int)$parentId,
'title' => (string)$page['title'], 'slug' => $fullSlug,
'doktype' => 1, 'hidden' => 1, 'layout' => (int)$page['layout'],
];
$output->writeln(sprintf(' + page %s "%s" (hidden, layout %d)', $fullSlug, (string)$page['title'], (int)$page['layout']));
}
// branch root layout updates
if (in_array('insights', $branches, true)) {
$this->datamap['pages'][$insightsUid]['layout'] = 1;
$output->writeln(' ~ /insights: layout -> 1 (Index Page)');
}
$pagesDatamap = $this->datamap['pages'] ?? [];
$pagesCmdmap = $this->cmdmap['pages'] ?? [];
unset($this->datamap['pages'], $this->cmdmap['pages']);
$pageOrder = [];
foreach ($plan as $key => $page) {
// the branch roots keep their own sorting among the main pages
if (in_array($page['branch'], $branches, true) && !str_ends_with($key, '-root')) {
$pageOrder[$page['branch']][] = $key;
}
}
if ($apply) {
// pass 1a: pages with known pids
$dh = GeneralUtility::makeInstance(DataHandler::class);
$dh->start(['pages' => array_filter($pagesDatamap, fn($f, $k) => !isset($pageDatamapA[$k]), ARRAY_FILTER_USE_BOTH)], []);
$dh->process_datamap();
if ($dh->errorLog !== []) {
$output->writeln('<error>DataHandler pages: ' . implode(' | ', $dh->errorLog) . '</error>');
return Command::FAILURE;
}
foreach ($this->pageIds as $key => $id) {
if (str_starts_with($id, 'NEW') && isset($dh->substNEWwithIDs[$id])) {
$this->pageIds[$key] = (string)(int)$dh->substNEWwithIDs[$id];
}
}
// pass 1b: pages whose parent was itself NEW (support children)
$late = [];
foreach ($pageDatamapA as $id => $info) {
$fields = $pagesDatamap[$id];
$fields['pid'] = (int)$this->pageIds[$info['pidKey']];
$late[$id] = $fields;
}
if ($late !== []) {
$dh2 = GeneralUtility::makeInstance(DataHandler::class);
$dh2->start(['pages' => $late], []);
$dh2->process_datamap();
if ($dh2->errorLog !== []) {
$output->writeln('<error>DataHandler pages(2): ' . implode(' | ', $dh2->errorLog) . '</error>');
return Command::FAILURE;
}
foreach ($this->pageIds as $key => $id) {
if (str_starts_with($id, 'NEW') && isset($dh2->substNEWwithIDs[$id])) {
$this->pageIds[$key] = (string)(int)$dh2->substNEWwithIDs[$id];
}
}
}
// moves (events page) + child ordering
if ($pagesCmdmap !== []) {
$dhm = GeneralUtility::makeInstance(DataHandler::class);
$dhm->start([], ['pages' => $pagesCmdmap]);
$dhm->process_cmdmap();
}
foreach ($pageOrder as $branch => $keys) {
$sorting = 0;
foreach ($keys as $key) {
$uid = (int)($this->pageIds[$key] ?? 0);
if ($uid > 0) {
$sorting += 256;
$pages->update('pages', ['sorting' => $sorting], ['uid' => $uid]);
}
}
}
}
// ---- phase B: content, moves, records, page properties -----------------------
$content = $pool->getConnectionForTable('tt_content');
foreach ($plan as $key => $page) {
if (!in_array($page['branch'], $branches, true)) {
continue;
}
$pageId = $this->pageIds[$key] ?? '';
$pageUid = (int)$pageId;
if (!$apply && $pageUid === 0) {
$output->writeln(sprintf(' · %s: %d element(s) planned (page is new - fills on apply)', $key, count($page['elements'])));
$this->seoDatamapForPlan($key, $page, 0);
continue;
}
foreach ($page['elements'] as $element) {
$ctype = array_shift($element);
$colPos = (int)($element['_colPos'] ?? 0);
unset($element['_colPos']);
$hidden = (int)($element['_hidden'] ?? 0);
unset($element['_hidden']);
$fields = [];
foreach ($element as $field => $value) {
$fields[$field] = is_string($value) ? $this->resolveLink($value) : $value;
}
if ($hidden === 1) {
$fields['hidden'] = 1;
}
$id = $this->slotNext($key, $pageUid, $colPos, $ctype);
foreach ($fields as $field => $value) {
$this->datamap['tt_content'][$id][$field] = $value;
}
if (!$apply) {
$output->writeln(sprintf(' %s %s #%s: %s', str_starts_with($id, 'NEW') ? '+' : '~', $ctype, $id, mb_substr((string)($fields['header'] ?? ($fields['vitec_primary_cta_label'] ?? '')), 0, 60)));
}
}
$this->seoDatamapForPlan($key, $page, $pageUid);
}
// special move 1: news plugin from /insights to /insights/news
if (in_array('insights', $branches, true)) {
$newsTarget = (int)($this->pageIds['insights/news'] ?? 0);
foreach ($content->fetchAllAssociative(
"SELECT uid, CType FROM tt_content WHERE deleted = 0 AND pid = ? AND CType LIKE 'news_%'", [$insightsUid]
) as $row) {
if ($newsTarget > 0) {
$this->cmdmap['tt_content'][(int)$row['uid']]['move'] = $newsTarget;
}
$output->writeln(sprintf(' ~ news plugin #%d (%s): MOVE /insights -> /insights/news', (int)$row['uid'], (string)$row['CType']));
}
}
// special move 2: the contact-form container from /contact to the form page
if (in_array('contact', $branches, true)) {
$formTarget = (int)($this->pageIds['contact/contact-form'] ?? 0);
$containers = $content->fetchAllAssociative(
"SELECT uid FROM tt_content WHERE deleted = 0 AND pid = ? AND CType = 'vitec_container' AND tx_container_parent = 0", [$contactUid]
);
foreach ($containers as $row) {
$containerUid = (int)$row['uid'];
$children = $content->fetchFirstColumn('SELECT uid FROM tt_content WHERE deleted = 0 AND tx_container_parent = ?', [$containerUid]);
if ($formTarget > 0) {
$this->cmdmap['tt_content'][$containerUid]['move'] = $formTarget;
foreach ($children as $childUid) {
$this->cmdmap['tt_content'][(int)$childUid]['move'] = $formTarget;
}
}
$output->writeln(sprintf(' ~ form container #%d (+%d child element(s)): MOVE /contact -> /contact/contact-form', $containerUid, count($children)));
}
}
// hidden Datapath location records
if (in_array('contact', $branches, true)) {
$this->planLocations($pool);
}
// repoint the "Get Support" meta-nav shortcut
if (in_array('support', $branches, true)) {
$nav = $pages->fetchAssociative("SELECT uid, doktype, shortcut FROM pages WHERE deleted = 0 AND title = 'Get Support'");
$supportId = (int)($this->pageIds['support'] ?? 0);
if ($nav !== null && (int)$nav['doktype'] === 4 && $supportId > 0) {
$this->datamap['pages'][(int)$nav['uid']] = ['shortcut' => $supportId, 'shortcut_mode' => 0];
$output->writeln(sprintf(' ~ meta-nav "Get Support" #%d: shortcut -> /support', (int)$nav['uid']));
} else {
$output->writeln(' <comment>! meta-nav "Get Support": ' . ($nav === null ? 'page not found' : 'doktype ' . $nav['doktype'] . ' (no shortcut)') . ' - repoint editorially</comment>');
}
}
if (!$apply) {
$output->writeln("\nDRY RUN - nothing written. Add --apply to write (then --publish to unhide).");
return Command::SUCCESS;
}
$dh = GeneralUtility::makeInstance(DataHandler::class);
$dh->start($this->datamap, []);
$dh->process_datamap();
if ($dh->errorLog !== []) {
$output->writeln('<error>DataHandler content: ' . implode(' | ', $dh->errorLog) . '</error>');
return Command::FAILURE;
}
if ($this->cmdmap !== []) {
$dhc = GeneralUtility::makeInstance(DataHandler::class);
$dhc->start([], $this->cmdmap);
$dhc->process_cmdmap();
if ($dhc->errorLog !== []) {
$output->writeln('<error>DataHandler moves: ' . implode(' | ', $dhc->errorLog) . '</error>');
}
}
$this->applySequence($content, $dh->substNEWwithIDs);
if ($publish) {
foreach ($this->pageIds as $key => $id) {
$pages->update('pages', ['hidden' => 0], ['uid' => (int)$id]);
}
$output->writeln('<info>Pages published (unhidden).</info>');
}
$output->writeln('<info>Done. Flush the frontend cache; review the pages, then re-run with --publish if not done yet.</info>');
return Command::SUCCESS;
}
// ---- the embedded workbook content ------------------------------------------------
/** @return array<string,array{branch:string,slug:string,title:string,layout:int,elements:array<int,array<mixed>>,seoTitle:string,description:string,keywords:string}> */
private function plan(): array
{
$p = [];
// ================= INSIGHTS =================
$p['insights-root'] = ['branch' => 'insights', 'slug' => '', 'title' => 'Insights', 'layout' => 1,
'seoTitle' => 'Insights from VITEC | News, Events & Perspectives',
'description' => 'Explore company news, technical perspectives, event updates and stories from the teams behind VITEC and Datapath video technologies.',
'keywords' => 'VITEC insights, video technology news, events, careers, sustainability, awards',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Insights from VITEC', 'header_layout' => 1,
'bodytext' => $this->p('Explore the ideas, developments, and people shaping professional video technology. Read the latest company news, technical perspectives, event updates, and stories from teams working across video distribution, visualization, and mission-critical operations.'),
'vitec_cta_label' => 'Explore insights'],
['vitec_introparagraph', 'header' => 'Perspectives grounded in real applications',
'bodytext' => $this->p('From complex control rooms to content delivery across distributed locations, video technology has a practical role to play. Our insights bring together engineering knowledge, market developments, and updates from VITEC and Datapath to help you understand what is changing and why it matters.')],
['header', 'header' => 'Explore our insights', 'header_layout' => 2],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'News & Articles', 'bodytext' => $this->p('Read company announcements, product updates, technical articles, and perspectives on the challenges facing our markets.'), 'vitec_primary_cta_label' => 'Read more', 'vitec_primary_cta' => '@insights/news'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Events', 'bodytext' => $this->p('Find opportunities to meet our teams, see demonstrations, and discuss your project requirements.'), 'vitec_primary_cta_label' => 'View events', 'vitec_primary_cta' => '@insights/events'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Who We Are', 'bodytext' => $this->p('Learn about our businesses, engineering expertise, and the people behind our technology.'), 'vitec_primary_cta_label' => 'Learn more', 'vitec_primary_cta' => '@insights/who-we-are'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Sustainability', 'bodytext' => $this->p('Explore our approach to responsible business and the information available about our environmental commitments.'), 'vitec_primary_cta_label' => 'Learn more', 'vitec_primary_cta' => '@insights/sustainability'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Careers', 'bodytext' => $this->p('Discover opportunities to work with the teams designing, developing, and supporting our solutions.'), 'vitec_primary_cta_label' => 'View careers', 'vitec_primary_cta' => '@insights/careers'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Awards', 'bodytext' => $this->p('Explore recognition received by our teams and technologies across professional AV and video processing.'), 'vitec_primary_cta_label' => 'View awards', 'vitec_primary_cta' => '@insights/awards'],
['vitec_ctabanner', '_colPos' => 3, 'header' => 'Have a question for our team?',
'bodytext' => $this->p('Whether you are evaluating a project or looking for technical guidance, our team can help you find the right information and next step.'),
'vitec_primary_cta_label' => 'Contact us', 'vitec_primary_cta' => '@/contact',
'vitec_secondary_cta_label' => 'Explore solutions', 'vitec_secondary_cta' => '@/solutions',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['insights/news'] = ['branch' => 'insights', 'slug' => 'news', 'title' => 'News & Articles', 'layout' => 12,
'seoTitle' => 'VITEC News and Articles',
'description' => 'Follow the latest announcements, product developments, engineering perspectives, and company news from VITEC and Datapath.',
'keywords' => 'VITEC news, press releases, product news, video technology articles',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'VITEC News and Articles', 'header_layout' => 1,
'bodytext' => $this->p('Follow the latest announcements, product developments, engineering perspectives, and company news from VITEC and Datapath. Browse articles by topic to find information relevant to your market, technology, or project.'),
'vitec_cta_label' => 'Browse latest news'],
// the moved news plugin lands here (listing, featured + filters are plugin features)
['vitec_ctabanner', 'header' => 'Looking for more technical insight?',
'bodytext' => $this->p('Explore the solutions behind the stories or speak with our team about how VITEC technologies can support your application.'),
'vitec_primary_cta_label' => 'Explore solutions', 'vitec_primary_cta' => '@/solutions',
'vitec_secondary_cta_label' => 'Contact us', 'vitec_secondary_cta' => '@/contact',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['insights/events'] = ['branch' => 'insights', 'slug' => 'events', 'title' => 'Events', 'layout' => 19,
'seoTitle' => 'Meet VITEC at Industry Events',
'description' => 'See where VITEC and Datapath teams will be demonstrating video technologies, sharing expertise, and meeting customers and partners.',
'keywords' => 'VITEC events, trade shows, exhibitions, product demonstrations',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Meet VITEC at Industry Events', 'header_layout' => 1,
'bodytext' => $this->p('See where VITEC and Datapath teams will be demonstrating video technologies, sharing expertise, and meeting customers and partners. Explore upcoming events and find opportunities to discuss your requirements in person.'),
'vitec_cta_label' => 'View upcoming events'],
['vitec_introparagraph', 'header' => 'See our technology in context',
'bodytext' => $this->p('Trade shows and industry events offer a chance to discuss real deployment challenges with the people who design and support our solutions. Find event details, locations, and information on how to connect with the team.')],
// slots the EXISTING vitec_eventlist into the sequence (page move keeps it)
['vitec_eventlist'],
['vitec_ctabanner', 'header' => 'Arrange a conversation with our team',
'bodytext' => $this->p('If you cannot meet us at an event, we can help you discuss your application and the technologies that may fit your requirements.'),
'vitec_primary_cta_label' => 'Contact us', 'vitec_primary_cta' => '@/contact',
'vitec_secondary_cta_label' => 'Explore markets', 'vitec_secondary_cta' => '@/markets',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['insights/who-we-are'] = ['branch' => 'insights', 'slug' => 'who-we-are', 'title' => 'Who We Are', 'layout' => 16,
'seoTitle' => 'Who We Are | VITEC and Datapath',
'description' => 'VITEC develops professional video technologies. Together with Datapath, our capabilities span video streaming, IPTV, digital signage, video processing, and visualization.',
'keywords' => 'about VITEC, Datapath, video technology company, engineering',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Engineering Video Solutions for Real-World Challenges', 'header_layout' => 1,
'bodytext' => $this->p('VITEC develops professional video technologies that help organizations distribute information, support operations, and communicate clearly. Together with Datapath, our capabilities span video streaming, IPTV, digital signage, video processing, and visualization for demanding environments.'),
'vitec_cta_label' => 'Explore our solutions', 'vitec_cta' => '@/solutions'],
['vitec_introparagraph', 'header' => 'Video expertise built around customer needs',
'bodytext' => $this->p('Our technologies are used wherever people need timely access to visual information, from enterprise communications and public venues to broadcast operations and control rooms. We work with customers and partners to address the practical requirements of deployment, management, and day-to-day operation.')],
['vitec_introparagraph', 'header' => 'VITEC and Datapath',
'bodytext' => $this->p('VITEC\'s experience in video encoding, streaming, IPTV, and content delivery complements Datapath\'s expertise in video processing, video walls, AV-over-IP, and control room systems. Datapath joined VITEC in 2025, bringing complementary engineering capabilities into a broader video technology portfolio.')],
['vitec_introparagraph', 'header' => 'Technology for operational confidence',
'bodytext' => $this->p('Reliable visual information matters when teams are coordinating activity, monitoring systems, or communicating across sites. Our approach focuses on helping people access the content and data they need through solutions designed for their operating environment.')],
['vitec_introparagraph', 'header' => 'Working with customers and partners',
'bodytext' => $this->p('Projects differ in their infrastructure, workflows, and support needs. Our teams work with integrators, partners, and end users to understand those requirements and identify appropriate technology and deployment approaches.')],
['vitec_ctabanner', 'header' => 'Talk to the people behind the technology',
'bodytext' => $this->p('Tell us about your application and we will help connect you with the right product and solution expertise.'),
'vitec_primary_cta_label' => 'Contact us', 'vitec_primary_cta' => '@/contact',
'vitec_secondary_cta_label' => 'View our markets', 'vitec_secondary_cta' => '@/markets',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['insights/sustainability'] = ['branch' => 'insights', 'slug' => 'sustainability', 'title' => 'Sustainability', 'layout' => 16,
'seoTitle' => 'Our Approach to Responsible Business | VITEC',
'description' => 'Explore the policies, initiatives, and commitments published by VITEC and Datapath on environmental and social responsibility.',
'keywords' => 'VITEC sustainability, responsible business, environmental policy, compliance',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Our Approach to Responsible Business', 'header_layout' => 1,
'bodytext' => $this->p('Responsible business decisions involve how technology is designed, how resources are used, and how people are treated. Explore the policies, initiatives, and commitments published by VITEC and Datapath, alongside the work still to be developed across the wider business.'),
'vitec_cta_label' => 'Explore our approach'],
['vitec_introparagraph', 'header' => 'Practical responsibility across our operations',
'bodytext' => $this->p('Environmental and social responsibilities need clear ownership and evidence. This page brings together the company\'s approved statements and supporting documents.')],
['vitec_introparagraph', 'header' => 'Environmental considerations', '_hidden' => 1,
'bodytext' => $this->p('EDITORIAL: verified VITEC and Datapath information on energy use, product design, waste, packaging, and other relevant environmental practices. Specific targets, certifications, and performance figures require approved sources before publication.')],
['vitec_introparagraph', 'header' => 'People and communities', '_hidden' => 1,
'bodytext' => $this->p('EDITORIAL: approved information about employee wellbeing, workplace practices, community involvement, and responsible relationships with partners and suppliers.')],
['vitec_introparagraph', 'header' => 'Policies and supporting information',
'bodytext' => $this->p('Links to current, approved environmental, compliance, and corporate policy documents are provided where available, identifying the issuing legal entity and publication date for each document.')],
['vitec_ctabanner', 'header' => 'Need information about our policies?',
'bodytext' => $this->p('Our team can direct customers and partners to relevant published documentation and help with specific questions about company policies or product compliance.'),
'vitec_primary_cta_label' => 'Contact us', 'vitec_primary_cta' => '@/contact',
'vitec_secondary_cta_label' => 'Explore products', 'vitec_secondary_cta' => '@/products',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['insights/careers'] = ['branch' => 'insights', 'slug' => 'careers', 'title' => 'Careers', 'layout' => 16,
'seoTitle' => 'Build Your Career with VITEC',
'description' => 'Explore opportunities to work with teams developing and supporting professional video technology across engineering, product, commercial, and support roles.',
'keywords' => 'VITEC careers, jobs, vacancies, video technology careers, Datapath careers',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Build Your Career with VITEC', 'header_layout' => 1,
'bodytext' => $this->p('Explore opportunities to work with teams developing and supporting professional video technology. Our work brings together engineering, product development, commercial expertise, and customer support across a range of markets and applications.'),
'vitec_cta_label' => 'View open roles'],
['vitec_introparagraph', 'header' => 'Work that connects technology with real needs',
'bodytext' => $this->p('From product design and software engineering to operations and customer relationships, our teams help organizations make better use of video and visual information. We value practical thinking, collaboration, and attention to quality.')],
['vitec_introparagraph', 'header' => 'Different skills, shared goals',
'bodytext' => $this->p('Our work spans hardware, software, video processing, streaming, and solution delivery. Career opportunities depend on current vacancies and location.')],
['vitec_introparagraph', 'header' => 'Life at VITEC and Datapath', '_hidden' => 1,
'bodytext' => $this->p('EDITORIAL: verified employee perspectives and workplace information; confirm which details apply to each employing entity before reusing them.')],
['header', 'header' => 'Current opportunities', 'header_layout' => 2],
['vitec_introparagraph', '_hidden' => 1, 'header' => 'Vacancy listing (pending)',
'bodytext' => $this->p('EDITORIAL: dynamic vacancy listings (job title, location, employment type, application link) need a feed or plugin - link applicants to the official recruitment system; show a no-open-roles message when empty.')],
['vitec_ctabanner', 'header' => 'Interested in joining the team?',
'bodytext' => $this->p('Review our current vacancies to see where your skills and experience could contribute. If there are no suitable openings today, check back for future opportunities.'),
'vitec_primary_cta_label' => 'View vacancies',
'vitec_secondary_cta_label' => 'Who we are', 'vitec_secondary_cta' => '@insights/who-we-are',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['insights/awards'] = ['branch' => 'insights', 'slug' => 'awards', 'title' => 'Awards', 'layout' => 16,
'seoTitle' => 'Awards and Recognition | VITEC',
'description' => 'Industry awards and nominations recognizing VITEC and Datapath teams and technology, attributed to the relevant product, team, and year.',
'keywords' => 'VITEC awards, Datapath awards, AV industry recognition, InAVation, InfoComm',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Recognition for Our Teams and Technology', 'header_layout' => 1,
'bodytext' => $this->p('Industry awards and nominations recognize the work of the engineers, designers, and specialists behind our products. Explore selected recognition from VITEC and Datapath, with each achievement attributed to the relevant product, team, and year.'),
'vitec_cta_label' => 'Explore recognition'],
['vitec_introparagraph', 'header' => 'A record of engineering achievement',
'bodytext' => $this->p('Recognition is most meaningful when it is specific. This page brings together verified awards and nominations and explains what was recognized.')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => '2022 Inavation Awards', 'bodytext' => $this->p('Aetria: Control category winner (Datapath).')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => '2021 InfoComm Best of Show', 'bodytext' => $this->p('WallControl 10 (Datapath).')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => '2020 DIGI Awards', 'bodytext' => $this->p('Hx4: Best Digital Signage Product (Datapath).')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => '2019 SCN Installation Product Award', 'bodytext' => $this->p('VSN1192 (Datapath).')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => '2018 InAVation Awards', 'bodytext' => $this->p('Fx4 winner (Datapath).')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => '2011 Queen\'s Award for Enterprise: Innovation', 'bodytext' => $this->p('Datapath (historic company recognition).')],
['vitec_ctabanner', 'header' => 'Explore the technology behind the recognition',
'bodytext' => $this->p('Learn more about the video technologies and solutions developed for customers working across demanding operational and communications environments.'),
'vitec_primary_cta_label' => 'Explore products', 'vitec_primary_cta' => '@/products',
'vitec_secondary_cta_label' => 'Contact us', 'vitec_secondary_cta' => '@/contact',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
// ================= SUPPORT =================
$p['support-root'] = ['branch' => 'support', 'slug' => '', 'title' => 'Support', 'layout' => 11,
'seoTitle' => 'VITEC Support',
'description' => 'Find the help you need to install, operate, and maintain VITEC solutions: technical assistance, support plans, training, product resources, and compliance requests.',
'keywords' => 'VITEC support, helpdesk, support plans, training, compliance',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'VITEC Support', 'header_layout' => 1,
'bodytext' => $this->p('Find the help you need to install, operate, and maintain VITEC solutions. Access technical assistance, support plans, training, product resources, and compliance requests from one place.'),
'vitec_cta_label' => 'Log in to HelpDesk', 'vitec_cta' => 'https://helpdesk.vitec.com'],
['header', 'header' => 'How can we help?', 'header_layout' => 2],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'HelpDesk', 'bodytext' => $this->p('Log in to the VITEC Customer Support HelpDesk to access technical assistance and available support resources.'), 'vitec_primary_cta_label' => 'Log in', 'vitec_primary_cta' => '@support/login-to-helpdesk'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Request access', 'bodytext' => $this->p('Need a HelpDesk account? Submit a request with your contact details and product information.'), 'vitec_primary_cta_label' => 'Request access', 'vitec_primary_cta' => '@support/login-to-helpdesk'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Support plans', 'bodytext' => $this->p('Review maintenance and support plans covering technical assistance, updates, and hardware services.'), 'vitec_primary_cta_label' => 'View plans', 'vitec_primary_cta' => '@support/support-plans'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Training', 'bodytext' => $this->p('Find training for VITEC IP video platforms and Datapath control room and video wall systems.'), 'vitec_primary_cta_label' => 'Explore training', 'vitec_primary_cta' => '@support/training'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Supported devices', 'bodytext' => $this->p('Check product documentation and compatibility information before planning an installation or upgrade.'), 'vitec_primary_cta_label' => 'Learn more', 'vitec_primary_cta' => '@support/supported-devices'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Material compliance', 'bodytext' => $this->p('Request information on product material compliance and environmental requirements.'), 'vitec_primary_cta_label' => 'Request information', 'vitec_primary_cta' => '@support/materials-compliance'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Resources', 'bodytext' => $this->p('Find product datasheets, drivers, brochures, and technical documentation.'), 'vitec_primary_cta_label' => 'Browse resources', 'vitec_primary_cta' => '@/datasheets'],
['vitec_ctabanner', 'header' => 'Need help with a VITEC product?',
'vitec_primary_cta_label' => 'Contact support', 'vitec_primary_cta' => '@/contact',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['support/login-to-helpdesk'] = ['branch' => 'support', 'slug' => 'login-to-helpdesk', 'title' => 'Log in to HelpDesk', 'layout' => 16,
'seoTitle' => 'Log in to VITEC HelpDesk',
'description' => 'Access the VITEC Customer Support HelpDesk for technical assistance and available product resources, or request HelpDesk access.',
'keywords' => 'VITEC helpdesk, technical support login, request access',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Log in to VITEC HelpDesk', 'header_layout' => 1,
'bodytext' => $this->p('Access the VITEC Customer Support HelpDesk for technical assistance and available product resources. If you do not have an account, request access before signing in.'),
'vitec_cta_label' => 'Log in to HelpDesk', 'vitec_cta' => 'https://helpdesk.vitec.com'],
['header', 'header' => 'Technical support and resources', 'header_layout' => 2],
['vitec_introparagraph',
'bodytext' => $this->p('Use the HelpDesk for support inquiries and to access resources available to your account. Support eligibility and response arrangements depend on your applicable agreement.')],
['vitec_helpdeskform', 'header' => 'Request HelpDesk access'],
['vitec_ctabanner', 'header' => 'Not sure which plan covers you?',
'vitec_primary_cta_label' => 'View support plans', 'vitec_primary_cta' => '@support/support-plans',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['support/support-plans'] = ['branch' => 'support', 'slug' => 'support-plans', 'title' => 'Support Plans', 'layout' => 16,
'seoTitle' => 'VITEC Maintenance and Support Plans',
'description' => 'Compare VITEC Silver, Gold, and Platinum support plans covering hardware services, software and firmware updates, training, and technical assistance.',
'keywords' => 'VITEC support plans, maintenance, Silver Gold Platinum, SLA',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'VITEC Maintenance and Support Plans', 'header_layout' => 1,
'bodytext' => $this->p('Choose the level of support that fits your deployment. VITEC offers Silver, Gold, and Platinum plans with differing access to hardware services, software and firmware updates, training, and technical assistance.'),
'vitec_cta_label' => 'Contact VITEC', 'vitec_cta' => '@/contact'],
['header', 'header' => 'Compare support plan features', 'header_layout' => 2],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Silver', 'bodytext' => $this->p('Hardware repair and critical security updates; discounted minor and major software or firmware updates; standard phone support during local business hours; HelpDesk response within one business day; remote troubleshooting.')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Gold', 'bodytext' => $this->p('Includes hardware repair, critical security updates, minor updates, discounted major updates, eligible advanced replacement shipping, training benefits, and an emergency hotline with a four-hour response commitment.')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Platinum', 'bodytext' => $this->p('Includes hardware repair, critical security updates, minor and major updates, eligible advanced replacement shipping, enhanced training benefits, and an emergency hotline with a two-hour response commitment.')],
['vitec_ctabanner', 'header' => 'Support plan documents',
'bodytext' => $this->p('Review the current Support Plans Datasheet and Brochure for plan details, conditions, and eligibility.'),
'vitec_primary_cta_label' => 'Download datasheet',
'vitec_secondary_cta_label' => 'Contact support', 'vitec_secondary_cta' => '@/contact',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['support/training'] = ['branch' => 'support', 'slug' => 'training', 'title' => 'Training', 'layout' => 16,
'seoTitle' => 'Technical Training for VITEC Systems',
'description' => 'Build the practical skills to specify, configure, operate, and maintain professional video and control room systems with VITEC and Datapath training.',
'keywords' => 'VITEC training, Datapath training, Aetria, VSN, Avedia, IP video course',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Technical Training for VITEC Systems', 'header_layout' => 1,
'bodytext' => $this->p('Build the practical skills to specify, configure, operate, and maintain professional video and control room systems. Explore VITEC IP video training and Datapath courses for Aetria and VSN platforms.'),
'vitec_cta_label' => 'Ask about training', 'vitec_cta' => '@/contact'],
['vitec_introparagraph', 'header' => 'Practical training for real installations',
'bodytext' => $this->p('Training options cover system design, installation, configuration, operation, and troubleshooting. Course format and availability depend on the product and program.')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Avedia IP Video Essentials', 'bodytext' => $this->p('A two-day course covering IP video fundamentals and hands-on configuration of the Avedia IPTV and digital signage platform.')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Aetria On-site Training', 'bodytext' => $this->p('A two-day practical course for people designing and delivering Aetria control room solutions.'), 'vitec_primary_cta_label' => 'Course details', 'vitec_primary_cta' => '@support/aetria-on-site-training'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'VSN System Training', 'bodytext' => $this->p('Training covering VSN system installation, video wall configuration, software operation, and maintenance.'), 'vitec_primary_cta_label' => 'Course details', 'vitec_primary_cta' => '@support/vsn-system-training'],
['vitec_ctabanner', 'header' => 'Discuss training for your team',
'vitec_primary_cta_label' => 'Contact us', 'vitec_primary_cta' => '@/contact',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['support/aetria-on-site-training'] = ['branch' => 'support', 'slug' => 'aetria-on-site-training', 'title' => 'Aetria On-site Training', 'layout' => 16,
'seoTitle' => 'Aetria On-site Training | VITEC',
'description' => 'A two-day practical course on designing, configuring, managing, and operating Aetria control room solutions. 6 CTS renewal units.',
'keywords' => 'Aetria training, control room training, Datapath course, CTS renewal units',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Aetria On-site Training', 'header_layout' => 1,
'bodytext' => $this->p('Learn to design, configure, manage, and operate Aetria control room solutions through practical instruction covering the hardware and software components of the system.')],
['vitec_introparagraph', 'header' => 'Course details',
'bodytext' => $this->p('Duration: 2 days. CTS Renewal Points: 6 RUs (CTS or CTS-D).')],
['vitec_introparagraph', 'header' => 'What you will learn',
'bodytext' => $this->ul(['Identify the components needed to design an Aetria solution around project requirements.', 'Set up and configure Aetria components for operation.', 'Operate Aetria video walls and workstations.', 'Present solution options and their practical benefits to end users.'])],
['vitec_introparagraph', 'header' => 'Who should attend?',
'bodytext' => $this->p('Designed for technical sales representatives, presales engineers, system designers, and consultants involved in control room projects.')],
['vitec_introparagraph', 'header' => 'Course outline',
'bodytext' => $this->ul(['Day 1: Introduction to Aetria, hardware and software components, Aetria Command Center, and workstation options.', 'Day 2: Practical Aetria deployment scenarios, project requirements, and assessments.'])],
['vitec_ctabanner', 'header' => 'Ask about course dates',
'vitec_primary_cta_label' => 'Contact us', 'vitec_primary_cta' => '@/contact',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['support/vsn-system-training'] = ['branch' => 'support', 'slug' => 'vsn-system-training', 'title' => 'VSN System Training', 'layout' => 16,
'seoTitle' => 'VSN System Training | VITEC',
'description' => 'Develop the skills to install, configure, and manage Datapath VSN video wall systems, tailored to your system components and operational requirements.',
'keywords' => 'VSN training, video wall training, Datapath VSN, WallControl',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'VSN System Training', 'header_layout' => 1,
'bodytext' => $this->p('Develop the skills to install, configure, and manage Datapath VSN video wall systems. Training can be tailored to the system components and operational requirements of your project.')],
['vitec_introparagraph', 'header' => 'Build confidence with VSN systems',
'bodytext' => $this->p('Datapath training covers the practical steps involved in video wall setup, configuration, and maintenance, with hands-on guidance from experienced specialists.')],
['vitec_introparagraph', 'header' => 'Training topics',
'bodytext' => $this->ul(['Introduction to VSN systems, capture cards, and graphics cards.', 'System installation and configuration.', 'Video wall setup and management.', 'System diagnostic tools and troubleshooting.', 'WallControl software operation where relevant to the installation.'])],
['vitec_introparagraph', 'header' => 'Training built around your needs',
'bodytext' => $this->p('Discuss introductory, advanced, or tailored training with the Datapath team. Confirm course format, duration, and current availability when booking.')],
['vitec_ctabanner', 'header' => 'Enquire about VSN training',
'vitec_primary_cta_label' => 'Contact us', 'vitec_primary_cta' => '@/contact',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['support/commissioning'] = ['branch' => 'support', 'slug' => 'commissioning', 'title' => 'Commissioning', 'layout' => 16,
'seoTitle' => 'Commissioning Services for Video Wall Systems | VITEC',
'description' => 'Practical assistance with installing and configuring Datapath video wall controllers and Aetria systems for integrators and suppliers.',
'keywords' => 'commissioning, video wall installation, Datapath, Aetria setup',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Commissioning Services for Video Wall Systems', 'header_layout' => 1,
'bodytext' => $this->p('Get practical assistance with installing and configuring Datapath video wall controllers and Aetria systems. Datapath commissioning services help integrators and suppliers prepare systems for operation.'),
'vitec_cta_label' => 'Discuss commissioning', 'vitec_cta' => '@/contact'],
['vitec_introparagraph', 'header' => 'What commissioning covers',
'bodytext' => $this->ul(['Installation of Datapath graphics and capture card drivers.', 'Video wall configuration using Datapath software.', 'Capture card configuration using Datapath software.', 'Installation of WallControl and Aetria software, where applicable.', 'Initial setup and configuration of installed Datapath software.'])],
['vitec_introparagraph', 'header' => 'Handover and training',
'bodytext' => $this->p('Training on the installed components may be included as defined in the signed statement of work, helping teams maintain and operate their systems. The final scope and responsibilities follow the agreed Commissioning Service Statement and project statement of work.')],
['vitec_ctabanner', 'header' => 'Plan your commissioning',
'vitec_primary_cta_label' => 'Contact the team', 'vitec_primary_cta' => '@/contact',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['support/supported-devices'] = ['branch' => 'support', 'slug' => 'supported-devices', 'title' => 'Supported Devices', 'layout' => 16,
'seoTitle' => 'Supported Devices and Compatibility | VITEC',
'description' => 'Find the information you need to check device compatibility with VITEC platforms across IPTV, signage, device management, software, and firmware.',
'keywords' => 'supported devices, compatibility, Site Manager, firmware, IPTV players',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Supported Devices and Compatibility', 'header_layout' => 1,
'bodytext' => $this->p('Find the information you need to check device compatibility with VITEC platforms. Supported features vary by product family, software version, and device model.'),
'vitec_cta_label' => 'View product resources', 'vitec_cta' => '@/datasheets'],
['vitec_introparagraph', 'header' => 'Check compatibility before deployment',
'bodytext' => $this->p('Consult the current product documentation and device support matrices to confirm compatibility for your specific platform, model, and firmware version.')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'IPTV and signage', 'bodytext' => $this->p('Review platform documentation for compatible players, displays, and supported control functions.')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Device management', 'bodytext' => $this->p('Check Site Manager documentation for supported VITEC devices and available management functions.')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Software and firmware', 'bodytext' => $this->p('Confirm that the versions deployed match the compatibility guidance for your environment.')],
['vitec_ctabanner', 'header' => 'Questions about compatibility?',
'vitec_primary_cta_label' => 'Contact technical support', 'vitec_primary_cta' => '@/contact',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['support/materials-compliance'] = ['branch' => 'support', 'slug' => 'materials-compliance', 'title' => 'Materials Compliance', 'layout' => 16,
'seoTitle' => 'Material Compliance and Environmental Information | VITEC',
'description' => 'Request information about material compliance and environmental requirements for VITEC products, including REACH, RoHS, and conflict minerals.',
'keywords' => 'material compliance, REACH, RoHS, conflict minerals, environmental documentation',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Material Compliance and Environmental Information', 'header_layout' => 1,
'bodytext' => $this->p('Request information about material compliance and environmental requirements for VITEC products. Our team can help route inquiries relating to relevant product documentation and declarations.'),
'vitec_cta_label' => 'Request compliance information', 'vitec_cta' => '@/contact'],
['vitec_introparagraph', 'header' => 'What information do you need?',
'bodytext' => $this->p('Use the compliance request route for inquiries concerning REACH, RoHS, conflict minerals, or other product-specific environmental and material documentation.')],
['vitec_ctabanner', 'header' => 'Material compliance request',
'bodytext' => $this->p('A dedicated compliance request form is being prepared; until then, please use the general contact route and name the product and the documentation you need.'),
'vitec_primary_cta_label' => 'Contact VITEC', 'vitec_primary_cta' => '@/contact',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
// ================= CONTACT =================
$p['contact-root'] = ['branch' => 'contact', 'slug' => '', 'title' => 'Contact', 'layout' => 20,
'seoTitle' => 'Contact VITEC',
'description' => 'Get in touch with VITEC for product information, sales enquiries, demonstrations, or help finding the right team.',
'keywords' => 'contact VITEC, sales enquiry, request demo, technical support, locations',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Contact VITEC', 'header_layout' => 1,
'bodytext' => $this->p('Get in touch with VITEC for product information, sales enquiries, demonstrations, or help finding the right team. Choose the most relevant contact route below.'),
'vitec_cta_label' => 'Contact VITEC', 'vitec_cta' => '@contact/contact-form'],
['header', 'header' => 'How can we help?', 'header_layout' => 2],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'General enquiries', 'bodytext' => $this->p('Ask about VITEC products, solutions, and services. Use the general contact form to share your requirements with our team.'), 'vitec_primary_cta_label' => 'Contact form', 'vitec_primary_cta' => '@contact/contact-form'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Request a demo', 'bodytext' => $this->p('Discuss a product demonstration with VITEC. Tell us about your environment, requirements, and the technology you would like to see.'), 'vitec_primary_cta_label' => 'Request a demo', 'vitec_primary_cta' => '@contact/request-a-demo'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Technical support', 'bodytext' => $this->p('Existing customers can access technical assistance through the VITEC HelpDesk. If you need an account, request HelpDesk access.'), 'vitec_primary_cta_label' => 'Go to support', 'vitec_primary_cta' => '@support/login-to-helpdesk'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Material compliance', 'bodytext' => $this->p('Request product material compliance or environmental information, including REACH, RoHS, and conflict minerals.'), 'vitec_primary_cta_label' => 'Learn more', 'vitec_primary_cta' => '@support/materials-compliance'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Locations', 'bodytext' => $this->p('Find VITEC office addresses and regional contact details.'), 'vitec_primary_cta_label' => 'View locations', 'vitec_primary_cta' => '@contact/locations'],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Showrooms', 'bodytext' => $this->p('Explore demonstration opportunities and contact VITEC to discuss an appropriate visit.'), 'vitec_primary_cta_label' => 'Explore showrooms', 'vitec_primary_cta' => '@contact/showrooms'],
['vitec_ctabanner', 'header' => 'Find the right VITEC contact',
'bodytext' => $this->p('For general enquiries, use our contact form. For technical assistance, please use the dedicated support routes so your request reaches the right team.'),
'vitec_primary_cta_label' => 'Contact VITEC', 'vitec_primary_cta' => '@contact/contact-form',
'vitec_secondary_cta_label' => 'Explore Support', 'vitec_secondary_cta' => '@support-root',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['contact/contact-form'] = ['branch' => 'contact', 'slug' => 'contact-form', 'title' => 'Contact Form', 'layout' => 20,
'seoTitle' => 'Contact VITEC | General Enquiries',
'description' => 'Complete the form to request general information about VITEC products and solutions. A VITEC representative will contact you soon.',
'keywords' => 'VITEC contact form, general enquiry, product information request',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Contact VITEC', 'header_layout' => 1,
'bodytext' => $this->p('Please complete the form to request general information about VITEC products and solutions. For technical support, log in to the VITEC HelpDesk or request access. For material compliance or environmental information, use the dedicated request form.')],
// the existing form container (vitec_contactform inside) moves onto this page
]];
$p['contact/request-a-demo'] = ['branch' => 'contact', 'slug' => 'request-a-demo', 'title' => 'Request a Demo', 'layout' => 20,
'seoTitle' => 'Request a VITEC Demo',
'description' => 'Tell us about your requirements and the VITEC solutions you would like to explore. We will follow up about a suitable demonstration.',
'keywords' => 'request demo, VITEC demonstration, product demo',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'Request a VITEC Demo', 'header_layout' => 1,
'bodytext' => $this->p('Tell us about your requirements and the VITEC solutions you would like to explore. We will use your information to follow up about a suitable demonstration.')],
['vitec_demoform', 'header' => 'Request a demo'],
['vitec_introparagraph',
'bodytext' => $this->p('Looking for technical assistance? Visit VITEC Support.'),
'vitec_button_text' => 'VITEC Support', 'vitec_button_link' => '@support-root'],
]];
$p['contact/locations'] = ['branch' => 'contact', 'slug' => 'locations', 'title' => 'Locations', 'layout' => 16,
'seoTitle' => 'VITEC Locations',
'description' => 'VITEC has offices across the Americas, Europe, the Middle East, Africa, and Asia Pacific. Find office addresses and regional contact details.',
'keywords' => 'VITEC offices, locations, regional contacts, addresses',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'VITEC Locations', 'header_layout' => 1,
'bodytext' => $this->p('VITEC has offices across the Americas, Europe, the Middle East, Africa, and Asia Pacific. This directory brings together existing VITEC offices and additional locations listed by Datapath following its acquisition.')],
['header', 'header' => 'Our offices around the world', 'header_layout' => 2],
['vitec_locationlist'],
['vitec_ctabanner', 'header' => 'Need help finding the right office?',
'bodytext' => $this->p('Use the VITEC contact form and tell us where you are based and what you need. We can direct your enquiry to the appropriate team.'),
'vitec_primary_cta_label' => 'Contact VITEC', 'vitec_primary_cta' => '@contact/contact-form',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
$p['contact/showrooms'] = ['branch' => 'contact', 'slug' => 'showrooms', 'title' => 'Showrooms', 'layout' => 16,
'seoTitle' => 'VITEC Showrooms | See Solutions in Action',
'description' => 'Explore VITEC solutions in person through the Aetria showroom network. Arrange a tailored demonstration at a participating venue or online.',
'keywords' => 'VITEC showrooms, Aetria showroom, demonstration, control room demo',
'elements' => [
['vitec_herosection', '_colPos' => 1, 'header' => 'See VITEC Solutions in Action', 'header_layout' => 1,
'bodytext' => $this->p('Explore VITEC solutions in person through the established Aetria showroom network, now part of the wider VITEC portfolio. Arrange a tailored demonstration of control room visualization, video wall management, and AV-over-IP workflows at a participating venue.')],
['vitec_introparagraph', 'header' => 'Explore our global demonstration network',
'bodytext' => $this->p('The network includes dedicated demonstration facilities and partner-hosted venues across the Americas, Asia Pacific, Europe, and the Middle East. Visits are free and by appointment only. Availability and demonstration scope depend on the venue.')],
['header', 'header' => 'What would you like to see?', 'header_layout' => 2],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'IPTV and video distribution', 'bodytext' => $this->p('Discuss video distribution, playback, and management across connected displays and devices.')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Digital signage', 'bodytext' => $this->p('Explore how signage and content delivery can support communications in your organization.')],
['vitec_card', 'vitec_layout_variant' => 'text_only', 'header' => 'Video walls and control rooms', 'bodytext' => $this->p('Discuss multi-source visualization and operator workflows for monitoring and control environments.')],
['vitec_introparagraph', 'header' => 'Showroom locations',
'bodytext' => '<p><strong>Americas:</strong></p>' . $this->ul(['Atlanta, USA', 'Philadelphia, USA', 'Providence, Rhode Island, USA', 'Lima, Peru'])
. '<p><strong>Asia Pacific:</strong></p>' . $this->ul(['Tokyo, Japan', 'Bengaluru, India (Mindstec)', 'Sydney, Australia (Midwich)'])
. '<p><strong>Europe:</strong></p>' . $this->ul(['Derby, UK - Aetria demonstration and training facility', 'UK (Midwich)', 'UK (onemedia)', 'Germany (Videlco)', 'France (LG Electronics)', 'Portugal (Subvertice)', 'Turkey (EgeRate Elektronik)'])
. '<p><strong>Middle East:</strong></p>' . $this->ul(['Dubai, UAE (Mindstec)', 'Saudi Arabia (Mindstec)'])
. $this->p('All showroom visits are by appointment. Online demonstrations are also available.')],
['vitec_ctabanner', 'header' => 'Arrange a VITEC demonstration',
'bodytext' => $this->p('Tell us what you want to evaluate and the team will help identify an appropriate next step.'),
'vitec_primary_cta_label' => 'Request a demo', 'vitec_primary_cta' => '@contact/request-a-demo',
'vitec_secondary_cta_label' => 'Contact VITEC', 'vitec_secondary_cta' => '@contact/contact-form',
'vitec_layout_variant' => 'centered', 'vitec_background_variant' => 'orange'],
]];
return $p;
}
/** The three Datapath offices, HIDDEN until confirmed (workbook QA note). */
private function planLocations(ConnectionPool $pool): void
{
$conn = $pool->getConnectionForTable(self::LOCATION_TABLE);
$rows = $conn->fetchAllAssociative('SELECT uid, pid, title FROM ' . self::LOCATION_TABLE . ' WHERE deleted = 0');
$pidCounts = array_count_values(array_map(static fn(array $r): int => (int)$r['pid'], $rows));
arsort($pidCounts);
$storagePid = (int)(array_key_first($pidCounts) ?? 0);
$existingTitles = array_map(static fn(array $r): string => mb_strtolower((string)$r['title']), $rows);
$locations = [
['title' => 'United Kingdom - Derby (Datapath)', 'country_code' => 'GB', 'street' => 'Bemrose House, Bemrose Park, Wayzgoose Drive', 'postal_code' => 'DE21 6XQ', 'city' => 'Derby', 'country' => 'United Kingdom', 'phone' => '+44 (0)1332 294 441', 'email' => 'sales-uk@datapath.co.uk'],
['title' => 'USA - Norristown, Pennsylvania (Datapath)', 'country_code' => 'US', 'street' => '2490 General Armistead Avenue, Suite 102', 'postal_code' => '19403', 'city' => 'Norristown, PA', 'country' => 'USA', 'phone' => '+1 484 679 1553', 'email' => 'sales-us@datapath.co.uk'],
['title' => 'Japan - Tokyo (Datapath)', 'country_code' => 'JP', 'street' => 'Axon Hamamatsucho, 1-1-23 Shibadaimon, Minato-ku', 'postal_code' => '105-0012', 'city' => 'Tokyo', 'country' => 'Japan', 'email' => 'sales-jp@datapath.co.uk'],
];
foreach ($locations as $location) {
if (in_array(mb_strtolower($location['title']), $existingTitles, true)) {
$this->output->writeln(' = location record "' . $location['title'] . '" exists');
continue;
}
$this->datamap[self::LOCATION_TABLE][$this->newId()] = $location + ['pid' => $storagePid, 'hidden' => 1];
$this->output->writeln(' + location record "' . $location['title'] . '" (HIDDEN - confirm branding/contacts, then unhide)');
}
}
// ---- plumbing -----------------------------------------------------------------
private function seoDatamapForPlan(string $key, array $page, int $pageUid): void
{
$id = $pageUid > 0 ? (string)$pageUid : ($this->pageIds[$key] ?? '');
if ($id === '' ) {
return;
}
$seo = [
'seo_title' => (string)$page['seoTitle'],
'og_title' => (string)$page['seoTitle'],
'twitter_title' => (string)$page['seoTitle'],
'description' => (string)$page['description'],
'og_description' => (string)$page['description'],
'twitter_description' => (string)$page['description'],
'abstract' => (string)$page['description'],
'keywords' => (string)$page['keywords'],
];
if (!str_starts_with($id, 'NEW')) {
$seo['canonical_link'] = 't3://page?uid=' . (int)$id;
}
foreach ($seo as $field => $value) {
$this->datamap['pages'][$id][$field] = $value;
}
}
/** '@key' -> t3://page of a plan page, '@/slug' -> page by slug, else verbatim. */
private function resolveLink(string $value): string
{
if (!str_starts_with($value, '@')) {
return $value;
}
$token = substr($value, 1);
if (str_starts_with($token, '/')) {
$uid = $this->pageUid(GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages'), $token);
if ($uid === 0) {
$this->output->writeln(' <comment>! link target ' . $token . ' not found - left empty, set editorially</comment>');
return '';
}
return 't3://page?uid=' . $uid;
}
$id = $this->pageIds[$token] ?? '';
if ($id === '' || str_starts_with($id, 'NEW')) {
// dry run (page not created yet): report the intention
return $id === '' ? '' : 't3://page?uid={' . $token . '}';
}
return 't3://page?uid=' . (int)$id;
}
private function slotNext(string $pageKey, int $pageUid, int $colPos, string $ctype): string
{
$key = $pageKey . '/' . $colPos . '/' . $ctype;
if (!isset($this->slotCursor[$key])) {
$this->slotCursor[$key] = $pageUid > 0 ? $this->slotElements($pageUid, $colPos, $ctype) : [];
}
$uid = array_shift($this->slotCursor[$key]);
if ($uid !== null) {
$this->sequence[$pageUid][$colPos][] = (string)$uid;
return (string)$uid;
}
$id = $this->newId();
$this->datamap['tt_content'][$id] = [
'pid' => $pageUid > 0 ? $pageUid : '@' . $pageKey,
'colPos' => $colPos,
'CType' => $ctype,
];
$this->sequence[$pageUid][$colPos][] = $id;
return $id;
}
/** @return int[] */
private function slotElements(int $pageUid, int $colPos, string $ctype): array
{
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('tt_content');
return array_map('intval', $connection->fetchFirstColumn(
'SELECT uid FROM tt_content WHERE deleted = 0 AND pid = ? AND colPos = ? AND CType = ? AND tx_container_parent = 0 ORDER BY sorting',
[$pageUid, $colPos, $ctype]
));
}
/** @param array<string,int> $substNEWwithIDs */
private function applySequence($connection, array $substNEWwithIDs): void
{
foreach ($this->sequence as $cols) {
foreach ($cols as $ids) {
$sorting = 0;
foreach ($ids as $id) {
$uid = str_starts_with($id, 'NEW') ? (int)($substNEWwithIDs[$id] ?? 0) : (int)$id;
if ($uid > 0) {
$sorting += 256;
$connection->update('tt_content', ['sorting' => $sorting], ['uid' => $uid]);
}
}
}
}
}
private function queueRedirect(string $sourcePath, int $targetUid): void
{
$this->datamap['sys_redirect'][$this->newId()] = [
'pid' => 0,
'source_host' => '*',
'source_path' => $sourcePath,
'target' => 't3://page?uid=' . $targetUid,
'target_statuscode' => 301,
];
}
private function pageUid($pages, string $slug): int
{
$row = $pages->fetchAssociative('SELECT uid FROM pages WHERE deleted = 0 AND slug = ? AND sys_language_uid IN (0,-1)', [$slug]);
return (int)($row['uid'] ?? 0);
}
private function p(string $text): string
{
$text = trim($text);
return $text === '' ? '' : '<p>' . htmlspecialchars($text, ENT_NOQUOTES | ENT_HTML5) . '</p>';
}
/** @param string[] $items */
private function ul(array $items): string
{
$li = array_map(fn(string $item): string => '<li>' . htmlspecialchars($item, ENT_NOQUOTES | ENT_HTML5) . '</li>', $items);
return '<ul>' . implode('', $li) . '</ul>';
}
private function newId(): string
{
return 'NEWCP' . (++$this->newCounter);
}
}