wecom.center.view.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. <?php declare(strict_types = 0);
  2. /*
  3. ** WeCom Center - page view (three tabs: Alert / Optimization / Maintenance).
  4. ** Rendered as clean card HTML on top of Zabbix's page chrome.
  5. ** @var CView $this
  6. ** @var array $data
  7. */
  8. use Modules\WecomCenter\Lib\I18n;
  9. $this->addCssFile('modules/WecomCenter/assets/wecom.center.css');
  10. $e = function ($v) { return htmlspecialchars((string) ($v ?? ''), ENT_QUOTES, 'UTF-8'); };
  11. $t = function (string $k, array $a = []) use ($e) { return $e(I18n::t($k, $a)); };
  12. $save = $e((new CUrl('zabbix.php'))->setArgument('action', 'wecom.center.save')->getUrl());
  13. $view_url = (new CUrl('zabbix.php'))->setArgument('action', 'wecom.center.view')->getUrl();
  14. $mtid = $e($data['mediatypeid']);
  15. $html_page = (new CHtmlPage())->setTitle(I18n::t('page.title'));
  16. if (!$data['mediatype']) {
  17. $html_page->addItem('<div class="wc-wrap"><div class="wc-card"><div class="wc-card__body">'
  18. .$t('msg.no_mediatype').'</div></div></div>');
  19. $html_page->show();
  20. return;
  21. }
  22. $mask = (int) $data['severity'];
  23. $enabled = ((int) $data['active'] === 0);
  24. /* helper to build a card */
  25. $card = function (string $ico, string $title, string $body, string $extra = '', string $sub = '') {
  26. return '<div class="wc-card'.$extra.'"><div class="wc-card__head"><span class="wc-card__ico">'.$ico.'</span>'
  27. .'<span class="wc-card__title">'.$title.'</span>'
  28. .($sub !== '' ? '<span class="wc-card__sub">'.$sub.'</span>' : '')
  29. .'</div><div class="wc-card__body">'.$body.'</div></div>';
  30. };
  31. /* ============================ TAB 1: ALERT ============================ */
  32. // -- robot --
  33. $robotBody =
  34. '<form method="post" action="'.$save.'">'
  35. .'<input type="hidden" name="mediatypeid" value="'.$mtid.'">'
  36. .'<div class="wc-field"><label>'.$t('lbl.key').'</label>'
  37. .'<input class="wc-input wc-input--mono" type="text" name="webhook_url" value="'.$e($data['webhook_url']).'"></div>'
  38. .'<div class="wc-actions">'
  39. .'<button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_robot">'.$t('btn.save').'</button>'
  40. .'<button class="wc-btn wc-btn--ghost" type="submit" name="op" value="test_robot">'.$t('btn.test').'</button>'
  41. .'</div></form>';
  42. $robotCard = $card('&#129302;', $t('sec.robot'), $robotBody, '', $e($data['mediatype']['name'] ?? ''));
  43. // -- threshold --
  44. $sevChecks = '';
  45. for ($i = 1; $i <= 6; $i++) {
  46. $bit = 1 << ($i - 1);
  47. $chk = (($mask & $bit) !== 0) ? ' checked' : '';
  48. $sevChecks .= '<label class="wc-checkrow"><input type="checkbox" name="sev_'.$i.'"'.$chk.'> '
  49. .'<span class="wc-sev wc-sev-'.$i.'">'.$t('sev.'.$i).'</span></label>';
  50. }
  51. $thresholdBody =
  52. '<div class="wc-hint">'.$t('lbl.threshold_hint').'</div>'
  53. .'<form method="post" action="'.$save.'">'
  54. .'<input type="hidden" name="mediatypeid" value="'.$mtid.'">'
  55. .'<div class="wc-field"><label class="wc-checkrow"><input type="checkbox" name="enabled"'.($enabled ? ' checked' : '').'> '.$t('lbl.alerting_on').'</label></div>'
  56. .'<div class="wc-field"><label>'.$t('lbl.severity').'</label><div class="wc-checks">'.$sevChecks.'</div></div>'
  57. .'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_alert">'.$t('btn.save').'</button></div>'
  58. .'</form>';
  59. $thresholdCard = $card('&#9881;&#65039;', $t('sec.threshold'), $thresholdBody);
  60. // -- strategy presets --
  61. $presetRows = '';
  62. foreach ($data['presets'] as $key => $p) {
  63. $is_cur = ((string) $data['current_strategy'] === (string) $key);
  64. $name = $t($p['label']).($is_cur ? ' <span class="wc-pill wc-pill--cur">'.$t('preset.current').'</span>' : '');
  65. $prevUrl = $e((new CUrl('zabbix.php'))
  66. ->setArgument('action', 'wecom.center.view')
  67. ->setArgument('mediatypeid', $data['mediatypeid'])
  68. ->setArgument('preview_preset', $key)
  69. ->getUrl());
  70. $presetRows .=
  71. '<div class="wc-preset">'
  72. .'<div class="wc-preset__main"><div class="wc-preset__name">'.$name.'</div>'
  73. .'<div class="wc-preset__desc">'.$t($p['desc']).'</div></div>'
  74. .'<div class="wc-preset__act">'
  75. .'<a class="wc-btn wc-btn--ghost wc-btn--sm" href="'.$prevUrl.'">'.$t('btn.preview').'</a>'
  76. .'<form method="post" action="'.$save.'" style="margin:0"><input type="hidden" name="mediatypeid" value="'.$mtid.'">'
  77. .'<button class="wc-btn wc-btn--primary wc-btn--sm" type="submit" name="apply_strategy" value="'.$e($key).'">'.$t('btn.apply').'</button></form>'
  78. .'</div></div>';
  79. }
  80. $strategyBody = '<div class="wc-hint">'.$t('lbl.strategy_hint').'</div>'.$presetRows;
  81. $strategyCard = $card('&#128680;', $t('sec.strategy'), $strategyBody, ' wc-card--wide');
  82. // -- preview --
  83. $previewCard = '';
  84. if ($data['preview'] !== null) {
  85. $pv = $data['preview'];
  86. $pkey = $data['preview_key'];
  87. $rows = '';
  88. foreach ($pv['classes'] as $ck => $c) {
  89. $label = $t($data['classes'][$ck]['label'] ?? $ck);
  90. $rows .= '<tr><td>'.$label.'</td><td><span class="wc-sev wc-sev-'.((int) $c['target'] + 1).'">'.$t('sev.'.($c['target'] + 1)).'</span></td>'
  91. .'<td class="num">'.$e($c['count']).'</td><td class="num">'.$e($c['changes']).'</td></tr>';
  92. }
  93. $rows .= '<tr class="wc-tr-total"><td>'.$t('cls.total').'</td><td></td>'
  94. .'<td class="num">'.$e($pv['total']).'</td><td class="num">'.$e($pv['changes']).'</td></tr>';
  95. $tbl = '<table class="wc-table"><thead><tr><th>'.$t('cls.col.class').'</th><th>'.$t('cls.col.target').'</th>'
  96. .'<th class="num">'.$t('cls.col.count').'</th><th class="num">'.$t('cls.col.changes').'</th></tr></thead><tbody>'.$rows.'</tbody></table>';
  97. $body = '<div class="wc-hint">'.$t('preview.hint').'</div>'.$tbl;
  98. $previewCard = $card('&#128269;', $t('preview.title', [I18n::t($data['presets'][$pkey]['label'] ?? $pkey)]), $body, ' wc-card--wide');
  99. }
  100. $tab_alert = '<div class="wc-grid">'.$robotCard.$thresholdCard.$strategyCard.$previewCard.'</div>';
  101. /* ======================== TAB 2: OPTIMIZATION ========================= */
  102. // -- tuning --
  103. $tuneOpts = '';
  104. foreach (['small', 'medium', 'large'] as $pk) {
  105. $tuneOpts .= '<option value="'.$e($pk).'"'.(($data['tuning_profile'] === $pk) ? ' selected' : '').'>'.$t($pk === 'small' ? 'tuning.small' : ($pk === 'medium' ? 'tuning.medium' : 'tuning.large')).'</option>';
  106. }
  107. $tuneBody =
  108. '<div class="wc-hint">'.$t('lbl.tuning_hint').'</div>'
  109. .'<form method="get" action="zabbix.php"><input type="hidden" name="action" value="wecom.center.view">'
  110. .'<input type="hidden" name="mediatypeid" value="'.$mtid.'">'
  111. .'<div class="wc-inline"><div class="wc-field"><label>'.$t('tuning.choose').'</label>'
  112. .'<select class="wc-input" name="tuning_profile">'.$tuneOpts.'</select></div>'
  113. .'<button class="wc-btn wc-btn--primary" type="submit" name="generate" value="1">'.$t('btn.generate').'</button></div></form>'
  114. .'<div class="wc-code__label" style="margin-top:16px">'.$t('tuning.env').'</div>'
  115. .'<div class="wc-code">'.$e($data['tuning']['env']).'</div>'
  116. .'<div class="wc-code__label">'.$t('tuning.cmd').'</div>'
  117. .'<div class="wc-code">'.$e($data['tuning']['cmd']).'</div>';
  118. $tuningCard = $card('&#9881;&#65039;', $t('sec.tuning'), $tuneBody, ' wc-card--wide');
  119. // -- template import --
  120. $tplOpts = '';
  121. if (!$data['templates']) {
  122. $tplOpts = '<option value="">'.$t('tpl.none').'</option>';
  123. }
  124. foreach ($data['templates'] as $tf) {
  125. $tplOpts .= '<option value="'.$e($tf['file']).'">'.$e($tf['name']).'</option>';
  126. }
  127. $importBody =
  128. '<div class="wc-hint">'.$t('lbl.template_hint').'</div>'
  129. .'<form method="post" action="'.$save.'">'
  130. .'<div class="wc-field"><label>'.$t('tpl.choose').'</label><select class="wc-input" name="template_file">'.$tplOpts.'</select></div>'
  131. .'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="import_template">'.$t('btn.import').'</button></div>'
  132. .'</form>';
  133. $importCard = $card('&#128230;', $t('sec.template'), $importBody);
  134. // -- map --
  135. if ($data['server_host']) {
  136. $mapBody =
  137. '<div class="wc-hint">'.$t('map.hint').'</div>'
  138. .'<form method="post" action="'.$save.'">'
  139. .'<input type="hidden" name="hostid" value="'.$e($data['server_host']['hostid']).'">'
  140. .'<div class="wc-field"><label>'.$t('map.host').'</label><div class="wc-muted">'.$e($data['server_host']['name']).'</div></div>'
  141. .'<div class="wc-inline">'
  142. .'<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>'
  143. .'<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>'
  144. .'</div>'
  145. .'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_map">'.$t('btn.save').'</button></div>'
  146. .'</form>';
  147. }
  148. else {
  149. $mapBody = '<div class="wc-empty">'.$t('msg.no_server_host').'</div>';
  150. }
  151. $mapCard = $card('&#128506;&#65039;', $t('sec.map'), $mapBody);
  152. // -- template switch --
  153. $srcOpts = '<option value="">'.$t('switch.pick').'</option>';
  154. $dstOpts = '<option value="">'.$t('switch.pick').'</option>';
  155. foreach ($data['huawei_tpls'] as $tp) {
  156. $selS = ((string) $data['switch_src'] === (string) $tp['templateid']) ? ' selected' : '';
  157. $selD = ((string) $data['switch_dst'] === (string) $tp['templateid']) ? ' selected' : '';
  158. $srcOpts .= '<option value="'.$e($tp['templateid']).'"'.$selS.'>'.$e($tp['host']).'</option>';
  159. $dstOpts .= '<option value="'.$e($tp['templateid']).'"'.$selD.'>'.$e($tp['host']).'</option>';
  160. }
  161. $switchBody =
  162. '<div class="wc-hint">'.$t('lbl.switch_hint').'</div>'
  163. .'<form method="get" action="zabbix.php"><input type="hidden" name="action" value="wecom.center.view">'
  164. .'<input type="hidden" name="mediatypeid" value="'.$mtid.'">'
  165. .'<div class="wc-inline">'
  166. .'<div class="wc-field"><label>'.$t('switch.src').'</label><select class="wc-input" name="switch_src">'.$srcOpts.'</select></div>'
  167. .'<div class="wc-field"><label>'.$t('switch.dst').'</label><select class="wc-input" name="switch_dst">'.$dstOpts.'</select></div>'
  168. .'<button class="wc-btn wc-btn--ghost" type="submit" name="preview" value="1">'.$t('btn.preview').'</button>'
  169. .'</div></form>';
  170. if ($data['switch_preview'] !== null) {
  171. $sp = $data['switch_preview'];
  172. if ((int) $sp['count'] === 0) {
  173. $switchBody .= '<div class="wc-divider"></div><div class="wc-empty">'.$t('switch.none', [$sp['src_name']]).'</div>';
  174. }
  175. else {
  176. $chips = '';
  177. foreach ($sp['hosts'] as $hn) {
  178. $chips .= '<span class="wc-chip">'.$e($hn).'</span>';
  179. }
  180. $switchBody .= '<div class="wc-divider"></div>'
  181. .'<div class="wc-muted">'.$t('switch.result', [$sp['count'], $sp['src_name'], $sp['dst_name']]).'</div>'
  182. .'<div class="wc-chips">'.$chips.'</div>'
  183. .'<form method="post" action="'.$save.'" style="margin-top:14px">'
  184. .'<input type="hidden" name="switch_src" value="'.$e($sp['src']).'">'
  185. .'<input type="hidden" name="switch_dst" value="'.$e($sp['dst']).'">'
  186. .'<button class="wc-btn wc-btn--danger" type="submit" name="op" value="apply_switch">'.$t('btn.confirm').'</button>'
  187. .'</form>';
  188. }
  189. }
  190. $switchCard = $card('&#128257;', $t('sec.switch'), $switchBody, ' wc-card--wide');
  191. $tab_opt = '<div class="wc-grid">'.$tuningCard.$importCard.$mapCard.$switchCard.'</div>';
  192. /* ======================== TAB 3: MAINTENANCE ========================== */
  193. $searchBody =
  194. '<div class="wc-hint">'.$t('lbl.trig_hint', [$data['trigger_count']]).'</div>'
  195. .'<form method="get" action="zabbix.php"><input type="hidden" name="action" value="wecom.center.view">'
  196. .'<input type="hidden" name="mediatypeid" value="'.$mtid.'">'
  197. .'<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>'
  198. .'<button class="wc-btn wc-btn--primary" type="submit" name="search" value="1">'.$t('btn.search').'</button></div></form>';
  199. $maintBody = $searchBody;
  200. if ($data['trig_search'] !== '') {
  201. if ($data['triggers']) {
  202. $ids = [];
  203. $list = '';
  204. foreach ($data['triggers'] as $tg) {
  205. $ids[] = $tg['triggerid'];
  206. $host = !empty($tg['hosts']) ? $tg['hosts'][0]['host'] : '';
  207. $sev = ((int) $tg['priority']) + 1;
  208. $chk = ((int) $tg['status'] === 0) ? ' checked' : '';
  209. $list .= '<label class="wc-checkrow" style="display:block;padding:6px 0;border-bottom:1px solid #f2f4f7">'
  210. .'<input type="checkbox" name="trig_'.$e($tg['triggerid']).'"'.$chk.'> '
  211. .'<span class="wc-sev wc-sev-'.$sev.'">'.$t('sev.'.$sev).'</span> '
  212. .'<span class="wc-muted">'.$e($host).'</span> — '.$e($tg['description']).'</label>';
  213. }
  214. $maintBody .= '<div class="wc-divider"></div>'
  215. .'<form method="post" action="'.$save.'">'
  216. .'<input type="hidden" name="trig_ids" value="'.$e(implode(',', $ids)).'">'
  217. .'<div class="wc-field"><label>'.$t('lbl.trig_list').'</label>'.$list.'</div>'
  218. .'<div class="wc-actions"><button class="wc-btn wc-btn--primary" type="submit" name="op" value="save_triggers">'.$t('btn.save').'</button></div>'
  219. .'</form>';
  220. }
  221. else {
  222. $maintBody .= '<div class="wc-divider"></div><div class="wc-empty">'.$t('msg.no_trig_search').'</div>';
  223. }
  224. }
  225. $tab_maint = '<div class="wc-grid">'.$card('&#128295;', $t('sec.triggers'), $maintBody, ' wc-card--wide').'</div>';
  226. /* ============================ assemble =============================== */
  227. $hero = '<div class="wc-hero"><div class="wc-hero__title">'.$t('page.title').'</div>'
  228. .'<div class="wc-hero__sub">'.$t('hero.sub').'</div></div>';
  229. $tabs = new CTabView();
  230. $tabs->addTab('alert', I18n::t('tab.alert'), '<div class="wc-wrap">'.$hero.$tab_alert.'</div>');
  231. $tabs->addTab('opt', I18n::t('tab.opt'), '<div class="wc-wrap">'.$tab_opt.'</div>');
  232. $tabs->addTab('maint', I18n::t('tab.maint'), '<div class="wc-wrap">'.$tab_maint.'</div>');
  233. $html_page->addItem($tabs);
  234. $html_page->show();