Product text import from agency XLSX workbooks
VITEC Import module: Products tab is now a searchable/sortable product overview. "Edit Product" opens an XLSX upload with per-field source mapping (component + cell), old/new preview and checkbox apply via DataHandler; the mapping and manual matches are persisted and preselect the next workbook. Category workbooks are detected and rejected. - new: ProductXlsxReader (PhpSpreadsheet), ProductTextImportController, Products/ProductTexts templates, 4 module routes - related products: per-pair card text in new side table tx_vitec_product_related_text (survives MM rewrites), emitted as `cardtext` in the product JSON; missing MM relations added add-only - card copy read from Body Copy (D) with fallback to CTA/Card Copy (E) - the workbooks fill either depending on row type - product detail page <title> now uses seotitle with title fallback (provider made singleton and fed from the JSON renderer) - per-user recent-search badges; last loaded workbook stored per product (tx_vitec_product_workbook), Edit Product reopens on it - composer: add phpoffice/phpspreadsheet ^5.9 - diagnostics: migrations/check_workbook.php
This commit is contained in:
1 parent
12eb40614b
commit
7bd1161abf
28 files changed
+2594
-39
No files matched your search
@@ -11,14 +11,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Model tabs -->
|
||||
<!-- Model tabs. Products left the CSV flow on 2026-08-21: its tab now leads to
|
||||
the XLSX product-text overview (ProductTextImportController); the CSV code
|
||||
path for products stays functional but is no longer linked. -->
|
||||
<ul class="nav nav-tabs" style="margin-bottom:1rem;">
|
||||
<f:for each="{models}" as="m">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {f:if(condition: '{m.key} == {model.key}', then: 'active')}"
|
||||
href="{f:be.uri(route: 'web_vitecimport', parameters: {model: m.key})}">{m.label}</a>
|
||||
</li>
|
||||
<f:if condition="{m.key} != 'product'">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {f:if(condition: '{m.key} == {model.key}', then: 'active')}"
|
||||
href="{f:be.uri(route: 'web_vitecimport', parameters: {model: m.key})}">{m.label}</a>
|
||||
</li>
|
||||
</f:if>
|
||||
</f:for>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{f:be.uri(route: 'web_vitecimport.products')}">Products</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{f:be.uri(route: 'web_vitecimport.seo')}">SEO Research</a>
|
||||
</li>
|
||||
@@ -134,6 +141,7 @@
|
||||
<f:case value="unchanged"><span class="vitec-badge vitec-badge-unchanged">unchanged</span></f:case>
|
||||
<f:defaultCase><span class="vitec-badge vitec-badge-dbonly">only in DB</span></f:defaultCase>
|
||||
</f:switch>
|
||||
<f:if condition="{row.viaAlias}"><br /><span class="vitec-badge vitec-badge-info">linked</span></f:if>
|
||||
</td>
|
||||
<td>
|
||||
<strong>{row.label}</strong>
|
||||
@@ -141,6 +149,14 @@
|
||||
<f:if condition="{row.status} == 'update'">
|
||||
<br /><small class="text-muted">changes: {row.diff}</small>
|
||||
</f:if>
|
||||
<f:if condition="{row.aliasable}">
|
||||
<select name="alias[{row.index}]" class="form-select form-select-sm" style="margin-top:4px;">
|
||||
<option value="0">— not linked —</option>
|
||||
<f:for each="{aliasCandidates}" as="cand">
|
||||
<option value="{cand.uid}" {f:if(condition: '{cand.uid} == {row.aliasUid}', then: 'selected')}>{cand.title} (uid {cand.uid})</option>
|
||||
</f:for>
|
||||
</select>
|
||||
</f:if>
|
||||
</td>
|
||||
<td>
|
||||
<f:if condition="{row.status} != 'dbonly'">
|
||||
@@ -156,10 +172,18 @@
|
||||
<div class="row row-cols-auto align-items-center g-2">
|
||||
<div class="col"><label for="pid"><strong>Storage pid for new records</strong></label></div>
|
||||
<div class="col"><input type="number" name="pid" id="pid" value="{pid}" class="form-control form-control-sm" style="width:110px;" /></div>
|
||||
<div class="col">
|
||||
<button type="submit" name="op" value="saveAliases" class="btn btn-default">Save record links</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<button type="submit" name="op" value="apply" class="btn btn-primary"
|
||||
onclick="return confirm('Apply the checked rows to the database?');">Apply checked rows</button>
|
||||
</div>
|
||||
<div class="col form-text">
|
||||
A "new" row that exists in TYPO3 under a different name: pick the record in
|
||||
its select, then "Save record links" - the link is stored per model and
|
||||
re-applied on every upload.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user