OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

controls.php

12 KB
<?php
$this->layout('layouts/main', [
    'page_title'    => $page_title,
    'current_route' => $current_route,
    'full_width'    => true,
    'extra_css'     => ['/assets/css/isms.css?v=' . filemtime(dirname(__DIR__, 2) . '/public/assets/css/isms.css')],
]);

use OpenNIT\Isms\IsmsManager;

$controls    = $controls ?? [];
$stats       = $stats ?? ['total' => 0, 'applicable' => 0, 'implemented' => 0, 'in_progress' => 0, 'coverage' => 0];
$activeTheme = $active_theme ?? '';
$users       = $users ?? [];
$pages       = $pages ?? [];
$canManage   = $can_manage ?? false;
$view        = $this;

$statusClass = [
    'not_implemented' => 'status-pill--danger',
    'in_progress'     => 'status-pill--warning',
    'implemented'     => 'status-pill--success',
    'not_applicable'  => 'status-pill--neutral',
];
?>

<div class="ism-wrap">
    <div class="content-header">
        <div>
            <h1><?= $this->te('Statement of Applicability') ?></h1>
            <ol class="breadcrumb">
                <li class="breadcrumb-item"><a href="/"><?= $this->te('Home') ?></a></li>
                <li class="breadcrumb-item"><a href="/isms">ISMS</a></li>
                <li class="breadcrumb-item active"><?= $this->te('Annex A (SoA)') ?></li>
            </ol>
        </div>
        <div class="content-header-actions">
            <a class="btn btn-default btn-sm" href="/isms/export/soa.csv" title="<?= $this->te('SoA als CSV') ?>"><i class="fas fa-file-csv"></i> <?= $this->te('CSV-Export') ?></a>
        </div>
    </div>

    <div class="page-body">

        <div class="kpi-row">
            <div class="kpi-col">
                <div class="small-box bg-info">
                    <div class="inner"><h3><?= (int)$stats['total'] ?></h3><p><?= $this->te('Controls (ISO 27001:2022)') ?></p></div>
                    <div class="icon"><i class="fas fa-tasks"></i></div>
                </div>
            </div>
            <div class="kpi-col">
                <div class="small-box bg-primary">
                    <div class="inner"><h3><?= (int)$stats['applicable'] ?></h3><p><?= $this->te('Anwendbar') ?></p></div>
                    <div class="icon"><i class="fas fa-check-double"></i></div>
                </div>
            </div>
            <div class="kpi-col">
                <div class="small-box bg-warning">
                    <div class="inner"><h3><?= (int)$stats['in_progress'] ?></h3><p><?= $this->te('In Umsetzung') ?></p></div>
                    <div class="icon"><i class="fas fa-spinner"></i></div>
                </div>
            </div>
            <div class="kpi-col">
                <div class="small-box bg-success">
                    <div class="inner"><h3><?= (int)$stats['coverage'] ?>%</h3><p><?= $this->te('Umgesetzt') ?></p></div>
                    <div class="icon"><i class="fas fa-shield-alt"></i></div>
                </div>
            </div>
        </div>

        <div class="subnav">
            <a href="/isms" class="subnav-tab"><?= $this->te('Risikoregister') ?></a>
            <a href="/isms/controls" class="subnav-tab active"><?= $this->te('Statement of Applicability (Annex A)') ?></a>
            <a href="/isms/reviews" class="subnav-tab"><?= $this->te('Management-Review') ?></a>
            <a href="/isms/guide" class="subnav-tab"><?= $this->te('Anleitung') ?></a>
        </div>

        <!-- Themen-Filter (Styleguide § 8) -->
        <div class="card card-outline card-primary mb-3">
            <div class="filter-card-body ism-toolbar">
                <a href="/isms/controls" class="ism-tag-chip <?= $activeTheme === '' ? 'active' : '' ?>"><?= $this->te('Alle Themen') ?></a>
                <?php foreach (IsmsManager::THEMES as $key => $label): ?>
                    <a href="/isms/controls?theme=<?= urlencode($key) ?>" class="ism-tag-chip <?= $activeTheme === $key ? 'active' : '' ?>"><?= $view->e($label) ?></a>
                <?php endforeach; ?>
            </div>
        </div>

        <div class="table-wrapper">
            <table class="data-table">
                <thead>
                    <tr>
                        <th scope="col"><?= $this->te('Ref') ?></th>
                        <th scope="col"><?= $this->te('Control') ?></th>
                        <th scope="col"><?= $this->te('Thema') ?></th>
                        <th scope="col"><?= $this->te('Anwendbar') ?></th>
                        <th scope="col"><?= $this->te('Status') ?></th>
                        <th scope="col"><?= $this->te('Owner') ?></th>
                        <?php if ($canManage): ?><th scope="col"></th><?php endif; ?>
                    </tr>
                </thead>
                <tbody>
                <?php foreach ($controls as $c): ?>
                    <tr id="<?= $view->e($c['ref']) ?>">
                        <td><strong><?= $view->e($c['ref']) ?></strong></td>
                        <td>
                            <?= $view->e($c['title']) ?>
                            <?php if (!empty($c['justification'])): ?><div class="ism-host-sub" title="<?= $this->te('Begründung') ?>"><?= $view->e($c['justification']) ?></div><?php endif; ?>
                            <?php if (!empty($c['policy_page_id'])): ?><div class="ism-host-sub"><i class="fas fa-file-alt"></i> <a href="/pages/<?= (int)$c['policy_page_id'] ?>" class="table-link"><?= $view->e($c['policy_page_title'] ?: 'Richtlinie') ?></a></div><?php endif; ?>
                        </td>
                        <td><span class="ism-host-sub"><?= $view->e(IsmsManager::THEMES[$c['theme']] ?? $c['theme']) ?></span></td>
                        <td><?= (int)$c['applicable'] === 1 ? '<span class="status-pill status-pill--success">Ja</span>' : '<span class="status-pill status-pill--neutral">Nein</span>' ?></td>
                        <td><span class="status-pill <?= $statusClass[$c['status']] ?? 'status-pill--neutral' ?>"><?= $view->e(IsmsManager::CONTROL_STATUS[$c['status']] ?? $c['status']) ?></span></td>
                        <td><?= $view->e($c['owner_name'] ?: '—') ?></td>
                        <?php if ($canManage): ?>
                        <td>
                            <button type="button" class="btn btn-default btn-xs"
                                onclick='ismEditControl(<?= json_encode([
                                    "id" => (int)$c["id"],
                                    "ref" => $c["ref"],
                                    "title" => $c["title"],
                                    "applicable" => (int)$c["applicable"],
                                    "status" => $c["status"],
                                    "justification" => $c["justification"] ?? "",
                                    "notes" => $c["notes"] ?? "",
                                    "owner_user_id" => (int)($c["owner_user_id"] ?? 0),
                                    "policy_page_id" => (int)($c["policy_page_id"] ?? 0),
                                ], JSON_HEX_APOS | JSON_HEX_QUOT) ?>)'><?= $this->te('Bearbeiten') ?></button>
                        </td>
                        <?php endif; ?>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
        </div>
    </div>
</div>

<?php if ($canManage): ?>
<!-- Shared Edit-Modal -->
<div id="ismControlModal" class="modal-overlay" role="dialog" aria-modal="true" style="display:none;" data-modal>
    <div class="modal-card">
        <div class="modal-header">
            <h2 id="ismControlTitle"><?= $this->te('Control bearbeiten') ?></h2>
            <button type="button" class="modal-close" onclick="document.getElementById('ismControlModal').style.display='none'" aria-label="<?= $this->te('Schließen') ?>">&times;</button>
        </div>
        <form method="post" id="ismControlForm">
            <input type="hidden" name="_csrf" value="<?= $view->e($csrf_token) ?>">
            <div class="modal-body">
                <div class="ism-form-row">
                    <div class="form-group ism-form-col">
                        <label for="c-applicable"><?= $this->te('Anwendbar (SoA)') ?></label>
                        <select id="c-applicable" name="applicable" class="form-control">
                            <option value="1"><?= $this->te('Ja') ?></option>
                            <option value="0"><?= $this->te('Nein') ?></option>
                        </select>
                    </div>
                    <div class="form-group ism-form-col">
                        <label for="c-status"><?= $this->te('Umsetzungsstatus') ?></label>
                        <select id="c-status" name="status" class="form-control">
                            <?php foreach (IsmsManager::CONTROL_STATUS as $key => $lbl): ?>
                                <option value="<?= $view->e($key) ?>"><?= $view->e($lbl) ?></option>
                            <?php endforeach; ?>
                        </select>
                    </div>
                </div>
                <div class="form-group">
                    <label for="c-owner"><?= $this->te('Verantwortlich') ?></label>
                    <select id="c-owner" name="owner_user_id" class="form-control">
                        <option value=""><?= $this->te('— Owner —') ?></option>
                        <?php foreach ($users as $u): ?>
                            <option value="<?= (int)$u['id'] ?>"><?= $view->e($u['display_name']) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="form-group">
                    <label for="c-just"><?= $this->te('Begründung (SoA)') ?></label>
                    <textarea id="c-just" name="justification" class="form-control" rows="2" placeholder="<?= $this->te('Warum ist dieses Control (nicht) anwendbar?') ?>"></textarea>
                </div>
                <div class="form-group">
                    <label for="c-notes"><?= $this->te('Umsetzungsnachweis / Notiz') ?></label>
                    <textarea id="c-notes" name="notes" class="form-control" rows="2"></textarea>
                </div>
                <?php if (!empty($pages)): ?>
                <div class="form-group">
                    <label for="c-page"><?= $this->te('Richtlinien-Seite (Nachweis)') ?></label>
                    <select id="c-page" name="policy_page_id" class="form-control">
                        <option value=""><?= $this->te('— keine —') ?></option>
                        <?php foreach ($pages as $p): ?>
                            <option value="<?= (int)$p['id'] ?>"><?= $view->e($p['title']) ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <?php endif; ?>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" onclick="document.getElementById('ismControlModal').style.display='none'"><?= $this->te('Abbrechen') ?></button>
                <button type="submit" class="btn btn-primary"><?= $this->te('Speichern') ?></button>
            </div>
        </form>
    </div>
</div>

<script>
function ismEditControl(c) {
    var form = document.getElementById('ismControlForm');
    form.action = '/isms/controls/' + c.id;
    document.getElementById('ismControlTitle').textContent = c.ref + ' · ' + c.title;
    document.getElementById('c-applicable').value = String(c.applicable);
    document.getElementById('c-status').value = c.status;
    document.getElementById('c-owner').value = c.owner_user_id ? String(c.owner_user_id) : '';
    var pageSel = document.getElementById('c-page');
    if (pageSel) pageSel.value = c.policy_page_id ? String(c.policy_page_id) : '';
    document.getElementById('c-just').value = c.justification || '';
    document.getElementById('c-notes').value = c.notes || '';
    document.getElementById('ismControlModal').style.display = 'flex';
}

// Modal schließen bei Klick auf den Backdrop oder Escape
(function () {
    var modal = document.getElementById('ismControlModal');
    if (!modal) return;
    modal.addEventListener('click', function (e) { if (e.target === modal) modal.style.display = 'none'; });
    document.addEventListener('keydown', function (e) { if (e.key === 'Escape') modal.style.display = 'none'; });
})();
</script>
<?php endif; ?>