Backend Modul Worpress Import added

This commit is contained in:
2026-08-14 13:26:40 +02:00
parent b08ea7a55f
commit 4ef3ad0026
10 changed files with 1259 additions and 29 deletions

View File

@@ -0,0 +1,179 @@
<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
data-namespace-typo3-fluid="true">
<f:layout name="Backend/Default" />
<f:section name="main">
<div class="module-docheader-bar module-docheader-bar-navigation">
<div class="module-docheader-bar-column-left">
<h2 class="t3js-title-inlineedit">WordPress Import</h2>
</div>
</div>
<f:if condition="{error}">
<div class="callout callout-danger"><div class="callout-body">{error}</div></div>
</f:if>
<form action="{f:be.uri(route: 'web_vitecwpimport.import')}" method="post">
<!-- Source -->
<div class="card" style="margin-bottom:1rem;">
<div class="card-body">
<div class="row g-2 align-items-end">
<div class="col-md-6">
<label class="form-label" for="wpsource">Source (WordPress site)</label>
<input type="text" class="form-control" id="wpsource" name="source" value="{source}"
list="wppresets" placeholder="https://www.vitec.com/blog" />
<datalist id="wppresets">
<f:for each="{presets}" as="p"><option value="{p.url}">{p.label}</option></f:for>
</datalist>
<div class="form-text">
Read through the WordPress REST API &mdash; only the address is needed, no credentials.
<f:if condition="{sourceKey}"><br />Import key: <code>{sourceKey}</code></f:if>
</div>
</div>
<div class="col-md-auto">
<button type="submit" name="op" value="load" class="btn btn-default">Load posts</button>
</div>
</div>
</div>
</div>
<f:if condition="{rows}">
<!-- Import settings -->
<div class="card" style="margin-bottom:1rem;">
<div class="card-body">
<div class="row g-2 align-items-end">
<div class="col-md-5">
<label class="form-label" for="wpcategory">Category for the imported posts</label>
<select class="form-select" id="wpcategory" name="category">
<option value="0">&mdash; none &mdash;</option>
<f:for each="{categories}" as="c">
<option value="{c.uid}" {f:if(condition: '{c.uid} == {category}', then: 'selected="selected"')}>{c.title} ({c.uid})</option>
</f:for>
</select>
</div>
<div class="col-md-3">
<label class="form-label" for="wppid">Storage folder (pid) for the news records</label>
<input type="number" class="form-control" id="wppid" name="pid" value="{pid}" />
</div>
</div>
<hr />
<div class="row g-2 align-items-end">
<div class="col-md-5">
<label class="form-label">Import as</label>
<div class="form-check">
<input class="form-check-input" type="radio" name="mode" id="modeArticle" value="article"
{f:if(condition: '{mode} != \'page\'', then: 'checked="checked"')} />
<label class="form-check-label" for="modeArticle">
<strong>Article</strong> &mdash; body in the news record
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="mode" id="modePage" value="page"
{f:if(condition: '{mode} == \'page\'', then: 'checked="checked"')} />
<label class="form-check-label" for="modePage">
<strong>Page + news</strong> &mdash; one page per post, news of type 1 pointing at it
</label>
</div>
<div class="form-text">
The blog is built with Divi and has no block boundaries, so the body arrives as
<em>one</em> content element either way. Page mode is a starting point for reworking
single posts later, not an automatic layout.
</div>
</div>
<div class="col-md-3">
<label class="form-label" for="wppageparent">Parent page for generated pages</label>
<input type="number" class="form-control" id="wppageparent" name="pageParent" value="{pageParent}" />
<div class="form-text">Only used in page mode.</div>
</div>
<div class="col-md-2">
<label class="form-label" for="wppagelayout">Page layout</label>
<input type="number" class="form-control" id="wppagelayout" name="pageLayout" value="{pageLayout}" />
<div class="form-text">14 = News Detail V2</div>
</div>
</div>
<hr />
<div class="row g-2 align-items-center">
<div class="col-md-auto">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="dryRun" id="wpdryrun" value="1"
{f:if(condition: dryRun, then: 'checked="checked"')} />
<label class="form-check-label" for="wpdryrun">
<strong>Dry run</strong> &mdash; report only, write nothing
</label>
</div>
</div>
<div class="col-md-auto">
<button type="submit" name="op" value="import" class="btn btn-primary">Import selected</button>
</div>
</div>
</div>
</div>
<f:if condition="{log}">
<div class="card" style="margin-bottom:1rem;">
<div class="card-header">What happened</div>
<div class="card-body" style="max-height:18rem;overflow:auto;">
<f:for each="{log}" as="line"><div><code>{line}</code></div></f:for>
</div>
</div>
</f:if>
<p>
<strong>{total}</strong> posts found,
<strong>{imported}</strong> already imported.
Ticking one that is already there updates it.
</p>
<table class="table table-striped table-hover">
<thead>
<tr>
<th style="width:2rem;"><input type="checkbox" data-vitec-toggle-all="1" title="Select all" /></th>
<th>Title</th>
<th style="width:7rem;">Date</th>
<th>WP categories</th>
<th style="width:6rem;">Body</th>
<th style="width:5rem;">Images</th>
<th style="width:8rem;">Status</th>
</tr>
</thead>
<tbody>
<f:for each="{rows}" as="row">
<tr>
<td><input type="checkbox" name="posts[]" value="{row.wpId}" data-vitec-post="1" /></td>
<td>
{row.title}
<br /><small class="text-muted">{row.slug}</small>
</td>
<td>{row.date}</td>
<td>{row.categories}</td>
<td>{row.bodyLength} chars</td>
<td>
<f:if condition="{row.image}"><span title="featured image">1</span></f:if>
<f:if condition="{row.inlineImages}"> + {row.inlineImages}</f:if>
</td>
<td>
<f:if condition="{row.existingUid}">
<f:then><span class="badge bg-info">imported (uid {row.existingUid})</span></f:then>
<f:else><span class="badge bg-success">new</span></f:else>
</f:if>
<f:if condition="{row.rawShortcodes}">
<br /><span class="badge bg-warning" title="Divi shortcodes were never rendered - this post needs a look by hand">shortcodes</span>
</f:if>
</td>
</tr>
</f:for>
</tbody>
</table>
</f:if>
</form>
</f:section>
</html>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
stroke="#f47937" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="8.5" r="6"/>
<path d="M6.8 5.6 9.4 12.2 11 8.1"/>
<path d="M12.2 5.1 14.8 12.2 17.2 5.6"/>
<path d="M12 15.5v6"/>
<path d="m9 18.5 3 3 3-3"/>
</svg>

After

Width:  |  Height:  |  Size: 379 B

View File

@@ -0,0 +1,45 @@
/**
* Select-all for the WordPress import list.
*
* Lives in a module rather than in an onclick attribute: the backend enforces a
* Content Security Policy that blocks inline event handlers, so the attribute
* version simply did nothing.
*
* Also keeps the header box honest - it shows indeterminate while only some
* rows are ticked, which matters on a list of a hundred posts.
*/
function boxes() {
return Array.from(document.querySelectorAll('[data-vitec-post]'));
}
function sync(toggle) {
const all = boxes();
const checked = all.filter((box) => box.checked).length;
toggle.checked = all.length > 0 && checked === all.length;
toggle.indeterminate = checked > 0 && checked < all.length;
}
function init() {
const toggle = document.querySelector('[data-vitec-toggle-all]');
if (toggle === null) {
return;
}
toggle.addEventListener('change', () => {
const state = toggle.checked;
boxes().forEach((box) => {
box.checked = state;
});
toggle.indeterminate = false;
});
boxes().forEach((box) => box.addEventListener('change', () => sync(toggle)));
sync(toggle);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}