| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 |
- <?php declare(strict_types = 0);
- /*
- ** WeCom Center - page view (three tabs: Alert / Optimization / Maintenance).
- ** Rendered as clean card HTML on top of Zabbix's page chrome.
- ** @var CView $this
- ** @var array $data
- */
- use Modules\WecomCenter\Lib\I18n;
- $this->addCssFile('modules/WecomCenter/assets/wecom.center.css');
- $e = function ($v) { return htmlspecialchars((string) ($v ?? ''), ENT_QUOTES, 'UTF-8'); };
- $t = function (string $k, array $a = []) use ($e) { return $e(I18n::t($k, $a)); };
- // Zabbix CTag escapes plain strings; wrap trusted, self-built markup so it renders as HTML.
- $raw = function (string $h) {
- return new class($h) {
- private $h;
- public function __construct(string $h) { $this->h = $h; }
- public function toString($destroy = true): string { return $this->h; }
- public function __toString(): string { return $this->h; }
- };
- };
- $save = $e((new CUrl('zabbix.php'))->setArgument('action', 'wecom.center.save')->getUrl());
- $view_url = (new CUrl('zabbix.php'))->setArgument('action', 'wecom.center.view')->getUrl();
- $mtid = $e($data['mediatypeid']);
- $active_tab = (string) ($_REQUEST['tab'] ?? 'alert');
- if (!in_array($active_tab, ['alert', 'opt', 'maint', 'l2'], true)) {
- $active_tab = 'alert';
- }
- $tabHref = function (string $id) use ($data) {
- return htmlspecialchars((new CUrl('zabbix.php'))
- ->setArgument('action', 'wecom.center.view')
- ->setArgument('mediatypeid', $data['mediatypeid'])
- ->setArgument('tab', $id)
- ->getUrl(), ENT_QUOTES, 'UTF-8');
- };
- $html_page = (new CHtmlPage())->setTitle(I18n::t('page.title'));
- if (!$data['mediatype']) {
- $initBody = '<div class="wc-hint">'.$t('init.sub').'</div>'
- .'<form method="post" action="'.$save.'">'
- .'<div class="wc-field"><label>'.$t('lbl.init_key').'</label>'
- .'<input class="wc-input wc-input--mono" type="text" name="key" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"></div>'
- .'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="init_env">'.$t('btn.init').'</button></div>'
- .'</form>';
- $initCard = '<div class="wc-card"><div class="wc-card__head"><span class="wc-card__ico">⚡</span>'
- .'<span class="wc-card__title">'.$t('init.title').'</span></div>'
- .'<div class="wc-card__body">'.$initBody.'</div></div>';
- $heroI = '<div class="wc-hero"><div class="wc-hero__title">'.$t('page.title').'</div>'
- .'<div class="wc-hero__sub">'.$t('init.sub').'</div></div>';
- $html_page->addItem($raw('<div class="wc-wrap">'.$heroI.$initCard.'</div>'));
- $html_page->show();
- return;
- }
- $mask = (int) $data['severity'];
- $enabled = ((int) $data['active'] === 0);
- /* helper to build a card */
- $card = function (string $ico, string $title, string $body, string $extra = '', string $sub = '') {
- return '<div class="wc-card'.$extra.'"><div class="wc-card__head"><span class="wc-card__ico">'.$ico.'</span>'
- .'<span class="wc-card__title">'.$title.'</span>'
- .($sub !== '' ? '<span class="wc-card__sub">'.$sub.'</span>' : '')
- .'</div><div class="wc-card__body">'.$body.'</div></div>';
- };
- /* ============================ TAB 1: ALERT ============================ */
- // -- robot --
- $robotBody =
- '<form method="post" action="'.$save.'">'
- .'<input type="hidden" name="mediatypeid" value="'.$mtid.'">'
- .'<input type="hidden" name="tab" value="alert">'
- .'<div class="wc-field"><label>'.$t('lbl.key').'</label>'
- .'<input class="wc-input wc-input--mono" type="text" name="webhook_url" value="'.$e($data['webhook_url']).'"></div>'
- .'<div class="wc-actions">'
- .'<button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_robot">'.$t('btn.save').'</button>'
- .'<button class="wc-btn wc-btn--ghost" type="submit" name="op" value="test_robot">'.$t('btn.test').'</button>'
- .'</div></form>';
- $robotCard = $card('🤖', $t('sec.robot'), $robotBody, '', $e($data['mediatype']['name'] ?? ''));
- // -- threshold --
- $sevChecks = '';
- for ($i = 1; $i <= 6; $i++) {
- $bit = 1 << ($i - 1);
- $chk = (($mask & $bit) !== 0) ? ' checked' : '';
- $sevChecks .= '<label class="wc-checkrow"><input type="checkbox" name="sev_'.$i.'"'.$chk.'> '
- .'<span class="wc-sev wc-sev-'.$i.'">'.$t('sev.'.$i).'</span></label>';
- }
- $thresholdBody =
- '<div class="wc-hint">'.$t('lbl.threshold_hint').'</div>'
- .'<form method="post" action="'.$save.'">'
- .'<input type="hidden" name="mediatypeid" value="'.$mtid.'">'
- .'<input type="hidden" name="tab" value="alert">'
- .'<div class="wc-field"><label class="wc-checkrow"><input type="checkbox" name="enabled"'.($enabled ? ' checked' : '').'> '.$t('lbl.alerting_on').'</label></div>'
- .'<div class="wc-field"><label>'.$t('lbl.severity').'</label><div class="wc-checks">'.$sevChecks.'</div></div>'
- .'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_alert">'.$t('btn.save').'</button></div>'
- .'</form>';
- $thresholdCard = $card('⚙️', $t('sec.threshold'), $thresholdBody);
- // -- strategy presets --
- $presetRows = '';
- foreach ($data['presets'] as $key => $p) {
- $is_cur = ((string) $data['current_strategy'] === (string) $key);
- $name = $t($p['label']).($is_cur ? ' <span class="wc-pill wc-pill--cur">'.$t('preset.current').'</span>' : '');
- $prevUrl = $e((new CUrl('zabbix.php'))
- ->setArgument('action', 'wecom.center.view')
- ->setArgument('mediatypeid', $data['mediatypeid'])
- ->setArgument('tab', 'alert')
- ->setArgument('preview_preset', $key)
- ->getUrl());
- $presetRows .=
- '<div class="wc-preset">'
- .'<div class="wc-preset__main"><div class="wc-preset__name">'.$name.'</div>'
- .'<div class="wc-preset__desc">'.$t($p['desc']).'</div></div>'
- .'<div class="wc-preset__act">'
- .'<a class="wc-btn wc-btn--ghost wc-btn--sm" href="'.$prevUrl.'">'.$t('btn.preview').'</a>'
- .'<form method="post" action="'.$save.'" style="margin:0"><input type="hidden" name="mediatypeid" value="'.$mtid.'"><input type="hidden" name="tab" value="alert">'
- .'<button class="wc-btn wc-btn--primary wc-btn--sm" type="submit" name="apply_strategy" value="'.$e($key).'">'.$t('btn.apply').'</button></form>'
- .'</div></div>';
- }
- $strategyBody = '<div class="wc-hint">'.$t('lbl.strategy_hint').'</div>'.$presetRows;
- $strategyCard = $card('🚨', $t('sec.strategy'), $strategyBody, ' wc-card--wide');
- // -- preview --
- $previewCard = '';
- if ($data['preview'] !== null) {
- $pv = $data['preview'];
- $pkey = $data['preview_key'];
- $rows = '';
- foreach ($pv['classes'] as $ck => $c) {
- $label = $t($data['classes'][$ck]['label'] ?? $ck);
- $rows .= '<tr><td>'.$label.'</td><td><span class="wc-sev wc-sev-'.((int) $c['target'] + 1).'">'.$t('sev.'.($c['target'] + 1)).'</span></td>'
- .'<td class="num">'.$e($c['count']).'</td><td class="num">'.$e($c['changes']).'</td></tr>';
- }
- $rows .= '<tr class="wc-tr-total"><td>'.$t('cls.total').'</td><td></td>'
- .'<td class="num">'.$e($pv['total']).'</td><td class="num">'.$e($pv['changes']).'</td></tr>';
- $tbl = '<table class="wc-table"><thead><tr><th>'.$t('cls.col.class').'</th><th>'.$t('cls.col.target').'</th>'
- .'<th class="num">'.$t('cls.col.count').'</th><th class="num">'.$t('cls.col.changes').'</th></tr></thead><tbody>'.$rows.'</tbody></table>';
- $body = '<div class="wc-hint">'.$t('preview.hint').'</div>'.$tbl;
- $previewCard = $card('🔍', $t('preview.title', [I18n::t($data['presets'][$pkey]['label'] ?? $pkey)]), $body, ' wc-card--wide');
- }
- $tab_alert = '<div class="wc-grid">'.$robotCard.$thresholdCard.$strategyCard.$previewCard.'</div>';
- /* ======================== TAB 2: OPTIMIZATION ========================= */
- // -- tuning --
- $tuneOpts = '';
- foreach (['small', 'medium', 'large'] as $pk) {
- $tuneOpts .= '<option value="'.$e($pk).'"'.(($data['tuning_profile'] === $pk) ? ' selected' : '').'>'.$t($pk === 'small' ? 'tuning.small' : ($pk === 'medium' ? 'tuning.medium' : 'tuning.large')).'</option>';
- }
- $tuneBody =
- '<div class="wc-hint">'.$t('lbl.tuning_hint').'</div>'
- .'<form method="get" action="zabbix.php"><input type="hidden" name="action" value="wecom.center.view">'
- .'<input type="hidden" name="mediatypeid" value="'.$mtid.'"><input type="hidden" name="tab" value="opt">'
- .'<div class="wc-inline"><div class="wc-field"><label>'.$t('tuning.choose').'</label>'
- .'<select class="wc-input" name="tuning_profile">'.$tuneOpts.'</select></div>'
- .'<button class="wc-btn wc-btn--primary" type="submit" name="generate" value="1">'.$t('btn.generate').'</button></div></form>'
- .'<div class="wc-code__label" style="margin-top:16px">'.$t('tuning.env').'</div>'
- .'<div class="wc-code">'.$e($data['tuning']['env']).'</div>'
- .'<div class="wc-code__label">'.$t('tuning.cmd').'</div>'
- .'<div class="wc-code">'.$e($data['tuning']['cmd']).'</div>';
- $tuningCard = $card('⚙️', $t('sec.tuning'), $tuneBody, ' wc-card--wide');
- // -- template import --
- $tplOpts = '';
- if (!$data['templates']) {
- $tplOpts = '<option value="">'.$t('tpl.none').'</option>';
- }
- foreach ($data['templates'] as $tf) {
- $tplOpts .= '<option value="'.$e($tf['file']).'">'.$e($tf['name']).'</option>';
- }
- $importBody =
- '<div class="wc-hint">'.$t('lbl.template_hint').'</div>'
- .'<form method="post" action="'.$save.'">'
- .'<div class="wc-field"><label>'.$t('tpl.choose').'</label><select class="wc-input" name="template_file">'.$tplOpts.'</select></div>'
- .'<input type="hidden" name="tab" value="opt">'
- .'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="import_template">'.$t('btn.import').'</button></div>'
- .'</form>';
- $importCard = $card('📦', $t('sec.template'), $importBody);
- // -- map --
- if ($data['server_host']) {
- $mapBody =
- '<div class="wc-hint">'.$t('map.hint').'</div>'
- .'<form method="post" action="'.$save.'">'
- .'<input type="hidden" name="hostid" value="'.$e($data['server_host']['hostid']).'">'
- .'<input type="hidden" name="tab" value="opt">'
- .'<div class="wc-field"><label>'.$t('map.host').'</label><div class="wc-muted">'.$e($data['server_host']['name']).'</div></div>'
- .'<div class="wc-inline">'
- .'<div class="wc-field"><label>'.$t('map.lat').'</label><input class="wc-input" type="text" name="lat" value="'.$e($data['server_loc']['location_lat']).'"></div>'
- .'<div class="wc-field"><label>'.$t('map.lon').'</label><input class="wc-input" type="text" name="lon" value="'.$e($data['server_loc']['location_lon']).'"></div>'
- .'</div>'
- .'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_map">'.$t('btn.save').'</button></div>'
- .'</form>';
- }
- else {
- $mapBody = '<div class="wc-empty">'.$t('msg.no_server_host').'</div>';
- }
- $mapCard = $card('🗺️', $t('sec.map'), $mapBody);
- // -- template switch --
- $srcOpts = '<option value="">'.$t('switch.pick').'</option>';
- $dstOpts = '<option value="">'.$t('switch.pick').'</option>';
- foreach ($data['huawei_tpls'] as $tp) {
- $selS = ((string) $data['switch_src'] === (string) $tp['templateid']) ? ' selected' : '';
- $selD = ((string) $data['switch_dst'] === (string) $tp['templateid']) ? ' selected' : '';
- $srcOpts .= '<option value="'.$e($tp['templateid']).'"'.$selS.'>'.$e($tp['host']).'</option>';
- $dstOpts .= '<option value="'.$e($tp['templateid']).'"'.$selD.'>'.$e($tp['host']).'</option>';
- }
- $switchBody =
- '<div class="wc-hint">'.$t('lbl.switch_hint').'</div>'
- .'<form method="get" action="zabbix.php"><input type="hidden" name="action" value="wecom.center.view">'
- .'<input type="hidden" name="mediatypeid" value="'.$mtid.'"><input type="hidden" name="tab" value="opt">'
- .'<div class="wc-inline">'
- .'<div class="wc-field"><label>'.$t('switch.src').'</label><select class="wc-input" name="switch_src">'.$srcOpts.'</select></div>'
- .'<div class="wc-field"><label>'.$t('switch.dst').'</label><select class="wc-input" name="switch_dst">'.$dstOpts.'</select></div>'
- .'<button class="wc-btn wc-btn--ghost" type="submit" name="preview" value="1">'.$t('btn.preview').'</button>'
- .'</div></form>';
- if ($data['switch_preview'] !== null) {
- $sp = $data['switch_preview'];
- if ((int) $sp['count'] === 0) {
- $switchBody .= '<div class="wc-divider"></div><div class="wc-empty">'.$t('switch.none', [$sp['src_name']]).'</div>';
- }
- else {
- $chips = '';
- foreach ($sp['hosts'] as $hn) {
- $chips .= '<span class="wc-chip">'.$e($hn).'</span>';
- }
- $switchBody .= '<div class="wc-divider"></div>'
- .'<div class="wc-muted">'.$t('switch.result', [$sp['count'], $sp['src_name'], $sp['dst_name']]).'</div>'
- .'<div class="wc-chips">'.$chips.'</div>'
- .'<form method="post" action="'.$save.'" style="margin-top:14px">'
- .'<input type="hidden" name="switch_src" value="'.$e($sp['src']).'">'
- .'<input type="hidden" name="switch_dst" value="'.$e($sp['dst']).'">'
- .'<input type="hidden" name="tab" value="opt">'
- .'<button class="wc-btn wc-btn--danger" type="submit" name="op" value="apply_switch">'.$t('btn.confirm').'</button>'
- .'</form>';
- }
- }
- $switchCard = $card('🔁', $t('sec.switch'), $switchBody, ' wc-card--wide');
- $initBody2 = '<div class="wc-hint">'.$t('lbl.init_hint').'</div>'
- .'<form method="post" action="'.$save.'">'
- .'<input type="hidden" name="tab" value="opt">'
- .'<div class="wc-field"><label>'.$t('lbl.init_key').'</label>'
- .'<input class="wc-input wc-input--mono" type="text" name="key" value="'.$e($data['webhook_url']).'"></div>'
- .'<div class="wc-actions"><button class="wc-btn wc-btn--ghost" type="submit" name="op" value="init_env">'.$t('btn.init').'</button></div>'
- .'</form>';
- $initCard2 = $card('⚡', $t('sec.init'), $initBody2);
- $tab_opt = '<div class="wc-grid">'.$tuningCard.$importCard.$mapCard.$initCard2.$switchCard.'</div>';
- /* ======================== TAB 3: MAINTENANCE ========================== */
- // -- by class: pick a class + a severity, apply to every trigger in that class --
- $classOpts = '';
- foreach ($data['classes'] as $ck => $c) {
- if ($ck === 'other') {
- continue;
- }
- $classOpts .= '<option value="'.$e($ck).'">'.$t($c['label']).'</option>';
- }
- $prioOpts = '';
- for ($p = 0; $p <= 5; $p++) {
- $prioOpts .= '<option value="'.$p.'"'.($p === 5 ? ' selected' : '').'>'.$t('sev.'.($p + 1)).'</option>';
- }
- $classBody =
- '<div class="wc-hint">'.$t('lbl.class_sev_hint').'</div>'
- .'<form method="post" action="'.$save.'"><input type="hidden" name="tab" value="maint">'
- .'<div class="wc-inline">'
- .'<div class="wc-field" style="flex:2;min-width:200px"><label>'.$t('lbl.class_pick').'</label><select class="wc-input" name="class_key">'.$classOpts.'</select></div>'
- .'<div class="wc-field" style="flex:1;min-width:140px"><label>'.$t('lbl.set_severity').'</label><select class="wc-input" name="class_priority">'.$prioOpts.'</select></div>'
- .'</div>'
- .'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_class_sev">'.$t('btn.apply').'</button></div>'
- .'</form>';
- $classCard = $card('🏷️', $t('sec.class_sev'), $classBody, ' wc-card--wide');
- // -- by single trigger: search, then adjust severity / enable per row --
- $searchBody =
- '<div class="wc-hint">'.$t('lbl.trig_hint', [$data['trigger_count']]).'</div>'
- .'<form method="get" action="zabbix.php"><input type="hidden" name="action" value="wecom.center.view">'
- .'<input type="hidden" name="mediatypeid" value="'.$mtid.'"><input type="hidden" name="tab" value="maint">'
- .'<div class="wc-inline"><div class="wc-field" style="flex:3"><input class="wc-input" type="text" name="trig_search" value="'.$e($data['trig_search']).'" placeholder="'.$t('lbl.trig_search').'"></div>'
- .'<button class="wc-btn wc-btn--primary" type="submit" name="search" value="1">'.$t('btn.search').'</button></div></form>';
- $maintBody = $searchBody;
- if ($data['trig_search'] !== '') {
- if ($data['triggers']) {
- $ids = [];
- $rows = '';
- foreach ($data['triggers'] as $tg) {
- $ids[] = $tg['triggerid'];
- $host = !empty($tg['hosts']) ? $tg['hosts'][0]['host'] : '';
- $chk = ((int) $tg['status'] === 0) ? ' checked' : '';
- $sevSel = '';
- for ($p = 0; $p <= 5; $p++) {
- $sevSel .= '<option value="'.$p.'"'.(((int) $tg['priority'] === $p) ? ' selected' : '').'>'.$t('sev.'.($p + 1)).'</option>';
- }
- $rows .= '<div class="wc-trow">'
- .'<label class="wc-tcheck"><input type="checkbox" name="trig_'.$e($tg['triggerid']).'"'.$chk.'> <span class="wc-tmain">'.($host !== '' ? $e($host).' — ' : '').$e($tg['description']).'</span></label>'
- .'<select class="wc-input" name="pse_'.$e($tg['triggerid']).'">'.$sevSel.'</select>'
- .'</div>';
- }
- $maintBody .= '<div class="wc-divider"></div>'
- .'<form method="post" action="'.$save.'">'
- .'<input type="hidden" name="trig_ids" value="'.$e(implode(',', $ids)).'">'
- .'<input type="hidden" name="tab" value="maint">'
- .'<input type="hidden" name="trig_search" value="'.$e($data['trig_search']).'">'
- .'<div class="wc-hint">'.$t('lbl.trig_edit_hint').'</div>'
- .'<div class="wc-field"><label>'.$t('lbl.trig_list').'</label>'.$rows.'</div>'
- .'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_triggers">'.$t('btn.save').'</button></div>'
- .'</form>';
- }
- else {
- $maintBody .= '<div class="wc-divider"></div><div class="wc-empty">'.$t('msg.no_trig_search').'</div>';
- }
- }
- $trigCard = $card('🔧', $t('sec.triggers'), $maintBody, ' wc-card--wide');
- $tab_maint = '<div class="wc-grid">'.$classCard.$trigCard.'</div>';
- /* ======================== TAB 4: L2 TOPOLOGY CONFIG ================== */
- $l2 = $data['l2'];
- // templates multi-select
- $tplSel = '';
- foreach ($data['l2_templates'] as $tn) {
- $sel = in_array($tn, (array) $l2['templates'], true) ? ' selected' : '';
- $tplSel .= '<option value="'.$e($tn).'"'.$sel.'>'.$e($tn).'</option>';
- }
- $collectBody =
- '<div class="wc-hint">'.$t('lbl.l2_templates_hint').'</div>'
- .'<div class="wc-field"><label>'.$t('lbl.l2_templates').'</label>'
- .'<select class="wc-input wc-input--multi" name="templates[]" multiple size="8">'.$tplSel.'</select></div>'
- .'<div class="wc-inline">'
- .'<div class="wc-field"><label>'.$t('lbl.l2_full').'</label><input class="wc-input" type="number" min="5" max="1440" step="5" name="full_interval_min" value="'.$e($l2['full_interval_min']).'"></div>'
- .'<div class="wc-field"><label>'.$t('lbl.l2_alarm').'</label><input class="wc-input" type="number" min="1" name="alarm_interval_min" value="'.$e($l2['alarm_interval_min']).'"></div>'
- .'</div>'
- .'<div class="wc-inline">'
- .'<div class="wc-field"><label>'.$t('lbl.l2_history_days').'</label><input class="wc-input" type="number" min="1" max="30" name="history_days" value="'.$e($l2['history_days']).'"></div>'
- .'</div>'
- .'<div class="wc-hint">'.$t('lbl.l2_history_hint').'</div>';
- $collectCard = $card('📡', $t('sec.l2_collect'), $collectBody, ' wc-card--wide');
- $displayBody =
- '<div class="wc-inline">'
- .'<div class="wc-field"><label>'.$t('lbl.l2_refresh').'</label><input class="wc-input" type="number" min="30" step="30" name="refresh_seconds" value="'.$e($l2['refresh_seconds']).'"></div>'
- .'<div class="wc-field"><label>'.$t('lbl.l2_root').'</label><input class="wc-input" type="text" name="root_label" value="'.$e($l2['root_label']).'"></div>'
- .'<div class="wc-field"><label>'.$t('lbl.l2_other').'</label><input class="wc-input" type="text" name="other_label" value="'.$e($l2['other_label']).'"></div>'
- .'</div>';
- $displayCard = $card('🖥️', $t('sec.l2_display'), $displayBody);
- $netLines = '';
- foreach ((array) ($l2['networks'] ?? []) as $nw) {
- $netLines .= ($nw['key'] ?? '').' | '.($nw['label'] ?? '').' | '.implode(',', (array) ($nw['keywords'] ?? [])).' | '.($nw['color'] ?? '#607D8B')."\n";
- }
- $networkBody =
- '<div class="wc-hint">'.$t('lbl.l2_networks_hint').'</div>'
- .'<div class="wc-field"><label>'.$t('lbl.l2_networks').'</label>'
- .'<textarea class="wc-input wc-input--mono" name="networks_text" rows="5" style="min-height:120px">'.$e(trim($netLines)).'</textarea></div>';
- $networkCard = $card('🌐', $t('sec.l2_networks'), $networkBody, ' wc-card--wide');
- $hier = is_array($l2['core_hierarchy'] ?? null) ? $l2['core_hierarchy'] : [];
- $matchSel = function (string $name, $value) use ($e) {
- $isRegex = ((string) $value === 'regex');
- return '<select class="wc-input" name="'.$e($name).'" style="max-width:120px">'
- .'<option value="contains"'.($isRegex ? '' : ' selected').'>contains</option>'
- .'<option value="regex"'.($isRegex ? ' selected' : '').'>regex</option></select>';
- };
- $hierRow = function (string $role, string $label, array $r) use ($e, $t, $matchSel) {
- return '<div class="wc-rule"><span class="wc-rule-label">'.$e($label).'</span>'
- .$matchSel('hier_'.$role.'_match', $r['match'] ?? 'contains')
- .'<input class="wc-input wc-input--mono" name="hier_'.$role.'_pattern" value="'.$e($r['pattern'] ?? '').'" placeholder="'.$t('lbl.core_pattern').'(留空 = 不显示)">'
- .'</div>';
- };
- $hierRows = '';
- foreach ([
- 'firewall' => '防火墙',
- 'behavior' => '行为管理',
- 'total_core' => '总核心'
- ] as $role => $label) {
- $hierRows .= $hierRow($role, $label, (array) ($hier[$role] ?? []));
- }
- $subRows = '';
- $defaultSubs = [
- ['key' => 'monitor', 'label' => '监控核心', 'net' => 'SURVE', 'match' => 'contains', 'pattern' => 'core surve'],
- ['key' => 'office', 'label' => '办公核心', 'net' => 'OFFICE', 'match' => 'contains', 'pattern' => 'core office'],
- ['key' => 'production', 'label' => '生产核心', 'net' => '', 'match' => 'contains', 'pattern' => '']
- ];
- $savedSubs = (array) ($hier['sub_cores'] ?? []);
- $savedByKey = [];
- foreach ($savedSubs as $sr) {
- $savedByKey[(string) ($sr['key'] ?? '')] = $sr;
- }
- $subDefs = [];
- foreach ($defaultSubs as $d) {
- $r = $savedByKey[$d['key']] ?? $d;
- $r['label'] = $d['label'];
- $subDefs[] = $r;
- unset($savedByKey[$d['key']]);
- }
- foreach ($savedByKey as $extra) {
- $subDefs[] = $extra;
- }
- foreach ($subDefs as $i => $r) {
- $netSel = '<select class="wc-input" name="sub_net[]" style="max-width:190px">'
- .'<option value="">'.$t('lbl.core_any').'</option>';
- foreach ((array) $l2['networks'] as $nw) {
- $k = (string) ($nw['key'] ?? '');
- $netSel .= '<option value="'.$e($k).'"'.((string) ($r['net'] ?? '') === $k ? ' selected' : '').'>'.$e($k).' · '.$e($nw['label'] ?? $k).'</option>';
- }
- $netSel .= '</select>';
- $subRows .= '<div class="wc-rule"><span class="wc-rule-label">'.$e($r['label'] ?? ('核心 '.($i + 1))).'</span>'
- .'<input type="hidden" name="sub_key[]" value="'.$e($r['key'] ?? ('sub'.($i + 1))).'">'
- .'<input type="hidden" name="sub_label[]" value="'.$e($r['label'] ?? ('核心 '.($i + 1))).'">'
- .$netSel
- .$matchSel('sub_match[]', $r['match'] ?? 'contains')
- .'<input class="wc-input wc-input--mono" name="sub_pattern[]" value="'.$e($r['pattern'] ?? '').'" placeholder="'.$t('lbl.core_pattern').'(留空 = 不显示)">'
- .'</div>';
- }
- $coreBody =
- '<div class="wc-hint">'.$t('lbl.l2_cores_hint').'</div>'
- .'<div class="wc-rules">'.$hierRows.'</div>'
- .'<div class="wc-divider"></div>'
- .'<div class="wc-hint">'.$t('lbl.l2_subcores_hint').'</div>'
- .'<div class="wc-rules">'.$subRows.'</div>';
- $coreCard = $card('🔑', $t('sec.l2_cores'), $coreBody, ' wc-card--wide');
- $colorFields = '';
- foreach (['100' => 'lbl.color_100', '1000' => 'lbl.color_1000', '2500' => 'lbl.color_2500', '10000' => 'lbl.color_10000',
- '20000' => 'lbl.color_20000', 'unknown' => 'lbl.color_unknown', 'down' => 'lbl.color_down'] as $ck => $lab) {
- $colorFields .= '<div class="wc-field" style="flex:1;min-width:110px"><label>'.$t($lab).'</label>'
- .'<input class="wc-input wc-input--color" type="color" name="color_'.$ck.'" value="'.$e($l2['colors'][$ck] ?? '#000000').'"></div>';
- }
- $colorCard = $card('🎨', $t('sec.l2_colors'), '<div class="wc-inline">'.$colorFields.'</div>', ' wc-card--wide');
- $latBody =
- '<div class="wc-inline">'
- .'<div class="wc-field"><label>'.$t('lbl.lat_warn').'</label><input class="wc-input" type="number" step="0.1" min="0" name="latency_warn" value="'.$e($l2['latency_warn']).'"></div>'
- .'<div class="wc-field"><label>'.$t('lbl.lat_bad').'</label><input class="wc-input" type="number" step="0.1" min="0" name="latency_bad" value="'.$e($l2['latency_bad']).'"></div>'
- .'</div>';
- $latCard = $card('⏱️', $t('sec.l2_latency'), $latBody);
- $ruleRowHtml = function (array $r) use ($e, $t) {
- $isRegex = (($r['match'] ?? 'contains') === 'regex');
- $big = !empty($r['big']);
- return '<div class="wc-rule">'
- .'<select class="wc-input" name="rule_match[]" style="max-width:120px">'
- .'<option value="contains"'.($isRegex ? '' : ' selected').'>contains</option>'
- .'<option value="regex"'.($isRegex ? ' selected' : '').'>regex</option></select>'
- .'<input class="wc-input wc-input--mono" name="rule_pattern[]" value="'.$e($r['pattern'] ?? '').'" placeholder="e.g. 4401">'
- .'<input class="wc-input" name="rule_label[]" value="'.$e($r['label'] ?? '').'" placeholder="Frame label">'
- .'<select class="wc-input" name="rule_big[]" style="max-width:90px">'
- .'<option value="0"'.($big ? '' : ' selected').'>'.$t('lbl.rule_big').' −</option>'
- .'<option value="1"'.($big ? ' selected' : '').'>'.$t('lbl.rule_big').' +</option></select>'
- .'<button type="button" class="wc-btn wc-btn--ghost wc-rule-del" title="remove">×</button>'
- .'</div>';
- };
- $ruleRows = '';
- foreach ((array) $l2['group_rules'] as $r) {
- $ruleRows .= $ruleRowHtml($r);
- }
- $groupBody =
- '<div class="wc-hint">'.$t('lbl.rule_hint').'</div>'
- .'<div class="wc-rules" id="wc-rules">'.$ruleRows.'</div>'
- .'<div class="wc-actions"><button type="button" class="wc-btn wc-btn--ghost" id="wc-rule-add">'.$t('btn.add_rule').'</button></div>'
- .'<script type="text/template" id="wc-rule-tpl">'.$ruleRowHtml(['match' => 'contains', 'pattern' => '', 'label' => '', 'big' => false]).'</script>';
- $groupCard = $card('📁', $t('sec.l2_group'), $groupBody, ' wc-card--wide');
- $l2InitBody = '<div class="wc-hint">'.$t('lbl.l2_init_hint').'</div>'
- .'<form method="post" action="'.$save.'">'
- .'<input type="hidden" name="op" value="init_l2"><input type="hidden" name="tab" value="l2">'
- .'<div class="wc-actions"><button class="wc-btn wc-btn--ghost" type="submit">'.$t('btn.l2_init').'</button></div>'
- .'</form>';
- $l2InitCard = $card('⚙️', $t('sec.l2_init'), $l2InitBody, ' wc-card--wide');
- $tab_l2 = $l2InitCard
- .'<form method="post" action="'.$save.'" id="wc-l2-form">'
- .'<input type="hidden" name="op" value="save_l2"><input type="hidden" name="tab" value="l2">'
- .'<input type="hidden" name="core_hierarchy_present" value="1">'
- .'<div class="wc-grid">'.$collectCard.$displayCard.$networkCard.$coreCard.$colorCard.$latCard.$groupCard.'</div>'
- .'<div class="wc-actions" style="margin-top:18px"><button class="wc-btn wc-btn--primary" type="submit">'.$t('btn.save').'</button></div>'
- .'</form>'
- .'<script>(function(){function wire(addId,boxId,tplId,allowEmpty){var add=document.getElementById(addId),box=document.getElementById(boxId),tpl=document.getElementById(tplId);'
- .'if(add&&box&&tpl){add.addEventListener("click",function(){var w=document.createElement("div");w.innerHTML=tpl.innerHTML.trim();box.appendChild(w.firstChild);});}'
- .'if(box){box.addEventListener("click",function(ev){if(ev.target.classList.contains("wc-rule-del")){var r=ev.target.closest(".wc-rule");if(r&&(allowEmpty||box.querySelectorAll(".wc-rule").length>1)){r.remove();}}});}}'
- .'wire("wc-rule-add","wc-rules","wc-rule-tpl",false);wire("wc-core-add","wc-cores","wc-core-tpl",true);'
- .'})();</script>';
- /* ============================ assemble =============================== */
- $hero = '<div class="wc-hero"><div class="wc-hero__title">'.$t('page.title').'</div>'
- .'<div class="wc-hero__sub">'.$t('hero.sub').'</div></div>';
- $tabbar = '<div class="wc-tabs">'
- .'<a class="wc-tab'.($active_tab === 'alert' ? ' is-active' : '').'" href="'.$tabHref('alert').'">'.$t('tab.alert').'</a>'
- .'<a class="wc-tab'.($active_tab === 'opt' ? ' is-active' : '').'" href="'.$tabHref('opt').'">'.$t('tab.opt').'</a>'
- .'<a class="wc-tab'.($active_tab === 'maint' ? ' is-active' : '').'" href="'.$tabHref('maint').'">'.$t('tab.maint').'</a>'
- .'<a class="wc-tab'.($active_tab === 'l2' ? ' is-active' : '').'" href="'.$tabHref('l2').'">'.$t('tab.l2').'</a>'
- .'</div>';
- $content = ($active_tab === 'opt') ? $tab_opt : (($active_tab === 'maint') ? $tab_maint : (($active_tab === 'l2') ? $tab_l2 : $tab_alert));
- $html_page->addItem($raw('<div class="wc-wrap">'.$hero.$tabbar.$content.'</div>'));
- $html_page->show();
|