Quelltext des aktuellen Standes
<?php
if (!defined('ABSPATH')) {
exit;
}
/**
* WordPress Admin Einstellungsseite
*
* Einstellungen unter: WordPress Admin → Einstellungen → OpenNIT
*/
class OpenNIT_AdminSettings {
public function register(): void {
add_action('admin_menu', [$this, 'add_settings_page']);
add_action('admin_init', [$this, 'register_settings']);
add_action('admin_init', [$this, 'maybe_sync_menu_name']);
add_action('admin_notices', [$this, 'show_notices']);
add_action('wp_ajax_opennit_test_connection', [$this, 'ajax_test_connection']);
add_action('wp_ajax_opennit_flush_wiki_cache', [$this, 'ajax_flush_wiki_cache']);
add_action('wp_ajax_opennit_diagnose', [$this, 'ajax_diagnose']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
}
public function add_settings_page(): void {
$menu_name = (string)get_option('opennit_menu_name', 'OpenNIT | Connector');
add_menu_page(
$menu_name,
$menu_name,
'manage_options',
'opennit-connector',
[$this, 'render_page'],
'dashicons-networking',
30
);
}
public function register_settings(): void {
register_setting('opennit_settings', 'opennit_api_url', [
'sanitize_callback' => 'esc_url_raw',
'default' => '',
]);
register_setting('opennit_settings', 'opennit_api_token', [
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
]);
register_setting('opennit_settings', 'opennit_api_signing_secret', [
'sanitize_callback' => 'sanitize_text_field',
'default' => '',
]);
register_setting('opennit_settings', 'opennit_ticket_page_url', [
'sanitize_callback' => 'esc_url_raw',
'default' => '',
]);
// Hinweis: Die KI-Assistent-Einstellungen (Bubble, Titel, Begrüßung,
// Kontext) werden zentral im OpenNIT-Backend unter Admin → API gepflegt
// und vom Plugin automatisch übernommen – daher hier keine Optionen mehr.
}
/**
* Menü-Namen einmalig pro Stunde vom API laden (nur im Admin-Kontext).
*/
public function maybe_sync_menu_name(): void {
if (get_transient('opennit_menu_name_synced')) {
return;
}
$client = new OpenNIT_ApiClient();
$config = $client->get_config();
if (!is_wp_error($config) && !empty($config['menu_name'])) {
update_option('opennit_menu_name', sanitize_text_field($config['menu_name']));
}
set_transient('opennit_menu_name_synced', 1, HOUR_IN_SECONDS);
}
public function render_page(): void {
if (!current_user_can('manage_options')) {
return;
}
$api_url = get_option('opennit_api_url', '');
$connected = !empty($api_url) && !empty(get_option('opennit_api_token', ''));
?>
<div class="wrap">
<style>
.opennit-header {
display: flex;
align-items: center;
gap: 14px;
margin: 24px 0 28px;
}
.opennit-header-logo {
width: 42px;
height: 42px;
background: #1d4ed8;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.opennit-header-logo svg {
width: 24px;
height: 24px;
fill: #fff;
}
.opennit-header h1 {
margin: 0;
padding: 0;
font-size: 22px;
font-weight: 700;
color: #111827;
line-height: 1.2;
}
.opennit-header p {
margin: 3px 0 0;
color: #6b7280;
font-size: 13px;
}
.opennit-card {
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 10px;
padding: 24px 28px;
margin-bottom: 20px;
}
.opennit-card h2 {
font-size: 15px;
font-weight: 700;
color: #111827;
margin: 0 0 18px;
padding: 0 0 12px;
border-bottom: 1px solid #f3f4f6;
display: flex;
align-items: center;
gap: 8px;
}
.opennit-card h2 .card-icon {
width: 20px;
height: 20px;
background: #eff6ff;
border-radius: 5px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 11px;
}
.opennit-steps {
counter-reset: steps;
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 14px;
}
.opennit-steps li {
counter-increment: steps;
display: flex;
align-items: flex-start;
gap: 14px;
font-size: 13.5px;
color: #374151;
line-height: 1.5;
}
.opennit-steps li::before {
content: counter(steps);
min-width: 26px;
height: 26px;
background: #1d4ed8;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 700;
flex-shrink: 0;
margin-top: 1px;
}
.opennit-steps li strong {
display: block;
color: #111827;
margin-bottom: 2px;
}
.opennit-steps code {
background: #f3f4f6;
padding: 1px 6px;
border-radius: 4px;
font-size: 12.5px;
}
.opennit-hint {
background: #eff6ff;
border: 1px solid #bfdbfe;
border-radius: 7px;
padding: 11px 15px;
font-size: 13px;
color: #1e40af;
margin-top: 16px;
display: flex;
align-items: flex-start;
gap: 8px;
}
.opennit-hint-warning {
background: #fffbeb;
border-color: #fde68a;
color: #92400e;
}
.opennit-hint-success {
background: #f0fdf4;
border-color: #bbf7d0;
color: #14532d;
}
.opennit-form-row {
margin-bottom: 20px;
}
.opennit-form-row:last-of-type {
margin-bottom: 0;
}
.opennit-form-row label {
display: block;
font-size: 13px;
font-weight: 600;
color: #374151;
margin-bottom: 6px;
}
.opennit-form-row input[type="url"],
.opennit-form-row input[type="password"],
.opennit-form-row input[type="text"] {
width: 100%;
max-width: 480px;
font-size: 13.5px;
padding: 7px 10px;
border: 1px solid #d1d5db;
border-radius: 6px;
box-shadow: none;
}
.opennit-form-row .field-hint {
font-size: 12.5px;
color: #6b7280;
margin-top: 5px;
}
.opennit-shortcodes {
border-collapse: collapse;
width: 100%;
font-size: 13.5px;
}
.opennit-shortcodes th {
text-align: left;
padding: 8px 12px;
background: #f9fafb;
border-bottom: 1px solid #e5e7eb;
font-size: 12px;
text-transform: uppercase;
letter-spacing: .04em;
color: #6b7280;
font-weight: 600;
}
.opennit-shortcodes td {
padding: 12px;
border-bottom: 1px solid #f3f4f6;
color: #374151;
vertical-align: top;
}
.opennit-shortcodes tr:last-child td {
border-bottom: none;
}
.opennit-shortcodes code {
background: #f3f4f6;
padding: 3px 8px;
border-radius: 5px;
font-size: 13px;
color: #1d4ed8;
white-space: nowrap;
cursor: pointer;
}
.opennit-shortcodes code:hover {
background: #dbeafe;
}
.opennit-test-row {
display: flex;
align-items: center;
gap: 14px;
margin-top: 4px;
}
#opennit-test-result {
font-size: 13.5px;
font-weight: 500;
}
.opennit-footer {
text-align: center;
padding: 12px 0 24px;
font-size: 12px;
color: #9ca3af;
}
.opennit-footer a {
color: #6b7280;
text-decoration: none;
}
.opennit-footer a:hover {
color: #1d4ed8;
}
</style>
<!-- Header -->
<div class="opennit-header">
<div class="opennit-header-logo">
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm-1 14V10l-2 1V9l3-1.5L14 9v2l-2-1v6h-1z"/>
</svg>
</div>
<div>
<h1>OpenNIT | Connector</h1>
<p>Verbindet WordPress mit der OpenNIT IT-Dokumentationsplattform</p>
</div>
</div>
<?php if ($connected): ?>
<div class="opennit-hint opennit-hint-success" style="margin-bottom:20px;">
<span>✓</span>
<span>Plugin ist verbunden und aktiv. Die Shortcodes können direkt auf Seiten und Beiträgen eingesetzt werden.</span>
</div>
<?php endif; ?>
<!-- Schritt-für-Schritt-Einrichtung -->
<div class="opennit-card">
<h2><span class="card-icon">📋</span> Einrichtung in 4 Schritten</h2>
<ol class="opennit-steps">
<li>
<div>
<strong>OpenNIT Backend öffnen</strong>
Melden Sie sich im OpenNIT Backend an und navigieren Sie zu
<strong>Admin → API</strong>.
</div>
</li>
<li>
<div>
<strong>API-URL kopieren</strong>
Auf der Seite <em>Admin → API</em> wird die fertige API-URL angezeigt.
Kopieren Sie diese und tragen Sie sie unten im Feld <em>API-Basis-URL</em> ein.
</div>
</li>
<li>
<div>
<strong>API-Token generieren</strong>
Klicken Sie im OpenNIT Backend auf <strong>„Neuen Token erstellen"</strong>.
Der Token wird <em>nur einmal</em> angezeigt — kopieren Sie ihn sofort und
tragen Sie ihn unten im Feld <em>API-Token</em> ein.
</div>
</li>
<li>
<div>
<strong>Speichern & Verbindung testen</strong>
Klicken Sie auf <em>Einstellungen speichern</em> und anschließend auf
<em>Verbindung testen</em>, um die Verbindung zu prüfen.
</div>
</li>
</ol>
<div class="opennit-hint">
<span>💡</span>
<span>
<strong>Beschriftungen anpassen:</strong>
Menünamen, Seitenüberschriften und Texte der Shortcodes lassen sich direkt im
OpenNIT Backend unter <strong>Admin → API</strong> anpassen —
ohne Änderungen an WordPress oder diesem Plugin.
</span>
</div>
</div>
<!-- Verbindungseinstellungen -->
<div class="opennit-card">
<h2><span class="card-icon">🔌</span> Verbindungseinstellungen</h2>
<form method="post" action="options.php">
<?php settings_fields('opennit_settings'); ?>
<?php if (defined('OPENNIT_API_URL') || defined('OPENNIT_API_TOKEN')): ?>
<div class="opennit-hint opennit-hint-success" style="margin-bottom:18px;">
<span>🔒</span>
<span>
<strong>Konstanten aktiv:</strong>
API-URL<?= defined('OPENNIT_API_URL') ? ' ✓' : '' ?> und Token<?= defined('OPENNIT_API_TOKEN') ? ' ✓' : '' ?>
sind über <code>wp-config.php</code>-Konstanten gesetzt und können hier nicht überschrieben werden.
</span>
</div>
<?php endif; ?>
<div class="opennit-form-row">
<label for="opennit_api_url">API-Basis-URL</label>
<input type="url" id="opennit_api_url" name="opennit_api_url"
value="<?= esc_attr(defined('OPENNIT_API_URL') ? OPENNIT_API_URL : get_option('opennit_api_url', '')) ?>"
placeholder="https://opennit.beispiel.de/api/ex"
<?= defined('OPENNIT_API_URL') ? 'readonly' : '' ?>>
<p class="field-hint">
Die URL finden Sie im OpenNIT Backend unter <strong>Admin → WordPress Plugin</strong>.
Die URL muss mit <code>https://</code> beginnen — HTTP wird von der API abgewiesen.
Alternativ: Konstante <code>OPENNIT_API_URL</code> in <code>wp-config.php</code> setzen.
</p>
</div>
<div class="opennit-form-row">
<label for="opennit_api_token">API-Token</label>
<input type="password" id="opennit_api_token" name="opennit_api_token"
value="<?= esc_attr(defined('OPENNIT_API_TOKEN') ? OPENNIT_API_TOKEN : get_option('opennit_api_token', '')) ?>"
autocomplete="off"
<?= defined('OPENNIT_API_TOKEN') ? 'readonly' : '' ?>>
<p class="field-hint">
Token wird im OpenNIT Backend generiert und nur einmal angezeigt.
Bei Verlust muss ein neuer Token erstellt werden.
Alternativ: Konstante <code>OPENNIT_API_TOKEN</code> in <code>wp-config.php</code> setzen.
</p>
</div>
<div class="opennit-form-row">
<label for="opennit_api_signing_secret">Signing-Secret (HMAC)</label>
<input type="password" id="opennit_api_signing_secret" name="opennit_api_signing_secret"
value="<?= esc_attr(defined('OPENNIT_API_SIGNING_SECRET') ? OPENNIT_API_SIGNING_SECRET : get_option('opennit_api_signing_secret', '')) ?>"
autocomplete="off"
<?= defined('OPENNIT_API_SIGNING_SECRET') ? 'readonly' : '' ?>>
<p class="field-hint">
<strong>Pflicht.</strong> Wird beim Erstellen des Tokens im OpenNIT-Backend
<em>einmalig</em> zusammen mit dem Token angezeigt. Jede API-Anfrage wird damit
signiert (HMAC-SHA256) — Schutz vor Manipulation und Replay.
Alternativ: Konstante <code>OPENNIT_API_SIGNING_SECRET</code> in <code>wp-config.php</code>.
</p>
</div>
<div class="opennit-form-row">
<label for="opennit_ticket_page_url">URL der Ticket-Seite <span style="font-weight:400;color:#6b7280;">(optional)</span></label>
<input type="url" id="opennit_ticket_page_url" name="opennit_ticket_page_url"
value="<?= esc_attr(get_option('opennit_ticket_page_url', '')) ?>"
placeholder="/it-support">
<p class="field-hint">
URL der WordPress-Seite mit dem Shortcode <code>[opennit_ticket_form]</code>.
Wenn gesetzt, erscheint in der Geräteübersicht (<code>[opennit_assets]</code>) ein
„Ticket melden"-Button direkt am jeweiligen Gerät.
</p>
</div>
<hr style="margin:1.5rem 0;border:none;border-top:1px solid #e5e7eb;">
<h2 style="font-size:1.05rem;">KI-Assistent (Website-Bot)</h2>
<div class="opennit-hint" style="margin-bottom:0;">
<span>💡</span>
<span>
Die Einstellungen des Website-Bots (Bubble anzeigen, nur für angemeldete Nutzer,
Titel, Begrüßungstext und zusätzlicher Kontext) werden jetzt zentral im
<strong>OpenNIT-Backend unter Admin → API</strong> gepflegt und vom Plugin
automatisch übernommen. Der Bot muss dort zudem aktiviert sein (Scope
<code>assistant</code>, für Tickets zusätzlich <code>tickets</code>).
Einbinden per Bubble oder Shortcode <code>[opennit_assistant]</code>.
</span>
</div>
<?php submit_button('Einstellungen speichern', 'primary', 'submit', false); ?>
<div style="display:inline-flex; align-items:center; gap:14px; margin-left:12px;">
<button type="button" id="opennit-test-btn" class="button button-secondary">
Verbindung testen
</button>
<span id="opennit-test-result"></span>
</div>
</form>
</div>
<!-- Shortcode-Übersicht -->
<div class="opennit-card">
<h2><span class="card-icon">{ }</span> Verfügbare Shortcodes</h2>
<p style="font-size:13px; color:#6b7280; margin:0 0 16px;">
Shortcodes können auf beliebigen WordPress-Seiten oder Beiträgen eingesetzt werden.
Klicken Sie auf einen Shortcode, um ihn in die Zwischenablage zu kopieren.
</p>
<table class="opennit-shortcodes">
<thead>
<tr>
<th style="width:230px;">Shortcode</th>
<th>Was wird angezeigt</th>
</tr>
</thead>
<tbody>
<tr>
<td><code onclick="navigator.clipboard.writeText('[opennit_loan_devices]')" title="Klicken zum Kopieren">[opennit_loan_devices]</code></td>
<td>
Leihgeräte-Katalog, aktive Leihen des angemeldeten Nutzers und
Anfrageformular — alles in einer vollbreiten Ansicht.
</td>
</tr>
<tr>
<td><code onclick="navigator.clipboard.writeText('[opennit_assets]')" title="Klicken zum Kopieren">[opennit_assets]</code></td>
<td>
Persönliche IT-Geräteübersicht des angemeldeten Nutzers
(Zuordnung über die WordPress-E-Mail-Adresse), inkl. Standort und Garantie.
</td>
</tr>
<tr>
<td><code onclick="navigator.clipboard.writeText('[opennit_ticket_form]')" title="Klicken zum Kopieren">[opennit_ticket_form]</code></td>
<td>
IT-Support-Ticket-Formular — Einsender kann ein neues Ticket mit
Kategorie, Standort, optionalem Gerätebezug und Dateianhang erstellen.
</td>
</tr>
<tr>
<td><code onclick="navigator.clipboard.writeText('[opennit_assistant]')" title="Klicken zum Kopieren">[opennit_assistant]</code></td>
<td>
KI-Assistent als eingebettetes Chat-Fenster. Beantwortet einfache PC-Fragen
und Fragen zur freigegebenen Dokumentation. Alternativ als schwebende Bubble
(siehe Einstellung oben). Benötigt Token-Scope <code>assistant</code>.
</td>
</tr>
<tr>
<td><code onclick="navigator.clipboard.writeText('[opennit_my_tickets]')" title="Klicken zum Kopieren">[opennit_my_tickets]</code></td>
<td>
Übersicht der eigenen Tickets mit Detail-Ansicht, Antwortenverlauf,
Ansprechpartner, Beobachter, verknüpftem Asset und Antwort-Funktion.
</td>
</tr>
<tr>
<td><code onclick="navigator.clipboard.writeText('[opennit_wiki]')" title="Klicken zum Kopieren">[opennit_wiki]</code></td>
<td>
Wissensdatenbank-Shortcode: zeigt alle freigegebenen Themen als
anklickbare Kacheln, Unterthemen und Seiteninhalte. Inhalte werden
24 Stunden gecacht (inkl. Bilder).<br><br>
<strong>Optionales Attribut <code>topic="ID"</code>:</strong>
<code>[opennit_wiki topic="5"]</code> legt ein bestimmtes Thema als festen
Einstiegspunkt fest. Die globale Themenübersicht wird übersprungen; die Seite
zeigt direkt die Unterthemen und Seiten dieses Themas. Der Zurück-Button wird
ausgeblendet, solange man sich auf der Wurzelebene befindet. Navigation in
Unterthemen funktioniert wie gewohnt – der Zurück-Link führt dann eine Ebene
höher, maximal aber zum konfigurierten Wurzelthema.
</td>
</tr>
</tbody>
</table>
<div class="opennit-hint opennit-hint-warning" style="margin-top:16px;">
<span>⚠</span>
<span>
Die Shortcodes setzen voraus, dass der Nutzer in WordPress eingeloggt ist.
Die E-Mail-Adresse des WordPress-Accounts muss mit dem Eintrag in OpenNIT übereinstimmen.
</span>
</div>
</div>
<!-- Cache -->
<div class="opennit-card">
<h2><span class="card-icon">📚</span> Cache leeren</h2>
<p style="font-size:13px; color:#6b7280; margin:0 0 16px;">
Der <code>[opennit_wiki]</code>-Shortcode speichert Themen, Seiten und Bilder
für <strong>24 Stunden</strong>; Plugin-Einstellungen (Menüname, Texte und die
<strong>Assistent-/Bubble-Einstellungen</strong>) werden für <strong>5 Minuten</strong>
gecacht. Nach Änderungen im OpenNIT-Backend (z. B. Bubble aktivieren) können Sie den
Cache hier sofort leeren, damit die Änderungen ohne Wartezeit wirksam werden.
</p>
<div style="display:flex; align-items:center; gap:14px;">
<button type="button" id="opennit-flush-wiki-btn" class="button button-secondary">
Cache jetzt leeren
</button>
<span id="opennit-flush-wiki-result" style="font-size:13.5px; font-weight:500;"></span>
</div>
</div>
<!-- API-Diagnose -->
<div class="opennit-card">
<h2><span class="card-icon">🔍</span> API-Diagnose</h2>
<p style="font-size:13px; color:#6b7280; margin:0 0 16px;">
Prüft jeden API-Endpunkt einzeln und zeigt Status, HTTP-Code und Antwort.
Hilfreich wenn Dropdowns nicht laden oder Daten fehlen.
</p>
<div style="display:flex; align-items:center; gap:14px; margin-bottom:12px;">
<button type="button" id="opennit-diagnose-btn" class="button button-secondary">
Diagnose starten
</button>
<span id="opennit-diagnose-status" style="font-size:13px; color:#6b7280;"></span>
</div>
<div id="opennit-diagnose-result" style="display:none; font-size:13px; line-height:1.7;"></div>
</div>
<div class="opennit-footer">
<a href="https://opennit.de" target="_blank">opennit.de</a>
· OpenNIT | Connector v<?= esc_html(OPENNIT_VERSION) ?>
</div>
</div>
<script>
document.getElementById('opennit-test-btn').addEventListener('click', function() {
var btn = this;
var result = document.getElementById('opennit-test-result');
btn.disabled = true;
result.textContent = 'Teste Verbindung…';
result.style.color = '#6b7280';
var formData = new FormData();
formData.append('action', 'opennit_test_connection');
formData.append('nonce', '<?= wp_create_nonce('opennit_test') ?>');
fetch(ajaxurl, { method: 'POST', body: formData })
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success) {
result.innerHTML = '✓ Verbindung erfolgreich';
result.style.color = '#16a34a';
} else {
result.innerHTML = '✗ Fehler: ' + (data.data || 'Unbekannter Fehler');
result.style.color = '#dc2626';
}
})
.catch(function() {
result.innerHTML = '✗ Netzwerkfehler';
result.style.color = '#dc2626';
})
.finally(function() {
btn.disabled = false;
});
});
document.getElementById('opennit-flush-wiki-btn').addEventListener('click', function() {
var btn = this;
var result = document.getElementById('opennit-flush-wiki-result');
btn.disabled = true;
result.textContent = 'Cache wird geleert…';
result.style.color = '#6b7280';
var formData = new FormData();
formData.append('action', 'opennit_flush_wiki_cache');
formData.append('nonce', '<?= wp_create_nonce('opennit_flush_wiki') ?>');
fetch(ajaxurl, { method: 'POST', body: formData })
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success) {
result.innerHTML = '✓ Cache geleert';
result.style.color = '#16a34a';
} else {
result.innerHTML = '✗ Fehler: ' + (data.data || 'Unbekannter Fehler');
result.style.color = '#dc2626';
}
})
.catch(function() {
result.innerHTML = '✗ Netzwerkfehler';
result.style.color = '#dc2626';
})
.finally(function() {
btn.disabled = false;
});
});
document.getElementById('opennit-diagnose-btn').addEventListener('click', function() {
var btn = this;
var status = document.getElementById('opennit-diagnose-status');
var result = document.getElementById('opennit-diagnose-result');
btn.disabled = true;
status.textContent = 'Diagnose läuft…';
status.style.color = '#6b7280';
result.style.display = 'none';
result.innerHTML = '';
var formData = new FormData();
formData.append('action', 'opennit_diagnose');
formData.append('nonce', '<?= wp_create_nonce('opennit_diagnose') ?>');
fetch(ajaxurl, { method: 'POST', body: formData })
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success) {
result.innerHTML = data.data.html;
result.style.display = 'block';
status.textContent = '';
} else {
status.textContent = '✗ ' + (data.data || 'Fehler');
status.style.color = '#dc2626';
}
})
.catch(function() {
status.textContent = '✗ Netzwerkfehler';
status.style.color = '#dc2626';
})
.finally(function() {
btn.disabled = false;
});
});
</script>
<?php
}
public function ajax_test_connection(): void {
check_ajax_referer('opennit_test', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Keine Berechtigung.');
return;
}
$client = new OpenNIT_ApiClient();
$result = $client->test_connection();
if (is_wp_error($result)) {
wp_send_json_error($result->get_error_message());
return;
}
wp_send_json_success('OK');
}
public function ajax_flush_wiki_cache(): void {
check_ajax_referer('opennit_flush_wiki', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Keine Berechtigung.');
return;
}
OpenNIT_Wiki::flush_all();
// Auch den Konfigurations-Cache leeren – darüber kommen u. a. die
// Assistent-Einstellungen (Bubble, Titel, Begrüßung) aus dem OpenNIT-Backend.
delete_transient('opennit_config_cache');
delete_transient('opennit_menu_name_synced');
wp_send_json_success('Cache geleert.');
}
public function ajax_diagnose(): void {
check_ajax_referer('opennit_diagnose', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error('Keine Berechtigung.');
return;
}
$api_url = rtrim((string)(defined('OPENNIT_API_URL') ? OPENNIT_API_URL : get_option('opennit_api_url', '')), '/');
$api_url = preg_replace('#^http://#', 'https://', $api_url);
$token = (string)(defined('OPENNIT_API_TOKEN') ? OPENNIT_API_TOKEN : get_option('opennit_api_token', ''));
$secret = (string)(defined('OPENNIT_API_SIGNING_SECRET') ? OPENNIT_API_SIGNING_SECRET : get_option('opennit_api_signing_secret', ''));
if (!$api_url || !$token) {
wp_send_json_error('API-URL oder Token nicht konfiguriert.');
return;
}
if (!$secret) {
wp_send_json_error('Signing-Secret nicht konfiguriert. Bitte eintragen (wird beim Token-Erstellen im OpenNIT-Backend angezeigt).');
return;
}
// Alle Aufrufe laufen über den ApiClient, damit sie korrekt signiert werden.
$client = new OpenNIT_ApiClient();
$endpoints = [
['label' => 'Konfiguration', 'path' => '/config', 'call' => fn() => $client->get_config(), 'key' => 'texts'],
['label' => 'Ticket-Standorte', 'path' => '/ticket-locations', 'call' => fn() => $client->get_ticket_locations(), 'key' => 'locations'],
['label' => 'Ticket-Kategorien', 'path' => '/ticket-categories','call' => fn() => $client->get_ticket_categories(),'key' => 'categories'],
['label' => 'Leihgeräte', 'path' => '/loan-devices', 'call' => fn() => $client->get_loan_devices(), 'key' => 'devices'],
['label' => 'Gerätetypen', 'path' => '/device-types', 'call' => fn() => $client->get_device_types(), 'key' => 'types'],
];
$rows = [];
foreach ($endpoints as $ep) {
$result = ($ep['call'])();
if (is_wp_error($result)) {
$code = '—';
if (preg_match('/api_error_(\d+)/', $result->get_error_code(), $m)) {
$code = (int) $m[1];
}
$rows[] = [
'label' => $ep['label'],
'path' => $ep['path'],
'code' => $code,
'ok' => false,
'detail' => $result->get_error_message(),
];
continue;
}
$key = $ep['key'];
if (isset($result[$key]) && is_array($result[$key])) {
$count = count($result[$key]);
$detail = $key . ': ' . $count . ' Einträge';
$ok = $count > 0;
if ($count === 0) {
$detail .= ' ⚠ Tabelle leer oder Scope fehlt?';
}
} else {
$detail = 'JSON OK (' . implode(', ', array_keys($result)) . ')';
$ok = true;
}
$rows[] = [
'label' => $ep['label'],
'path' => $ep['path'],
'code' => $ok ? 200 : '—',
'ok' => $ok,
'detail' => $detail,
];
}
$html = '<table style="width:100%;border-collapse:collapse;font-size:13px;">';
$html .= '<tr style="background:#f9fafb;">';
$html .= '<th style="text-align:left;padding:6px 10px;border-bottom:1px solid #e5e7eb;white-space:nowrap;">Endpunkt</th>';
$html .= '<th style="text-align:left;padding:6px 10px;border-bottom:1px solid #e5e7eb;font-family:monospace;font-size:12px;">Pfad</th>';
$html .= '<th style="text-align:center;padding:6px 10px;border-bottom:1px solid #e5e7eb;white-space:nowrap;">HTTP</th>';
$html .= '<th style="text-align:left;padding:6px 10px;border-bottom:1px solid #e5e7eb;">Ergebnis</th>';
$html .= '</tr>';
foreach ($rows as $r) {
$color = $r['ok'] ? '#16a34a' : '#dc2626';
$icon = $r['ok'] ? '✓' : '✗';
$html .= '<tr>';
$html .= '<td style="padding:6px 10px;border-bottom:1px solid #f3f4f6;white-space:nowrap;">' . esc_html($r['label']) . '</td>';
$html .= '<td style="padding:6px 10px;border-bottom:1px solid #f3f4f6;font-family:monospace;font-size:12px;">' . esc_html($r['path']) . '</td>';
$html .= '<td style="padding:6px 10px;border-bottom:1px solid #f3f4f6;text-align:center;color:' . $color . ';font-weight:700;">' . esc_html((string)$r['code']) . '</td>';
$html .= '<td style="padding:6px 10px;border-bottom:1px solid #f3f4f6;color:' . $color . ';">' . $icon . ' ' . esc_html($r['detail']) . '</td>';
$html .= '</tr>';
}
$html .= '</table>';
$html .= '<p style="margin:10px 0 0;font-size:12px;color:#9ca3af;">API-URL: <code>' . esc_html($api_url) . '</code></p>';
wp_send_json_success(['html' => $html]);
}
public function show_notices(): void {
$screen = get_current_screen();
if (!$screen || $screen->id !== 'toplevel_page_opennit-connector') {
return;
}
$url = get_option('opennit_api_url', '');
$token = get_option('opennit_api_token', '');
$secret = defined('OPENNIT_API_SIGNING_SECRET') ? OPENNIT_API_SIGNING_SECRET : get_option('opennit_api_signing_secret', '');
if (empty($url) || empty($token) || empty($secret)) {
$plugin_name = esc_html(get_option('opennit_menu_name', 'OpenNIT | Connector'));
echo '<div class="notice notice-warning"><p><strong>' . $plugin_name . ':</strong> Bitte API-URL, Token und Signing-Secret konfigurieren.</p></div>';
}
}
public function enqueue_admin_scripts(string $hook): void {
// Nur auf unserer Settings-Seite
if ($hook !== 'settings_page_opennit-connector') {
return;
}
}
}