Quelltext des aktuellen Standes
<?php $this->layout('layouts/admin', [
'page_title' => 'Relution MDM',
'current_admin_page' => 'relution-mdm',
]); ?>
<?php $connections = $connections ?? []; ?>
<div class="settings-page">
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">📱</span>
<div>
<h2><?= $this->te('Relution-Instanzen') ?></h2>
<p class="settings-card-desc">
<?= $this->te('Woher die verwalteten Geräte stammen. Mehrere Instanzen nebeneinander sind möglich – je Kunde oder Mandant eine, jede mit eigenen Zugangsdaten und Filtern.') ?>
</p>
</div>
</div>
<div class="settings-card-body" style="padding:0;">
<?php if (empty($connections)): ?>
<div class="empty-state" style="padding:2rem;">
<i class="fas fa-mobile-alt" aria-hidden="true"></i>
<p><?= $this->te('Noch keine Instanz eingerichtet.') ?></p>
</div>
<?php else: ?>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th scope="col"><?= $this->te('Name') ?></th>
<th scope="col"><?= $this->t('Server') ?></th>
<th scope="col"><?= $this->te('Geräte') ?></th>
<th scope="col"><?= $this->te('Letzter Abgleich') ?></th>
<th scope="col"><?= $this->te('Zustand') ?></th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<?php foreach ($connections as $c): ?>
<?php $s = (array)($c['settings'] ?? []); ?>
<tr>
<td>
<strong><?= $this->e($c['name']) ?></strong>
<?php if (empty($c['enabled'])): ?>
<span class="status-pill status-pill--neutral"><?= $this->te('aus') ?></span>
<?php endif; ?>
<?php if (empty($s['server_url']) || empty($s['access_token'])): ?>
<br><small class="text-danger"><?= $this->te('Zugangsdaten unvollständig – bitte bearbeiten') ?></small>
<?php endif; ?>
</td>
<td><small><?= $this->e((string)($s['server_url'] ?? '–')) ?></small></td>
<td><?= (int)$c['device_count'] ?></td>
<td>
<?php if (!empty($c['last_run_at'])): ?>
<?= date('d.m.Y H:i', strtotime((string)$c['last_run_at'])) ?>
<?php else: ?>
<small class="text-muted"><?= $this->te('noch nie') ?></small>
<?php endif; ?>
</td>
<td>
<?php if ($c['last_status'] === 'ok'): ?>
<span class="status-pill status-pill--success"><?= $this->e((string)$c['last_message']) ?></span>
<?php elseif ($c['last_status'] === 'error'): ?>
<span class="status-pill status-pill--danger"><?= $this->te('Fehler') ?></span><br>
<small class="text-muted"><?= $this->e((string)$c['last_message']) ?></small>
<?php else: ?>
<small class="text-muted">–</small>
<?php endif; ?>
</td>
<td style="white-space:nowrap;text-align:right;">
<button type="button" class="btn btn-default btn-sm" onclick="relSync(<?= (int)$c['id'] ?>, true)"><?= $this->te('Probelauf') ?></button>
<button type="button" class="btn btn-default btn-sm" onclick="relSync(<?= (int)$c['id'] ?>, false)"><?= $this->te('Abgleichen') ?></button>
<a href="/admin/inventory/relution/<?= (int)$c['id'] ?>/edit" class="btn btn-default btn-sm"><?= $this->te('Bearbeiten') ?></a>
<form method="POST" action="/admin/inventory/relution/<?= (int)$c['id'] ?>/delete" style="display:inline;"
onsubmit="return confirm('Instanz „<?= $this->e(addslashes($c['name'])) ?>“ entfernen? Die Geräte bleiben erhalten und sind danach wieder von Hand pflegbar.');">
<input type="hidden" name="_csrf" value="<?= $this->csrf() ?>">
<button type="submit" class="btn btn-danger btn-sm"><?= $this->te('Entfernen') ?></button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<div class="settings-card-body" style="display:flex;gap:.75rem;flex-wrap:wrap;align-items:center;">
<a href="/admin/inventory/relution/new" class="btn btn-primary"><?= $this->te('Instanz hinzufügen') ?></a>
<button type="button" class="btn btn-default" onclick="relSync(null, false)"><?= $this->te('Alle abgleichen') ?></button>
<?php if (!empty($last_sync)): ?>
<small class="text-muted"><?= $this->t('Zuletzt') ?>: <?= date('d.m.Y H:i', strtotime((string)$last_sync)) ?></small>
<?php endif; ?>
</div>
<div class="settings-card-body" id="rel-ergebnis" style="display:none;"></div>
</section>
<section class="settings-card">
<div class="settings-card-header">
<span class="settings-card-icon">🕐</span>
<div>
<h2><?= $this->te('Zeitplan') ?></h2>
<p class="settings-card-desc"><?= $this->te('Der regelmäßige Abgleich läuft über den gemeinsamen Zeitplan.') ?></p>
</div>
</div>
<div class="settings-card-body">
<p style="line-height:1.55;">
<?= $this->t('Die Aufgabe <code>relution_sync</code> gleicht alle <strong>eingeschalteten</strong> Instanzen nacheinander ab. Eine Instanz, die gerade nicht erreichbar ist, hält die übrigen nicht auf. Intervall und Ein-/Ausschalten unter <a href="/admin/cronjobs">Cronjobs</a>.') ?>
</p>
<p style="line-height:1.55;">
<?= $this->t('Geräte, die eine Instanz beim Abgleich nicht mehr meldet, werden ausgemustert – aber nur innerhalb <em>ihrer</em> Instanz. Die Geräte der übrigen bleiben unberührt, und auch die Sicherheitsgrenze für den Höchstanteil misst je Instanz.') ?>
</p>
</div>
</section>
</div>
<script>
/**
* Stösst einen Abgleich an. Ohne ID laufen alle eingeschalteten Instanzen.
*/
function relSync(id, dryRun) {
var ziel = id === null
? '/admin/inventory/relution/sync'
: '/admin/inventory/relution/' + id + (dryRun ? '/preview' : '/sync');
var kasten = document.getElementById('rel-ergebnis');
kasten.style.display = '';
kasten.innerHTML = '<em>Läuft…</em>';
fetch(ziel, {
method: 'POST',
headers: {
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content,
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(function (r) { return r.json(); })
.then(function (d) {
var teile = [];
teile.push('<strong>' + (d.dry_run ? 'Probelauf' : 'Abgleich') + ': '
+ (d.ok ? 'abgeschlossen' : 'mit Fehlern') + '</strong>');
if (d.instances) {
d.instances.forEach(function (i) {
teile.push('<div>' + esc(i.name) + ': ' + esc(i.message) + '</div>');
});
} else if (d.message) {
teile.push('<div>' + esc(d.message) + '</div>');
}
kasten.innerHTML = teile.join('');
if (!d.dry_run && d.ok) { setTimeout(function () { location.reload(); }, 1500); }
})
.catch(function () { kasten.innerHTML = '<span class="text-danger">Netzwerkfehler.</span>'; });
}
function esc(s) {
var d = document.createElement('div');
d.textContent = s === undefined || s === null ? '' : String(s);
return d.innerHTML;
}
</script>