Procházet zdrojové kódy

fix(WecomCenter): replace CTabView with self-contained tab bar; render raw HTML unescaped (custom RawHtml wrapper); tab param preserved across GET/POST

DSH Agent před 4 dny
rodič
revize
43f0349c09

+ 4 - 0
web-modules/WecomCenter/actions/WecomCenterSave.php

@@ -57,6 +57,10 @@ class WecomCenterSave extends CController {
 		if ($mediatypeid !== '') {
 		if ($mediatypeid !== '') {
 			$url->setArgument('mediatypeid', $mediatypeid);
 			$url->setArgument('mediatypeid', $mediatypeid);
 		}
 		}
+		$tab = (string) ($_REQUEST['tab'] ?? '');
+		if (in_array($tab, ['alert', 'opt', 'maint'], true)) {
+			$url->setArgument('tab', $tab);
+		}
 		$this->setResponse(new CControllerResponseRedirect($url));
 		$this->setResponse(new CControllerResponseRedirect($url));
 	}
 	}
 
 

+ 10 - 0
web-modules/WecomCenter/assets/wecom.center.css

@@ -109,3 +109,13 @@ select.wc-input { appearance: auto; }
 .wc-empty { color: #808793; font-size: 13px; padding: 10px 0; }
 .wc-empty { color: #808793; font-size: 13px; padding: 10px 0; }
 .wc-inline { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
 .wc-inline { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; }
 .wc-inline .wc-field { flex: 1; min-width: 160px; margin-bottom: 0; }
 .wc-inline .wc-field { flex: 1; min-width: 160px; margin-bottom: 0; }
+
+/* tab bar */
+.wc-tabs { display: flex; gap: 4px; margin: 0 0 18px; border-bottom: 2px solid #e6eaef; }
+.wc-tab {
+	display: inline-block; padding: 10px 22px; font-size: 14px; font-weight: 600;
+	color: #667080; text-decoration: none !important; border-bottom: 2px solid transparent;
+	margin-bottom: -2px; border-radius: 8px 8px 0 0;
+}
+.wc-tab:hover { color: #2f6fdd; background: #f2f6ff; }
+.wc-tab.is-active { color: #2f6fdd; border-bottom-color: #2f6fdd; background: #eef4ff; }

+ 42 - 11
web-modules/WecomCenter/views/wecom.center.view.php

@@ -12,16 +12,37 @@ $this->addCssFile('modules/WecomCenter/assets/wecom.center.css');
 
 
 $e = function ($v) { return htmlspecialchars((string) ($v ?? ''), ENT_QUOTES, 'UTF-8'); };
 $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)); };
 $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());
 $save = $e((new CUrl('zabbix.php'))->setArgument('action', 'wecom.center.save')->getUrl());
 $view_url = (new CUrl('zabbix.php'))->setArgument('action', 'wecom.center.view')->getUrl();
 $view_url = (new CUrl('zabbix.php'))->setArgument('action', 'wecom.center.view')->getUrl();
 $mtid = $e($data['mediatypeid']);
 $mtid = $e($data['mediatypeid']);
 
 
+$active_tab = (string) ($_REQUEST['tab'] ?? 'alert');
+if (!in_array($active_tab, ['alert', 'opt', 'maint'], 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'));
 $html_page = (new CHtmlPage())->setTitle(I18n::t('page.title'));
 
 
 if (!$data['mediatype']) {
 if (!$data['mediatype']) {
-	$html_page->addItem('<div class="wc-wrap"><div class="wc-card"><div class="wc-card__body">'
-		.$t('msg.no_mediatype').'</div></div></div>');
+	$html_page->addItem($raw('<div class="wc-wrap"><div class="wc-card"><div class="wc-card__body">'
+		.$t('msg.no_mediatype').'</div></div></div>'));
 	$html_page->show();
 	$html_page->show();
 	return;
 	return;
 }
 }
@@ -43,6 +64,7 @@ $card = function (string $ico, string $title, string $body, string $extra = '',
 $robotBody =
 $robotBody =
 	'<form method="post" action="'.$save.'">'
 	'<form method="post" action="'.$save.'">'
 	.'<input type="hidden" name="mediatypeid" value="'.$mtid.'">'
 	.'<input type="hidden" name="mediatypeid" value="'.$mtid.'">'
+	.'<input type="hidden" name="tab" value="alert">'
 	.'<div class="wc-field"><label>'.$t('lbl.key').'</label>'
 	.'<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>'
 	.'<input class="wc-input wc-input--mono" type="text" name="webhook_url" value="'.$e($data['webhook_url']).'"></div>'
 	.'<div class="wc-actions">'
 	.'<div class="wc-actions">'
@@ -63,6 +85,7 @@ $thresholdBody =
 	'<div class="wc-hint">'.$t('lbl.threshold_hint').'</div>'
 	'<div class="wc-hint">'.$t('lbl.threshold_hint').'</div>'
 	.'<form method="post" action="'.$save.'">'
 	.'<form method="post" action="'.$save.'">'
 	.'<input type="hidden" name="mediatypeid" value="'.$mtid.'">'
 	.'<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 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-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>'
 	.'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_alert">'.$t('btn.save').'</button></div>'
@@ -77,6 +100,7 @@ foreach ($data['presets'] as $key => $p) {
 	$prevUrl = $e((new CUrl('zabbix.php'))
 	$prevUrl = $e((new CUrl('zabbix.php'))
 		->setArgument('action', 'wecom.center.view')
 		->setArgument('action', 'wecom.center.view')
 		->setArgument('mediatypeid', $data['mediatypeid'])
 		->setArgument('mediatypeid', $data['mediatypeid'])
+		->setArgument('tab', 'alert')
 		->setArgument('preview_preset', $key)
 		->setArgument('preview_preset', $key)
 		->getUrl());
 		->getUrl());
 	$presetRows .=
 	$presetRows .=
@@ -85,7 +109,7 @@ foreach ($data['presets'] as $key => $p) {
 		.'<div class="wc-preset__desc">'.$t($p['desc']).'</div></div>'
 		.'<div class="wc-preset__desc">'.$t($p['desc']).'</div></div>'
 		.'<div class="wc-preset__act">'
 		.'<div class="wc-preset__act">'
 		.'<a class="wc-btn wc-btn--ghost wc-btn--sm" href="'.$prevUrl.'">'.$t('btn.preview').'</a>'
 		.'<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.'">'
+		.'<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>'
 		.'<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>';
 		.'</div></div>';
 }
 }
@@ -123,7 +147,7 @@ foreach (['small', 'medium', 'large'] as $pk) {
 $tuneBody =
 $tuneBody =
 	'<div class="wc-hint">'.$t('lbl.tuning_hint').'</div>'
 	'<div class="wc-hint">'.$t('lbl.tuning_hint').'</div>'
 	.'<form method="get" action="zabbix.php"><input type="hidden" name="action" value="wecom.center.view">'
 	.'<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="mediatypeid" value="'.$mtid.'"><input type="hidden" name="tab" value="opt">'
 	.'<div class="wc-inline"><div class="wc-field"><label>'.$t('tuning.choose').'</label>'
 	.'<div class="wc-inline"><div class="wc-field"><label>'.$t('tuning.choose').'</label>'
 	.'<select class="wc-input" name="tuning_profile">'.$tuneOpts.'</select></div>'
 	.'<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>'
 	.'<button class="wc-btn wc-btn--primary" type="submit" name="generate" value="1">'.$t('btn.generate').'</button></div></form>'
@@ -145,6 +169,7 @@ $importBody =
 	'<div class="wc-hint">'.$t('lbl.template_hint').'</div>'
 	'<div class="wc-hint">'.$t('lbl.template_hint').'</div>'
 	.'<form method="post" action="'.$save.'">'
 	.'<form method="post" action="'.$save.'">'
 	.'<div class="wc-field"><label>'.$t('tpl.choose').'</label><select class="wc-input" name="template_file">'.$tplOpts.'</select></div>'
 	.'<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>'
 	.'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="import_template">'.$t('btn.import').'</button></div>'
 	.'</form>';
 	.'</form>';
 $importCard = $card('&#128230;', $t('sec.template'), $importBody);
 $importCard = $card('&#128230;', $t('sec.template'), $importBody);
@@ -155,6 +180,7 @@ if ($data['server_host']) {
 		'<div class="wc-hint">'.$t('map.hint').'</div>'
 		'<div class="wc-hint">'.$t('map.hint').'</div>'
 		.'<form method="post" action="'.$save.'">'
 		.'<form method="post" action="'.$save.'">'
 		.'<input type="hidden" name="hostid" value="'.$e($data['server_host']['hostid']).'">'
 		.'<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-field"><label>'.$t('map.host').'</label><div class="wc-muted">'.$e($data['server_host']['name']).'</div></div>'
 		.'<div class="wc-inline">'
 		.'<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.lat').'</label><input class="wc-input" type="text" name="lat" value="'.$e($data['server_loc']['location_lat']).'"></div>'
@@ -180,7 +206,7 @@ foreach ($data['huawei_tpls'] as $tp) {
 $switchBody =
 $switchBody =
 	'<div class="wc-hint">'.$t('lbl.switch_hint').'</div>'
 	'<div class="wc-hint">'.$t('lbl.switch_hint').'</div>'
 	.'<form method="get" action="zabbix.php"><input type="hidden" name="action" value="wecom.center.view">'
 	.'<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="mediatypeid" value="'.$mtid.'"><input type="hidden" name="tab" value="opt">'
 	.'<div class="wc-inline">'
 	.'<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.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>'
 	.'<div class="wc-field"><label>'.$t('switch.dst').'</label><select class="wc-input" name="switch_dst">'.$dstOpts.'</select></div>'
@@ -203,6 +229,7 @@ if ($data['switch_preview'] !== null) {
 			.'<form method="post" action="'.$save.'" style="margin-top:14px">'
 			.'<form method="post" action="'.$save.'" style="margin-top:14px">'
 			.'<input type="hidden" name="switch_src" value="'.$e($sp['src']).'">'
 			.'<input type="hidden" name="switch_src" value="'.$e($sp['src']).'">'
 			.'<input type="hidden" name="switch_dst" value="'.$e($sp['dst']).'">'
 			.'<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>'
 			.'<button class="wc-btn wc-btn--danger" type="submit" name="op" value="apply_switch">'.$t('btn.confirm').'</button>'
 			.'</form>';
 			.'</form>';
 	}
 	}
@@ -216,7 +243,7 @@ $tab_opt = '<div class="wc-grid">'.$tuningCard.$importCard.$mapCard.$switchCard.
 $searchBody =
 $searchBody =
 	'<div class="wc-hint">'.$t('lbl.trig_hint', [$data['trigger_count']]).'</div>'
 	'<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">'
 	.'<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="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>'
 	.'<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>';
 	.'<button class="wc-btn wc-btn--primary" type="submit" name="search" value="1">'.$t('btn.search').'</button></div></form>';
 $maintBody = $searchBody;
 $maintBody = $searchBody;
@@ -238,6 +265,7 @@ if ($data['trig_search'] !== '') {
 		$maintBody .= '<div class="wc-divider"></div>'
 		$maintBody .= '<div class="wc-divider"></div>'
 			.'<form method="post" action="'.$save.'">'
 			.'<form method="post" action="'.$save.'">'
 			.'<input type="hidden" name="trig_ids" value="'.$e(implode(',', $ids)).'">'
 			.'<input type="hidden" name="trig_ids" value="'.$e(implode(',', $ids)).'">'
+			.'<input type="hidden" name="tab" value="maint">'
 			.'<div class="wc-field"><label>'.$t('lbl.trig_list').'</label>'.$list.'</div>'
 			.'<div class="wc-field"><label>'.$t('lbl.trig_list').'</label>'.$list.'</div>'
 			.'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_triggers">'.$t('btn.save').'</button></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>';
 			.'</form>';
@@ -252,10 +280,13 @@ $tab_maint = '<div class="wc-grid">'.$card('&#128295;', $t('sec.triggers'), $mai
 $hero = '<div class="wc-hero"><div class="wc-hero__title">'.$t('page.title').'</div>'
 $hero = '<div class="wc-hero"><div class="wc-hero__title">'.$t('page.title').'</div>'
 	.'<div class="wc-hero__sub">'.$t('hero.sub').'</div></div>';
 	.'<div class="wc-hero__sub">'.$t('hero.sub').'</div></div>';
 
 
-$tabs = new CTabView();
-$tabs->addTab('alert', I18n::t('tab.alert'), '<div class="wc-wrap">'.$hero.$tab_alert.'</div>');
-$tabs->addTab('opt', I18n::t('tab.opt'), '<div class="wc-wrap">'.$tab_opt.'</div>');
-$tabs->addTab('maint', I18n::t('tab.maint'), '<div class="wc-wrap">'.$tab_maint.'</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>'
+	.'</div>';
+
+$content = ($active_tab === 'opt') ? $tab_opt : (($active_tab === 'maint') ? $tab_maint : $tab_alert);
 
 
-$html_page->addItem($tabs);
+$html_page->addItem($raw('<div class="wc-wrap">'.$hero.$tabbar.$content.'</div>'));
 $html_page->show();
 $html_page->show();