diff --git a/public/product-check/index.php b/public/product-check/index.php new file mode 100644 index 0000000..a0493cd --- /dev/null +++ b/public/product-check/index.php @@ -0,0 +1,328 @@ + PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC] +); + +// ---- data ------------------------------------------------------------------- + +$products = $pdo->query( + "SELECT uid, title, slug, teaser, subtitle, description, description2, highlights, + capabilities, applications, resources, resources_layout, + hidden, legacy, supportproduct, subproduct, hideonwebsite, hideonproducts, showdatapath + FROM tx_vitec_domain_model_product WHERE deleted = 0 ORDER BY LOWER(title), uid" +)->fetchAll(); + +// FAL references per product and fieldname, with file identifiers +$fileRefs = $pdo->query( + "SELECT r.uid_foreign, r.fieldname, f.identifier, f.name + FROM sys_file_reference r JOIN sys_file f ON f.uid = r.uid_local + WHERE r.deleted = 0 AND r.hidden = 0 AND r.tablenames = 'tx_vitec_domain_model_product' + AND r.fieldname IN ('heroimage','productimage','ogimage','videofile') + ORDER BY r.uid_foreign, r.fieldname, r.sorting_foreign" +)->fetchAll(); +$files = []; +foreach ($fileRefs as $r) { + $files[(int)$r['uid_foreign']][$r['fieldname']][] = ['url' => '/fileadmin' . $r['identifier'], 'name' => (string)$r['name']]; +} + +// inline resource cards +$resourceRows = $pdo->query( + 'SELECT * FROM tx_vitec_product_resource WHERE deleted = 0 AND hidden = 0 ORDER BY product, sorting' +)->fetchAll(); +$resources = []; +foreach ($resourceRows as $r) { + $resources[(int)$r['product']][] = $r; +} + +// MM: datasheets (download records) and solutions, in MM order +$datasheets = []; +foreach ($pdo->query( + 'SELECT mm.uid_local AS product, d.title FROM tx_vitec_product_datasheet_mm mm + JOIN tx_vitec_domain_model_download d ON d.uid = mm.uid_foreign AND d.deleted = 0 + ORDER BY mm.uid_local, mm.sorting' +)->fetchAll() as $r) { + $datasheets[(int)$r['product']][] = (string)$r['title']; +} +$solutions = []; +foreach ($pdo->query( + 'SELECT mm.uid_local AS product, s.title FROM tx_vitec_product_solution_mm mm + JOIN tx_vitec_domain_model_solution s ON s.uid = mm.uid_foreign AND s.deleted = 0 + ORDER BY mm.uid_local, mm.sorting' +)->fetchAll() as $r) { + $solutions[(int)$r['product']][] = (string)$r['title']; +} + +// ---- helpers ------------------------------------------------------------------ + +function h(string $v): string +{ + return htmlspecialchars($v, ENT_QUOTES | ENT_HTML5); +} + +/** check/cross cell; a count > 1 is shown next to the check */ +function cell(bool $ok, int $count = 0): string +{ + if (!$ok) { + return '✗'; + } + return '✓' . ($count > 1 ? '' . $count . '' : '') . ''; +} + +function filled(?string $v): bool +{ + return trim(strip_tags((string)$v)) !== ''; +} + +/** labeled block in the detail pane; HTML of RTE fields is rendered */ +function textBlock(string $label, ?string $value): string +{ + if (!filled($value)) { + return ''; + } + return '
' . h($label) . '
' . $value . '
'; +} + +/** @param array $images */ +function imageBlock(string $label, array $images): string +{ + if ($images === []) { + return ''; + } + $out = '
' . h($label) . ' (' . count($images) . ')
'; + foreach ($images as $img) { + $out .= '' + . '' . h($img['name']) . ''; + } + return $out . '
'; +} + +/** @param string[] $items */ +function listBlock(string $label, array $items): string +{ + if ($items === []) { + return ''; + } + return '
' . h($label) . ' (' . count($items) . ')
'; +} + +// ---- columns ------------------------------------------------------------------ + +$columns = ['Hero', 'Photos', 'OG', 'Video', 'Teaser', 'Subtitle', 'Description', 'Descr. 2', 'Highlights', 'Capabilities', 'Applications', 'Resources', 'Datasheets', 'Solutions']; + +$filledPerColumn = array_fill_keys($columns, 0); +$rowsHtml = ''; +foreach ($products as $p) { + $uid = (int)$p['uid']; + $f = $files[$uid] ?? []; + $res = $resources[$uid] ?? []; + $ds = $datasheets[$uid] ?? []; + $sol = $solutions[$uid] ?? []; + + $checks = [ + 'Hero' => [count($f['heroimage'] ?? []), true], + 'Photos' => [count($f['productimage'] ?? []), true], + 'OG' => [count($f['ogimage'] ?? []), true], + 'Video' => [count($f['videofile'] ?? []), true], + 'Teaser' => [filled($p['teaser']) ? 1 : 0, false], + 'Subtitle' => [filled($p['subtitle']) ? 1 : 0, false], + 'Description' => [filled($p['description']) ? 1 : 0, false], + 'Descr. 2' => [filled($p['description2']) ? 1 : 0, false], + 'Highlights' => [filled($p['highlights']) ? 1 : 0, false], + 'Capabilities' => [filled($p['capabilities']) ? 1 : 0, false], + 'Applications' => [filled($p['applications']) ? 1 : 0, false], + 'Resources' => [count($res), true], + 'Datasheets' => [count($ds), true], + 'Solutions' => [count($sol), true], + ]; + + $cells = ''; + foreach ($columns as $column) { + [$count, $showCount] = $checks[$column]; + $filledPerColumn[$column] += $count > 0 ? 1 : 0; + $cells .= cell($count > 0, $showCount ? $count : 0); + } + + $flags = []; + foreach (['hidden' => 'hidden', 'legacy' => 'legacy', 'supportproduct' => 'support', 'subproduct' => 'sub', 'hideonwebsite' => 'hideWeb', 'hideonproducts' => 'hideList', 'showdatapath' => 'datapath'] as $col => $label) { + if ((int)$p[$col] === 1) { + $flags[] = $label; + } + } + + // detail pane: every content of the record + $resourceLines = []; + foreach ($res as $r) { + $parts = []; + foreach (['title', 'header', 'label', 'kind', 'link'] as $key) { + if (isset($r[$key]) && trim((string)$r[$key]) !== '') { + $parts[] = (string)$r[$key]; + } + } + $resourceLines[] = $parts === [] ? ('#' . $r['uid']) : implode(' · ', $parts); + } + + $detail = '
' + . '/product/' . h(trim((string)$p['slug'], '/')) . '' + . ' · uid ' . $uid + . ' · resources_layout: ' . h((string)$p['resources_layout']) + . ($flags !== [] ? ' · Flags: ' . h(implode(', ', $flags)) : '') + . '
' + . imageBlock('Hero Image', $f['heroimage'] ?? []) + . imageBlock('Product Images', $f['productimage'] ?? []) + . imageBlock('OG Image', $f['ogimage'] ?? []) + . listBlock('Video Files', array_map(static fn(array $v): string => $v['name'] . ' (' . $v['url'] . ')', $f['videofile'] ?? [])) + . textBlock('Teaser', (string)$p['teaser']) + . textBlock('Subtitle', (string)$p['subtitle']) + . textBlock('Description', (string)$p['description']) + . textBlock('Description 2', (string)$p['description2']) + . textBlock('Highlights', (string)$p['highlights']) + . textBlock('Capabilities', (string)$p['capabilities']) + . textBlock('Applications', (string)$p['applications']) + . listBlock('Resources', $resourceLines) + . listBlock('Datasheets', $ds) + . listBlock('Solutions', $sol); + + $rowsHtml .= '' + . '▸' . h((string)$p['title']) + . ($flags !== [] ? ' ' . h(implode(' ', $flags)) . '' : '') + . '' . $cells . '' . "\n" + . '
' . $detail . '
' . "\n"; +} + +$headCells = 'Product (' . count($products) . ')'; +foreach ($columns as $column) { + $headCells .= '' + . h($column) . '' . $filledPerColumn[$column] . ''; +} + +// ---- page --------------------------------------------------------------------- +?> + + + + + + +VITEC Product Content Check + + + +
+

VITEC Product Content Check

+ products · as of · click a row for details + +
+
+ + + + + +
+
+ + + +