├── .gitignore
├── CHANGELOG.md
├── Module.php
├── README.md
├── actions
├── CControllerBGHost.php
├── CControllerBGHostView.php
└── CControllerBGHostViewRefresh.php
├── manifest.json
├── partials
├── js
│ └── monitoring.host.view.refresh.js.php
└── module.monitoring.host.view.html.php
├── screenshots
└── zabbix-module-hosts-tree-1.png
└── views
├── css
└── bghost.css
├── js
└── monitoring.host.view.js.php
├── module.monitoring.bghost.view.php
└── module.monitoring.bghost.view.refresh.php
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | All notable changes to this project will be documented in this file.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6 |
7 | ## [Unreleased]
8 |
9 | ## [2.0.1] - 2023-06-24
10 | ### Changed
11 | - Fixed bug with showing in browser for Zabbix >= 6.0.18
12 |
13 | ## [4.1.1] - 2023-06-11
14 | ### Changed
15 | - Fixed bug with hiding childeren groups at all levels.
16 |
17 | ## [4.1.0] - 2023-06-08
18 | ### Changed
19 | - Show group tree expanded at first load
20 | - Fixed bug with showing in browser for Zabbix >= 6.4.3
21 |
22 | ## [4.0.0] - 2023-04-08
23 | ### Changed
24 | - Updated to work with Zabbix 6.4
25 |
26 | ## [3.0.0] - 2022-07-24
27 | ### Changed
28 | - Updated to work with Zabbix 6.2.0
29 |
30 | ## [2.0.0] - 2022-02-16
31 | ### Changed
32 | - Updated to work with Zabbix 6.0.0
33 |
34 | ## [1.3.0] - 2021-11-12
35 | ### Changed
36 | - Lots of code re-written
37 | - All calculations moved from View to Controller
38 | - Performance improvements (should be noticable when there is a lot of groups)
39 | - Fix number of hosts and number of problems calculation per group
40 | - Change the way of presenting Groups with paging (too many hosts belong to one group and to different groups)
41 |
42 | ## [1.2.0] - 2021-08-20
43 | ### Changed
44 | - Alphabetically arranged groups in the tree branches
45 | - Add space between the group name and the host number
46 |
47 | ## [1.1.0] - 2021-08-15
48 | ### Added
49 | - show number of hosts in groups and subgroups
50 | - show active triggers for groups
51 |
52 | ## [1.0.3] - 2021-08-11
53 | ### Changed
54 | - fixed pagination
55 |
56 | ## [1.0.2] - 2021-08-11
57 | ### Changed
58 | - fixed issue with "fake" group IDs duplication leading to hiding/showing multiple groups simultaneously
59 |
60 | ## [1.0.1] - 2021-08-10
61 | ### Changed
62 | - show all parent groups that do not exist in Zabbix DB or do not have any hosts
63 |
64 | ## [1.0.0] - 2021-08-08
65 | ### Added
66 | - the first version released
67 |
--------------------------------------------------------------------------------
/Module.php:
--------------------------------------------------------------------------------
1 | get('menu.main')
15 | ->findOrAdd(_('Monitoring'))
16 | ->getSubmenu()
17 | ->insertAfter('Hosts', (new \CMenuItem(_('Hosts tree')))
18 | ->setAction('bghost.view')
19 | );
20 | }
21 |
22 | /**
23 | * Event handler, triggered before executing the action.
24 | *
25 | * @param CAction $action Action instance responsible for current request.
26 | */
27 | public function onBeforeAction(CAction $action): void {
28 | }
29 |
30 | /**
31 | * Event handler, triggered on application exit.
32 | *
33 | * @param CAction $action Action instance responsible for current request.
34 | */
35 | public function onTerminate(CAction $action): void {
36 | }
37 | }
38 | ?>
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # zabbix-module-hosts-tree
2 | Written according to Zabbix official documentation [Modules](https://www.zabbix.com/documentation/current/en/devel/modules/file_structure)
3 |
4 | A Zabbix module to show groups/hosts as a tree under Monitoring -> Hosts Tree menu item in Zabbix.
5 | 
6 |
7 | IMPORTANT: pick module version according to Zabbix version:
8 | | Module version | Zabbix version |
9 | |:--------------:|:--------------:|
10 | | v1.3.0 | 5.4 |
11 | | v2.0.1 | 6.0 |
12 | | v3.0.0 | 6.2 |
13 | | v4.1.1 | 6.4 |
14 | | v6.0.1 | 7.0 |
15 | | v6.1.1 | 7.2 |
16 |
17 | # How to use
18 | 1) Create a folder in your Zabbix server modules folder (by default /usr/share/zabbix/) and copy contents of this repository into folder `zabbix-module-hosts-tree`.
19 | 2) Go to Administration -> General -> Modules click Scan directory and enable the module. You should get new 'Hosts tree' menu item under Monitoring.
20 |
21 | ## Authors
22 | See [Contributors](https://github.com/BGmot/zabbix-module-hosts-tree/graphs/contributors)
23 |
--------------------------------------------------------------------------------
/actions/CControllerBGHost.php:
--------------------------------------------------------------------------------
1 | '',
39 | 'groupids' => [],
40 | 'ip' => '',
41 | 'dns' => '',
42 | 'port' => '',
43 | 'status' => -1,
44 | 'evaltype' => TAG_EVAL_TYPE_AND_OR,
45 | 'tags' => [],
46 | 'severities' => [],
47 | 'show_suppressed' => ZBX_PROBLEM_SUPPRESSED_FALSE,
48 | 'maintenance_status' => HOST_MAINTENANCE_STATUS_ON,
49 | 'page' => null,
50 | 'sort' => 'name',
51 | 'sortorder' => ZBX_SORT_UP
52 | ];
53 |
54 | /**
55 | * Get host list results count for passed filter.
56 | *
57 | * @param array $filter Filter options.
58 | * @param string $filter['name'] Filter hosts by name.
59 | * @param array $filter['groupids'] Filter hosts by host groups.
60 | * @param string $filter['ip'] Filter hosts by IP.
61 | * @param string $filter['dns'] Filter hosts by DNS.
62 | * @param string $filter['port'] Filter hosts by port.
63 | * @param string $filter['status'] Filter hosts by status.
64 | * @param string $filter['evaltype'] Filter hosts by tags.
65 | * @param string $filter['tags'] Filter hosts by tag names and values.
66 | * @param string $filter['severities'] Filter problems on hosts by severities.
67 | * @param string $filter['show_suppressed'] Filter suppressed problems.
68 | * @param int $filter['maintenance_status'] Filter hosts by maintenance.
69 | *
70 | * @return int
71 | */
72 | protected function getCount(array $filter): int {
73 | $groupids = $filter['groupids'] ? getSubGroups($filter['groupids']) : null;
74 |
75 | return (int) API::Host()->get([
76 | 'countOutput' => true,
77 | 'evaltype' => $filter['evaltype'],
78 | 'tags' => $filter['tags'],
79 | 'inheritedTags' => true,
80 | 'groupids' => $groupids,
81 | 'severities' => $filter['severities'] ? $filter['severities'] : null,
82 | 'withProblemsSuppressed' => $filter['severities']
83 | ? (($filter['show_suppressed'] == ZBX_PROBLEM_SUPPRESSED_TRUE) ? null : false)
84 | : null,
85 | 'search' => [
86 | 'name' => ($filter['name'] === '') ? null : $filter['name'],
87 | 'ip' => ($filter['ip'] === '') ? null : $filter['ip'],
88 | 'dns' => ($filter['dns'] === '') ? null : $filter['dns']
89 | ],
90 | 'filter' => [
91 | 'status' => ($filter['status'] == -1) ? null : $filter['status'],
92 | 'port' => ($filter['port'] === '') ? null : $filter['port'],
93 | 'maintenance_status' => ($filter['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON)
94 | ? null
95 | : HOST_MAINTENANCE_STATUS_OFF
96 | ],
97 | 'limit' => CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT) + 1
98 | ]);
99 | }
100 |
101 | /**
102 | * Prepares the host list based on the given filter and sorting options.
103 | *
104 | * @param array $filter Filter options.
105 | * @param string $filter['name'] Filter hosts by name.
106 | * @param array $filter['groupids'] Filter hosts by host groups.
107 | * @param string $filter['ip'] Filter hosts by IP.
108 | * @param string $filter['dns'] Filter hosts by DNS.
109 | * @param string $filter['port'] Filter hosts by port.
110 | * @param string $filter['status'] Filter hosts by status.
111 | * @param string $filter['evaltype'] Filter hosts by tags.
112 | * @param string $filter['tags'] Filter hosts by tag names and values.
113 | * @param string $filter['severities'] Filter problems on hosts by severities.
114 | * @param string $filter['show_suppressed'] Filter suppressed problems.
115 | * @param int $filter['maintenance_status'] Filter hosts by maintenance.
116 | * @param int $filter['page'] Page number.
117 | * @param string $filter['sort'] Sorting field.
118 | * @param string $filter['sortorder'] Sorting order.
119 | *
120 | * @return array
121 | */
122 | protected function getData(array $filter): array {
123 | $limit = CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT) + 1;
124 | $groupids = $filter['groupids'] ? getSubGroups($filter['groupids']) : null;
125 | $hosts = API::Host()->get([
126 | 'output' => ['hostid', 'name', 'status'],
127 | 'evaltype' => $filter['evaltype'],
128 | 'tags' => $filter['tags'],
129 | 'inheritedTags' => true,
130 | 'groupids' => $groupids,
131 | 'severities' => $filter['severities'] ? $filter['severities'] : null,
132 | 'withProblemsSuppressed' => $filter['severities']
133 | ? (($filter['show_suppressed'] == ZBX_PROBLEM_SUPPRESSED_TRUE) ? null : false)
134 | : null,
135 | 'search' => [
136 | 'name' => ($filter['name'] === '') ? null : $filter['name'],
137 | 'ip' => ($filter['ip'] === '') ? null : $filter['ip'],
138 | 'dns' => ($filter['dns'] === '') ? null : $filter['dns']
139 | ],
140 | 'filter' => [
141 | 'status' => ($filter['status'] == -1) ? null : $filter['status'],
142 | 'port' => ($filter['port'] === '') ? null : $filter['port'],
143 | 'maintenance_status' => ($filter['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON)
144 | ? null
145 | : HOST_MAINTENANCE_STATUS_OFF
146 | ],
147 | 'selectHostGroups' => ['groupid', 'name'],
148 | 'sortfield' => 'name',
149 | 'limit' => $limit,
150 | 'preservekeys' => true
151 | ]);
152 |
153 | $host_groups = []; // Information about all groups to build a tree
154 | $fake_group_id = 100000;
155 |
156 | foreach ($hosts as &$host) {
157 | foreach ($host['hostgroups'] as $group) {
158 | $groupid = $group['groupid'];
159 | $groupname_full = $group['name'];
160 | if (!array_key_exists($groupname_full, $host_groups)) {
161 | $host_groups[$groupname_full] = [
162 | 'groupid' => $groupid,
163 | 'hosts' => [
164 | $host['hostid']
165 | ],
166 | 'children' => [],
167 | 'parent_group_name' => '',
168 | 'num_of_hosts' => 1,
169 | 'problem_count' => [],
170 | 'is_collapsed' => true
171 | ];
172 | for ($severity = TRIGGER_SEVERITY_COUNT - 1; $severity >= TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity--) {
173 | $host_groups[$groupname_full]['problem_count'][$severity] = 0;
174 | }
175 | } else {
176 | $host_groups[$groupname_full]['hosts'][] = $host['hostid'];
177 | $host_groups[$groupname_full]['num_of_hosts']++;
178 | }
179 |
180 | $grp_arr = explode('/', $groupname_full);
181 | if (count($grp_arr) > 1) {
182 | // Find all parent groups and create respective array elements in $host_groups
183 | $this->add_parent($host_groups, $fake_group_id, $groupname_full, $filter);
184 | }
185 | }
186 | }
187 | unset($host);
188 |
189 | $filter['sortorder'] == 'ASC' ? ksort($host_groups) : krsort($host_groups);
190 |
191 | $hosts_sorted_by_group = [];
192 | foreach ($host_groups as $host_group_name => $host_group) {
193 | $this->add_hosts_of_child_group($hosts_sorted_by_group, $hosts, $host_groups, $host_group_name, $filter);
194 | }
195 |
196 | $view_curl = (new CUrl())->setArgument('action', 'bghost.view');
197 |
198 | // Split result array and create paging.
199 | $paging = CPagerHelper::paginate($filter['page'], $hosts_sorted_by_group, $filter['sortorder'], $view_curl);
200 |
201 | // Get additional data to limited host amount.
202 | $hosts = API::Host()->get([
203 | 'output' => ['hostid', 'name', 'status', 'maintenance_status', 'maintenanceid', 'maintenance_type'],
204 | 'selectInterfaces' => ['ip', 'dns', 'port', 'main', 'type', 'useip', 'available', 'error', 'details'],
205 | 'selectGraphs' => API_OUTPUT_COUNT,
206 | 'selectHttpTests' => API_OUTPUT_COUNT,
207 | 'selectTags' => ['tag', 'value'],
208 | 'selectInheritedTags' => ['tag', 'value'],
209 | 'hostids' => array_keys($hosts_sorted_by_group),
210 | 'preservekeys' => true
211 | ]);
212 |
213 | // Get only those groups that need to be shown
214 | $host_groups_to_show = [];
215 | foreach ($hosts_sorted_by_group as $host) {
216 | foreach ($host['hostgroups'] as $group) {
217 | if (!array_key_exists($group['name'], $host_groups_to_show)) {
218 | $host_groups_to_show[$group['name']] = $host_groups[$group['name']];
219 | $host_groups_to_show[$group['name']]['hosts'] = [ $host['hostid'] ];
220 | // Make sure parent group exists as well
221 | $grp_arr = explode('/', $group['name']);
222 | for ($i = 1, $g_name = $grp_arr[0]; $i < count($grp_arr); $i++) {
223 | if (!array_key_exists($g_name, $host_groups_to_show)) {
224 | $host_groups_to_show[$g_name] = $host_groups[$g_name];
225 | $host_groups_to_show[$g_name]['hosts'] = [];
226 | }
227 | $g_name = $g_name.'/'.$grp_arr[$i];
228 | }
229 | } else {
230 | $host_groups_to_show[$group['name']]['hosts'][] = $host['hostid'];
231 | }
232 | }
233 | }
234 | // Remove groups that are not to be shown from 'children' groups list
235 | foreach ($host_groups_to_show as $group_name => &$group) {
236 | $groups_to_delete = [];
237 | foreach ($group['children'] as $child_group_name) {
238 | if (!array_key_exists($child_group_name, $host_groups_to_show)) {
239 | $groups_to_delete[] = $child_group_name;
240 | }
241 | }
242 | foreach ($groups_to_delete as $group_name) {
243 | if (($key = array_search($group_name, $group['children'])) !== false) {
244 | unset($group['children'][$key]);
245 | }
246 | }
247 | }
248 | unset($group);
249 |
250 | $filter['sortorder'] == 'ASC' ? ksort($host_groups_to_show) : krsort($host_groups_to_show);
251 |
252 | // Some hosts for shown groups can be on other pages thus not in $hosts_sorted_by_group
253 | // as we already applied paging. To calculate number of problems we need all hosts belonging to shown groups
254 | $all_hosts_in_groups_to_show = [];
255 | foreach ($host_groups_to_show as $group_name => $group) {
256 | foreach ($host_groups[$group_name]['hosts'] as $host) {
257 | $all_hosts_in_groups_to_show[] = $host;
258 | }
259 | }
260 |
261 | $maintenanceids = [];
262 |
263 | // Select triggers and problems to calculate number of problems for each host.
264 | $triggers = API::Trigger()->get([
265 | 'output' => [],
266 | 'selectHosts' => ['hostid'],
267 | 'hostids' => $all_hosts_in_groups_to_show,
268 | 'skipDependent' => true,
269 | 'monitored' => true,
270 | 'preservekeys' => true
271 | ]);
272 |
273 | $problems = API::Problem()->get([
274 | 'output' => ['eventid', 'objectid', 'severity'],
275 | 'objectids' => array_keys($triggers),
276 | 'source' => EVENT_SOURCE_TRIGGERS,
277 | 'object' => EVENT_OBJECT_TRIGGER,
278 | 'suppressed' => ($filter['show_suppressed'] == ZBX_PROBLEM_SUPPRESSED_TRUE) ? null : false
279 | ]);
280 |
281 | // Group all problems per host per severity.
282 | $host_problems = [];
283 | foreach ($problems as $problem) {
284 | foreach ($triggers[$problem['objectid']]['hosts'] as $trigger_host) {
285 | $host_problems[$trigger_host['hostid']][$problem['severity']][$problem['eventid']] = true;
286 | }
287 | }
288 |
289 | // Count problems for each shown group - take into account only hosts belonging to each group (no parents/children)
290 | foreach ($host_groups_to_show as $group_name => &$group) {
291 | foreach($host_groups[$group_name]['hosts'] as $hostid) {
292 | // Count the number of problems (as value) per severity (as key).
293 | for ($severity = TRIGGER_SEVERITY_COUNT - 1; $severity >= TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity--) {
294 | // Fill empty arrays for hosts without problems.
295 | if (array_key_exists($hostid, $host_problems)) {
296 | if (array_key_exists($severity, $host_problems[$hostid])) {
297 | $group['problem_count'][$severity] += count($host_problems[$hostid][$severity]);
298 |
299 | // Increment problems count in parent groups
300 | $grp_arr = explode('/', $group_name);
301 | for ($i = count($grp_arr)-1, $g_name_child = $group_name; $i > 0; $i--) {
302 | array_pop($grp_arr);
303 | $g_name_parent = implode('/', $grp_arr);
304 | $host_groups_to_show[$g_name_parent]['problem_count'][$severity] +=
305 | count($host_problems[$hostid][$severity]);
306 | $g_name_child = $g_name_parent;
307 | }
308 | }
309 | }
310 | }
311 | }
312 | }
313 | unset($group);
314 |
315 | foreach ($hosts as &$host) {
316 | // Count number of dashboards for each host.
317 | $host['dashboards'] = count(getHostDashboards($host['hostid']));
318 |
319 | CArrayHelper::sort($host['interfaces'], [['field' => 'main', 'order' => ZBX_SORT_DOWN]]);
320 |
321 | if ($host['status'] == HOST_STATUS_MONITORED && $host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON) {
322 | $maintenanceids[$host['maintenanceid']] = true;
323 | }
324 |
325 | // Fill empty arrays for hosts without problems.
326 | if (!array_key_exists($host['hostid'], $host_problems)) {
327 | $host_problems[$host['hostid']] = [];
328 | }
329 |
330 | // Count the number of problems (as value) per severity (as key).
331 | for ($severity = TRIGGER_SEVERITY_COUNT - 1; $severity >= TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity--) {
332 | $host['problem_count'][$severity] = array_key_exists($severity, $host_problems[$host['hostid']])
333 | ? count($host_problems[$host['hostid']][$severity])
334 | : 0;
335 | }
336 |
337 | // Merge host tags with template tags, and skip duplicate tags and values.
338 | if (!$host['inheritedTags']) {
339 | $tags = $host['tags'];
340 | }
341 | elseif (!$host['tags']) {
342 | $tags = $host['inheritedTags'];
343 | }
344 | else {
345 | $tags = $host['tags'];
346 |
347 | foreach ($host['inheritedTags'] as $template_tag) {
348 | foreach ($tags as $host_tag) {
349 | // Skip tags with same name and value.
350 | if ($host_tag['tag'] === $template_tag['tag']
351 | && $host_tag['value'] === $template_tag['value']) {
352 | continue 2;
353 | }
354 | }
355 | $tags[] = $template_tag;
356 | }
357 | }
358 |
359 | $host['tags'] = $tags;
360 |
361 | }
362 | unset($host);
363 |
364 | $maintenances = [];
365 |
366 | if ($maintenanceids) {
367 | $maintenances = API::Maintenance()->get([
368 | 'output' => ['name', 'description'],
369 | 'maintenanceids' => array_keys($maintenanceids),
370 | 'preservekeys' => true
371 | ]);
372 | }
373 |
374 | $tags = makeTags($hosts, true, 'hostid', ZBX_TAG_COUNT_DEFAULT, $filter['tags']);
375 |
376 | foreach ($hosts as &$host) {
377 | $host['tags'] = $tags[$host['hostid']];
378 | }
379 | unset($host);
380 |
381 | return [
382 | 'paging' => $paging,
383 | 'hosts' => $hosts,
384 | 'host_groups' => $host_groups_to_show,
385 | 'maintenances' => $maintenances
386 | ];
387 | }
388 |
389 | // Adds all hosts belonging to $host_group_name to global array $hosts_sorted_by_group
390 | protected function add_hosts_of_child_group(&$hosts_sorted_by_group, $hosts, $host_groups, $host_group_name, $filter) {
391 | // First add all the hosts belonging to this group
392 | $hosts_to_add = [];
393 | foreach($host_groups[$host_group_name]['hosts'] as $hostid) {
394 | $hosts_to_add[$hostid] = $hosts[$hostid];
395 | }
396 | $hosts_to_add = $this->array_sort($hosts_to_add, 'name', $filter['sortorder']);
397 | foreach($hosts_to_add as $hostid => $host){
398 | $hosts_sorted_by_group[$hostid] = $host;
399 | }
400 | // Add all hosts of children groups
401 | if (count($host_groups[$host_group_name]['children']) > 0) {
402 | foreach($host_groups[$host_group_name]['children'] as $child_group_name){
403 | $this->add_hosts_of_child_group($hosts_sorted_by_group, $hosts, $host_groups, $child_group_name, $filter);
404 | }
405 | }
406 | }
407 |
408 | protected function array_sort($array, $on, $order='ASC')
409 | {
410 | $new_array = array();
411 | $sortable_array = array();
412 |
413 | if (count($array) > 0) {
414 | foreach ($array as $k => $v) {
415 | if (is_array($v)) {
416 | foreach ($v as $k2 => $v2) {
417 | if ($k2 == $on) {
418 | $sortable_array[$k] = $v2;
419 | }
420 | }
421 | } else {
422 | $sortable_array[$k] = $v;
423 | }
424 | }
425 |
426 | switch ($order) {
427 | case 'ASC':
428 | asort($sortable_array, SORT_STRING);
429 | break;
430 | case 'DESC':
431 | arsort($sortable_array, SORT_STRING);
432 | break;
433 | }
434 |
435 | foreach ($sortable_array as $k => $v) {
436 | $new_array[$k] = $array[$k];
437 | }
438 | }
439 |
440 | return $new_array;
441 | }
442 |
443 | /**
444 | * Adds parent group
445 | *
446 | * @param array $host_groups All the groups to be shown in hierarchy
447 | * @param int $fake_group_id ID for groups that do not exist in Zabbix DB (autoincremented)
448 | * @param string $groupname_full Group name parent group of which needs to be added
449 | * @param array $filter Filter options.
450 | *
451 | * @return array $host_groups modified in-place
452 | */
453 | protected function add_parent(&$host_groups, &$fake_group_id, $groupname_full, $filter) {
454 | // There is a '/' in group name
455 | $grp_arr = explode('/', $groupname_full);
456 | unset($grp_arr[count($grp_arr)-1]); // Remove last element
457 | $parent_group_name = implode('/', $grp_arr);
458 | // In Zabbix it is possible to have parent name that does not exist
459 | // e.g.: group '/level0/level1/level2' exists but '/level0/level1' does not
460 | if (array_key_exists($parent_group_name, $host_groups)) {
461 | // Parent group exists
462 | if (!in_array($groupname_full, $host_groups[$parent_group_name]['children'])) {
463 | $host_groups[$parent_group_name]['children'][] = $groupname_full;
464 | }
465 | $host_groups[$parent_group_name]['num_of_hosts']++;
466 | } else {
467 | // Parent group does not exist or does not have any hosts to show
468 | $host_groups[$parent_group_name] = [
469 | 'groupid' => $fake_group_id++,
470 | 'hosts' => [],
471 | 'children' => [$groupname_full],
472 | 'parent_group_name' => '',
473 | 'num_of_hosts' => 1,
474 | 'problem_count' => [],
475 | 'is_collapsed' => true
476 | ];
477 | for ($severity = TRIGGER_SEVERITY_COUNT - 1; $severity >= TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity--) {
478 | $host_groups[$parent_group_name]['problem_count'][$severity] = 0;
479 | }
480 | }
481 | $host_groups[$groupname_full]['parent_group_name'] = $parent_group_name;
482 | $parent_group_name_arr = explode('/', $parent_group_name);
483 | if (count($parent_group_name_arr) > 1) {
484 | // Parent group also has parent
485 | $this->add_parent($host_groups, $fake_group_id, $parent_group_name, $filter);
486 | }
487 | // Sort group names
488 | $filter['sortorder'] == 'ASC' ? sort($host_groups[$parent_group_name]['children']) : rsort($host_groups[$parent_group_name]['children']);
489 | }
490 |
491 | /**
492 | * Get additional data for filters. Selected groups for multiselect, etc.
493 | *
494 | * @param array $filter Filter fields values array.
495 | *
496 | * @return array
497 | */
498 | protected function getAdditionalData($filter): array {
499 | $data = [];
500 |
501 | if ($filter['groupids']) {
502 | $groups = API::HostGroup()->get([
503 | 'output' => ['groupid', 'name'],
504 | 'groupids' => $filter['groupids']
505 | ]);
506 | $data['groups_multiselect'] = CArrayHelper::renameObjectsKeys(array_values($groups), ['groupid' => 'id']);
507 | }
508 |
509 | return $data;
510 | }
511 |
512 | /**
513 | * Clean passed filter fields in input from default values required for HTML presentation. Convert field
514 | *
515 | * @param array $input Filter fields values.
516 | *
517 | * @return array
518 | */
519 | protected function cleanInput(array $input): array {
520 | if (array_key_exists('tags', $input) && $input['tags']) {
521 | $input['tags'] = array_filter($input['tags'], function($tag) {
522 | return !($tag['tag'] === '' && $tag['value'] === '');
523 | });
524 | $input['tags'] = array_values($input['tags']);
525 | }
526 |
527 | return $input;
528 | }
529 |
530 | /**
531 | * Clean the filter from non-existing host group IDs.
532 | *
533 | * @param array $filter
534 | *
535 | * $filter = [
536 | * 'groupids' => (array) Group IDs from filter to check.
537 | * ]
538 | *
539 | * @return array
540 | */
541 | protected static function sanitizeFilter(array $filter): array {
542 | if ($filter['groupids']) {
543 | $groups = API::HostGroup()->get([
544 | 'output' => [],
545 | 'groupids' => $filter['groupids'],
546 | 'preservekeys' => true
547 | ]);
548 |
549 | $filter['groupids'] = array_filter($filter['groupids'], static fn($groupid) =>
550 | array_key_exists($groupid, $groups)
551 | );
552 | }
553 |
554 | return $filter;
555 | }
556 | }
557 |
--------------------------------------------------------------------------------
/actions/CControllerBGHostView.php:
--------------------------------------------------------------------------------
1 | disableCsrfValidation();
36 | }
37 |
38 | protected function checkInput(): bool {
39 | $fields = [
40 | 'name' => 'string',
41 | 'groupids' => 'array_id',
42 | 'ip' => 'string',
43 | 'dns' => 'string',
44 | 'port' => 'string',
45 | 'status' => 'in -1,'.HOST_STATUS_MONITORED.','.HOST_STATUS_NOT_MONITORED,
46 | 'evaltype' => 'in '.TAG_EVAL_TYPE_AND_OR.','.TAG_EVAL_TYPE_OR,
47 | 'tags' => 'array',
48 | 'severities' => 'array',
49 | 'show_suppressed' => 'in '.ZBX_PROBLEM_SUPPRESSED_FALSE.','.ZBX_PROBLEM_SUPPRESSED_TRUE,
50 | 'maintenance_status' => 'in '.HOST_MAINTENANCE_STATUS_OFF.','.HOST_MAINTENANCE_STATUS_ON,
51 | 'sort' => 'in name,status',
52 | 'sortorder' => 'in '.ZBX_SORT_UP.','.ZBX_SORT_DOWN,
53 | 'page' => 'ge 1',
54 | 'filter_name' => 'string',
55 | 'filter_custom_time' => 'in 1,0',
56 | 'filter_show_counter' => 'in 1,0',
57 | 'filter_counters' => 'in 1',
58 | 'filter_reset' => 'in 1',
59 | 'counter_index' => 'ge 0'
60 | ];
61 |
62 | $ret = $this->validateInput($fields);
63 |
64 | // Validate tags filter.
65 | if ($ret && $this->hasInput('tags')) {
66 | foreach ($this->getInput('tags') as $filter_tag) {
67 | if (count($filter_tag) != 3
68 | || !array_key_exists('tag', $filter_tag) || !is_string($filter_tag['tag'])
69 | || !array_key_exists('value', $filter_tag) || !is_string($filter_tag['value'])
70 | || !array_key_exists('operator', $filter_tag) || !is_string($filter_tag['operator'])) {
71 | $ret = false;
72 | break;
73 | }
74 | }
75 | }
76 |
77 | // Validate severity checkbox filter.
78 | if ($ret && $this->hasInput('severities')) {
79 | $ret = !array_diff($this->getInput('severities'),
80 | range(TRIGGER_SEVERITY_NOT_CLASSIFIED, TRIGGER_SEVERITY_COUNT - 1)
81 | );
82 | }
83 |
84 | if (!$ret) {
85 | $this->setResponse(new CControllerResponseFatal());
86 | }
87 |
88 | return $ret;
89 | }
90 |
91 | protected function checkPermissions(): bool {
92 | return $this->checkAccess(CRoleHelper::UI_MONITORING_HOSTS);
93 | }
94 |
95 | protected function doAction(): void {
96 | $filter_tabs = [];
97 | $profile = (new CTabFilterProfile(static::FILTER_IDX, static::FILTER_FIELDS_DEFAULT))->read();
98 |
99 | if ($this->hasInput('filter_reset')) {
100 | $profile->reset();
101 | }
102 | else {
103 | $profile->setInput($this->cleanInput($this->getInputAll()));
104 | }
105 |
106 | foreach ($profile->getTabsWithDefaults() as $index => $filter_tab) {
107 | if ($index == $profile->selected) {
108 | // Initialize multiselect data for filter_scr to allow tabfilter correctly handle unsaved state.
109 | $filter_tab['filter_src']['filter_view_data'] = $this->getAdditionalData($filter_tab['filter_src']);
110 | }
111 |
112 | $filter_tabs[] = $filter_tab + ['filter_view_data' => $this->getAdditionalData($filter_tab)];
113 | }
114 |
115 | $filter = $filter_tabs[$profile->selected];
116 | $filter = self::sanitizeFilter($filter);
117 |
118 | $refresh_curl = new CUrl('zabbix.php');
119 | $filter['action'] = 'bghost.view.refresh';
120 | array_map([$refresh_curl, 'setArgument'], array_keys($filter), $filter);
121 |
122 | $data = [
123 | 'refresh_url' => $refresh_curl->getUrl(),
124 | 'refresh_interval' => 3600000,
125 | 'filter_view' => 'monitoring.host.filter',
126 | 'filter_defaults' => $profile->filter_defaults,
127 | 'filter_groupids' => $this->getInput('groupids', []),
128 | 'filter_tabs' => $filter_tabs,
129 | 'can_create_hosts' => $this->checkAccess(CRoleHelper::UI_CONFIGURATION_HOSTS),
130 | 'tabfilter_options' => [
131 | 'idx' => static::FILTER_IDX,
132 | 'selected' => $profile->selected,
133 | 'support_custom_time' => 0,
134 | 'expanded' => $profile->expanded,
135 | 'page' => $filter['page'],
136 | 'csrf_token' => CCsrfTokenHelper::get('tabfilter')
137 | ]
138 | ];
139 |
140 | $response = new CControllerResponseData($data);
141 | $response->setTitle(_('Hosts'));
142 | $this->setResponse($response);
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/actions/CControllerBGHostViewRefresh.php:
--------------------------------------------------------------------------------
1 | Monitoring" asynchronous refresh page.
31 | */
32 | class CControllerBGHostViewRefresh extends CControllerBGHostView {
33 |
34 | protected function init(): void {
35 | $this->disableCsrfValidation();
36 | }
37 |
38 | protected function doAction(): void {
39 | $filter = static::FILTER_FIELDS_DEFAULT;
40 |
41 | if ($this->getInput('filter_counters', 0)) {
42 | $profile = (new CTabFilterProfile(static::FILTER_IDX, static::FILTER_FIELDS_DEFAULT))->read();
43 | $filters = $this->hasInput('counter_index')
44 | ? [$profile->getTabFilter($this->getInput('counter_index'))]
45 | : $profile->getTabsWithDefaults();
46 | $filter_counters = [];
47 |
48 | foreach ($filters as $index => $tabfilter) {
49 | $tabfilter = self::sanitizeFilter($tabfilter);
50 |
51 | $filter_counters[$index] = $tabfilter['filter_show_counter'] ? $this->getCount($tabfilter) : 0;
52 | }
53 |
54 | $this->setResponse(
55 | (new CControllerResponseData([
56 | 'main_block' => json_encode(['filter_counters' => $filter_counters])
57 | ]))->disableView()
58 | );
59 | }
60 | else {
61 | $this->getInputs($filter, array_keys($filter));
62 | $filter = $this->cleanInput($filter);
63 | $filter = self::sanitizeFilter($filter);
64 |
65 | $view_url = (new CUrl())
66 | ->setArgument('action', 'bghost.view')
67 | ->removeArgument('page');
68 |
69 | $data = [
70 | 'filter' => $filter,
71 | 'view_curl' => $view_url,
72 | 'sort' => $filter['sort'],
73 | 'sortorder' => $filter['sortorder'],
74 | 'allowed_ui_latest_data' => $this->checkAccess(CRoleHelper::UI_MONITORING_LATEST_DATA),
75 | 'allowed_ui_problems' => $this->checkAccess(CRoleHelper::UI_MONITORING_PROBLEMS)
76 | ] + $this->getData($filter);
77 |
78 | $response = new CControllerResponseData($data);
79 | $this->setResponse($response);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2.0,
3 | "id": "bghosts",
4 | "name": "Groups/Hosts tree",
5 | "version": "6.1.1",
6 | "namespace": "BGmotHosts",
7 | "author": "Evgeny Yurchenko",
8 | "url": "https://bgmot.com",
9 | "description": "Show groups/hosts as a tree under Monitoring -> Hosts Tree menu item",
10 | "actions": {
11 | "bghost.view": {
12 | "class": "CControllerBGHostView",
13 | "view": "module.monitoring.bghost.view"
14 | },
15 | "bghost.view.refresh": {
16 | "class": "CControllerBGHostViewRefresh",
17 | "view": "module.monitoring.bghost.view.refresh",
18 | "layout": "layout.json"
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/partials/js/monitoring.host.view.refresh.js.php:
--------------------------------------------------------------------------------
1 |
27 |
28 |
105 |
--------------------------------------------------------------------------------
/partials/module.monitoring.host.view.html.php:
--------------------------------------------------------------------------------
1 | includeJsFile('monitoring.host.view.refresh.js.php');
22 |
23 | $form = (new CForm())
24 | ->setName('host_view');
25 |
26 | $view_url = $data['view_curl']->getUrl();
27 |
28 | $table = (new CTableInfo())
29 | ->setHeader([
30 | make_sorting_header(_('Name'), 'name', $data['sort'], $data['sortorder'], $view_url),
31 | (new CColHeader(_('Interface'))),
32 | (new CColHeader(_('Availability'))),
33 | (new CColHeader(_('Tags'))),
34 | // Fix: problems renamed to triggers to distinguish from the problems counter column
35 | (new CColHeader(_('Problems'))),
36 | make_sorting_header(_('Status'), 'status', $data['sort'], $data['sortorder'], $view_url),
37 | (new CColHeader(_('Latest data'))),
38 | (new CColHeader(_('Problems'))),
39 | (new CColHeader(_('Graphs'))),
40 | (new CColHeader(_('Dashboards'))),
41 | (new CColHeader(_('Web')))
42 | ])
43 | ->setPageNavigation($data['paging']);
44 |
45 | foreach ($data['host_groups'] as $group_name => $group) {
46 | if ($group['parent_group_name'] == '') {
47 | // Add only top level groups, children will be added recursively in addGroupRow()
48 | $rows = [];
49 | addGroupRow($data, $rows, $group_name, '', 0, $child_stat);
50 |
51 | foreach ($rows as $row) {
52 | $table->addRow($row);
53 | }
54 | }
55 | }
56 |
57 | $form->addItem($table);
58 |
59 | echo $form;
60 |
61 | // Adds one Group to the table (recursively calls itself for all sub-groups)
62 | function addGroupRow($data, &$rows, $group_name, $parent_group_name, $level, &$child_stat) {
63 | $interface_types = [INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI];
64 |
65 | $group = $data['host_groups'][$group_name];
66 |
67 | $host_rows = [];
68 | foreach ($group['hosts'] as $hostid) {
69 | $host = $data['hosts'][$hostid];
70 | $host_name = (new CLinkAction($host['name']))->setMenuPopup(CMenuPopupHelper::getHost($hostid));
71 |
72 | $interface = null;
73 | if ($host['interfaces']) {
74 | foreach ($host['interfaces'] as $index => $value) {
75 | $host['interfaces'][$index]['has_enabled_items'] = true;
76 | }
77 | foreach ($interface_types as $interface_type) {
78 | $host_interfaces = array_filter($host['interfaces'], function(array $host_interface) use ($interface_type) {
79 | return ($host_interface['type'] == $interface_type);
80 | });
81 | if ($host_interfaces) {
82 | $interface = reset($host_interfaces);
83 | break;
84 | }
85 | }
86 | }
87 |
88 | $problems_link = new CLink('', (new CUrl('zabbix.php'))
89 | ->setArgument('action', 'problem.view')
90 | ->setArgument('filter_name', '')
91 | ->setArgument('severities', $data['filter']['severities'])
92 | ->setArgument('hostids', [$host['hostid']]));
93 |
94 | $total_problem_count = 0;
95 |
96 | // Fill the severity icons by problem count and style, and calculate the total number of problems.
97 | // Need this to have cosntant order of triggers from disater to information.
98 | krsort($host['problem_count'],true);
99 |
100 | foreach ($host['problem_count'] as $severity => $count) {
101 | if (($count > 0 && $data['filter']['severities'] && in_array($severity, $data['filter']['severities']))
102 | || (!$data['filter']['severities'] && $count > 0)) {
103 | $total_problem_count += $count;
104 |
105 | $problems_link->addItem((new CSpan($count))
106 | ->addClass(ZBX_STYLE_PROBLEM_ICON_LIST_ITEM)
107 | ->addClass(CSeverityHelper::getStatusStyle($severity))
108 | ->setAttribute('title', CSeverityHelper::getName($severity))
109 | );
110 | }
111 | }
112 |
113 | if ($total_problem_count == 0) {
114 | $problems_link->addItem('Problems');
115 | }
116 | else {
117 | $problems_link->addClass(ZBX_STYLE_PROBLEM_ICON_LINK);
118 | }
119 |
120 | $maintenance_icon = '';
121 |
122 | if ($host['status'] == HOST_STATUS_MONITORED && $host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON) {
123 | if (array_key_exists($host['maintenanceid'], $data['maintenances'])) {
124 | $maintenance = $data['maintenances'][$host['maintenanceid']];
125 | $maintenance_icon = makeMaintenanceIcon($host['maintenance_type'], $maintenance['name'],
126 | $maintenance['description']
127 | );
128 | }
129 | else {
130 | $maintenance_icon = makeMaintenanceIcon($host['maintenance_type'],
131 | _('Inaccessible maintenance'), ''
132 | );
133 | }
134 | }
135 |
136 | $col1 = new CCol();
137 | for($i = 0; $i <= (6 + $level*5); $i++) {
138 | $col1 -> addItem(NBSP_BG());
139 | }
140 | $col1 -> addItem($host_name) -> addItem($maintenance_icon);
141 | $table_row_host = new CRow([
142 | $col1,
143 | (new CCol(getHostInterface($interface)))->addClass(ZBX_STYLE_NOWRAP),
144 | getHostAvailabilityTable($host['interfaces']),
145 | $host['tags'],
146 | $problems_link,
147 | ($host['status'] == HOST_STATUS_MONITORED)
148 | ? (new CSpan(_('Enabled')))->addClass(ZBX_STYLE_GREEN)
149 | : (new CSpan(_('Disabled')))->addClass(ZBX_STYLE_RED),
150 | [
151 | $data['allowed_ui_latest_data']
152 | ? new CLink(_('Latest data'),
153 | (new CUrl('zabbix.php'))
154 | ->setArgument('action', 'latest.view')
155 | ->setArgument('filter_set', '1')
156 | ->setArgument('filter_hostids', [$host['hostid']])
157 | )
158 | : _('Latest data')
159 | ],
160 | [
161 | $data['allowed_ui_problems']
162 | ? new CLink(_('Problems'),
163 | (new CUrl('zabbix.php'))
164 | ->setArgument('action', 'problem.view')
165 | ->setArgument('filter_name', '')
166 | ->setArgument('severities', $data['filter']['severities'])
167 | ->setArgument('hostids', [$host['hostid']])
168 | )
169 | : _('Problems'),
170 | CViewHelper::showNum($total_problem_count)
171 | ],
172 | $host['graphs']
173 | ? [
174 | new CLink(_('Graphs'),
175 | (new CUrl('zabbix.php'))
176 | ->setArgument('action', 'charts.view')
177 | ->setArgument('filter_set', '1')
178 | ->setArgument('filter_hostids', (array) $host['hostid'])
179 | ),
180 | CViewHelper::showNum($host['graphs'])
181 | ]
182 | : (new CSpan(_('Graphs')))->addClass(ZBX_STYLE_DISABLED),
183 | $host['dashboards']
184 | ? [
185 | new CLink(_('Dashboards'),
186 | (new CUrl('zabbix.php'))
187 | ->setArgument('action', 'host.dashboard.view')
188 | ->setArgument('hostid', $host['hostid'])
189 | ),
190 | CViewHelper::showNum($host['dashboards'])
191 | ]
192 | : (new CSpan(_('Dashboards')))->addClass(ZBX_STYLE_DISABLED),
193 | $host['httpTests']
194 | ? [
195 | new CLink(_('Web'),
196 | (new CUrl('zabbix.php'))
197 | ->setArgument('action', 'web.view')
198 | ->setArgument('filter_set', '1')
199 | ->setArgument('filter_hostids', (array) $host['hostid'])
200 | ),
201 | CViewHelper::showNum($host['httpTests'])
202 | ]
203 | : (new CSpan(_('Web')))->addClass(ZBX_STYLE_DISABLED)
204 | ]);
205 |
206 | if ($data['host_groups'][$group_name]['is_collapsed'] )
207 | $table_row_host->addClass(ZBX_STYLE_DISPLAY_NONE);
208 |
209 | addParentGroupClass($data, $table_row_host, $group_name);
210 | $host_rows[] = $table_row_host;
211 | }
212 |
213 | $subgroup_rows=[];
214 |
215 | foreach ($data['host_groups'][$group_name]['children'] as $child_group_name) {
216 | addGroupRow($data, $subgroup_rows, $child_group_name, $group_name, $level + 1, $my_stat);
217 | }
218 |
219 |
220 | $is_collapsed = $data['host_groups'][$group_name]['is_collapsed'];
221 | $toggle_tag = (new CSimpleButton())
222 | ->addClass(ZBX_STYLE_TREEVIEW)
223 | ->addClass('js-toggle')
224 | ->addItem(
225 | (new CSpan())->addClass($is_collapsed ? ZBX_STYLE_ARROW_RIGHT : ZBX_STYLE_ARROW_DOWN)
226 | );
227 | $toggle_tag->setAttribute(
228 | 'data-group_id_'.$data['host_groups'][$group_name]['groupid'],
229 | $data['host_groups'][$group_name]['groupid']
230 | );
231 |
232 | $group_name_arr = explode('/', $group_name);
233 |
234 | $group_problems_div = (new CDiv())->addClass(ZBX_STYLE_PROBLEM_ICON_LIST);
235 |
236 | foreach ($data['host_groups'][$group_name]['problem_count'] as $severity => $count) {
237 | if (($count > 0 && $data['filter']['severities'] && in_array($severity, $data['filter']['severities']))
238 | || (!$data['filter']['severities'] && $count > 0)) {
239 | $group_problems_div->addItem((new CSpan($count))
240 | ->addClass(ZBX_STYLE_PROBLEM_ICON_LIST_ITEM)
241 | ->addClass(CSeverityHelper::getStatusStyle($severity))
242 | ->setAttribute('title', CSeverityHelper::getName($severity))
243 | );
244 | }
245 | }
246 |
247 | $col2 = (new CCol())
248 | -> setColSpan(4);
249 | for ($i = 0; $i < $level*5; $i++) {
250 | $col2 -> addItem(NBSP_BG());
251 | }
252 | $col2 -> addItem($toggle_tag);
253 | $col2 -> addItem(bold(end($group_name_arr)));
254 | $col2 -> addItem(NBSP_BG());
255 | $col2 -> addItem(bold('(' . $data['host_groups'][$group_name]['num_of_hosts']. ')'));
256 | $table_row = new CRow([
257 | $col2,
258 | $group_problems_div,
259 | (new CCol())
260 | -> setColSpan(6)
261 | ]);
262 |
263 | if ($data['host_groups'][$group_name]['is_collapsed'] && $parent_group_name != '')
264 | $table_row->addClass(ZBX_STYLE_DISPLAY_NONE);
265 |
266 | // We don't render here, but just add rows to the array
267 | addParentGroupClass($data, $table_row, $parent_group_name);
268 |
269 | $rows[] = $table_row;
270 |
271 | // Now all subgroup rows
272 | foreach ($subgroup_rows as $idx=>$row) {
273 | $rows[] = $row;
274 | }
275 |
276 | // And finally, the hosts rows
277 | foreach ($host_rows as $idx=>$row) {
278 | $rows[] = $row;
279 | }
280 | }
281 |
282 | // Adds class 'data-group_id_=' to $element
283 | function addParentGroupClass($data, &$element, $parent_group_name) {
284 | if ($parent_group_name != '') {
285 | $element->setAttribute(
286 | 'data-group_id_'.$data['host_groups'][$parent_group_name]['groupid'],
287 | $data['host_groups'][$parent_group_name]['groupid']
288 | );
289 | }
290 | }
291 |
292 | function NBSP_BG() {
293 | return new CHtmlEntityBG(' ');
294 | }
295 |
296 | class CHtmlEntityBG {
297 | private $entity = '';
298 | public function __construct(string $entity) {
299 | $this->entity = $entity;
300 | }
301 | public function toString(): string {
302 | return $this->entity;
303 | }
304 | }
305 |
--------------------------------------------------------------------------------
/screenshots/zabbix-module-hosts-tree-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BGmot/zabbix-module-hosts-tree/e51637de0fdf61d8d30088bbf0649ed4f85154d5/screenshots/zabbix-module-hosts-tree-1.png
--------------------------------------------------------------------------------
/views/css/bghost.css:
--------------------------------------------------------------------------------
1 | .treeview {
2 | display: inline-block;
3 | width: 14px;
4 | height: 16px;
5 | min-height: auto;
6 | line-height: 16px;
7 | padding: 0;
8 | margin: 0 2px 0 0;
9 | cursor: auto;
10 | text-align: center;
11 | border: 0;
12 | background-color: transparent; }
13 | .treeview .arrow-right {
14 | border-left-color: #768d99; }
15 | .treeview .arrow-down {
16 | margin: 0 0 2px;
17 | border-top-color: #768d99; }
18 | .treeview:hover, .treeview:focus {
19 | background-color: transparent; }
20 | .treeview:hover .arrow-right, .treeview:focus .arrow-right {
21 | border-left-color: #0275b8; }
22 | .treeview:hover .arrow-down, .treeview:focus .arrow-down {
23 | border-top-color: #0275b8; }
24 |
--------------------------------------------------------------------------------
/views/js/monitoring.host.view.js.php:
--------------------------------------------------------------------------------
1 |
27 |
349 |
--------------------------------------------------------------------------------
/views/module.monitoring.bghost.view.php:
--------------------------------------------------------------------------------
1 | addJsFile('layout.mode.js');
27 | $this->addJsFile('gtlc.js');
28 | $this->addJsFile('class.tabfilter.js');
29 | $this->addJsFile('class.tabfilteritem.js');
30 |
31 | $this->includeJsFile('monitoring.host.view.js.php', $data);
32 |
33 | $this->enableLayoutModes();
34 | $web_layout_mode = $this->getLayoutMode();
35 | $nav_items = new CList();
36 |
37 | if ($data['can_create_hosts']) {
38 | $nav_items->addItem(
39 | (new CSimpleButton(_('Create host')))
40 | ->addClass('js-create-host')
41 | );
42 | }
43 |
44 | $nav_items->addItem(get_icon('kioskmode', ['mode' => $web_layout_mode]));
45 |
46 | $html_page = (new CHtmlPage())
47 | ->setTitle(_('Hosts'))
48 | ->setWebLayoutMode($web_layout_mode)
49 | ->setDocUrl(CDocHelper::getUrl(CDocHelper::MONITORING_HOST_VIEW))
50 | ->setControls((new CTag('nav', true, $nav_items))
51 | ->setAttribute('aria-label', _('Content controls'))
52 | );
53 |
54 | if ($web_layout_mode == ZBX_LAYOUT_NORMAL) {
55 | $filter = (new CTabFilter())
56 | ->setId('monitoring_hosts_filter')
57 | ->setOptions($data['tabfilter_options'])
58 | ->addTemplate(new CPartial($data['filter_view'], $data['filter_defaults']));
59 |
60 | foreach ($data['filter_tabs'] as $tab) {
61 | $tab['tab_view'] = $data['filter_view'];
62 | $filter->addTemplatedTab($tab['filter_name'], $tab);
63 | }
64 |
65 | // Set javascript options for tab filter initialization in monitoring.host.view.js.php file.
66 | $data['filter_options'] = $filter->options;
67 | $html_page->addItem($filter);
68 | }
69 | else {
70 | $data['filter_options'] = null;
71 | }
72 |
73 | $html_page
74 | ->addItem(
75 | (new CForm())
76 | ->setName('host_view')
77 | ->addClass('is-loading')
78 | )->show();
79 | $this->addCssFile('modules/zabbix-module-hosts-tree/views/css/bghost.css');
80 |
81 | (new CScriptTag('
82 | view.init('.json_encode([
83 | 'filter_options' => $data['filter_options'],
84 | 'refresh_url' => $data['refresh_url'],
85 | 'refresh_interval' => $data['refresh_interval'],
86 | 'applied_filter_groupids' => $data['filter_groupids']
87 | ]).');
88 | '))
89 | ->setOnDocumentReady()
90 | ->show();
91 |
--------------------------------------------------------------------------------
/views/module.monitoring.bghost.view.refresh.php:
--------------------------------------------------------------------------------
1 | (new CPartial('module.monitoring.host.view.html', $data))->getOutput(),
24 | 'groupids' => $data['filter']['groupids']
25 | ];
26 |
27 | if (($messages = getMessages()) !== null) {
28 | $output['messages'] = $messages->toString();
29 | }
30 |
31 | echo json_encode($output);
32 |
--------------------------------------------------------------------------------